From 76458664d8e210bd3d457e73b1ec7da76422a233 Mon Sep 17 00:00:00 2001 From: LYP <986263666@qq.com> Date: Wed, 7 Aug 2024 20:22:28 +0800 Subject: [PATCH] Ta android + ios --- .../Assets/Scenes/SampleScene.unity | 204 +- sdk-intergration/Assets/Scripts/SDKTest.cs | 32 + sdk-intergration/Packages/TA/Editor.meta | 8 + .../Packages/TA/Editor/TDInspectors.cs | 140 ++ .../Packages/TA/Editor/TDInspectors.cs.meta | 11 + .../Packages/TA/Editor/TDPostprocessBuild.cs | 104 + .../TA/Editor/TDPostprocessBuild.cs.meta | 11 + sdk-intergration/Packages/TA/Plugins.meta | 8 + .../Packages/TA/Plugins/Android.meta | 8 + .../TA/Plugins/Android/TDAnalytics.aar | Bin 0 -> 218619 bytes .../TA/Plugins/Android/TDAnalytics.aar.meta | 32 + .../Packages/TA/Plugins/Android/TDCore.aar | Bin 0 -> 44138 bytes .../TA/Plugins/Android/TDCore.aar.meta | 32 + .../Android/ThinkingSDK-gameengine.aar | Bin 0 -> 15947 bytes .../Android/ThinkingSDK-gameengine.aar.meta | 32 + .../Android/ThinkingSDK-thirdparty.aar | Bin 0 -> 23316 bytes .../Android/ThinkingSDK-thirdparty.aar.meta | 32 + sdk-intergration/Packages/TA/Plugins/PC.meta | 8 + .../Packages/TA/Plugins/PC/AutoTrack.meta | 8 + .../PC/AutoTrack/ThinkingSDKAutoTrack.cs | 213 ++ .../PC/AutoTrack/ThinkingSDKAutoTrack.cs.meta | 11 + .../Packages/TA/Plugins/PC/Config.meta | 8 + .../TA/Plugins/PC/Config/ThinkingSDKConfig.cs | 192 ++ .../PC/Config/ThinkingSDKConfig.cs.meta | 11 + .../PC/Config/ThinkingSDKPublicConfig.cs | 52 + .../PC/Config/ThinkingSDKPublicConfig.cs.meta | 11 + .../Packages/TA/Plugins/PC/Constant.meta | 8 + .../PC/Constant/ThinkingSDKConstant.cs | 116 ++ .../PC/Constant/ThinkingSDKConstant.cs.meta | 11 + .../Packages/TA/Plugins/PC/DataModel.meta | 8 + .../PC/DataModel/ThinkingSDKBaseData.cs | 125 ++ .../PC/DataModel/ThinkingSDKBaseData.cs.meta | 11 + .../PC/DataModel/ThinkingSDKEventData.cs | 82 + .../PC/DataModel/ThinkingSDKEventData.cs.meta | 11 + .../PC/DataModel/ThinkingSDKFirstEvent.cs | 35 + .../DataModel/ThinkingSDKFirstEvent.cs.meta | 11 + .../DataModel/ThinkingSDKOverWritableEvent.cs | 24 + .../ThinkingSDKOverWritableEvent.cs.meta | 11 + .../PC/DataModel/ThinkingSDKUpdateEvent.cs | 25 + .../DataModel/ThinkingSDKUpdateEvent.cs.meta | 11 + .../PC/DataModel/ThinkingSDKUserData.cs | 31 + .../PC/DataModel/ThinkingSDKUserData.cs.meta | 11 + .../Packages/TA/Plugins/PC/Main.meta | 8 + .../PC/Main/LightThinkingSDKInstance.cs | 97 + .../PC/Main/LightThinkingSDKInstance.cs.meta | 11 + .../TA/Plugins/PC/Main/ThinkingPCSDK.cs | 362 ++++ .../TA/Plugins/PC/Main/ThinkingPCSDK.cs.meta | 11 + .../TA/Plugins/PC/Main/ThinkingSDKInstance.cs | 786 ++++++++ .../PC/Main/ThinkingSDKInstance.cs.meta | 11 + .../Packages/TA/Plugins/PC/Request.meta | 8 + .../PC/Request/ThinkingSDKBaseRequest.cs | 155 ++ .../PC/Request/ThinkingSDKBaseRequest.cs.meta | 11 + .../PC/Request/ThinkingSDKDebugRequest.cs | 95 + .../Request/ThinkingSDKDebugRequest.cs.meta | 11 + .../PC/Request/ThinkingSDKNormalRequest.cs | 106 + .../Request/ThinkingSDKNormalRequest.cs.meta | 11 + .../Packages/TA/Plugins/PC/Storage.meta | 8 + .../TA/Plugins/PC/Storage/ThinkingSDKFile.cs | 80 + .../PC/Storage/ThinkingSDKFile.cs.meta | 11 + .../Plugins/PC/Storage/ThinkingSDKFileJson.cs | 188 ++ .../PC/Storage/ThinkingSDKFileJson.cs.meta | 11 + .../Packages/TA/Plugins/PC/TaskManager.meta | 8 + .../Plugins/PC/TaskManager/ThinkingSDKTask.cs | 130 ++ .../PC/TaskManager/ThinkingSDKTask.cs.meta | 11 + .../Packages/TA/Plugins/PC/ThinkingSDK.asmdef | 3 + .../TA/Plugins/PC/ThinkingSDK.asmdef.meta | 7 + .../Packages/TA/Plugins/PC/Time.meta | 8 + .../Packages/TA/Plugins/PC/Time/TDTimeout.cs | 44 + .../TA/Plugins/PC/Time/TDTimeout.cs.meta | 11 + .../PC/Time/ThinkingSDKCalibratedTime.cs | 43 + .../PC/Time/ThinkingSDKCalibratedTime.cs.meta | 11 + .../Plugins/PC/Time/ThinkingSDKDefinedTime.cs | 25 + .../PC/Time/ThinkingSDKDefinedTime.cs.meta | 11 + .../PC/Time/ThinkingSDKNTPCalibration.cs | 131 ++ .../PC/Time/ThinkingSDKNTPCalibration.cs.meta | 11 + .../TA/Plugins/PC/Time/ThinkingSDKTime.cs | 42 + .../Plugins/PC/Time/ThinkingSDKTime.cs.meta | 11 + .../PC/Time/ThinkingSDKTimeCalibration.cs | 32 + .../Time/ThinkingSDKTimeCalibration.cs.meta | 11 + .../Plugins/PC/Time/ThinkingSDKTimeInter.cs | 9 + .../PC/Time/ThinkingSDKTimeInter.cs.meta | 11 + .../Time/ThinkingSDKTimestampCalibration.cs | 21 + .../ThinkingSDKTimestampCalibration.cs.meta | 11 + .../Packages/TA/Plugins/PC/Utils.meta | 8 + .../TA/Plugins/PC/Utils/ThinkingSDKAppInfo.cs | 48 + .../PC/Utils/ThinkingSDKAppInfo.cs.meta | 11 + .../Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs | 229 +++ .../PC/Utils/ThinkingSDKDeviceInfo.cs.meta | 11 + .../TA/Plugins/PC/Utils/ThinkingSDKJSON.cs | 630 ++++++ .../Plugins/PC/Utils/ThinkingSDKJSON.cs.meta | 11 + .../TA/Plugins/PC/Utils/ThinkingSDKLogger.cs | 23 + .../PC/Utils/ThinkingSDKLogger.cs.meta | 11 + .../Plugins/PC/Utils/ThinkingSDKTimeUtil.cs | 15 + .../PC/Utils/ThinkingSDKTimeUtil.cs.meta | 11 + .../TA/Plugins/PC/Utils/ThinkingSDKUtil.cs | 199 ++ .../Plugins/PC/Utils/ThinkingSDKUtil.cs.meta | 11 + sdk-intergration/Packages/TA/Plugins/iOS.meta | 8 + .../Packages/TA/Plugins/iOS/TAThirdParty.meta | 8 + .../iOS/TAThirdParty/TAAdjustSyncData.h | 16 + .../iOS/TAThirdParty/TAAdjustSyncData.h.meta | 33 + .../iOS/TAThirdParty/TAAdjustSyncData.m | 33 + .../iOS/TAThirdParty/TAAdjustSyncData.m.meta | 33 + .../iOS/TAThirdParty/TAAppLovinSyncData.h | 16 + .../TAThirdParty/TAAppLovinSyncData.h.meta | 33 + .../iOS/TAThirdParty/TAAppLovinSyncData.m | 62 + .../TAThirdParty/TAAppLovinSyncData.m.meta | 33 + .../iOS/TAThirdParty/TAAppsFlyerSyncData.h | 16 + .../TAThirdParty/TAAppsFlyerSyncData.h.meta | 33 + .../iOS/TAThirdParty/TAAppsFlyerSyncData.m | 39 + .../TAThirdParty/TAAppsFlyerSyncData.m.meta | 33 + .../Plugins/iOS/TAThirdParty/TABaseSyncData.h | 76 + .../iOS/TAThirdParty/TABaseSyncData.h.meta | 33 + .../Plugins/iOS/TAThirdParty/TABaseSyncData.m | 21 + .../iOS/TAThirdParty/TABaseSyncData.m.meta | 33 + .../iOS/TAThirdParty/TABranchSyncData.h | 16 + .../iOS/TAThirdParty/TABranchSyncData.h.meta | 33 + .../iOS/TAThirdParty/TABranchSyncData.m | 37 + .../iOS/TAThirdParty/TABranchSyncData.m.meta | 33 + .../iOS/TAThirdParty/TAFirebaseSyncData.h | 16 + .../TAThirdParty/TAFirebaseSyncData.h.meta | 33 + .../iOS/TAThirdParty/TAFirebaseSyncData.m | 32 + .../TAThirdParty/TAFirebaseSyncData.m.meta | 33 + .../iOS/TAThirdParty/TAIronSourceSyncData.h | 16 + .../TAThirdParty/TAIronSourceSyncData.h.meta | 33 + .../iOS/TAThirdParty/TAIronSourceSyncData.m | 61 + .../TAThirdParty/TAIronSourceSyncData.m.meta | 33 + .../iOS/TAThirdParty/TAKochavaSyncData.h | 16 + .../iOS/TAThirdParty/TAKochavaSyncData.h.meta | 33 + .../iOS/TAThirdParty/TAKochavaSyncData.m | 42 + .../iOS/TAThirdParty/TAKochavaSyncData.m.meta | 33 + .../iOS/TAThirdParty/TAReYunSyncData.h | 16 + .../iOS/TAThirdParty/TAReYunSyncData.h.meta | 33 + .../iOS/TAThirdParty/TAReYunSyncData.m | 31 + .../iOS/TAThirdParty/TAReYunSyncData.m.meta | 33 + .../Plugins/iOS/TAThirdParty/TAThirdParty.h | 18 + .../iOS/TAThirdParty/TAThirdParty.h.meta | 33 + .../iOS/TAThirdParty/TAThirdPartyManager.h | 20 + .../TAThirdParty/TAThirdPartyManager.h.meta | 33 + .../iOS/TAThirdParty/TAThirdPartyManager.m | 168 ++ .../TAThirdParty/TAThirdPartyManager.m.meta | 33 + .../TAThirdParty/TAThirdPartySyncProtocol.h | 36 + .../TAThirdPartySyncProtocol.h.meta | 33 + .../iOS/TAThirdParty/TATopOnSyncData.h | 16 + .../iOS/TAThirdParty/TATopOnSyncData.h.meta | 33 + .../iOS/TAThirdParty/TATopOnSyncData.m | 39 + .../iOS/TAThirdParty/TATopOnSyncData.m.meta | 33 + .../iOS/TAThirdParty/TATradPlusSyncData.h | 16 + .../TAThirdParty/TATradPlusSyncData.h.meta | 33 + .../iOS/TAThirdParty/TATradPlusSyncData.m | 39 + .../TAThirdParty/TATradPlusSyncData.m.meta | 33 + .../TA/Plugins/iOS/ThinkingAnalytics.m | 517 +++++ .../TA/Plugins/iOS/ThinkingAnalytics.m.meta | 33 + .../Packages/TA/Plugins/iOS/ThinkingSDK.meta | 8 + .../TA/Plugins/iOS/ThinkingSDK/Info.plist | 24 + .../Plugins/iOS/ThinkingSDK/Info.plist.meta | 7 + .../TA/Plugins/iOS/ThinkingSDK/Source.meta | 8 + .../iOS/ThinkingSDK/Source/AppLaunch.meta | 8 + .../ThinkingSDK/Source/AppLaunch/AppPush.meta | 8 + .../AppLaunch/AppPush/TDAPPPushParams.h | 18 + .../AppLaunch/AppPush/TDAPPPushParams.h.meta | 33 + .../AppLaunch/AppPush/TDAPPPushParams.m | 70 + .../AppLaunch/AppPush/TDAPPPushParams.m.meta | 33 + .../AppPush/TDAppDelegateProxyManager.h | 14 + .../AppPush/TDAppDelegateProxyManager.h.meta | 33 + .../AppPush/TDAppDelegateProxyManager.m | 49 + .../AppPush/TDAppDelegateProxyManager.m.meta | 33 + .../AppPush/TDApplicationDelegateProxy.h | 10 + .../AppPush/TDApplicationDelegateProxy.h.meta | 33 + .../AppPush/TDApplicationDelegateProxy.m | 119 ++ .../AppPush/TDApplicationDelegateProxy.m.meta | 33 + .../TDUNUserNotificationCenterDelegateProxy.h | 16 + ...UserNotificationCenterDelegateProxy.h.meta | 33 + .../TDUNUserNotificationCenterDelegateProxy.m | 55 + ...UserNotificationCenterDelegateProxy.m.meta | 33 + .../AppPush/UIApplication+TDPushClick.h | 13 + .../AppPush/UIApplication+TDPushClick.h.meta | 33 + .../AppPush/UIApplication+TDPushClick.m | 29 + .../AppPush/UIApplication+TDPushClick.m.meta | 33 + .../UNUserNotificationCenter+TDPushClick.h | 14 + ...NUserNotificationCenter+TDPushClick.h.meta | 33 + .../UNUserNotificationCenter+TDPushClick.m | 18 + ...NUserNotificationCenter+TDPushClick.m.meta | 33 + .../Source/AppLaunch/HookDelegate.meta | 8 + .../HookDelegate/NSObject+TDDelegateProxy.h | 29 + .../NSObject+TDDelegateProxy.h.meta | 33 + .../HookDelegate/NSObject+TDDelegateProxy.m | 69 + .../NSObject+TDDelegateProxy.m.meta | 33 + .../AppLaunch/HookDelegate/TDClassHelper.h | 16 + .../HookDelegate/TDClassHelper.h.meta | 33 + .../AppLaunch/HookDelegate/TDClassHelper.m | 36 + .../HookDelegate/TDClassHelper.m.meta | 33 + .../AppLaunch/HookDelegate/TDDelegateProxy.h | 25 + .../HookDelegate/TDDelegateProxy.h.meta | 33 + .../AppLaunch/HookDelegate/TDDelegateProxy.m | 200 ++ .../HookDelegate/TDDelegateProxy.m.meta | 33 + .../HookDelegate/TDDelegateProxyObject.h | 34 + .../HookDelegate/TDDelegateProxyObject.h.meta | 33 + .../HookDelegate/TDDelegateProxyObject.m | 56 + .../HookDelegate/TDDelegateProxyObject.m.meta | 33 + .../AppLaunch/HookDelegate/TDMethodHelper.h | 24 + .../HookDelegate/TDMethodHelper.h.meta | 33 + .../AppLaunch/HookDelegate/TDMethodHelper.m | 59 + .../HookDelegate/TDMethodHelper.m.meta | 33 + .../Source/AppLaunch/NewSwizzle.meta | 8 + .../AppLaunch/NewSwizzle/TDNewSwizzle.h | 14 + .../AppLaunch/NewSwizzle/TDNewSwizzle.h.meta | 33 + .../AppLaunch/NewSwizzle/TDNewSwizzle.m | 155 ++ .../AppLaunch/NewSwizzle/TDNewSwizzle.m.meta | 33 + .../Source/AppLaunch/TDAppLaunchReason.h | 31 + .../Source/AppLaunch/TDAppLaunchReason.h.meta | 33 + .../Source/AppLaunch/TDAppLaunchReason.m | 177 ++ .../Source/AppLaunch/TDAppLaunchReason.m.meta | 33 + .../iOS/ThinkingSDK/Source/AppLifeCycle.meta | 8 + .../Source/AppLifeCycle/TAAppLifeCycle.h | 47 + .../Source/AppLifeCycle/TAAppLifeCycle.h.meta | 33 + .../Source/AppLifeCycle/TAAppLifeCycle.m | 246 +++ .../Source/AppLifeCycle/TAAppLifeCycle.m.meta | 33 + .../Source/AppLifeCycle/TDAppState.h | 28 + .../Source/AppLifeCycle/TDAppState.h.meta | 33 + .../Source/AppLifeCycle/TDAppState.m | 48 + .../Source/AppLifeCycle/TDAppState.m.meta | 33 + .../iOS/ThinkingSDK/Source/AutoTrack.meta | 8 + .../ThinkingSDK/Source/AutoTrack/Event.meta | 8 + .../Source/AutoTrack/Event/TAAppEndEvent.h | 17 + .../AutoTrack/Event/TAAppEndEvent.h.meta | 33 + .../Source/AutoTrack/Event/TAAppEndEvent.m | 23 + .../AutoTrack/Event/TAAppEndEvent.m.meta | 33 + .../Source/AutoTrack/Event/TAAppStartEvent.h | 19 + .../AutoTrack/Event/TAAppStartEvent.h.meta | 33 + .../Source/AutoTrack/Event/TAAppStartEvent.m | 27 + .../AutoTrack/Event/TAAppStartEvent.m.meta | 33 + .../Source/AutoTrack/Event/TAAutoClickEvent.h | 22 + .../AutoTrack/Event/TAAutoClickEvent.h.meta | 33 + .../Source/AutoTrack/Event/TAAutoClickEvent.m | 38 + .../AutoTrack/Event/TAAutoClickEvent.m.meta | 33 + .../AutoTrack/Event/TAAutoPageViewEvent.h | 20 + .../Event/TAAutoPageViewEvent.h.meta | 33 + .../AutoTrack/Event/TAAutoPageViewEvent.m | 31 + .../Event/TAAutoPageViewEvent.m.meta | 33 + .../Source/AutoTrack/Event/TAAutoTrackEvent.h | 26 + .../AutoTrack/Event/TAAutoTrackEvent.h.meta | 33 + .../Source/AutoTrack/Event/TAAutoTrackEvent.m | 58 + .../AutoTrack/Event/TAAutoTrackEvent.m.meta | 33 + .../Source/AutoTrack/Event/TAPushClickEvent.h | 16 + .../AutoTrack/Event/TAPushClickEvent.h.meta | 33 + .../Source/AutoTrack/Event/TAPushClickEvent.m | 18 + .../AutoTrack/Event/TAPushClickEvent.m.meta | 33 + .../Source/AutoTrack/TAAutoTrackProtocol.h | 80 + .../AutoTrack/TAAutoTrackProtocol.h.meta | 33 + .../AutoTrack/TAAutoTrackPublicHeader.h | 23 + .../AutoTrack/TAAutoTrackPublicHeader.h.meta | 33 + .../Source/AutoTrack/TDAutoTrackManager.h | 34 + .../AutoTrack/TDAutoTrackManager.h.meta | 33 + .../Source/AutoTrack/TDAutoTrackManager.m | 764 +++++++ .../AutoTrack/TDAutoTrackManager.m.meta | 33 + .../ThinkingSDK/Source/AutoTrack/Tracker.meta | 8 + .../AutoTrack/Tracker/TDAppEndTracker.h | 16 + .../AutoTrack/Tracker/TDAppEndTracker.h.meta | 33 + .../AutoTrack/Tracker/TDAppEndTracker.m | 12 + .../AutoTrack/Tracker/TDAppEndTracker.m.meta | 33 + .../Source/AutoTrack/Tracker/TDAutoTracker.h | 28 + .../AutoTrack/Tracker/TDAutoTracker.h.meta | 33 + .../Source/AutoTrack/Tracker/TDAutoTracker.m | 67 + .../AutoTrack/Tracker/TDAutoTracker.m.meta | 33 + .../AutoTrack/Tracker/TDColdStartTracker.h | 16 + .../Tracker/TDColdStartTracker.h.meta | 33 + .../AutoTrack/Tracker/TDColdStartTracker.m | 19 + .../Tracker/TDColdStartTracker.m.meta | 33 + .../AutoTrack/Tracker/TDHotStartTracker.h | 16 + .../Tracker/TDHotStartTracker.h.meta | 33 + .../AutoTrack/Tracker/TDHotStartTracker.m | 12 + .../Tracker/TDHotStartTracker.m.meta | 33 + .../AutoTrack/Tracker/TDInstallTracker.h | 17 + .../AutoTrack/Tracker/TDInstallTracker.h.meta | 33 + .../AutoTrack/Tracker/TDInstallTracker.m | 22 + .../AutoTrack/Tracker/TDInstallTracker.m.meta | 33 + .../ThinkingSDK/Source/AutoTrack/UIKit.meta | 8 + .../AutoTrack/UIKit/UIApplication+AutoTrack.h | 14 + .../UIKit/UIApplication+AutoTrack.h.meta | 33 + .../AutoTrack/UIKit/UIApplication+AutoTrack.m | 24 + .../UIKit/UIApplication+AutoTrack.m.meta | 33 + .../UIKit/UIView+ThinkingAnalytics.h | 51 + .../UIKit/UIView+ThinkingAnalytics.h.meta | 33 + .../UIKit/UIView+ThinkingAnalytics.m | 77 + .../UIKit/UIView+ThinkingAnalytics.m.meta | 33 + .../UIKit/UIViewController+AutoTrack.h | 7 + .../UIKit/UIViewController+AutoTrack.h.meta | 33 + .../UIKit/UIViewController+AutoTrack.m | 16 + .../UIKit/UIViewController+AutoTrack.m.meta | 33 + .../iOS/ThinkingSDK/Source/Config.meta | 8 + .../iOS/ThinkingSDK/Source/Config/TDConfig.h | 121 ++ .../ThinkingSDK/Source/Config/TDConfig.h.meta | 33 + .../iOS/ThinkingSDK/Source/Config/TDConfig.m | 187 ++ .../ThinkingSDK/Source/Config/TDConfig.m.meta | 33 + .../Source/Config/TDPublicConfig.h | 22 + .../Source/Config/TDPublicConfig.h.meta | 33 + .../Source/Config/TDPublicConfig.m | 62 + .../Source/Config/TDPublicConfig.m.meta | 33 + .../Plugins/iOS/ThinkingSDK/Source/Core.meta | 8 + .../Source/Core/CalibratedTime.meta | 8 + .../Core/CalibratedTime/TDCalibratedTime.h | 17 + .../CalibratedTime/TDCalibratedTime.h.meta | 33 + .../Core/CalibratedTime/TDCalibratedTime.m | 29 + .../CalibratedTime/TDCalibratedTime.m.meta | 33 + .../CalibratedTime/TDCalibratedTimeWithNTP.h | 17 + .../TDCalibratedTimeWithNTP.h.meta | 33 + .../CalibratedTime/TDCalibratedTimeWithNTP.m | 88 + .../TDCalibratedTimeWithNTP.m.meta | 33 + .../Source/Core/CalibratedTime/TDNTPServer.h | 28 + .../Core/CalibratedTime/TDNTPServer.h.meta | 33 + .../Source/Core/CalibratedTime/TDNTPServer.m | 217 ++ .../Core/CalibratedTime/TDNTPServer.m.meta | 33 + .../Source/Core/CalibratedTime/TDNTPTypes.c | 61 + .../Core/CalibratedTime/TDNTPTypes.c.meta | 33 + .../Source/Core/CalibratedTime/TDNTPTypes.h | 51 + .../Core/CalibratedTime/TDNTPTypes.h.meta | 33 + .../iOS/ThinkingSDK/Source/Core/Logger.meta | 8 + .../Source/Core/Logger/TDLogging.h | 34 + .../Source/Core/Logger/TDLogging.h.meta | 33 + .../Source/Core/Logger/TDLogging.m | 35 + .../Source/Core/Logger/TDLogging.m.meta | 33 + .../ThinkingSDK/Source/Core/Logger/TDOSLog.h | 45 + .../Source/Core/Logger/TDOSLog.h.meta | 33 + .../ThinkingSDK/Source/Core/Logger/TDOSLog.m | 198 ++ .../Source/Core/Logger/TDOSLog.m.meta | 33 + .../Source/Core/TAThreadSafeArray.h | 13 + .../Source/Core/TAThreadSafeArray.h.meta | 33 + .../Source/Core/TAThreadSafeArray.m | 372 ++++ .../Source/Core/TAThreadSafeArray.m.meta | 33 + .../Source/Core/TAThreadSafeDictionary.h | 11 + .../Source/Core/TAThreadSafeDictionary.h.meta | 33 + .../Source/Core/TAThreadSafeDictionary.m | 223 +++ .../Source/Core/TAThreadSafeDictionary.m.meta | 33 + .../iOS/ThinkingSDK/Source/Core/TDConstant.h | 210 ++ .../ThinkingSDK/Source/Core/TDConstant.h.meta | 33 + .../iOS/ThinkingSDK/Source/DeviceInfo.meta | 8 + .../Source/DeviceInfo/TDDeviceInfo.h | 35 + .../Source/DeviceInfo/TDDeviceInfo.h.meta | 33 + .../Source/DeviceInfo/TDDeviceInfo.m | 481 +++++ .../Source/DeviceInfo/TDDeviceInfo.m.meta | 33 + .../Source/DeviceInfo/TDFPSMonitor.h | 20 + .../Source/DeviceInfo/TDFPSMonitor.h.meta | 33 + .../Source/DeviceInfo/TDFPSMonitor.m | 76 + .../Source/DeviceInfo/TDFPSMonitor.m.meta | 33 + .../Source/DeviceInfo/TDPerformance.h | 17 + .../Source/DeviceInfo/TDPerformance.h.meta | 33 + .../Source/DeviceInfo/TDPerformance.m | 176 ++ .../Source/DeviceInfo/TDPerformance.m.meta | 33 + .../iOS/ThinkingSDK/Source/Encrypt.meta | 8 + .../ThinkingSDK/Source/Encrypt/TDEncrypt.h | 11 + .../Source/Encrypt/TDEncrypt.h.meta | 33 + .../Source/Encrypt/TDEncryptAlgorithm.h | 20 + .../Source/Encrypt/TDEncryptAlgorithm.h.meta | 33 + .../Source/Encrypt/TDEncryptManager.h | 32 + .../Source/Encrypt/TDEncryptManager.h.meta | 33 + .../Source/Encrypt/TDEncryptManager.m | 217 ++ .../Source/Encrypt/TDEncryptManager.m.meta | 33 + .../Source/Encrypt/TDEncryptProtocol.h | 26 + .../Source/Encrypt/TDEncryptProtocol.h.meta | 33 + .../ThinkingSDK/Source/Encrypt/TDSecretKey.h | 37 + .../Source/Encrypt/TDSecretKey.h.meta | 33 + .../ThinkingSDK/Source/Encrypt/TDSecretKey.m | 80 + .../Source/Encrypt/TDSecretKey.m.meta | 33 + .../ThinkingSDK/Source/Encrypt/plugin.meta | 8 + .../Source/Encrypt/plugin/TDAESEncryptor.h | 19 + .../Encrypt/plugin/TDAESEncryptor.h.meta | 33 + .../Source/Encrypt/plugin/TDAESEncryptor.m | 85 + .../Encrypt/plugin/TDAESEncryptor.m.meta | 33 + .../Source/Encrypt/plugin/TDRSAEncryptor.h | 19 + .../Encrypt/plugin/TDRSAEncryptor.h.meta | 33 + .../Source/Encrypt/plugin/TDRSAEncryptor.m | 180 ++ .../Encrypt/plugin/TDRSAEncryptor.m.meta | 33 + .../Encrypt/plugin/TDRSAEncryptorPlugin.h | 18 + .../plugin/TDRSAEncryptorPlugin.h.meta | 33 + .../Encrypt/plugin/TDRSAEncryptorPlugin.m | 53 + .../plugin/TDRSAEncryptorPlugin.m.meta | 33 + .../iOS/ThinkingSDK/Source/EventTracker.meta | 8 + .../Source/EventTracker/EventModel.meta | 8 + .../EventTracker/EventModel/TABaseEvent+H5.h | 18 + .../EventModel/TABaseEvent+H5.h.meta | 33 + .../EventTracker/EventModel/TABaseEvent+H5.m | 32 + .../EventModel/TABaseEvent+H5.m.meta | 33 + .../EventTracker/EventModel/TABaseEvent.h | 83 + .../EventModel/TABaseEvent.h.meta | 33 + .../EventTracker/EventModel/TABaseEvent.m | 215 ++ .../EventModel/TABaseEvent.m.meta | 33 + .../EventTracker/EventModel/TATrackEvent.h | 34 + .../EventModel/TATrackEvent.h.meta | 33 + .../EventTracker/EventModel/TATrackEvent.m | 75 + .../EventModel/TATrackEvent.m.meta | 33 + .../EventModel/TATrackFirstEvent.h | 18 + .../EventModel/TATrackFirstEvent.h.meta | 33 + .../EventModel/TATrackFirstEvent.m | 42 + .../EventModel/TATrackFirstEvent.m.meta | 33 + .../EventModel/TATrackOverwriteEvent.h | 18 + .../EventModel/TATrackOverwriteEvent.h.meta | 33 + .../EventModel/TATrackOverwriteEvent.m | 42 + .../EventModel/TATrackOverwriteEvent.m.meta | 33 + .../EventModel/TATrackUpdateEvent.h | 18 + .../EventModel/TATrackUpdateEvent.h.meta | 33 + .../EventModel/TATrackUpdateEvent.m | 42 + .../EventModel/TATrackUpdateEvent.m.meta | 33 + .../EventModel/TDEditableEventModel.h | 12 + .../EventModel/TDEditableEventModel.h.meta | 33 + .../EventTracker/EventModel/TDEventModel.h | 31 + .../EventModel/TDEventModel.h.meta | 33 + .../EventTracker/EventModel/TDEventModel.m | 72 + .../EventModel/TDEventModel.m.meta | 33 + .../EventModel/TDFirstEventModel.h | 19 + .../EventModel/TDFirstEventModel.h.meta | 33 + .../EventModel/TDFirstEventModel.m | 17 + .../EventModel/TDFirstEventModel.m.meta | 33 + .../EventModel/TDOverwriteEventModel.h | 22 + .../EventModel/TDOverwriteEventModel.h.meta | 33 + .../EventModel/TDOverwriteEventModel.m | 20 + .../EventModel/TDOverwriteEventModel.m.meta | 33 + .../EventModel/TDUpdateEventModel.h | 22 + .../EventModel/TDUpdateEventModel.h.meta | 33 + .../EventModel/TDUpdateEventModel.m | 20 + .../EventModel/TDUpdateEventModel.m.meta | 33 + .../EventTracker/EventModel/UserProperty.meta | 8 + .../EventModel/UserProperty/TAUserEvent.h | 16 + .../UserProperty/TAUserEvent.h.meta | 33 + .../EventModel/UserProperty/TAUserEvent.m | 35 + .../UserProperty/TAUserEvent.m.meta | 33 + .../EventModel/UserProperty/TAUserEventAdd.h | 16 + .../UserProperty/TAUserEventAdd.h.meta | 33 + .../EventModel/UserProperty/TAUserEventAdd.m | 36 + .../UserProperty/TAUserEventAdd.m.meta | 33 + .../UserProperty/TAUserEventAppend.h | 16 + .../UserProperty/TAUserEventAppend.h.meta | 33 + .../UserProperty/TAUserEventAppend.m | 32 + .../UserProperty/TAUserEventAppend.m.meta | 33 + .../UserProperty/TAUserEventDelete.h | 16 + .../UserProperty/TAUserEventDelete.h.meta | 33 + .../UserProperty/TAUserEventDelete.m | 19 + .../UserProperty/TAUserEventDelete.m.meta | 33 + .../EventModel/UserProperty/TAUserEventSet.h | 16 + .../UserProperty/TAUserEventSet.h.meta | 33 + .../EventModel/UserProperty/TAUserEventSet.m | 19 + .../UserProperty/TAUserEventSet.m.meta | 33 + .../UserProperty/TAUserEventSetOnce.h | 16 + .../UserProperty/TAUserEventSetOnce.h.meta | 33 + .../UserProperty/TAUserEventSetOnce.m | 19 + .../UserProperty/TAUserEventSetOnce.m.meta | 33 + .../UserProperty/TAUserEventUniqueAppend.h | 16 + .../TAUserEventUniqueAppend.h.meta | 33 + .../UserProperty/TAUserEventUniqueAppend.m | 19 + .../TAUserEventUniqueAppend.m.meta | 33 + .../UserProperty/TAUserEventUnset.h | 16 + .../UserProperty/TAUserEventUnset.h.meta | 33 + .../UserProperty/TAUserEventUnset.m | 19 + .../UserProperty/TAUserEventUnset.m.meta | 33 + .../UserProperty/TAUserPropertyHeader.h | 19 + .../UserProperty/TAUserPropertyHeader.h.meta | 33 + .../Source/EventTracker/Property.meta | 8 + .../EventTracker/Property/PresetProperty.meta | 8 + .../TDPresetProperties+TDDisProperties.h | 72 + .../TDPresetProperties+TDDisProperties.h.meta | 33 + .../TDPresetProperties+TDDisProperties.m | 342 ++++ .../TDPresetProperties+TDDisProperties.m.meta | 33 + .../PresetProperty/TDPresetProperties.h | 36 + .../PresetProperty/TDPresetProperties.h.meta | 33 + .../PresetProperty/TDPresetProperties.m | 88 + .../PresetProperty/TDPresetProperties.m.meta | 33 + .../Property/PropertyPlugins.meta | 8 + .../PropertyPlugins/TAPresetPropertyPlugin.h | 23 + .../TAPresetPropertyPlugin.h.meta | 33 + .../PropertyPlugins/TAPresetPropertyPlugin.m | 61 + .../TAPresetPropertyPlugin.m.meta | 33 + .../PropertyPlugins/TAPropertyPluginManager.h | 42 + .../TAPropertyPluginManager.h.meta | 33 + .../PropertyPlugins/TAPropertyPluginManager.m | 127 ++ .../TAPropertyPluginManager.m.meta | 33 + .../PropertyPlugins/TASessionIdManager.h | 19 + .../PropertyPlugins/TASessionIdManager.h.meta | 33 + .../PropertyPlugins/TASessionIdManager.m | 75 + .../PropertyPlugins/TASessionIdManager.m.meta | 33 + .../TASessionIdPropertyPlugin.h | 23 + .../TASessionIdPropertyPlugin.h.meta | 33 + .../TASessionIdPropertyPlugin.m | 66 + .../TASessionIdPropertyPlugin.m.meta | 33 + .../Property/TAAutoTrackSuperProperty.h | 34 + .../Property/TAAutoTrackSuperProperty.h.meta | 33 + .../Property/TAAutoTrackSuperProperty.m | 121 ++ .../Property/TAAutoTrackSuperProperty.m.meta | 33 + .../EventTracker/Property/TASuperProperty.h | 35 + .../Property/TASuperProperty.h.meta | 33 + .../EventTracker/Property/TASuperProperty.m | 106 + .../Property/TASuperProperty.m.meta | 33 + .../EventTracker/Property/Validate.meta | 8 + .../Property/Validate/NSArray+TAProperty.h | 17 + .../Validate/NSArray+TAProperty.h.meta | 33 + .../Property/Validate/NSArray+TAProperty.m | 16 + .../Validate/NSArray+TAProperty.m.meta | 33 + .../Property/Validate/NSDate+TAProperty.h | 17 + .../Validate/NSDate+TAProperty.h.meta | 33 + .../Property/Validate/NSDate+TAProperty.m | 16 + .../Validate/NSDate+TAProperty.m.meta | 33 + .../Validate/NSDictionary+TAProperty.h | 17 + .../Validate/NSDictionary+TAProperty.h.meta | 33 + .../Validate/NSDictionary+TAProperty.m | 16 + .../Validate/NSDictionary+TAProperty.m.meta | 33 + .../Property/Validate/NSNumber+TAProperty.h | 17 + .../Validate/NSNumber+TAProperty.h.meta | 33 + .../Property/Validate/NSNumber+TAProperty.m | 20 + .../Validate/NSNumber+TAProperty.m.meta | 33 + .../Property/Validate/NSString+TAProperty.h | 17 + .../Validate/NSString+TAProperty.h.meta | 33 + .../Property/Validate/NSString+TAProperty.m | 36 + .../Validate/NSString+TAProperty.m.meta | 33 + .../Validate/TAPropertyDefaultValidator.h | 17 + .../TAPropertyDefaultValidator.h.meta | 33 + .../Validate/TAPropertyDefaultValidator.m | 17 + .../TAPropertyDefaultValidator.m.meta | 33 + .../Property/Validate/TAPropertyValidator.h | 30 + .../Validate/TAPropertyValidator.h.meta | 33 + .../Property/Validate/TAPropertyValidator.m | 133 ++ .../Validate/TAPropertyValidator.m.meta | 33 + .../Property/Validate/TAValidatorProtocol.h | 45 + .../Validate/TAValidatorProtocol.h.meta | 33 + .../Source/EventTracker/TAEventTracker.h | 45 + .../Source/EventTracker/TAEventTracker.h.meta | 33 + .../Source/EventTracker/TAEventTracker.m | 305 +++ .../Source/EventTracker/TAEventTracker.m.meta | 33 + .../Source/EventTracker/TrackDuration.meta | 8 + .../EventTracker/TrackDuration/TATrackTimer.h | 33 + .../TrackDuration/TATrackTimer.h.meta | 33 + .../EventTracker/TrackDuration/TATrackTimer.m | 125 ++ .../TrackDuration/TATrackTimer.m.meta | 33 + .../TrackDuration/TATrackTimerItem.h | 25 + .../TrackDuration/TATrackTimerItem.h.meta | 33 + .../TrackDuration/TATrackTimerItem.m | 17 + .../TrackDuration/TATrackTimerItem.m.meta | 33 + .../iOS/ThinkingSDK/Source/Exception.meta | 8 + .../Exception/ThinkingExceptionHandler.h | 21 + .../Exception/ThinkingExceptionHandler.h.meta | 33 + .../Exception/ThinkingExceptionHandler.m | 179 ++ .../Exception/ThinkingExceptionHandler.m.meta | 33 + .../iOS/ThinkingSDK/Source/Extension.meta | 8 + .../Source/Extension/TAAppExtensionAnalytic.h | 45 + .../Extension/TAAppExtensionAnalytic.h.meta | 33 + .../Source/Extension/TAAppExtensionAnalytic.m | 222 +++ .../Extension/TAAppExtensionAnalytic.m.meta | 33 + .../Extension/TAAppExtensionAnalyticConfig.h | 20 + .../TAAppExtensionAnalyticConfig.h.meta | 33 + .../Extension/TAAppExtensionAnalyticConfig.m | 12 + .../TAAppExtensionAnalyticConfig.m.meta | 33 + .../iOS/ThinkingSDK/Source/Network.meta | 8 + .../ThinkingSDK/Source/Network/TANetwork.h | 27 + .../Source/Network/TANetwork.h.meta | 33 + .../ThinkingSDK/Source/Network/TANetwork.m | 309 +++ .../Source/Network/TANetwork.m.meta | 33 + .../Source/Network/TAReachability.h | 28 + .../Source/Network/TAReachability.h.meta | 33 + .../Source/Network/TAReachability.m | 192 ++ .../Source/Network/TAReachability.m.meta | 33 + .../Source/Network/TDSecurityPolicy.h | 50 + .../Source/Network/TDSecurityPolicy.h.meta | 33 + .../Source/Network/TDSecurityPolicy.m | 248 +++ .../Source/Network/TDSecurityPolicy.m.meta | 33 + .../iOS/ThinkingSDK/Source/Router.meta | 8 + .../ThinkingSDK/Source/Router/TAAnnotation.h | 38 + .../Source/Router/TAAnnotation.h.meta | 33 + .../ThinkingSDK/Source/Router/TAAnnotation.m | 89 + .../Source/Router/TAAnnotation.m.meta | 33 + .../iOS/ThinkingSDK/Source/Router/TAContext.h | 28 + .../Source/Router/TAContext.h.meta | 33 + .../iOS/ThinkingSDK/Source/Router/TAContext.m | 68 + .../Source/Router/TAContext.m.meta | 33 + .../Source/Router/TAModuleManager.h | 72 + .../Source/Router/TAModuleManager.h.meta | 33 + .../Source/Router/TAModuleManager.m | 509 +++++ .../Source/Router/TAModuleManager.m.meta | 33 + .../Source/Router/TAModuleProtocol.h | 81 + .../Source/Router/TAModuleProtocol.h.meta | 33 + .../iOS/ThinkingSDK/Source/Router/TARouter.h | 38 + .../ThinkingSDK/Source/Router/TARouter.h.meta | 33 + .../iOS/ThinkingSDK/Source/Router/TARouter.m | 491 +++++ .../ThinkingSDK/Source/Router/TARouter.m.meta | 33 + .../Source/Router/TAServiceManager.h | 28 + .../Source/Router/TAServiceManager.h.meta | 33 + .../Source/Router/TAServiceManager.m | 163 ++ .../Source/Router/TAServiceManager.m.meta | 33 + .../Source/Router/TAServiceProtocol.h | 22 + .../Source/Router/TAServiceProtocol.h.meta | 33 + .../Plugins/iOS/ThinkingSDK/Source/Store.meta | 8 + .../iOS/ThinkingSDK/Source/Store/TDFile.h | 63 + .../ThinkingSDK/Source/Store/TDFile.h.meta | 33 + .../iOS/ThinkingSDK/Source/Store/TDFile.m | 283 +++ .../ThinkingSDK/Source/Store/TDFile.m.meta | 33 + .../Source/Store/TDKeychainHelper.h | 14 + .../Source/Store/TDKeychainHelper.h.meta | 33 + .../Source/Store/TDKeychainHelper.m | 121 ++ .../Source/Store/TDKeychainHelper.m.meta | 33 + .../Source/Store/TDSqliteDataQueue.h | 19 + .../Source/Store/TDSqliteDataQueue.h.meta | 33 + .../Source/Store/TDSqliteDataQueue.m | 347 ++++ .../Source/Store/TDSqliteDataQueue.m.meta | 33 + .../iOS/ThinkingSDK/Source/TDRuntime.meta | 8 + .../ThinkingSDK/Source/TDRuntime/Swizzle.meta | 8 + .../TDRuntime/Swizzle/NSObject+TDSwizzle.h | 10 + .../Swizzle/NSObject+TDSwizzle.h.meta | 33 + .../TDRuntime/Swizzle/NSObject+TDSwizzle.m | 41 + .../Swizzle/NSObject+TDSwizzle.m.meta | 33 + .../Source/TDRuntime/Swizzle/TDSwizzler.h | 13 + .../TDRuntime/Swizzle/TDSwizzler.h.meta | 33 + .../Source/TDRuntime/Swizzle/TDSwizzler.m | 241 +++ .../TDRuntime/Swizzle/TDSwizzler.m.meta | 33 + .../ThinkingSDK/Source/TDRuntime/TDRunTime.h | 21 + .../Source/TDRuntime/TDRunTime.h.meta | 33 + .../ThinkingSDK/Source/TDRuntime/TDRunTime.m | 46 + .../Source/TDRuntime/TDRunTime.m.meta | 33 + .../Plugins/iOS/ThinkingSDK/Source/Util.meta | 8 + .../iOS/ThinkingSDK/Source/Util/Category.meta | 8 + .../Source/Util/Category/NSData+TDGzip.h | 7 + .../Source/Util/Category/NSData+TDGzip.h.meta | 33 + .../Source/Util/Category/NSData+TDGzip.m | 40 + .../Source/Util/Category/NSData+TDGzip.m.meta | 33 + .../Source/Util/Category/NSDate+TAFormat.h | 25 + .../Util/Category/NSDate+TAFormat.h.meta | 33 + .../Source/Util/Category/NSDate+TAFormat.m | 29 + .../Util/Category/NSDate+TAFormat.m.meta | 33 + .../Util/Category/NSDictionary+TDJsonOutput.h | 17 + .../Category/NSDictionary+TDJsonOutput.h.meta | 33 + .../Util/Category/NSDictionary+TDJsonOutput.m | 30 + .../Category/NSDictionary+TDJsonOutput.m.meta | 33 + .../Source/Util/Category/NSObject+TDUtil.h | 19 + .../Util/Category/NSObject+TDUtil.h.meta | 33 + .../Source/Util/Category/NSObject+TDUtil.m | 185 ++ .../Util/Category/NSObject+TDUtil.m.meta | 33 + .../Source/Util/Category/NSString+TDString.h | 21 + .../Util/Category/NSString+TDString.h.meta | 33 + .../Source/Util/Category/NSString+TDString.m | 37 + .../Util/Category/NSString+TDString.m.meta | 33 + .../iOS/ThinkingSDK/Source/Util/TDCheck.h | 36 + .../ThinkingSDK/Source/Util/TDCheck.h.meta | 33 + .../iOS/ThinkingSDK/Source/Util/TDCheck.m | 68 + .../ThinkingSDK/Source/Util/TDCheck.m.meta | 33 + .../ThinkingSDK/Source/Util/TDCommonUtil.h | 21 + .../Source/Util/TDCommonUtil.h.meta | 33 + .../ThinkingSDK/Source/Util/TDCommonUtil.m | 28 + .../Source/Util/TDCommonUtil.m.meta | 33 + .../iOS/ThinkingSDK/Source/Util/TDJSONUtil.h | 13 + .../ThinkingSDK/Source/Util/TDJSONUtil.h.meta | 33 + .../iOS/ThinkingSDK/Source/Util/TDJSONUtil.m | 87 + .../ThinkingSDK/Source/Util/TDJSONUtil.m.meta | 33 + .../iOS/ThinkingSDK/Source/Util/TDSDKUtil.h | 17 + .../ThinkingSDK/Source/Util/TDSDKUtil.h.meta | 33 + .../iOS/ThinkingSDK/Source/Util/TDSDKUtil.m | 13 + .../ThinkingSDK/Source/Util/TDSDKUtil.m.meta | 33 + .../iOS/ThinkingSDK/Source/Util/TDWeakProxy.h | 24 + .../Source/Util/TDWeakProxy.h.meta | 33 + .../iOS/ThinkingSDK/Source/Util/TDWeakProxy.m | 81 + .../Source/Util/TDWeakProxy.m.meta | 33 + .../iOS/ThinkingSDK/Source/Util/Toast.meta | 8 + .../Source/Util/Toast/TDToastView.h | 11 + .../Source/Util/Toast/TDToastView.h.meta | 33 + .../Source/Util/Toast/TDToastView.m | 97 + .../Source/Util/Toast/TDToastView.m.meta | 33 + .../Plugins/iOS/ThinkingSDK/Source/main.meta | 8 + .../Source/main/LightThinkingAnalyticsSDK.m | 119 ++ .../main/LightThinkingAnalyticsSDK.m.meta | 33 + .../ThinkingSDK/Source/main/TDEventRecord.h | 33 + .../Source/main/TDEventRecord.h.meta | 33 + .../ThinkingSDK/Source/main/TDEventRecord.m | 97 + .../Source/main/TDEventRecord.m.meta | 33 + .../Source/main/ThinkingAnalyticsSDK.h | 520 +++++ .../Source/main/ThinkingAnalyticsSDK.h.meta | 33 + .../Source/main/ThinkingAnalyticsSDK.m | 1471 ++++++++++++++ .../Source/main/ThinkingAnalyticsSDK.m.meta | 33 + .../Source/main/ThinkingAnalyticsSDKPrivate.h | 168 ++ .../main/ThinkingAnalyticsSDKPrivate.h.meta | 33 + .../iOS/ThinkingSDK/Source/main/ThinkingSDK.h | 38 + .../Source/main/ThinkingSDK.h.meta | 33 + sdk-intergration/Packages/TA/Runtime.meta | 8 + .../TA/Runtime/ThinkingAnalytics.meta | 8 + .../Runtime/ThinkingAnalytics/Exception.meta | 8 + .../Exception/TDException.cs | 168 ++ .../Exception/TDException.cs.meta | 11 + .../Runtime/ThinkingAnalytics/Resources.meta | 8 + .../Resources/ta_public_config.xml | 43 + .../Resources/ta_public_config.xml.meta | 7 + .../ThinkingAnalytics/TDAnalytics.asmdef | 16 + .../ThinkingAnalytics/TDAnalytics.asmdef.meta | 7 + .../Runtime/ThinkingAnalytics/TDAnalytics.cs | 1493 ++++++++++++++ .../ThinkingAnalytics/TDAnalytics.cs.meta | 11 + .../ThinkingAnalytics/TDAnalytics.prefab | 61 + .../ThinkingAnalytics/TDAnalytics.prefab.meta | 7 + .../TA/Runtime/ThinkingAnalytics/TDEnum.cs | 64 + .../Runtime/ThinkingAnalytics/TDEnum.cs.meta | 11 + .../TA/Runtime/ThinkingAnalytics/TDEvent.cs | 130 ++ .../Runtime/ThinkingAnalytics/TDEvent.cs.meta | 11 + .../Runtime/ThinkingAnalytics/TDInterface.cs | 30 + .../ThinkingAnalytics/TDInterface.cs.meta | 11 + .../ThinkingAnalytics/TDPresetProperties.cs | 175 ++ .../TDPresetProperties.cs.meta | 11 + .../ThinkingAnalytics/ThinkingAnalyticsAPI.cs | 1753 +++++++++++++++++ .../ThinkingAnalyticsAPI.cs.meta | 11 + .../TA/Runtime/ThinkingAnalytics/Utils.meta | 8 + .../ThinkingAnalytics/Utils/TDCommonUtils.cs | 86 + .../Utils/TDCommonUtils.cs.meta | 11 + .../Runtime/ThinkingAnalytics/Utils/TDLog.cs | 51 + .../ThinkingAnalytics/Utils/TDLog.cs.meta | 11 + .../ThinkingAnalytics/Utils/TDMiniJson.cs | 610 ++++++ .../Utils/TDMiniJson.cs.meta | 11 + .../Utils/TDPropertiesChecker.cs | 186 ++ .../Utils/TDPropertiesChecker.cs.meta | 11 + .../ThinkingAnalytics/Utils/TDPublicConfig.cs | 74 + .../Utils/TDPublicConfig.cs.meta | 11 + .../TA/Runtime/ThinkingAnalytics/Wrapper.meta | 8 + .../Wrapper/TDAndroidWrapper.cs | 941 +++++++++ .../Wrapper/TDAndroidWrapper.cs.meta | 11 + .../ThinkingAnalytics/Wrapper/TDPCWrapper.cs | 415 ++++ .../Wrapper/TDPCWrapper.cs.meta | 11 + .../ThinkingAnalytics/Wrapper/TDWrapper.cs | 472 +++++ .../Wrapper/TDWrapper.cs.meta | 11 + .../ThinkingAnalytics/Wrapper/TDiOSWrapper.cs | 516 +++++ .../Wrapper/TDiOSWrapper.cs.meta | 11 + sdk-intergration/Packages/TA/package.json | 6 + .../Packages/TA/package.json.meta | 7 + sdk-intergration/Packages/packages-lock.json | 6 + 722 files changed, 41709 insertions(+), 1 deletion(-) create mode 100644 sdk-intergration/Packages/TA/Editor.meta create mode 100644 sdk-intergration/Packages/TA/Editor/TDInspectors.cs create mode 100644 sdk-intergration/Packages/TA/Editor/TDInspectors.cs.meta create mode 100644 sdk-intergration/Packages/TA/Editor/TDPostprocessBuild.cs create mode 100644 sdk-intergration/Packages/TA/Editor/TDPostprocessBuild.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/Android.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/Android/TDAnalytics.aar create mode 100644 sdk-intergration/Packages/TA/Plugins/Android/TDAnalytics.aar.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/Android/TDCore.aar create mode 100644 sdk-intergration/Packages/TA/Plugins/Android/TDCore.aar.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/Android/ThinkingSDK-gameengine.aar create mode 100644 sdk-intergration/Packages/TA/Plugins/Android/ThinkingSDK-gameengine.aar.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/Android/ThinkingSDK-thirdparty.aar create mode 100644 sdk-intergration/Packages/TA/Plugins/Android/ThinkingSDK-thirdparty.aar.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/AutoTrack.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Config.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKConfig.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKConfig.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKPublicConfig.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKPublicConfig.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Constant.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Constant/ThinkingSDKConstant.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Constant/ThinkingSDKConstant.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKBaseData.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKBaseData.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKEventData.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKEventData.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUserData.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUserData.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Main.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Main/LightThinkingSDKInstance.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Main/LightThinkingSDKInstance.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingPCSDK.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingPCSDK.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingSDKInstance.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingSDKInstance.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Request.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKBaseRequest.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKBaseRequest.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKDebugRequest.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKDebugRequest.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKNormalRequest.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKNormalRequest.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Storage.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFile.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFile.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFileJson.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFileJson.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/TaskManager.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/TaskManager/ThinkingSDKTask.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/TaskManager/ThinkingSDKTask.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/ThinkingSDK.asmdef create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/ThinkingSDK.asmdef.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/TDTimeout.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/TDTimeout.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKDefinedTime.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKDefinedTime.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTime.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTime.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeInter.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeInter.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKAppInfo.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKAppInfo.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKJSON.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKJSON.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKLogger.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKLogger.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKUtil.cs create mode 100644 sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKUtil.cs.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdParty.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdParty.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingAnalytics.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingAnalytics.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Info.plist create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Info.plist.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackProtocol.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackProtocol.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackPublicHeader.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackPublicHeader.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.c create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.c.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TDConstant.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TDConstant.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserPropertyHeader.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserPropertyHeader.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAValidatorProtocol.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAValidatorProtocol.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleProtocol.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleProtocol.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceProtocol.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceProtocol.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h.meta create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h create mode 100644 sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h.meta create mode 100644 sdk-intergration/Packages/TA/Runtime.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception/TDException.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception/TDException.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources/ta_public_config.xml create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources/ta_public_config.xml.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.asmdef create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.asmdef.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.prefab create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.prefab.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEnum.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEnum.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEvent.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEvent.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDInterface.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDInterface.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDPresetProperties.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDPresetProperties.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/ThinkingAnalyticsAPI.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/ThinkingAnalyticsAPI.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDCommonUtils.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDCommonUtils.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDLog.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDLog.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDMiniJson.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDMiniJson.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPropertiesChecker.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPropertiesChecker.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPublicConfig.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPublicConfig.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDAndroidWrapper.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDAndroidWrapper.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDPCWrapper.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDPCWrapper.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDWrapper.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDWrapper.cs.meta create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs create mode 100644 sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs.meta create mode 100644 sdk-intergration/Packages/TA/package.json create mode 100644 sdk-intergration/Packages/TA/package.json.meta diff --git a/sdk-intergration/Assets/Scenes/SampleScene.unity b/sdk-intergration/Assets/Scenes/SampleScene.unity index bb4e021..6db7b74 100644 --- a/sdk-intergration/Assets/Scenes/SampleScene.unity +++ b/sdk-intergration/Assets/Scenes/SampleScene.unity @@ -38,7 +38,7 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0.12733836, g: 0.13418846, b: 0.12118524, a: 1} + m_IndirectSpecularColor: {r: 0.12731749, g: 0.13414757, b: 0.1210787, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -1003,6 +1003,7 @@ RectTransform: - {fileID: 1110300853} - {fileID: 120435830} - {fileID: 1574191670} + - {fileID: 1523131986} m_Father: {fileID: 1451597222} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -1113,6 +1114,85 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1071692829} m_CullTransparentMesh: 1 +--- !u!1 &1088770786 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1088770787} + - component: {fileID: 1088770789} + - component: {fileID: 1088770788} + m_Layer: 5 + m_Name: Text (Legacy) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1088770787 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1088770786} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1523131986} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1088770788 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1088770786} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 33 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 0 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: TaInit +--- !u!222 &1088770789 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1088770786} + m_CullTransparentMesh: 1 --- !u!1 &1110300852 GameObject: m_ObjectHideFlags: 0 @@ -1658,6 +1738,127 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0} +--- !u!1 &1523131985 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1523131986} + - component: {fileID: 1523131989} + - component: {fileID: 1523131988} + - component: {fileID: 1523131987} + m_Layer: 5 + m_Name: TA + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1523131986 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1523131985} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1088770787} + m_Father: {fileID: 1044920373} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 1} +--- !u!114 &1523131987 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1523131985} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 1523131988} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &1523131988 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1523131985} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1523131989 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1523131985} + m_CullTransparentMesh: 1 --- !u!1 &1574191669 GameObject: m_ObjectHideFlags: 0 @@ -2017,6 +2218,7 @@ MonoBehaviour: _payListBtn: {fileID: 1110300854} _gaTestBtn: {fileID: 29523100} _gaAppMaxBtn: {fileID: 1574191671} + _taBtn: {fileID: 1523131987} _messageTxt: {fileID: 2070325010} --- !u!4 &1788702880 Transform: diff --git a/sdk-intergration/Assets/Scripts/SDKTest.cs b/sdk-intergration/Assets/Scripts/SDKTest.cs index a197676..0f8fb39 100644 --- a/sdk-intergration/Assets/Scripts/SDKTest.cs +++ b/sdk-intergration/Assets/Scripts/SDKTest.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using ThinkingData.Analytics; using tysdk; using UnityEngine; using UnityEngine.UI; @@ -24,6 +26,8 @@ public class SDKTest : MonoBehaviour [SerializeField] private Button _gaAppMaxBtn; [SerializeField] + private Button _taBtn; + [SerializeField] private Text _messageTxt; void Start() @@ -38,6 +42,7 @@ public class SDKTest : MonoBehaviour _payListBtn.onClick.AddListener(OnPayListBtn); _gaTestBtn.onClick.AddListener(OnGaBtn); _gaAppMaxBtn.onClick.AddListener(OnAppMaxBtn); + _taBtn.onClick.AddListener(OnTaBtn); } private async void OnLogin(EAccoutType accoutType) @@ -91,4 +96,31 @@ public class SDKTest : MonoBehaviour // TYSdkFacade.Instance.PlayAd(); } + public void OnTaBtn() + { + // 初始化SDK + TDAnalytics.Init("0caf287574bd4a9bb08be8995705ef6f", "https://122-slg-online01.qijihdhk.com:8991"); + // //如果用户已登录,可以设置用户的账号ID作为身份唯一标识 + // TDAnalytics.Login("TA"); + //设置公共事件属性以后,每个事件都会带有公共事件属性 + // Dictionary superProperties = new Dictionary(); + // superProperties["channel"] = "ta";//字符串 + // superProperties["age"] = 1;//数字 + // superProperties["isSuccess"] = true;//布尔 + // superProperties["birthday"] = DateTime.Now;//时间 + // superProperties["object"] = new Dictionary(){{ "key", "value"}};//对象 + // superProperties["object_arr"] = new List() {new Dictionary(){{ "key", "value" }}};//对象组 + // superProperties["arr"] = new List() { "value" };//数组 + // TDAnalytics.SetSuperProperties(superProperties);//设置公共事件属性 + //开启自动采集事件 + // TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.AppInstall | TDAutoTrackEventType.AppStart | TDAutoTrackEventType.AppEnd); + //发送事件 + // Dictionary properties = new Dictionary(){{"product_name", "商品名")}}; + // TDAnalytics.Track("product_buy", properties); + //设置用户属性 + // TDAnalytics.UserSet(new Dictionary(){{"user_name", "TA"}}); + + } + + } diff --git a/sdk-intergration/Packages/TA/Editor.meta b/sdk-intergration/Packages/TA/Editor.meta new file mode 100644 index 0000000..9fa18b1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1c3596aacf52e4dce91260e35b40c684 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Editor/TDInspectors.cs b/sdk-intergration/Packages/TA/Editor/TDInspectors.cs new file mode 100644 index 0000000..2f69826 --- /dev/null +++ b/sdk-intergration/Packages/TA/Editor/TDInspectors.cs @@ -0,0 +1,140 @@ +using UnityEngine; +using UnityEditor; +using UnityEditorInternal; + +namespace ThinkingData.Analytics.Editors +{ + [CustomEditor(typeof(TDAnalytics))] + [CanEditMultipleObjects] + public class TD_Inspectors : Editor + { + private ReorderableList _stringArray; + + public void OnEnable() + { + + var appId = this.serializedObject.FindProperty("configs"); + + _stringArray = new ReorderableList(appId.serializedObject, appId, true, true, true, true) + { + drawHeaderCallback = DrawListHeader, + drawElementCallback = DrawListElement, + onRemoveCallback = RemoveListElement, + onAddCallback = AddListElement + }; + + _stringArray.elementHeight = 5 * (EditorGUIUtility.singleLineHeight + 10); + + _stringArray.serializedProperty.isExpanded = true; + } + + void DrawListHeader(Rect rect) + { + var arect = rect; + arect.height = EditorGUIUtility.singleLineHeight + 10; + arect.x += 14; + arect.width = 80; + GUIStyle style = new GUIStyle(); + style.fontStyle = FontStyle.Bold; + + GUI.Label(arect, "Instance Configurations", style); + } + + void DrawListElement(Rect rect, int index, bool isActive, bool isFocused) + { + var spacing = 5; + var xSpacing = 85; + var arect = rect; + SerializedProperty item = _stringArray.serializedProperty.GetArrayElementAtIndex(index); + var serElem = this._stringArray.serializedProperty.GetArrayElementAtIndex(index); + arect.height = EditorGUIUtility.singleLineHeight; + arect.width = 240; + + if (index == 0) + { + EditorGUI.PropertyField(arect, item, new GUIContent((index + 1) + " (default)")); + } + else + { + EditorGUI.PropertyField(arect, item, new GUIContent("" + (index + 1))); + + } + arect.y += EditorGUIUtility.singleLineHeight + spacing; + GUIStyle style = new GUIStyle(); + style.fontStyle = FontStyle.Bold; + + + EditorGUI.LabelField(arect, "APP ID:", style); + arect.x += xSpacing; + EditorGUI.PropertyField(arect, serElem.FindPropertyRelative("appId"), GUIContent.none); + + arect.y += EditorGUIUtility.singleLineHeight + spacing; + arect.x -= xSpacing; + + EditorGUI.LabelField(arect, "SERVER URL:", style); + arect.x += xSpacing; + EditorGUI.PropertyField(new Rect(arect.x, arect.y, arect.width, arect.height), serElem.FindPropertyRelative("serverUrl"), GUIContent.none); + + arect.y += EditorGUIUtility.singleLineHeight + spacing; + arect.x -= xSpacing; + + EditorGUI.LabelField(arect, "MODE:", style); + arect.x += xSpacing; + EditorGUI.PropertyField(arect, serElem.FindPropertyRelative("mode"), GUIContent.none); + + arect.y += EditorGUIUtility.singleLineHeight + spacing; + arect.x -= xSpacing; + + EditorGUI.LabelField(arect, "TimeZone:", style); + arect.x += xSpacing; + var a = serElem.FindPropertyRelative("timeZone"); + if (a.intValue == 100) + { + EditorGUI.PropertyField(new Rect(arect.x, arect.y, 115, arect.height), a, GUIContent.none); + arect.x += 125; + EditorGUI.PropertyField(new Rect(arect.x, arect.y, 115, arect.height), serElem.FindPropertyRelative("timeZoneId"), GUIContent.none); + } + else + { + EditorGUI.PropertyField(arect, a, GUIContent.none); + } + } + + void AddListElement(ReorderableList list) + { + if (list.serializedProperty != null) + { + list.serializedProperty.arraySize++; + list.index = list.serializedProperty.arraySize - 1; + SerializedProperty item = list.serializedProperty.GetArrayElementAtIndex(list.index); + item.FindPropertyRelative("appId").stringValue = ""; + } + else + { + ReorderableList.defaultBehaviours.DoAddButton(list); + } + } + + void RemoveListElement(ReorderableList list) + { + if (EditorUtility.DisplayDialog("Warnning", "Do you want to remove this element?", "Remove", "Cancel")) + { + ReorderableList.defaultBehaviours.DoRemoveButton(list); + } + } + + public override void OnInspectorGUI() + { + DrawDefaultInspector(); + this.serializedObject.Update(); + var property = _stringArray.serializedProperty; + property.isExpanded = EditorGUILayout.Foldout(property.isExpanded, property.displayName); + if (property.isExpanded) + { + + _stringArray.DoLayoutList(); + } + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/sdk-intergration/Packages/TA/Editor/TDInspectors.cs.meta b/sdk-intergration/Packages/TA/Editor/TDInspectors.cs.meta new file mode 100644 index 0000000..d24fd67 --- /dev/null +++ b/sdk-intergration/Packages/TA/Editor/TDInspectors.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e175a5169e2c4de78d15b28f2ce6520 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Editor/TDPostprocessBuild.cs b/sdk-intergration/Packages/TA/Editor/TDPostprocessBuild.cs new file mode 100644 index 0000000..0c60fed --- /dev/null +++ b/sdk-intergration/Packages/TA/Editor/TDPostprocessBuild.cs @@ -0,0 +1,104 @@ +#if UNITY_EDITOR && UNITY_IOS +using System.IO; +using ThinkingData.Analytics.Utils; +using UnityEditor; +using UnityEditor.Callbacks; +using UnityEditor.iOS.Xcode; +using UnityEngine; + +namespace ThinkingData.Analytics.Editors +{ + public class TD_PostProcessBuild + { + //Xcode Build Settings + //[PostProcessBuild] + [PostProcessBuildAttribute(88)] + public static void OnPostProcessBuild(BuildTarget target, string targetPath) + { + if (target != BuildTarget.iOS) + { + Debug.LogWarning("[ThinkingData] Warning: Target is not iOS. XCodePostProcess will not run"); + return; + } + + string projPath = Path.GetFullPath(targetPath) + "/Unity-iPhone.xcodeproj/project.pbxproj"; + + PBXProject proj = new 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 + proj.AddBuildProperty(targetGuid, "OTHER_LDFLAGS", "-ObjC"); + + 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); + proj.AddFrameworkToProject(targetGuid, "UserNotifications.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); + + //Info.plist + //Disable preset properties + string plistPath = Path.Combine(targetPath, "Info.plist"); + PlistDocument plist = new PlistDocument(); + plist.ReadFromFile(plistPath); + plist.root.CreateArray("TDDisPresetProperties"); + TDPublicConfig.GetPublicConfig(); + foreach (string item in TDPublicConfig.DisPresetProperties) + { + plist.root["TDDisPresetProperties"].AsArray().AddString(item); + } + plist.WriteToFile(plistPath); + } + } +} +#endif + + +#if UNITY_EDITOR && UNITY_ANDROID && UNITY_2019_1_OR_NEWER +using UnityEditor; +using UnityEditor.Android; +using UnityEngine; +using System.IO; +using System.Xml; +using System.Collections.Generic; + +namespace ThinkingData.Analytics.Editors +{ + + class TD_PostProcessBuild : IPostGenerateGradleAndroidProject + { + // Copy configuration file ta_public_config.xml + public int callbackOrder { get { return 0; } } + public void OnPostGenerateGradleAndroidProject(string path) + { + // Copy configuration file ta_public_config.xml + string desPath = path + "/../launcher/src/main/res/values/ta_public_config.xml"; + if (File.Exists(desPath)) + { + File.Delete(desPath); + } + TextAsset textAsset = Resources.Load("ta_public_config"); + if (textAsset != null && textAsset.bytes != null) + { + File.WriteAllBytes(desPath, textAsset.bytes); + } + } + } +} +#endif diff --git a/sdk-intergration/Packages/TA/Editor/TDPostprocessBuild.cs.meta b/sdk-intergration/Packages/TA/Editor/TDPostprocessBuild.cs.meta new file mode 100644 index 0000000..340f92a --- /dev/null +++ b/sdk-intergration/Packages/TA/Editor/TDPostprocessBuild.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 75476b67117c64ab1a25a2b94db96dcb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins.meta b/sdk-intergration/Packages/TA/Plugins.meta new file mode 100644 index 0000000..6ee2a7b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b539e47b3e609b0489cf78ee8db1ca03 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/Android.meta b/sdk-intergration/Packages/TA/Plugins/Android.meta new file mode 100644 index 0000000..4876f8f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/Android.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e71769993b31fd643ab8c24976630778 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/Android/TDAnalytics.aar b/sdk-intergration/Packages/TA/Plugins/Android/TDAnalytics.aar new file mode 100644 index 0000000000000000000000000000000000000000..ecf7359e07e4f7701f08927f1fb986ced926897a GIT binary patch literal 218619 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𝔨Ve0WUa-ZSo8 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 literal 0 HcmV?d00001 diff --git a/sdk-intergration/Packages/TA/Plugins/Android/TDAnalytics.aar.meta b/sdk-intergration/Packages/TA/Plugins/Android/TDAnalytics.aar.meta new file mode 100644 index 0000000..7abadc3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/Android/TDAnalytics.aar.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: 5f7f0ddaf71ac4a00a8d6e31e982eaed +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Android: Android + second: + enabled: 1 + settings: {} + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/Android/TDCore.aar b/sdk-intergration/Packages/TA/Plugins/Android/TDCore.aar new file mode 100644 index 0000000000000000000000000000000000000000..a3f8de505e495d10a29e1769b62c3a32e50f8628 GIT binary patch literal 44138 zcmZ5`V~j3Ju9M{C}_f=NJg& zf0zFN{vVb8*FEU}xrEzxW?KKB5epay2=%{R(9Xok-qJ+Y(9Y7#)Y*l>!`8+$z87JT z2`=Q7Uo@o`9tuL``4nCwVZC;^MKMBqg!23= zQhs!Cev*85GWn{z=>G3Z7@p{0q%{#Lg|!yfDCe3~Bf$cw`n%rw1Z_qx%s9=S3Gw1| z0Y2Qil&mTqedp8I-;ffhH$fKtQw8(84hV1Y%P4e93^9g{4%e-yp3-Q?HU0eJFJY1p z;KMX-GV|0L+~?Yj-v6Ty5@p_DqY?;+VGsxi_CI~bHipj5rp^pjhE65jrViSGG`fq7 z%>M^TGqKD}lL%rnGqae?4dyLo)-C2Sti!2HuGd|6xs~Z{_1iH9U+t=-Dz)c}l^t(M zRbUVlR5&;|j2O5yxNWC@uaLxGSnIfCSXf(o?lZX>0qkvZFKu$G7(he;BikD@Nh6>Z zm;19M$7cW_AntBSEfD&d-#(UX9-!?VQZ_gh_EKhn0!_88_JO#S6&_pN36cVa;T3-c zYILECCG<(x$%W`mN6#U;wFaa&!*H~nh@7Mt(t;e~2=W8Ml`6<8Yd=gc6NJ!uFW~7T z8{Xrt_&l4;<=cQ(as5MGWYoU|9byRhCEbwEm9PU^=pHmJ@OP`#xEa0UW^ zbcJ*Ot~fgVGGYX)TR+X~G43Y|Y8YBx^OmwGM0~|8^cCzTbB>s@v!Xq9U%%zQpA)Wg zT8+dEc&af1PYGw{@Tq2nXPmhP(~<7nXp-hMAc^0PW_^fz1WgZIpoGH5Im#d{zH5XW z&Lqu2CY&Neh`%{33M>|orC#Qqv$0n6QCI zN_z*TVVF!sg`}{EP{roVRnz}{5#txh9!W>R{M8??ajCU=xqcRir9X-XG2t#qjDcPn zvS*bZ>(j*6*9JGkJ?jvIjsmulis4xCD}ww5@pD+u+6;yEoa&2OPr&h#OqbW;OVgSW zKZXh-wy}rKz++-tH%cEvA|yunFBycXu3j{1cIHe3Mp7FQQD{_ zMA~8d3^W^Qd>G2q4%Cyco5cN{BM4|?QB`*-0AKxB+pMh8*6tpX#)YW(!hupN`h!Oi zAJNumeOZ77x(qU~%Dmf@hvZa0LrA~bo7ZJSqFrgd&&YAIR|E8#h7UnV#vlY(m{lmQ-AJQ3DMLgq6?k%HtmRY#d!V_e< zmDxL1(cg8;TnvCgU0TQCT3?Vdr02QQn;=SmZPQ{3OtDexqnLo5Ky~vKhVaX6XNr0i z?OH8F(O{j|sAvr4`#Yo%Jp5mMk*9No2mbYf?8zseySVgTAY_TUdkO`WhbFC zu#0x;1FHT~5zGNltp|C7XoQIAzlh+?mybnYxs(MuSC%%x_XIqWm$=IfFH78vGt0KVa$h`}9a(5}?WSd&}S6Q;zI8=-V^5fQ3 z1_B6y!4KU>srZPCYBp5UI>>b*RQm^;fq3oP9H)`?7QK4EGsArDs?S>$WE4BpB;iS+ z4pjJk*#nTZEszRRK?(viFvyhYAB^E#ZFr4>@ELidxT%E>`SY)Am`$`m?>Q#` z@XVe~cKyqn+8O=GoF`xs2hXg36+bunz*q52J{_c;u^H3%K>xZ5ue6)Z*2+0j~g7r8+W_y@yvPY z!rYdeok~CVcf%^$5(ZGgR<^wYR<%t{>9xi(>)NBV{cdO*7zvM{^^QGU7JGcdCb%skeMDL0Kh&gAQ;NROrmQCL-Z!2 zT=e>(9UBt4+JOEFIPSo|l9xVr`0=F(n2tC~Xfh6;X&tbVSNkXsK>M4Y2OjH7!wx0# zcz?RbNDl-AE_}Zl>nyDq_gN!i9!iJ_w&04V~_kB%?NwlY1S2)kc8CAqJ7Q9ZC_A&(&Igvg^Og(PYFtU z>KDw25w|htfhU_q2&}vF7R^q}D`a&!fu2-H$pskkSAafKoJ4Z#YM&vki=b*4 z=I`5K!iyBJW0|j+gsVP}OiJtLk?TJruyJid*4>uC`!R)H31|VH-TD+>&>L}k-fF}1 zwS?xD7|9%|&DST?ey4s$?AP^>NSaF&X@jMJ%Vy8E0(wYf8fNgI678x2m9ZCCkm zV!}&`E7bGYlg#vt*udA`V7tsKx~2_&aztV4N(BFIpXhc90WWNZ*hxe&bMKfl*M==W-SV(Y=VGliSf@IsS4JK7EEfT3gkB zIB;6cl-Tw}yOPo-27!6O6VQk4gp<_gQeFzkNcfxv*Q2mke0WUX@qOc2mGJwc;wlP< z3!gXRlX2C=bXv_gKT4a^yyy7rQt-9*XmF)fhN>d7#9@a6%~Bfb9oxmY;o!WSNuQ1O z<>YbqfBo{yYck%7vVgX5Q^6hseYTVW+C#v`-GwrGrOJm3ew1giEYCmkm5Ti}^SCT^b%JBMoS0U|-0EfTdZs(? zY8KbdEYVQ|V%_=zUhX^nM?Bd?JAO_uK@(gP)I_O|w6BQ0cnMJat384KFIsydoWzgr zuN5+o-`uZX{gfJNa#*mgui%Uspp%#1qxk*@LSeW^(2^ai5O`0CUfYjjmLf^~*gz?b z6f||gG`Bwg;=nVTfhHSUTLHRz=lElY_>q(cj1#VbeEPYCJT|go;1OSn+qoEZdxWtG zDgMau^h&g_)!C&W&|S;K?z4iKni9 zXR}7a{G`iFksD!~M-Gpn4#|V_l%mg7P+r?6`o?_Wk1(we?Q(bZa_7a4)N^xHQga1P zd`0cnq|eAxNprm2L0iCfWe~#3fUam7Cw0+zXJC?n47>~7Fm!=f4+kfHMwtr>JcTu{ zucOL$%Tp+p5Zu3wa+>ebmudR@6i976t!Qo&Df?yY*@``aRZ;ruETDqt0Gfg>s7SHs z73&PrAnQ@#l0|6r<0v)G#Ddtb!RK0N4%y!2pS}@XX}QNzrYy~QIxBK?Y{cQBUH}mf z0KK5hqn1`WP?2jyF4M{*r5zNPKv1Ksg5mAeo%<|6%t?trsmSx+ENx05xvmmL z%w)br4^o0KOsBbto|Vlg7~FLedSk@BPZNf?VQ zW=vyt17ES^QFCX|1p_)EGc7aP=TC^V(q~|Pi@MgcT3XiL;}s!{Zv+*aY{O@$hF&4* zGK#ms+z_EM71L~<-nrhw=Jf@OA}#TUaHng6Uq(@BobSNxh9* zbel!O`(`tN_C3c)`-_&uVoJ>;W9b|P&RL=lw zfbTrj?~(WNwOJiwQM6Ak`l z3$IWfJ2a3njX*2tm{}^`sg6oxwPD{$7Z2W~oA2Bw33$)z6X9zSV33HUT`!p zdB4rsNgc%f=J(^?6mA(+|VmWm&G9LQOkOGNX0C(1Wh zEl=XdO4D2CS&D_2#jK1MzEkIp*>zDWr2kHI9tu35&QV)H6}@2CfA?S~5~2tmU`NUG zHAhH`Wwjr7P-hikPA-IqIj4CKG>}DbV@9`)7yVU~A-PB954gSCqu;31T?;$Wzw#0K z{cSMG`Hd{U&C;I4vZ^2?vbiFBLgWNhg)g+cXgNA6yzA0mKsRFUBxBy@3B^#N->S=5 zp1z&<0$iksL_7M(ljKM*>zEId8;vN3rFL*_#|waO$KRslN&G{4eR~z7T*{_4OT>t+ za?v)#R-c92M#FIE1hYkdL8HP`CUL7-lH!qbUHqQ8g~bJ7>0FYM&w7V@n3brLxZ{lF z5;l}XZceSVgg1+d!%`-b+?blBG!jtP9$;m~))%rUlgaE=YArF-iCJx&-Y%4o$9sff2?DnhmV&_lUMZOE?&WZKuOfpk}ZOPlj)q4QaZ4e6M%~+4xk7 zR_uk=WoWB6{W&V4og)}X2K=4-XU{a$-MyMKOp5wGP{;AdrM9g12iXWRLq+3T<>9Z? zE`lncH*z@q+UDShYd_$&Dkk&N96<+oOEdC!9jk(f!-m+zLj6f@hb`(O=3Q9qjEtcL z!G^fjn(@RR?CE+e{~jWI1S&#QvCNC zbMmrnlr+bR26L>>>a$aO7J}{10kc0^EUcrZ6!Zcyv>IQEZeK-w=vYJOWTdkk3E5pl z^0kt-q!x|v^@M}|1A|N*BEKTv3}t8eJY1t<$RF$k#33GfN4!!E8X$WM+iv|Pg~QRb zu70YIn&@APQMI|`otp;_>#guV4YR!IVIJV_HD8aXtz)}lrFk}e@GkqDbKqA5nHq}Z z)FR{L20z&H(l_?1ellyAkK?4;UNy-Xg&a=agrmXTJZSZU-Cr>7(Hq;&GgerUt~9ss zsOx&pW^`==F!eo^Xv~}LS*>0`{jUMJ-zcHh*PI3DxX^IDxR(LNMitygWoT>4#ghVG zRl6!=0-*bh_ZuQ**2`(in}+m28b~7}B#uvy;ppcKbKJD35J~D+oTZxA=`Sc;Ag%E)PMM`4kuen~lhCEs&y2jIQw<6vnf*~AIg%%!!1@m!ok)Ky(~X)VGsHC~qhXe>H`M8X z-ss%KCE|qpYIU8RfDn=OPoxp)%*w`%`X5#U9`VOl#g|}9q4}cO}mLpyjNytSfb?-@bp{Ppi1ap|3aAgu+ppWIZV!TkxM3sKR!J+EfJ#;@)OCgl&w`fd z7J8BfLEgZt8b5VQ-1y37xZpy60?)+z+o_YCC1YW9Y!fo%!exR9WhpjkztAOA>3+J4 z?jAMBVzUhFiFg7gtagC+lw*i{BxI7^SgF>dU1kVFaIY$cYX%BFENUs_JnXQOBHwXr zi0^4x=$^W;gE7S!;%5iTN7T|<$~TvgKV6Z5I7gqoT<3$#vYTLm*sY=lGiEu1_6 zr$?E2)R#vi5CY!lmdRv5VC6I@IJ9H{6X-&1U*C=6B zLvdxD?h5b(Ty)cG?m^b*1_(gz5p4yjJ;4>tjKxiYQ5=Hl-kZ+#DS7X-vbYtyLX4-D zhSEP^_Fj$3?VwS7n|)-q^oEUVn89Ni{x zVS*EE`+^=PmvLe89vp}T2~#%7jI$@*Mko(KFt0_I$LwV_M4N+ zBHaN&5Mf5Y2FH?I1R&v84yZip!yM}OO*)ox&C z{x~Y_{m2tX6Geu#QGJ#VFIr5Gea7!Qi*Wf+w#iawV%o7{Tp-ia{ z{5`doW52V#By&=>b{-olafc7@NaJR?HxK_HjIWka@;Co$1DK*)+o2OR#qgZssD?N* zV987CiI_lB+a%n}IRgDi+$USzhWAP#D?LBH^|nB!Rqo=&?k2PME?f- zsJn^Rn>No$xxmg621lizA@}D{os1&y>UE*bFIi? zlF1PioZ4E=VstE8K4#WOAt7kw@tK~sFYBjRCeNbq*= zat1-Pr8+cPm?`s)A^lsRVwJn;!LD0$nc`QWBEzmqb6Ys@xc(+mk#<8!ef14hk_mo_ z_nv-}HP7^X*Hyo+5i>ZpD#>zHhvxv+iFT$7Fn?DVXFT%ztNq?)5~pw&B_tE^m8h;# z5E0?P)?V@Y$zkWaiz!|2f9n_vK@1E?>IQu(b)ZM}5y{YxZH(vSk_>^C25{2xH zV=@vl$yky#)YLb?0V{}3NvEJ6wDmBPDCz8V(Dw@b>a2%x~B;`DLqluK~|$L&G1-k~3HQ}tZ6a6#|gfEeXzRbc@w>BrJl2N)cqaW>MH9tER$`J-IYc^A7C`guJHVci(f zqLpo&A8-!S93!f?7{U$n+T%4M6ml@o&tClsx&89(d_B_k^BVipY-(3|OOqK3*=i`H z4*D1B2Qa}BOAi6#IFC(fBNb(u#&$z#I%dvMBydz3hwE<$WJB5c_DecIw%_ymW2tNW z%!*dFLQ@!M!@fCZ8E^l!lE4O~3!Ew_O{VI7>o^LThPAv?KEOAyc40@ zArwprfx$OirfV4lcvz#xBL&n*Ic{e!xB#sG=C4pUQ@R7lI5NpfUxI{hjPfZ)@(g1$oq=)thCzJI~|U8U6#rCsIs$|bO@ZV>DM1; zJc0KLvIlhhwWXv?Jf(;+CUYz`Ml)ZTI$VQ#5xxVk?Y8_e74OH5+l0LT+ulOIy>&yE zA$1y+U{Z>sc6_VK*XstnWN0%G+HV2AEp>m~DR*1MejsT8UnCzbiNr{5H_-r$QDR*6 zCBB*+fBja?#C9AUS{{MoPSIZlj&rL&uGz2I%mNAemIyBB@AYx;wJZYKabHt1v9lXW z-!1fKdsGvgqE9KOw)cZ}vaw;jW!2IBpgz^rPVCl4s!4z~j#$qmAyEWFHt)Hy6iN3T z%H{xB9gK+)`Yu68W1VXLkjn|^`iq~-PrFxKaE*bS$}Y|>k<`wq14Yv}&}i6A@I&gl zX+n?th>Uh>-4Kbz3nS-M3;=od-{~!yd^f34u5=t`Xt` z=tK8`zm?A>IMW*&QuOu1CR5CoGf{|#OFYII94{^tL7Drt&?&jA@u&}>YeqbWS2@!Ulna$l1HV=J&Np!b_CwZ zEFl9PW?gW|ER?jXCU^C-(gjwy;M+yCBz>0J37CnpGt94uX}84GyQcaj+(l0p+)YW} zx~62_qK<*eks0!i!dP_Ti`72Zi0U-e$EJ78^=0@ruV9@-> zt$G9Q@jf&Bb>0|2@cKx-uDr}Lpb26O(SM6P)AyT=gjO5c`u&0F$l+jMaP^yh^<3}V ztv00xDh$b?fbCNo_+Qm;mkyZ&IRvNGN_;FBiTtOOXI~PAMfF9EM{ep^J_$Tg z7vB#CRxAz@G;pLKKM)<5cLM{yuxyL)XLq z?B_maTU*~WRo=R7Gu?ZfSw5{m=Ea3+p5buLq5PHq>PWROoB}nJBnRana*ee)Di%Q3 z>Xw~;LkKd+UE7;mfQ&Y%{{yRx9thaxDz^wwwylcn2cadU~*v z>8~1E#L}+5HT3vO=lK`g_BRNqcH>DWR90(I?4z*@j*f2YFLJzKG;KPhJ(O)ExT`ltlm5)D=RCA^U z_k-j=++pbCJo4uAm2|*Oiixw423tx$t6~w8sulfK4?L}0Zp^&?uZ4KtQ?W&=g~U(` ziDG)53M3R3c`+1$m!nXFo|z#x*u6^HNIsSGbQCR#g;q#DJn#wyW>NJRNT)U$$1(2Y zl@E8R#XY zyro6xJ$qLlMe~T#S}?FQJ^Pbzx8Le}@xfy|rkEvjC7a&T?A60@V(^!3xkNm{R%oj1W$bN-; zVPk`3+d(>-_;G#=Z9j87P7VSMOOvqb(Y>Rk^C0#_cd8DH=g>Ew+HV1`sj+GQrNVj~ zoPcFYvaWq$?w--1qrBHZBY{@U!&%&8KZ>aF$VXbZDvy4**d=~R#Fi5Ilv4%xpX?H2 zoi0hA?k5`Lbq^7$;V4&K=X^^-eRWiPy?l}J-=Lsc2zlJ_{3Uy ze^e5{uGjWTlE_pBc<8Cyteeo~5TD_^o|VpE?jV4GwKcFWkcHwwQTUCtm;Xf_B7oIs zc8bYVH1WTup24>5YeK2YWmN6Uf^L)4?q}%u+TQN>sDi6Tt3;-uu+qqEs#$(~qKxxM zhRdHYjJepkk=Vsfz7+4#|Iy8-_?5Da;*i*2_veFV2f@PH^Cyh#=H%gNkkHZs=EV!B z@}E=MO*9|9oZ~GW1)9c4B1@s%Ays$0#ohS`y6^J!=M+Mvy8%{9Wca8eLd< z)PbvbvlMUgLt#sQXI73kUCB?|$X4!rBQB>qbU!ZPHV{_n<1}^j^&4=1@H%!j3_^Jd zqLf;)j@&vw{Av4`HyHC7H$9r{v@jmNU^rtu+s~T%7t6waM$oMK=kTKOI?~zEIhu0i zWay00@E?Az4WONQ&q}UE{O~*=!N?Kb9Tm}w}wTHXL5`wK5??PTkCrhHGq3Cpf zXLrNGnhmh|1e>Fi*GcHN+DdwBIh`urCy#L4&;uPM{Kc~Km#t`0y<#1-jXo*}2r1JRRQRDADs&pvjj-A{0>%yle1pmIoK}okG=QF}07aa? zGZ9^LNd-(Z1AHf4Qc%8uts7?A^*w^?J4|bkQt*R&FNHmh#Tx@gwuUQB3nhLJtT%KR zjzp{vDBLPTEvctH2DhF)Dp--lGUyPsXa7Bon<~G^lcZs$b0~sa4`@x9$&0whuJw-& zc3%X=XWy(ituxn$HM7wcWMG8;Zan82`s!UgEnHf2o`y$bYq>nk6!O%9K$`1U5T!Am z#)=W?p?UUG%}*H7IALWGv?}KjdLI@-Vm5UV?o}F2z&Wm8l_&!1G(nbU7-(@$QLIpG zRTH2UZmMcVt#m@%(Ti6yDLo>;sN&h*hHFgmPc2mL4;E$US=xS|TGFMCF94SGQ;3VD zJPYOCbR^X>K{f{)9lNCtj6xTI-2E+*r`R^kGXSf+Cxc_Qc zGJK=fFvWQUXW}+N!b~E4ny_+Dqu42JEIX3s$7D6^0UjeG`rJuPrGT(VQjkC+lw>?S zj!+^ZRqBsdgP}RT`t@XuS8patI=Y3{d%;CW_Q7%^kITX!+c?Pk)J0|S?5l2qB)>r>eaN=B#j~MxaN6|h*-@=D5M-y=RU@DtJzyg&pw;2aOj8)7=xvM?;o-m zG3u1F$qkO+abZ^3%7j6RT+bHniM)>@!Itu9nt z{XW31`Fs%vr2fV$a|aq;QIXV(mwQER4h{jyz${TY@wKae|A# zWW@D1muFsrE6f|XMHn?f4qFm{(lBSUC#&6UY2(JSU2j);DuLXvjRG&(>6ZESFN_Z% zbwiu6Gp&EkcU^w_^OfnbJA^exiSx5-!cY2aYoEbSgIx#GPj;4^Amb_e+tdt#HgYC~~;QP#x@(^)Km>>#nID7>D;c#;SzII15Y#S8pr) zMT-cI%S08xpBC-<>s_3dz+(Zu$}P|cO$r&xi@M228lnO}TF+0)lIBn>97Wg)Hw!Yz zFWGk25Hsr2gBrD0&czigQCn(F=eAUol{Bfv9`{+I^b^y1qfc#2q%9QtP6ZA`!x4$V z)(A^~z)HwZD{;khDGtDi)Y7gtkV&B(!PU|}BiRNk$ud{Ss}x|-1XgW4OYsYeWYzWX z^>l^Tmdd>vexSp)rH~onx~TfxsKYM>GNP`zWlYt(V^rlxt#3=q-JmJ0%htiksk5z8 zWpU(Rn6!O6ni+kTbq?>Pb^%QmjGaK1@S~ z6Xq8?#^b=?l6&R3=)Qu#l?r5y84Nzk1oo}XH$3{u$Mu_4od-78&g8U{Rt-J7m7_Z| zA9T?o7s&_Bv}Zd^kG{H=uXm)xT6zTzU6KK&say-Yb7Rc zqQgJxzMZhElix5X6(&w2LFXU{Pv+Gw!DM51bNcGmnm1K}OMh4>+gF>uFQYO2BImfw z;zLC9X3HGM=D9FXA&4%a`_m^R z{HXWpG{6R7ZocgEZJP5|v~~WAm?z3aEW=5=PXYyd{TkT7PNBjG-{An>5oi_^mC5&~ zT9paw*q(kY)na@&osY9(32}LlW2S&4TmASi$Fvuc?wJd)q`fo)E{*kO1${SBU8FCSqi_@_&>aJ}akSI! zT+NqZ&y+Jom0Uu=&Xn3<*aiP2{%loB;&^&0i_AN(hyxut>LzA$Yag^~j}hH(mtVqM z-uPR_m+}?R19Z7t-fk;!@Gl+_rU4ty`V9k>u{d@1;_+xY0a*6i48F%mdvW9dJ=2-I zQ0tTJ5Pv7viLVseu<(z$bVgRXvJu5ta|dLk+}3aon)kI<@vV9`;|p-benk&lTM}k3 z_2c_hpI;7yJ~h{5#72PMY%wPEKh|$K`cNl2`*Gt#DYC6$jB4-m)oMN0RM$55TaAPE~PH#n&$@yYB;<~EnjZEwd zyT8&0QXl5>s(@Ks%?2_G{@a0+HLH48W4x2iN+%Ayq0aOvNZVL#a}ru;<-k7`qjz2e)jr%jgYedGqjIoDP~CKMlYRFafjWXr0jAKFKs+dI zs6qNs(9Q*+Bh=XCM3zsg3w_$qkxemS%i3>DIyrGrN2`LnteN*(1uG*3 zU)(|`Z19ADLA$XxnS)>8A&!XHLPF=R=>^=f)urfh8^c4duv){gM&Ad zU*)p@UA~ugGu`)Lsd6paLt?X}wq!}7=u(FXnp()iX_sY0x!(@XPZ$lPcxzn$NpR7R zAzJ3MnKrC6wC6IJ2-$^O3hv`fqjOPfR_SA`H#czcA{oouf*KC$h}y+H9Ib_Ax;UCN zn3BjISd?`vsfR69rcoJC^Gs6RmwVox_FxL9x96X_xKad){K7#glRG@ACEBlPPkm7o z#pjVY)t1;B3KCcV4htMxd?7p4gBpvQg}%1j!(pgJNE6L?7qWZ0CARQ5ACkx%iVOJjI0cN_Dr3 z;BJdi9qpfwNSa;3H{0xH3uRNT`_Co&C%%aN;U!|3SV5+>d25N#P zzGU!t9PHTHjK-vRJw#m{%&;lQn=a6H&_&krBUGp)lnE<+9Cp>`368j8Q5QkX4x(;g z!j>w|09c(M&I>iE+LS-am@e|Wv%7-i$k(WI456!t$~C=0&$(#2Mu%M*ZIe_y3=dxM zNWfqma!@^k4N@bUZdTN*lW`E&VyZZF16)f1WqRoLq4XaGF z3(hoSERX)Xv~A8IHH$6+jOwb1^|efE6-OfJ2ns^YqqnjWFj*{QN^^EK)$-MjH|J)D zuzY89wS6azxP3gE=N$4b8MayB)#dE8r*yvr!{lxzl7Q}%vM@a|K5 z5TIvlT2-Y@#j&f=3r!}u`_xJ;Ck0kZqP8Ec5>48`WzBJ*iIF!*{61F~LQ)EIS872c zQJxuNh*m8kcu&!U6Nnl$B01%f@f4Vzm(RnnGNIyAM+kC$Dxc9$R!vN3uo#a*H-5z? zhOdA2xa~41g7%O~v=y!S%72b-A^f?I)yx{ebpj@Z1LpY^k#E9=6JReS!(>g8roSA& zzuN~xr*YT4HqK;U*UG)v57Fys4EtTD{%&9l{Mv#d|IG}Gz$k)|XY^geouDp~x^1ah zy>K`x&1imjS|Yn1G3DE#FAm!1nuQ6I6d$0A$0+10A?JrZeY>(&a*zOm8$=4e*YVdr45=9ybI-i3-{X)c_EZ&kX^V?y2DijofgoasZJG!_`&or z@Nm-Ox-`q2_7g&QM4Lh;->{9lxbrgl>@gTJvup|xgXa)++p0@~9wshII=6(5T)ZNO ze+-jZ& zkwb=pbBdrV83%AHDsLZnLW_d|F;pTyBMlt)c=Qx5gTJ8HKZIrs6V${%=@6Cndm(yt zljl47IPXng%Vs0IRkjnMf^fGNfE?DIK-Hmq&rZM03=SZR(l?=syDdBF5Bb{sXf$9(XN+v>maz82Sc3M#DZ@GTv0P1W#KJ8Y ze|>3gWJU7xloH?b0(2J+z0V`nAC z#e057;4jYW0kmrK)4i2FCL}40#iyUbFaI^*ogg}`O6yV_`0QVaF?>VEYZEW{HkY=K z#JknBvredM8A4!&VW)KX9wN1Bco3O?`HA}P%O`~VYA@Dl({AwU%Y4fuMJc6czdHZ2 ziYb%X7mA+2{a|%d*nX01Gu*%*$Sm-$W@DyJ(6P=`S4F}bfwTgSXO{zk^M98xC7Z{? zIF|pVDC;S#%3|HZv3n7%XAt~1;5khA+Sy0wZObY=;bV^)SEO^Q=Oc8;spfF2!i8N9 zBS_K%BuqzJST9Wvmt;7%7pq|#0#eabi|Lq(JC39J_?|TSF=~f5zR=d_hj(|3zxzf? zc-OcwMP)4qKuUj|;T4&>WfnD!W4_TJ*H~@#pqA2j(V78r4zZ&ANCfJ z;s;t+XwmK#tk>B`JMoV;(eC#_$PVe&^bc)YE-PmiFsE<6?7ft-NRA)Lf~C z_9rzfotma~Z9$uDWD*5p^^4r<{eW3yLj7Os_-fZL+5e-_6ZneA_w z!#KxuLwx+Nz?H5t0>*rY-GO?q)&qyFtC&V11&((zoVMrIj9D3 zHVxD9t`2$G&JiQf(2N(?zR|B7vQf9}R-)PP`v=z?)`0Qlz||}g)lggh)ufr)nor0( zQTFuJCTC08fhne(zg~5R4C#eHs$7VH0C%&?rH)Iz@vGMI;;2I(mC$VeN=A|1*z~v* z)m$JE1voK$H%cX}?|_R`W@%NDP`X4N-?i~htwj+Y^{hjBH+5>8n@H?|bx0mg#4%>55@?9QuaBcy3+$}oNCQ5;4X9DMm-xv5y&-`nZsjj1W=&8S<8FRVc|5Emz{W_+ zpL`;#>@-E*uqbW2yk$`0o~(ujP&zsM(Hg#66kKstNr(^}Dct3`>l!o;9wTx&%XwM% zQtLh|bce~pOEm8Oit{$h*v+Px-2)>gpF zyH9ZBnbRa#jd7gE0mV!WdiD|ZN6jFy9WF(xd1H%2n>FrNct-M=E*X6f2Ny$e!@u=u zmJ~EAazn+fesHxUNV*~`WSwpNv}H)WP8T#9e2y0l&ygXFgh>cMa_S8Dl3;&z?RIX| zCUs*1iLlDphx zdpL|8i@ffNIlJEhn>Uc4n=^u*JZDhO+ooG5GN9to@evEv$|m4FqbU`j#(IOmJw|?M zz9#EVb|SAKbsH)imp8@&wO(hZjW~w5(?2hf+v2EKnJDuc? zZQJSCwr!goTb*Qj?|Jro$MeiF`~7C-$BpY)>({mFs;YBUooBgqN%d7jK4$3%%tD_m zk=l|zRwh`DbK0(PL^r+MHWaOyv6<6Erf86FxEPP!9pEo^=D*!XJR>eHM#Wx2>M?UL z{&E=Z8X`?CYi*Y^UEHSZ9>hcGh~`)g`I5G@R)&|?Yv(82_MwD*#kk)Zwj=BqS23sl z4Zho)=gi$^I;F{9M~fU&zcfdk_6zi`k@bY-bPH@rLvPOn@SCO*NF4{ zj_731m!R2+j76d_J3;upP_!T;zFMP*bVefBo?$=arnsf{rzehpUhg#C^~~U+`q=ZLD49~8Yl41p(TxI5 zwHjS?;{@IQPfEOj!NtcaBgk-iOV)Sj@lNOv@XE3wU+gb6#kl4UBt7X?K5%B= z*%m#Lvs8>_*}m=i{8>E1@)8)xviW_fhSE@u(jQ(2T~gV1Pt%V2bIEr|>d$?cKJx;g z>1t_5u>o%2S+9>D`uJ@3pEs(640!u}J$HdndK>RVO9weW%3CAYULKRQBy*`tlP+Y5 zm?=wejjqf9wcDn6#{MH8hy+P{D9_G4tJnU24`j>X-1b2*ARwyf|D}iXFJ8NllBt=E zsj=48zGT)&Xs{+Ac+4R0j2ogw*GUBfP zxRFQ_v#~tsDtzlIJWWoXaY>q2u(Z1mb=z_=kV&`9{Djijn0JI`kHha^c$!m6NGIIf_*LztvxT;Jx zSr@dq(PZF_H@SVO5L1h3%3fm8v^$=HX6J8ld^=hy=|BcU)~HS+ShASoO=1;CEiZDj zD2v>RtB)6JBHeOUAYyl!AHhTm z2w}+tN>I_6LOWo((uOPYnqS~z%&j+5~9P<;Lg|fc9~RQ@iKBB zo6t(jG%I2T)9w?mfEZUb&D7u-3o7-%NtPa*1fw3$z!R7Y6G+T@;_P^3rk=7ov5PVsWE2D_bk`5LCxv-9UT z4{|W{fgxsM&-|QK5jIdqR5^xdMv z%c+?cx^*RzvhXkzp6|j=?IF?ApJNOb(Z47~wHX-VyAr$!rbl}?@p==Qxd9Z{bpP^I zlqz}>HXOHQI{QP3Vk=0G=(4vW;0>Ps3pQEb*sXW>gtb;N0{(7`VHn zt$N|Q+q^XY3L4##!%sCAWTsWL{X#i4r4VL#syhoM4%84;(qS!_>rV0cdmDDozL^V* zfRM}7!9k~bjG0?x!W1E*pSiAs6p+Ja_eZN^cF@sp*J`}I{f1tR%Z&FZiw+wMXNr`+ zuk-T)9&sY3?buf{}g4WFhoomC(L)A%9#LlO)?8`XHy za7Lp}i&c-z!YKzXRi-3@CK54l<D(YnwUP{7;0h* z`H1Xg^)t&Q?)@AfoGI>WF%$AG-3yE@EM_##J z)k(P_^2yM9n^Wd z>b;L6r3>quo7Il}2pL91{4&g$I^BG;%xVOkTQJ+0ai#pRwi8Z#^mBiDO@p%aztPE$ z9eDB)L!uvS!bwIKA?j|qy?ZFSjeYf#z&NYRvl*?METzHF`<+_Jy+3RkqKal?+t{gm zUFXbnIa`@&HBx3|ZK6WI`yg&luq1yy<`cPJ-gB#cspJr4-hDxiCq9C^2q?FuK4_Uv z*20_!1-GHnMHq?5xt-d!rns-qB4+UWRztGE;~kNeNdE|hlx{&K#w%QA2=@_=A+bU8 z9ZnLuXh~mQUz5%+-f}Q`MxZ3Y%#MSC@Zs$$y=^YIK$>MErV8>AI>burl52N$vRX=R zJqHrpio0rUvyC_zpGlQAW!GMxE@|AN!x)=eEnx%hV7O2{dlCP0q}ekyM(pFL89&E= zZ=w_bWirg0BxRQK%gge2(Z>cI!rT$wXj+nddoWpmuWR*J4MBM5qVJ0@V3#omtp#5m zEQ|#1jLP`ney1t1vJI-|#h&;yC_c2s{{lykFIUGx5*KCC-*3T#K)ye<5n^p11JMrXR(LBg4)E0THKGcWZ~ls z`%JtcC1zZY2!0rupEBWkJ5wj$Rq?!$;89#B=LJZzioyi*` z4NV>)!zE?C+ai_+C7K}GofCiiCi|{?K)qm*a0I9dqwpx!z`YA?T!?JxEApS8nZNZO zp1g!ya{#x^=_vt1*?%1ai4gKVQWKKhxg)U$)RA9-+#-P1cCLiWHPw;0svB*gm++$6 zLh7qXsLRADIfS0a?ik;PJs~caK={U!t)RCW77#qw+1la;o03_BPD{dcDgM~WX}USo z2yARQ!Lu-mCHlsE=BN*oOKu15IZouK0>oL-_jvm-z5Zuu4^;z;onOu;HlAur z^T`(DM`tig$)B37yc~J}M*ZY0eTUYf@A^=B&UMD)zkx9)s#D1j(ud8k*%_fUEzIOE z;xef}&X@@d#gu>R>NoW@{Ko8d?$z8?GDV4Kvjp&?Qk^qdY*5w zQY~-m)!k%%qe-UNqug&|gm4$(C17wak*9}u9W7JMd>)0g<0K)_$7}-Zk$L9(m->5r z{m=x71Vw;doM@9cOZVAAh66+3lL!9%I^xhmw&C1HTHizL(_B7+v+c-KQaoKHiR2hHS()P;)R%AsNYa(;7F+%KTVTPt1wy}v@ zp*wpd<0Dh1ry3RH1wvVgpyD-te}I_+LV&5Ai)yQOvB#C4E%0&Zoz&4ffwCN-euIZ> zZl*$Uh6QprUTFo581F7Ss@6P)Fz_?c?$@OyFi9lgp7-cLKj$OoH$hKt=Sm<}--(3- z1JDsBg#1YfJByQ~H3dftP3e`YEl^JTA$M!_* ztEjP8c>TBq>KWY zpmNR%d#A#)4=hD4-zU#tP8e2!;nFdHNn;geuXY!!VA>p6YEMnxg9PU8JFP$55)KuK z`I+N9+E(a8ri>ylHxSsjhfxx={CE;h?N}*p6w6)E&|P+kn#3K15pLY3muwO_EpqEj!DaK7^iF8 zc!bEYp`bO{uD*kws61-lE!_pQ$$`p3i^Y~?=a)2lv(E9Lh(VemL(!G5(D}Afc|oRl zu}@d9C;_47f-K?I-`G9StTz&?rBgk3lj*~)Y77=_R7ckO9IcFky9~W|BScK5!9{r3 zsUO#+i_yz!+a zeS|&YNY{Y)L%)Gq3nzkMd*V2<4??J;_~lE3*k{)cLf^n-*N#;$tW_PF?(j!%?5uI= zQ6A0|z2O%fD{w3vyS$K89H4$&c1Hf&3U_$(FMnjLtM#5Yda9}=f9d%qX+qoY%{%;U zutM5{wR<$tGF5#7X=5b?hbKn2-^vHyuB(^)yZO?(ViW*V>M|cc*c(WK7)IaO!=noc zSo6sU24clS%96?h^SWD_VAEryUarcW8YA?Zf;+u1@3f z>Mh@K-#+?sf6Q{H*{gQ=M1i@67j96G*j_5H+mRkzVqT>fz+wJ!Y4U7qwDmMuCIQ9# zll$a%%4w|^0tjvCT=r*t6B9`qE}yO*!a-qriWbHDY)d^w=U$p-?5JfhFrUV-1JUj%S`|hG_9rS2OIQHq+`=w4(KC!+E;1-TUdv|Kl0H zhjPVu#pA)seD}D)@QNta^)juAt=V2(#Ea=7v~8zKV!GIRBa6kV&*Sv<&CctFtGeBY z-IoeKWhbbE$=};=*X%V#h!KiW1~2(?QRb}3Qk8aNzs-r8iY;8cbXme=@v^4i%T^f5k+lx@!x4C~J{c3YJ79Tdh{Z3)Be;5-!Fr*>5E^ z6r!sQD|bNm5xfuSxQzIDj9}g<-2NRoQF@oTs`MGz5;#vFtHLPzK=e#zD|BY3-%LJl zE47PTg|th<3Z<&nX7=XoilTe2b#64>V3ZLAXLd+f)hKP@^#kl!bwbD-I}tU!Ni_$En`U!(-SB-<24OJt9)*(84M6r}6~#k-6Uaap$lc-W z1ax!w%`2Hkso(4wbC(`7$vPK~_>4|B&J%BMZ@2^F^;{IrP}9dqcDG$?o35|c@MH7S z*;rT%(*2Nc)pi-V4GiN+<-k#3WAq62b2^K+3(t{92HcoD)swn(lBo{IOb1qc*Jzp^AP%mZZjp6f!J{M);hms zrDx%4gzWkNCVg;%sUfFlSm9?t40j%f0}HNKYk)jY9{e+1kRA*q0!LnbR*UF?(Qfj! zr;E@$;>KxEmC;Z0i(t7EZgwM61~Ip{-Z*dg^*nFh6??(aWXtAgP^4Nku%Qw9edxM# z`Yx*%={$dCG70u;7m7E`Z9*Xr2QAc3u)iPsPn(U!`#&pEP!JH#|3Q{VQbknx-~SX{ zO`SadE>1=t-75vogdIewW^1eJ10v6*YJo>bQbZU5sfhg3aB;SOH;Y7&9g1rb`vnT z>npruR;Vs}4wp%1DLWRQQSoLo+Gb;V*rV{2{nAV zuHk)CWU*Rovvi!UTs^h2m9}tBC#CDv&6Bli-;l*??^|)lQ&rDcKT4*hXawJFfuKCN zUzjgj#koiT8v+3`^-p=U7kApP@n~=C;}jf)Mk-r-^o=II*SzL^GYP`U#&~=Noz~dc z8l_u}vZ|WFuN0jiL!ev=l(YAc9!&=undG|^zwX1phl$KqCA+du<~ukhaw&qRPS zV^65AnT=3<`|3KK_fj|7#3#@?qSw%JW>y{FzG3C$ga%-uY7woNN^3fuWI@m|B+IE} z3_=|Z4_|NDXA!fAbG>ZovsdoYEuc{u_{|*-K_^fL3j^0Umjuw?LqJcyg;}<2qe?dqyoT^6lw4ztP-63N_j2+{>Zcf{Z=tyS z7E%vgN+b!OXp|`6e;7oPIJ*g(!J&|kLkM@_q#AlR^5xLfRsn)?3t2 zy)4bOs+YA%6S*ipS$6NJt)fT&VC)}Ig`o;%7NK)Q7Qs;}o5MLTH`nx8M;~-K7wUy84*DU+cMr~%2u|1Vaj+ys6TIa5eVleBDsK}E zL>11dgi!l4vW;E>jN0e7A#h6Ct^8|0a#1<0u#_GO)4^{z%>$ejHEd%9!{sky!`CA@ zXZ;ZUJFjKB&X^oXx`33{ab7s%c%k>0_Ol4`_I!iz83bUSbWp1Z&28&$M;Hy>%Rhj$K&PM? zzL|SXd^lAP{S5 zus{@}Jm0fFknp5`!&-{g$6|%1l84cYM!e3wM&cp2kEt^(JCRib_fN_qRm^7c;-3}T zp9skKKdCO*xSCtqF-R)>`}Z&BEjuX(#)O^u+E3zjjpL;R&WdUbLntc~ii=3LCD5MJ z9&9JQ_6>^^>8Ka_Ixlh%Uj3MnCFJGhtuZyu6;u?1rkshv zIJ)MesjOqJ_0sqXyQ19IpTIZH7LvNp<;m4S7%hHV0RJcO!C$D9M``1#CnZW)5@|qg zQ+%;9T_59Q^Zl3yg9a;s*Gi~6pn14&GsuE-fspI)K2UkPO|Hbi&f@!R?6%y(<&SbQ z>5Ip@jWh0fgACb({8ArL@DsprNBHRfkI+WA?s8oV1p-op_+O+;|JouMdvi-?7fWMj zz#oMFpC0-owRd-PbsRstx=|Ynz&f4&V16I&iCIF8l6DXT3QU7+8>~cALx7({J_cJZj`tW`mNOkDTrH20(T zt|#A1@9pmQr>rN?jhJp2UZYnH+jggA0DHmH7c2dM8vw+pY60QhK$HPK3j*duoaITXs^5`aw7&t8LT?P8%pi>&Mu@KB~*TycLjpi6Jj#|M^SkJZ_PF`?Zr z4!IU?Rx^#CfWZ?AAyY`dSZgr}#UG8x%@2%%T8mY1;rOpUP>D>7tKx~l}rCSf>h z4pi1_whIqpz%Q^CYEVbKjPyJ}EYV4nbq|^AS-LdkgXE1X!UKn_eJDGU_bH#IQ5~gBx<$I zy*LWv0awGOPfj4m?P}0D!qJ8=TlWrZPtzd5iVrY7%6~ocwGCYTmdNHZbCygS0S8=) z=D@Ye?_6qIT6)BOZ^)E%&$M2R(`pkEPpSK5x8HxVe1i;yslq9upNujznNay?G(vri zjolZZhOqY)UR{Mdp*UvR%&1;JIh!kHHBWxOQrVh(A*k$nRM~vi%_W zLOrvW94KKr8~)3naELipT!_nv+NgJwnP9dhE&4TU2XpofQG>H8a)nMCf1d7MatqW^ z2Iiz;DNzim?P?fQawC6Yl zO|nrIu~Kv1VzAB=pQg-osBFG4#UYxG^t8qik29FzB(={HydvN4na)tFss?}ktj8s- z=bfah@cWyo@I|r9XIO`(8df{7y5=k~jT&@7n(=tF!*ZwMS2kxvb#0XuT#alI?Tq7F z6xYeCke(Ohl2)!SS2}LjAfP|NLjkI2Q1=c>sXOrM*E4$;WsO^{OLci_I<{Z#erDS; zYBZh=c70|a@Z`(y30cS)vDL2vt-95CkOH_qrKlS;a{e$&s@!x@uADO;h zt*L6T9l+(Jz-_T7@s;;QNRyp>ttWIp@qli@Yv{$K;_G6;iouN9B^(5uA+^D7RGbau zn&ZOgQVIKl@&onOcAh&oTQ$q})kdOZKWdxvtCLj42i@MA0i;4S{A9OaKP}+TeA2-1 z5}!v$sUoRR6RpF8(nmpj^bQ0(#W$-1*+Wk=+Jk;V<*GL$ zt_aa4pWJUxQ`XO!f)-pW7@AXhSLf+01UPL#alF3f>=oAN?40*uglv3IBi(bFXmYU% z?6WX(Db{&Tma^p19^-MkzP=0#&;&V!UpKkB2l}euj@Lsp*1{lF77;6r;utKp^n9>_CmslR2}tTCCfdi9f#-VJsNL}+LYfbS`-HHRDXAc zORILM4!WF^C`dO!B7Tas1Iwa9>shVsa`T9V`w`5rcQ$D~z6k@sKL$$V^4cKok1&68 z9k74=YZzp4KX!3t14F7`6!v9OpTVY*FSO|S{ez3sI zG=%}1BzysYLX3+7E9BC1B6!vA=#EcdZuG57#7_gsSC9xJ>JeFl!FHvFA)@hdjVQbj?&2jQW2rFF2am4O=QtR`p#%^8g=2FQVZef%GHCbnS(=#FtICA zQkyW7oI$XVZ;-MK#xIf!foO?m2q}cA55mb5Pvjq&#x=gN^eTZV zG?Sz5NJQA@pSaoP0x5@ELx46ccmwj3U@;;(rqWFd*`|^iuJD1y4G7v3Et+r&HR1rz z8VhGcs~c0iTJ0YXsTsB9efY`&R%KuhgJv)Ae}5j?J+}0Wf8fe9I0y*a|1MXEOvu^O z&iH?^hW;a`UJTh(If!YYDgwC-*2#cu41>#ryRGS{i)e>OU%EHe|BimsO3w1dvHF4Ll=oWM)^Oq@_nql`FUDBZTJ|rG z^2Tw}%kZy>Rq?ZLU(Z`yN8eR1-&#{%3sA-C7<*#3_TM%hTCG~OC;6yJ-~kB6DNA1Z z)LZvO!4UlujZzvlIKa=PJd(VHPC-k{1qv(zpwz{f%DQ!RL8&{az9BYA<@GGvO#7+_!#ZAu;J+BGX=J)DgY9o}=*B2`6x)=ZY3<|>jTBayUg#8jf z28ZS5C4QJy7H=y<;BA9RgXG0E=HckUr?m4IPrQ06MvVY1`2Mdl`7X(A6O1)dXH$%G zp`q&(SGE}h#u8PuBkI`<)HbnI$(Zm=k!f#`bCxWiUdl{$G4V-x zQfp*T$R#YQ8>`d=q<0wZsq~2dPa(l3|<_EJ#O}cg!FJRD$&#t8GC2I^&x+)~ zDc=3%5+v;`T|_;MO&we;?f(J|veA=L04C&Mc`Mtq#x~@Q78cqH+dvYf@)i`xSylK; z0Qck2Rz@OOGmO#pXwpvKALKAV4Z{un1>ra&r}`>J9;9Rivqk%xcp#Rg4_9-r+)}&V zDJ5A5vw{$J{P~#9aQ5ivgpsNkm(PM|VU>Nwadl|0kx7GNML&Sig;KMZOfX%=+0hdg$ zYJn@>hS@$bAO&;4E5_%y*eG8Ngw2&7H8HMJYUCP`bN z0EolSDAVs8eb8R;8u-Y^ecZ`^b(q=R@!2i_^31q}&grz77)!Jj;@w!ppMBWZ6czI@((C)_dvO&XfH`2uAR$ z-3pHXMBOmGcSGr$*N}q_%RV>8whIg8-SC1iKq2>{fxpaLs*Nw*^M1i{{+f*y)cVy-jh1Ts_-#h%LCKK286K=J*y^XqenTyc)$>{K_Qhwr^ykEA1%~Cja z*Y^R3Je?K&qg5hSpP*xG4_fy}AchiTG2#wbO+k6Pb^%cluY|LmOurT!Xp$Uc3+vK|b^xC@dg6)#J`(zf5utc$xZR#1lA$RL#Z z??$;LI^~C;&QVGz)xh~FHX}VT`WgAE`GOJ!ZDi$5I`k;~W2-DASR|8sqD6EWt$F4$ z>`eMj`levkY81sfv~x`t?x8V%k;O?=RV$ISAmSBXR9~X`N`bxad)CloSreXAQe3YBF-;18|Q(_!a#k729QU`CCZk)Orvf+8q1n~2NP2E`EmXNxL?I0POg074p3DS zox?X;YPck&xkYKW35m;_vr=i)k0hLw(3W404^gC>2XCQd6COR(X+qK>rgtrIwjcvw z5G$65O!CC8JuL@%>cM`Fi**AT6p}kSl29wgjiRK&cBG%3;JZSr9U`m1lLClE**XME z@V!yU0yZBiY%nUVjC?Q{ns!LKhHRq#0H$cu`lDP8Ch*f2>1Q!)K9983lxxn+2D`y) z0(Ap9SpmTj9n2| zOp0h26js}~VOk~SL_4l&ZR6wL0?Fs!#XGnuU~+QqT#b3JenD9uQ{JY3GWm^->T~E; zu~=dpMlq>qTRwB0$@b>1e>irk7;a2*SuZTThmX6~Tu#r>mY1ey3`E&Q-AH7isx~ zZ|p*o{e27CpQZ0~tOwBomS5KAYLnI9)E9Tw!r_WoyFKQPHRr?Yvf!~!P3_510;i6W zBg(vndyzR&=#y(Cyt4AjPz$!}k=Y*FK(+$at$V zvj4S7&04hOoAEa9%&8E{??Uiyuk1rgx*~_+eoITe%@_{%JA9iR;xohzkq?^f{sRjU zG|0h$xK3?wSf4HS>zCc#wOIh$^xCQKBPu&nbgw2adx#EkdUy12$zi$jH%yyG;SDky zM43J`sdY-lQY$on%0i(z2`^0fE}$5bFQk_jQ~?W>JC;{q%FVB(p>d1!K7l!B2e?%i z^Yk%FvmO2Z)#mF}YY!`^X`Q~R1?^D9>sCCEF3_ty-_=inJtM4*0<-+T zn=AA(qFEG>*rKY5i?~5RcHdo{IYVsgGfe)mhHPxss&@ zu_pN&q0BgnQ!2Y5@KY@-L8}uAQY@m5lr=?`Y^5XYsS)V*7`LaVJ$Z5%P?&~9$=_eu z`WG_qi&DbsaL9qbp)(K@nnM^mh??u-K($uU9At$8)A^q~L1$ zr3kgtw?2A0V9Sqnut9bX&97dT2&T65S)`N-T&z;s!(`Q?NH#eFRo0Yg`Nw4xHq5d; z7xczeT9cljOFFRAbNJ(MRkiL3KM~Ot4Rlxe`M0=+weIj6?N^>7ePA~RUiA&4z&%Zn zZT)+3r7vhNb#mca#=S?jJasBli$)gp2bYNY`t1L12#AjnG?jl0q4vKS!rxzA%bL1a z*qi(>6lD9K(LBP<`WmHR;o;0kP&B3mpa7|U_%J3=BO^Ri!r_Y+9!8$^iK$zo0}ji4 zVyQi(9dR6cEX06E)62=u<7{VvqhHh2+aT<{IFHlQ0yh3fLa0bdm~!OB1eD;TJR4H< zqBJkuF(PYUNN_TXH8XFgDD#wCr=|;KVdX~D@$A`~^zRr<8KD>(!1voF(v(g60v!5~ zP-%TLOysouOY0SIJ{8)iaL!?W^2snwWQVfsS{n*~z7M0}a3YZdl`;#k)1f(Dvg-%h z`xQ{wCyXq0#%5ZeOyn#-6%eS~U3umR%sE>sTI;1jE%E|}5Pm!p5B_IA?nvSJ_E zJ#U4vlE@)BtFq3P7KCd-&U^GId~BolM=Ux{HaaSJIv|qM@I8L{hTR?NZa%mI#yTn34A{5b9>ZS6b!dpt+u z+m7{@wqn0OIBSFU1M=?=-@EkT68R4RcKrV{P>1GBgR#s;G&E_hV!UIup3k##v zO7a9C6(Bk=WfF>d_yOPpsNHR zP7AoBa5thk0c!DLXXep^@Y2G{Pw;f?p;bPlm`-Wm;c$N{pa&A>PHg%hD6=Jv&Pee4 zo%7n>&w%*jxghV>(c8Kv?aFpEA%A)^3_IrIAfC+50v`o=eij&upDj9~X9X-(7r+mL zO&Wv_fOjW^istRob$Vm0e!nj9eT$k2j;CxXBJQ2p;So;6`L*v3c10#ufZdA?xZ>xf zO}g%o(uAeG+dx0u;bfa(UmIH}7^5w36$GGhQ~WsJZ|Olkp1ajqm(px^-mvEO=MXn) zr90TdkU{ZY3TnGrC5C*0nvM-cI{t!GACk4`h{B>Mvya+8)BR!r<5BDEhj6OJV9!~x z4_77Th_j6SZK1{<(Yb+8{lgB?EgsR0IVKo$LlB5?cQ0Bv4^P=OFdm#0u~@GJ7xQKQ z2~yWcTP!{LOc_*KI7ughINhB#o7!$IT2LTPw7{M;uDFwL2UD=lSRJ)mSh5n1yf*ek zS$ta3C1II<4)tJLqK>2X^?FwVqad)#Hr}&GOCfia>7jLA|>JcJyF{;No0ZrsKP6_vfm-Yu%A+#c9WKut+ZFL zUl_6cz7v_!oo5anm^d*Dbn9<}K((Nz5`3BdSBuQM86Ek)ZuKMWUoA4ano8%V@va!| zu8I3>t0TIt$H?{Blj|Qf0&NL7vVUUhhINUOrH<)_nP^zzV}PnpGevEB2k$pIR>wGi z*H`P5!U{0NfHV3FNlhm_HhnwI60j7{hs$o-nU*v+J;*iqZus<^$is8p+#V(p0k&Cg z)V0=WhY#q#Z<1Ir9PZMeSd{xG7E$~^ZPLG$UIF%YW|roE?MU2Tn$>v?y4FI1B4?CU z>7@bmY(vPb6zg`x8ER`bhG-qD2!gG2! z^X}{^CI@6|4-#;8JGcH}1iU%X5z`shu#Mj9VsxJn5&2Qlx4{^~yNM7&!Ye&ou&}|k zt4PB>0UuXLMrk_fWp3Ne)QxL^V4#7ds)|&^onSR{oUY!_iL&^rA@(j-DR+Ip1T z_Uo-cQ4h2_epU%;3k3sgSuPv{sZAJ`B zQn{IuDFCwfK%i(E>=n>*P$%<5e9#VY7*MxKvNLKW-2=|0UDRtbXSXQyCa($h*fz3m z`1YiRKE9vqvK4=m?UFcV@zTs@j`e>hINNq$J3Cgky!?lgAg z-mW}@{le2l!yhY=AI6{hoDj*BP2Y@cy&&s0rPJj+)qLzzzwPJu472m)l4;`Vib1|Z z$+f-1I8_I?iA}4CBZ#|<5#HG-esvC>T8oWb^EUaXS+5dkxh(fX$RqGa;2~^ z#|PJccinGFjl~<<;w>FPrSO)mM@wi4U#?EM)#Xk~wJPsiK7utr7dxOYur%1*=w`FF zHl|Jdw1?HDu`rPM)Gz!H9!2pn2A<8i=~dy@HH?f43w_TkI6?t9P^XMn9kNWbLM7iQ zYW~6|30^h5Q1tql`;*=Z5@_+5sjJ5=a>Nf%7LuleYn77Wg#&G1gcyL&Q>Oy&->H=s z#igiix)0xnk)~)w6V&5u2Y#cH>rXVqxg`!{VDCT`ckW%3x#W}5C{UT1|g{3KJ(Mb&qMwZ%9LoFHnE{EvB2LI zdyA9`AI~+4kCX?^fs;O+CY!P0iV?}&kYyiV-8sB0`ZPTKC?(g*LhwInQvK+PJ4uG3JZA+jD{#WYam!jA`m_pxrdyL z-VF_gkwCPoQ51cYbTp48>sG+y3}Ajcm(mkyI40vS=&xvLF7>2G9SzVyV)3n^ySU`? zc6+*f-_P0hgLWW{!L}3d^>RNF-*n-?O}2$dK9jL+QLUQ(McCZB>+GvkjrWnLd3@Bi zy>b1pdWE=tobXEVxD23BCuM##VlKK=-w zbOvuHD@4@4V{AN6wE|S8rTfWuWS0B2H`i{0XDEG=Iy~U#kR&D3mvb%Y=zcz+$gET4 z4l{t4r?yfEpcVWh5CJC#`U>jFR9$oiv{30}7&&kWr;r$jTWnPE%t7K-HD1w_GeLJ{ zQTq!o&BdJG_yF^wDW~dmV|m@qkIKgr?|o_9=p6tC(GKaDH52+a{IX%$0^d^@m0xD>W3z<^77YElguttcBapED|JXNPZC{-7;l~0^R zCeI*V%+|RCbK#IxVxAR_ruZ5bFvs4Z#o6nOt#0`v2X3^^H-`z|E9F9KzOG_J*+FqI zl9QIYyY)yKt6!r@C4EJ|0H~>h`VvkyV8Pk~2r(>|W_dI&a1n9DQ5P9vkaXHfE(juB ztvanTlpD}9$DaI1$;A_%k>&m>&zjih)s1TJqycYIQuPh^NPa_cu^sEH4B%=|AINB! zr% z;cJ#sUk1h|$Y7)4ro*J0@7b^ebN<9~EdWmZT_{qWZRf~$+*-n3j{`nQZsYqLiYt~v zrx$h3raI{rq(>l)#4I%r3b7nYcT3S1si5?6`L4dv&RBsm6HqP;p>POfNilM=1U&96 ztW%%?|7=*LMt@UxRYC;ow_p z>PkpMwqq$&ZDd503hagI7J&MbokJ7vzug&bJTnpKeP;U>^XBg@4E1e&Q*es87u(x1t(IbpY`wK1hKoOk2J7@HX zI}2P;m+8_jmBv*w#IXN9R1+`L_U<;`TO4^aiWny-#rk4-G(jJ~ktRoER81JHc5;Od ziaWlCk6yTI0`;N042jB`ine^H9`mpla~wY_<h?L*&LrZ<4Q`}Et0JRTwMyrM%>uh)ROvjNYW5muI;$JV zkmLikF3p03PhSidHl>H92(@Dq!ZzpW93kmQrZT5_7bIUk>_H0rkFB#cA1^JuV90&t z%Tp2EqWuNVy#9TSo=^=$Jc6Iw*cxqX1|xR|1_?Qu39cMz+)Q!0k?Fh$wO351F1z8! z8@FlxLQ|Un092+)flz1|y;9vM3IRIJQtUxE)7TT6P&&0RXgadb!CKN6g}Y=+?YP{g z>t(!A91-6b(Zl-nkA5W%mdfn7AH<$&zQmp<3C=(6>ElQiDD4Pc)nK7Mr{W%et1E#m zTDu4Ob$O8sS&qvD}|e;*wL!*;GkcKlX#c)#5a|_`f{UcbnI+zGOZ079Xn& zX78Rur_Jnq#vzfm)dK9C-*Hp{c*; ziDFSsIU#S(7m*ioKaJYwVU{l(n$xPnF}Id0tKxVzH>PDx!1nmXpSfAs&@@E|1hdyO zUmkyeYM`T0*>7k*Hf_=RXRpeA-BqU_c$LoEVoe=2KyguhPfVWa6tWfF>2O#w@UIYD zWTc|cct$Y26P^9*5K}F`sQv=iM7N!&^P_c;P#-(7gaVClm3I7ZpFq&d5H;YpF8>K= zli7TCd<2EPKe6MV{*Fi+oVB)gEDvJgY-d-Rh*kATHRvT-ALu=q=&ITT=i<(2gv5=8 z-Eyy7*hC7dFOdPh72t=erZAwQRr7_Nek8B&R7dbI<+`+yN}-4XL;NT`|BQd%rF&rm zU(-XF`2|V%&|>)b#6bTIPR8ar(1wfJcr(PZL%sHgI+`^UN+5klxt<}9@0dMLTV2ZOE*mXe=6 zoU~(o6DLQRS_{^nP}c!2brD4!B_oVBO3_7S(U#v_ni5a+Q*E8R71arJUn(}E7Wfc_ z9`%H_yrUUlkyJqAbT>Gs=oGDTTiGnUa->*`2!EM=jUsxUdCDd5`o;>NZGkpPZW)YS|GeZ$5o_iM+bGS{#5gYy47M@7Zbs7 z#D;?bn-{6WxO5UYSvuJnnX|kDPC_aJR3HA-{Nr4@c3!W=D6Mkn?mXYPTSVs&mICkq zCFyQX@+SOM?V?9@c!sp|z*WGz%6gK4fm}NNm{tR~u#5rh8KsJ4pp`=9n~{5eX5opr z`uhNKqX09{P^W?UuT*XT%3BYH0F385Zej3(OEK$_0aL!%sI=dc3cZ6Y_;Ali6zU*7 znVG3;A#4W31f+Xgk~M8#IKBa^Z*ig&mhFN`@X3v zED4&l*$$_W>o1FMvb;42tHk;3pIsbq3DB@feO-3iYzM6aZ8a_m*98HjsrlbFuPG$V zd~jLo=emSop{@N9narOdn%>M&_=EJ~wB-s2gYKjOyN?fYsio5ek_R*;8cEpPjNXTsVOz<=7iVgU zYb0$NL5&M{F408Q@5yZF1V#BI2k6R*Q?FZ8Dj z=BeS8`Wz$!63YhQF{G+iNjQxO=I=ZG}H1M{& zKduRE|G5zKMFz6KJ#&7k!?HXkJqsZcrM+1%n4di^KFEbFeQ^O~vEzsmJ2G#~Y{V01 z3XLkw5ys*8=meyqOngM7FAY=*>0+%4I}`Gfu0DuUF`n4RK&xf=CCtR)q~D?%>{k7< zX47GPz9d4; zJ8ZmP&O89x&%m8NOp_+#>4!3)+&eTttPTG;)3+1}n_PwPD{!#>cjzGz@m3mO0?6iU zeAOUTDw;4#L`L@_#xlINICd)DSI-Nf%sGJwKwc}w&tdxu=$8955Gcxc@*|ZIy#~Vg zqQFo^4C0f;hmj51&M7e2BvYlF+>_uP^**e zct8Ywlkt0!7VD2%0M7&kXP!?aM^(icZF9h{>3aH+f|LM6l_1_5m{e#e182-m(p9No+WMDUC)@-Ko#joJ2Ft4{Jq(F^Z#blrA-JVX7c zTs{^SGGYisf5pn_mP_Xjki#wxO7WX>>*R(p{*VbAs+kQcI4WKLST*fLA}|?BNa=(AidC84S_~ zcKLhfUg;hkvn4PrAkVl6M0#kGr4lS4Fy6Q7&1(IcD{@%@_?oSVibm=n*v9ZgOj$F3 zDF#pzS`-=Ct%5aG<>L=1G5e@u5?jl7r;-Ekjzn-Q=5qPky|JX85#wThW5=k-&wy4U zwG-yben@LRl|NsgFRJI7KJ&PB`9zZHojuoC$VnX5)h9iig&qcc0_(KYn44{DI< z3i|j`BtGh4lOONc;|is459xhCU-&v256xs&1cQFW)?^amj%z@X3ioXJNmQ=X#?G3g zEn7zn7=W$0-&K0_g`Ivb>uEuhELG%a@|8g(T~f0dkC0Sclcql(nZLTX-(c z+`xEgGtZc6W1Q{j%5*&wzf}LgTR%d+I`c83fKWt3WI(QYR}KKfUdRkn`knhTMm_0N zZmaDyBxocrL^b=k21qJ|F57)x6ONmN-hDw8Z!DNaR1jhv+L@25zG1d}sCaus!6ikU z@z~IsPJ_jpZq^MRp(H}16?rg;#EbRCU9h;+>WY1O!p z;|-m*s##RhOLG>wEWs72u^Nk^)>!={_6z4;p+gu#&d}EO9zNHPA+?K%j6jjI_@1!ohnS3 zRk%cvqK^$FY*IOu?K^`Ri5xr9$li1PFqixIq^9s}5-)yXfi?bq+h~^&C{8fcaWadR zTtxxv-=a=>BY5p-R4Gm?EckzTScp!3iS{YZbbtXeS~6@6X>TGBOj_=cIR2LY%-G{J zT`Y!)iVG81k7UHY2b~mT&mM;*m^NN!E<~&)WW!DroyO=O^x$u1+WCz(woFnDdY;OK zCNhE8qL`U8eKx}x|7a9S6_Y4%Q&?e%mM52mYl)t01K6kpR=GX^~aY7 zCi23eJO@z|tJdZ03Vjyo`gHA1_os+AtmVk4m>P|DhDbeA{siiHzAf@E?D6E=N48+F zDp?LJMjlLX4?|7|y?23(iR(vf(!<`YR|6MHk%w=1;n$q@AM2Xi&*6E%tn7cxM-E4KJMvz;~f3aJpvvK3FANeNQ_Ol^JA6yG*yI0WAA)>uv@zyv*m`eeQ^kkoLws$a6#Y_d&*bLn^Y;}s_YuGQ# zJi_0QiN({;QkiE;!RPzsf@zTC z^j=;)*$@5V*+SatVC{uZ_Ziv z^pwv+2L*MFd|c-rOix*T-w3KlJW1sZ`CbS#an_XjpoU}Iz@-=^UdBWVcq3=;J6^EC z;yQ+XNnvI{@abRnmp8i;CdI{tY1(vZ>H4=#=8L=nEp8h9#d65;A(%tXSil)9L>wn=aLs`1DQvjS}#II_d!dEA!B4h|K^_^=azHQBY`S~U~?0E&uWIcduou@jpQnP~#=BM?T74VR7rExsW zCANSUPRC*Rb_!whO)(fOyIKZfdTNR-HqELHZyg^@!FJtq8O2DoLx_$q>%y)AtfKF* zXkD2_FESW|?@=*EfhZ>mwgsIF9tK?ePp*85LvwgJBR1h`b8MQjNxzYZoyY6D`{~P~ zRxiqun_iV8))y3Ps$P&fTq1Ds)UhDeG+~u&fDDzz%_-_b2AcYL z0TiFuvzRNx6;9+pR!kupPEO zzP`8MxTg1JwW=4iVHVX|m~Vbd&3*J{dqUKc)I2PoPn3z9XA5*>BovOZVyDMK_aT>e zytNdkL-P>t;o~CVpj)H-3D(lPCY3ed(UGCr@lSQ(U|VqnIi$sK2#?D8V9YhcryZv{ zqDLaWL3M7L3Wp=Ni8mI0Y36$L42}D|<=&Bi=%S^az2C13Hx%QOL|(WdTel=T4Cjf} zbNdk?1Fo!rRLyX}H9%CZi@}aHO5df2308K{enL*;XmHl{ly11FZkcf_PEZ6n2}#LF(^U7qW+av-tFO{mAoU)-Y<~UF-3Go4i>vJtQPmgXa76h{?Su3ID)(P(dEQUj)Jr%lzJhDY8Bi94$d@ zG3+8~E*SkKFOTJYS#bVI$9|&PJkK_g*A;JAPu!^ZPVRa_5=%FfdgNS)@NzL)6M0Gw zt=)oN6Gt)51m45C(buIuoI5^@h_$e)u;IW)KaAy)SA7zrJymL=EpU;P>w zdG*}vD`M!sKB-$)IeXV>`MH>v;b2m%thf1I%c=~VRxsCelG$)ow@-?T<0~`OS#$(L zFr`>c$c#aK95*AL7q8d-}Xrj;uE zJ)y~sGI209>cD>p$xZ-#0;zgWrl`f4CbrDeHwa16&TXHR|hbk>~63U`6e(V zvA^~oZwV$2WjY;_*Y{LQk-1T!P{V&;mU*tdMX@dI|eO+UMn>=090a&~spM6;!%+cf=Jcdq6BvA$8atG!!M z&NOWBlfB4UOKA|TYg*f3%(715jNA2JZ`PY0)AEwLCyqO;?Pt_w?Y?8G9d+Mb>W=~D ztz?ZdpAY|Q4B+Q;8578o>3O+Z8}uXu4Dq$zyy!@YK;h4nIIauK(Mzj)TC9_)6VfU& zXj)o4EG@-!f(wzA?x3B+NxXMcgnDO1w9LCS#)PMGwzKUkzbl*a}-7cMPXVRdGk?`#XTz^tjgDkpo>!c4;<+VpG{Hf zo_zf%Uh7sBVpp|8D;5y73nT%r;;r7?a4a5*>m|jPw6Fg ziyxh2iunMR+9$1t1`m6f)Me`%sB5gDYiJ`hD;uxtHUdxm#C zm4do0weott^U<-VtWFr!YZn=<7&?CTjx>t24>`1|{FqV~JVzSys@Hn?eAFYfmR~-U zO1AIwZzDg|A$U85djEIuhV(T!$Mm82)G^m2rH0$yuLY267l=%UjhrkXZsMGS4P$%G zt4aTrT{VbR7ZlB#1oL<9JV(s?>@j0%_h!osJUzJJ!!&VSBmn~Kgu6CRr-Hg$X!HH-%V|7jXvUa~4MZlVd4-gB|q$#%y!s?8T&9f#iL zU5k3#96fOQ#~@Uo&ES{9(8`S@YvkSp_AW9@tgQsD-Uqvv1X;14IC#c23DY_6fwhV< za}|S>Fqzt_5qS&E+@PA7f|cncTiq7lyz$y%-akme^1s&QkQ#yXz40WdZ4 zw!<7qy65ddKS?R(`|?z#$yFL5a-M@V#gFr<;8Zu5!Oj$3R$1!;}W5xudg`iBNOgf{}n8g9B&#S{4V z)q#AN8%%idu%i73Rrl#0u^juWf(UKd=g(P%kHvBw(kKt=^F~ToV$D7-8M`4%dGPOm z8I8>5*j~7&IQ-t)nFW|!@A66&wv~2B*V$l83WE$R~KLV2nVr@60C#g%_wE>N& zU95>g^4pC>t)kIYBgX4kPVS1Ad9PFE`*r&-Go*}!)9-sONLY)Oo!i&yalzw z+sb2PHgX&88>{i@_fQH5M@VdHKz3;jSC-wUnWOVv1P(b5$CnV`qup_aE3f~v(IK+B z&1Qlgp}Xy2ThO8Xl@m&At)nsWBKZz>O7Qi8?=K?Ya&1&?WAWH=D;W!md#k3`9!v3t zyZOIgki9?k2h?q^tBT(_BWYjCUSp~Aj>ewPnF%lWAU%9>meM!D%e~BTa?7RxriP|E4tP@`+Rp(=j&YTrNjw0KcCqK1De% z&%y2kShZ!Zer?l4C`bsC?PzRw39)KQU|Y_F_BbI_&$d;pZab3ktY-iBg3rFP2P&PL zx@Y%-h{KE*0}EUCLhxL!e^cs#XlJo&4@yU?CK$grNpI${n~ryiJG3J~RboP4#-oJi zixHdGlPozrrvbi*fRVAhh}?kDvJ0K-0Aw25tRv-T5!4uKnU9WYK*QmMhg7Y-1E?6M zn4CW9SKkS#i{ys=O_D$DKa@UR$G!Q32!B6F7@`#LG0x#5(||ErU$vpkv3kGA)?9+T zE&sRMxY(Z1C$%KeMqoD5A!oZax~w+~4A!Q;a}QNU${_{}>lTPhXM%2*5;(2m@jame zi3#<=E)Iz&T8$&IW49kNIxt{<SzjS-Z9FbAv0u}bOdJWEnmWs(HzHonn z^=aI@Wm~ClA{T=UrHjVtt1`P1yM7TMzVV(-1Gz4MF5cLt_Jw6F&+CV86Nv@}Apv_P z%y1k4RDs3g$*QY|S;hTslpB?j97d8vOp6d=58H$Zf(*Oi0`~{!O#4M%!c0EMpFfz+ zC!IvMD=vlyq2DovMEk!H?)2R0^9lGa$h zmkY70z7iELl?y@M5^XtQzJ$3d%T4cGrkYF3Q?wx0s|{Y@2->Cw(ddh%VjKAC zMNAYicyA%iRVW=0&-Xmyq1YfP7pR*l`mIgVlwW3UXvrzl2444mGst`+2H}Vw^T#MBi_k_Z%Xxt@+MnYGsy}k3Ac-QB<9o{=iGok*!oP>(D0r-)%%)t z=Te=y{vL;e`MtwF$&Z<~42Bw}MUu?Z@Sx=<-wQ7z0rm-@^;$a|fl=_tA|3GkANruS zsJ571e#t{?e1LPNggb}v>y&bjj3TO&9QT-K-sx9y(_$k7r547i&_P2+t^gqCdc{M% z7QFyLA^+LX@9-L@P8J;3`h*?Dd?|KpH7P6_Mm`AqcViLRI7P>;Oba|9TceG}$nz0W zOg~`B2j$l9RmPD`>WuXF^A6**$XY8$fVpbwG#D+#?17REG=Uql^R5Wes?N*RYo*Jn zysdissNosY*gW)XA|5t;cv>y_fyzQpy00|e#DWBhEL|xo1CqEtPh~|#ujQjG3ZeLY zBIjw_Bby-+n7CTYW~eKuE;8q5m8JN6Mm(}R3y}RtN*>NA)R}rm?3bDPjJ%Y5E_LTf zm6rOG2Gad6vq~$S%RDK}G>D|9yhC;f+>uibhU1Dqm>|3(i9Rsu98`xXvh|m5W5iP= zKX(}ZLYY30-!*xUH3?nJE@?jVW!LsZ*46K4z?QIl7YXIT+;Lur_2&rW9$B=sodC!w zq5n4Fn&L{DZteZjewRyA@v%|X4&_Wkc@6nNdD}!16gDS~m7}BftBx1a%I@g^sKnV1 z$b^xjW&nt@DsX!g-?4Ms*h+goh3BR$rqa*-^ozlD0*W;(?bhwWs71-J!Q6@InhdgD z8a$M!N>lekDkdT}{n7sSpa#M_=4G4{H4O;#sEte{O1`!V=N`WDyb9#fJQi>2Nj2|D>O|G)_G*xh9t zTnYY5hBQdd5Zs2OAek;ij@R0@>M@x(K@UX_zf6cmNi}1cUchQB&;jMXrN_|qb-imf z9gx1VY)`B+3Vv^&cVk+!GI8=Sb@AHDULMY;{C5PiWVy?N1-r@}v~9m?eU}8c)5Pv~ z2?4tu`pb=)idyc=0Ok7VJJywS?mhJ^eEa1!DYwcmvB7o!qhhb~mjTof(_8n>y`^m0 z=RZ@2RRpdZSi9 ztZ_Blr)F}2CH7|uXL3H{`>mlIkSOP+wFTk3lq*iib5`zs-@NF_-wugh zjQZGtj!QyfV7+k3tSNZ+K%F=&+_Z`Hv1@6&NylzV2v$U(-$E$75 z&%%s4!4+Gf?0sWvHWlYph21JA5WNO|JdB{u)oEzR{m46G!62!n2X zV^Nd<`k)R@5g=Eb{_sqbkiXSZX(KUFDSZQ1m=k^)%n0RGd`ZjhvD?djg(^44Mm6>m zqu*zU>vsJl6AXg_HF+bvrCKfc+5hb$Qlh*&H&Gx(K={ykB5@@;1^$8{if0sa!CpuCZe>V)dDlZxN_~u;KcI>*m+H6oi$D+XlVE z-XIa!PENwY>nxmawF3OK^&&EM+_}P>9@Y3tGsNoa>U(;bmQndWw|wnW4XW8MxYgXx zh&>373ij_VNGkh${?QXm4%$g0OfGx9ESMI&8jKiOFkAGEeGwUa8vIH4E$rL)P2VcL zli5cUKAqU~5)&WZM;at}md|SW2#ojWJLto5oDOP!#T4f@HZ*q2v6A8WrmVu>0qHBF zomO%Gl*gj$0}YDf%_lwndbZ~aA_v7s7bspmcUr*!i2zN15;hxppN9Jk<_(tIoqoA` zU#8gWLk${go)$rRX;jlURx*CvVkZ&%icd%8L8@yz!QD6c&QK%YY3tidv$F4fr=*2v zVXfoZ!L3paRHnUgde;06cV?A1_*Lp_`!9*+_UYg8-}-4CeZfIh+y%RtFWk1g!GA%P zg@wpzPp>t^!A3xtz1{y`&;RUT|FM4r`G3U!<|V|}5eF0m1pR-w#Qa-{I-0vW z*_bPuINDfRxVbZVJJ|ofdqShlIImWKfG`b!fWZHc0CxXEn%SGUxmma|*_ybj$U{Q? QXNUMVj{ixAs{dC13kwJZZ2$lO literal 0 HcmV?d00001 diff --git a/sdk-intergration/Packages/TA/Plugins/Android/TDCore.aar.meta b/sdk-intergration/Packages/TA/Plugins/Android/TDCore.aar.meta new file mode 100644 index 0000000..42467fe --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/Android/TDCore.aar.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: 640f04af2bfe244b69b495a42f69c4c6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Android: Android + second: + enabled: 1 + settings: {} + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/Android/ThinkingSDK-gameengine.aar b/sdk-intergration/Packages/TA/Plugins/Android/ThinkingSDK-gameengine.aar new file mode 100644 index 0000000000000000000000000000000000000000..5bd615a7ecd42705af9b6b69571e5d2ecd618801 GIT binary patch literal 15947 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 literal 0 HcmV?d00001 diff --git a/sdk-intergration/Packages/TA/Plugins/Android/ThinkingSDK-thirdparty.aar.meta b/sdk-intergration/Packages/TA/Plugins/Android/ThinkingSDK-thirdparty.aar.meta new file mode 100644 index 0000000..8b74aaa --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/Android/ThinkingSDK-thirdparty.aar.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: 04844570eb35e4807bd54a34a89619c0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Android: Android + second: + enabled: 1 + settings: {} + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC.meta b/sdk-intergration/Packages/TA/Plugins/PC.meta new file mode 100644 index 0000000..bbe9ed9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b5e64871c9af447029fea9ebb8a01507 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/AutoTrack.meta b/sdk-intergration/Packages/TA/Plugins/PC/AutoTrack.meta new file mode 100644 index 0000000..ca4fbe3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/AutoTrack.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eefcaefb9c6534ae78d8e6d4345a9531 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs b/sdk-intergration/Packages/TA/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs new file mode 100644 index 0000000..5aceb94 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs @@ -0,0 +1,213 @@ +using System.Collections.Generic; +using UnityEngine; +using ThinkingSDK.PC.Main; +using ThinkingSDK.PC.Storage; +using ThinkingSDK.PC.Utils; +using ThinkingSDK.PC.Constant; +public class ThinkingSDKAutoTrack : MonoBehaviour +{ + private string mAppId; + private TDAutoTrackEventType mAutoTrackEvents = TDAutoTrackEventType.None; + private Dictionary> mAutoTrackProperties = new Dictionary>(); + private bool mStarted = false; + private TDAutoTrackEventHandler_PC mEventCallback_PC; + + private static string TDAutoTrackEventType_APP_START = "AppStart"; + private static string TDAutoTrackEventType_APP_END = "AppEnd"; + private static string TDAutoTrackEventType_APP_CRASH = "AppCrash"; + private static string TDAutoTrackEventType_APP_INSTALL = "AppInstall"; + + // Start is called before the first frame update + void Start() + { + } + void OnApplicationFocus(bool hasFocus) + { + if (hasFocus) + { + if ((mAutoTrackEvents & TDAutoTrackEventType.AppStart) != 0) + { + Dictionary properties = new Dictionary(); + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_START)) + { + ThinkingSDKUtil.AddDictionary(properties, mAutoTrackProperties[TDAutoTrackEventType_APP_START]); + } + if (mEventCallback_PC != null) + { + ThinkingSDKUtil.AddDictionary(properties, mEventCallback_PC.AutoTrackEventCallback_PC((int) TDAutoTrackEventType.AppStart, properties)); + } + ThinkingPCSDK.Track(ThinkingSDKConstant.START_EVENT, properties, this.mAppId); + } + if ((mAutoTrackEvents & TDAutoTrackEventType.AppEnd) != 0) + { + ThinkingPCSDK.TimeEvent(ThinkingSDKConstant.END_EVENT, this.mAppId); + } + + ThinkingPCSDK.PauseTimeEvent(false, appId: this.mAppId); + } + else + { + if ((mAutoTrackEvents & TDAutoTrackEventType.AppEnd) != 0) + { + Dictionary properties = new Dictionary(); + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_END)) + { + ThinkingSDKUtil.AddDictionary(properties, mAutoTrackProperties[TDAutoTrackEventType_APP_END]); + } + if (mEventCallback_PC != null) + { + ThinkingSDKUtil.AddDictionary(properties, mEventCallback_PC.AutoTrackEventCallback_PC((int) TDAutoTrackEventType.AppEnd, properties)); + } + ThinkingPCSDK.Track(ThinkingSDKConstant.END_EVENT, properties, this.mAppId); + } + ThinkingPCSDK.Flush(this.mAppId); + + ThinkingPCSDK.PauseTimeEvent(true, appId: this.mAppId); + } + } + + void OnApplicationQuit() + { + if (Application.isFocused == true) + { + OnApplicationFocus(false); + } + //ThinkingPCSDK.FlushImmediately(this.mAppId); + } + + public void SetAppId(string appId) + { + this.mAppId = appId; + } + + public void EnableAutoTrack(TDAutoTrackEventType events, Dictionary properties, string appId) + { + SetAutoTrackProperties(events, properties); + if ((events & TDAutoTrackEventType.AppInstall) != 0) + { + object result = ThinkingSDKFile.GetData(appId, ThinkingSDKConstant.IS_INSTALL, typeof(int)); + if (result == null) + { + Dictionary mProperties = new Dictionary(properties); + ThinkingSDKFile.SaveData(appId, ThinkingSDKConstant.IS_INSTALL, 1); + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_INSTALL)) + { + ThinkingSDKUtil.AddDictionary(mProperties, mAutoTrackProperties[TDAutoTrackEventType_APP_INSTALL]); + } + ThinkingPCSDK.Track(ThinkingSDKConstant.INSTALL_EVENT, mProperties, this.mAppId); + ThinkingPCSDK.Flush(this.mAppId); + } + } + if ((events & TDAutoTrackEventType.AppStart) != 0 && mStarted == false) + { + Dictionary mProperties = new Dictionary(properties); + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_START)) + { + ThinkingSDKUtil.AddDictionary(mProperties, mAutoTrackProperties[TDAutoTrackEventType_APP_START]); + } + ThinkingPCSDK.Track(ThinkingSDKConstant.START_EVENT, mProperties, this.mAppId); + ThinkingPCSDK.Flush(this.mAppId); + } + if ((events & TDAutoTrackEventType.AppEnd) != 0 && mStarted == false) + { + ThinkingPCSDK.TimeEvent(ThinkingSDKConstant.END_EVENT, this.mAppId); + } + + mStarted = true; + } + + public void EnableAutoTrack(TDAutoTrackEventType events, TDAutoTrackEventHandler_PC eventCallback, string appId) + { + mAutoTrackEvents = events; + mEventCallback_PC = eventCallback; + if ((events & TDAutoTrackEventType.AppInstall) != 0) + { + object result = ThinkingSDKFile.GetData(appId, ThinkingSDKConstant.IS_INSTALL, typeof(int)); + if (result == null) + { + ThinkingSDKFile.SaveData(appId, ThinkingSDKConstant.IS_INSTALL, 1); + Dictionary properties = null; + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_INSTALL)) + { + properties = mAutoTrackProperties[TDAutoTrackEventType_APP_INSTALL]; + } + else + { + properties = new Dictionary(); + } + if (mEventCallback_PC != null) + { + ThinkingSDKUtil.AddDictionary(properties, mEventCallback_PC.AutoTrackEventCallback_PC((int)TDAutoTrackEventType.AppInstall, properties)); + } + ThinkingPCSDK.Track(ThinkingSDKConstant.INSTALL_EVENT, properties, this.mAppId); + ThinkingPCSDK.Flush(this.mAppId); + } + } + if ((events & TDAutoTrackEventType.AppStart) != 0 && mStarted == false) + { + Dictionary properties = null; + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_START)) + { + properties = mAutoTrackProperties[TDAutoTrackEventType_APP_START]; + } + else + { + properties = new Dictionary(); + } + if (mEventCallback_PC != null) + { + ThinkingSDKUtil.AddDictionary(properties, mEventCallback_PC.AutoTrackEventCallback_PC((int) TDAutoTrackEventType.AppStart, properties)); + } + ThinkingPCSDK.Track(ThinkingSDKConstant.START_EVENT, properties, this.mAppId); + ThinkingPCSDK.Flush(this.mAppId); + } + if ((events & TDAutoTrackEventType.AppEnd) != 0 && mStarted == false) + { + ThinkingPCSDK.TimeEvent(ThinkingSDKConstant.END_EVENT, this.mAppId); + } + + mStarted = true; + } + + public void SetAutoTrackProperties(TDAutoTrackEventType events, Dictionary properties) + { + mAutoTrackEvents = events; + if ((events & TDAutoTrackEventType.AppInstall) != 0) + { + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_INSTALL)) + { + ThinkingSDKUtil.AddDictionary(mAutoTrackProperties[TDAutoTrackEventType_APP_INSTALL], properties); + } + else + mAutoTrackProperties[TDAutoTrackEventType_APP_INSTALL] = properties; + } + if ((events & TDAutoTrackEventType.AppStart) != 0) + { + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_START)) + { + ThinkingSDKUtil.AddDictionary(mAutoTrackProperties[TDAutoTrackEventType_APP_START], properties); + } + else + mAutoTrackProperties[TDAutoTrackEventType_APP_START] = properties; + } + if ((events & TDAutoTrackEventType.AppEnd) != 0) + { + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_END)) + { + ThinkingSDKUtil.AddDictionary(mAutoTrackProperties[TDAutoTrackEventType_APP_END], properties); + } + else + mAutoTrackProperties[TDAutoTrackEventType_APP_END] = properties; + } + if ((events & TDAutoTrackEventType.AppCrash) != 0) + { + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_CRASH)) + { + ThinkingSDKUtil.AddDictionary(mAutoTrackProperties[TDAutoTrackEventType_APP_CRASH], properties); + } + else + mAutoTrackProperties[TDAutoTrackEventType_APP_CRASH] = properties; + } + } + +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs.meta new file mode 100644 index 0000000..5401f4e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0ac1f96dfbfb046e79d0e2f5c3d94261 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Config.meta b/sdk-intergration/Packages/TA/Plugins/PC/Config.meta new file mode 100644 index 0000000..0d08ab0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Config.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 255aaff7c590d43c0893234eb5279770 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKConfig.cs b/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKConfig.cs new file mode 100644 index 0000000..904c955 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKConfig.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using ThinkingSDK.PC.Utils; +using ThinkingSDK.PC.Request; +using ThinkingSDK.PC.Constant; +using UnityEngine; +using System.Collections; + +namespace ThinkingSDK.PC.Config +{ + public enum Mode + { + /* normal mode, the data will be stored in the cache and reported in batches */ + NORMAL, + /* debug mode, the data will be reported one by one */ + DEBUG, + /* debug only mode, only verify the data, and will not store it */ + DEBUG_ONLY + } + public class ThinkingSDKConfig + { + private string mToken; + private string mServerUrl; + private string mNormalUrl; + private string mDebugUrl; + private string mConfigUrl; + private string mInstanceName; + private Mode mMode = Mode.NORMAL; + private TimeZoneInfo mTimeZone; + public int mUploadInterval = 30; + public int mUploadSize = 30; + private List mDisableEvents = new List(); + private static Dictionary sInstances = new Dictionary(); + private ResponseHandle mCallback; + private ThinkingSDKConfig(string token,string serverUrl, string instanceName) + { + //verify server url + serverUrl = this.VerifyUrl(serverUrl); + this.mServerUrl = serverUrl; + this.mNormalUrl = serverUrl + "/sync"; + this.mDebugUrl = serverUrl + "/data_debug"; + this.mConfigUrl = serverUrl + "/config"; + this.mToken = token; + this.mInstanceName = instanceName; + try + { + this.mTimeZone = TimeZoneInfo.Local; + } + catch (Exception) + { + //if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("TimeZoneInfo initial failed :" + e.Message); + } + } + private string VerifyUrl(string serverUrl) + { + Uri uri = new Uri(serverUrl); + serverUrl = uri.Scheme + "://" + uri.Host + ":" + uri.Port; + return serverUrl; + } + public void SetMode(Mode mode) + { + this.mMode = mode; + } + public Mode GetMode() + { + return this.mMode; + } + public string DebugURL() + { + return this.mDebugUrl; + } + public string NormalURL() + { + return this.mNormalUrl; + } + public string ConfigURL() + { + return this.mConfigUrl; + } + public string Server() + { + return this.mServerUrl; + } + public string InstanceName() + { + return this.mInstanceName; + } + public static ThinkingSDKConfig GetInstance(string token, string server, string instanceName) + { + ThinkingSDKConfig config = null; + if (!string.IsNullOrEmpty(instanceName)) + { + if (sInstances.ContainsKey(instanceName)) + { + config = sInstances[instanceName]; + } + else + { + config = new ThinkingSDKConfig(token, server, instanceName); + sInstances.Add(instanceName, config); + } + } + else + { + if (sInstances.ContainsKey(token)) + { + config = sInstances[token]; + } + else + { + config = new ThinkingSDKConfig(token, server, null); + sInstances.Add(token, config); + } + } + return config; + } + public void SetTimeZone(TimeZoneInfo timeZoneInfo) + { + this.mTimeZone = timeZoneInfo; + } + public TimeZoneInfo TimeZone() + { + return this.mTimeZone; + } + public List DisableEvents() { + return this.mDisableEvents; + } + public bool IsDisabledEvent(string eventName) + { + if (this.mDisableEvents == null) + { + return false; + } + else + { + return this.mDisableEvents.Contains(eventName); + } + } + public void UpdateConfig(MonoBehaviour mono, ResponseHandle callback = null) + { + mCallback = callback; + mono.StartCoroutine(this.GetWithFORM(this.mConfigUrl,this.mToken,null, ConfigResponseHandle)); + } + + private void ConfigResponseHandle(Dictionary result) + { + try + { + int code = int.Parse(result["code"].ToString()); + if (result != null && code == 0) + { + Dictionary data = (Dictionary)result["data"]; + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Get remote config success: " + ThinkingSDKJSON.Serialize(data)); + foreach (KeyValuePair kv in data) + { + if (kv.Key == "sync_interval") + { + this.mUploadInterval = int.Parse(kv.Value.ToString()); + } + else if (kv.Key == "sync_batch_size") + { + this.mUploadSize = int.Parse(kv.Value.ToString()); + } + else if (kv.Key == "disable_event_list") + { + foreach (var item in (List)kv.Value) + { + this.mDisableEvents.Add((string)item); + } + } + } + } + else + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Get remote config failed: " + ThinkingSDKJSON.Serialize(result)); + } + } + catch (Exception ex) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Get remote config failed: " + ex.Message); + } + if (mCallback != null) + { + mCallback(); + } + } + + private IEnumerator GetWithFORM (string url, string appId, Dictionary param, ResponseHandle responseHandle) { + yield return ThinkingSDKBaseRequest.GetWithFORM_2(this.mConfigUrl,this.mToken,param,responseHandle); + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKConfig.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKConfig.cs.meta new file mode 100644 index 0000000..243dc8d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 472854a90c78a4dd4a16616ba3d3437d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKPublicConfig.cs b/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKPublicConfig.cs new file mode 100644 index 0000000..9c95ea9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKPublicConfig.cs @@ -0,0 +1,52 @@ +namespace ThinkingSDK.PC.Config +{ + public class ThinkingSDKPublicConfig + { + // Whether to print log + bool isPrintLog; + // sdk version + string version = "1.0"; + // sdk name + string name = "Unity"; + private static readonly ThinkingSDKPublicConfig config = null; + + static ThinkingSDKPublicConfig() + { + config = new ThinkingSDKPublicConfig(); + } + + private static ThinkingSDKPublicConfig GetConfig() + { + return config; + } + public ThinkingSDKPublicConfig() + { + isPrintLog = false; + } + public static void SetIsPrintLog(bool isPrint) + { + GetConfig().isPrintLog = isPrint; + } + public static bool IsPrintLog() + { + return GetConfig().isPrintLog; + } + public static void SetVersion(string libVersion) + { + GetConfig().version = libVersion; + } + public static void SetName(string libName) + { + GetConfig().name = libName; + } + public static string Version() + { + return GetConfig().version; + } + public static string Name() + { + return GetConfig().name; + } + + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKPublicConfig.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKPublicConfig.cs.meta new file mode 100644 index 0000000..d514b1a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Config/ThinkingSDKPublicConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47e9c09a25d664062bd8264d072b2f34 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Constant.meta b/sdk-intergration/Packages/TA/Plugins/PC/Constant.meta new file mode 100644 index 0000000..416a115 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Constant.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e5cb18d25c15d4fca82ba5c10c117b9e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Constant/ThinkingSDKConstant.cs b/sdk-intergration/Packages/TA/Plugins/PC/Constant/ThinkingSDKConstant.cs new file mode 100644 index 0000000..679f81e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Constant/ThinkingSDKConstant.cs @@ -0,0 +1,116 @@ +using System.Collections.Generic; + +namespace ThinkingSDK.PC.Constant +{ + public delegate void ResponseHandle(Dictionary result = null); + public class ThinkingSDKConstant + { + + // current platform + public static readonly string PLATFORM = "PC"; + // date format style + public static readonly string TIME_PATTERN = "{0:yyyy-MM-dd HH:mm:ss.fff}"; + + // event type + public static readonly string TYPE = "#type"; + // event time + public static readonly string TIME = "#time"; + // distinct ID + public static readonly string DISTINCT_ID = "#distinct_id"; + // event name + public static readonly string EVENT_NAME = "#event_name"; + // account ID + public static readonly string ACCOUNT_ID = "#account_id"; + // event properties + public static readonly string PROPERTIES = "properties"; + // network type + public static readonly string NETWORK_TYPE = "#network_type"; + // sdk version + public static readonly string LIB_VERSION = "#lib_version"; + // carrier name + public static readonly string CARRIER = "#carrier"; + // sdk name + public static readonly string LIB = "#lib"; + // os name + public static readonly string OS = "#os"; + // device ID + public static readonly string DEVICE_ID = "#device_id"; + // device screen height + public static readonly string SCREEN_HEIGHT = "#screen_height"; + //device screen width + public static readonly string SCREEN_WIDTH = "#screen_width"; + // device manufacturer + public static readonly string MANUFACTURE = "#manufacturer"; + // device model + public static readonly string DEVICE_MODEL = "#device_model"; + // device system language + public static readonly string SYSTEM_LANGUAGE = "#system_language"; + // os version + public static readonly string OS_VERSION = "#os_version"; + // app version + public static readonly string APP_VERSION = "#app_version"; + // app bundle ID + public static readonly string APP_BUNDLEID = "#bundle_id"; + // zone offset + public static readonly string ZONE_OFFSET = "#zone_offset"; + // project ID + public static readonly string APPID = "#app_id"; + // unique ID for the event + public static readonly string UUID = "#uuid"; + // first event ID + public static readonly string FIRST_CHECK_ID = "#first_check_id"; + // special event ID + public static readonly string EVENT_ID = "#event_id"; + // random ID + public static readonly string RANDOM_ID = "RANDDOM_ID"; + // random ID(WebGL) + public static readonly string RANDOM_DEVICE_ID = "RANDOM_DEVICE_ID"; + // event duration + public static readonly string DURATION = "#duration"; + // flush time + public static readonly string FLUSH_TIME = "#flush_time"; + // request data + public static readonly string REQUEST_DATA = "data"; + + // super properties + public static readonly string SUPER_PROPERTY = "super_properties"; + + // user properties action + public static readonly string USER_ADD = "user_add"; + public static readonly string USER_SET = "user_set"; + public static readonly string USER_SETONCE = "user_setOnce"; + public static readonly string USER_UNSET = "user_unset"; + public static readonly string USER_DEL = "user_del"; + public static readonly string USER_APPEND = "user_append"; + public static readonly string USER_UNIQ_APPEND = "user_uniq_append"; + + // Whether to pause data reporting + public static readonly string ENABLE_TRACK = "enable_track"; + // Whether to stop data reporting + public static readonly string OPT_TRACK = "opt_track"; + // Whether the installation is recorded + public static readonly string IS_INSTALL = "is_install"; + + // app install event + public static readonly string INSTALL_EVENT = "ta_app_install"; + // app start event + public static readonly string START_EVENT = "ta_app_start"; + // app end event + public static readonly string END_EVENT = "ta_app_end"; + // app crash event + public static readonly string CRASH_EVENT = "ta_app_crash"; + // app crash reason + public static readonly string CRASH_REASON = "#app_crashed_reason"; + // scene load + public static readonly string APP_SCENE_LOAD = "ta_scene_loaded"; + // scene unload + public static readonly string APP_SCENE_UNLOAD = "ta_scene_unloaded"; + + + + + + + + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Constant/ThinkingSDKConstant.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Constant/ThinkingSDKConstant.cs.meta new file mode 100644 index 0000000..0271e3a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Constant/ThinkingSDKConstant.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 361e81159859b41d797fc238c8f55f17 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel.meta b/sdk-intergration/Packages/TA/Plugins/PC/DataModel.meta new file mode 100644 index 0000000..48cac20 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a138ac43e601b433093a111aff909263 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKBaseData.cs b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKBaseData.cs new file mode 100644 index 0000000..4fceb96 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKBaseData.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using ThinkingSDK.PC.Time; + +namespace ThinkingSDK.PC.DataModel +{ + public abstract class ThinkingSDKBaseData + { + // event type + private string mType; + // event time + private ThinkingSDKTimeInter mTime; + // distinct ID + private string mDistinctID; + // event name + private string mEventName; + // account ID + private string mAccountID; + + // unique ID for the event + private string mUUID; + private Dictionary mProperties = new Dictionary(); + public Dictionary Properties() + { + return mProperties; + } + public void SetEventName(string eventName) + { + this.mEventName = eventName; + } + public void SetEventType(string eventType) + { + this.mType = eventType; + } + public string EventName() + { + return this.mEventName; + } + public void SetTime(ThinkingSDKTimeInter time) + { + this.mTime = time; + } + public ThinkingSDKTimeInter EventTime() + { + return this.mTime; + } + public void SetDataType(string type) + { + this.mType = type; + } + virtual public String GetDataType() + { + return this.mType; + } + public string AccountID() + { + return this.mAccountID; + } + public string DistinctID() + { + return this.mDistinctID; + } + public void SetAccountID(string accuntID) + { + this.mAccountID = accuntID; + } + public void SetDistinctID(string distinctID) + { + this.mDistinctID = distinctID; + } + public string UUID() + { + return this.mUUID; + } + public ThinkingSDKBaseData() { } + public ThinkingSDKBaseData(ThinkingSDKTimeInter time,string eventName) + { + this.SetBaseData(eventName); + this.SetTime(time); + } + public ThinkingSDKBaseData(string eventName) + { + this.SetBaseData(eventName); + } + public void SetBaseData(string eventName) + { + this.mEventName = eventName; + this.mUUID = System.Guid.NewGuid().ToString(); + } + + public ThinkingSDKBaseData(ThinkingSDKTimeInter time, string eventName, Dictionary properties):this(time,eventName) + { + if (properties != null) + { + this.SetProperties(properties); + } + } + + abstract public Dictionary ToDictionary(); + public void SetProperties(Dictionary properties,bool isOverwrite = true) + { + if (isOverwrite) + { + foreach (KeyValuePair kv in properties) + { + mProperties[kv.Key] = kv.Value; + + } + } + else + { + foreach (KeyValuePair kv in properties) + { + if (!mProperties.ContainsKey(kv.Key)) + { + mProperties[kv.Key] = kv.Value; + } + + } + } + + } + + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKBaseData.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKBaseData.cs.meta new file mode 100644 index 0000000..bf4ed94 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKBaseData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69b1ae68ce0724b4290b99fb1920ffca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKEventData.cs b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKEventData.cs new file mode 100644 index 0000000..3d90731 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKEventData.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.Time; + +namespace ThinkingSDK.PC.DataModel +{ + public class ThinkingSDKEventData:ThinkingSDKBaseData + { + private DateTime mEventTime; + private TimeZoneInfo mTimeZone; + private float mDuration; + private static Dictionary mData; + public void SetEventTime(DateTime dateTime) + { + this.mEventTime = dateTime; + } + public void SetTimeZone(TimeZoneInfo timeZone) + { + this.mTimeZone = timeZone; + } + //public DateTime EventTime() + //{ + // return this.mEventTime; + //} + public DateTime Time() + { + return mEventTime; + } + public ThinkingSDKEventData(string eventName) : base(eventName) + { + } + + public ThinkingSDKEventData(ThinkingSDKTimeInter time, string eventName):base(time,eventName) + { + } + public ThinkingSDKEventData(ThinkingSDKTimeInter time, string eventName, Dictionary properties):base(time,eventName,properties) + { + } + public override string GetDataType() + { + return "track"; + } + public void SetDuration(float duration) + { + this.mDuration = duration; + } + + public override Dictionary ToDictionary() + { + if (mData == null) + { + mData = new Dictionary(); + } + else + { + mData.Clear(); + } + mData[ThinkingSDKConstant.TYPE] = GetDataType(); + mData[ThinkingSDKConstant.TIME] = this.EventTime().GetTime(this.mTimeZone); + mData[ThinkingSDKConstant.DISTINCT_ID] = this.DistinctID(); + if (!string.IsNullOrEmpty(this.EventName())) + { + mData[ThinkingSDKConstant.EVENT_NAME] = this.EventName(); + } + if (!string.IsNullOrEmpty(this.AccountID())) + { + mData[ThinkingSDKConstant.ACCOUNT_ID] = this.AccountID(); + } + mData[ThinkingSDKConstant.UUID] = this.UUID(); + Dictionary properties = this.Properties(); + properties[ThinkingSDKConstant.ZONE_OFFSET] = this.EventTime().GetZoneOffset(this.mTimeZone); + if (mDuration != 0) + { + properties[ThinkingSDKConstant.DURATION] = mDuration; + } + mData[ThinkingSDKConstant.PROPERTIES] = properties; + + return mData; + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKEventData.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKEventData.cs.meta new file mode 100644 index 0000000..f3b1fa0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKEventData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3d815c0c9730f473387bb0c0cd83098c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs new file mode 100644 index 0000000..b4b0669 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.Utils; +namespace ThinkingSDK.PC.DataModel +{ + public class ThinkingSDKFirstEvent:ThinkingSDKEventData + { + private string mFirstCheckId; + public ThinkingSDKFirstEvent(string eventName):base(eventName) + { + + } + public void SetFirstCheckId(string firstCheckId) + { + mFirstCheckId = firstCheckId; + } + public string FirstCheckId() + { + if (string.IsNullOrEmpty(mFirstCheckId)) + { + return ThinkingSDKDeviceInfo.DeviceID(); + } + else + { + return mFirstCheckId; + } + } + override public Dictionary ToDictionary() + { + Dictionary dictionary = base.ToDictionary(); + dictionary[ThinkingSDKConstant.FIRST_CHECK_ID] = FirstCheckId(); + return dictionary; + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs.meta new file mode 100644 index 0000000..e177423 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 285f00076791947c8a99103da1a2653d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs new file mode 100644 index 0000000..ec2c1b2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using ThinkingSDK.PC.Constant; + +namespace ThinkingSDK.PC.DataModel +{ + public class ThinkingSDKOverWritableEvent:ThinkingSDKEventData + { + private string mEventID; + public ThinkingSDKOverWritableEvent(string eventName,string eventID) : base(eventName) + { + this.mEventID = eventID; + } + public override string GetDataType() + { + return "track_overwrite"; + } + override public Dictionary ToDictionary() + { + Dictionary dictionary = base.ToDictionary(); + dictionary[ThinkingSDKConstant.EVENT_ID] = mEventID; + return dictionary; + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs.meta new file mode 100644 index 0000000..b4ba599 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9609b6c1610254c6795e60fe0ce7a92b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs new file mode 100644 index 0000000..da8139c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using ThinkingSDK.PC.Constant; + +namespace ThinkingSDK.PC.DataModel +{ + public class ThinkingSDKUpdateEvent:ThinkingSDKEventData + { + + private string mEventID; + public ThinkingSDKUpdateEvent(string eventName, string eventID) : base(eventName) + { + this.mEventID = eventID; + } + public override string GetDataType() + { + return "track_update"; + } + override public Dictionary ToDictionary() + { + Dictionary dictionary = base.ToDictionary(); + dictionary[ThinkingSDKConstant.EVENT_ID] = mEventID; + return dictionary; + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs.meta new file mode 100644 index 0000000..a6ac63d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 25db2db9cd0d24311b5c5db04b42a604 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUserData.cs b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUserData.cs new file mode 100644 index 0000000..652721d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUserData.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.Time; + +namespace ThinkingSDK.PC.DataModel +{ + public class ThinkingSDKUserData:ThinkingSDKBaseData + { + public ThinkingSDKUserData(ThinkingSDKTimeInter time,string eventType, Dictionary properties) + { + this.SetEventType(eventType); + this.SetTime(time); + this.SetBaseData(null); + this.SetProperties(properties); + } + override public Dictionary ToDictionary() + { + Dictionary data = new Dictionary(); + data[ThinkingSDKConstant.TYPE] = GetDataType(); + data[ThinkingSDKConstant.TIME] = EventTime().GetTime(null); + data[ThinkingSDKConstant.DISTINCT_ID] = DistinctID(); + if (!string.IsNullOrEmpty(AccountID())) + { + data[ThinkingSDKConstant.ACCOUNT_ID] = AccountID(); + } + data[ThinkingSDKConstant.UUID] = UUID(); + data[ThinkingSDKConstant.PROPERTIES] = Properties(); + return data; + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUserData.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUserData.cs.meta new file mode 100644 index 0000000..74880bb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/DataModel/ThinkingSDKUserData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 890bcbb20acb741689b72692aaf6f60a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Main.meta b/sdk-intergration/Packages/TA/Plugins/PC/Main.meta new file mode 100644 index 0000000..7a6a436 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Main.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3166be662c1c1445387662fcb7118be5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Main/LightThinkingSDKInstance.cs b/sdk-intergration/Packages/TA/Plugins/PC/Main/LightThinkingSDKInstance.cs new file mode 100644 index 0000000..b5e421a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Main/LightThinkingSDKInstance.cs @@ -0,0 +1,97 @@ +using ThinkingSDK.PC.Config; +using System.Collections.Generic; +using ThinkingSDK.PC.Utils; +using UnityEngine; + +namespace ThinkingSDK.PC.Main +{ + public class LightThinkingSDKInstance : ThinkingSDKInstance + { + public LightThinkingSDKInstance(string appId, string server, ThinkingSDKConfig config, MonoBehaviour mono = null) : base(appId, server, null, config, mono) + { + } + public override void Identifiy(string distinctID) + { + if (IsPaused()) + { + return; + } + if (!string.IsNullOrEmpty(distinctID)) + { + this.mDistinctID = distinctID; + } + } + public override string DistinctId() + { + if (string.IsNullOrEmpty(this.mDistinctID)) + { + this.mDistinctID = ThinkingSDKUtil.RandomID(false); + } + return this.mDistinctID; + } + public override void Login(string accountID) + { + if (IsPaused()) + { + return; + } + if (!string.IsNullOrEmpty(accountID)) + { + this.mAccountID = accountID; + } + } + public override string AccountID() + { + return this.mAccountID; + } + public override void Logout() + { + if (IsPaused()) + { + return; + } + this.mAccountID = ""; + } + public override void SetSuperProperties(Dictionary superProperties) + { + if (IsPaused()) + { + return; + } + ThinkingSDKUtil.AddDictionary(this.mSupperProperties, superProperties); + } + public override void UnsetSuperProperty(string propertyKey) + { + if (IsPaused()) + { + return; + } + if (this.mSupperProperties.ContainsKey(propertyKey)) + { + this.mSupperProperties.Remove(propertyKey); + } + } + public override Dictionary SuperProperties() + { + return this.mSupperProperties; + } + public override void ClearSuperProperties() + { + if (IsPaused()) + { + return; + } + this.mSupperProperties.Clear(); + } + public override void EnableAutoTrack(TDAutoTrackEventType events, Dictionary properties) + { + } + public override void SetAutoTrackProperties(TDAutoTrackEventType events, Dictionary properties) + { + } + public override void Flush() + { + } + } + +} \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Main/LightThinkingSDKInstance.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Main/LightThinkingSDKInstance.cs.meta new file mode 100644 index 0000000..9fba05a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Main/LightThinkingSDKInstance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7bff4ee40c5ed4a0cb4e4afe060d03c2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingPCSDK.cs b/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingPCSDK.cs new file mode 100644 index 0000000..21b038f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingPCSDK.cs @@ -0,0 +1,362 @@ +using System; +using System.Collections.Generic; +using ThinkingSDK.PC.Config; +using ThinkingSDK.PC.DataModel; +using ThinkingSDK.PC.Time; +using ThinkingSDK.PC.Utils; +using UnityEngine; +namespace ThinkingSDK.PC.Main +{ + public class ThinkingPCSDK + { + private ThinkingPCSDK() + { + + } + private static readonly Dictionary Instances = new Dictionary(); + private static readonly Dictionary LightInstances = new Dictionary(); + private static string CurrentAppid; + + private static ThinkingSDKInstance GetInstance(string appId) + { + ThinkingSDKInstance instance = null; + if (!string.IsNullOrEmpty(appId)) + { + appId = appId.Replace(" ", ""); + if (LightInstances.ContainsKey(appId)) + { + instance = LightInstances[appId]; + } + else if (Instances.ContainsKey(appId)) + { + instance = Instances[appId]; + } + } + if (instance == null) + { + instance = Instances[CurrentAppid]; + } + return instance; + } + + public static ThinkingSDKInstance CurrentInstance() + { + ThinkingSDKInstance instance = Instances[CurrentAppid]; + return instance; + } + + public static ThinkingSDKInstance Init(string appId, string server, string instanceName, ThinkingSDKConfig config = null, MonoBehaviour mono = null) + { + if (ThinkingSDKUtil.IsEmptyString(appId)) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("appId is empty"); + return null; + } + ThinkingSDKInstance instance = null; + if (!string.IsNullOrEmpty(instanceName)) + { + if (Instances.ContainsKey(instanceName)) + { + instance = Instances[instanceName]; + } + else + { + instance = new ThinkingSDKInstance(appId, server, instanceName, config, mono); + if (string.IsNullOrEmpty(CurrentAppid)) + { + CurrentAppid = instanceName; + } + Instances[instanceName] = instance; + } + } + else + { + if (Instances.ContainsKey(appId)) + { + instance = Instances[appId]; + } + else + { + instance = new ThinkingSDKInstance(appId, server, null, config, mono); + if (string.IsNullOrEmpty(CurrentAppid)) + { + CurrentAppid = appId; + } + Instances[appId] = instance; + } + } + return instance; + } + /// + /// Sets distinct ID + /// + /// + /// + public static void Identifiy(string distinctID, string appId = "") + { + GetInstance(appId).Identifiy(distinctID); + } + + /// + /// Gets distinct ID + /// + /// + /// + public static string DistinctId(string appId = "") + { + return GetInstance(appId).DistinctId(); + } + /// + /// Sets account ID + /// + /// + /// + public static void Login(string accountID,string appId = "") + { + GetInstance(appId).Login(accountID); + } + /// + /// Gets account ID + /// + /// + /// + public static string AccountID(string appId = "") + { + return GetInstance(appId).AccountID(); + } + /// + /// Clear account ID + /// + public static void Logout(string appId = "") + { + GetInstance(appId).Logout(); + } + + /// + /// Enable Auto-tracking Events + /// + /// + /// + public static void EnableAutoTrack(TDAutoTrackEventType events, Dictionary properties, string appId = "") + { + GetInstance(appId).EnableAutoTrack(events, properties); + } + + public static void EnableAutoTrack(TDAutoTrackEventType events, TDAutoTrackEventHandler_PC eventCallback, string appId = "") + { + GetInstance(appId).EnableAutoTrack(events, eventCallback); + } + + public static void SetAutoTrackProperties(TDAutoTrackEventType events, Dictionary properties, string appId = "") + { + GetInstance(appId).SetAutoTrackProperties(events, properties); + } + + public static void Track(string eventName,string appId = "") + { + GetInstance(appId).Track(eventName); + } + public static void Track(string eventName, Dictionary properties, string appId = "") + { + GetInstance(appId).Track(eventName,properties); + } + public static void Track(string eventName, Dictionary properties, DateTime date, string appId = "") + { + GetInstance(appId).Track(eventName, properties, date); + } + public static void Track(string eventName, Dictionary properties, DateTime date, TimeZoneInfo timeZone, string appId = "") + { + GetInstance(appId).Track(eventName, properties, date, timeZone); + } + public static void TrackForAll(string eventName, Dictionary properties) + { + foreach (string appId in Instances.Keys) + { + GetInstance(appId).Track(eventName, properties); + } + } + public static void Track(ThinkingSDKEventData eventModel,string appId = "") + { + GetInstance(appId).Track(eventModel); + } + + public static void Flush (string appId = "") + { + GetInstance(appId).Flush(); + } + //public static void FlushImmediately (string appId = "") + //{ + // GetInstance(appId).FlushImmediately(); + //} + public static void SetSuperProperties(Dictionary superProperties,string appId = "") + { + GetInstance(appId).SetSuperProperties(superProperties); + } + public static void UnsetSuperProperty(string propertyKey, string appId = "") + { + GetInstance(appId).UnsetSuperProperty(propertyKey); + } + public static Dictionary SuperProperties(string appId="") + { + return GetInstance(appId).SuperProperties(); + } + + public static Dictionary PresetProperties(string appId="") + { + return GetInstance(appId).PresetProperties(); + } + + public static void ClearSuperProperties(string appId= "") + { + GetInstance(appId).ClearSuperProperties(); + } + + public static void TimeEvent(string eventName,string appId="") + { + GetInstance(appId).TimeEvent(eventName); + } + public static void TimeEventForAll(string eventName) + { + foreach (string appId in Instances.Keys) + { + GetInstance(appId).TimeEvent(eventName); + } + } + /// + /// Pause Event timing + /// + /// ture: puase timing, false: resume timing + /// event name (null or empty is for all event) + public static void PauseTimeEvent(bool status, string eventName = "", string appId = "") + { + GetInstance(appId).PauseTimeEvent(status, eventName); + } + public static void UserSet(Dictionary properties, string appId = "") + { + GetInstance(appId).UserSet(properties); + } + public static void UserSet(Dictionary properties, DateTime dateTime,string appId = "") + { + GetInstance(appId).UserSet(properties, dateTime); + } + public static void UserUnset(string propertyKey,string appId = "") + { + GetInstance(appId).UserUnset(propertyKey); + } + public static void UserUnset(string propertyKey, DateTime dateTime,string appId = "") + { + GetInstance(appId).UserUnset(propertyKey,dateTime); + } + public static void UserUnset(List propertyKeys, string appId = "") + { + GetInstance(appId).UserUnset(propertyKeys); + } + public static void UserUnset(List propertyKeys, DateTime dateTime, string appId = "") + { + GetInstance(appId).UserUnset(propertyKeys,dateTime); + } + public static void UserSetOnce(Dictionary properties,string appId = "") + { + GetInstance(appId).UserSetOnce(properties); + } + public static void UserSetOnce(Dictionary properties, DateTime dateTime, string appId = "") + { + GetInstance(appId).UserSetOnce(properties,dateTime); + } + public static void UserAdd(Dictionary properties, string appId = "") + { + GetInstance(appId).UserAdd(properties); + } + public static void UserAdd(Dictionary properties, DateTime dateTime, string appId = "") + { + GetInstance(appId).UserAdd(properties,dateTime); + } + public static void UserAppend(Dictionary properties, string appId = "") + { + GetInstance(appId).UserAppend(properties); + } + public static void UserAppend(Dictionary properties, DateTime dateTime, string appId = "") + { + GetInstance(appId).UserAppend(properties,dateTime); + } + public static void UserUniqAppend(Dictionary properties, string appId = "") + { + GetInstance(appId).UserUniqAppend(properties); + } + public static void UserUniqAppend(Dictionary properties, DateTime dateTime, string appId = "") + { + GetInstance(appId).UserUniqAppend(properties,dateTime); + } + public static void UserDelete(string appId="") + { + GetInstance(appId).UserDelete(); + } + public static void UserDelete(DateTime dateTime,string appId = "") + { + GetInstance(appId).UserDelete(dateTime); + } + public static void SetDynamicSuperProperties(TDDynamicSuperPropertiesHandler_PC dynamicSuperProperties, string appId = "") + { + GetInstance(appId).SetDynamicSuperProperties(dynamicSuperProperties); + } + public static void SetTrackStatus(TDTrackStatus status, string appId = "") + { + GetInstance(appId).SetTrackStatus(status); + } + public static void OptTracking(bool optTracking,string appId = "") + { + GetInstance(appId).OptTracking(optTracking); + } + public static void EnableTracking(bool isEnable, string appId = "") + { + GetInstance(appId).EnableTracking(isEnable); + } + public static void OptTrackingAndDeleteUser(string appId = "") + { + GetInstance(appId).OptTrackingAndDeleteUser(); + } + public static string CreateLightInstance() + { + string randomID = System.Guid.NewGuid().ToString("N"); + ThinkingSDKInstance lightInstance = ThinkingSDKInstance.CreateLightInstance(); + LightInstances[randomID] = lightInstance; + return randomID; + } + public static void CalibrateTime(long timestamp) + { + ThinkingSDKTimestampCalibration timestampCalibration = new ThinkingSDKTimestampCalibration(timestamp); + ThinkingSDKInstance.SetTimeCalibratieton(timestampCalibration); + } + public static void CalibrateTimeWithNtp(string ntpServer) + { + ThinkingSDKNTPCalibration ntpCalibration = new ThinkingSDKNTPCalibration(ntpServer); + ThinkingSDKInstance.SetNtpTimeCalibratieton(ntpCalibration); + } + + public static void OnDestory() { + Instances.Clear(); + LightInstances.Clear(); + } + + public static string GetDeviceId() + { + return ThinkingSDKDeviceInfo.DeviceID(); + } + public static void EnableLog(bool isEnable) + { + ThinkingSDKPublicConfig.SetIsPrintLog(isEnable); + } + public static void SetLibName(string name) + { + ThinkingSDKPublicConfig.SetName(name); + } + public static void SetLibVersion(string versionCode) + { + ThinkingSDKPublicConfig.SetVersion(versionCode); + } + public static string TimeString(DateTime dateTime, string appId = "") + { + return GetInstance(appId).TimeString(dateTime); + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingPCSDK.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingPCSDK.cs.meta new file mode 100644 index 0000000..0cb66c0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingPCSDK.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee46cd65b236c4202a800c812df46325 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingSDKInstance.cs b/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingSDKInstance.cs new file mode 100644 index 0000000..fdf463c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingSDKInstance.cs @@ -0,0 +1,786 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using ThinkingSDK.PC.Config; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.DataModel; +using ThinkingSDK.PC.Request; +using ThinkingSDK.PC.Storage; +using ThinkingSDK.PC.TaskManager; +using ThinkingSDK.PC.Time; +using ThinkingSDK.PC.Utils; +using UnityEngine; + +namespace ThinkingSDK.PC.Main +{ + [Flags] + // Auto-tracking Events Type + public enum TDAutoTrackEventType + { + None = 0, + AppStart = 1 << 0, // reporting when the app enters the foreground (ta_app_start) + AppEnd = 1 << 1, // reporting when the app enters the background (ta_app_end) + AppCrash = 1 << 4, // reporting when an uncaught exception occurs (ta_app_crash) + AppInstall = 1 << 5, // reporting when the app is opened for the first time after installation (ta_app_install) + AppSceneLoad = 1 << 6, // reporting when the scene is loaded in the app (ta_scene_loaded) + AppSceneUnload = 1 << 7, // reporting when the scene is unloaded in the app (ta_scene_loaded) + All = AppStart | AppEnd | AppInstall | AppCrash | AppSceneLoad | AppSceneUnload + } + // Data Reporting Status + public enum TDTrackStatus + { + Pause = 1, // pause data reporting + Stop = 2, // stop data reporting, and clear caches + SaveOnly = 3, // data stores in the cache, but not be reported + Normal = 4 // resume data reporting + } + + public interface TDDynamicSuperPropertiesHandler_PC + { + Dictionary GetDynamicSuperProperties_PC(); + } + public interface TDAutoTrackEventHandler_PC + { + Dictionary AutoTrackEventCallback_PC(int type, Dictionaryproperties); + } + public class ThinkingSDKInstance + { + private string mAppid; + private string mServer; + protected string mDistinctID; + protected string mAccountID; + private bool mOptTracking = true; + private Dictionary mTimeEvents = new Dictionary(); + private Dictionary mTimeEventsBefore = new Dictionary(); + private bool mEnableTracking = true; + private bool mEventSaveOnly = false; //data stores in the cache, but not be reported + protected Dictionary mSupperProperties = new Dictionary(); + protected Dictionary> mAutoTrackProperties = new Dictionary>(); + private ThinkingSDKConfig mConfig; + private ThinkingSDKBaseRequest mRequest; + private static ThinkingSDKTimeCalibration mTimeCalibration; + private static ThinkingSDKTimeCalibration mNtpTimeCalibration; + private TDDynamicSuperPropertiesHandler_PC mDynamicProperties; + private ThinkingSDKTask mTask { + get { + return ThinkingSDKTask.SingleTask(); + } + set { + this.mTask = value; + } + } + private static ThinkingSDKInstance mCurrentInstance; + private MonoBehaviour mMono; + private static MonoBehaviour sMono; + private ThinkingSDKAutoTrack mAutoTrack; + + WaitForSeconds flushDelay; + public static void SetTimeCalibratieton(ThinkingSDKTimeCalibration timeCalibration) + { + mTimeCalibration = timeCalibration; + } + public static void SetNtpTimeCalibratieton(ThinkingSDKTimeCalibration timeCalibration) + { + mNtpTimeCalibration = timeCalibration; + } + private ThinkingSDKInstance() + { + + } + private void DefaultData() + { + DistinctId(); + AccountID(); + SuperProperties(); + DefaultTrackState(); + } + public ThinkingSDKInstance(string appId,string server):this(appId,server,null,null) + { + + + } + public ThinkingSDKInstance(string appId, string server, string instanceName, ThinkingSDKConfig config, MonoBehaviour mono = null) + { + this.mMono = mono; + sMono = mono; + if (config == null) + { + this.mConfig = ThinkingSDKConfig.GetInstance(appId, server, instanceName); + } + else + { + this.mConfig = config; + } + this.mConfig.UpdateConfig(mono, ConfigResponseHandle); + this.mAppid = appId; + this.mServer = server; + if (this.mConfig.GetMode() == Mode.NORMAL) + { + this.mRequest = new ThinkingSDKNormalRequest(appId, this.mConfig.NormalURL()); + } + else + { + this.mRequest = new ThinkingSDKDebugRequest(appId,this.mConfig.DebugURL()); + if (this.mConfig.GetMode() == Mode.DEBUG_ONLY) + { + ((ThinkingSDKDebugRequest)this.mRequest).SetDryRun(1); + } + } + DefaultData(); + mCurrentInstance = this; + // dynamic loading ThinkingSDKTask ThinkingSDKAutoTrack + GameObject mThinkingSDKTask = new GameObject("ThinkingSDKTask", typeof(ThinkingSDKTask)); + UnityEngine.Object.DontDestroyOnLoad(mThinkingSDKTask); + + GameObject mThinkingSDKAutoTrack = new GameObject("ThinkingSDKAutoTrack", typeof(ThinkingSDKAutoTrack)); + this.mAutoTrack = (ThinkingSDKAutoTrack) mThinkingSDKAutoTrack.GetComponent(typeof(ThinkingSDKAutoTrack)); + if (!string.IsNullOrEmpty(instanceName)) + { + this.mAutoTrack.SetAppId(instanceName); + } + else + { + this.mAutoTrack.SetAppId(this.mAppid); + } + UnityEngine.Object.DontDestroyOnLoad(mThinkingSDKAutoTrack); + } + private void EventResponseHandle(Dictionary result) + { + int eventCount = 0; + if (result != null) + { + int flushCount = 0; + if (result.ContainsKey("flush_count")) + { + flushCount = (int)result["flush_count"]; + } + if (!string.IsNullOrEmpty(this.mConfig.InstanceName())) + { + eventCount = ThinkingSDKFileJson.DeleteBatchTrackingData(flushCount, this.mConfig.InstanceName()); + } + else + { + eventCount = ThinkingSDKFileJson.DeleteBatchTrackingData(flushCount, this.mAppid); + } + } + mTask.Release(); + if (eventCount > 0) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Flush automatically (" + this.mAppid + ")"); + Flush(); + } + } + private void ConfigResponseHandle(Dictionary result) + { + if (this.mConfig.GetMode() == Mode.NORMAL) + { + flushDelay = new WaitForSeconds(mConfig.mUploadInterval); + sMono.StartCoroutine(WaitAndFlush()); + } + } + public static ThinkingSDKInstance CreateLightInstance() + { + ThinkingSDKInstance lightInstance = new LightThinkingSDKInstance(mCurrentInstance.mAppid, mCurrentInstance.mServer, mCurrentInstance.mConfig, sMono); + return lightInstance; + } + public ThinkingSDKTimeInter GetTime(DateTime dateTime) + { + ThinkingSDKTimeInter time = null; + + if ( dateTime == DateTime.MinValue || dateTime == null) + { + if (mNtpTimeCalibration != null)// check if time calibrated + { + time = new ThinkingSDKCalibratedTime(mNtpTimeCalibration, mConfig.TimeZone()); + } + else if (mTimeCalibration != null)// check if time calibrated + { + time = new ThinkingSDKCalibratedTime(mTimeCalibration, mConfig.TimeZone()); + } + else + { + time = new ThinkingSDKTime(mConfig.TimeZone(), DateTime.Now); + } + } + else + { + time = new ThinkingSDKTime(mConfig.TimeZone(), dateTime); + } + + return time; + } + // sets distisct ID + public virtual void Identifiy(string distinctID) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Setting distinct ID, DistinctId = " + distinctID); + if (IsPaused()) + { + return; + } + if (!string.IsNullOrEmpty(distinctID)) + { + this.mDistinctID = distinctID; + ThinkingSDKFile.SaveData(mAppid, ThinkingSDKConstant.DISTINCT_ID,distinctID); + } + } + public virtual string DistinctId() + { + this.mDistinctID = (string)ThinkingSDKFile.GetData(this.mAppid,ThinkingSDKConstant.DISTINCT_ID, typeof(string)); + if (string.IsNullOrEmpty(this.mDistinctID)) + { + this.mDistinctID = ThinkingSDKUtil.RandomID(); + ThinkingSDKFile.SaveData(this.mAppid, ThinkingSDKConstant.DISTINCT_ID, this.mDistinctID); + } + + return this.mDistinctID; + } + + public virtual void Login(string accountID) + { + if (IsPaused()) + { + return; + } + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Login SDK, AccountId = " + accountID); + if (!string.IsNullOrEmpty(accountID)) + { + this.mAccountID = accountID; + ThinkingSDKFile.SaveData(mAppid, ThinkingSDKConstant.ACCOUNT_ID, accountID); + } + } + public virtual string AccountID() + { + this.mAccountID = (string)ThinkingSDKFile.GetData(this.mAppid,ThinkingSDKConstant.ACCOUNT_ID, typeof(string)); + return this.mAccountID; + } + public virtual void Logout() + { + if (IsPaused()) + { + return; + } + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Logout SDK"); + this.mAccountID = ""; + ThinkingSDKFile.DeleteData(this.mAppid,ThinkingSDKConstant.ACCOUNT_ID); + } + //TODO + public virtual void EnableAutoTrack(TDAutoTrackEventType events, Dictionary properties) + { + this.mAutoTrack.EnableAutoTrack(events, properties, mAppid); + } + public virtual void EnableAutoTrack(TDAutoTrackEventType events, TDAutoTrackEventHandler_PC eventCallback) + { + this.mAutoTrack.EnableAutoTrack(events, eventCallback, mAppid); + } + // sets auto-tracking events properties + public virtual void SetAutoTrackProperties(TDAutoTrackEventType events, Dictionary properties) + { + this.mAutoTrack.SetAutoTrackProperties(events, properties); + } + public void Track(string eventName) + { + Track(eventName, null, DateTime.MinValue); + } + public void Track(string eventName, Dictionary properties) + { + Track(eventName, properties, DateTime.MinValue); + } + public void Track(string eventName, Dictionary properties, DateTime date) + { + Track(eventName, properties, date, null, false); + } + public void Track(string eventName, Dictionary properties, DateTime date, TimeZoneInfo timeZone) + { + Track(eventName, properties, date, timeZone, false); + } + public void Track(string eventName, Dictionary properties, DateTime date, TimeZoneInfo timeZone, bool immediately) + { + ThinkingSDKTimeInter time = GetTime(date); + ThinkingSDKEventData data = new ThinkingSDKEventData(time, eventName, properties); + if (timeZone != null) + { + data.SetTimeZone(timeZone); + } + SendData(data, immediately); + } + private void SendData(ThinkingSDKEventData data) + { + SendData(data, false); + } + private void SendData(ThinkingSDKEventData data, bool immediately) + { + if (this.mDynamicProperties != null) + { + data.SetProperties(this.mDynamicProperties.GetDynamicSuperProperties_PC(),false); + } + if (this.mSupperProperties != null && this.mSupperProperties.Count > 0) + { + data.SetProperties(this.mSupperProperties,false); + } + Dictionary deviceInfo = ThinkingSDKUtil.DeviceInfo(); + foreach (string item in ThinkingSDKUtil.DisPresetProperties) + { + if (deviceInfo.ContainsKey(item)) + { + deviceInfo.Remove(item); + } + } + data.SetProperties(deviceInfo, false); + + float duration = 0; + if (mTimeEvents.ContainsKey(data.EventName())) + { + int beginTime = (int)mTimeEvents[data.EventName()]; + int nowTime = Environment.TickCount; + duration = (float)((nowTime - beginTime) / 1000.0); + mTimeEvents.Remove(data.EventName()); + if (mTimeEventsBefore.ContainsKey(data.EventName())) + { + int beforeTime = (int)mTimeEventsBefore[data.EventName()]; + duration = duration + (float)(beforeTime / 1000.0); + mTimeEventsBefore.Remove(data.EventName()); + } + } + if (duration != 0) + { + data.SetDuration(duration); + } + + SendData((ThinkingSDKBaseData)data, immediately); + } + private void SendData(ThinkingSDKBaseData data) + { + SendData(data, false); + } + private void SendData(ThinkingSDKBaseData data, bool immediately) + { + if (IsPaused()) + { + return; + } + if (!string.IsNullOrEmpty(this.mAccountID)) + { + data.SetAccountID(this.mAccountID); + } + if (string.IsNullOrEmpty(this.mDistinctID)) + { + DistinctId(); + } + data.SetDistinctID(this.mDistinctID); + + if (this.mConfig.IsDisabledEvent(data.EventName())) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Disabled Event: " + data.EventName()); + return; + } + if (this.mConfig.GetMode() == Mode.NORMAL && this.mRequest.GetType() != typeof(ThinkingSDKNormalRequest)) + { + this.mRequest = new ThinkingSDKNormalRequest(this.mAppid, this.mConfig.NormalURL()); + } + + if (immediately) + { + Dictionary dataDic = data.ToDictionary(); + this.mMono.StartCoroutine(mRequest.SendData_2(null, ThinkingSDKJSON.Serialize(dataDic), 1)); + } + else + { + Dictionary dataDic = data.ToDictionary(); + int count = 0; + if (!string.IsNullOrEmpty(this.mConfig.InstanceName())) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Enqueue data: \n" + ThinkingSDKJSON.Serialize(dataDic) + "\n AppId: " + this.mAppid); + count = ThinkingSDKFileJson.EnqueueTrackingData(dataDic, this.mConfig.InstanceName()); + } + else + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Enqueue data: \n" + ThinkingSDKJSON.Serialize(dataDic) + "\n AppId: " + this.mAppid); + count = ThinkingSDKFileJson.EnqueueTrackingData(dataDic, this.mAppid); + } + if (this.mConfig.GetMode() != Mode.NORMAL || count >= this.mConfig.mUploadSize) + { + if (count >= this.mConfig.mUploadSize) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Flush automatically (" + this.mAppid + ")"); + } + Flush(); + } + } + } + + private IEnumerator WaitAndFlush() + { + while (true) + { + yield return flushDelay; + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Flush automatically (" + this.mAppid + ")"); + Flush(); + } + } + + /// + /// flush events data + /// + public virtual void Flush() + { + if (mEventSaveOnly == false) { + mTask.SyncInvokeAllTask(); + + int batchSize = (this.mConfig.GetMode() != Mode.NORMAL) ? 1 : mConfig.mUploadSize; + if (!string.IsNullOrEmpty(this.mConfig.InstanceName())) + { + mTask.StartRequest(mRequest, EventResponseHandle, batchSize, this.mConfig.InstanceName()); + } + else + { + mTask.StartRequest(mRequest, EventResponseHandle, batchSize, this.mAppid); + } + } + } + //public void FlushImmediately() + //{ + // if (mEventSaveOnly == false) + // { + // mTask.SyncInvokeAllTask(); + + // int batchSize = (this.mConfig.GetMode() != Mode.NORMAL) ? 1 : mConfig.mUploadSize; + // string list; + // int eventCount = 0; + // if (!string.IsNullOrEmpty(this.mConfig.InstanceName())) + // { + // list = ThinkingSDKFileJson.DequeueBatchTrackingData(batchSize, this.mConfig.InstanceName(), out eventCount); + // } + // else + // { + // list = ThinkingSDKFileJson.DequeueBatchTrackingData(batchSize, this.mAppid, out eventCount); + // } + // if (eventCount > 0) + // { + // this.mMono.StartCoroutine(mRequest.SendData_2(EventResponseHandle, list, eventCount)); + // } + // } + //} + public void Track(ThinkingSDKEventData eventModel) + { + ThinkingSDKTimeInter time = GetTime(eventModel.Time()); + eventModel.SetTime(time); + SendData(eventModel); + } + + public virtual void SetSuperProperties(Dictionary superProperties) + { + if (IsPaused()) + { + return; + } + Dictionary properties = new Dictionary(); + string propertiesStr = (string)ThinkingSDKFile.GetData(this.mAppid, ThinkingSDKConstant.SUPER_PROPERTY, typeof(string)); + if (!string.IsNullOrEmpty(propertiesStr)) + { + properties = ThinkingSDKJSON.Deserialize(propertiesStr); + } + ThinkingSDKUtil.AddDictionary(properties, superProperties); + this.mSupperProperties = properties; + ThinkingSDKFile.SaveData(this.mAppid, ThinkingSDKConstant.SUPER_PROPERTY, ThinkingSDKJSON.Serialize(this.mSupperProperties)); + } + public virtual void UnsetSuperProperty(string propertyKey) + { + if (IsPaused()) + { + return; + } + Dictionary properties = new Dictionary(); + string propertiesStr = (string)ThinkingSDKFile.GetData(this.mAppid, ThinkingSDKConstant.SUPER_PROPERTY, typeof(string)); + if (!string.IsNullOrEmpty(propertiesStr)) + { + properties = ThinkingSDKJSON.Deserialize(propertiesStr); + } + if (properties.ContainsKey(propertyKey)) + { + properties.Remove(propertyKey); + } + this.mSupperProperties = properties; + ThinkingSDKFile.SaveData(this.mAppid, ThinkingSDKConstant.SUPER_PROPERTY, ThinkingSDKJSON.Serialize(this.mSupperProperties)); + } + public virtual Dictionary SuperProperties() + { + string propertiesStr = (string)ThinkingSDKFile.GetData(this.mAppid, ThinkingSDKConstant.SUPER_PROPERTY, typeof(string)); + if (!string.IsNullOrEmpty(propertiesStr)) + { + this.mSupperProperties = ThinkingSDKJSON.Deserialize(propertiesStr); + } + return this.mSupperProperties; + } + public Dictionary PresetProperties() + { + Dictionary presetProperties = new Dictionary(); + presetProperties[ThinkingSDKConstant.DEVICE_ID] = ThinkingSDKDeviceInfo.DeviceID(); + presetProperties[ThinkingSDKConstant.CARRIER] = ThinkingSDKDeviceInfo.Carrier(); + presetProperties[ThinkingSDKConstant.OS] = ThinkingSDKDeviceInfo.OS(); + presetProperties[ThinkingSDKConstant.SCREEN_HEIGHT] = ThinkingSDKDeviceInfo.ScreenHeight(); + presetProperties[ThinkingSDKConstant.SCREEN_WIDTH] = ThinkingSDKDeviceInfo.ScreenWidth(); + presetProperties[ThinkingSDKConstant.MANUFACTURE] = ThinkingSDKDeviceInfo.Manufacture(); + presetProperties[ThinkingSDKConstant.DEVICE_MODEL] = ThinkingSDKDeviceInfo.DeviceModel(); + presetProperties[ThinkingSDKConstant.SYSTEM_LANGUAGE] = ThinkingSDKDeviceInfo.MachineLanguage(); + presetProperties[ThinkingSDKConstant.OS_VERSION] = ThinkingSDKDeviceInfo.OSVersion(); + presetProperties[ThinkingSDKConstant.NETWORK_TYPE] = ThinkingSDKDeviceInfo.NetworkType(); + presetProperties[ThinkingSDKConstant.APP_BUNDLEID] = ThinkingSDKAppInfo.AppIdentifier(); + presetProperties[ThinkingSDKConstant.APP_VERSION] = ThinkingSDKAppInfo.AppVersion(); + presetProperties[ThinkingSDKConstant.ZONE_OFFSET] = ThinkingSDKUtil.ZoneOffset(DateTime.Now, this.mConfig.TimeZone()); + + return presetProperties; + } + public virtual void ClearSuperProperties() + { + if (IsPaused()) + { + return; + } + this.mSupperProperties.Clear(); + ThinkingSDKFile.DeleteData(this.mAppid,ThinkingSDKConstant.SUPER_PROPERTY); + } + + public void TimeEvent(string eventName) + { + if (!mTimeEvents.ContainsKey(eventName)) + { + mTimeEvents.Add(eventName, Environment.TickCount); + } + } + /// + /// Pause Event timing + /// + /// ture: puase timing, false: resume timing + /// event name (null or empty is for all event) + public void PauseTimeEvent(bool status, string eventName = "") + { + if (string.IsNullOrEmpty(eventName)) + { + string[] eventNames = new string[mTimeEvents.Keys.Count]; + mTimeEvents.Keys.CopyTo(eventNames, 0); + for (int i=0; i< eventNames.Length; i++) + { + string key = eventNames[i]; + if (status == true) + { + int startTime = int.Parse(mTimeEvents[key].ToString()); + int pauseTime = Environment.TickCount; + int duration = pauseTime - startTime; + if (mTimeEventsBefore.ContainsKey(key)) + { + duration = duration + int.Parse(mTimeEventsBefore[key].ToString()); + } + mTimeEventsBefore[key] = duration; + } + else + { + mTimeEvents[key] = Environment.TickCount; + } + } + } + else + { + if (status == true) + { + int startTime = int.Parse(mTimeEvents[eventName].ToString()); + int pauseTime = Environment.TickCount; + int duration = pauseTime - startTime; + mTimeEventsBefore[eventName] = duration; + } + else + { + mTimeEvents[eventName] = Environment.TickCount; + } + } + } + public void UserSet(Dictionary properties) + { + UserSet(properties, DateTime.MinValue); + } + public void UserSet(Dictionary properties,DateTime dateTime) + { + ThinkingSDKTimeInter time = GetTime(dateTime); + ThinkingSDKUserData data = new ThinkingSDKUserData(time, ThinkingSDKConstant.USER_SET, properties); + SendData(data); + } + public void UserUnset(string propertyKey) + { + UserUnset(propertyKey, DateTime.MinValue); + } + public void UserUnset(string propertyKey, DateTime dateTime) + { + ThinkingSDKTimeInter time = GetTime(dateTime); + Dictionary properties = new Dictionary(); + properties[propertyKey] = 0; + ThinkingSDKUserData data = new ThinkingSDKUserData(time, ThinkingSDKConstant.USER_UNSET, properties); + SendData(data); + } + public void UserUnset(List propertyKeys) + { + UserUnset(propertyKeys,DateTime.MinValue); + } + public void UserUnset(List propertyKeys, DateTime dateTime) + { + ThinkingSDKTimeInter time = GetTime(dateTime); + Dictionary properties = new Dictionary(); + foreach (string key in propertyKeys) + { + properties[key] = 0; + } + ThinkingSDKUserData data = new ThinkingSDKUserData(time, ThinkingSDKConstant.USER_UNSET, properties); + SendData(data); + } + public void UserSetOnce(Dictionary properties) + { + UserSetOnce(properties, DateTime.MinValue); + } + public void UserSetOnce(Dictionary properties, DateTime dateTime) + { + ThinkingSDKTimeInter time = GetTime(dateTime); + ThinkingSDKUserData data = new ThinkingSDKUserData(time, ThinkingSDKConstant.USER_SETONCE, properties); + SendData(data); + } + public void UserAdd(Dictionary properties) + { + UserAdd(properties, DateTime.MinValue); + } + public void UserAdd(Dictionary properties, DateTime dateTime) + { + ThinkingSDKTimeInter time = GetTime(dateTime); + ThinkingSDKUserData data = new ThinkingSDKUserData(time, ThinkingSDKConstant.USER_ADD, properties); + SendData(data); + } + public void UserAppend(Dictionary properties) + { + UserAppend(properties, DateTime.MinValue); + } + public void UserAppend(Dictionary properties, DateTime dateTime) + { + ThinkingSDKTimeInter time = GetTime(dateTime); + ThinkingSDKUserData data = new ThinkingSDKUserData(time, ThinkingSDKConstant.USER_APPEND, properties); + SendData(data); + } + public void UserUniqAppend(Dictionary properties) + { + UserUniqAppend(properties, DateTime.MinValue); + } + public void UserUniqAppend(Dictionary properties, DateTime dateTime) + { + ThinkingSDKTimeInter time = GetTime(dateTime); + ThinkingSDKUserData data = new ThinkingSDKUserData(time, ThinkingSDKConstant.USER_UNIQ_APPEND, properties); + SendData(data); + } + public void UserDelete() + { + UserDelete(DateTime.MinValue); + } + public void UserDelete(DateTime dateTime) + { + ThinkingSDKTimeInter time = GetTime(dateTime); + Dictionary properties = new Dictionary(); + ThinkingSDKUserData data = new ThinkingSDKUserData(time, ThinkingSDKConstant.USER_DEL,properties); + SendData(data); + } + public void SetDynamicSuperProperties(TDDynamicSuperPropertiesHandler_PC dynamicSuperProperties) + { + if (IsPaused()) + { + return; + } + this.mDynamicProperties = dynamicSuperProperties; + } + protected bool IsPaused() + { + bool mIsPaused = !mEnableTracking || !mOptTracking; + if (mIsPaused) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("SDK Track status is Pause or Stop"); + } + return mIsPaused; + } + + public void SetTrackStatus(TDTrackStatus status) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Change Status to " + status); + switch (status) + { + case TDTrackStatus.Pause: + mEventSaveOnly = false; + OptTracking(true); + EnableTracking(false); + break; + case TDTrackStatus.Stop: + mEventSaveOnly = false; + EnableTracking(true); + OptTracking(false); + break; + case TDTrackStatus.SaveOnly: + mEventSaveOnly = true; + EnableTracking(true); + OptTracking(true); + break; + case TDTrackStatus.Normal: + default: + mEventSaveOnly = false; + OptTracking(true); + EnableTracking(true); + Flush(); + break; + } + } + + public void OptTracking(bool optTracking) + { + mOptTracking = optTracking; + int opt = optTracking ? 1 : 0; + ThinkingSDKFile.SaveData(mAppid, ThinkingSDKConstant.OPT_TRACK, opt); + if (!optTracking) + { + ThinkingSDKFile.DeleteData(mAppid, ThinkingSDKConstant.ACCOUNT_ID); + ThinkingSDKFile.DeleteData(mAppid, ThinkingSDKConstant.DISTINCT_ID); + ThinkingSDKFile.DeleteData(mAppid, ThinkingSDKConstant.SUPER_PROPERTY); + this.mAccountID = null; + this.mDistinctID = null; + this.mSupperProperties = new Dictionary(); + ThinkingSDKFileJson.DeleteAllTrackingData(mAppid); + } + } + public void EnableTracking(bool isEnable) + { + mEnableTracking = isEnable; + int enable = isEnable ? 1 : 0; + ThinkingSDKFile.SaveData(mAppid, ThinkingSDKConstant.ENABLE_TRACK,enable); + } + private void DefaultTrackState() + { + object enableTrack = ThinkingSDKFile.GetData(mAppid, ThinkingSDKConstant.ENABLE_TRACK, typeof(int)); + object optTrack = ThinkingSDKFile.GetData(mAppid, ThinkingSDKConstant.OPT_TRACK, typeof(int)); + if (enableTrack != null) + { + this.mEnableTracking = ((int)enableTrack) == 1; + } + else + { + this.mEnableTracking = true; + } + if (optTrack != null) + { + this.mOptTracking = ((int)optTrack) == 1; + } + else + { + this.mOptTracking = true; + } + } + public void OptTrackingAndDeleteUser() + { + UserDelete(); + OptTracking(false); + } + public string TimeString(DateTime dateTime) + { + return ThinkingSDKUtil.FormatDate(dateTime, mConfig.TimeZone()); + } + } +} + diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingSDKInstance.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingSDKInstance.cs.meta new file mode 100644 index 0000000..02bd125 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Main/ThinkingSDKInstance.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8287bb3311be54f52be240639203175f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Request.meta b/sdk-intergration/Packages/TA/Plugins/PC/Request.meta new file mode 100644 index 0000000..4d9cce4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Request.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: db5e3ec02eff54121adead1f781264b7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKBaseRequest.cs b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKBaseRequest.cs new file mode 100644 index 0000000..9a67146 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKBaseRequest.cs @@ -0,0 +1,155 @@ +using System; +using System.Net; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +using System.Collections.Generic; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.Utils; +using System.IO; +using UnityEngine.Networking; +using System.Collections; +using ThinkingSDK.PC.Config; + +namespace ThinkingSDK.PC.Request +{ + /* + * Enumerate the form of data reported by post, and the enumeration value represents json and form forms + */ + enum POST_TYPE { JSON, FORM }; + public abstract class ThinkingSDKBaseRequest + { + private string mAppid; + private string mURL; + private string mData; + public ThinkingSDKBaseRequest(string appId, string url, string data) + { + mAppid = appId; + mURL = url; + mData = data; + } + public ThinkingSDKBaseRequest(string appId, string url) + { + mAppid = appId; + mURL = url; + } + public void SetData(string data) + { + this.mData = data; + } + public string APPID() { + return mAppid; + } + public string URL() + { + return mURL; + } + public string Data() + { + return mData; + } + /** + * initialization interface + */ + public static void GetConfig(string url,ResponseHandle responseHandle) + { + if (!ThinkingSDKUtil.IsValiadURL(url)) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Invalid Url:\n" + url); + } + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.Method = "GET"; + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + var responseResult = new StreamReader(response.GetResponseStream()).ReadToEnd(); + if (responseResult != null) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Request URL:\n"+url); + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Response:\n"+responseResult); + } + } + + public bool MyRemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + { + bool isOk = true; + // If there are errors in the certificate chain, + // look at each error to determine the cause. + if (sslPolicyErrors != SslPolicyErrors.None) { + for (int i=0; i param, ResponseHandle responseHandle) + { + string uri = url + "?appid=" + appId; + if (param != null) + { + uri = uri + "&data=" + ThinkingSDKJSON.Serialize(param); + } + + using (UnityWebRequest webRequest = UnityWebRequest.Get(uri)) + { + webRequest.timeout = 30; + webRequest.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + + //if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Request URL: \n" + uri); + + // Request and wait for the desired page. + yield return webRequest.SendWebRequest(); + + Dictionary resultDict = null; +#if UNITY_2020_1_OR_NEWER + switch (webRequest.result) + { + case UnityWebRequest.Result.ConnectionError: + case UnityWebRequest.Result.DataProcessingError: + case UnityWebRequest.Result.ProtocolError: + //if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Error response: \n" + webRequest.error); + break; + case UnityWebRequest.Result.Success: + string resultText = webRequest.downloadHandler.text; + //if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Response: \n" + resultText); + if (!string.IsNullOrEmpty(resultText)) + { + resultDict = ThinkingSDKJSON.Deserialize(resultText); + } + break; + } +#else + if (webRequest.isHttpError || webRequest.isNetworkError) + { + //if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Error response: \n" + webRequest.error); + } + else + { + string resultText = webRequest.downloadHandler.text; + //if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Response: \n" + resultText); + if (!string.IsNullOrEmpty(resultText)) + { + resultDict = ThinkingSDKJSON.Deserialize(resultText); + } + } +#endif + if (responseHandle != null) + { + responseHandle(resultDict); + } + } + } + } +} + diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKBaseRequest.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKBaseRequest.cs.meta new file mode 100644 index 0000000..c52a4c1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKBaseRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b31966af44764a508681ee9d6e24f7a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKDebugRequest.cs b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKDebugRequest.cs new file mode 100644 index 0000000..8aeb9b2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKDebugRequest.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using ThinkingSDK.PC.Config; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.Utils; +using UnityEngine; +using UnityEngine.Networking; +using System.Collections; + +namespace ThinkingSDK.PC.Request +{ + public class ThinkingSDKDebugRequest:ThinkingSDKBaseRequest + { + private int mDryRun = 0; + private string mDeviceID = ThinkingSDKDeviceInfo.DeviceID(); + public void SetDryRun(int dryRun) + { + mDryRun = dryRun; + } + public ThinkingSDKDebugRequest(string appId, string url, string data):base(appId,url,data) + { + + } + public ThinkingSDKDebugRequest(string appId, string url) : base(appId, url) + { + } + + public override IEnumerator SendData_2(ResponseHandle responseHandle, string data, int eventCount) + { + this.SetData(data); + string uri = this.URL(); + //string content = ThinkingSDKJSON.Serialize(this.Data()[0]); + string content = data.Substring(1,data.Length-2); + + WWWForm form = new WWWForm(); + form.AddField("appid", this.APPID()); + form.AddField("source", "client"); + form.AddField("dryRun", mDryRun); + form.AddField("deviceId", mDeviceID); + form.AddField("data", content); + + using (UnityWebRequest webRequest = UnityWebRequest.Post(uri, form)) + { + webRequest.timeout = 30; + webRequest.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Send event Request:\n " + content + "\n URL = " + uri); + + // Request and wait for the desired page. + yield return webRequest.SendWebRequest(); + + Dictionary resultDict = null; +#if UNITY_2020_1_OR_NEWER + switch (webRequest.result) + { + case UnityWebRequest.Result.ConnectionError: + case UnityWebRequest.Result.DataProcessingError: + case UnityWebRequest.Result.ProtocolError: + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Send event Response Error:\n " + webRequest.error); + break; + case UnityWebRequest.Result.Success: + string resultText = webRequest.downloadHandler.text; + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Send event Response:\n " + resultText); + if (!string.IsNullOrEmpty(resultText)) + { + resultDict = ThinkingSDKJSON.Deserialize(resultText); + } + break; + } +#else + if (webRequest.isHttpError || webRequest.isNetworkError) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Send event Response Error:\n " + webRequest.error); + } + else + { + string resultText = webRequest.downloadHandler.text; + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Send event Response:\n " + resultText); + if (!string.IsNullOrEmpty(resultText)) + { + resultDict = ThinkingSDKJSON.Deserialize(resultText); + } + } +#endif + if (responseHandle != null) + { + if (resultDict != null) + { + resultDict.Add("flush_count", eventCount); + } + responseHandle(resultDict); + } + } + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKDebugRequest.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKDebugRequest.cs.meta new file mode 100644 index 0000000..e8c9592 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKDebugRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7849691904f3c426e81a91572a027863 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKNormalRequest.cs b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKNormalRequest.cs new file mode 100644 index 0000000..082d158 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKNormalRequest.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.Utils; +using UnityEngine.Networking; +using System.Collections; +using ThinkingSDK.PC.Config; + +namespace ThinkingSDK.PC.Request +{ + public class ThinkingSDKNormalRequest:ThinkingSDKBaseRequest + { + public ThinkingSDKNormalRequest(string appId, string url, string data) :base(appId, url, data) + { + } + public ThinkingSDKNormalRequest(string appId, string url) : base(appId, url) + { + } + + public override IEnumerator SendData_2(ResponseHandle responseHandle, string data, int eventCount) + { + this.SetData(data); + string uri = this.URL(); + var flush_time = ThinkingSDKUtil.GetTimeStamp(); + + string content = "{\"#app_id\":\"" + this.APPID() + "\",\"data\":" + data + ",\"#flush_time\":" + flush_time + "}"; + string encodeContent = Encode(content); + byte[] contentCompressed = Encoding.UTF8.GetBytes(encodeContent); + + using (UnityWebRequest webRequest = new UnityWebRequest(uri, "POST")) + { + webRequest.timeout = 30; + webRequest.SetRequestHeader("Content-Type", "text/plain"); + webRequest.SetRequestHeader("appid", this.APPID()); + webRequest.SetRequestHeader("TA-Integration-Type", "PC"); + webRequest.SetRequestHeader("TA-Integration-Version", ThinkingSDKPublicConfig.Version()); + webRequest.SetRequestHeader("TA-Integration-Count", "1"); + webRequest.SetRequestHeader("TA-Integration-Extra", "PC"); + webRequest.uploadHandler = (UploadHandler)new UploadHandlerRaw(contentCompressed); + webRequest.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); + + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Send event Request:\n " + content + "\n URL = " + uri); + + // Request and wait for the desired page. + yield return webRequest.SendWebRequest(); + + Dictionary resultDict = null; +#if UNITY_2020_1_OR_NEWER + switch (webRequest.result) + { + case UnityWebRequest.Result.ConnectionError: + case UnityWebRequest.Result.DataProcessingError: + case UnityWebRequest.Result.ProtocolError: + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Send event Response Error:\n " + webRequest.error); + break; + case UnityWebRequest.Result.Success: + string resultText = webRequest.downloadHandler.text; + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Send event Response:\n " + resultText); + if (!string.IsNullOrEmpty(resultText)) + { + resultDict = ThinkingSDKJSON.Deserialize(resultText); + } + break; + } +#else + if (webRequest.isHttpError || webRequest.isNetworkError) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Send event Response Error:\n " + webRequest.error); + } + else + { + string resultText = webRequest.downloadHandler.text; + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Send event Response:\n " + resultText); + if (!string.IsNullOrEmpty(resultText)) + { + resultDict = ThinkingSDKJSON.Deserialize(resultText); + } + } +#endif + if (responseHandle != null) + { + if (resultDict != null) + { + resultDict.Add("flush_count", eventCount); + } + responseHandle(resultDict); + } + } + } + private static string Encode(string inputStr) + { + byte[] inputBytes = Encoding.UTF8.GetBytes(inputStr); + using (var outputStream = new MemoryStream()) + { + using (var gzipStream = new GZipStream(outputStream, CompressionMode.Compress)) + gzipStream.Write(inputBytes, 0, inputBytes.Length); + byte[] output = outputStream.ToArray(); + return Convert.ToBase64String(output); + } + } + + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKNormalRequest.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKNormalRequest.cs.meta new file mode 100644 index 0000000..b1ff204 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Request/ThinkingSDKNormalRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 992a71ad5ae554eb98a712f6a65b3a7f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Storage.meta b/sdk-intergration/Packages/TA/Plugins/PC/Storage.meta new file mode 100644 index 0000000..56bae73 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Storage.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 21f73e209611b40d3a1c87e9418529b2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFile.cs b/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFile.cs new file mode 100644 index 0000000..caca170 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFile.cs @@ -0,0 +1,80 @@ +using System; +using UnityEngine; + +namespace ThinkingSDK.PC.Storage +{ + public class ThinkingSDKFile + { + private static string connectorKey = "_"; + public static string GetKey(string prefix,string key) + { + return prefix + connectorKey + key; + } + public static void SaveData(string prefix, string key, object value) + { + SaveData(GetKey(prefix, key), value); + } + public static void SaveData(string key, object value) + { + if (!string.IsNullOrEmpty(key)) + { + if (value.GetType() == typeof(int)) + { + PlayerPrefs.SetInt(key, (int)value); + } + else if (value.GetType() == typeof(float)) + { + PlayerPrefs.SetFloat(key, (float)value); + } + else if (value.GetType() == typeof(string)) + { + PlayerPrefs.SetString(key, (string)value); + } + PlayerPrefs.Save(); + } + } + public static object GetData(string key, Type type) + { + if (!string.IsNullOrEmpty(key) && PlayerPrefs.HasKey(key)) + { + if (type == typeof(int)) + { + return PlayerPrefs.GetInt(key); + } + else if (type == typeof(float)) + { + return PlayerPrefs.GetFloat(key); + } + else if (type == typeof(string)) + { + return PlayerPrefs.GetString(key); + } + PlayerPrefs.Save(); + } + return null; + + } + public static object GetData(string prefix,string key, Type type) + { + key = GetKey(prefix, key); + return GetData(key, type); + } + + public static void DeleteData(string key) + { + if (!string.IsNullOrEmpty(key)) + { + if (PlayerPrefs.HasKey(key)) + { + PlayerPrefs.DeleteKey(key); + } + } + } + public static void DeleteData(string prefix,string key) + { + key = GetKey(prefix, key); + DeleteData(key); + } + } +} + diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFile.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFile.cs.meta new file mode 100644 index 0000000..8ad1e33 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be6ada75d19854acca2314015d2c6376 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFileJson.cs b/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFileJson.cs new file mode 100644 index 0000000..18e9cd7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFileJson.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using ThinkingSDK.PC.Utils; + +namespace ThinkingSDK.PC.Storage +{ + public class ThinkingSDKFileJson + { + // Save the event, return the number of cached events + internal static int EnqueueTrackingData(Dictionary data, string prefix) + { + int eventId = EventAutoIncrementingID(prefix); + string trackingKey = GetEventKeysPrefix(prefix, eventId); + + var dataJson = ThinkingSDKJSON.Serialize(data); + PlayerPrefs.SetString(trackingKey, dataJson); + IncreaseTrackingDataID(prefix); + int eventCount = EventAutoIncrementingID(prefix) - EventIndexID(prefix); + return eventCount; + } + + // Get event end ID + internal static int EventAutoIncrementingID(string prefix) + { + string mEventAutoIncrementingID = GetEventAutoIncrementingIDKey(prefix); + return PlayerPrefs.HasKey(mEventAutoIncrementingID) ? PlayerPrefs.GetInt(mEventAutoIncrementingID) : 0; + } + + // Auto increment event end ID + private static void IncreaseTrackingDataID(string prefix) + { + int id = EventAutoIncrementingID(prefix); + id += 1; + PlayerPrefs.SetInt(GetEventAutoIncrementingIDKey(prefix), id); + } + + // Reset event end ID + private static void ResetTrackingDataID(string prefix) + { + int id = 0; + PlayerPrefs.SetInt(GetEventAutoIncrementingIDKey(prefix), id); + } + + // Get event start ID + internal static int EventIndexID(string prefix) + { + string eventIndexID = GetEventIndexIDKey(prefix); + return PlayerPrefs.HasKey(eventIndexID) ? PlayerPrefs.GetInt(eventIndexID) : 0; + } + + // Save time start ID + private static void SaveEventIndexID(int indexID, string prefix) + { + string eventIndexID = GetEventIndexIDKey(prefix); + PlayerPrefs.SetInt(eventIndexID, indexID); + } + + // Fetch a specified number of events in batches + internal static string DequeueBatchTrackingData(int batchSize, string prefix, out int eventCount) + { + string batchData = eventBatchPrefix; + List> tempDataList = new List>(); + int dataIndex = EventIndexID(prefix); + int maxIndex = EventAutoIncrementingID(prefix) - 1; + eventCount = 0; + while (eventCount < batchSize && dataIndex <= maxIndex) { + string trackingKey = GetEventKeysPrefix(prefix, dataIndex); + if (PlayerPrefs.HasKey(trackingKey)) { + string dataJson = PlayerPrefs.GetString(trackingKey); + if (eventCount < batchSize - 1 && dataIndex < maxIndex) + { + batchData = batchData + dataJson + eventBatchInfix; + } + else + { + batchData = batchData + dataJson; + } + eventCount++; + } + dataIndex++; + } + + if (eventCount > 0) + { + batchData = batchData + eventBatchSuffix; + return batchData; + } + else + { + return null; + } + } + + // Batch delete the specified number of events and return the remaining number of events + internal static int DeleteBatchTrackingData(int batchSize, string prefix) + { + int deletedCount = 0; + int dataIndex = EventIndexID(prefix); + int maxIndex = EventAutoIncrementingID(prefix) - 1; + while (deletedCount < batchSize && dataIndex <= maxIndex) { + string trackingKey = GetEventKeysPrefix(prefix, dataIndex); + if (PlayerPrefs.HasKey(trackingKey)) { + PlayerPrefs.DeleteKey(trackingKey); + deletedCount++; + } + dataIndex++; + } + SaveEventIndexID(dataIndex, prefix); + + int eventCount = EventAutoIncrementingID(prefix) - EventIndexID(prefix); + return eventCount; + } + + // Batch delete specified events + // internal static void DeleteBatchTrackingData(List> batch, string prefix) { + // foreach(Dictionary data in batch) { + // String id = data["id"].ToString(); + // if (id != null && PlayerPrefs.HasKey(id)) { + // PlayerPrefs.DeleteKey(id); + // } + // } + // } + + // Batch delete all events + internal static int DeleteAllTrackingData(string prefix) + { + DeleteBatchTrackingData(int.MaxValue, prefix); + SaveEventIndexID(0, prefix); + ResetTrackingDataID(prefix); + return 0; + } + + private static string eventKeyInfix = "Event"; + private static string eventIndexIDSuffix = "EventIndexID"; + private static string eventAutoIncrementingIDSuffix = "EventAutoIncrementingID"; + + private static string eventBatchPrefix = "["; + private static string eventBatchInfix = ","; + private static string eventBatchSuffix = "]"; + + private static Dictionary eventKeysPrefix = new Dictionary() { }; + private static Dictionary eventIndexIDKeys = new Dictionary() { }; + private static Dictionary eventAutoIncrementingIDKeys = new Dictionary() { }; + + private static string GetEventKeysPrefix(string prefix, int index) + { + if (eventKeysPrefix.ContainsKey(prefix)) + { + return eventKeysPrefix[prefix] + index; + } + else + { + string eventKey = prefix + eventKeyInfix; + eventKeysPrefix[prefix] = eventKey; + return eventKey + index; + } + } + + private static string GetEventIndexIDKey(string prefix) + { + if (eventIndexIDKeys.ContainsKey(prefix)) + { + return eventIndexIDKeys[prefix]; + } + else + { + string eventKey = prefix + eventIndexIDSuffix; + eventIndexIDKeys[prefix] = eventKey; + return eventKey; + } + } + + private static string GetEventAutoIncrementingIDKey(string prefix) + { + if (eventAutoIncrementingIDKeys.ContainsKey(prefix)) + { + return eventAutoIncrementingIDKeys[prefix]; + } + else + { + string eventKey = prefix + eventAutoIncrementingIDSuffix; + eventAutoIncrementingIDKeys[prefix] = eventKey; + return eventKey; + } + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFileJson.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFileJson.cs.meta new file mode 100644 index 0000000..b08277c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Storage/ThinkingSDKFileJson.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d4bedacbe272d46bda26db9388665562 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/TaskManager.meta b/sdk-intergration/Packages/TA/Plugins/PC/TaskManager.meta new file mode 100644 index 0000000..bd2f83f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/TaskManager.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 372921461f6fe4648827f1a91a1759e1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/TaskManager/ThinkingSDKTask.cs b/sdk-intergration/Packages/TA/Plugins/PC/TaskManager/ThinkingSDKTask.cs new file mode 100644 index 0000000..3d1380e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/TaskManager/ThinkingSDKTask.cs @@ -0,0 +1,130 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using ThinkingSDK.PC.Request; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.Storage; +using ThinkingSDK.PC.Main; + +namespace ThinkingSDK.PC.TaskManager +{ + [DisallowMultipleComponent] + public class ThinkingSDKTask : MonoBehaviour + { + private readonly static object _locker = new object(); + + private List requestList = new List(); + private List responseHandleList = new List(); + private List batchSizeList = new List(); + private List appIdList = new List(); + + + private static ThinkingSDKTask mSingleTask; + + private bool isWaiting = false; + private float updateInterval = 0; + + public static ThinkingSDKTask SingleTask() + { + return mSingleTask; + } + + private void Awake() { + mSingleTask = this; + } + + private void Start() { + } + + private void Update() { + updateInterval += UnityEngine.Time.deltaTime; + if (updateInterval > 0.2) + { + updateInterval = 0; + if (!isWaiting && requestList.Count > 0) + { + WaitOne(); + StartRequestSendData(); + } + } + } + + //private void OnDestroy() + //{ + // ThinkingPCSDK.OnDestory(); + //} + + /// + /// hold signal + /// + public void WaitOne() + { + isWaiting = true; + } + /// + /// release signal + /// + public void Release() + { + isWaiting = false; + } + public void SyncInvokeAllTask() + { + + } + + public void StartRequest(ThinkingSDKBaseRequest mRequest, ResponseHandle responseHandle, int batchSize, string appId) + { + lock(_locker) + { + requestList.Add(mRequest); + responseHandleList.Add(responseHandle); + batchSizeList.Add(batchSize); + appIdList.Add(appId); + } + } + + private void StartRequestSendData() + { + if (requestList.Count > 0) + { + ThinkingSDKBaseRequest mRequest; + ResponseHandle responseHandle; + string list; + int eventCount = 0; + lock (_locker) + { + mRequest = requestList[0]; + responseHandle = responseHandleList[0]; + list = ThinkingSDKFileJson.DequeueBatchTrackingData(batchSizeList[0], appIdList[0], out eventCount); + } + if (mRequest != null) + { + if (eventCount > 0 && list.Length > 0) + { + this.StartCoroutine(this.SendData(mRequest, responseHandle, list, eventCount)); + } + else + { + if (responseHandle != null) + { + responseHandle(null); + } + } + lock(_locker) + { + requestList.RemoveAt(0); + responseHandleList.RemoveAt(0); + batchSizeList.RemoveAt(0); + appIdList.RemoveAt(0); + } + } + + } + } + private IEnumerator SendData(ThinkingSDKBaseRequest mRequest, ResponseHandle responseHandle, string list, int eventCount) { + yield return mRequest.SendData_2(responseHandle, list, eventCount); + } + } +} + diff --git a/sdk-intergration/Packages/TA/Plugins/PC/TaskManager/ThinkingSDKTask.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/TaskManager/ThinkingSDKTask.cs.meta new file mode 100644 index 0000000..ce3d9c5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/TaskManager/ThinkingSDKTask.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb96ead89c0c544608be4e0ad781de78 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/ThinkingSDK.asmdef b/sdk-intergration/Packages/TA/Plugins/PC/ThinkingSDK.asmdef new file mode 100644 index 0000000..4c20b5e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/ThinkingSDK.asmdef @@ -0,0 +1,3 @@ +{ + "name": "ThinkingSDK" +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/ThinkingSDK.asmdef.meta b/sdk-intergration/Packages/TA/Plugins/PC/ThinkingSDK.asmdef.meta new file mode 100644 index 0000000..a78dc3e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/ThinkingSDK.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0a958a7eb80a248e1b8bc4553787c209 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time.meta b/sdk-intergration/Packages/TA/Plugins/PC/Time.meta new file mode 100644 index 0000000..ee5221a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c859b53e53b824a2aa36430c980862cf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/TDTimeout.cs b/sdk-intergration/Packages/TA/Plugins/PC/Time/TDTimeout.cs new file mode 100644 index 0000000..3c2de2c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/TDTimeout.cs @@ -0,0 +1,44 @@ +using UnityEngine; +using System.Collections; +using System; +using System.Threading; + +namespace ThinkingSDK.PC.Time +{ + public class TDTimeout : MonoBehaviour + { + // Use this for initialization + void Start() + { + + } + + // Update is called once per frame + void Update() + { + + } + + public static void SetTimeout(int timeout, Action action, object obj) + { + GameObject gameObject = new GameObject("TDTimeout"); + var tdTimeout = gameObject.AddComponent(); + tdTimeout._setTimeout(timeout, action, obj); + } + + private void _setTimeout(int timeout, Action action, object obj) + { + StartCoroutine(_wait(timeout, action, obj)); + } + + private IEnumerator _wait(int timeout, Action action, object obj) + { + yield return new WaitForSeconds(timeout); + if (action != null) + { + action(obj); + } + Destroy(gameObject); + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/TDTimeout.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Time/TDTimeout.cs.meta new file mode 100644 index 0000000..81367bd --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/TDTimeout.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4c4b6a31edd864c43a12c23d11ac6ddd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs new file mode 100644 index 0000000..cd24363 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs @@ -0,0 +1,43 @@ +using System; +using ThinkingSDK.PC.Utils; + +namespace ThinkingSDK.PC.Time +{ + public class ThinkingSDKCalibratedTime : ThinkingSDKTimeInter + { + private ThinkingSDKTimeCalibration mCalibratedTime; + private long mSystemElapsedRealtime; + private TimeZoneInfo mTimeZone; + private DateTime mDate; + public ThinkingSDKCalibratedTime(ThinkingSDKTimeCalibration calibrateTimeInter,TimeZoneInfo timeZoneInfo) + { + this.mCalibratedTime = calibrateTimeInter; + this.mTimeZone = timeZoneInfo; + this.mDate = mCalibratedTime.NowDate(); + } + public string GetTime(TimeZoneInfo timeZone) + { + if (timeZone == null) + { + return ThinkingSDKUtil.FormatDate(mDate, mTimeZone); + } + else + { + return ThinkingSDKUtil.FormatDate(mDate, timeZone); + } + } + + public double GetZoneOffset(TimeZoneInfo timeZone) + { + if (timeZone == null) + { + return ThinkingSDKUtil.ZoneOffset(mDate, mTimeZone); + } + else + { + return ThinkingSDKUtil.ZoneOffset(mDate, timeZone); + } + } + } + +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs.meta new file mode 100644 index 0000000..4d4dc14 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 46014ab35934a4a90b8329f357801cdf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKDefinedTime.cs b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKDefinedTime.cs new file mode 100644 index 0000000..654a19e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKDefinedTime.cs @@ -0,0 +1,25 @@ +using System; + +namespace ThinkingSDK.PC.Time +{ + public class ThinkingSDKDefinedTime : ThinkingSDKTimeInter + { + private string mTime; + private double mZoneOffset; + public ThinkingSDKDefinedTime(string time,double zoneOffset) + { + this.mTime = time; + this.mZoneOffset = zoneOffset; + } + public string GetTime(TimeZoneInfo timeZone) + { + return this.mTime; + } + + public double GetZoneOffset(TimeZoneInfo timeZone) + { + return this.mZoneOffset; + } + } +} + diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKDefinedTime.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKDefinedTime.cs.meta new file mode 100644 index 0000000..7cdf3fa --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKDefinedTime.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86eabc3f1cff146f89092cd516702db9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs new file mode 100644 index 0000000..647b7c6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs @@ -0,0 +1,131 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Linq; + +namespace ThinkingSDK.PC.Time +{ + public class ThinkingSDKNTPCalibration : ThinkingSDKTimeCalibration + { + public ThinkingSDKNTPCalibration(string ntpServer) { + double totalMilliseconds = ConvertDateTimeInt(DateTime.UtcNow); + this.mStartTime = (long)totalMilliseconds; + this.mSystemElapsedRealtime = Environment.TickCount; + + // request scoket time + Socket socket = GetNetworkTimeSync(ntpServer, this); + // set scoket timeout + TDTimeout.SetTimeout(3, new Action(ScoketTimeout), (object)socket); + } + + private void ScoketTimeout(object obj) + { + if (obj is Socket) + { + Socket socket = (Socket)obj; + if (socket.Connected == true) + { + socket.Close(); + } + } + } + + protected static new double ConvertDateTimeInt(System.DateTime time) + { + DateTime startTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + return (double)(time - startTime).TotalMilliseconds; + } + + private static Socket GetNetworkTimeSync(string ntpServer, ThinkingSDKTimeCalibration timeCalibration) + { + // NTP message size - 16 bytes of the digest (RFC 2030) + var ntpData = new byte[48]; + + //Setting the Leap Indicator, Version Number and Mode values + ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode) + + var addresses = Dns.GetHostEntry(ntpServer).AddressList; + var addressFirst = addresses.First(e => e.AddressFamily == AddressFamily.InterNetwork); + if (addressFirst == null) + { + addressFirst = addresses[0]; + } + + //The UDP port number assigned to NTP is 123 + var ipEndPoint = new IPEndPoint(addressFirst, 123); + //NTP uses UDP + var socket = new Socket(ipEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + + socket.Connect(ipEndPoint); + + SocketAsyncEventArgs socketAsyncEventArgs = new SocketAsyncEventArgs(); + socketAsyncEventArgs.SetBuffer(ntpData, 0, ntpData.Length); + socketAsyncEventArgs.UserToken = timeCalibration; + socketAsyncEventArgs.RemoteEndPoint = ipEndPoint; + socketAsyncEventArgs.Completed += SocketAsyncEventArgs_Completed; + // send socket request + socket.SendAsync(socketAsyncEventArgs); + + return socket; + } + + private static void SocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs eventArgs) + { + Socket socket = (Socket)sender; + if (eventArgs.SocketError == SocketError.Success) + { + if (eventArgs.LastOperation == SocketAsyncOperation.Send) + { + socket.ReceiveAsync(eventArgs); + } + else if (eventArgs.LastOperation == SocketAsyncOperation.Receive) + { + if (eventArgs.SocketError == SocketError.Success && eventArgs.Buffer.Length > 0) + { + DateTime ntpTime = ParseDateTimeWithNTPData(eventArgs.Buffer); + double totalMilliseconds = ConvertDateTimeInt(ntpTime); + ThinkingSDKTimeCalibration timeCalibration = (ThinkingSDKTimeCalibration)eventArgs.UserToken; + timeCalibration.mStartTime = (long)totalMilliseconds; + } + socket.Close(); + } + else + { + socket.Close(); + } + } + else + { + socket.Close(); + } + } + + static uint SwapEndianness(ulong x) + { + return (uint)(((x & 0x000000ff) << 24) + ((x & 0x0000ff00) << 8) + ((x & 0x00ff0000) >> 8) + ((x & 0xff000000) >> 24)); + } + + private static DateTime ParseDateTimeWithNTPData(byte[] ntpData) + { + //Offset to get to the "Transmit Timestamp" field (time at which the reply + //departed the server for the client, in 64-bit timestamp format." + const byte serverReplyTime = 40; + + //Get the seconds part + ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime); + + //Get the seconds fraction + ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4); + + //Convert From big-endian to little-endian + intPart = SwapEndianness(intPart); + fractPart = SwapEndianness(fractPart); + + var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L); + + //**UTC** time + var networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds((long)milliseconds); + return networkDateTime; + } + } +} \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs.meta new file mode 100644 index 0000000..09d2677 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 610a0d13d17714116b2d436c4daf69a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTime.cs b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTime.cs new file mode 100644 index 0000000..4f15c55 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTime.cs @@ -0,0 +1,42 @@ +using System; +using ThinkingSDK.PC.Utils; + +namespace ThinkingSDK.PC.Time +{ + public class ThinkingSDKTime : ThinkingSDKTimeInter + { + private TimeZoneInfo mTimeZone; + private DateTime mDate; + + public ThinkingSDKTime(TimeZoneInfo timezone, DateTime date) + { + this.mTimeZone = timezone; + this.mDate = date; + } + + public string GetTime(TimeZoneInfo timeZone) + { + if (timeZone == null) + { + return ThinkingSDKUtil.FormatDate(mDate, mTimeZone); + } + else + { + return ThinkingSDKUtil.FormatDate(mDate, timeZone); + } + } + + public double GetZoneOffset(TimeZoneInfo timeZone) + { + if (timeZone == null) + { + return ThinkingSDKUtil.ZoneOffset(mDate, mTimeZone); + } + else + { + return ThinkingSDKUtil.ZoneOffset(mDate, timeZone); + } + } + } + +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTime.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTime.cs.meta new file mode 100644 index 0000000..97d6856 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTime.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 482ba0db5b92b4f9e9475acfda6e496c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs new file mode 100644 index 0000000..a4096a0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs @@ -0,0 +1,32 @@ +using System; + +namespace ThinkingSDK.PC.Time +{ + public class ThinkingSDKTimeCalibration + { + /// + /// Timestamp when time was calibrated + /// + public long mStartTime; + /// + /// System boot time when calibrating time + /// + public long mSystemElapsedRealtime; + public DateTime NowDate() + { + long nowTime = Environment.TickCount; + long timestamp = nowTime - this.mSystemElapsedRealtime + this.mStartTime; + // DateTime dt = DateTimeOffset.FromUnixTimeMilliseconds(timestamp).LocalDateTime; + // return dt; + + DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + return dt.AddMilliseconds(timestamp); + } + + protected static double ConvertDateTimeInt(System.DateTime time) + { + DateTime startTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + return (double)(time - startTime).TotalMilliseconds; + } + } +} \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs.meta new file mode 100644 index 0000000..202b812 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b9bb8b764cd4d47bd91702ea2c9a5ad5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeInter.cs b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeInter.cs new file mode 100644 index 0000000..82c17fa --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeInter.cs @@ -0,0 +1,9 @@ +using System; +namespace ThinkingSDK.PC.Time +{ + public interface ThinkingSDKTimeInter + { + string GetTime(TimeZoneInfo timeZone); + Double GetZoneOffset(TimeZoneInfo timeZone); + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeInter.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeInter.cs.meta new file mode 100644 index 0000000..1671847 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimeInter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0cc5c74f7de224d3aa2fa4668ae7a9f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs new file mode 100644 index 0000000..d3da381 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs @@ -0,0 +1,21 @@ +using System; +using ThinkingSDK.PC.Config; +using ThinkingSDK.PC.Utils; + +namespace ThinkingSDK.PC.Time +{ + public class ThinkingSDKTimestampCalibration : ThinkingSDKTimeCalibration + { + + public ThinkingSDKTimestampCalibration(long timestamp) + { + DateTime dateTimeUtcNow = DateTime.UtcNow; + this.mStartTime = timestamp; + this.mSystemElapsedRealtime = Environment.TickCount; + + double time_offset = (ConvertDateTimeInt(dateTimeUtcNow) - timestamp) / 1000; + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Time Calibration with NTP (" + timestamp + "), diff = " + time_offset.ToString("0.000s")); + } + } +} + diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs.meta new file mode 100644 index 0000000..f2bbbca --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aef0ebc4f720643f3878a26fc71b6ddd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils.meta b/sdk-intergration/Packages/TA/Plugins/PC/Utils.meta new file mode 100644 index 0000000..570e5c6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0ff15ea6a25e04a79b1289cb0664ef16 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKAppInfo.cs b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKAppInfo.cs new file mode 100644 index 0000000..12b4eb5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKAppInfo.cs @@ -0,0 +1,48 @@ +using System; +using ThinkingSDK.PC.Config; +using ThinkingSDK.PC.Constant; +using UnityEngine; + +namespace ThinkingSDK.PC.Utils +{ + public class ThinkingSDKAppInfo + { + // sdk version + public static string LibVersion() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.LIB_VERSION)) + { + return ""; + } + return ThinkingSDKPublicConfig.Version() ; + } + // sdk name + public static string LibName() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.LIB)) + { + return ""; + } + return ThinkingSDKPublicConfig.Name(); + } + // app version + public static string AppVersion() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.APP_VERSION)) + { + return ""; + } + return Application.version; + } + // app identifier, bundle ID + public static string AppIdentifier() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.APP_BUNDLEID)) + { + return ""; + } + return Application.identifier; + } + + } +} \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKAppInfo.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKAppInfo.cs.meta new file mode 100644 index 0000000..eee0a1f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKAppInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85a9d7d4b192d42fb826f9620d01ef01 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs new file mode 100644 index 0000000..153ced7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs @@ -0,0 +1,229 @@ +using System; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.Storage; +using UnityEngine; + +namespace ThinkingSDK.PC.Utils +{ + public class ThinkingSDKDeviceInfo + { + // devide ID + public static string DeviceID() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.DEVICE_ID)) + { + return ""; + } + #if (UNITY_WEBGL) + return RandomDeviceID(); + #else + return SystemInfo.deviceUniqueIdentifier; + #endif + } + // A persistent random number, used as an alternative to the device ID (WebGL cannot obtain the device ID) + private static string RandomDeviceID() + { + string randomID = (string)ThinkingSDKFile.GetData(ThinkingSDKConstant.RANDOM_DEVICE_ID, typeof(string)); + if (string.IsNullOrEmpty(randomID)) + { + randomID = System.Guid.NewGuid().ToString("N"); + ThinkingSDKFile.SaveData(ThinkingSDKConstant.RANDOM_DEVICE_ID, randomID); + } + return randomID; + } + // network type + public static string NetworkType() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.NETWORK_TYPE)) + { + return ""; + } + string networkType = "NULL"; + if (Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork) + { + networkType = "Mobile"; + } + else if (Application.internetReachability == NetworkReachability.ReachableViaLocalAreaNetwork) + { + networkType = "LAN"; + } + return networkType; + } + // carrier name + public static string Carrier() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.CARRIER)) + { + return ""; + } + return "NULL"; + } + // os name + public static string OS() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.OS)) + { + return ""; + } + string os = "other"; + if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.Linux) + { + os = "Linux"; + } + else if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX) + { + os = "MacOSX"; + } + else if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.Windows) + { + os = "Windows"; + } + return os; + } + // os version + public static string OSVersion() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.OS_VERSION)) + { + return ""; + } + return SystemInfo.operatingSystem; + } + // device screen width + public static int ScreenWidth() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.SCREEN_WIDTH)) + { + return 0; + } + return (int)(UnityEngine.Screen.currentResolution.width); + } + // device screen height + public static int ScreenHeight() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.SCREEN_HEIGHT)) + { + return 0; + } + return (int)(UnityEngine.Screen.currentResolution.height); + } + // graphics card manufacturer name + public static string Manufacture() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.MANUFACTURE)) + { + return ""; + } + return SystemInfo.graphicsDeviceVendor; + } + // devide model + public static string DeviceModel() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.DEVICE_MODEL)) + { + return ""; + } + return SystemInfo.deviceModel; + } + // device language + public static string MachineLanguage() + { + if (ThinkingSDKUtil.DisPresetProperties.Contains(ThinkingSDKConstant.SYSTEM_LANGUAGE)) + { + return ""; + } + switch (Application.systemLanguage) + { + case SystemLanguage.Afrikaans: + return "af"; + case SystemLanguage.Arabic: + return "ar"; + case SystemLanguage.Basque: + return "eu"; + case SystemLanguage.Belarusian: + return "be"; + case SystemLanguage.Bulgarian: + return "bg"; + case SystemLanguage.Catalan: + return "ca"; + case SystemLanguage.Chinese: + return "zh"; + case SystemLanguage.Czech: + return "cs"; + case SystemLanguage.Danish: + return "da"; + case SystemLanguage.Dutch: + return "nl"; + case SystemLanguage.English: + return "en"; + case SystemLanguage.Estonian: + return "et"; + case SystemLanguage.Faroese: + return "fo"; + case SystemLanguage.Finnish: + return "fu"; + case SystemLanguage.French: + return "fr"; + case SystemLanguage.German: + return "de"; + case SystemLanguage.Greek: + return "el"; + case SystemLanguage.Hebrew: + return "he"; + case SystemLanguage.Icelandic: + return "is"; + case SystemLanguage.Indonesian: + return "id"; + case SystemLanguage.Italian: + return "it"; + case SystemLanguage.Japanese: + return "ja"; + case SystemLanguage.Korean: + return "ko"; + case SystemLanguage.Latvian: + return "lv"; + case SystemLanguage.Lithuanian: + return "lt"; + case SystemLanguage.Norwegian: + return "nn"; + case SystemLanguage.Polish: + return "pl"; + case SystemLanguage.Portuguese: + return "pt"; + case SystemLanguage.Romanian: + return "ro"; + case SystemLanguage.Russian: + return "ru"; + case SystemLanguage.SerboCroatian: + return "sr"; + case SystemLanguage.Slovak: + return "sk"; + case SystemLanguage.Slovenian: + return "sl"; + case SystemLanguage.Spanish: + return "es"; + case SystemLanguage.Swedish: + return "sv"; + case SystemLanguage.Thai: + return "th"; + case SystemLanguage.Turkish: + return "tr"; + case SystemLanguage.Ukrainian: + return "uk"; + case SystemLanguage.Vietnamese: + return "vi"; + case SystemLanguage.ChineseSimplified: + return "zh"; + case SystemLanguage.ChineseTraditional: + return "zh"; + case SystemLanguage.Hungarian: + return "hu"; + case SystemLanguage.Unknown: + return "unknown"; + + }; + return ""; + } + } +} + diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs.meta new file mode 100644 index 0000000..e9931ec --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c84fe92f43c114ba7ad64250db8e124e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKJSON.cs b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKJSON.cs new file mode 100644 index 0000000..b3d844f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKJSON.cs @@ -0,0 +1,630 @@ +/* + * 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 ThinkingSDK.PC.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. + */ + /// + /// 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. + /// + public class ThinkingSDKJSON + { + /// + /// Parses the string json into a value + /// + /// A JSON string. + /// An List<object>, a Dictionary<string, object>, a double, an integer,a string, null, true, or false + public static Dictionary Deserialize(string json) + { + // save the string for debug information + if (json == null) + { + return null; + } + + return Parser.Parse(json); + } + + // Use caution! + public static List DeserializeArray(string json) + { + // save the string for debug information + if (json == null) + { + return null; + } + + return Parser.ParseArray(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 Parse(string jsonString) + { + using (var instance = new Parser(jsonString)) + { + return instance.ParseObject(); + } + } + + public static List ParseArray(string jsonString) + { + using (var instance = new Parser(jsonString)) + { + return instance.ParseArray(); + } + } + + public void Dispose() + { + json.Dispose(); + json = null; + } + + Dictionary ParseObject() + { + Dictionary table = new Dictionary(); + + // 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 ParseArray() + { + List array = new List(); + + // 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) + { + long parsedInt; + Int64.TryParse(number, out parsedInt); + return parsedInt; + } + + double parsedDouble; + if (!Double.TryParse(number, System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowLeadingSign, System.Globalization.CultureInfo.InvariantCulture, out parsedDouble)) + { + Double.TryParse(number, System.Globalization.NumberStyles.AllowDecimalPoint | System.Globalization.NumberStyles.AllowLeadingSign, 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; + } + } + } + + /// + /// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string + /// + /// A Dictionary<string, object> / List<object> + /// A JSON encoded string, or null if object 'json' is not serializable + public static string Serialize(object obj, Func func = null) + { + return Serializer.Serialize(obj, func); + } + + sealed class Serializer + { + StringBuilder builder; + Func func; + + Serializer() + { + builder = new StringBuilder(); + } + + public static string Serialize(object obj, Func 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()); + } + } + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKJSON.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKJSON.cs.meta new file mode 100644 index 0000000..de5cd85 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKJSON.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f521a02d525fd485c93a25ce7f330b0d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKLogger.cs b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKLogger.cs new file mode 100644 index 0000000..aaebf35 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKLogger.cs @@ -0,0 +1,23 @@ +using System; +using System.IO; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.Config; +using UnityEngine; + +namespace ThinkingSDK.PC.Utils +{ + public class ThinkingSDKLogger + { + public ThinkingSDKLogger() + { + + } + public static void Print(string str) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) + { + Debug.Log("[ThinkingData] Info: " + str); + } + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKLogger.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKLogger.cs.meta new file mode 100644 index 0000000..4de59a8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKLogger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7003bc870a4644632aa259a02b235ec6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs new file mode 100644 index 0000000..2bbb844 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs @@ -0,0 +1,15 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace ThinkingSDK.PC.Utils +{ + public class ThinkingSDKTimeUtil + { + public static string Time() + { + return ""; + } + } +} + diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs.meta new file mode 100644 index 0000000..8371099 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb171e9f7fda1436a83fc7be10687f92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKUtil.cs b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKUtil.cs new file mode 100644 index 0000000..4a47bf1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKUtil.cs @@ -0,0 +1,199 @@ +using UnityEngine; +using System; +using System.Xml; +using System.Collections.Generic; +using ThinkingSDK.PC.Config; +using ThinkingSDK.PC.Constant; +using ThinkingSDK.PC.Storage; + +namespace ThinkingSDK.PC.Utils +{ + public class ThinkingSDKUtil + { + private static Dictionary deviceInfo = null; + public ThinkingSDKUtil() + { + + } + public static List DisPresetProperties = ThinkingSDKUtil.GetDisPresetProperties(); + /* + * Check if the URL is valid + */ + public static bool IsValiadURL(string url) + { + return !(url == null || url.Length == 0 || !url.Contains("http") || !url.Contains("https")); + } + /* + * Check if the string is empty + */ + public static bool IsEmptyString(string str) + { + return (str == null || str.Length == 0); + } + public static Dictionary DeviceInfo() + { + if (deviceInfo == null) + { + deviceInfo = new Dictionary(); + deviceInfo[ThinkingSDKConstant.DEVICE_ID] = ThinkingSDKDeviceInfo.DeviceID(); + //deviceInfo[ThinkingSDKConstant.CARRIER] = ThinkingSDKDeviceInfo.Carrier(); //PC端不采集 + deviceInfo[ThinkingSDKConstant.OS] = ThinkingSDKDeviceInfo.OS(); + deviceInfo[ThinkingSDKConstant.OS_VERSION] = ThinkingSDKDeviceInfo.OSVersion(); + deviceInfo[ThinkingSDKConstant.SCREEN_HEIGHT] = ThinkingSDKDeviceInfo.ScreenHeight(); + deviceInfo[ThinkingSDKConstant.SCREEN_WIDTH] = ThinkingSDKDeviceInfo.ScreenWidth(); + deviceInfo[ThinkingSDKConstant.MANUFACTURE] = ThinkingSDKDeviceInfo.Manufacture(); + deviceInfo[ThinkingSDKConstant.DEVICE_MODEL] = ThinkingSDKDeviceInfo.DeviceModel(); + deviceInfo[ThinkingSDKConstant.APP_VERSION] = ThinkingSDKAppInfo.AppVersion(); + deviceInfo[ThinkingSDKConstant.APP_BUNDLEID] = ThinkingSDKAppInfo.AppIdentifier(); + deviceInfo[ThinkingSDKConstant.LIB] = ThinkingSDKAppInfo.LibName(); + deviceInfo[ThinkingSDKConstant.LIB_VERSION] = ThinkingSDKAppInfo.LibVersion(); + } + deviceInfo[ThinkingSDKConstant.SYSTEM_LANGUAGE] = ThinkingSDKDeviceInfo.MachineLanguage(); + deviceInfo[ThinkingSDKConstant.NETWORK_TYPE] = ThinkingSDKDeviceInfo.NetworkType(); + return deviceInfo; + } + // Get disabled preset properties + private static List GetDisPresetProperties() + { + List properties = new List(); + + TextAsset textAsset = Resources.Load("ta_public_config"); + if (textAsset != null && textAsset.text != null) + { + XmlDocument xmlDoc = new XmlDocument(); + // xmlDoc.Load(srcPath); + xmlDoc.LoadXml(textAsset.text); + XmlNode root = xmlDoc.SelectSingleNode("resources"); + for (int i=0; i originalDic, Dictionary subDic) + { + if (originalDic != subDic) + { + foreach (KeyValuePair kv in subDic) + { + originalDic[kv.Key] = kv.Value; + } + } + } + //get timestamp + public static long GetTimeStamp() + { + TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); + return Convert.ToInt64(ts.TotalMilliseconds); + } + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKUtil.cs.meta b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKUtil.cs.meta new file mode 100644 index 0000000..5e83b32 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/PC/Utils/ThinkingSDKUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ed8643f4a1b924df991677307901a014 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS.meta b/sdk-intergration/Packages/TA/Plugins/iOS.meta new file mode 100644 index 0000000..cc591f4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2442dee47d8bca24c84d6e07839265c2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty.meta new file mode 100644 index 0000000..8b9f7cf --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 59fbb0cc06a0dc044ba427309e4b308a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.h new file mode 100644 index 0000000..111feeb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.h @@ -0,0 +1,16 @@ +// +// TAAdjustSyncData.h +// ThinkingSDK +// +// Created by wwango on 2022/3/25. +// + +#import "TABaseSyncData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAAdjustSyncData : TABaseSyncData + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.h.meta new file mode 100644 index 0000000..59a6002 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f23d1fb8c784ff9439ce700b9f055a1c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m new file mode 100644 index 0000000..8d047c2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m @@ -0,0 +1,33 @@ +// +// TAAdjustSyncData.m +// ThinkingSDK +// +// Created by wwango on 2022/3/25. +// + +#import "TAAdjustSyncData.h" + +@implementation TAAdjustSyncData + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property { + + if (!self.customPropety || [self.customPropety isKindOfClass:[NSDictionary class]]) { + self.customPropety = @{}; + } + + NSString *accountID = [taInstance getAccountId] ? [taInstance getAccountId] : @""; + NSString *distinctId = [taInstance getDistinctId] ? [taInstance getDistinctId] : @""; + + Class cls = NSClassFromString(@"Adjust"); + SEL sel = NSSelectorFromString(@"addSessionCallbackParameter:value:"); + if (cls && [cls respondsToSelector:sel]) { + [cls performSelector:sel withObject:TA_ACCOUNT_ID withObject:accountID]; + [cls performSelector:sel withObject:TA_DISTINCT_ID withObject:distinctId]; + } +} +#pragma clang diagnostic pop + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m.meta new file mode 100644 index 0000000..98d4d96 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bcdd8f297c5691e4a9ec48396c0ba117 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h new file mode 100644 index 0000000..74e9db6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h @@ -0,0 +1,16 @@ +// +// TAAppLovinSyncData.h +// ThinkingSDK.default-Base-Core-Extension-Util-iOS +// +// Created by wwango on 2022/9/28. +// + +#import "TABaseSyncData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAAppLovinSyncData : TABaseSyncData + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h.meta new file mode 100644 index 0000000..56564ac --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f589c9d5fa5d0a74da38516b85b415dd +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m new file mode 100644 index 0000000..1998f2f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m @@ -0,0 +1,62 @@ +// +// TAAppLovinSyncData.m +// ThinkingSDK.default-Base-Core-Extension-Util-iOS +// +// Created by wwango on 2022/9/28. +// + +#import "TAAppLovinSyncData.h" + +@implementation TAAppLovinSyncData + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property { + + if (!self.customPropety || [self.customPropety isKindOfClass:[NSDictionary class]]) { + self.customPropety = @{}; + } + + NSString *distinctId = [taInstance getDistinctId] ? [taInstance getDistinctId] : @""; + + static dispatch_once_t onceToken; + Class cls = NSClassFromString(@"ALSdk"); + SEL sel1 = NSSelectorFromString(@"shared"); + SEL sel2 = NSSelectorFromString(@"setUserIdentifier"); + __block id instance; + dispatch_once(&onceToken, ^{ + if (cls && [cls respondsToSelector:sel1]) { + instance = [cls performSelector:sel1]; + if ([instance respondsToSelector:sel2]) { + [instance performSelector:sel2 withObject:distinctId]; + } + } + }); + + + if ([property isKindOfClass:[NSDictionary class]] && [property.allKeys containsObject:TASyncDataKey]) { + id customData = property[TASyncDataKey]; + Class cls = NSClassFromString(@"MAAd"); + if ([customData isKindOfClass:cls]) { + double revenue = [(NSNumber *)[customData performSelector:@selector(revenue)] doubleValue]; + NSString *networkName = [customData performSelector:@selector(networkName)]; + NSString *placement = [customData performSelector:@selector(placement)]; + NSString *adUnitId = [customData performSelector:@selector(adUnitIdentifier)]; + NSString *format = [[customData performSelector:@selector(format)] performSelector:@selector(label)]; + NSString *countryCode = [instance valueForKeyPath:@"configuration.countryCode"]; + + [taInstance track:@"appLovin_sdk_ad_revenue" properties:@{ + @"revenue":@(revenue), + @"networkName":networkName, + @"placement":placement, + @"adUnitId":adUnitId, + @"format":format, + @"countryCode":countryCode}]; + } + } + +} +#pragma clang diagnostic pop + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m.meta new file mode 100644 index 0000000..2968e28 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1555b4e0a58fc644093686c88d04d988 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h new file mode 100644 index 0000000..3e2e4da --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h @@ -0,0 +1,16 @@ +// +// TAAppsFlyerSyncData.h +// ThinkingSDK +// +// Created by wwango on 2022/2/14. +// + +#import "TABaseSyncData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAAppsFlyerSyncData : TABaseSyncData + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h.meta new file mode 100644 index 0000000..169b877 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: dcb2268fa2aab7a45bc7f1bc87cac652 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m new file mode 100644 index 0000000..75b7171 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m @@ -0,0 +1,39 @@ +// +// TAAppsFlyerSyncData.m +// ThinkingSDK +// +// Created by wwango on 2022/2/14. +// + +#import "TAAppsFlyerSyncData.h" + +@implementation TAAppsFlyerSyncData + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property { + + if (!self.customPropety || [self.customPropety isKindOfClass:[NSDictionary class]]) { + self.customPropety = @{}; + } + + NSMutableDictionary * datas = [NSMutableDictionary dictionaryWithDictionary:property]; + NSString *accountID = [taInstance getAccountId]; + NSString *distinctId = [taInstance getDistinctId]; + [datas setObject:(accountID ? accountID : @"") forKey:TA_ACCOUNT_ID]; + [datas setObject:distinctId ? distinctId : @"" forKey:TA_DISTINCT_ID]; + + Class cls = NSClassFromString(@"AppsFlyerLib"); + SEL sel1 = NSSelectorFromString(@"shared"); + SEL sel2 = NSSelectorFromString(@"setAdditionalData:"); + if (cls && [cls respondsToSelector:sel1]) { + id instance = [cls performSelector:sel1]; + if ([instance respondsToSelector:sel2]) { + [instance performSelector:sel2 withObject:datas]; + } + } +} +#pragma clang diagnostic pop + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m.meta new file mode 100644 index 0000000..969be11 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 130022dcb686e874c9a601a19526e9d7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.h new file mode 100644 index 0000000..99e9b02 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.h @@ -0,0 +1,76 @@ +// +// TABaseSyncData.h +// ThinkingSDK +// +// Created by wwango on 2022/2/14. +// + +#import +#import +#import "TAThirdPartySyncProtocol.h" + +#define TA_DISTINCT_ID @"ta_distinct_id" +#define TA_ACCOUNT_ID @"ta_account_id" + + +#define td_force_inline __inline__ __attribute__((always_inline)) + + +#define TASyncDataKey @"TASyncDataKey" + + +NS_ASSUME_NONNULL_BEGIN + +static td_force_inline void __td_td__swizzleWithClassMethod(Class c, NSString *oriSELStr, SEL newSel, IMP newIMP) { + SEL orig = NSSelectorFromString(oriSELStr); + Method origMethod = class_getClassMethod(c, orig); + c = object_getClass((id)c); + + class_addMethod(c, newSel, newIMP, method_getTypeEncoding(origMethod)); + + + if(class_addMethod(c, orig, newIMP, method_getTypeEncoding(origMethod))) { + class_replaceMethod(c, newSel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); + } else { + Method newMethod = class_getClassMethod(c, newSel); + method_exchangeImplementations(origMethod, newMethod); + } +} + + +static td_force_inline void __td_td_swizzleWithOriSELStr(id target, NSString *oriSELStr, SEL newSEL, IMP newIMP) { + SEL origSEL = NSSelectorFromString(oriSELStr); + Method origMethod = class_getInstanceMethod([target class], origSEL); + + if ([target respondsToSelector:origSEL]) { + + class_addMethod([target class], newSEL, newIMP, method_getTypeEncoding(origMethod)); + + + Method origMethod = class_getInstanceMethod([target class], origSEL); + + Method newMethod = class_getInstanceMethod([target class], newSEL); + + + if(class_addMethod([target class], origSEL, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { + + class_replaceMethod([target class], newSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); + } else { + + method_exchangeImplementations(origMethod, newMethod); + } + } else { + + class_addMethod([target class], origSEL, newIMP, method_getTypeEncoding(origMethod)); + } +} + +@interface TABaseSyncData : NSObject + +@property (nonatomic, weak) id taInstance; +@property (nonatomic, strong) NSDictionary *customPropety; +@property (nonatomic, assign) BOOL isSwizzleMethod; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.h.meta new file mode 100644 index 0000000..3724909 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fd5f4f80c653bbb4599cc3e51e9bc0c8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.m new file mode 100644 index 0000000..b276be8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.m @@ -0,0 +1,21 @@ +// +// TABaseSyncData.m +// ThinkingSDK +// +// Created by wwango on 2022/2/14. +// + +#import "TABaseSyncData.h" + +@implementation TABaseSyncData + +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property { + self.taInstance = taInstance; + self.customPropety = property; +} + +- (void)syncThirdData:(id)taInstance { + [self syncThirdData:taInstance property:@{}]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.m.meta new file mode 100644 index 0000000..efaa837 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABaseSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 94f4814aeb93aab4d9a3be4e72fe10e8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.h new file mode 100644 index 0000000..0d11db3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.h @@ -0,0 +1,16 @@ +// +// TABranchSyncData.h +// ThinkingSDK +// +// Created by wwango on 2022/3/25. +// + +#import "TABaseSyncData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TABranchSyncData : TABaseSyncData + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.h.meta new file mode 100644 index 0000000..d1b9fc3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8f4ba7dc2005f644391a51e99729e172 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.m new file mode 100644 index 0000000..c810dd0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.m @@ -0,0 +1,37 @@ +// +// TABranchSyncData.m +// ThinkingSDK +// +// Created by wwango on 2022/3/25. +// + +#import "TABranchSyncData.h" + +@implementation TABranchSyncData + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property { + + if (!self.customPropety || [self.customPropety isKindOfClass:[NSDictionary class]]) { + self.customPropety = @{}; + } + + NSString *accountID = [taInstance getAccountId]; + NSString *distinctId = [taInstance getDistinctId]; + + Class cls = NSClassFromString(@"Branch"); + SEL sel1 = NSSelectorFromString(@"getInstance"); + SEL sel2 = NSSelectorFromString(@"setRequestMetadataKey:value:"); + if (cls && [cls respondsToSelector:sel1]) { + id instance = [cls performSelector:sel1]; + if ([instance respondsToSelector:sel2]) { + [instance performSelector:sel2 withObject:TA_ACCOUNT_ID withObject:accountID]; + [instance performSelector:sel2 withObject:TA_DISTINCT_ID withObject:distinctId]; + } + } +} +#pragma clang diagnostic pop + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.m.meta new file mode 100644 index 0000000..314bd5e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TABranchSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: cc4b09aa40210974b81be376181cc1bc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h new file mode 100644 index 0000000..346faca --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h @@ -0,0 +1,16 @@ +// +// TAFirebaseSyncData.h +// ThinkingSDK.default-Base-Core-Extension-Util-iOS +// +// Created by wwango on 2022/9/28. +// + +#import "TABaseSyncData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAFirebaseSyncData : TABaseSyncData + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h.meta new file mode 100644 index 0000000..7bf10c8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 92d5e3c0b05abb34ba96a3685d214252 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m new file mode 100644 index 0000000..5d65e13 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m @@ -0,0 +1,32 @@ +// +// TAFirebaseSyncData.m +// ThinkingSDK.default-Base-Core-Extension-Util-iOS +// +// Created by wwango on 2022/9/28. +// + +#import "TAFirebaseSyncData.h" + +@implementation TAFirebaseSyncData + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property { + + if (!self.customPropety || [self.customPropety isKindOfClass:[NSDictionary class]]) { + self.customPropety = @{}; + } + + NSString *distinctId = [taInstance getDistinctId] ? [taInstance getDistinctId] : @""; + + Class cls = NSClassFromString(@"FIRAnalytics"); + SEL sel = NSSelectorFromString(@"setUserID:"); + if (cls && [cls respondsToSelector:sel]) { + [cls performSelector:sel withObject:distinctId]; + } + +} +#pragma clang diagnostic pop + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m.meta new file mode 100644 index 0000000..ccaca23 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 645cd0e0a5de8ce4e95db6dd93942fa8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h new file mode 100644 index 0000000..e89d705 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h @@ -0,0 +1,16 @@ +// +// TAIronSourceSyncData.h +// ThinkingSDK +// +// Created by wwango on 2022/2/16. +// + +#import "TABaseSyncData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAIronSourceSyncData : TABaseSyncData + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h.meta new file mode 100644 index 0000000..871e5be --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7ec36aa488f5b3d49a55407eddacbb1f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m new file mode 100644 index 0000000..99c24f8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m @@ -0,0 +1,61 @@ +// +// TAIronSourceSyncData.m +// ThinkingSDK +// +// Created by wwango on 2022/2/16. +// + +#import "TAIronSourceSyncData.h" + +@implementation TAIronSourceSyncData + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +static id _td_last_IronSource_delegate; + +- (void)syncThirdData:(id)taInstance { + + [super syncThirdData:taInstance]; + + if (self.isSwizzleMethod) return; + + Class class = NSClassFromString(@"IronSource"); + NSString *oriSELString = @"addImpressionDataDelegate:"; + SEL newSel = NSSelectorFromString([NSString stringWithFormat:@"td_%@", oriSELString]); + IMP newIMP = imp_implementationWithBlock(^(id _self, id delegate) { + if ([_self respondsToSelector:newSel]) { + [_self performSelector:newSel withObject:delegate]; + _td_last_IronSource_delegate = delegate; + } + + id class1 = delegate; + NSString *oriSELString1 = @"impressionDataDidSucceed:"; + SEL newSel1 = NSSelectorFromString([NSString stringWithFormat:@"td_%@", oriSELString1]); + IMP newIMP1 = imp_implementationWithBlock(^(id _self1, id impressionData) { + if ([_self1 respondsToSelector:newSel1]) { + [_self1 performSelector:newSel1 withObject:impressionData]; + } + + NSDictionary *all_data; + SEL sel = NSSelectorFromString(@"all_data"); + if ([impressionData respondsToSelector:sel]) { + all_data = [impressionData performSelector:sel]; + } + + if (_td_last_IronSource_delegate == _self1) { + [self.taInstance track:@"ta_ironSource_callback" properties:all_data]; + } + }); + __td_td_swizzleWithOriSELStr(class1, oriSELString1, newSel1, newIMP1); + }); + + __td_td__swizzleWithClassMethod(class, oriSELString, newSel, newIMP); + + self.isSwizzleMethod = YES; +} + +#pragma clang diagnostic pop + +@end + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m.meta new file mode 100644 index 0000000..2da85b8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: dfc95bf40b9c2ea409c12c2efd32f205 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h new file mode 100644 index 0000000..ada1929 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h @@ -0,0 +1,16 @@ +// +// TAKochavaSyncData.h +// ThinkingSDK.default-Base-Core-Extension-Util-iOS +// +// Created by wwango on 2022/9/28. +// + +#import "TABaseSyncData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAKochavaSyncData : TABaseSyncData + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h.meta new file mode 100644 index 0000000..75c2715 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 666f09a63236aa2428d8f5724fbae835 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m new file mode 100644 index 0000000..29a1fc2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m @@ -0,0 +1,42 @@ +// +// TAKochavaSyncData.m +// ThinkingSDK.default-Base-Core-Extension-Util-iOS +// +// Created by wwango on 2022/9/28. +// + +#import "TAKochavaSyncData.h" + +@implementation TAKochavaSyncData + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property { + + if (!self.customPropety || [self.customPropety isKindOfClass:[NSDictionary class]]) { + self.customPropety = @{}; + } + + NSString *accountID = [taInstance getAccountId]; + NSString *distinctId = [taInstance getDistinctId]; + + Class cls = NSClassFromString(@"KVATracker"); + SEL sel = NSSelectorFromString(@"shared"); + SEL sel1 = NSSelectorFromString(@"identityLink"); + SEL sel2 = NSSelectorFromString(@"registerWithNameString:identifierString:"); + if (cls && [cls respondsToSelector:sel]) { + id shared = [cls performSelector:sel]; + if (shared && [shared respondsToSelector:sel1]) { + id identityLink = [shared performSelector:sel1]; + if (identityLink && [identityLink respondsToSelector:sel2]) { + [identityLink performSelector:sel2 withObject:TA_ACCOUNT_ID withObject:(accountID ? accountID : @"")]; + [identityLink performSelector:sel2 withObject:TA_DISTINCT_ID withObject:(distinctId ? distinctId : @"")]; + } + } + } +} +#pragma clang diagnostic pop + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m.meta new file mode 100644 index 0000000..c3b68a4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9d8dd1140d4ed51499d93137d3b38e3f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.h new file mode 100644 index 0000000..516410d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.h @@ -0,0 +1,16 @@ +// +// TAReYunSyncData.h +// ThinkingSDK +// +// Created by wwango on 2022/3/25. +// + +#import "TABaseSyncData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAReYunSyncData : TABaseSyncData + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.h.meta new file mode 100644 index 0000000..ed93232 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 159742403ce39d048b1045b491ea75b1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.m new file mode 100644 index 0000000..d26258e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.m @@ -0,0 +1,31 @@ +// +// TAReYunSyncData.m +// ThinkingSDK +// +// Created by wwango on 2022/3/25. +// + +#import "TAReYunSyncData.h" + +@implementation TAReYunSyncData + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property { + + if (!self.customPropety || [self.customPropety isKindOfClass:[NSDictionary class]]) { + self.customPropety = @{}; + } + + NSString *distinctId = [taInstance getDistinctId] ? [taInstance getDistinctId] : @""; + + Class cls = NSClassFromString(@"Tracking"); + SEL sel = NSSelectorFromString(@"setRegisterWithAccountID:"); + if (cls && [cls respondsToSelector:sel]) { + [cls performSelector:sel withObject: distinctId]; + } +} +#pragma clang diagnostic pop + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.m.meta new file mode 100644 index 0000000..c1c6e97 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAReYunSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 70d865d81fb98f64da6bb47bf5e2626d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdParty.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdParty.h new file mode 100644 index 0000000..f1a3a14 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdParty.h @@ -0,0 +1,18 @@ +// +// TAThirdParty.h +// TAThirdParty +// +// Created by Charles on 9.1.23. +// + +#import + +//! Project version number for TAThirdParty. +FOUNDATION_EXPORT double TAThirdPartyVersionNumber; + +//! Project version string for TAThirdParty. +FOUNDATION_EXPORT const unsigned char TAThirdPartyVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdParty.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdParty.h.meta new file mode 100644 index 0000000..177cdd1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdParty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 80d6e065164b28e4d9f22797357c96ad +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h new file mode 100644 index 0000000..01bd1c6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h @@ -0,0 +1,20 @@ +// +// TAThirdPartyManager.h +// ThinkingSDK +// +// Created by wwango on 2022/2/11. +// + +#import +#import "TAThirdPartySyncProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAThirdPartyManager : NSObject + +- (void)enableThirdPartySharing:(NSNumber *)type instance:(id)instance; +- (void)enableThirdPartySharing:(NSNumber *)type instance:(id)instance property:(NSDictionary *)property; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h.meta new file mode 100644 index 0000000..598475c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4d27babfde311a54b9a1c303f0e191f6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m new file mode 100644 index 0000000..03c771b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m @@ -0,0 +1,168 @@ +// +// TAThirdPartyManager.m +// ThinkingSDK +// +// Created by wwango on 2022/2/11. +// + +#import "TAThirdPartyManager.h" +#import "TAAppsFlyerSyncData.h" +#import "TAIronSourceSyncData.h" +#import "TAAdjustSyncData.h" +#import "TABranchSyncData.h" +#import "TATopOnSyncData.h" +#import "TAReYunSyncData.h" +#import "TATradPlusSyncData.h" +#import "TAKochavaSyncData.h" + + + typedef NS_OPTIONS(NSInteger, TAInnerThirdPartyShareType) { + TAInnerThirdPartyShareTypeNONE = 0, + TAInnerThirdPartyShareTypeAPPSFLYER = 1 << 0, + TAInnerThirdPartyShareTypeIRONSOURCE = 1 << 1, + TAInnerThirdPartyShareTypeADJUST = 1 << 2, + TAInnerThirdPartyShareTypeBRANCH = 1 << 3, + TAInnerThirdPartyShareTypeTOPON = 1 << 4, + TAInnerThirdPartyShareTypeTRACKING = 1 << 5, + TAInnerThirdPartyShareTypeTRADPLUS = 1 << 6, + TAInnerThirdPartyShareTypeAPPLOVIN = 1 << 7, + TAInnerThirdPartyShareTypeKOCHAVA = 1 << 8, + TAInnerThirdPartyShareTypeTALKINGDATA = 1 << 9, + TAInnerThirdPartyShareTypeFIREBASE = 1 << 10, + + }; + +static NSMutableDictionary *_thirdPartyManagerMap; + +// Register a third-party data collection service, and when the APP starts, it will start from the data area +char * kThinkingServices_service __attribute((used, section("__DATA, ThinkingServices"))) = "{ \"TAThirdPartyProtocol\" : \"TAThirdPartyManager\"}"; +@interface TAThirdPartyManager() + +@end + + +@implementation TAThirdPartyManager + +- (instancetype)init +{ + self = [super init]; + if (self) { + _thirdPartyManagerMap = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)enableThirdPartySharing:(NSNumber *)type instance:(id)instance +{ + [self enableThirdPartySharing:type instance:instance property:@{}]; +} + +- (void)enableThirdPartySharing:(NSNumber *)typee instance:(id)instance property:(NSDictionary *)property +{ + NSDictionary *info = [self _getThridInfoWithType:typee]; + + NSString *libClass = info[@"libClass"]; + NSString *taThirdClass = info[@"taThirdClass"]; + NSString *errorMes = info[@"errorMes"]; + + if (!NSClassFromString(libClass)) { + NSLog(@"[THINKING] %@", errorMes); + }else { + id syncData = [_thirdPartyManagerMap objectForKey:taThirdClass]; + if (!syncData) { + syncData = [NSClassFromString(taThirdClass) new]; + [_thirdPartyManagerMap setObject:syncData forKey:taThirdClass]; + } + [syncData syncThirdData:instance property:[property copy]]; + NSLog(@"[THINKING] %@ , SyncThirdData Success", NSClassFromString(libClass)); + } +} + + + + +- (NSDictionary *)_getThridInfoWithType:(NSNumber *)typee { + + static NSDictionary *_ta_ThridInfo; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _ta_ThridInfo = @{ + @(TAInnerThirdPartyShareTypeAPPSFLYER):@{ + + @"libClass": @"AppsFlyerLib", + @"taThirdClass":@"TAAppsFlyerSyncData", + @"errorMes":@"AppsFlyer Data synchronization exception: not installed AppsFlyer SDK" + + }, + @(TAInnerThirdPartyShareTypeIRONSOURCE):@{ + + @"libClass": @"IronSource", + @"taThirdClass":@"TAIronSourceSyncData", + @"errorMes": @"IronSource Data synchronization exception: not installed IronSource SDK" + + }, + @(TAInnerThirdPartyShareTypeADJUST):@{ + + @"libClass": @"Adjust", + @"taThirdClass":@"TAAdjustSyncData", + @"errorMes": @"Adjust Data synchronization exception: not installed Adjust SDK" + + }, + @(TAInnerThirdPartyShareTypeBRANCH):@{ + + @"libClass": @"Branch", + @"taThirdClass":@"TABranchSyncData", + @"errorMes": @"Branch Data synchronization exception: not installed Branch SDK" + + }, + @(TAInnerThirdPartyShareTypeTOPON):@{ + + @"libClass": @"ATAPI", + @"taThirdClass":@"TATopOnSyncData", + @"errorMes": @"TopOn Data synchronization exception: not installed TopOn SDK" + + }, + @(TAInnerThirdPartyShareTypeTRACKING):@{ + + @"libClass": @"Tracking", + @"taThirdClass":@"TAReYunSyncData", + @"errorMes": @"ReYun Data synchronization exception: Data synchronization exception: not installed SDK" + + }, + @(TAInnerThirdPartyShareTypeTRADPLUS):@{ + + @"libClass": @"TradPlus", + @"taThirdClass":@"TATradPlusSyncData", + @"errorMes": @"TradPlus Data synchronization exception: not installed TradPlus SDK" + + }, + @(TAInnerThirdPartyShareTypeAPPLOVIN):@{ + + @"libClass": @"ALSdk", + @"taThirdClass":@"TAAppLovinSyncData", + @"errorMes": @"AppLovin Data synchronization exception: not installed AppLovin SDK" + + }, + @(TAInnerThirdPartyShareTypeKOCHAVA):@{ + + @"libClass": @"KVATracker", + @"taThirdClass":@"TAKochavaSyncData", + @"errorMes": @"Kochava Data synchronization exception: not installed Kochava SDK" + + }, + @(TAInnerThirdPartyShareTypeFIREBASE):@{ + + @"libClass": @"FIRAnalytics", + @"taThirdClass":@"TAFirebaseSyncData", + @"errorMes": @"FIREBASE Data synchronization exception: not installed FIRAnalytics SDK" + + }, + }; + }); + + return _ta_ThridInfo[typee]; + +} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m.meta new file mode 100644 index 0000000..b489456 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b349605eb647b244fba3407e9a3a8dcf +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h new file mode 100644 index 0000000..3f78b17 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h @@ -0,0 +1,36 @@ +// +// TAThirdPartySyncProtocol.h +// ThinkingSDKDEMO +// +// Created by wwango on 2022/2/17. +// Copyright © 2022 thinking. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol TAThinkingTrackProtocol + +- (void)track:(NSString *)event properties:(nullable NSDictionary *)propertieDict; +- (NSString *)getDistinctId; +- (NSString *)getAccountId; + +@end + + +@protocol TAThirdPartySyncProtocol + +- (void)syncThirdData:(id)taInstance; +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property; + +@end + +@protocol TAThirdPartyProtocol + +- (void)enableThirdPartySharing:(NSNumber *)type instance:(id)instance; +- (void)enableThirdPartySharing:(NSNumber *)type instance:(id)instance property:(NSDictionary *)property; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h.meta new file mode 100644 index 0000000..b8abc8a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fcd8d7f779be07f42890854c530eaa6d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.h new file mode 100644 index 0000000..e3eb68b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.h @@ -0,0 +1,16 @@ +// +// TATopOnSyncData.h +// ThinkingSDK +// +// Created by wwango on 2022/3/25. +// + +#import "TABaseSyncData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TATopOnSyncData : TABaseSyncData + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.h.meta new file mode 100644 index 0000000..16b7485 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1d075aaf9e8e763468a2486c2de5dada +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.m new file mode 100644 index 0000000..b383cd6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.m @@ -0,0 +1,39 @@ +// +// TATopOnSyncData.m +// ThinkingSDK +// +// Created by wwango on 2022/3/25. +// + +#import "TATopOnSyncData.h" + +@implementation TATopOnSyncData + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property { + + if (!self.customPropety || [self.customPropety isKindOfClass:[NSDictionary class]]) { + self.customPropety = @{}; + } + + NSMutableDictionary * datas = [NSMutableDictionary dictionaryWithDictionary:property]; + NSString *accountID = [taInstance getAccountId]; + NSString *distinctId = [taInstance getDistinctId]; + [datas setObject:(accountID ? accountID : @"") forKey:TA_ACCOUNT_ID]; + [datas setObject:distinctId ? distinctId : @"" forKey:TA_DISTINCT_ID]; + + Class cls = NSClassFromString(@"ATAPI"); + SEL sel1 = NSSelectorFromString(@"sharedInstance"); + SEL sel2 = NSSelectorFromString(@"setCustomData:"); + if (cls && [cls respondsToSelector:sel1]) { + id instance = [cls performSelector:sel1]; + if ([instance respondsToSelector:sel2]) { + [instance performSelector:sel2 withObject:datas]; + } + } +} +#pragma clang diagnostic pop + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.m.meta new file mode 100644 index 0000000..b1307d7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATopOnSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3aa53540e955f33409921ec15cf41d9a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h new file mode 100644 index 0000000..5d94ce6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h @@ -0,0 +1,16 @@ +// +// TATradPlusSyncData.h +// ThinkingSDK +// +// Created by wwango on 2022/3/25. +// + +#import "TABaseSyncData.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TATradPlusSyncData : TABaseSyncData + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h.meta new file mode 100644 index 0000000..da03e0f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1b430f4186c171b47a3decec06726940 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m new file mode 100644 index 0000000..1603792 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m @@ -0,0 +1,39 @@ +// +// TATradPlusSyncData.m +// ThinkingSDK +// +// Created by wwango on 2022/3/25. +// + +#import "TATradPlusSyncData.h" + +@implementation TATradPlusSyncData + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +- (void)syncThirdData:(id)taInstance property:(NSDictionary *)property { + + if (!self.customPropety || [self.customPropety isKindOfClass:[NSDictionary class]]) { + self.customPropety = @{}; + } + + NSMutableDictionary * datas = [NSMutableDictionary dictionaryWithDictionary:property]; +// NSString *accountID = [taInstance getAccountId]; + NSString *distinctId = [taInstance getDistinctId]; +// [datas setObject:(accountID ? accountID : @"") forKey:TA_ACCOUNT_ID]; + [datas setObject:distinctId ? distinctId : @"" forKey:TA_DISTINCT_ID]; + + Class cls = NSClassFromString(@"TradPlus"); + SEL sel1 = NSSelectorFromString(@"sharedInstance"); + SEL sel2 = NSSelectorFromString(@"setDicCustomValue:"); + if (cls && [cls respondsToSelector:sel1]) { + id instance = [cls performSelector:sel1]; + if ([instance respondsToSelector:sel2]) { + [instance performSelector:sel2 withObject:datas]; + } + } +} +#pragma clang diagnostic pop + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m.meta new file mode 100644 index 0000000..903bfc7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a4aaa7712d062974ba1a32f80e1aab52 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingAnalytics.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingAnalytics.m new file mode 100644 index 0000000..75688bc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingAnalytics.m @@ -0,0 +1,517 @@ +#if __has_include() +#import +#import +#else +#import "ThinkingAnalyticsSDK.h" +#import "TDDeviceInfo.h" +#endif +#import + +#define NETWORK_TYPE_DEFAULT 1 +#define NETWORK_TYPE_WIFI 2 +#define NETWORK_TYPE_ALL 3 + +typedef const char * (*ResultHandler) (const char *type, const char *jsonData); +static ResultHandler resultHandler; +void RegisterRecieveGameCallback(ResultHandler handlerPointer) +{ + resultHandler = handlerPointer; +} + +static NSMutableDictionary *light_instances; +static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; + +ThinkingAnalyticsSDK* ta_getInstance(NSString *app_id) { + ThinkingAnalyticsSDK *result = nil; + + if (app_id == nil || [app_id isEqualToString:@""]) { + return [ThinkingAnalyticsSDK sharedInstance]; + } + + pthread_rwlock_rdlock(&rwlock); + if (light_instances[app_id] != nil) { + result = light_instances[app_id]; + } + pthread_rwlock_unlock(&rwlock); + + if (result != nil) return result; + + return [ThinkingAnalyticsSDK sharedInstanceWithAppid: app_id]; +} + +void ta_convertToDictionary(const char *json, NSDictionary **properties_dict) { + NSString *json_string = json != NULL ? [NSString stringWithUTF8String:json] : nil; + if (json_string) { + *properties_dict = [NSJSONSerialization JSONObjectWithData:[json_string dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil]; + } +} + +NSDictionary * ta_parse_date(NSDictionary *properties_dict) { + NSMutableDictionary *properties = [NSMutableDictionary dictionary]; + for (NSString *key in properties_dict.allKeys) { + id value = properties_dict[key]; + if ([value isKindOfClass:[NSDate class]]) { + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS"; + NSString *dateStr = [formatter stringFromDate:(NSDate *)value]; + properties[key] = dateStr; + } else if ([value isKindOfClass:[NSDictionary class]]) { + properties[key] = ta_parse_date((NSDictionary *)value); + } else { + properties[key] = value; + } + } + return properties; +} + +char* ta_strdup(const char* string) { + if (string == NULL) + return NULL; + char* res = (char*)malloc(strlen(string) + 1); + strcpy(res, string); + return res; +} + + +void ta_start(const char *app_id, const char *url, int mode, const char *timezone_id, bool enable_encrypt, int encrypt_version, const char *encrypt_public_key, int pinning_mode, bool allow_invalid_certificates, bool validates_domain_name, const char *instance_name) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSString *url_string = url != NULL ? [NSString stringWithUTF8String:url] : nil; + NSString *instance_name_string = instance_name != NULL ? [NSString stringWithUTF8String:instance_name] : nil; + TDConfig *config = [[TDConfig alloc] init]; + config.appid = app_id_string; + config.configureURL = url_string; + if (instance_name) { + [config setName:instance_name_string]; + } + if (mode == 1) { + // DEBUG + config.debugMode = ThinkingAnalyticsDebug; + } else if (mode == 2) { + // DEBUG_ONLY + config.debugMode = ThinkingAnalyticsDebugOnly; + } + NSString *timezone_id_string = timezone_id != NULL ? [NSString stringWithUTF8String:timezone_id] : nil; + NSTimeZone *timezone = [NSTimeZone timeZoneWithName:timezone_id_string]; + if (timezone) { + config.defaultTimeZone = timezone; + } + if (enable_encrypt == YES) { + NSString *encrypt_public_key_string = encrypt_public_key != NULL ? [NSString stringWithUTF8String:encrypt_public_key] : nil; + // Enable data encryption + config.enableEncrypt = YES; + // Set public key and version + config.secretKey = [[TDSecretKey alloc] initWithVersion:encrypt_version publicKey:encrypt_public_key_string]; + } + + [ThinkingAnalyticsSDK startWithConfig:config]; +} + +void ta_enable_log(BOOL enable_log) { + if (enable_log) { + [ThinkingAnalyticsSDK setLogLevel:TDLoggingLevelDebug]; + } else { + [ThinkingAnalyticsSDK setLogLevel:TDLoggingLevelNone]; + } +} + +void ta_set_network_type(int type) { + switch (type) { + case NETWORK_TYPE_DEFAULT: + [ta_getInstance(nil) setNetworkType:TDNetworkTypeDefault]; + break; + case NETWORK_TYPE_WIFI: + [ta_getInstance(nil) setNetworkType:TDNetworkTypeOnlyWIFI]; + break; + case NETWORK_TYPE_ALL: + [ta_getInstance(nil) setNetworkType:TDNetworkTypeALL]; + break; + } +} + +void ta_identify(const char *app_id, const char *unique_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSString *id_string = unique_id != NULL ? [NSString stringWithUTF8String:unique_id] : nil; + [ta_getInstance(app_id_string) identify:id_string]; +} + +const char *ta_get_distinct_id(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSString *distinct_id =[ta_getInstance(app_id_string) getDistinctId]; + return ta_strdup([distinct_id UTF8String]); +} + +void ta_login(const char *app_id, const char *account_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSString *id_string = account_id != NULL ? [NSString stringWithUTF8String:account_id] : nil; + [ta_getInstance(app_id_string) login:id_string]; +} + +void ta_logout(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) logout]; +} + +void ta_config_custom_lib_info(const char *lib_name, const char *lib_version) { + NSString *lib_name_string = lib_name != NULL ? [NSString stringWithUTF8String:lib_name] : nil; + NSString *lib_version_string = lib_version != NULL ? [NSString stringWithUTF8String:lib_version] : nil; + [ThinkingAnalyticsSDK setCustomerLibInfoWithLibName:lib_name_string libVersion:lib_version_string]; +} + +void ta_track_event(const char *app_id, const char *event_model_json) { + NSDictionary *event_model_dic = nil; + ta_convertToDictionary(event_model_json, &event_model_dic); + TDEventModel *eventModel; + NSString *eventType = event_model_dic[@"event_type"]; + if ([eventType isEqualToString:@"track_first"]) { + eventModel = [[TDFirstEventModel alloc] initWithEventName:event_model_dic[@"event_name"] firstCheckID:event_model_dic[@"extra_id"]]; + } else if ([eventType isEqualToString:@"track_update"]) { + eventModel = [[TDUpdateEventModel alloc] initWithEventName:event_model_dic[@"event_name"] eventID:event_model_dic[@"extra_id"]]; + } else if ([eventType isEqualToString:@"track_overwrite"]) { + eventModel = [[TDOverwriteEventModel alloc] initWithEventName:event_model_dic[@"event_name"] eventID:event_model_dic[@"extra_id"]]; + } + + eventModel.properties = event_model_dic[@"event_properties"]; + + long event_time = [event_model_dic[@"event_time"] longValue]; + NSString *timezoneString = event_model_dic[@"event_timezone"]; + NSTimeZone *tz; + if ([@"Local" isEqualToString:timezoneString]) { + tz = [NSTimeZone localTimeZone]; + } else { + tz = [NSTimeZone timeZoneWithName:timezoneString]; + } + + if (event_time) { + NSDate *date = [NSDate dateWithTimeIntervalSince1970:event_time/1000.0]; + [eventModel configTime:date timeZone:tz]; + } + + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) trackWithEventModel:eventModel]; +} + +void ta_track(const char *app_id, const char *event_name, const char *properties, long long time_stamp_millis, const char *timezone) { + NSString *event_name_string = event_name != NULL ? [NSString stringWithUTF8String:event_name] : nil; + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + + NSString *time_zone_string = timezone != NULL ? [NSString stringWithUTF8String:timezone] : nil; + NSTimeZone *tz; + if ([@"Local" isEqualToString:time_zone_string]) { + tz = [NSTimeZone localTimeZone]; + } else { + tz = [NSTimeZone timeZoneWithName:time_zone_string]; + } + + NSDate *time = [NSDate dateWithTimeIntervalSince1970:time_stamp_millis / 1000.0]; + + if (tz) { + [ta_getInstance(app_id_string) track:event_name_string properties:properties_dict time:time timeZone:tz]; + } else { + if (time_stamp_millis > 0) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [ta_getInstance(app_id_string) track:event_name_string properties:properties_dict time:time]; +#pragma clang diagnostic pop + } else { + [ta_getInstance(app_id_string) track:event_name_string properties:properties_dict]; + } + } +} + +void ta_flush(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) flush]; +} + +void ta_set_super_properties(const char *app_id, const char *properties) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) setSuperProperties:properties_dict]; + } +} + +void ta_unset_super_property(const char *app_id, const char *property_name) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSString *property_name_string = property_name != NULL ? [NSString stringWithUTF8String:property_name] : nil; + [ta_getInstance(app_id_string) unsetSuperProperty:property_name_string]; +} + +void ta_clear_super_properties(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) clearSuperProperties]; +} + +const char *ta_get_super_properties(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *property_dict = [ta_getInstance(app_id_string) currentSuperProperties]; + // nsdictionary --> nsdata + NSData *data = [NSJSONSerialization dataWithJSONObject:property_dict options:kNilOptions error:nil]; + // nsdata -> nsstring + NSString *jsonString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; + return ta_strdup([jsonString UTF8String]); +} + +const char *ta_get_preset_properties(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *property_dict = [[ta_getInstance(app_id_string) getPresetProperties] toEventPresetProperties]; + // nsdate => nsstring + property_dict = ta_parse_date(property_dict); + // nsdictionary --> nsdata + NSData *data = [NSJSONSerialization dataWithJSONObject:property_dict options:kNilOptions error:nil]; + // nsdata -> nsstring + NSString *jsonString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; + return ta_strdup([jsonString UTF8String]); +} + +void ta_time_event(const char *app_id, const char *event_name) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSString *event_name_string = event_name != NULL ? [NSString stringWithUTF8String:event_name] : nil; + [ta_getInstance(app_id_string) timeEvent:event_name_string]; +} + +void ta_user_set(const char *app_id, const char *properties) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) user_set:properties_dict]; + } +} + +void ta_user_set_with_time(const char *app_id, const char *properties, long long time_stamp_millis) { + NSDate *time = [NSDate dateWithTimeIntervalSince1970:time_stamp_millis / 1000.0]; + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) user_set:properties_dict withTime:time]; + } +} + +void ta_user_unset(const char *app_id, const char *properties) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSString *properties_string = properties != NULL ? [NSString stringWithUTF8String:properties] : nil; + [ta_getInstance(app_id_string) user_unset:properties_string]; +} + +void ta_user_unset_with_time(const char *app_id, const char *properties, long long time_stamp_millis) { + NSDate *time = [NSDate dateWithTimeIntervalSince1970:time_stamp_millis / 1000.0]; + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSString *properties_string = properties != NULL ? [NSString stringWithUTF8String:properties] : nil; + [ta_getInstance(app_id_string) user_unset:properties_string withTime:time]; +} + +void ta_user_set_once(const char *app_id, const char *properties) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) user_setOnce:properties_dict]; + } +} + +void ta_user_set_once_with_time(const char *app_id, const char *properties, long long time_stamp_millis) { + NSDate *time = [NSDate dateWithTimeIntervalSince1970:time_stamp_millis / 1000.0]; + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) user_setOnce:properties_dict withTime:time]; + } +} + +void ta_user_add(const char *app_id, const char *properties) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) user_add:properties_dict]; + } +} + +void ta_user_add_with_time(const char *app_id, const char *properties, long long time_stamp_millis) { + NSDate *time = [NSDate dateWithTimeIntervalSince1970:time_stamp_millis / 1000.0]; + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) user_add:properties_dict withTime:time]; + } +} + +void ta_user_delete(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) user_delete]; +} + +void ta_user_delete_with_time(const char *app_id, long long time_stamp_millis) { + NSDate *time = [NSDate dateWithTimeIntervalSince1970:time_stamp_millis / 1000.0]; + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) user_delete:time]; +} + +void ta_user_append(const char *app_id, const char *properties) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) user_append:properties_dict]; + } +} + +void ta_user_append_with_time(const char *app_id, const char *properties, long long time_stamp_millis) { + NSDate *time = [NSDate dateWithTimeIntervalSince1970:time_stamp_millis / 1000.0]; + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) user_append:properties_dict withTime:time]; + } +} + +void ta_user_uniq_append(const char *app_id, const char *properties) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) user_uniqAppend:properties_dict]; + } +} + +void ta_user_uniq_append_with_time(const char *app_id, const char *properties, long long time_stamp_millis) { + NSDate *time = [NSDate dateWithTimeIntervalSince1970:time_stamp_millis / 1000.0]; + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) user_uniqAppend:properties_dict withTime:time]; + } +} + +const char *ta_get_device_id() { + NSString *distinct_id = [ta_getInstance(nil) getDeviceId]; + return ta_strdup([distinct_id UTF8String]); +} + +void ta_set_dynamic_super_properties(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) setAutoTrackDynamicProperties:^NSDictionary * _Nonnull{ + const char *ret = resultHandler("DynamicSuperProperties", nil); + NSDictionary *dynamicSuperProperties = nil; + ta_convertToDictionary(ret, &dynamicSuperProperties); + return dynamicSuperProperties; + }]; +} + +void ta_set_track_status(const char *app_id, int status) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + ThinkingAnalyticsSDK* instance = ta_getInstance(app_id_string); + switch (status) { + case 1: + [instance setTrackStatus:TATrackStatusPause]; + break; + case 2: + [instance setTrackStatus:TATrackStatusStop]; + break; + case 3: + [instance setTrackStatus:TATrackStatusSaveOnly]; + break; + case 4: + default: + [instance setTrackStatus:TATrackStatusNormal]; + } +} + +void ta_enable_tracking(const char *app_id, BOOL enabled) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) enableTracking:enabled]; +} + +void ta_opt_out_tracking(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) optOutTracking]; +} + +void ta_opt_out_tracking_and_delete_user(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) optOutTrackingAndDeleteUser]; +} + +void ta_opt_in_tracking(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + [ta_getInstance(app_id_string) optInTracking]; +} + +void ta_create_light_instance(const char *delegate_app_id) { + NSString *delegate_app_id_string = delegate_app_id != NULL ? [NSString stringWithUTF8String:delegate_app_id] : nil; + ThinkingAnalyticsSDK *light = [ta_getInstance(nil) createLightInstance]; + + pthread_rwlock_wrlock(&rwlock); + if (light_instances == nil) { + light_instances = [NSMutableDictionary dictionary]; + } + + [light_instances setObject:light forKey:delegate_app_id_string]; + pthread_rwlock_unlock(&rwlock); +} + +void ta_enable_autoTrack(const char *app_id, int autoTrackEvents, const char *properties) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + [ta_getInstance(app_id_string) enableAutoTrack: autoTrackEvents properties:properties_dict]; +} + +void ta_enable_autoTrack_with_callback(const char *app_id, int autoTrackEvents) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + __block NSString * w_app_id_string = app_id_string; + [ta_getInstance(app_id_string) enableAutoTrack: autoTrackEvents callback:^NSDictionary * _Nonnull(ThinkingAnalyticsAutoTrackEventType eventType, NSDictionary * _Nonnull properties) { + NSMutableDictionary *callbackProperties = [NSMutableDictionary dictionaryWithDictionary:properties]; + [callbackProperties setObject:@(eventType) forKey:@"EventType"]; + [callbackProperties setObject:w_app_id_string forKey:@"AppID"]; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:ta_parse_date(callbackProperties) options:NSJSONWritingPrettyPrinted error:nil]; + NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + const char *ret = resultHandler("AutoTrackProperties", [jsonString UTF8String]); + NSDictionary *autoTrackProperties = nil; + ta_convertToDictionary(ret, &autoTrackProperties); + return autoTrackProperties; + }]; +} + +void ta_set_autoTrack_properties(const char *app_id, int autoTrackEvents, const char *properties) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + [ta_getInstance(app_id_string) setAutoTrackProperties: autoTrackEvents properties:properties_dict]; +} + +const char *ta_get_time_string(long long time_stamp_millis) { + NSDate *time = [NSDate dateWithTimeIntervalSince1970:time_stamp_millis / 1000.0]; + NSString *time_string = [ta_getInstance(nil) getTimeString:time]; + return ta_strdup([time_string UTF8String]); +} + +void ta_calibrate_time(long long time_stamp_millis) { + [ThinkingAnalyticsSDK calibrateTime:time_stamp_millis]; +} + +void ta_calibrate_time_with_ntp(const char *ntp_server) { + NSString *ntp_server_string = ntp_server != NULL ? [NSString stringWithUTF8String:ntp_server] : nil; + [ThinkingAnalyticsSDK calibrateTimeWithNtp:ntp_server_string]; +} + +void ta_enable_third_party_sharing(const char *app_id, int share_type, const char *properties) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSDictionary *properties_dict = nil; + ta_convertToDictionary(properties, &properties_dict); + if (properties_dict) { + [ta_getInstance(app_id_string) enableThirdPartySharing:share_type customMap:properties_dict]; + } else { + [ta_getInstance(app_id_string) enableThirdPartySharing:share_type]; + } +} diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingAnalytics.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingAnalytics.m.meta new file mode 100644 index 0000000..a8ef485 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingAnalytics.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 09c80c73f507b6d4c87031d5071a1c58 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK.meta new file mode 100644 index 0000000..48b5f3f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 23f79757cf08ea94bae41b508aa42105 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Info.plist b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Info.plist new file mode 100644 index 0000000..e500a04 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Thinking + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Info.plist.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Info.plist.meta new file mode 100644 index 0000000..7091b0e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Info.plist.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 52021cd50984a2d4cae62442fa198774 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source.meta new file mode 100644 index 0000000..eb8b8dc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fcfa66e7a927578468292698867b2c89 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch.meta new file mode 100644 index 0000000..e13784e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 274775549bf01f14ba0369ee739f32bd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush.meta new file mode 100644 index 0000000..00afcd6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2e585adef85fe2c46a99fcafc29c636a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h new file mode 100644 index 0000000..a46db5f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h @@ -0,0 +1,18 @@ +// +// TDAPPPushParams.h +// ThinkingSDK +// +// Created by Charles on 6.5.23. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern id __td_get_userNotificationCenter(void); +extern id __td_get_userNotificationCenter_delegate(void); +extern NSDictionary * __td_get_userNotificationCenterResponse(id response); +extern NSString * __td_get_userNotificationCenterRequestContentTitle(id response); +extern NSString * __td_get_userNotificationCenterRequestContentBody(id response); + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h.meta new file mode 100644 index 0000000..2856c24 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 32c5d3589963cd04d91efdf431b4250e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m new file mode 100644 index 0000000..98548ba --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m @@ -0,0 +1,70 @@ +// +// TDAPPPushParams.m +// ThinkingSDK +// +// Created by Charles on 6.5.23. +// + +#import "TDAPPPushParams.h" + +id __td_get_userNotificationCenter(void) { + Class cls = NSClassFromString(@"UNUserNotificationCenter"); + SEL sel = NSSelectorFromString(@"currentNotificationCenter"); + if ([cls respondsToSelector:sel]) { + id (*getUserNotificationCenterIMP)(id, SEL) = (NSString * (*)(id, SEL))[cls methodForSelector:sel]; + return getUserNotificationCenterIMP(cls, sel); + } + return nil; +} + +id __td_get_userNotificationCenter_delegate(void) { + Class cls = NSClassFromString(@"UNUserNotificationCenter"); + SEL sel = NSSelectorFromString(@"currentNotificationCenter"); + SEL delegateSel = NSSelectorFromString(@"delegate"); + if ([cls respondsToSelector:sel]) { + id (*getUserNotificationCenterIMP)(id, SEL) = (id (*)(id, SEL))[cls methodForSelector:sel]; + id center = getUserNotificationCenterIMP(cls, sel); + if (center) { + id (*getUserNotificationCenterDelegateIMP)(id, SEL) = (id (*)(id, SEL))[center methodForSelector:delegateSel]; + id delegate = getUserNotificationCenterDelegateIMP(center, delegateSel); + return delegate; + } + } + return nil; +} + +NSDictionary * __td_get_userNotificationCenterResponse(id response) { + + @try { + if ([response isKindOfClass:[NSClassFromString(@"UNNotificationResponse") class]]) { + return [response valueForKeyPath:@"notification.request.content.userInfo"]; + } + } @catch (NSException *exception) { + + } + return nil; +} + +NSString * __td_get_userNotificationCenterRequestContentTitle(id response) { + + @try { + if ([response isKindOfClass:[NSClassFromString(@"UNNotificationResponse") class]]) { + return [response valueForKeyPath:@"notification.request.content.title"]; + } + } @catch (NSException *exception) { + + } + return nil; +} + +NSString * __td_get_userNotificationCenterRequestContentBody(id response) { + + @try { + if ([response isKindOfClass:[NSClassFromString(@"UNNotificationResponse") class]]) { + return [response valueForKeyPath:@"notification.request.content.body"]; + } + } @catch (NSException *exception) { + + } + return nil; +} diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m.meta new file mode 100644 index 0000000..12d2b75 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a2b06d0e8887f27468ea036fd26d2a42 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h new file mode 100644 index 0000000..3960b15 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h @@ -0,0 +1,14 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAppDelegateProxyManager : NSObject + ++ (instancetype)defaultManager; + +- (void)proxyNotifications; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h.meta new file mode 100644 index 0000000..ed87a35 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4410b4aaaa5a62549a9c6d948a8d1577 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m new file mode 100644 index 0000000..3989735 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m @@ -0,0 +1,49 @@ + +#import "TDAppDelegateProxyManager.h" +#import "TDApplicationDelegateProxy.h" +#import "TDNewSwizzle.h" +#import "UIApplication+TDPushClick.h" +#import "TDMethodHelper.h" + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +#import "TDUNUserNotificationCenterDelegateProxy.h" +#endif + +@implementation TDAppDelegateProxyManager + ++ (instancetype)defaultManager { + static dispatch_once_t onceToken; + static TDAppDelegateProxyManager *manager = nil; + dispatch_once(&onceToken, ^{ + manager = [[TDAppDelegateProxyManager alloc] init]; + }); + return manager; +} + +- (void)proxyNotifications NS_EXTENSION_UNAVAILABLE_IOS(""){ + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [TDMethodHelper swizzleRespondsToSelector]; + + [TDApplicationDelegateProxy resolveOptionalSelectorsForDelegate:[UIApplication sharedApplication].delegate]; + [TDApplicationDelegateProxy proxyDelegate:[UIApplication sharedApplication].delegate selectors:[NSSet setWithArray:@[@"application:didReceiveLocalNotification:", + @"application:didReceiveRemoteNotification:fetchCompletionHandler:", + @"application:handleOpenURL:", + @"application:openURL:options:", + @"application:continueUserActivity:restorationHandler:", + @"application:performActionForShortcutItem:completionHandler:"]]]; + if (@available(iOS 10.0, *)) { + if ([UNUserNotificationCenter currentNotificationCenter].delegate) { + [TDUNUserNotificationCenterDelegateProxy proxyDelegate:[UNUserNotificationCenter currentNotificationCenter].delegate selectors:[NSSet setWithArray:@[@"userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:"]]]; + } + NSError *error = NULL; + [UNUserNotificationCenter td_new_swizzleMethod:@selector(setDelegate:) withMethod:@selector(thinkingdata_setDelegate:) error:&error]; + if (error) { + NSLog(@"proxy notification delegate error: %@", error); + } + } + }); +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m.meta new file mode 100644 index 0000000..86bd500 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: abfec29fce672064aab3a832900a873e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h new file mode 100644 index 0000000..1b1a54c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h @@ -0,0 +1,10 @@ + +#import "TDDelegateProxy.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDApplicationDelegateProxy : TDDelegateProxy + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h.meta new file mode 100644 index 0000000..45d281e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ca10079e00bccb14a87fcf2f6bd06b08 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m new file mode 100644 index 0000000..beb05b4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m @@ -0,0 +1,119 @@ + +#import "TDApplicationDelegateProxy.h" +#import "TDClassHelper.h" +#import "NSObject+TDDelegateProxy.h" +#import "UIApplication+TDPushClick.h" +#import +#import "TDPresetProperties+TDDisProperties.h" +#import "TDAppLaunchReason.h" +#import "TDCommonUtil.h" + +@implementation TDApplicationDelegateProxy + +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + SEL selector = @selector(application:didReceiveRemoteNotification:fetchCompletionHandler:); + [TDApplicationDelegateProxy invokeWithTarget:self selector:selector, application, userInfo, completionHandler]; + [TDApplicationDelegateProxy trackEventWithTarget:self application:application remoteNotification:userInfo]; +} + +- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { + SEL selector = @selector(application:didReceiveLocalNotification:); + [TDApplicationDelegateProxy invokeWithTarget:self selector:selector, application, notification]; + [TDApplicationDelegateProxy trackEventWithTarget:self application:application localNotification:notification]; +} + +- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> * _Nullable))restorationHandler { + SEL selector = @selector(application:continueUserActivity:restorationHandler:); + if (![TDPresetProperties disableStartReason]) { + [[TDAppLaunchReason sharedInstance] clearAppLaunchParams]; + [TDAppLaunchReason sharedInstance].appLaunchParams = @{@"url": [TDCommonUtil string: userActivity.webpageURL.absoluteString],@"data":@{}}; + } + return [TDApplicationDelegateProxy invokeReturnBOOLWithTarget:self selector:selector arg1:application arg2:userActivity arg3:restorationHandler]; +} + +- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { + SEL selector = @selector(application:openURL:options:); + if (![TDPresetProperties disableStartReason]) { + [[TDAppLaunchReason sharedInstance] clearAppLaunchParams]; + [TDAppLaunchReason sharedInstance].appLaunchParams = @{@"url": [TDCommonUtil string:url.absoluteString],@"data":@{}}; + } + return [TDApplicationDelegateProxy invokeReturnBOOLWithTarget:self selector:selector arg1:app arg2:url arg3:options]; +} + +- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { + SEL selector = @selector(application:handleOpenURL:); + if (![TDPresetProperties disableStartReason]) { + [[TDAppLaunchReason sharedInstance] clearAppLaunchParams]; + [TDAppLaunchReason sharedInstance].appLaunchParams = @{@"url": [TDCommonUtil string:url.absoluteString], @"data":@{}}; + } + return [TDApplicationDelegateProxy invokeReturnBOOLWithTarget:self selector:selector arg1:application arg2:url]; +} + +- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler{ + SEL selector = @selector(application:performActionForShortcutItem:completionHandler:); + [TDApplicationDelegateProxy invokeWithTarget:self selector:selector, application, shortcutItem, completionHandler]; + if (![TDPresetProperties disableStartReason]) { + [[TDAppLaunchReason sharedInstance] clearAppLaunchParams]; + [TDAppLaunchReason sharedInstance].appLaunchParams = @{@"url": @"",@"data": [TDCommonUtil dictionary:shortcutItem.userInfo]}; + } +} + + ++ (void)trackEventWithTarget:(NSObject *)target application:(UIApplication *)application remoteNotification:(NSDictionary *)userInfo { + + if (target != application.delegate) { + return; + } + + if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) { + NSLog(@"iOS version >= 10.0, callback for %@ was ignored.", @"application:didReceiveRemoteNotification:fetchCompletionHandler:"); + return; + } + + if (application.applicationState != UIApplicationStateInactive) { + return; + } + + if (![TDPresetProperties disableStartReason]) { + [[TDAppLaunchReason sharedInstance] clearAppLaunchParams]; + [TDAppLaunchReason sharedInstance].appLaunchParams = @{@"url": @"", @"data": [TDCommonUtil dictionary:userInfo]}; + } + +} + ++ (void)trackEventWithTarget:(NSObject *)target application:(UIApplication *)application localNotification:(UILocalNotification *)notification { + + if (target != application.delegate) { + return; + } + + BOOL isValidPushClick = NO; + if (application.applicationState == UIApplicationStateInactive) { + isValidPushClick = YES; + } + + if (!isValidPushClick) { + return; + } + + if (![TDPresetProperties disableStartReason]) { + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + properties[@"alertBody"] = notification.alertBody; + if (@available(iOS 8.2, *)) { + properties[@"alertTitle"] = notification.alertTitle; + } + [[TDAppLaunchReason sharedInstance] clearAppLaunchParams]; + [TDAppLaunchReason sharedInstance].appLaunchParams = @{@"url": @"", @"data": [TDCommonUtil dictionary:properties]}; + } +} + ++ (NSSet *)optionalSelectors { + return [NSSet setWithArray:@[@"application:didReceiveLocalNotification:", + @"application:didReceiveRemoteNotification:fetchCompletionHandler:", + @"application:handleOpenURL:", + @"application:openURL:options:", + @"application:continueUserActivity:restorationHandler:", + @"application:performActionForShortcutItem:completionHandler:"]]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m.meta new file mode 100644 index 0000000..7e351f9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6fcf40a52d81b34458cb1a00a973d114 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h new file mode 100644 index 0000000..94be8fc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h @@ -0,0 +1,16 @@ + +#import "TDDelegateProxy.h" + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDUNUserNotificationCenterDelegateProxy : TDDelegateProxy + + + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h.meta new file mode 100644 index 0000000..70d73ec --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 805747b0abfc919409c3f41976e0da46 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m new file mode 100644 index 0000000..c667448 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m @@ -0,0 +1,55 @@ + +#import "TDUNUserNotificationCenterDelegateProxy.h" +#import "TDClassHelper.h" +#import "NSObject+TDDelegateProxy.h" +#import +#import "TDAPPPushParams.h" +#import "TDPresetProperties+TDDisProperties.h" +#import "TDAppLaunchReason.h" +#import "TDCommonUtil.h" + +@implementation TDUNUserNotificationCenterDelegateProxy + +- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(ios(10.0)){ + SEL selector = @selector(userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:); + [TDUNUserNotificationCenterDelegateProxy invokeWithTarget:self selector:selector, center, response, completionHandler]; + [TDUNUserNotificationCenterDelegateProxy trackEventWithTarget:self notificationCenter:center notificationResponse:response]; +} + ++ (void)trackEventWithTarget:(NSObject *)target notificationCenter:(UNUserNotificationCenter *)center notificationResponse:(UNNotificationResponse *)response API_AVAILABLE(ios(10.0)){ + + if (target != center.delegate) { + return; + } + + if (![TDPresetProperties disableStartReason]) { + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + NSDictionary *userInfo = __td_get_userNotificationCenterResponse(response); + if (userInfo) { + [properties addEntriesFromDictionary:userInfo]; + } + properties[@"title"] = __td_get_userNotificationCenterRequestContentTitle(response); + properties[@"body"] = __td_get_userNotificationCenterRequestContentBody(response); + + [[TDAppLaunchReason sharedInstance] clearAppLaunchParams]; + [TDAppLaunchReason sharedInstance].appLaunchParams = @{@"url": @"", @"data": [TDCommonUtil dictionary:properties]}; + } + + if (![TDPresetProperties disableOpsReceiptProperties]) { + @try { + if ([response isKindOfClass:[NSClassFromString(@"UNNotificationResponse") class]]) { + NSDictionary *userInfo = [response valueForKeyPath:@"notification.request.content.userInfo"]; + [TDAppLaunchReason td_ops_push_click:userInfo]; + } + } @catch (NSException *exception) { + + } + } + +} + ++ (NSSet *)optionalSelectors { + return [NSSet setWithArray:@[@"userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:"]]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m.meta new file mode 100644 index 0000000..3ceeb61 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0e1d9a52ad6d1bb448047d9511f65af6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h new file mode 100644 index 0000000..373abc9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h @@ -0,0 +1,13 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIApplication (TDPushClick) + +- (void)thinkingdata_setDelegate:(id )delegate; +@property (nonatomic, copy, nullable) NSDictionary *thinkingdata_launchOptions; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h.meta new file mode 100644 index 0000000..13541fb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d1079c5db6551594781ca867e7f6839e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m new file mode 100644 index 0000000..0e24334 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m @@ -0,0 +1,29 @@ + +#import "UIApplication+TDPushClick.h" +#import "TDApplicationDelegateProxy.h" +#import + +static void *const kSALaunchOptions = (void *)&kSALaunchOptions; + +@implementation UIApplication (TDPushClick) + +- (void)thinkingdata_setDelegate:(id)delegate { + [TDApplicationDelegateProxy resolveOptionalSelectorsForDelegate:delegate]; + + [self thinkingdata_setDelegate:delegate]; + + if (!self.delegate) { + return; + } + [TDApplicationDelegateProxy proxyDelegate:self.delegate selectors:[NSSet setWithArray:@[@"application:didReceiveLocalNotification:", @"application:didReceiveRemoteNotification:fetchCompletionHandler:"]]]; +} + +- (NSDictionary *)thinkingdata_launchOptions { + return objc_getAssociatedObject(self, kSALaunchOptions); +} + +- (void)setThinkingdata_launchOptions:(NSDictionary *)thinkingdata_launchOptions { + objc_setAssociatedObject(self, kSALaunchOptions, thinkingdata_launchOptions, OBJC_ASSOCIATION_COPY); +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m.meta new file mode 100644 index 0000000..f4af4bd --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: cfc60789a07708e4da5c6c0d000e37ee +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h new file mode 100644 index 0000000..3b0adc8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h @@ -0,0 +1,14 @@ + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +#import +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface UNUserNotificationCenter (TDPushClick) + +- (void)thinkingdata_setDelegate:(id )delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h.meta new file mode 100644 index 0000000..19ecb08 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 649e54e5cf5169f46806d54fb005049f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m new file mode 100644 index 0000000..2ad56f6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m @@ -0,0 +1,18 @@ + +#import "UNUserNotificationCenter+TDPushClick.h" +#import "TDUNUserNotificationCenterDelegateProxy.h" + +@implementation UNUserNotificationCenter (TDPushClick) + +- (void)thinkingdata_setDelegate:(id)delegate { + + [TDUNUserNotificationCenterDelegateProxy resolveOptionalSelectorsForDelegate:delegate]; + + [self thinkingdata_setDelegate:delegate]; + if (!self.delegate) { + return; + } + [TDUNUserNotificationCenterDelegateProxy proxyDelegate:self.delegate selectors:[NSSet setWithArray:@[@"userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:"]]]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m.meta new file mode 100644 index 0000000..4d0781d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 450f884bacd370f45b28a1869861f2d8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate.meta new file mode 100644 index 0000000..239a90d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f9fdc832dd8173149b1df9df00164436 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.h new file mode 100644 index 0000000..022903c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.h @@ -0,0 +1,29 @@ + +#import +#import "TDDelegateProxyObject.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSObject (TDDelegateProxy) + +@property (nonatomic, copy, nullable) NSSet *thinkingdata_optionalSelectors; +@property (nonatomic, strong, nullable) TDDelegateProxyObject *thinkingdata_delegateObject; + +/// hook respondsToSelector to resolve optional selectors +/// @param aSelector selector +- (BOOL)thinkingdata_respondsToSelector:(SEL)aSelector; + +@end + +@interface NSProxy (TDDelegateProxy) + +@property (nonatomic, copy, nullable) NSSet *thinkingdata_optionalSelectors; +@property (nonatomic, strong, nullable) TDDelegateProxyObject *thinkingdata_delegateObject; + +/// hook respondsToSelector to resolve optional selectors +/// @param aSelector selector +- (BOOL)thinkingdata_respondsToSelector:(SEL)aSelector; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.h.meta new file mode 100644 index 0000000..ebb94ad --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b9402120bfa12c849bdbf985c51c0535 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.m new file mode 100644 index 0000000..6f94546 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.m @@ -0,0 +1,69 @@ + +#import "NSObject+TDDelegateProxy.h" +#import + +static void *const kTDNSObjectDelegateOptionalSelectorsKey = (void *)&kTDNSObjectDelegateOptionalSelectorsKey; +static void *const kTDNSObjectDelegateObjectKey = (void *)&kTDNSObjectDelegateObjectKey; + +static void *const kTDNSProxyDelegateOptionalSelectorsKey = (void *)&kTDNSProxyDelegateOptionalSelectorsKey; +static void *const kTDNSProxyDelegateObjectKey = (void *)&kTDNSProxyDelegateObjectKey; + +@implementation NSObject (TDDelegateProxy) + +- (NSSet *)thinkingdata_optionalSelectors { + return objc_getAssociatedObject(self, kTDNSObjectDelegateOptionalSelectorsKey); +} + +- (void)setThinkingdata_optionalSelectors:(NSSet *)thinkingdata_optionalSelectors { + objc_setAssociatedObject(self, kTDNSObjectDelegateOptionalSelectorsKey, thinkingdata_optionalSelectors, OBJC_ASSOCIATION_COPY); +} + +- (TDDelegateProxyObject *)thinkingdata_delegateObject { + return objc_getAssociatedObject(self, kTDNSObjectDelegateObjectKey); +} + +- (void)setThinkingdata_delegateObject:(TDDelegateProxyObject *)thinkingdata_delegateObject { + objc_setAssociatedObject(self, kTDNSObjectDelegateObjectKey, thinkingdata_delegateObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)thinkingdata_respondsToSelector:(SEL)aSelector { + if ([self thinkingdata_respondsToSelector:aSelector]) { + return YES; + } + if ([self.thinkingdata_optionalSelectors containsObject:NSStringFromSelector(aSelector)]) { + return YES; + } + return NO; +} + +@end + +@implementation NSProxy (TDDelegateProxy) + +- (NSSet *)thinkingdata_optionalSelectors { + return objc_getAssociatedObject(self, kTDNSProxyDelegateOptionalSelectorsKey); +} + +- (void)setThinkingdata_optionalSelectors:(NSSet *)thinkingdata_optionalSelectors { + objc_setAssociatedObject(self, kTDNSProxyDelegateOptionalSelectorsKey, thinkingdata_optionalSelectors, OBJC_ASSOCIATION_COPY); +} + +- (TDDelegateProxyObject *)thinkingdata_delegateObject { + return objc_getAssociatedObject(self, kTDNSProxyDelegateObjectKey); +} + +- (void)setThinkingdata_delegateObject:(TDDelegateProxyObject *)thinkingdata_delegateObject { + objc_setAssociatedObject(self, kTDNSProxyDelegateObjectKey, thinkingdata_delegateObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)thinkingdata_respondsToSelector:(SEL)aSelector { + if ([self thinkingdata_respondsToSelector:aSelector]) { + return YES; + } + if ([self.thinkingdata_optionalSelectors containsObject:NSStringFromSelector(aSelector)]) { + return YES; + } + return NO; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.m.meta new file mode 100644 index 0000000..857c225 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/NSObject+TDDelegateProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7c8d3b9224659564b9fe4db839be6c5b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.h new file mode 100644 index 0000000..48b0055 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.h @@ -0,0 +1,16 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDClassHelper : NSObject + ++ (Class _Nullable)allocateClassWithObject:(id)object className:(NSString *)className; + ++ (void)registerClass:(Class)cla; + ++ (BOOL)setObject:(id)object toClass:(Class)cla; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.h.meta new file mode 100644 index 0000000..1827a1d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7e6c0a59660b31f419175804f218c192 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.m new file mode 100644 index 0000000..4c7c01a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.m @@ -0,0 +1,36 @@ + +#import "TDClassHelper.h" +#import + +@implementation TDClassHelper + ++ (Class _Nullable)allocateClassWithObject:(id)object className:(NSString *)className { + if (!object || className.length <= 0) { + return nil; + } + Class originalClass = object_getClass(object); + Class subclass = NSClassFromString(className); + if (subclass) { + return nil; + } + subclass = objc_allocateClassPair(originalClass, className.UTF8String, 0); + if (class_getInstanceSize(originalClass) != class_getInstanceSize(subclass)) { + return nil; + } + return subclass; +} + ++ (void)registerClass:(Class)cla { + if (cla) { + objc_registerClassPair(cla); + } +} + ++ (BOOL)setObject:(id)object toClass:(Class)cla { + if (cla && object && object_getClass(object) != cla) { + return object_setClass(object, cla); + } + return NO; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.m.meta new file mode 100644 index 0000000..2789233 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDClassHelper.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 420d8a217a542bf4981ba78f9aed757c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.h new file mode 100644 index 0000000..5a14c76 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.h @@ -0,0 +1,25 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol TDHookDelegateProtocol +@optional ++ (NSSet *)optionalSelectors; + +@end + +@interface TDDelegateProxy : NSObject + ++ (void)proxyDelegate:(id)delegate selectors:(NSSet*)selectors; + ++ (void)invokeWithTarget:(NSObject *)target selector:(SEL)selector, ...; + ++ (BOOL)invokeReturnBOOLWithTarget:(NSObject *)target selector:(SEL)selector arg1:(id)arg1 arg2:(id)arg2; ++ (BOOL)invokeReturnBOOLWithTarget:(NSObject *)target selector:(SEL)selector arg1:(id)arg1 arg2:(id)arg2 arg3:(id)arg3; + ++ (void)resolveOptionalSelectorsForDelegate:(id)delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.h.meta new file mode 100644 index 0000000..5bc0f7b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4777c08c43760724ab9649b8c1a1752d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.m new file mode 100644 index 0000000..68dc63b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.m @@ -0,0 +1,200 @@ + +#import "TDDelegateProxy.h" +#import "TDClassHelper.h" +#import "TDMethodHelper.h" +#import "NSObject+TDDelegateProxy.h" +#import + +static NSString * const kTDNSObjectRemoveObserverSelector = @"removeObserver:forKeyPath:"; +static NSString * const kTDNSObjectAddObserverSelector = @"addObserver:forKeyPath:options:context:"; +static NSString * const kTDNSObjectClassSelector = @"class"; + +@implementation TDDelegateProxy + ++ (void)proxyDelegate:(id)delegate selectors:(NSSet *)selectors { + if (object_isClass(delegate) || selectors.count == 0) { + return; + } + + Class proxyClass = [self class]; + NSMutableSet *delegateSelectors = [NSMutableSet setWithSet:selectors]; + + TDDelegateProxyObject *object = [delegate thinkingdata_delegateObject]; + if (!object) { + object = [[TDDelegateProxyObject alloc] initWithDelegate:delegate proxy:proxyClass]; + [delegate setThinkingdata_delegateObject:object]; + } + + [delegateSelectors minusSet:object.selectors]; + if (delegateSelectors.count == 0) { + return; + } + + if (object.thinkingClass) { + [self addInstanceMethodWithSelectors:delegateSelectors fromClass:proxyClass toClass:object.thinkingClass]; + [object.selectors unionSet:delegateSelectors]; + + if (![object_getClass(delegate) isSubclassOfClass:object.thinkingClass]) { + [TDClassHelper setObject:delegate toClass:object.thinkingClass]; + } + return; + } + + if (object.kvoClass) { + if ([delegate isKindOfClass:NSObject.class] && ![object.selectors containsObject:kTDNSObjectRemoveObserverSelector]) { + [delegateSelectors addObject:kTDNSObjectRemoveObserverSelector]; + } + [self addInstanceMethodWithSelectors:delegateSelectors fromClass:proxyClass toClass:object.kvoClass]; + [object.selectors unionSet:delegateSelectors]; + return; + } + + Class thinkingClass = [TDClassHelper allocateClassWithObject:delegate className:object.thinkingClassName]; + [TDClassHelper registerClass:thinkingClass]; + + if ([delegate isKindOfClass:NSObject.class] && ![object.selectors containsObject:kTDNSObjectAddObserverSelector]) { + [delegateSelectors addObject:kTDNSObjectAddObserverSelector]; + } + + if (![object.selectors containsObject:kTDNSObjectClassSelector]) { + [delegateSelectors addObject:kTDNSObjectClassSelector]; + } + + [self addInstanceMethodWithSelectors:delegateSelectors fromClass:proxyClass toClass:thinkingClass]; + [object.selectors unionSet:delegateSelectors]; + + [TDClassHelper setObject:delegate toClass:thinkingClass]; +} + ++ (void)addInstanceMethodWithSelectors:(NSSet *)selectors fromClass:(Class)fromClass toClass:(Class)toClass { + for (NSString *selector in selectors) { + SEL sel = NSSelectorFromString(selector); + [TDMethodHelper addInstanceMethodWithSelector:sel fromClass:fromClass toClass:toClass]; + } +} + ++ (BOOL)invokeReturnBOOLWithTarget:(NSObject *)target selector:(SEL)selector arg1:(id)arg1 arg2:(id)arg2 arg3:(id)arg3 { + Class originalClass = target.thinkingdata_delegateObject.delegateISA; + + struct objc_super targetSuper = { + .receiver = target, + .super_class = originalClass + }; + + BOOL returnValue = NO; + @try { + returnValue = ((BOOL (*)(struct objc_super *, SEL, id, id, id))objc_msgSendSuper)(&targetSuper, selector, arg1, arg2, arg3); + } @catch (NSException *exception) { + NSLog(@"msgSendSuper with exception: %@", exception); + } @finally { + + } + return returnValue; +} + ++ (BOOL)invokeReturnBOOLWithTarget:(NSObject *)target selector:(SEL)selector arg1:(id)arg1 arg2:(id)arg2 { + Class originalClass = target.thinkingdata_delegateObject.delegateISA; + + struct objc_super targetSuper = { + .receiver = target, + .super_class = originalClass + }; + + BOOL returnValue = NO; + @try { + returnValue = ((BOOL (*)(struct objc_super *, SEL, id, id))objc_msgSendSuper)(&targetSuper, selector, arg1, arg2); + } @catch (NSException *exception) { + NSLog(@"msgSendSuper with exception: %@", exception); + } @finally { + + } + return returnValue; +} + ++ (void)invokeWithTarget:(NSObject *)target selector:(SEL)selector, ... { + Class originalClass = target.thinkingdata_delegateObject.delegateISA; + + va_list args; + va_start(args, selector); + id arg1 = nil, arg2 = nil, arg3 = nil, arg4 = nil; + NSInteger count = [NSStringFromSelector(selector) componentsSeparatedByString:@":"].count - 1; + for (NSInteger i = 0; i < count; i++) { + i == 0 ? (arg1 = va_arg(args, id)) : nil; + i == 1 ? (arg2 = va_arg(args, id)) : nil; + i == 2 ? (arg3 = va_arg(args, id)) : nil; + i == 3 ? (arg4 = va_arg(args, id)) : nil; + } + struct objc_super targetSuper = { + .receiver = target, + .super_class = originalClass + }; + + @try { + void (*func)(struct objc_super *, SEL, id, id, id, id) = (void *)&objc_msgSendSuper; + func(&targetSuper, selector, arg1, arg2, arg3, arg4); + } @catch (NSException *exception) { + NSLog(@"msgSendSuper with exception: %@", exception); + } @finally { + va_end(args); + } +} + + ++ (void)resolveOptionalSelectorsForDelegate:(id)delegate { + if (object_isClass(delegate)) { + return; + } + + NSSet *currentOptionalSelectors = ((NSObject *)delegate).thinkingdata_optionalSelectors; + NSMutableSet *optionalSelectors = [[NSMutableSet alloc] init]; + if (currentOptionalSelectors) { + [optionalSelectors unionSet:currentOptionalSelectors]; + } + + if ([self respondsToSelector:@selector(optionalSelectors)] &&[self optionalSelectors]) { + [optionalSelectors unionSet:[self optionalSelectors]]; + } + ((NSObject *)delegate).thinkingdata_optionalSelectors = [optionalSelectors copy]; +} + +@end + +#pragma mark - Class +@implementation TDDelegateProxy (Class) + +- (Class)class { + if (self.thinkingdata_delegateObject.delegateClass) { + return self.thinkingdata_delegateObject.delegateClass; + } + return [super class]; +} + +@end + +#pragma mark - KVO +@implementation TDDelegateProxy (KVO) + +- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context { + [super addObserver:observer forKeyPath:keyPath options:options context:context]; + if (self.thinkingdata_delegateObject) { + [TDMethodHelper replaceInstanceMethodWithDestinationSelector:@selector(class) sourceSelector:@selector(class) fromClass:TDDelegateProxy.class toClass:object_getClass(self)]; + } +} + +- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath { + BOOL oldClassIsKVO = [TDDelegateProxyObject isKVOClass:object_getClass(self)]; + [super removeObserver:observer forKeyPath:keyPath]; + BOOL newClassIsKVO = [TDDelegateProxyObject isKVOClass:object_getClass(self)]; + + if (oldClassIsKVO && !newClassIsKVO) { + Class delegateProxy = self.thinkingdata_delegateObject.delegateProxy; + NSSet *selectors = [self.thinkingdata_delegateObject.selectors copy]; + + [self.thinkingdata_delegateObject removeKVO]; + if ([delegateProxy respondsToSelector:@selector(proxyDelegate:selectors:)]) { + [delegateProxy proxyDelegate:self selectors:selectors]; + } + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.m.meta new file mode 100644 index 0000000..47eef32 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 315c41452d35ed64aa832c93b52b2b3a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.h new file mode 100644 index 0000000..76d5b16 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.h @@ -0,0 +1,34 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDDelegateProxyObject : NSObject + +@property (nonatomic, strong) Class delegateISA; + +@property (nonatomic, strong, nullable) Class kvoClass; + +@property (nonatomic, copy, nullable) NSString *thinkingClassName; + +@property (nonatomic, strong, readonly, nullable) Class thinkingClass; + +@property (nonatomic, strong) id delegateClass; + +@property (nonatomic, strong) Class delegateProxy; + +@property (nonatomic, strong) NSMutableSet *selectors; + +- (instancetype)initWithDelegate:(id)delegate proxy:(id)proxy; + +- (void)removeKVO; + +@end + +@interface TDDelegateProxyObject (Utils) + ++ (BOOL)isKVOClass:(Class _Nullable)cls; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.h.meta new file mode 100644 index 0000000..912e229 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 85ad8b65ff9356a47a3a7ae39c279ddb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.m new file mode 100644 index 0000000..63164d3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.m @@ -0,0 +1,56 @@ + +#import "TDDelegateProxyObject.h" +#import + +NSString * const kTDDelegateClassThinkingSuffix = @"_TD.THINKINGDATA"; +NSString * const kTDDelegateClassKVOPrefix = @"KVONotifying_"; + +@implementation TDDelegateProxyObject + +- (instancetype)initWithDelegate:(id)delegate proxy:(id)proxy { + self = [super init]; + if (self) { + _delegateProxy = proxy; + + _selectors = [NSMutableSet set]; + _delegateClass = [delegate class]; + + Class cla = object_getClass(delegate); + NSString *name = NSStringFromClass(cla); + + if ([name containsString:kTDDelegateClassKVOPrefix]) { + _delegateISA = class_getSuperclass(cla); + _kvoClass = cla; + } else if ([name containsString:kTDDelegateClassThinkingSuffix]) { + _delegateISA = class_getSuperclass(cla); + _thinkingClassName = name; + } else { + _delegateISA = cla; + _thinkingClassName = [NSString stringWithFormat:@"%@%@", name, kTDDelegateClassThinkingSuffix]; + } + } + return self; +} + +- (Class)thinkingClass { + return NSClassFromString(self.thinkingClassName); +} + +- (void)removeKVO { + self.kvoClass = nil; + self.thinkingClassName = [NSString stringWithFormat:@"%@%@", self.delegateISA, kTDDelegateClassThinkingSuffix]; + [self.selectors removeAllObjects]; +} + +@end + +#pragma mark - Utils + +@implementation TDDelegateProxyObject (Utils) + ++ (BOOL)isKVOClass:(Class _Nullable)cls { + return [NSStringFromClass(cls) containsString:kTDDelegateClassKVOPrefix]; +} + +@end + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.m.meta new file mode 100644 index 0000000..9494450 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDDelegateProxyObject.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bc0971c2929b9c34095463469ddf31cd +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.h new file mode 100644 index 0000000..68a6211 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.h @@ -0,0 +1,24 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDMethodHelper : NSObject + ++ (IMP)implementationOfMethodSelector:(SEL)selector fromClass:(Class)aClass; + ++ (void)addInstanceMethodWithSelector:(SEL)methodSelector fromClass:(Class)fromClass toClass:(Class)toClass; + ++ (void)addInstanceMethodWithDestinationSelector:(SEL)destinationSelector sourceSelector:(SEL)sourceSelector fromClass:(Class)fromClass toClass:(Class)toClass; + ++ (void)addClassMethodWithDestinationSelector:(SEL)destinationSelector sourceSelector:(SEL)sourceSelector fromClass:(Class)fromClass toClass:(Class)toClass; + ++ (IMP _Nullable)replaceInstanceMethodWithDestinationSelector:(SEL)destinationSelector sourceSelector:(SEL)sourceSelector fromClass:(Class)fromClass toClass:(Class)toClass; + +/// swizzle respondsToSelector 方法 +/// 用于处理未实现代理方法也能采集事件的逻辑 ++ (void)swizzleRespondsToSelector; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.h.meta new file mode 100644 index 0000000..cec23d2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4b944f39f6447d9459b90a9b3dcf34e8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.m new file mode 100644 index 0000000..8f0b01f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.m @@ -0,0 +1,59 @@ + +#import "TDMethodHelper.h" +#import +#import "TDNewSwizzle.h" +#import "NSObject+TDDelegateProxy.h" + +@implementation TDMethodHelper + ++ (IMP)implementationOfMethodSelector:(SEL)selector fromClass:(Class)aClass { + Method aMethod = class_getInstanceMethod(aClass, selector); + return method_getImplementation(aMethod); +} + ++ (void)addInstanceMethodWithSelector:(SEL)methodSelector fromClass:(Class)fromClass toClass:(Class)toClass { + [self addInstanceMethodWithDestinationSelector:methodSelector sourceSelector:methodSelector fromClass:fromClass toClass:toClass]; +} + ++ (void)addInstanceMethodWithDestinationSelector:(SEL)destinationSelector sourceSelector:(SEL)sourceSelector fromClass:(Class)fromClass toClass:(Class)toClass { + Method method = class_getInstanceMethod(fromClass, sourceSelector); + if (!method) { + return; + } + IMP methodIMP = method_getImplementation(method); + const char *types = method_getTypeEncoding(method); + if (!class_addMethod(toClass, destinationSelector, methodIMP, types)) { + IMP destinationIMP = [self implementationOfMethodSelector:destinationSelector fromClass:toClass]; + if (destinationIMP == methodIMP) { + return; + } + class_replaceMethod(toClass, destinationSelector, methodIMP, types); + } +} + ++ (void)addClassMethodWithDestinationSelector:(SEL)destinationSelector sourceSelector:(SEL)sourceSelector fromClass:(Class)fromClass toClass:(Class)toClass { + Method method = class_getClassMethod(fromClass, sourceSelector); + IMP methodIMP = method_getImplementation(method); + const char *types = method_getTypeEncoding(method); + if (!class_addMethod(toClass, destinationSelector, methodIMP, types)) { + class_replaceMethod(toClass, destinationSelector, methodIMP, types); + } +} + ++ (IMP _Nullable)replaceInstanceMethodWithDestinationSelector:(SEL)destinationSelector sourceSelector:(SEL)sourceSelector fromClass:(Class)fromClass toClass:(Class)toClass { + Method method = class_getInstanceMethod(fromClass, sourceSelector); + IMP methodIMP = method_getImplementation(method); + const char *types = method_getTypeEncoding(method); + return class_replaceMethod(toClass, destinationSelector, methodIMP, types); +} + ++ (void)swizzleRespondsToSelector { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [NSObject td_new_swizzleMethod:@selector(respondsToSelector:) + withMethod:@selector(thinkingdata_respondsToSelector:) + error:NULL]; + }); +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.m.meta new file mode 100644 index 0000000..34fd1c8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/HookDelegate/TDMethodHelper.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0b2861556d1c5c44eac95b8110fb3481 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle.meta new file mode 100644 index 0000000..2f75772 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8c523c7ccc8260b4296c4f5e308de62a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.h new file mode 100644 index 0000000..cb3c0ff --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.h @@ -0,0 +1,14 @@ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSObject (TDNewSwizzle) + ++ (BOOL)td_new_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError **)error_; ++ (BOOL)td_new_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError **)error_; ++ (BOOL)td_new_swizzleMethod:(SEL)origSel_ withClass:(Class)altCla_ withMethod:(SEL)altSel_ error:(NSError **)error_; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.h.meta new file mode 100644 index 0000000..9599cc0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f13e46f9ceae76041a668ce21ee63c12 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.m new file mode 100644 index 0000000..10e998e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.m @@ -0,0 +1,155 @@ + +#import "TDNewSwizzle.h" + +#if TARGET_OS_IOS + #import + #import +#else + #import +#endif + +#define TDSetNSErrorFor(FUNC, ERROR_VAR, FORMAT,...) \ + if (ERROR_VAR) { \ + NSString *errStr = [NSString stringWithFormat:@"%s: " FORMAT,FUNC,##__VA_ARGS__]; \ + *ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \ + code:-1 \ + userInfo:[NSDictionary dictionaryWithObject:errStr forKey:NSLocalizedDescriptionKey]]; \ + } +#define TDSetNSError(ERROR_VAR, FORMAT,...) TDSetNSErrorFor(__func__, ERROR_VAR, FORMAT, ##__VA_ARGS__) + +#if OBJC_API_VERSION >= 2 +#define TDGetClass(obj) object_getClass(obj) +#else +#define TDGetClass(obj) (obj ? obj->isa : Nil) +#endif + +@implementation NSObject (TDNewSwizzle) + ++ (BOOL)td_new_swizzleMethod:(SEL)origSel_ withClass:(Class)altCla_ withMethod:(SEL)altSel_ error:(NSError **)error_ { + Method origMethod = class_getInstanceMethod(self, origSel_); + if (!origMethod) { + TDSetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]); + return NO; + } + + Method altMethod = class_getInstanceMethod(altCla_, altSel_); + if (!altMethod) { + TDSetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [altCla_ class]); + return NO; + } + + class_addMethod(self, + origSel_, + class_getMethodImplementation(self, origSel_), + method_getTypeEncoding(origMethod)); + class_addMethod(altCla_, + altSel_, + class_getMethodImplementation(altCla_, altSel_), + method_getTypeEncoding(altMethod)); + + //交换之前,先对自定义方法进行添加 + BOOL didAddMethod = class_addMethod(self, + altSel_, + method_getImplementation(altMethod), + method_getTypeEncoding(altMethod)); + if (didAddMethod) { + method_exchangeImplementations(origMethod, class_getInstanceMethod(self, altSel_)); + } + return didAddMethod; +} + ++ (BOOL)td_new_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError **)error_ { +#if OBJC_API_VERSION >= 2 + Method origMethod = class_getInstanceMethod(self, origSel_); + if (!origMethod) { + TDSetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]); + return NO; + } + + Method altMethod = class_getInstanceMethod(self, altSel_); + if (!altMethod) { + TDSetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self class]); + return NO; + } + + class_addMethod(self, + origSel_, + class_getMethodImplementation(self, origSel_), + method_getTypeEncoding(origMethod)); + class_addMethod(self, + altSel_, + class_getMethodImplementation(self, altSel_), + method_getTypeEncoding(altMethod)); + + method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_)); + return YES; +#else + // Scan for non-inherited methods. + Method directOriginalMethod = NULL, directAlternateMethod = NULL; + + void *iterator = NULL; + struct objc_method_list *mlist = class_nextMethodList(self, &iterator); + while (mlist) { + int method_index = 0; + for (; method_index < mlist->method_count; method_index++) { + if (mlist->method_list[method_index].method_name == origSel_) { + assert(!directOriginalMethod); + directOriginalMethod = &mlist->method_list[method_index]; + } + if (mlist->method_list[method_index].method_name == altSel_) { + assert(!directAlternateMethod); + directAlternateMethod = &mlist->method_list[method_index]; + } + } + mlist = class_nextMethodList(self, &iterator); + } + + // If either method is inherited, copy it up to the target class to make it non-inherited. + if (!directOriginalMethod || !directAlternateMethod) { + Method inheritedOriginalMethod = NULL, inheritedAlternateMethod = NULL; + if (!directOriginalMethod) { + inheritedOriginalMethod = class_getInstanceMethod(self, origSel_); + if (!inheritedOriginalMethod) { + TDSetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); + return NO; + } + } + if (!directAlternateMethod) { + inheritedAlternateMethod = class_getInstanceMethod(self, altSel_); + if (!inheritedAlternateMethod) { + TDSetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); + return NO; + } + } + + int hoisted_method_count = !directOriginalMethod && !directAlternateMethod ? 2 : 1; + struct objc_method_list *hoisted_method_list = malloc(sizeof(struct objc_method_list) + (sizeof(struct objc_method)*(hoisted_method_count-1))); + hoisted_method_list->obsolete = NULL; // soothe valgrind - apparently ObjC runtime accesses this value and it shows as uninitialized in valgrind + hoisted_method_list->method_count = hoisted_method_count; + Method hoisted_method = hoisted_method_list->method_list; + + if (!directOriginalMethod) { + bcopy(inheritedOriginalMethod, hoisted_method, sizeof(struct objc_method)); + directOriginalMethod = hoisted_method++; + } + if (!directAlternateMethod) { + bcopy(inheritedAlternateMethod, hoisted_method, sizeof(struct objc_method)); + directAlternateMethod = hoisted_method; + } + class_addMethods(self, hoisted_method_list); + } + + // Swizzle. + IMP temp = directOriginalMethod->method_imp; + directOriginalMethod->method_imp = directAlternateMethod->method_imp; + directAlternateMethod->method_imp = temp; + + return YES; +#endif +} + ++ (BOOL)td_new_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError **)error_ { + return [TDGetClass((id)self) td_new_swizzleMethod:origSel_ withMethod:altSel_ error:error_]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.m.meta new file mode 100644 index 0000000..44905b1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/NewSwizzle/TDNewSwizzle.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 83e5e45c60e92754d917ed33e60f7552 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h new file mode 100644 index 0000000..e7b6240 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h @@ -0,0 +1,31 @@ +// +// TDAppLaunchReason.h +// ThinkingSDK +// +// Created by wwango on 2021/11/17. +// Copyright © 2021 thinkingdata. All rights reserved. + + +#import + +NS_ASSUME_NONNULL_BEGIN + +static NSDictionary *appPushClickDic; + +@interface TDAppLaunchReason : NSObject + +@property(nonatomic, copy) NSDictionary *appLaunchParams; + ++ (TDAppLaunchReason *)sharedInstance; + +- (void)clearAppLaunchParams; + ++ (void)td_ops_push_click:(NSDictionary *)userInfo; + ++ (NSDictionary*)getAppPushDic; + ++ (void)clearAppPushParams; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h.meta new file mode 100644 index 0000000..72d0ba5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 265a13baa4b98574a93342705cae65eb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m new file mode 100644 index 0000000..08d07d1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m @@ -0,0 +1,177 @@ +// +// TDAppLaunchReason.m +// ThinkingSDK +// +// Created by wwango on 2021/11/17. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDAppLaunchReason.h" +#import +#import "TDCommonUtil.h" +#import "TDPresetProperties+TDDisProperties.h" +#import "TDAppState.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDAppDelegateProxyManager.h" +#import "TAPushClickEvent.h" + +@implementation TDAppLaunchReason + + ++ (void)load { + + [TDPresetProperties disPresetProperties]; + + [[NSNotificationCenter defaultCenter] addObserver:[TDAppLaunchReason sharedInstance] selector:@selector(_applicationDidFinishLaunchingNotification:) name:UIApplicationDidFinishLaunchingNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:[TDAppLaunchReason sharedInstance] selector:@selector(_applicationDidEnterBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil]; +} + ++ (void)td_ops_push_click:(NSDictionary *)userInfo { + + @try { + if ([userInfo.allKeys containsObject:@"te_extras"] && [userInfo[@"te_extras"] isKindOfClass:[NSString class]]) { + NSData *jsonData = [userInfo[@"te_extras"] dataUsingEncoding:NSUTF8StringEncoding]; + NSError *err; + NSDictionary *responseDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err]; + NSDictionary *opsReceiptProperties = responseDic[@"#ops_receipt_properties"]; + if ([opsReceiptProperties isKindOfClass:[NSString class]]) { + NSString *opsStr = (NSString *)opsReceiptProperties; + opsReceiptProperties = [NSJSONSerialization JSONObjectWithData:[opsStr dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&err]; + } + if (opsReceiptProperties && [opsReceiptProperties isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *dic = [ThinkingAnalyticsSDK _getAllInstances]; + if(dic == nil || dic.count == 0){ + appPushClickDic = opsReceiptProperties; + }else{ + for (NSString *instanceToken in dic.allKeys) { + ThinkingAnalyticsSDK *instance = dic[instanceToken]; + TAPushClickEvent *pushEvent = [[TAPushClickEvent alloc]initWithName: @"ops_push_click"]; + pushEvent.ops = opsReceiptProperties; + [instance autoTrackWithEvent:pushEvent properties:@{}]; + [instance flush]; + } + } + } + } + } @catch (NSException *exception) { + + } +} + ++ (NSDictionary *)getAppPushDic{ + return appPushClickDic; +} + ++ (void)clearAppPushParams{ + appPushClickDic = nil; +} + ++ (TDAppLaunchReason *)sharedInstance { + static dispatch_once_t onceToken; + static TDAppLaunchReason *appLaunchManager; + + dispatch_once(&onceToken, ^{ + appLaunchManager = [TDAppLaunchReason new]; + }); + return appLaunchManager; +} + +- (void)clearAppLaunchParams { + self.appLaunchParams = @{@"url":@"", + @"data":@{}}; +} + +- (void)_applicationDidEnterBackgroundNotification:(NSNotification *)notification { + [self clearAppLaunchParams]; +} + +// 拦截冷启动和热启动的参数 +- (void)_applicationDidFinishLaunchingNotification:(NSNotification *)notification { + + __weak TDAppLaunchReason *weakSelf = self; + + NSDictionary *launchOptions = notification.userInfo; + NSString *url = [self getInitDeeplink:launchOptions]; + NSDictionary *data = [self getInitData:launchOptions]; + + // 发送推送事件 + if (![TDPresetProperties disableOpsReceiptProperties] && launchOptions) { + NSDictionary *remoteNotification = [launchOptions objectForKey:@"UIApplicationLaunchOptionsRemoteNotificationKey"]; + [TDAppLaunchReason td_ops_push_click:remoteNotification]; + } + + // 记录冷启动启动原因 + if (![TDPresetProperties disableStartReason]) { + + if (!launchOptions) { + [weakSelf clearAppLaunchParams]; + } else if ([url isKindOfClass:[NSString class]] && url.length) { + self.appLaunchParams = @{@"url": [TDCommonUtil string:url], + @"data": @{}}; + } else { + self.appLaunchParams = @{@"url": @"", + @"data": [TDCommonUtil dictionary:data]}; + } + } + + UIApplication *application = [TDAppState sharedApplication]; + id applicationDelegate = [application delegate]; + if (applicationDelegate == nil) + { + return; + } + + if (![TDPresetProperties disableStartReason]) { + [[TDAppDelegateProxyManager defaultManager] proxyNotifications]; + } + + if (![TDPresetProperties disableOpsReceiptProperties]) { + [[TDAppDelegateProxyManager defaultManager] proxyNotifications]; + } +} + +- (NSString *)getInitDeeplink:(NSDictionary *)launchOptions { + + if (!launchOptions || ![launchOptions isKindOfClass:[NSDictionary class]]) { + return @""; + } + + if ([launchOptions isKindOfClass:[NSDictionary class]] && + [launchOptions.allKeys containsObject:UIApplicationLaunchOptionsURLKey]) { + + return launchOptions[UIApplicationLaunchOptionsURLKey]; + + } else if ([launchOptions isKindOfClass:[NSDictionary class]] && + [launchOptions.allKeys containsObject:UIApplicationLaunchOptionsUserActivityDictionaryKey]) { + + NSDictionary *userActivityDictionary = launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey]; + NSString *type = userActivityDictionary[UIApplicationLaunchOptionsUserActivityTypeKey]; + if ([type isEqualToString:NSUserActivityTypeBrowsingWeb]) { + NSUserActivity *userActivity = userActivityDictionary[@"UIApplicationLaunchOptionsUserActivityKey"]; + return userActivity.webpageURL.absoluteString; + } + } + return @""; +} + +- (NSDictionary *)getInitData:(NSDictionary *)launchOptions { + + if (!launchOptions || ![launchOptions isKindOfClass:[NSDictionary class]]) { + return @{}; + } + + if ([launchOptions.allKeys containsObject:UIApplicationLaunchOptionsLocalNotificationKey]) { + // 本地推送可能会解析不出alertbody,这里特殊处理一下 + UILocalNotification *notification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + properties[@"alertBody"] = notification.alertBody; + if (@available(iOS 8.2, *)) { + properties[@"alertTitle"] = notification.alertTitle; + } + return properties; + } + + return launchOptions; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m.meta new file mode 100644 index 0000000..da9f0e7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 504f113ee087c6c40bbe3ab24800661c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle.meta new file mode 100644 index 0000000..7116d96 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ddeb7d21ec2b50d40b2d4448337958ff +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.h new file mode 100644 index 0000000..f63165d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.h @@ -0,0 +1,47 @@ +// +// TAAppLifeCycle.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/28. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// APP life cycle +typedef NS_ENUM(NSUInteger, TAAppLifeCycleState) { + TAAppLifeCycleStateInit = 1, // init status + TAAppLifeCycleStateBackgroundStart, + TAAppLifeCycleStateStart, + TAAppLifeCycleStateEnd, + TAAppLifeCycleStateTerminate, +}; + +/// When the life cycle status is about to change, this notification will be sent +/// object: The object is the current life cycle object +/// userInfo: Contains two keys kTAAppLifeCycleNewStateKey and kTAAppLifeCycleOldStateKey +extern NSNotificationName const kTAAppLifeCycleStateWillChangeNotification; + +/// When the life cycle status changes, this notification will be sent +/// object: The object is the current lifecycle object +/// userInfo: Contains two keys kTAAppLifeCycleNewStateKey and kTAAppLifeCycleOldStateKey +extern NSNotificationName const kTAAppLifeCycleStateDidChangeNotification; + +/// In the status change notification, get the new status +extern NSString * const kTAAppLifeCycleNewStateKey; + +/// In the status change notification, get the status before the change +extern NSString * const kTAAppLifeCycleOldStateKey; + +@interface TAAppLifeCycle : NSObject + +@property (nonatomic, assign, readonly) TAAppLifeCycleState state; + ++ (void)startMonitor; + ++ (instancetype)shareInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.h.meta new file mode 100644 index 0000000..fd33470 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 610fdfd3759c45f439e92e6bf7db0aa2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.m new file mode 100644 index 0000000..ee53f7f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.m @@ -0,0 +1,246 @@ +// +// TAAppLifeCycle.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/28. +// + +#import "TAAppLifeCycle.h" +#import "TDAppState.h" + +#if TARGET_OS_IOS +#import +#endif + +#if __has_include() +#import +#else +#import "TDLogging.h" +#endif + +NSNotificationName const kTAAppLifeCycleStateWillChangeNotification = @"cn.thinkingdata.TAAppLifeCycleStateWillChange"; +NSNotificationName const kTAAppLifeCycleStateDidChangeNotification = @"cn.thinkingdata.TAAppLifeCycleStateDidChange"; +NSString * const kTAAppLifeCycleNewStateKey = @"new"; +NSString * const kTAAppLifeCycleOldStateKey = @"old"; + + +@interface TAAppLifeCycle () +/// status +@property (nonatomic, assign) TAAppLifeCycleState state; + +@end + +@implementation TAAppLifeCycle + ++ (void)startMonitor { + [TAAppLifeCycle shareInstance]; +} + ++ (instancetype)shareInstance { + static dispatch_once_t onceToken; + static TAAppLifeCycle *appLifeCycle = nil; + dispatch_once(&onceToken, ^{ + appLifeCycle = [[TAAppLifeCycle alloc] init]; + }); + return appLifeCycle; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _state = TAAppLifeCycleStateInit; + [self registerListeners]; + [self setupLaunchedState]; + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)registerListeners { + if ([TDAppState runningInAppExtension]) { + return; + } + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; +#if TARGET_OS_IOS + [notificationCenter addObserver:self + selector:@selector(applicationDidBecomeActive:) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + + [notificationCenter addObserver:self + selector:@selector(applicationDidEnterBackground:) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + + [notificationCenter addObserver:self + selector:@selector(applicationWillTerminate:) + name:UIApplicationWillTerminateNotification + object:nil]; + +#elif TARGET_OS_OSX + +// [notificationCenter addObserver:self +// selector:@selector(applicationDidFinishLaunching:) +// name:NSApplicationDidFinishLaunchingNotification +// object:nil]; +// +// [notificationCenter addObserver:self +// selector:@selector(applicationDidBecomeActive:) +// name:NSApplicationDidBecomeActiveNotification +// object:nil]; +// [notificationCenter addObserver:self +// selector:@selector(applicationDidResignActive:) +// name:NSApplicationDidResignActiveNotification +// object:nil]; +// +// [notificationCenter addObserver:self +// selector:@selector(applicationWillTerminate:) +// name:NSApplicationWillTerminateNotification +// object:nil]; +#endif +} + +- (void)setupLaunchedState { + if ([TDAppState runningInAppExtension]) { + return; + } + + dispatch_block_t mainThreadBlock = ^(){ +#if TARGET_OS_IOS + UIApplication *application = [TDAppState sharedApplication]; + BOOL isAppStateBackground = application.applicationState == UIApplicationStateBackground; +#else + BOOL isAppStateBackground = NO; +#endif + [TDAppState shareInstance].relaunchInBackground = isAppStateBackground; + + self.state = isAppStateBackground ? TAAppLifeCycleStateBackgroundStart : TAAppLifeCycleStateStart; + }; + + if (@available(iOS 13.0, *)) { + // The reason why iOS 13 and above modify the status in the block of the asynchronous main queue:+ + // 1. Make sure that the initialization of the SDK has been completed before sending the appstatus change notification. This can ensure that the public properties have been set when the automatic collection management class sends the app_start event (in fact, it can also be achieved by listening to UIApplicationDidFinishLaunchingNotification) + // 2. In a project that contains SceneDelegate, it is accurate to delay obtaining applicationState (obtaining by listening to UIApplicationDidFinishLaunchingNotification is inaccurate) + dispatch_async(dispatch_get_main_queue(), mainThreadBlock); + } else { + // iOS 13 and below handle background wakeup and cold start (non-delayed initialization) by listening to the notification of UIApplicationDidFinishLaunchingNotification: + // 1. When iOS 13 or later wakes up in the background, the block of the asynchronous main queue will not be executed. So you need to listen to UIApplicationDidFinishLaunchingNotification at the same time + // 2. iOS 13 and below will not contain SceneDelegate +#if TARGET_OS_IOS + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationDidFinishLaunching:) + name:UIApplicationDidFinishLaunchingNotification + object:nil]; +#endif + // Handle cold start below iOS 13, where the client delays initialization. UIApplicationDidFinishLaunchingNotification notification has been missed when lazy initialization + dispatch_async(dispatch_get_main_queue(), mainThreadBlock); + } +} + +//MARK: - Notification Action + +- (void)applicationDidFinishLaunching:(NSNotification *)notification { +#if TARGET_OS_IOS + UIApplication *application = [TDAppState sharedApplication]; + BOOL isAppStateBackground = application.applicationState == UIApplicationStateBackground; +#else + BOOL isAppStateBackground = NO; +#endif + + [TDAppState shareInstance].relaunchInBackground = isAppStateBackground; + + self.state = isAppStateBackground ? TAAppLifeCycleStateBackgroundStart : TAAppLifeCycleStateStart; +} + +- (void)applicationDidBecomeActive:(NSNotification *)notification { + TDLogDebug(@"application did become active"); + +#if TARGET_OS_IOS + if (![notification.object isKindOfClass:[UIApplication class]]) { + return; + } + + UIApplication *application = (UIApplication *)notification.object; + if (application.applicationState != UIApplicationStateActive) { + return; + } +#elif TARGET_OS_OSX + if (![notification.object isKindOfClass:[NSApplication class]]) { + return; + } + + NSApplication *application = (NSApplication *)notification.object; + if (!application.isActive) { + return; + } +#endif + + [TDAppState shareInstance].relaunchInBackground = NO; + + self.state = TAAppLifeCycleStateStart; +} + +#if TARGET_OS_IOS +- (void)applicationDidEnterBackground:(NSNotification *)notification { + TDLogDebug(@"application did enter background"); + + if (![notification.object isKindOfClass:[UIApplication class]]) { + return; + } + + UIApplication *application = (UIApplication *)notification.object; + if (application.applicationState != UIApplicationStateBackground) { + return; + } + + self.state = TAAppLifeCycleStateEnd; +} + +#elif TARGET_OS_OSX +- (void)applicationDidResignActive:(NSNotification *)notification { + TDLogDebug(@"application did resignActive"); + + if (![notification.object isKindOfClass:[NSApplication class]]) { + return; + } + + NSApplication *application = (NSApplication *)notification.object; + if (application.isActive) { + return; + } + self.state = TAAppLifeCycleStateEnd; +} +#endif + +- (void)applicationWillTerminate:(NSNotification *)notification { + TDLogDebug(@"application will terminate"); + + self.state = TAAppLifeCycleStateTerminate; +} + +//MARK: - Setter + +- (void)setState:(TAAppLifeCycleState)state { + + if (_state == state) { + return; + } + + [TDAppState shareInstance].isActive = (state == TAAppLifeCycleStateStart); + + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:2]; + userInfo[kTAAppLifeCycleNewStateKey] = @(state); + userInfo[kTAAppLifeCycleOldStateKey] = @(_state); + + [[NSNotificationCenter defaultCenter] postNotificationName:kTAAppLifeCycleStateWillChangeNotification object:self userInfo:userInfo]; + + _state = state; + + [[NSNotificationCenter defaultCenter] postNotificationName:kTAAppLifeCycleStateDidChangeNotification object:self userInfo:userInfo]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.m.meta new file mode 100644 index 0000000..ac37c1e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TAAppLifeCycle.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c63bdf355fa2241419ab65c6105d3acf +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h new file mode 100644 index 0000000..c1053ac --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h @@ -0,0 +1,28 @@ +// +// TDAppState.h +// ThinkingSDK +// +// Created by wwango on 2021/9/24. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAppState : NSObject +/// Whether to start in the background. When the app is woken up by silently pushing the background, or when the location change wakes up the app, value = YES. (thread safe) +@property (atomic, assign) BOOL relaunchInBackground; + +/// Whether the current app is in the foreground +@property (atomic, assign) BOOL isActive; + ++ (instancetype)shareInstance; + ++ (id)sharedApplication; + ++ (BOOL)runningInAppExtension; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h.meta new file mode 100644 index 0000000..c29b515 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c75a2cc6ed1843642a1e845ac0df06f8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m new file mode 100644 index 0000000..f1427bb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m @@ -0,0 +1,48 @@ +// +// TDAppState.m +// ThinkingSDK +// +// Created by wwango on 2021/9/24. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDAppState.h" + +#if TARGET_OS_IOS +#import +#endif + +NSString *_td_lastKnownState; + +@implementation TDAppState + ++ (instancetype)shareInstance { + static dispatch_once_t onceToken; + static TDAppState *appState; + dispatch_once(&onceToken, ^{ + appState = [TDAppState new]; + }); + return appState; +} + ++ (id)sharedApplication { + +#if TARGET_OS_IOS + + if ([self runningInAppExtension]) { + return nil; + } + return [[UIApplication class] performSelector:@selector(sharedApplication)]; + +#endif + return nil; +} + ++ (BOOL)runningInAppExtension { +#if TARGET_OS_IOS + return [[[[NSBundle mainBundle] bundlePath] pathExtension] isEqualToString:@"appex"]; +#endif + return NO; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m.meta new file mode 100644 index 0000000..131681f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 624b80054b0aaa046bd67b8df72def6f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack.meta new file mode 100644 index 0000000..0bcac19 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6a2a6dbeec53a564e855dfaecb9f6128 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event.meta new file mode 100644 index 0000000..8e090f2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7f8b2a224e9edc24da27dd4ea03a3b39 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.h new file mode 100644 index 0000000..443ec95 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.h @@ -0,0 +1,17 @@ +// +// TAAppEndEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/17. +// + +#import "TAAutoTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAAppEndEvent : TAAutoTrackEvent +@property (nonatomic, copy) NSString *screenName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.h.meta new file mode 100644 index 0000000..c98589c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 40a6155cb5c236b4b8087a56f07b6b99 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.m new file mode 100644 index 0000000..eb1f866 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.m @@ -0,0 +1,23 @@ +// +// TAAppEndEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/17. +// + +#import "TAAppEndEvent.h" +#import "TDPresetProperties+TDDisProperties.h" + +@implementation TAAppEndEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + if (![TDPresetProperties disableScreenName]) { + self.properties[@"#screen_name"] = self.screenName ?: @""; + } + + return dict; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.m.meta new file mode 100644 index 0000000..13ac99d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppEndEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 70f1894193c33dd49b8c7a95f3483903 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.h new file mode 100644 index 0000000..8c61e3d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.h @@ -0,0 +1,19 @@ +// +// TAAppStartEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/17. +// + +#import "TAAutoTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAAppStartEvent : TAAutoTrackEvent +@property (nonatomic, copy) NSString *startReason; +@property (nonatomic, assign) BOOL resumeFromBackground; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.h.meta new file mode 100644 index 0000000..bb9b112 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 10585deadfd0a0b41b8af66b473a82d1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.m new file mode 100644 index 0000000..92fdad5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.m @@ -0,0 +1,27 @@ +// +// TAAppStartEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/17. +// + +#import "TAAppStartEvent.h" +#import +#import "TDPresetProperties+TDDisProperties.h" + +@implementation TAAppStartEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + if (![TDPresetProperties disableResumeFromBackground]) { + self.properties[@"#resume_from_background"] = @(self.resumeFromBackground); + } + if (![TDPresetProperties disableStartReason]) { + self.properties[@"#start_reason"] = self.startReason; + } + + return dict; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.m.meta new file mode 100644 index 0000000..c9a5b45 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAppStartEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7dc79746c3bbc9e448ed4c906d915101 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.h new file mode 100644 index 0000000..0ec76bb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.h @@ -0,0 +1,22 @@ +// +// TAAutoClickEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TAAutoTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAAutoClickEvent : TAAutoTrackEvent +@property (nonatomic, copy) NSString *elementId; +@property (nonatomic, copy) NSString *elementContent; +@property (nonatomic, copy) NSString *elementType; +@property (nonatomic, copy) NSString *elementPosition; +@property (nonatomic, copy) NSString *pageTitle; +@property (nonatomic, copy) NSString *screenName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.h.meta new file mode 100644 index 0000000..d5ff1c0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: de4bf411c264c504a864d1110c8edb45 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.m new file mode 100644 index 0000000..7db74b0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.m @@ -0,0 +1,38 @@ +// +// TAAutoClickEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TAAutoClickEvent.h" +#import "TDPresetProperties+TDDisProperties.h" + +@implementation TAAutoClickEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + if (![TDPresetProperties disableScreenName]) { + self.properties[@"#screen_name"] = self.screenName; + } + if (![TDPresetProperties disableElementId]) { + self.properties[@"#element_id"] = self.elementId; + } + if (![TDPresetProperties disableElementType]) { + self.properties[@"#element_type"] = self.elementType; + } + if (![TDPresetProperties disableElementContent]) { + self.properties[@"#element_content"] = self.elementContent; + } + if (![TDPresetProperties disableElementPosition]) { + self.properties[@"#element_position"] = self.elementPosition; + } + if (![TDPresetProperties disableTitle]) { + self.properties[@"#title"] = self.pageTitle; + } + + return dict; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.m.meta new file mode 100644 index 0000000..8b55cdb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoClickEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6822b7c6562f6234992a3529f37709f0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.h new file mode 100644 index 0000000..ae1b2bc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.h @@ -0,0 +1,20 @@ +// +// TAAutoPageViewEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TAAutoTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAAutoPageViewEvent : TAAutoTrackEvent +@property (nonatomic, copy) NSString *pageUrl; +@property (nonatomic, copy) NSString *referrer; +@property (nonatomic, copy) NSString *pageTitle; +@property (nonatomic, copy) NSString *screenName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.h.meta new file mode 100644 index 0000000..fab9689 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f3496858b20c04445b91102798553dc1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.m new file mode 100644 index 0000000..f38b51d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.m @@ -0,0 +1,31 @@ +// +// TAAutoPageViewEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TAAutoPageViewEvent.h" +#import "TDPresetProperties+TDDisProperties.h" + +@implementation TAAutoPageViewEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + if (![TDPresetProperties disableScreenName]) { + self.properties[@"#screen_name"] = self.screenName; + } + if (![TDPresetProperties disableTitle]) { + self.properties[@"#title"] = self.pageTitle; + } + if (![TDPresetProperties disableUrl]) { + self.properties[@"#url"] = self.pageUrl; + } + if (![TDPresetProperties disableReferrer]) { + self.properties[@"#referrer"] = self.referrer; + } + return dict; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.m.meta new file mode 100644 index 0000000..0f97048 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoPageViewEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 80c9b30ac6f8e9f4793c4c456666088f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.h new file mode 100644 index 0000000..b49d549 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.h @@ -0,0 +1,26 @@ +// +// TAAutoTrackEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TATrackEvent.h" +#import "TDConstant.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAAutoTrackEvent : TATrackEvent + +/// It is used to record the dynamic public properties of automatic collection events. The dynamic public properties need to be obtained in the current thread where the event occurs +@property (nonatomic, strong) NSDictionary *autoDynamicSuperProperties; + +/// Static public property for logging autocollection events +@property (nonatomic, strong) NSDictionary *autoSuperProperties; + +/// Returns the automatic collection type +- (ThinkingAnalyticsAutoTrackEventType)autoTrackEventType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.h.meta new file mode 100644 index 0000000..c868830 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3090a87679ab84a4abee1eec9aad1b89 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.m new file mode 100644 index 0000000..970a224 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.m @@ -0,0 +1,58 @@ +// +// TAAutoTrackEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TAAutoTrackEvent.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDPresetProperties+TDDisProperties.h" + +@implementation TAAutoTrackEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + // Reprocess the duration of automatic collection events, mainly app_start, app_end + // app_start app_end events are collected by the automatic collection management class. There are the following problems: the automatic collection management class and the timeTracker event duration management class are processed by listening to appLifeCycle notifications, so they are not at a precise and unified time point. There will be small errors that need to be eliminated. + // After testing, the error is less than 0.01s. + CGFloat minDuration = 0.01; + if (![TDPresetProperties disableDuration]) { + if (self.foregroundDuration > minDuration) { + self.properties[@"#duration"] = @([NSString stringWithFormat:@"%.3f", self.foregroundDuration].floatValue); + } + } + if (![TDPresetProperties disableBackgroundDuration]) { + if (self.backgroundDuration > minDuration) { + self.properties[@"#background_duration"] = @([NSString stringWithFormat:@"%.3f", self.backgroundDuration].floatValue); + } + } + + return dict; +} + +- (ThinkingAnalyticsAutoTrackEventType)autoTrackEventType { + if ([self.eventName isEqualToString:TD_APP_START_EVENT]) { + return ThinkingAnalyticsEventTypeAppStart; + } else if ([self.eventName isEqualToString:TD_APP_START_BACKGROUND_EVENT]) { + return ThinkingAnalyticsEventTypeAppStart; + } else if ([self.eventName isEqualToString:TD_APP_END_EVENT]) { + return ThinkingAnalyticsEventTypeAppEnd; + } else if ([self.eventName isEqualToString:TD_APP_VIEW_EVENT]) { + return ThinkingAnalyticsEventTypeAppViewScreen; + } else if ([self.eventName isEqualToString:TD_APP_CLICK_EVENT]) { + return ThinkingAnalyticsEventTypeAppClick; + } else if ([self.eventName isEqualToString:TD_APP_CRASH_EVENT]) { + return ThinkingAnalyticsEventTypeAppViewCrash; + } else if ([self.eventName isEqualToString:TD_APP_INSTALL_EVENT]) { + return ThinkingAnalyticsEventTypeAppInstall; + } else { + return ThinkingAnalyticsEventTypeNone; + } +} + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + [TAPropertyValidator validateAutoTrackEventPropertyKey:key value:value error:error]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.m.meta new file mode 100644 index 0000000..e2ea653 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAAutoTrackEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b29666e1c445d994991de96212385d20 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.h new file mode 100644 index 0000000..e41f275 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.h @@ -0,0 +1,16 @@ +// +// TAPushClickEvent.h +// ThinkingSDK +// +// Created by liulongbing on 2023/5/31. +// +#import "TAAutoTrackEvent.h" +NS_ASSUME_NONNULL_BEGIN + +@interface TAPushClickEvent : TAAutoTrackEvent + +@property (nonatomic, strong) NSDictionary *ops; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.h.meta new file mode 100644 index 0000000..d9795f7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fec359995ca52d242b7b0857f45a3b4d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.m new file mode 100644 index 0000000..5b4d8a4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.m @@ -0,0 +1,18 @@ +// +// TAPushClickEvent.m +// ThinkingSDK +// +// Created by liulongbing on 2023/5/31. +// + +#import "TAPushClickEvent.h" + +@implementation TAPushClickEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + self.properties[@"#ops_receipt_properties"] = self.ops; + return dict; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.m.meta new file mode 100644 index 0000000..76c6a61 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TAPushClickEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d30d06823cd9afb49b3635a39a6adcf5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackProtocol.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackProtocol.h new file mode 100644 index 0000000..b10c386 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackProtocol.h @@ -0,0 +1,80 @@ +// +// TAAutoTrackProtocol.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#ifndef TAAutoTrackProtocol_h +#define TAAutoTrackProtocol_h + +#import + +@protocol TDUIViewAutoTrackDelegate + +@optional + +/** + UITableView event properties + + @return event properties + */ +- (NSDictionary *)thinkingAnalytics_tableView:(UITableView *)tableView autoTrackPropertiesAtIndexPath:(NSIndexPath *)indexPath; + +/** + APPID UITableView event properties + + @return event properties + */ +- (NSDictionary *)thinkingAnalyticsWithAppid_tableView:(UITableView *)tableView autoTrackPropertiesAtIndexPath:(NSIndexPath *)indexPath; + +@optional + +/** + UICollectionView event properties + + @return event properties + */ +- (NSDictionary *)thinkingAnalytics_collectionView:(UICollectionView *)collectionView autoTrackPropertiesAtIndexPath:(NSIndexPath *)indexPath; + +/** + APPID UICollectionView event properties + + @return event properties + */ +- (NSDictionary *)thinkingAnalyticsWithAppid_collectionView:(UICollectionView *)collectionView autoTrackPropertiesAtIndexPath:(NSIndexPath *)indexPath; + +@end + + +@protocol TDAutoTracker + +@optional + +- (NSDictionary *)getTrackProperties; + + +- (NSDictionary *)getTrackPropertiesWithAppid; + +@end + +/** + Automatically track the page + */ +@protocol TDScreenAutoTracker + +@optional + +/** + Attributes for custom page view events + */ +- (NSString *)getScreenUrl; + +/** + Configure the properties of the APPID custom page view event + */ +- (NSDictionary *)getScreenUrlWithAppid; + +@end + +#endif /* TAAutoTrackProtocol_h */ diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackProtocol.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackProtocol.h.meta new file mode 100644 index 0000000..1b36d0d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f5cf0e391e1be6f4dae7d1d501d2efaa +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackPublicHeader.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackPublicHeader.h new file mode 100644 index 0000000..ef7ab68 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackPublicHeader.h @@ -0,0 +1,23 @@ +// +// TAAutoTrackPublicHeader.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#ifndef TAAutoTrackPublicHeader_h +#define TAAutoTrackPublicHeader_h + +#if __has_include() +#import +#else +#import "TAAutoTrackProtocol.h" +#endif + +#if __has_include() +#import +#else +#import "UIView+ThinkingAnalytics.h" +#endif + +#endif /* TAAutoTrackPublicHeader_h */ diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackPublicHeader.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackPublicHeader.h.meta new file mode 100644 index 0000000..a021284 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TAAutoTrackPublicHeader.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e5d98e43931f6fd4a8ccf60e1c84f678 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h new file mode 100644 index 0000000..24795ef --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h @@ -0,0 +1,34 @@ +#import "ThinkingAnalyticsSDK.h" +#import "TDColdStartTracker.h" +#import "TDHotStartTracker.h" +#import "TDInstallTracker.h" + +FOUNDATION_EXTERN NSString * const TD_EVENT_PROPERTY_TITLE; +FOUNDATION_EXTERN NSString * const TD_EVENT_PROPERTY_URL_PROPERTY; +FOUNDATION_EXTERN NSString * const TD_EVENT_PROPERTY_REFERRER_URL; +FOUNDATION_EXTERN NSString * const TD_EVENT_PROPERTY_SCREEN_NAME; +FOUNDATION_EXTERN NSString * const TD_EVENT_PROPERTY_ELEMENT_ID; +FOUNDATION_EXTERN NSString * const TD_EVENT_PROPERTY_ELEMENT_TYPE; +FOUNDATION_EXTERN NSString * const TD_EVENT_PROPERTY_ELEMENT_CONTENT; +FOUNDATION_EXTERN NSString * const TD_EVENT_PROPERTY_ELEMENT_POSITION; + +@interface TDAutoTrackManager : NSObject + ++ (instancetype)sharedManager; + +- (void)trackEventView:(UIView *)view; + +- (void)trackEventView:(UIView *)view withIndexPath:(NSIndexPath *)indexPath; + +- (void)trackWithAppid:(NSString *)appid withOption:(ThinkingAnalyticsAutoTrackEventType)type; + +- (void)viewControlWillAppear:(UIViewController *)controller; + ++ (UIViewController *)topPresentedViewController; + +#pragma mark - UNAVAILABLE +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h.meta new file mode 100644 index 0000000..8d870c4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8b472c2607fe7874b967a410a658ac97 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m new file mode 100644 index 0000000..dafd005 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m @@ -0,0 +1,764 @@ +#import "TDAutoTrackManager.h" + +#import "TDSwizzler.h" +#import "UIViewController+AutoTrack.h" +#import "NSObject+TDSwizzle.h" +#import "TDJSONUtil.h" +#import "UIApplication+AutoTrack.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDPublicConfig.h" +#import "TAAutoClickEvent.h" +#import "TAAutoPageViewEvent.h" +#import "TAAppLifeCycle.h" +#import "TDAppState.h" +#import "TDRunTime.h" +#import "TDPresetProperties+TDDisProperties.h" + +#import "TAAppStartEvent.h" +#import "TAAppEndEvent.h" +#import "TDAppEndTracker.h" +#import "TDColdStartTracker.h" +#import "TDInstallTracker.h" +#import "TDAppState.h" + +#ifndef TD_LOCK +#define TD_LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); +#endif + +#ifndef TD_UNLOCK +#define TD_UNLOCK(lock) dispatch_semaphore_signal(lock); +#endif + +NSString * const TD_EVENT_PROPERTY_TITLE = @"#title"; +NSString * const TD_EVENT_PROPERTY_URL_PROPERTY = @"#url"; +NSString * const TD_EVENT_PROPERTY_REFERRER_URL = @"#referrer"; +NSString * const TD_EVENT_PROPERTY_SCREEN_NAME = @"#screen_name"; +NSString * const TD_EVENT_PROPERTY_ELEMENT_ID = @"#element_id"; +NSString * const TD_EVENT_PROPERTY_ELEMENT_TYPE = @"#element_type"; +NSString * const TD_EVENT_PROPERTY_ELEMENT_CONTENT = @"#element_content"; +NSString * const TD_EVENT_PROPERTY_ELEMENT_POSITION = @"#element_position"; + +@interface TDAutoTrackManager () +@property (atomic, strong) NSMutableDictionary *autoTrackOptions; +@property (nonatomic, strong, nonnull) dispatch_semaphore_t trackOptionLock; +@property (atomic, copy) NSString *referrerViewControllerUrl; +@property (nonatomic, strong) TDHotStartTracker *appHotStartTracker; +@property (nonatomic, strong) TDAppEndTracker *appEndTracker; +@property (nonatomic, strong) TDColdStartTracker *appColdStartTracker; +@property (nonatomic, strong) TDInstallTracker *appInstallTracker; + +@end + + +@implementation TDAutoTrackManager + +#pragma mark - Public + ++ (instancetype)sharedManager { + static dispatch_once_t once; + static TDAutoTrackManager *manager = nil; + dispatch_once(&once, ^{ + manager = [[[TDAutoTrackManager class] alloc] init]; + manager.autoTrackOptions = [NSMutableDictionary new]; + manager.trackOptionLock = dispatch_semaphore_create(1); + [manager registerAppLifeCycleListener]; + }); + return manager; +} + +- (void)trackEventView:(UIView *)view { + [self trackEventView:view withIndexPath:nil]; +} + +- (void)trackEventView:(UIView *)view withIndexPath:(NSIndexPath *)indexPath { + if (view.thinkingAnalyticsIgnoreView) { + return; + } + + NSString *elementId = nil; + NSString *elementType = nil; + NSString *elementContent = nil; + NSString *elementPosition = nil; + NSString *elementPageTitle = nil; + NSString *elementScreenName = nil; + NSMutableDictionary *yx_customProperties = [NSMutableDictionary dictionary]; + + + elementId = view.thinkingAnalyticsViewID; + elementType = NSStringFromClass([view class]); + + + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + properties[TD_EVENT_PROPERTY_ELEMENT_ID] = view.thinkingAnalyticsViewID; + properties[TD_EVENT_PROPERTY_ELEMENT_TYPE] = NSStringFromClass([view class]); + UIViewController *viewController = [self viewControllerForView:view]; + if (viewController != nil) { + NSString *screenName = NSStringFromClass([viewController class]); + properties[TD_EVENT_PROPERTY_SCREEN_NAME] = screenName; + + elementScreenName = screenName; + + NSString *controllerTitle = [self titleFromViewController:viewController]; + if (controllerTitle) { + properties[TD_EVENT_PROPERTY_TITLE] = controllerTitle; + + elementPageTitle = controllerTitle; + } + } + + + NSDictionary *propDict = view.thinkingAnalyticsViewProperties; + if ([propDict isKindOfClass:[NSDictionary class]]) { + [properties addEntriesFromDictionary:propDict]; + + [yx_customProperties addEntriesFromDictionary:propDict]; + } + + UIView *contentView; + NSDictionary *propertyWithAppid; + if (indexPath) { + if ([view isKindOfClass:[UITableView class]]) { + UITableView *tableView = (UITableView *)view; + contentView = [tableView cellForRowAtIndexPath:indexPath]; + if (!contentView) { + [tableView layoutIfNeeded]; + contentView = [tableView cellForRowAtIndexPath:indexPath]; + } + properties[TD_EVENT_PROPERTY_ELEMENT_POSITION] = [NSString stringWithFormat: @"%ld:%ld", (unsigned long)indexPath.section, (unsigned long)indexPath.row]; + + elementPosition = [NSString stringWithFormat: @"%ld:%ld", (unsigned long)indexPath.section, (unsigned long)indexPath.row]; + + if ([tableView.thinkingAnalyticsDelegate conformsToProtocol:@protocol(TDUIViewAutoTrackDelegate)]) { + if ([tableView.thinkingAnalyticsDelegate respondsToSelector:@selector(thinkingAnalytics_tableView:autoTrackPropertiesAtIndexPath:)]) { + NSDictionary *dic = [view.thinkingAnalyticsDelegate thinkingAnalytics_tableView:tableView autoTrackPropertiesAtIndexPath:indexPath]; + if ([dic isKindOfClass:[NSDictionary class]]) { + [properties addEntriesFromDictionary:dic]; + + [yx_customProperties addEntriesFromDictionary:dic]; + + } + } + + if ([tableView.thinkingAnalyticsDelegate respondsToSelector:@selector(thinkingAnalyticsWithAppid_tableView:autoTrackPropertiesAtIndexPath:)]) { + propertyWithAppid = [view.thinkingAnalyticsDelegate thinkingAnalyticsWithAppid_tableView:tableView autoTrackPropertiesAtIndexPath:indexPath]; + } + } + } else if ([view isKindOfClass:[UICollectionView class]]) { + UICollectionView *collectionView = (UICollectionView *)view; + contentView = [collectionView cellForItemAtIndexPath:indexPath]; + if (!contentView) { + [collectionView layoutIfNeeded]; + contentView = [collectionView cellForItemAtIndexPath:indexPath]; + } + properties[TD_EVENT_PROPERTY_ELEMENT_POSITION] = [NSString stringWithFormat: @"%ld:%ld", (unsigned long)indexPath.section, (unsigned long)indexPath.row]; + + elementPosition = [NSString stringWithFormat: @"%ld:%ld", (unsigned long)indexPath.section, (unsigned long)indexPath.row]; + + if ([collectionView.thinkingAnalyticsDelegate conformsToProtocol:@protocol(TDUIViewAutoTrackDelegate)]) { + if ([collectionView.thinkingAnalyticsDelegate respondsToSelector:@selector(thinkingAnalytics_collectionView:autoTrackPropertiesAtIndexPath:)]) { + NSDictionary *dic = [view.thinkingAnalyticsDelegate thinkingAnalytics_collectionView:collectionView autoTrackPropertiesAtIndexPath:indexPath]; + if ([dic isKindOfClass:[NSDictionary class]]) { + [properties addEntriesFromDictionary:dic]; + + [yx_customProperties addEntriesFromDictionary:dic]; + + } + } + if ([collectionView.thinkingAnalyticsDelegate respondsToSelector:@selector(thinkingAnalyticsWithAppid_collectionView:autoTrackPropertiesAtIndexPath:)]) { + propertyWithAppid = [view.thinkingAnalyticsDelegate thinkingAnalyticsWithAppid_collectionView:collectionView autoTrackPropertiesAtIndexPath:indexPath]; + } + } + } + } else { + contentView = view; + properties[TD_EVENT_PROPERTY_ELEMENT_POSITION] = [TDAutoTrackManager getPosition:contentView]; + + elementPosition = [TDAutoTrackManager getPosition:contentView]; + + } + + NSString *content = [TDAutoTrackManager getText:contentView]; + if (content.length > 0) { + properties[TD_EVENT_PROPERTY_ELEMENT_CONTENT] = content; + + elementContent = content; + + } + + + + NSDate *trackDate = [NSDate date]; + for (NSString *appid in self.autoTrackOptions) { + + ThinkingAnalyticsAutoTrackEventType type = (ThinkingAnalyticsAutoTrackEventType)[self.autoTrackOptions[appid] integerValue]; + + if (type & ThinkingAnalyticsEventTypeAppClick) { + + + + + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK sharedInstanceWithAppid:appid]; + NSMutableDictionary *trackProperties = [properties mutableCopy]; + + NSMutableDictionary *yx_trackProperties = [yx_customProperties mutableCopy]; + + if ([instance isViewTypeIgnored:[view class]]) { + continue; + } + NSDictionary *ignoreViews = view.thinkingAnalyticsIgnoreViewWithAppid; + if (ignoreViews != nil && [[ignoreViews objectForKey:appid] isKindOfClass:[NSNumber class]]) { + BOOL ignore = [[ignoreViews objectForKey:appid] boolValue]; + if (ignore) + continue; + } + + if ([instance isViewControllerIgnored:viewController]) { + continue; + } + + NSDictionary *viewIDs = view.thinkingAnalyticsViewIDWithAppid; + if (viewIDs != nil && [viewIDs objectForKey:appid]) { + trackProperties[TD_EVENT_PROPERTY_ELEMENT_ID] = [viewIDs objectForKey:appid]; + + elementId = [viewIDs objectForKey:appid]; + + } + + NSDictionary *viewProperties = view.thinkingAnalyticsViewPropertiesWithAppid; + if (viewProperties != nil && [viewProperties objectForKey:appid]) { + NSDictionary *properties = [viewProperties objectForKey:appid]; + if ([properties isKindOfClass:[NSDictionary class]]) { + [trackProperties addEntriesFromDictionary:properties]; + + [yx_trackProperties addEntriesFromDictionary:properties]; + } + } + + if (propertyWithAppid) { + NSDictionary *autoTrackproperties = [propertyWithAppid objectForKey:appid]; + if ([autoTrackproperties isKindOfClass:[NSDictionary class]]) { + [trackProperties addEntriesFromDictionary:autoTrackproperties]; + + [yx_trackProperties addEntriesFromDictionary:autoTrackproperties]; + } + } + + TAAutoClickEvent *yx_event = [[TAAutoClickEvent alloc] initWithName:TD_APP_CLICK_EVENT]; + yx_event.time = trackDate; + yx_event.elementId = elementId; + yx_event.elementType = elementType; + yx_event.elementContent = elementContent; + yx_event.elementPosition = elementPosition; + yx_event.pageTitle = elementPageTitle; + yx_event.screenName = elementScreenName; + + [instance autoTrackWithEvent:yx_event properties:yx_trackProperties]; + } + } +} + +- (void)trackWithAppid:(NSString *)appid withOption:(ThinkingAnalyticsAutoTrackEventType)type { + TD_LOCK(self.trackOptionLock); + self.autoTrackOptions[appid] = @(type); + TD_UNLOCK(self.trackOptionLock); + + if (type & ThinkingAnalyticsEventTypeAppClick || type & ThinkingAnalyticsEventTypeAppViewScreen) { + [self swizzleVC]; + } + + if (type & ThinkingAnalyticsEventTypeAppInstall) { + TAAutoTrackEvent *event = [[TAAutoTrackEvent alloc] initWithName:TD_APP_INSTALL_EVENT]; + event.time = [[NSDate date] dateByAddingTimeInterval: -1]; + [self.appInstallTracker trackWithInstanceTag:appid event:event params:nil]; + } + + if (type & ThinkingAnalyticsEventTypeAppEnd) { + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK sharedInstanceWithAppid:appid]; + [instance timeEvent:TD_APP_END_EVENT]; + } + + if (type & ThinkingAnalyticsEventTypeAppStart) { + dispatch_block_t mainThreadBlock = ^(){ + NSString *eventName = [TDAppState shareInstance].relaunchInBackground ? TD_APP_START_BACKGROUND_EVENT : TD_APP_START_EVENT; + TAAppStartEvent *event = [[TAAppStartEvent alloc] initWithName:eventName]; + event.resumeFromBackground = NO; + if (![TDPresetProperties disableStartReason]) { + NSString *reason = [TDRunTime getAppLaunchReason]; + if (reason && reason.length) { + event.startReason = reason; + } + } + [self.appColdStartTracker trackWithInstanceTag:appid event:event params:nil]; + }; + dispatch_async(dispatch_get_main_queue(), mainThreadBlock); + } + + if (type & ThinkingAnalyticsEventTypeAppViewCrash) { + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK sharedInstanceWithAppid:appid]; + [[ThinkingExceptionHandler sharedHandler] addThinkingInstance:instance]; + } +} + +- (void)viewControlWillAppear:(UIViewController *)controller { + [self trackViewController:controller]; +} + ++ (UIViewController *)topPresentedViewController { + UIWindow *keyWindow = [self findWindow]; + if (keyWindow != nil && !keyWindow.isKeyWindow) { + [keyWindow makeKeyWindow]; + } + + UIViewController *topController = keyWindow.rootViewController; + if ([topController isKindOfClass:[UINavigationController class]]) { + topController = [(UINavigationController *)topController topViewController]; + } + while (topController.presentedViewController) { + topController = topController.presentedViewController; + } + return topController; +} + +#pragma mark - Private + +- (BOOL)isAutoTrackEventType:(ThinkingAnalyticsAutoTrackEventType)eventType { + BOOL isIgnored = YES; + for (NSString *appid in self.autoTrackOptions) { + ThinkingAnalyticsAutoTrackEventType type = (ThinkingAnalyticsAutoTrackEventType)[self.autoTrackOptions[appid] integerValue]; + isIgnored = !(type & eventType); + if (isIgnored == NO) + break; + } + return !isIgnored; +} + +- (UIViewController *)viewControllerForView:(UIView *)view { + UIResponder *responder = view.nextResponder; + while (responder) { + if ([responder isKindOfClass:[UIViewController class]]) { + if ([responder isKindOfClass:[UINavigationController class]]) { + responder = [(UINavigationController *)responder topViewController]; + continue; + } else if ([responder isKindOfClass:UITabBarController.class]) { + responder = [(UITabBarController *)responder selectedViewController]; + continue; + } + return (UIViewController *)responder; + } + responder = responder.nextResponder; + } + return nil; +} + +- (void)trackViewController:(UIViewController *)controller { + if (![self shouldTrackViewContrller:[controller class]]) { + return; + } + + NSString *pageUrl = nil; + NSString *pageReferrer = nil; + NSString *pageTitle = nil; + NSString *pageScreenName = nil; + NSMutableDictionary *yx_customProperties = [NSMutableDictionary dictionary]; + + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + [properties setValue:NSStringFromClass([controller class]) forKey:TD_EVENT_PROPERTY_SCREEN_NAME]; + + pageScreenName = NSStringFromClass([controller class]); + + NSString *controllerTitle = [self titleFromViewController:controller]; + if (controllerTitle) { + [properties setValue:controllerTitle forKey:TD_EVENT_PROPERTY_TITLE]; + + pageTitle = controllerTitle; + } + + NSDictionary *autoTrackerAppidDic; + if ([controller conformsToProtocol:@protocol(TDAutoTracker)]) { + UIViewController *autoTrackerController = (UIViewController *)controller; + NSDictionary *autoTrackerDic; + if ([controller respondsToSelector:@selector(getTrackPropertiesWithAppid)]) + autoTrackerAppidDic = [autoTrackerController getTrackPropertiesWithAppid]; + if ([controller respondsToSelector:@selector(getTrackProperties)]) + autoTrackerDic = [autoTrackerController getTrackProperties]; + + if ([autoTrackerDic isKindOfClass:[NSDictionary class]]) { + [properties addEntriesFromDictionary:autoTrackerDic]; + + [yx_customProperties addEntriesFromDictionary:autoTrackerDic]; + } + } + + NSDictionary *screenAutoTrackerAppidDic; + if ([controller conformsToProtocol:@protocol(TDScreenAutoTracker)]) { + UIViewController *screenAutoTrackerController = (UIViewController *)controller; + if ([screenAutoTrackerController respondsToSelector:@selector(getScreenUrlWithAppid)]) + screenAutoTrackerAppidDic = [screenAutoTrackerController getScreenUrlWithAppid]; + if ([screenAutoTrackerController respondsToSelector:@selector(getScreenUrl)]) { + NSString *currentUrl = [screenAutoTrackerController getScreenUrl]; + [properties setValue:currentUrl forKey:TD_EVENT_PROPERTY_URL_PROPERTY]; + + pageUrl = currentUrl; + + [properties setValue:_referrerViewControllerUrl forKey:TD_EVENT_PROPERTY_REFERRER_URL]; + + pageReferrer = _referrerViewControllerUrl; + + _referrerViewControllerUrl = currentUrl; + } + } + + NSDate *trackDate = [NSDate date]; + for (NSString *appid in self.autoTrackOptions) { + ThinkingAnalyticsAutoTrackEventType type = [self.autoTrackOptions[appid] integerValue]; + if (type & ThinkingAnalyticsEventTypeAppViewScreen) { + + + + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK sharedInstanceWithAppid:appid]; + NSMutableDictionary *trackProperties = [properties mutableCopy]; + + NSMutableDictionary *yx_trackProperties = [yx_customProperties mutableCopy]; + + if ([instance isViewControllerIgnored:controller] + || [instance isViewTypeIgnored:[controller class]]) { + continue; + } + + if (autoTrackerAppidDic && [autoTrackerAppidDic objectForKey:appid]) { + NSDictionary *dic = [autoTrackerAppidDic objectForKey:appid]; + if ([dic isKindOfClass:[NSDictionary class]]) { + [trackProperties addEntriesFromDictionary:dic]; + + [yx_trackProperties addEntriesFromDictionary:dic]; + } + } + + if (screenAutoTrackerAppidDic && [screenAutoTrackerAppidDic objectForKey:appid]) { + NSString *screenUrl = [screenAutoTrackerAppidDic objectForKey:appid]; + [trackProperties setValue:screenUrl forKey:TD_EVENT_PROPERTY_URL_PROPERTY]; + + pageUrl = screenUrl; + } + + TAAutoPageViewEvent *pageEvent = [[TAAutoPageViewEvent alloc] initWithName:TD_APP_VIEW_EVENT]; + pageEvent.time = trackDate; + pageEvent.pageUrl = pageUrl; + pageEvent.pageTitle = pageTitle; + pageEvent.referrer = pageReferrer; + pageEvent.screenName = pageScreenName; + + [instance autoTrackWithEvent:pageEvent properties:yx_trackProperties]; + } + } +} + +- (BOOL)shouldTrackViewContrller:(Class)aClass { + return ![TDPublicConfig.controllers containsObject:NSStringFromClass(aClass)]; +} + +- (ThinkingAnalyticsAutoTrackEventType)autoTrackOptionForAppid:(NSString *)appid { + return (ThinkingAnalyticsAutoTrackEventType)[[self.autoTrackOptions objectForKey:appid] integerValue]; +} + +- (void)swizzleSelected:(UIView *)view delegate:(id)delegate { + if ([view isKindOfClass:[UITableView class]] + && [delegate conformsToProtocol:@protocol(UITableViewDelegate)]) { + void (^block)(id, SEL, id, id) = ^(id target, SEL command, UITableView *tableView, NSIndexPath *indexPath) { + [self trackEventView:tableView withIndexPath:indexPath]; + }; + + [TDSwizzler swizzleSelector:@selector(tableView:didSelectRowAtIndexPath:) + onClass:[delegate class] + withBlock:block + named:@"td_table_select"]; + } + + if ([view isKindOfClass:[UICollectionView class]] + && [delegate conformsToProtocol:@protocol(UICollectionViewDelegate)]) { + + void (^block)(id, SEL, id, id) = ^(id target, SEL command, UICollectionView *collectionView, NSIndexPath *indexPath) { + [self trackEventView:collectionView withIndexPath:indexPath]; + }; + [TDSwizzler swizzleSelector:@selector(collectionView:didSelectItemAtIndexPath:) + onClass:[delegate class] + withBlock:block + named:@"td_collection_select"]; + } +} + +- (void)swizzleVC { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + void (^tableViewBlock)(UITableView *tableView, + SEL cmd, + id delegate) = + ^(UITableView *tableView, SEL cmd, id delegate) { + if (!delegate) { + return; + } + + [self swizzleSelected:tableView delegate:delegate]; + }; + + [TDSwizzler swizzleSelector:@selector(setDelegate:) + onClass:[UITableView class] + withBlock:tableViewBlock + named:@"td_table_delegate"]; + + void (^collectionViewBlock)(UICollectionView *, SEL, id) = ^(UICollectionView *collectionView, SEL cmd, id delegate) { + if (nil == delegate) { + return; + } + + [self swizzleSelected:collectionView delegate:delegate]; + }; + [TDSwizzler swizzleSelector:@selector(setDelegate:) + onClass:[UICollectionView class] + withBlock:collectionViewBlock + named:@"td_collection_delegate"]; + + + [UIViewController td_swizzleMethod:@selector(viewWillAppear:) + withMethod:@selector(td_autotrack_viewWillAppear:) + error:NULL]; + + [UIApplication td_swizzleMethod:@selector(sendAction:to:from:forEvent:) + withMethod:@selector(td_sendAction:to:from:forEvent:) + error:NULL]; + }); +} + ++ (NSString *)getPosition:(UIView *)view { + NSString *position = nil; + if ([view isKindOfClass:[UIView class]] && view.thinkingAnalyticsIgnoreView) { + return nil; + } + + if ([view isKindOfClass:[UITabBar class]]) { + UITabBar *tabbar = (UITabBar *)view; + position = [NSString stringWithFormat: @"%ld", (long)[tabbar.items indexOfObject:tabbar.selectedItem]]; + } else if ([view isKindOfClass:[UISegmentedControl class]]) { + UISegmentedControl *segment = (UISegmentedControl *)view; + position = [NSString stringWithFormat:@"%ld", (long)segment.selectedSegmentIndex]; + } else if ([view isKindOfClass:[UIProgressView class]]) { + UIProgressView *progress = (UIProgressView *)view; + position = [NSString stringWithFormat:@"%f", progress.progress]; + } else if ([view isKindOfClass:[UIPageControl class]]) { + UIPageControl *pageControl = (UIPageControl *)view; + position = [NSString stringWithFormat:@"%ld", (long)pageControl.currentPage]; + } + + return position; +} + ++ (NSString *)getText:(NSObject *)obj { + NSString *text = nil; + if ([obj isKindOfClass:[UIView class]] && [(UIView *)obj thinkingAnalyticsIgnoreView]) { + return nil; + } + + if ([obj isKindOfClass:[UIButton class]]) { + text = ((UIButton *)obj).currentTitle; + } else if ([obj isKindOfClass:[UITextView class]] || + [obj isKindOfClass:[UITextField class]]) { + //ignore + } else if ([obj isKindOfClass:[UILabel class]]) { + text = ((UILabel *)obj).text; + } else if ([obj isKindOfClass:[UIPickerView class]]) { + UIPickerView *picker = (UIPickerView *)obj; + NSInteger sections = picker.numberOfComponents; + NSMutableArray *titles = [NSMutableArray array]; + + for(NSInteger i = 0; i < sections; i++) { + NSInteger row = [picker selectedRowInComponent:i]; + NSString *title; + if ([picker.delegate + respondsToSelector:@selector(pickerView:titleForRow:forComponent:)]) { + title = [picker.delegate pickerView:picker titleForRow:row forComponent:i]; + } else if ([picker.delegate + respondsToSelector:@selector(pickerView:attributedTitleForRow:forComponent:)]) { + title = [picker.delegate + pickerView:picker + attributedTitleForRow:row forComponent:i].string; + } + [titles addObject:title ?: @""]; + } + if (titles.count > 0) { + text = [titles componentsJoinedByString:@","]; + } + } else if ([obj isKindOfClass:[UIDatePicker class]]) { + UIDatePicker *picker = (UIDatePicker *)obj; + NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + formatter.dateFormat = kDefaultTimeFormat; + text = [formatter stringFromDate:picker.date]; + } else if ([obj isKindOfClass:[UISegmentedControl class]]) { + UISegmentedControl *segment = (UISegmentedControl *)obj; + text = [NSString stringWithFormat:@"%@", [segment titleForSegmentAtIndex:segment.selectedSegmentIndex]]; + } else if ([obj isKindOfClass:[UISwitch class]]) { + UISwitch *switchItem = (UISwitch *)obj; + text = switchItem.on ? @"on" : @"off"; + } else if ([obj isKindOfClass:[UISlider class]]) { + UISlider *slider = (UISlider *)obj; + text = [NSString stringWithFormat:@"%f", [slider value]]; + } else if ([obj isKindOfClass:[UIStepper class]]) { + UIStepper *step = (UIStepper *)obj; + text = [NSString stringWithFormat:@"%f", [step value]]; + } else { + if ([obj isKindOfClass:[UIView class]]) { + for(UIView *subView in [(UIView *)obj subviews]) { + text = [TDAutoTrackManager getText:subView]; + if ([text isKindOfClass:[NSString class]] && text.length > 0) { + break; + } + } + } + } + return text; +} + +- (NSString *)titleFromViewController:(UIViewController *)viewController { + if (!viewController) { + return nil; + } + + UIView *titleView = viewController.navigationItem.titleView; + NSString *elementContent = nil; + if (titleView) { + elementContent = [TDAutoTrackManager getText:titleView]; + } + + return elementContent.length > 0 ? elementContent : viewController.navigationItem.title; +} + ++ (UIWindow *)findWindow { + UIApplication *application = [TDAppState sharedApplication]; + if (![application isKindOfClass:UIApplication.class]) { + return nil; + } + + UIWindow *window = application.keyWindow; + if (window == nil || window.windowLevel != UIWindowLevelNormal) { + for (window in application.windows) { + if (window.windowLevel == UIWindowLevelNormal) { + break; + } + } + } + + if (@available(iOS 13.0, tvOS 13, *)) { + NSSet *scenes = [[TDAppState sharedApplication] valueForKey:@"connectedScenes"]; + for (id scene in scenes) { + if (window) { + break; + } + + id activationState = [scene valueForKeyPath:@"activationState"]; + BOOL isActive = activationState != nil && [activationState integerValue] == 0; + if (isActive) { + Class WindowScene = NSClassFromString(@"UIWindowScene"); + if ([scene isKindOfClass:WindowScene]) { + NSArray *windows = [scene valueForKeyPath:@"windows"]; + for (UIWindow *w in windows) { + if (w.isKeyWindow) { + window = w; + break; + } + } + } + } + } + } + + return window; +} + +//MARK: - App Life Cycle + +- (void)registerAppLifeCycleListener { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter addObserver:self selector:@selector(appStateWillChangeNotification:) name:kTAAppLifeCycleStateWillChangeNotification object:nil]; +} + +- (void)appStateWillChangeNotification:(NSNotification *)notification { + TAAppLifeCycleState newState = [[notification.userInfo objectForKey:kTAAppLifeCycleNewStateKey] integerValue]; + TAAppLifeCycleState oldState = [[notification.userInfo objectForKey:kTAAppLifeCycleOldStateKey] integerValue]; + + if (newState == TAAppLifeCycleStateStart) { + for (NSString *appid in self.autoTrackOptions) { + ThinkingAnalyticsAutoTrackEventType type = (ThinkingAnalyticsAutoTrackEventType)[self.autoTrackOptions[appid] integerValue]; + + // Only open the start event of collecting hot start. Cold start event, reported when automatic collection is turned on + if ((type & ThinkingAnalyticsEventTypeAppStart) && oldState != TAAppLifeCycleStateInit) { + NSString *eventName = [TDAppState shareInstance].relaunchInBackground ? TD_APP_START_BACKGROUND_EVENT : TD_APP_START_EVENT; + TAAppStartEvent *event = [[TAAppStartEvent alloc] initWithName:eventName]; + event.resumeFromBackground = YES; + + if (![TDPresetProperties disableStartReason]) { + NSString *reason = [TDRunTime getAppLaunchReason]; + if (reason && reason.length) { + event.startReason = reason; + } + } + [self.appHotStartTracker trackWithInstanceTag:appid event:event params:@{}]; + } + + if (type & ThinkingAnalyticsEventTypeAppEnd) { + + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK sharedInstanceWithAppid:appid]; + [instance timeEvent:TD_APP_END_EVENT]; + } + } + } else if (newState == TAAppLifeCycleStateEnd) { + for (NSString *appid in self.autoTrackOptions) { + ThinkingAnalyticsAutoTrackEventType type = (ThinkingAnalyticsAutoTrackEventType)[self.autoTrackOptions[appid] integerValue]; + if (type & ThinkingAnalyticsEventTypeAppEnd) { + TAAppEndEvent *event = [[TAAppEndEvent alloc] initWithName:TD_APP_END_EVENT]; + td_dispatch_main_sync_safe(^{ + + NSString *screenName = NSStringFromClass([[TDAutoTrackManager topPresentedViewController] class]); + event.screenName = screenName; + [self.appEndTracker trackWithInstanceTag:appid event:event params:@{}]; + }); + } + + if (type & ThinkingAnalyticsEventTypeAppStart) { + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK sharedInstanceWithAppid:appid]; + [instance timeEvent:TD_APP_START_EVENT]; + } + } + } +} + +//MARK: - Getter & Setter + +- (TDHotStartTracker *)appHotStartTracker { + if (!_appHotStartTracker) { + _appHotStartTracker = [[TDHotStartTracker alloc] init]; + } + return _appHotStartTracker; +} + +- (TDColdStartTracker *)appColdStartTracker { + if (!_appColdStartTracker) { + _appColdStartTracker = [[TDColdStartTracker alloc] init]; + } + return _appColdStartTracker; +} + +- (TDInstallTracker *)appInstallTracker { + if (!_appInstallTracker) { + _appInstallTracker = [[TDInstallTracker alloc] init]; + } + return _appInstallTracker; +} + +- (TDAppEndTracker *)appEndTracker { + if (!_appEndTracker) { + _appEndTracker = [[TDAppEndTracker alloc] init]; + } + return _appEndTracker; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m.meta new file mode 100644 index 0000000..681c17b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e28ed5bde9eff6e4ead5f94647ed98c3 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker.meta new file mode 100644 index 0000000..0043fd0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 462bc89e58151374fbaed6eab40f2d41 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h new file mode 100644 index 0000000..b8787ad --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h @@ -0,0 +1,16 @@ +// +// TDAppEndTracker.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/16. +// + +#import "TDAutoTracker.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAppEndTracker : TDAutoTracker + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h.meta new file mode 100644 index 0000000..4f13fb1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ab31df7ede2aa184285f3bd836116e7a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m new file mode 100644 index 0000000..7618737 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m @@ -0,0 +1,12 @@ +// +// TDAppEndTracker.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/16. +// + +#import "TDAppEndTracker.h" + +@implementation TDAppEndTracker + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m.meta new file mode 100644 index 0000000..5a65621 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9a3a631587e8c0741954d648c11fd3ec +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h new file mode 100644 index 0000000..88f06d4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h @@ -0,0 +1,28 @@ +// +// TDAutoTracker.h +// ThinkingSDK +// +// Created by wwango on 2021/10/13. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import +#import "ThinkingAnalyticsSDK.h" +#import "TAAutoTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAutoTracker : NSObject + +@property (atomic, assign) BOOL isOneTime; + +@property (atomic, assign) BOOL autoFlush; + +@property (atomic, assign) BOOL additionalCondition; + +- (void)trackWithInstanceTag:(NSString *)instanceName event:(TAAutoTrackEvent *)event params:(nullable NSDictionary *)params; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h.meta new file mode 100644 index 0000000..d7a3fd4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 474c41104156fac46955dcbb252c69ea +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m new file mode 100644 index 0000000..93ae854 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m @@ -0,0 +1,67 @@ +// +// TDAutoTracker.m +// ThinkingSDK +// +// Created by wwango on 2021/10/13. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDAutoTracker.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +@interface TDAutoTracker () + +@property (nonatomic, strong) NSMutableDictionary *trackCounts; + +@end + +@implementation TDAutoTracker + +- (instancetype)init +{ + self = [super init]; + if (self) { + _isOneTime = NO; + _autoFlush = YES; + _additionalCondition = YES; + + self.trackCounts = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)trackWithInstanceTag:(NSString *)instanceName event:(TAAutoTrackEvent *)event params:(NSDictionary *)params { + if ([self canTrackWithInstanceToken:instanceName]) { + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK sharedInstanceWithAppid:instanceName]; +#ifdef DEBUG + if (!instance) { + @throw [NSException exceptionWithName:@"Thinkingdata Exception" reason:[NSString stringWithFormat:@"check this thinking instance, instanceTag: %@", instanceName] userInfo:nil]; + } +#endif + [instance autoTrackWithEvent:event properties:params]; + + if (self.autoFlush) [instance flush]; + } +} + +- (BOOL)canTrackWithInstanceToken:(NSString *)token { + + if (!self.additionalCondition) { + return NO; + } + + NSInteger trackCount = [self.trackCounts[token] integerValue]; + + if (self.isOneTime && trackCount >= 1) { + return NO; + } + + if (self.isOneTime) { + trackCount++; + self.trackCounts[token] = @(trackCount); + } + + return YES; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m.meta new file mode 100644 index 0000000..23dc844 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4c3592f187e49044e9af02246ae39d74 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h new file mode 100644 index 0000000..9b76e5c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h @@ -0,0 +1,16 @@ +// +// TDColdStartTracker.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDAutoTracker.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDColdStartTracker : TDAutoTracker + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h.meta new file mode 100644 index 0000000..7507dc0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 50b8dbdd05dbf5140b35a61ce964b9bc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m new file mode 100644 index 0000000..04fc57a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m @@ -0,0 +1,19 @@ +// +// TDColdStartTracker.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDColdStartTracker.h" + +@implementation TDColdStartTracker + +- (instancetype)init { + if (self = [super init]) { + self.isOneTime = YES; + } + return self; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m.meta new file mode 100644 index 0000000..796f518 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: aeb40d37805f0f144ac7a1ad54ddc002 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h new file mode 100644 index 0000000..4323db2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h @@ -0,0 +1,16 @@ +// +// TDHotStartTracker.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDAutoTracker.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDHotStartTracker : TDAutoTracker + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h.meta new file mode 100644 index 0000000..20c1c41 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e58eeefebbc316246ab90b1315c79eaa +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m new file mode 100644 index 0000000..53cecc0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m @@ -0,0 +1,12 @@ +// +// TDHotStartTracker.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDHotStartTracker.h" + +@implementation TDHotStartTracker + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m.meta new file mode 100644 index 0000000..0ceb34c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f47fcc87472a6bc408fd5c7f132fc9f4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h new file mode 100644 index 0000000..f9f6aeb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h @@ -0,0 +1,17 @@ +// +// TDInstallTracker.h +// ThinkingSDK +// +// Created by wwango on 2021/10/13. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDAutoTracker.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDInstallTracker : TDAutoTracker + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h.meta new file mode 100644 index 0000000..6eb6c59 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4fd599bc5ddf3e540985439a2c683254 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m new file mode 100644 index 0000000..26d31cb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m @@ -0,0 +1,22 @@ +// +// TDInstallTracker.m +// ThinkingSDK +// +// Created by wwango on 2021/10/13. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDInstallTracker.h" +#import "TDDeviceInfo.h" + +@implementation TDInstallTracker + +- (BOOL)isOneTime { + return YES; +} + +- (BOOL)additionalCondition { + return [TDDeviceInfo sharedManager].isFirstOpen; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m.meta new file mode 100644 index 0000000..4f1b275 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8b55c1946deacef4fb3b8d68e0e9cd18 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit.meta new file mode 100644 index 0000000..7354dce --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b80d07502d2a4be4d875233ba8604a5b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h new file mode 100644 index 0000000..6560494 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h @@ -0,0 +1,14 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIApplication (AutoTrack) + +- (BOOL)td_sendAction:(SEL)action + to:(nullable id)to + from:(nullable id)from + forEvent:(nullable UIEvent *)event; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h.meta new file mode 100644 index 0000000..aae127b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 76827556b4c29d74b9d9866d1532361a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m new file mode 100644 index 0000000..1d3640f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m @@ -0,0 +1,24 @@ +#import "UIApplication+AutoTrack.h" +#import "TDAutoTrackManager.h" + +@implementation UIApplication (AutoTrack) + +- (BOOL)td_sendAction:(SEL)action to:(id)to from:(id)from forEvent:(UIEvent *)event { + if ([from isKindOfClass:[UIControl class]]) { + if (([from isKindOfClass:[UISwitch class]] || + [from isKindOfClass:[UISegmentedControl class]] || + [from isKindOfClass:[UIStepper class]])) { + [[TDAutoTrackManager sharedManager] trackEventView:from]; + } + + else if ([event isKindOfClass:[UIEvent class]] && + event.type == UIEventTypeTouches && + [[[event allTouches] anyObject] phase] == UITouchPhaseEnded) { + [[TDAutoTrackManager sharedManager] trackEventView:from]; + } + } + + return [self td_sendAction:action to:to from:from forEvent:event]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m.meta new file mode 100644 index 0000000..a7d5546 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2e342b8a785318f428bfd623fa57d1da +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h new file mode 100644 index 0000000..30d41cc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h @@ -0,0 +1,51 @@ +// +// UIView+ThinkingAnalytics.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIView (ThinkingAnalytics) + +/** + Set the control element ID + */ +@property (copy,nonatomic) NSString *thinkingAnalyticsViewID; + +/** + Configure the control element ID of APPID + */ +@property (strong,nonatomic) NSDictionary *thinkingAnalyticsViewIDWithAppid; + +/** + Ignore the click event of a control + */ +@property (nonatomic,assign) BOOL thinkingAnalyticsIgnoreView; + +/** + Configure APPID to ignore the click event of a control + */ +@property (strong,nonatomic) NSDictionary *thinkingAnalyticsIgnoreViewWithAppid; + +/** + Properties of custom control click event + */ +@property (strong,nonatomic) NSDictionary *thinkingAnalyticsViewProperties; + +/** + Configure the properties of the APPID custom control click event + */ +@property (strong,nonatomic) NSDictionary *thinkingAnalyticsViewPropertiesWithAppid; + +/** + thinkingAnalyticsDelegate + */ +@property (nonatomic, weak, nullable) id thinkingAnalyticsDelegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h.meta new file mode 100644 index 0000000..60c786c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c67fc5d11c1b49f48b6d63493119861d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m new file mode 100644 index 0000000..6cb6509 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m @@ -0,0 +1,77 @@ +// +// UIView+ThinkingAnalytics.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "UIView+ThinkingAnalytics.h" +#import + +static char TD_AUTOTRACK_VIEW_ID; +static char TD_AUTOTRACK_VIEW_ID_APPID; +static char TD_AUTOTRACK_VIEW_IGNORE; +static char TD_AUTOTRACK_VIEW_IGNORE_APPID; +static char TD_AUTOTRACK_VIEW_PROPERTIES; +static char TD_AUTOTRACK_VIEW_PROPERTIES_APPID; +static char TD_AUTOTRACK_VIEW_DELEGATE; + +@implementation UIView (ThinkingAnalytics) + +- (NSString *)thinkingAnalyticsViewID { + return objc_getAssociatedObject(self, &TD_AUTOTRACK_VIEW_ID); +} + +- (void)setThinkingAnalyticsViewID:(NSString *)thinkingAnalyticsViewID { + objc_setAssociatedObject(self, &TD_AUTOTRACK_VIEW_ID, thinkingAnalyticsViewID, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (BOOL)thinkingAnalyticsIgnoreView { + return [objc_getAssociatedObject(self, &TD_AUTOTRACK_VIEW_IGNORE) boolValue]; +} + +- (void)setThinkingAnalyticsIgnoreView:(BOOL)thinkingAnalyticsIgnoreView { + objc_setAssociatedObject(self, &TD_AUTOTRACK_VIEW_IGNORE, [NSNumber numberWithBool:thinkingAnalyticsIgnoreView], OBJC_ASSOCIATION_ASSIGN); +} + +- (NSDictionary *)thinkingAnalyticsIgnoreViewWithAppid { + return objc_getAssociatedObject(self, &TD_AUTOTRACK_VIEW_IGNORE_APPID); +} + +- (void)setThinkingAnalyticsIgnoreViewWithAppid:(NSDictionary *)thinkingAnalyticsViewProperties { + objc_setAssociatedObject(self, &TD_AUTOTRACK_VIEW_IGNORE_APPID, thinkingAnalyticsViewProperties, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSDictionary *)thinkingAnalyticsViewIDWithAppid { + return objc_getAssociatedObject(self, &TD_AUTOTRACK_VIEW_ID_APPID); +} + +- (void)setThinkingAnalyticsViewIDWithAppid:(NSDictionary *)thinkingAnalyticsViewProperties { + objc_setAssociatedObject(self, &TD_AUTOTRACK_VIEW_ID_APPID, thinkingAnalyticsViewProperties, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSDictionary *)thinkingAnalyticsViewProperties { + return objc_getAssociatedObject(self, &TD_AUTOTRACK_VIEW_PROPERTIES); +} + +- (void)setThinkingAnalyticsViewProperties:(NSDictionary *)thinkingAnalyticsViewProperties { + objc_setAssociatedObject(self, &TD_AUTOTRACK_VIEW_PROPERTIES, thinkingAnalyticsViewProperties, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSDictionary *)thinkingAnalyticsViewPropertiesWithAppid { + return objc_getAssociatedObject(self, &TD_AUTOTRACK_VIEW_PROPERTIES_APPID); +} + +- (void)setThinkingAnalyticsViewPropertiesWithAppid:(NSDictionary *)thinkingAnalyticsViewProperties { + objc_setAssociatedObject(self, &TD_AUTOTRACK_VIEW_PROPERTIES_APPID, thinkingAnalyticsViewProperties, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (id)thinkingAnalyticsDelegate { + return objc_getAssociatedObject(self, &TD_AUTOTRACK_VIEW_DELEGATE); +} + +- (void)setThinkingAnalyticsDelegate:(id)thinkingAnalyticsDelegate { + objc_setAssociatedObject(self, &TD_AUTOTRACK_VIEW_DELEGATE, thinkingAnalyticsDelegate, OBJC_ASSOCIATION_ASSIGN); +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m.meta new file mode 100644 index 0000000..e2b2ac5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a3caba30b926a2441ae7a9a0b657b4e5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h new file mode 100644 index 0000000..4e2e17c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h @@ -0,0 +1,7 @@ +#import + +@interface UIViewController (AutoTrack) + +- (void)td_autotrack_viewWillAppear:(BOOL)animated; + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h.meta new file mode 100644 index 0000000..d5669a1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c7a1ed12078ed9c4ba5c3e3587346e4e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m new file mode 100644 index 0000000..f563f69 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m @@ -0,0 +1,16 @@ +#import "UIViewController+AutoTrack.h" +#import "TDAutoTrackManager.h" +#import "TDLogging.h" + +@implementation UIViewController (AutoTrack) + +- (void)td_autotrack_viewWillAppear:(BOOL)animated { + @try { + [[TDAutoTrackManager sharedManager] viewControlWillAppear:self]; + } @catch (NSException *exception) { + TDLogError(@"%@ error: %@", self, exception); + } + [self td_autotrack_viewWillAppear:animated]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m.meta new file mode 100644 index 0000000..cc94f07 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 081cc82a073a6aa489fa263acf6f362a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config.meta new file mode 100644 index 0000000..4cbe83d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f70c2d5e2dacb6440b02ffdbb385de5a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h new file mode 100644 index 0000000..ec231ff --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h @@ -0,0 +1,121 @@ +#import + +#if __has_include() +#import +#else +#import "TDConstant.h" +#endif + +#if __has_include() +#import +#else +#import "TDSecurityPolicy.h" +#endif + +#if TARGET_OS_IOS +#if __has_include() +#import +#else +#import "TDSecretKey.h" +#endif +#endif + + + +NS_ASSUME_NONNULL_BEGIN + + + +@interface TDConfig:NSObject +/** + Set automatic burying type + */ +@property (assign, nonatomic) ThinkingAnalyticsAutoTrackEventType autoTrackEventType; +/** + Network environment for data transmission + */ +@property (assign, nonatomic) ThinkingNetworkType networkTypePolicy; +/** + Data upload interval + */ +@property (nonatomic, strong) NSNumber *uploadInterval; +/** + When there is data to upload, when the number of data cache reaches the uploadsize, upload the data immediately + */ +@property (nonatomic, strong) NSNumber *uploadSize; +/** + Event blacklist, event names that are not counted are added here + */ +@property (strong, nonatomic) NSArray *disableEvents; +/** + The maximum number of cached events, the default is 10000, the minimum is 5000 + */ +@property (class, nonatomic) NSInteger maxNumEvents DEPRECATED_MSG_ATTRIBUTE("Please config TAConfigInfo in main info.plist"); +/** + Data cache expiration time, the default is 10 days, the longest is 10 days + */ +@property (class, nonatomic) NSInteger expirationDays DEPRECATED_MSG_ATTRIBUTE("Please config TAConfigInfo in main info.plist"); +/** + appid + */ +@property (atomic, copy) NSString *appid; +/** + instance Token + */ +@property (atomic, copy) NSString *(^getInstanceName)(void); +/** + Server URL + */ +@property (atomic, copy) NSString *configureURL; + +/** + Initialize and configure background self-starting events + YES: Collect background self-starting events + NO: Do not collect background self-starting events + */ +@property (nonatomic, assign) BOOL trackRelaunchedInBackgroundEvents; +/** + Debug Mode +*/ +@property (nonatomic, assign) ThinkingAnalyticsDebugMode debugMode; + +/** +app launchOptions +*/ +@property (nonatomic, copy) NSDictionary *launchOptions; + +/** + Initialize and configure the certificate verification policy +*/ +@property (nonatomic, strong) TDSecurityPolicy *securityPolicy; + +/** + Set default time zone + You can use this time zone to compare the offset of the current time zone and the default time zone +*/ +@property (nonatomic, strong) NSTimeZone *defaultTimeZone; + +/** + instance name +*/ +@property (nonatomic, copy) NSString *name; + ++ (TDConfig *)defaultTDConfig; +- (instancetype)initWithAppId:(NSString *)appId serverUrl:(NSString *)serverUrl; +- (void)updateConfig:(void(^)(NSDictionary *secretKey))block; +- (void)setNetworkType:(ThinkingAnalyticsNetworkType)type; + + +/// enable encryption +@property (nonatomic, assign) BOOL enableEncrypt; + +#if TARGET_OS_IOS +/// Get local key configuration +@property (nonatomic, strong) TDSecretKey *secretKey; +#endif + +/// instance token +- (NSString *)getMapInstanceToken; + +@end +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h.meta new file mode 100644 index 0000000..3606936 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e5805b4fe4e0cb14b99e72c26b9bb196 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m new file mode 100644 index 0000000..9e85ee1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m @@ -0,0 +1,187 @@ +#import "TDConfig.h" + +#import "TANetwork.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDSecurityPolicy.h" +#import "TDFile.h" +#import "NSString+TDString.h" + +#define TDSDKSETTINGS_PLIST_SETTING_IMPL(TYPE, PLIST_KEY, GETTER, SETTER, DEFAULT_VALUE, ENABLE_CACHE) \ +static TYPE *g_##PLIST_KEY = nil; \ ++ (TYPE *)GETTER \ +{ \ + if (!g_##PLIST_KEY && ENABLE_CACHE) { \ + g_##PLIST_KEY = [[[NSUserDefaults standardUserDefaults] objectForKey:@#PLIST_KEY] copy]; \ + } \ + if (!g_##PLIST_KEY) { \ + g_##PLIST_KEY = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@#PLIST_KEY] copy] ?: DEFAULT_VALUE; \ + } \ + return g_##PLIST_KEY; \ +} \ ++ (void)SETTER:(TYPE *)value { \ + g_##PLIST_KEY = [value copy]; \ + if (ENABLE_CACHE) { \ + if (value) { \ + [[NSUserDefaults standardUserDefaults] setObject:value forKey:@#PLIST_KEY]; \ + } else { \ + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@#PLIST_KEY]; \ + } \ + } \ +} + + +#define kTAConfigInfo @"TAConfigInfo" + + +static TDConfig * _defaultTDConfig; +static NSDictionary *configInfo; + +@implementation TDConfig + +TDSDKSETTINGS_PLIST_SETTING_IMPL(NSNumber, ThinkingSDKMaxCacheSize, _maxNumEventsNumber, _setMaxNumEventsNumber, @10000, NO); +TDSDKSETTINGS_PLIST_SETTING_IMPL(NSNumber, ThinkingSDKExpirationDays, _expirationDaysNumber, _setExpirationDaysNumber, @10, NO); + ++ (TDConfig *)defaultTDConfig { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _defaultTDConfig = [TDConfig new]; + + }); + return _defaultTDConfig; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _trackRelaunchedInBackgroundEvents = NO; + _autoTrackEventType = ThinkingAnalyticsEventTypeNone; + _networkTypePolicy = ThinkingNetworkTypeWIFI | ThinkingNetworkType3G | ThinkingNetworkType4G | ThinkingNetworkType2G | ThinkingNetworkType5G; + _securityPolicy = [TDSecurityPolicy defaultPolicy]; + _defaultTimeZone = [NSTimeZone localTimeZone]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + if (!configInfo) { + configInfo = (NSDictionary *)[[[NSBundle mainBundle] infoDictionary] objectForKey: kTAConfigInfo]; + } + + if (configInfo && [configInfo.allKeys containsObject:@"maxNumEvents"]) { + [TDConfig setMaxNumEvents:[configInfo[@"maxNumEvents"] integerValue]]; + } + if (configInfo && [configInfo.allKeys containsObject:@"expirationDays"]) { + [TDConfig setExpirationDays:[configInfo[@"expirationDays"] integerValue]]; + } +#pragma clang diagnostic pop + + } + return self; +} + +- (instancetype)initWithAppId:(NSString *)appId serverUrl:(NSString *)serverUrl +{ + self = [self init]; + if (self) { + _appid = appId; + _configureURL = serverUrl; + } + return self; +} + + +- (void)setName:(NSString *)name { + _name = name.td_trim; +} + +- (void)updateConfig:(void(^)(NSDictionary *secretKey))block { + NSString *serverUrlStr = [NSString stringWithFormat:@"%@/config",self.configureURL]; + TANetwork *network = [[TANetwork alloc] init]; + network.serverURL = [NSURL URLWithString:serverUrlStr]; + network.securityPolicy = _securityPolicy; + + [network fetchRemoteConfig:self.appid handler:^(NSDictionary * _Nonnull result, NSError * _Nullable error) { + if (!error) { + NSInteger uploadInterval = [[result objectForKey:@"sync_interval"] integerValue]; + NSInteger uploadSize = [[result objectForKey:@"sync_batch_size"] integerValue]; + if (uploadInterval != [self->_uploadInterval integerValue] || uploadSize != [self->_uploadSize integerValue]) { + TDFile *file = [[TDFile alloc] initWithAppid:self.appid]; + if (uploadInterval > 0) { + self.uploadInterval = [NSNumber numberWithInteger:uploadInterval]; + [file archiveUploadInterval:self.uploadInterval]; + NSString *name = self.getInstanceName ? self.getInstanceName() : self.appid; + [[ThinkingAnalyticsSDK sharedInstanceWithAppid:name] startFlushTimer]; + } + if (uploadSize > 0) { + self.uploadSize = [NSNumber numberWithInteger:uploadSize]; + [file archiveUploadSize:self.uploadSize]; + } + } + self.disableEvents = [result objectForKey:@"disable_event_list"]; + + if (block) { + block([result objectForKey:@"secret_key"]); + } + } + }]; +} + +- (void)setNetworkType:(ThinkingAnalyticsNetworkType)type { + if (type == TDNetworkTypeDefault) { + _networkTypePolicy = ThinkingNetworkTypeWIFI | ThinkingNetworkType3G | ThinkingNetworkType4G | ThinkingNetworkType2G | ThinkingNetworkType5G; + } else if (type == TDNetworkTypeOnlyWIFI) { + _networkTypePolicy = ThinkingNetworkTypeWIFI; + } else if (type == TDNetworkTypeALL) { + _networkTypePolicy = ThinkingNetworkTypeWIFI | ThinkingNetworkType3G | ThinkingNetworkType4G | ThinkingNetworkType2G | ThinkingNetworkType5G; + } +} + +#pragma mark - NSCopying +- (id)copyWithZone:(NSZone *)zone { + TDConfig *config = [[[self class] allocWithZone:zone] init]; + config.trackRelaunchedInBackgroundEvents = self.trackRelaunchedInBackgroundEvents; + config.autoTrackEventType = self.autoTrackEventType; + config.networkTypePolicy = self.networkTypePolicy; + config.launchOptions = [self.launchOptions copyWithZone:zone]; + config.debugMode = self.debugMode; + config.securityPolicy = [self.securityPolicy copyWithZone:zone]; + config.defaultTimeZone = [self.defaultTimeZone copyWithZone:zone]; + config.name = [self.name copy]; + config.enableEncrypt = self.enableEncrypt; +#if TARGET_OS_IOS + config.secretKey = [self.secretKey copyWithZone:zone]; +#endif + + return config; +} + +#pragma mark - SETTINGS ++ (NSInteger)maxNumEvents { + NSInteger maxNumEvents = [self _maxNumEventsNumber].integerValue; + if (maxNumEvents < 5000) { + maxNumEvents = 5000; + } + return maxNumEvents; +} + ++ (void)setMaxNumEvents:(NSInteger)maxNumEventsNumber { + [self _setMaxNumEventsNumber:@(maxNumEventsNumber)]; +} + ++ (NSInteger)expirationDays { + NSInteger maxNumEvents = [self _expirationDaysNumber].integerValue; + return maxNumEvents >= 0 ? maxNumEvents : 10; +} + ++ (void)setExpirationDays:(NSInteger)expirationDays { + [self _setExpirationDaysNumber:@(expirationDays)]; +} + + +- (NSString *)getMapInstanceToken { + if (self.name && [self.name isKindOfClass:[NSString class]] && self.name.length) { + return self.name; + } else { + return self.appid; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m.meta new file mode 100644 index 0000000..14f4ac4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b007bca1536bc814290c35117a66cd02 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h new file mode 100644 index 0000000..37de6ab --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h @@ -0,0 +1,22 @@ +// +// TDPublicConfig.h +// ThinkingSDK +// +// Created by LiHuanan on 2020/9/8. +// Copyright © 2020 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDPublicConfig : NSObject + +@property(copy,nonatomic) NSArray* controllers; +@property(copy,nonatomic) NSString* version; ++ (NSArray*)controllers; ++ (NSString*)version; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h.meta new file mode 100644 index 0000000..0276cbe --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c13fea031fd993643bd0ce9283f94fd1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m new file mode 100644 index 0000000..707e8ee --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m @@ -0,0 +1,62 @@ +// +// TDPublicConfig.m +// ThinkingSDK +// +// Created by LiHuanan on 2020/9/8. +// Copyright © 2020 thinkingdata. All rights reserved. +// + +#import "TDPublicConfig.h" +static TDPublicConfig* config; + +@implementation TDPublicConfig ++ (void)load +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + config = [TDPublicConfig new]; + }); +} +- (instancetype)init +{ + self = [super init]; + if(self) + { + self.controllers = @[ + @"UICompatibilityInputViewController", + @"UIKeyboardCandidateGridCollectionViewController", + @"UIInputWindowController", + @"UIApplicationRotationFollowingController", + @"UIApplicationRotationFollowingControllerNoTouches", + @"UISystemKeyboardDockController", + @"UINavigationController", + @"SFBrowserRemoteViewController", + @"SFSafariViewController", + @"UIAlertController", + @"UIImagePickerController", + @"PUPhotoPickerHostViewController", + @"UIViewController", + @"UITableViewController", + @"UITabBarController", + @"_UIRemoteInputViewController", + @"UIEditingOverlayViewController", + @"_UIAlertControllerTextFieldViewController", + @"UIActivityGroupViewController", + @"_UISFAirDropInstructionsViewController", + @"_UIActivityGroupListViewController", + @"_UIShareExtensionRemoteViewController", + @"SLRemoteComposeViewController", + @"SLComposeViewController", + ]; + } + return self; +} ++ (NSArray*)controllers +{ + return config.controllers; +} ++ (NSString*)version +{ + return @"2.8.6"; +} +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m.meta new file mode 100644 index 0000000..2518d4b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1a055ec55ad5b354ead3b844939d7eba +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core.meta new file mode 100644 index 0000000..3828925 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6e7be466f123241458d4ff7045ae83ae +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime.meta new file mode 100644 index 0000000..505ccad --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8e3e6576158a1604b9ce8407b173db92 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.h new file mode 100644 index 0000000..fa4f944 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.h @@ -0,0 +1,17 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCalibratedTime : NSObject + +@property (nonatomic, assign) NSTimeInterval systemUptime; +@property (nonatomic, assign) NSTimeInterval serverTime; +@property (nonatomic, assign) BOOL stopCalibrate; + ++ (instancetype)sharedInstance; + +- (void)recalibrationWithTimeInterval:(NSTimeInterval)timestamp; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.h.meta new file mode 100644 index 0000000..5931de7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 140933c2000556441a96638a2e1b876e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.m new file mode 100644 index 0000000..3d131aa --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.m @@ -0,0 +1,29 @@ +#import "TDCalibratedTime.h" +#import "TDDeviceInfo.h" + +@implementation TDCalibratedTime + ++ (instancetype)sharedInstance { + static dispatch_once_t once; + static id sharedInstance; + dispatch_once(&once, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + +- (instancetype)init { + if (self = [super init]) { + self.serverTime = [[NSDate date] timeIntervalSince1970]; + self.systemUptime = [TDDeviceInfo uptime]; + } + + return self; +} + +- (void)recalibrationWithTimeInterval:(NSTimeInterval)timestamp { + self.serverTime = timestamp; + self.systemUptime = [TDDeviceInfo uptime]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.m.meta new file mode 100644 index 0000000..27989a1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTime.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3eae1ec6bc4dc264f860a60148927c82 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.h new file mode 100644 index 0000000..c53c3a3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.h @@ -0,0 +1,17 @@ +#import + +#if __has_include() +#import +#else +#import "TDCalibratedTime.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCalibratedTimeWithNTP : TDCalibratedTime + +- (void)recalibrationWithNtps:(NSArray *)ntpServers; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.h.meta new file mode 100644 index 0000000..c0779c9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c096ce4f5f3f0d448bd517bb45a6a15b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.m new file mode 100644 index 0000000..9dd26a5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.m @@ -0,0 +1,88 @@ +#import "TDCalibratedTimeWithNTP.h" +#import "TDNTPServer.h" +#import "TDLogging.h" +#import "TDDeviceInfo.h" + +@interface TDCalibratedTimeWithNTP() +@end + +static dispatch_group_t _ta_ntpGroup; +static NSString *_ta_ntpQueuelabel; +static dispatch_queue_t _ta_ntpSerialQueue; + +@implementation TDCalibratedTimeWithNTP + +@synthesize serverTime = _serverTime; + ++ (instancetype)sharedInstance { + static dispatch_once_t once; + static id sharedInstance; + dispatch_once(&once, ^{ + sharedInstance = [[TDCalibratedTimeWithNTP alloc] init]; + _ta_ntpGroup = dispatch_group_create(); + _ta_ntpQueuelabel = [NSString stringWithFormat:@"cn.thinkingdata.ntp.%p", (void *)self]; + _ta_ntpSerialQueue = dispatch_queue_create([_ta_ntpQueuelabel UTF8String], DISPATCH_QUEUE_SERIAL); + }); + return sharedInstance; +} + +- (void)recalibrationWithNtps:(NSArray *)ntpServers { + + if (_ta_ntpGroup) { + TDLogDebug(@"ntp servers async start"); + } else { + TDLogDebug(@"ntp servers async start, _ntpGroup is nil"); + } + dispatch_group_async(_ta_ntpGroup, _ta_ntpSerialQueue, ^{ + [self startNtp:ntpServers]; + }); +} + +- (NSTimeInterval)serverTime { + + if (_ta_ntpGroup) { + + long ret = dispatch_group_wait(_ta_ntpGroup, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC))); + if (ret != 0) { + self.stopCalibrate = YES; + } + return _serverTime; + } else { + self.stopCalibrate = YES; + } + + return 0; + +} + +- (void)startNtp:(NSArray *)ntpServerHost { + NSMutableArray *serverHostArr = [NSMutableArray array]; + for (NSString *host in ntpServerHost) { + if ([host isKindOfClass:[NSString class]] && host.length > 0) { + [serverHostArr addObject:host]; + } + } + NSError *err; + for (NSString *host in serverHostArr) { + TDLogDebug(@"ntp host :%@", host); + err = nil; + TDNTPServer *server = [[TDNTPServer alloc] initWithHostname:host port:123]; + NSTimeInterval offset = [server dateWithError:&err]; + [server disconnect]; + + if (err) { + TDLogDebug(@"ntp failed :%@", err); + } else { + self.systemUptime = [TDDeviceInfo uptime]; + self.serverTime = [[NSDate dateWithTimeIntervalSinceNow:offset] timeIntervalSince1970]; + break; + } + } + + if (err) { + TDLogDebug(@"get ntp time failed"); + self.stopCalibrate = YES; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.m.meta new file mode 100644 index 0000000..986b80e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDCalibratedTimeWithNTP.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0ca58f784bb556e4d8c33dc926f7ad15 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.h new file mode 100644 index 0000000..fbae756 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.h @@ -0,0 +1,28 @@ + +#import + +@interface TDNTPServer : NSObject + +NS_ASSUME_NONNULL_BEGIN + +@property (readonly, strong, nonatomic) NSString *hostname; + +@property (readonly, assign, nonatomic) NSUInteger port; + +@property (assign, atomic) NSTimeInterval timeout; + +@property (readonly, atomic, getter=isConnected) BOOL connected; + +@property (class, readonly, nonatomic) TDNTPServer *defaultServer; + +- (instancetype)initWithHostname:(NSString *)hostname port:(NSUInteger)port NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithHostname:(NSString *)hostname; +- (instancetype)init; +- (BOOL)connectWithError:(NSError *__autoreleasing _Nullable *_Nullable)error NS_REQUIRES_SUPER; +- (void)disconnect NS_REQUIRES_SUPER; +- (BOOL)syncWithError:(NSError *__autoreleasing _Nullable *_Nullable)error NS_REQUIRES_SUPER; +- (NSTimeInterval)dateWithError:(NSError *__autoreleasing _Nullable *_Nullable)error; + +NS_ASSUME_NONNULL_END + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.h.meta new file mode 100644 index 0000000..751cc12 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5605c0d54f2731d42b28d6f72b697f30 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.m new file mode 100644 index 0000000..19584fb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.m @@ -0,0 +1,217 @@ + +#import "TDNTPServer.h" + +#import "TDNTPTypes.h" +#import "TDLogging.h" + +#import +#import +#import +#import + +@implementation TDNTPServer { + NSTimeInterval _timeout; + int _socket; + + NSTimeInterval _offset; +} + +static const uint32_t kSecondsFrom1900To1970 = 2208988800UL; + +static ufixed64_t ntp_localtime_get_ufixed64() { + struct timeval tv; + gettimeofday(&tv, NULL); + return ufixed64((uint32_t)tv.tv_sec + kSecondsFrom1900To1970, tv.tv_usec * (pow(2, 32) / USEC_PER_SEC)); +} + ++ (TDNTPServer *)defaultServer { + static TDNTPServer *server = nil; + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + server = [[TDNTPServer alloc] init]; + }); + return server; +} + +- (instancetype)initWithHostname:(NSString *)hostname port:(NSUInteger)port { + self = [super init]; + if (self) { + _hostname = [hostname copy]; + _port = port; + _timeout = 3.0; + _socket = -1; + + _offset = NAN; + } + return self; +} + +- (instancetype)initWithHostname:(NSString *)hostname { + return [self initWithHostname:hostname port:123]; +} + +- (instancetype)init { + return [self initWithHostname:@"pool.ntp.org"]; +} + +- (void)dealloc { + [self disconnect]; +} + +- (void)setTimeout:(NSTimeInterval)timeout { + assert(timeout > 0 && isfinite(timeout)); + @synchronized (self) { + _timeout = timeout; + if (_socket >= 0) { + struct timeval tv = { .tv_sec = _timeout, .tv_usec = (_timeout - trunc(_timeout)) * USEC_PER_SEC }; + setsockopt(_socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + } + + } +} + +- (NSTimeInterval)timeout { + @synchronized (self) { + return _timeout; + } +} + +- (BOOL)isConnected { + @synchronized (self) { + return _socket >= 0; + } +} + +- (BOOL)connectWithError:(NSError *__autoreleasing _Nullable *_Nullable)error { + @synchronized (self) { + if (_socket >= 0) { + return YES; + } + + struct addrinfo hints = {0}, *addrinfo = NULL; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + NSString *port = [[NSString alloc] initWithFormat:@"%lu", (unsigned long) _port]; + + int getaddrinfo_err = getaddrinfo(_hostname.UTF8String, port.UTF8String, &hints, &addrinfo); + if (getaddrinfo_err != 0) { + if (error) { + NSString *errorDescription = [[NSString alloc] initWithUTF8String:gai_strerror(getaddrinfo_err)]; + NSDictionary *errorInfo = [[NSDictionary alloc] initWithObjectsAndKeys:errorDescription, NSLocalizedDescriptionKey, nil]; + *error = [NSError errorWithDomain:@"netdb" code:getaddrinfo_err userInfo:errorInfo]; + } + return NO; + } + + const int sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol); + if (sock < 0) { + if (error) { + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil]; + } + freeaddrinfo(addrinfo); + return NO; + } + + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK); + + struct timeval timeout = { .tv_sec = _timeout, .tv_usec = (_timeout - trunc(_timeout)) * USEC_PER_SEC }; + int connect_err = connect(sock, addrinfo->ai_addr, addrinfo->ai_addrlen) ? errno : 0; + if (connect_err == EINPROGRESS) { + fd_set fd; + FD_ZERO(&fd); + FD_SET(sock, &fd); + + const int select_err = select(sock + 1, &fd, &fd, NULL, &timeout); + if (select_err <= 0) { + connect_err = select_err ? errno : ETIMEDOUT; + } else { + socklen_t optlen = sizeof(connect_err); + getsockopt(sock, SOL_SOCKET, SO_ERROR, &connect_err, &optlen); + } + } + freeaddrinfo(addrinfo); + if (connect_err) { + if (error) { + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:connect_err userInfo:nil]; + } + close(sock); + return NO; + } + + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK); + + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + + [self willChangeValueForKey:@"connected"]; + _socket = sock; + [self didChangeValueForKey:@"connected"]; + + return YES; + } +} + +- (void)disconnect { + @synchronized (self) { + if (_socket >= 0) { + close(_socket); + + [self willChangeValueForKey:@"connected"]; + _socket = -1; + [self didChangeValueForKey:@"connected"]; + } + } +} + +- (BOOL)syncWithError:(NSError *__autoreleasing _Nullable *_Nullable)error { + @synchronized (self) { + if (![self connectWithError:error]) { + return NO; + } + + ntp_packet_t packet = {0}; + packet.version_number = 4; + packet.mode = 3; + packet.transmit_timestamp = ntp_localtime_get_ufixed64(); + packet = hton_ntp_packet(packet); + const ssize_t send_s = send(_socket, &packet, sizeof(packet), 0); + const int send_err = send_s == sizeof(packet) ? 0 : send_s >= 0 ? EIO : errno; + if (send_err) { + if (error) { + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:send_err userInfo:nil]; + } + return NO; + } + + const ssize_t recv_s = recv(_socket, &packet, sizeof(packet), 0); + const int recv_err = recv_s == sizeof(packet) ? 0 : recv_s >= 0 ? EIO : errno; + if (recv_err) { + if (error) { + *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:recv_err userInfo:nil]; + } + return NO; + } + + packet = ntoh_ntp_packet(packet); + const double T[4] = { + ufixed64_as_double(packet.originate_timestamp), + ufixed64_as_double(packet.receive_timestamp), + ufixed64_as_double(packet.transmit_timestamp), + ufixed64_as_double(ntp_localtime_get_ufixed64()), + }; + _offset = ((T[1] - T[0]) + (T[2] - T[3])) / 2.0; + + TDLogDebug(@"%s %f", __func__, _offset); + return YES; + } +} + +- (NSTimeInterval)dateWithError:(NSError *__autoreleasing _Nullable *_Nullable)error { + @synchronized (self) { + return isfinite(_offset) || [self syncWithError:error] ? _offset : 0; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.m.meta new file mode 100644 index 0000000..42cbb44 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPServer.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fc855a23b2721ac46b2392d18a2ada2b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.c b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.c new file mode 100644 index 0000000..d09df8b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.c @@ -0,0 +1,61 @@ +#include "TDNTPTypes.h" + +#include +#include +#include + +ufixed32_t ufixed32(uint16_t whole, uint16_t fraction) { + return (struct ufixed32) { .whole = whole, .fraction = fraction }; +} + +ufixed64_t ufixed64(uint32_t whole, uint32_t fraction) { + return (struct ufixed64) { .whole = whole, .fraction = fraction }; +} + +double ufixed64_as_double(ufixed64_t uf64) { + return uf64.whole + uf64.fraction * pow(2, -32); +} + +ufixed64_t ufixed64_with_double(double value) { + assert(value >= 0); + return ufixed64(value, (value - trunc(value) * pow(2, 32))); +} + +ufixed32_t hton_ufixed32(ufixed32_t uf32) { + return ufixed32(htons(uf32.whole), htons(uf32.fraction)); +} +ufixed32_t ntoh_ufixed32(ufixed32_t uf32) { + return ufixed32(ntohs(uf32.whole), ntohs(uf32.fraction)); +} + +ufixed64_t hton_ufixed64(ufixed64_t uf64) { + return ufixed64(htonl(uf64.whole), htonl(uf64.fraction)); +} +ufixed64_t ntoh_ufixed64(ufixed64_t uf64) { + return ufixed64(ntohl(uf64.whole), ntohl(uf64.fraction)); +} + +ntp_packet_t hton_ntp_packet(ntp_packet_t p) { + p.root_delay = hton_ufixed32(p.root_delay); + p.root_dispersion = hton_ufixed32(p.root_dispersion); + + p.reference_timestamp = hton_ufixed64(p.reference_timestamp); + p.originate_timestamp = hton_ufixed64(p.originate_timestamp); + p.receive_timestamp = hton_ufixed64(p.receive_timestamp); + p.transmit_timestamp = hton_ufixed64(p.transmit_timestamp); + + return p; +} + +ntp_packet_t ntoh_ntp_packet(ntp_packet_t p) { + p.root_delay = ntoh_ufixed32(p.root_delay); + p.root_dispersion = ntoh_ufixed32(p.root_dispersion); + + p.reference_timestamp = ntoh_ufixed64(p.reference_timestamp); + p.originate_timestamp = ntoh_ufixed64(p.originate_timestamp); + p.receive_timestamp = ntoh_ufixed64(p.receive_timestamp); + p.transmit_timestamp = ntoh_ufixed64(p.transmit_timestamp); + + return p; +} + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.c.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.c.meta new file mode 100644 index 0000000..1d034fb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.c.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7bb26cf1c1142214c993c708c149e69a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.h new file mode 100644 index 0000000..cab6a44 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.h @@ -0,0 +1,51 @@ +#ifndef NTPTypes_h +#define NTPTypes_h + +#include + +typedef struct ufixed32 { + uint16_t whole, fraction; +} ufixed32_t; + +ufixed32_t ufixed32(uint16_t whole, uint16_t fraction); + +typedef struct ufixed64 { + uint32_t whole, fraction; +} ufixed64_t; + +ufixed64_t ufixed64(uint32_t whole, uint32_t fraction); + +double ufixed64_as_double(ufixed64_t); +ufixed64_t ufixed64_with_double(double); + +typedef struct ntp_packet_t { + uint8_t mode : 3; + uint8_t version_number : 3; + uint8_t leap_indicator : 2; + + uint8_t stratum; + uint8_t poll; + uint8_t precision; + + ufixed32_t root_delay; + ufixed32_t root_dispersion; + uint8_t reference_identifier[4]; + + ufixed64_t reference_timestamp; + ufixed64_t originate_timestamp; + ufixed64_t receive_timestamp; + ufixed64_t transmit_timestamp; +} ntp_packet_t; + + + +ufixed32_t hton_ufixed32(ufixed32_t); +ufixed32_t ntoh_ufixed32(ufixed32_t); + +ufixed64_t hton_ufixed64(ufixed64_t); +ufixed64_t ntoh_ufixed64(ufixed64_t); + +ntp_packet_t hton_ntp_packet(ntp_packet_t); +ntp_packet_t ntoh_ntp_packet(ntp_packet_t); + +#endif diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.h.meta new file mode 100644 index 0000000..bc60758 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/CalibratedTime/TDNTPTypes.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1538e7eab85b93a4f9507c1e9140f943 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger.meta new file mode 100644 index 0000000..86af6b5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8884cfb40d7575f458bed380cd4877f6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.h new file mode 100644 index 0000000..0336c25 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.h @@ -0,0 +1,34 @@ +#import + +#if __has_include() +#import +#else +#import "TDConstant.h" +#endif + + +NS_ASSUME_NONNULL_BEGIN + +#define TDLogDebug(message, ...) TDLogWithType(TDLoggingLevelDebug, message, ##__VA_ARGS__) +#define TDLogInfo(message, ...) TDLogWithType(TDLoggingLevelInfo, message, ##__VA_ARGS__) +#define TDLogError(message, ...) TDLogWithType(TDLoggingLevelError, message, ##__VA_ARGS__) + +#define TDLogWithType(type, message, ...) \ +{ \ +if ([TDLogging sharedInstance].loggingLevel != TDLoggingLevelNone && type <= [TDLogging sharedInstance].loggingLevel) \ +{ \ +[[TDLogging sharedInstance] logCallingFunction:type format:(message), ##__VA_ARGS__]; \ +} \ +} + + + +@interface TDLogging : NSObject + +@property (class, nonatomic, readonly) TDLogging *sharedInstance; +@property (assign, nonatomic) TDLoggingLevel loggingLevel; +- (void)logCallingFunction:(TDLoggingLevel)type format:(id)messageFormat, ...; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.h.meta new file mode 100644 index 0000000..aaed012 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b9bbd374c99f04548a8114abaadd9900 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.m new file mode 100644 index 0000000..6f46fc7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.m @@ -0,0 +1,35 @@ +#import "TDLogging.h" + +#import +#import "TDOSLog.h" + +@implementation TDLogging + ++ (instancetype)sharedInstance { + static dispatch_once_t once; + static id sharedInstance; + dispatch_once(&once, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + +- (void)logCallingFunction:(TDLoggingLevel)type format:(id)messageFormat, ... { + if (messageFormat) { + va_list formatList; + va_start(formatList, messageFormat); + NSString *formattedMessage = [[NSString alloc] initWithFormat:messageFormat arguments:formatList]; + va_end(formatList); + +#ifdef __IPHONE_10_0 + if (@available(iOS 10.0, *)) { + [TDOSLog log:NO message:formattedMessage type:type]; + } +#else + NSLog(@"[THINKING] %@", formattedMessage); +#endif + } +} + +@end + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.m.meta new file mode 100644 index 0000000..ddf98b1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDLogging.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: af4005be7602ad3478220a9f6d857e28 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.h new file mode 100644 index 0000000..9082a33 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.h @@ -0,0 +1,45 @@ +#import + +#if __has_include() +#import +#else +#import "TDConstant.h" +#endif + +@class TDLogMessage; +@protocol TDLogger; + +NS_ASSUME_NONNULL_BEGIN + +@interface TDOSLog : NSObject + ++ (void)log:(BOOL)asynchronous + message:(NSString *)message + type:(TDLoggingLevel)type; + +@end + +@protocol TDLogger + +- (void)logMessage:(TDLogMessage *)logMessage; + +@optional + +@property (nonatomic, strong, readonly) dispatch_queue_t loggerQueue; + +@end + +@interface TDLogMessage : NSObject + +- (instancetype)initWithMessage:(NSString *)message + type:(TDLoggingLevel)type; + +@end + +@interface TDAbstractLogger : NSObject + ++ (instancetype)sharedInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.h.meta new file mode 100644 index 0000000..cce229c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 56c1d21b7c76d0744b94ceabbc5b58f2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.m new file mode 100644 index 0000000..ce46107 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.m @@ -0,0 +1,198 @@ +#import "TDOSLog.h" + +#import + +#ifndef DDLOG_MAX_QUEUE_SIZE + #define DDLOG_MAX_QUEUE_SIZE 1000 +#endif + +static void *const GlobalLoggingQueueIdentityKey = (void *)&GlobalLoggingQueueIdentityKey; + +@interface TDOSLog () +{ + id _logger; + dispatch_queue_t _loggerQueue; +} + +@end + +@implementation TDOSLog + +static dispatch_queue_t _loggingQueue; +static dispatch_group_t _loggingGroup; +static dispatch_semaphore_t _queueSemaphore; + ++ (instancetype)sharedInstance { + static id sharedInstance = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + [sharedInstance addLogger:[TDAbstractLogger sharedInstance]]; + }); + + return sharedInstance; +} + ++ (void)initialize { + static dispatch_once_t TDLogOnceToken; + + dispatch_once(&TDLogOnceToken, ^{ + _loggingQueue = dispatch_queue_create("cn.thinking.log", NULL); + _loggingGroup = dispatch_group_create(); + + void *nonNullValue = GlobalLoggingQueueIdentityKey; + dispatch_queue_set_specific(_loggingQueue, GlobalLoggingQueueIdentityKey, nonNullValue, NULL); + + _queueSemaphore = dispatch_semaphore_create(DDLOG_MAX_QUEUE_SIZE); + }); +} + ++ (void)addLogger { + [self.sharedInstance addLogger:[TDAbstractLogger sharedInstance]]; +} + +- (void)addLogger:(id )logger { + dispatch_async(_loggingQueue, ^{ @autoreleasepool { + [self lt_addLogger:logger]; + } }); +} + +- (void)queueLogMessage:(TDLogMessage *)logMessage asynchronously:(BOOL)asyncFlag { + dispatch_block_t logBlock = ^{ + dispatch_semaphore_wait(_queueSemaphore, DISPATCH_TIME_FOREVER); + @autoreleasepool { + [self lt_log:logMessage]; + } + }; + + if (asyncFlag) { + dispatch_async(_loggingQueue, logBlock); + } else if (dispatch_get_specific(GlobalLoggingQueueIdentityKey)) { + logBlock(); + } else { + dispatch_sync(_loggingQueue, logBlock); + } +} + ++ (void)log:(BOOL)asynchronous + message:(NSString *)message + type:(TDLoggingLevel)type { + [self.sharedInstance log:asynchronous message:message type:type]; +} + +- (void)log:(BOOL)asynchronous + message:(NSString *)message + type:(TDLoggingLevel)type { + TDLogMessage *logMessage = [[TDLogMessage alloc] initWithMessage:message type:type]; + [self queueLogMessage:logMessage asynchronously:asynchronous]; +} + +- (void)lt_addLogger:(id )logger { + NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), + @"This method should only be run on the logging thread/queue"); + const char *loggerQueueName = [@"cn.thinkingdata.analytics.osLogger" UTF8String]; + dispatch_queue_t loggerQueue = dispatch_queue_create(loggerQueueName, NULL); + _logger = logger; + _loggerQueue = loggerQueue; +} + +- (void)lt_log:(TDLogMessage *)logMessage { + NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), + @"This method should only be run on the logging thread/queue"); + + dispatch_group_async(_loggingGroup, _loggerQueue, ^{ @autoreleasepool { + [self->_logger logMessage:logMessage]; + } }); + + dispatch_group_wait(_loggingGroup, DISPATCH_TIME_FOREVER); + dispatch_semaphore_signal(_queueSemaphore); +} + +@end + +@interface TDLogMessage () +{ +@public + NSString *_message; + TDLoggingLevel _type; +} + +@end + +@implementation TDLogMessage + +- (instancetype)initWithMessage:(NSString *)message + type:(TDLoggingLevel)type { + if ((self = [super init])) { + _message = [message copy]; + _type = type; + } + return self; +} + +@end + +@interface TDAbstractLogger () + +@property (strong, nonatomic, readwrite) os_log_t logger; +@property (class, readonly, strong) TDAbstractLogger *sharedInstance; + +@end + +@implementation TDAbstractLogger + +static TDAbstractLogger *sharedInstance; + ++ (instancetype)sharedInstance { + static dispatch_once_t TDOSLoggerOnceToken; + + dispatch_once(&TDOSLoggerOnceToken, ^{ + sharedInstance = [[[self class] alloc] init]; + }); + + return sharedInstance; +} + +- (os_log_t)getLogger { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + return os_log_create("cn.thinkingdata.analytics.log", "THINKING"); +#pragma clang diagnostic pop +} + +- (os_log_t)logger { + if (_logger == nil) { + _logger = [self getLogger]; + } + return _logger; +} + +- (void)logMessage:(TDLogMessage *)logMessage { +#ifdef __IPHONE_10_0 + if (@available(iOS 10.0, *)) { + NSString *message = logMessage->_message; + if (message != nil) { + const char *msg = [message UTF8String]; + __auto_type logger = [self logger]; + switch (logMessage->_type) { + case TDLoggingLevelDebug: + os_log_debug(logger, "%{public}s", msg); + break; + case TDLoggingLevelInfo: + os_log_info(logger, "%{public}s", msg); + break; + case TDLoggingLevelError: + os_log_error(logger, "%{public}s", msg); + break; + case TDLoggingLevelNone: + default: + break; + } + } + } +#endif +} + +@end + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.m.meta new file mode 100644 index 0000000..bd440ea --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/Logger/TDOSLog.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 656a3586e9e7eb1459c502562aeca578 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.h new file mode 100644 index 0000000..e587a83 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.h @@ -0,0 +1,13 @@ +// +// TAThreadSafeArray.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import + +/// A simple implementation of thread safe mutable array. +@interface TAThreadSafeArray : NSMutableArray + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.h.meta new file mode 100644 index 0000000..e4b8e57 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a43133928440ec94e8d9ccf0268ed722 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.m new file mode 100644 index 0000000..aece95c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.m @@ -0,0 +1,372 @@ +// +// TAThreadSafeArray.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import "TAThreadSafeArray.h" + +#define INIT(...) self = super.init; \ +if (!self) return nil; \ +__VA_ARGS__; \ +if (!_arr) return nil; \ +_lock = dispatch_semaphore_create(1); \ +return self; + + +#define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \ +__VA_ARGS__; \ +dispatch_semaphore_signal(_lock); + + +@implementation TAThreadSafeArray { + NSMutableArray *_arr; //Subclass a class cluster... + dispatch_semaphore_t _lock; +} + +#pragma mark - init + +- (instancetype)init { + INIT(_arr = [[NSMutableArray alloc] init]); +} + +- (instancetype)initWithCapacity:(NSUInteger)numItems { + INIT(_arr = [[NSMutableArray alloc] initWithCapacity:numItems]); +} + +- (instancetype)initWithArray:(NSArray *)array { + INIT(_arr = [[NSMutableArray alloc] initWithArray:array]); +} + +- (instancetype)initWithObjects:(const id[])objects count:(NSUInteger)cnt { + INIT(_arr = [[NSMutableArray alloc] initWithObjects:objects count:cnt]); +} + +- (instancetype)initWithContentsOfFile:(NSString *)path { + INIT(_arr = [[NSMutableArray alloc] initWithContentsOfFile:path]); +} + +- (instancetype)initWithContentsOfURL:(NSURL *)url { + INIT(_arr = [[NSMutableArray alloc] initWithContentsOfURL:url]); +} + +#pragma mark - method + +- (NSUInteger)count { + LOCK(NSUInteger count = _arr.count); return count; +} + +- (id)objectAtIndex:(NSUInteger)index { + LOCK(id obj = [_arr objectAtIndex:index]); return obj; +} + +- (NSArray *)arrayByAddingObject:(id)anObject { + LOCK(NSArray * arr = [_arr arrayByAddingObject:anObject]); return arr; +} + +- (NSArray *)arrayByAddingObjectsFromArray:(NSArray *)otherArray { + LOCK(NSArray * arr = [_arr arrayByAddingObjectsFromArray:otherArray]); return arr; +} + +- (NSString *)componentsJoinedByString:(NSString *)separator { + LOCK(NSString * str = [_arr componentsJoinedByString:separator]); return str; +} + +- (BOOL)containsObject:(id)anObject { + LOCK(BOOL c = [_arr containsObject:anObject]); return c; +} + +- (NSString *)description { + LOCK(NSString * d = _arr.description); return d; +} + +- (NSString *)descriptionWithLocale:(id)locale { + LOCK(NSString * d = [_arr descriptionWithLocale:locale]); return d; +} + +- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level { + LOCK(NSString * d = [_arr descriptionWithLocale:locale indent:level]); return d; +} + +- (id)firstObjectCommonWithArray:(NSArray *)otherArray { + LOCK(id o = [_arr firstObjectCommonWithArray:otherArray]); return o; +} + +- (void)getObjects:(id __unsafe_unretained[])objects range:(NSRange)range { + LOCK([_arr getObjects:objects range:range]); +} + +- (NSUInteger)indexOfObject:(id)anObject { + LOCK(NSUInteger i = [_arr indexOfObject:anObject]); return i; +} + +- (NSUInteger)indexOfObject:(id)anObject inRange:(NSRange)range { + LOCK(NSUInteger i = [_arr indexOfObject:anObject inRange:range]); return i; +} + +- (NSUInteger)indexOfObjectIdenticalTo:(id)anObject { + LOCK(NSUInteger i = [_arr indexOfObjectIdenticalTo:anObject]); return i; +} + +- (NSUInteger)indexOfObjectIdenticalTo:(id)anObject inRange:(NSRange)range { + LOCK(NSUInteger i = [_arr indexOfObjectIdenticalTo:anObject inRange:range]); return i; +} + +- (id)firstObject { + LOCK(id o = _arr.firstObject); return o; +} + +- (id)lastObject { + LOCK(id o = _arr.lastObject); return o; +} + +- (NSEnumerator *)objectEnumerator { + LOCK(NSEnumerator * e = [_arr objectEnumerator]); return e; +} + +- (NSEnumerator *)reverseObjectEnumerator { + LOCK(NSEnumerator * e = [_arr reverseObjectEnumerator]); return e; +} + +- (NSData *)sortedArrayHint { + LOCK(NSData * d = [_arr sortedArrayHint]); return d; +} + +- (NSArray *)sortedArrayUsingFunction:(NSInteger (NS_NOESCAPE *)(id, id, void *))comparator context:(void *)context { + LOCK(NSArray * arr = [_arr sortedArrayUsingFunction:comparator context:context]) return arr; +} + +- (NSArray *)sortedArrayUsingFunction:(NSInteger (NS_NOESCAPE *)(id, id, void *))comparator context:(void *)context hint:(NSData *)hint { + LOCK(NSArray * arr = [_arr sortedArrayUsingFunction:comparator context:context hint:hint]); return arr; +} + +- (NSArray *)sortedArrayUsingSelector:(SEL)comparator { + LOCK(NSArray * arr = [_arr sortedArrayUsingSelector:comparator]); return arr; +} + +- (NSArray *)subarrayWithRange:(NSRange)range { + LOCK(NSArray * arr = [_arr subarrayWithRange:range]) return arr; +} + +- (void)makeObjectsPerformSelector:(SEL)aSelector { + LOCK([_arr makeObjectsPerformSelector:aSelector]); +} + +- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)argument { + LOCK([_arr makeObjectsPerformSelector:aSelector withObject:argument]); +} + +- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes { + LOCK(NSArray * arr = [_arr objectsAtIndexes:indexes]); return arr; +} + +- (id)objectAtIndexedSubscript:(NSUInteger)idx { + LOCK(id o = [_arr objectAtIndexedSubscript:idx]); return o; +} + +- (void)enumerateObjectsUsingBlock:(void (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))block { + LOCK([_arr enumerateObjectsUsingBlock:block]); +} + +- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))block { + LOCK([_arr enumerateObjectsWithOptions:opts usingBlock:block]); +} + +- (void)enumerateObjectsAtIndexes:(NSIndexSet *)s options:(NSEnumerationOptions)opts usingBlock:(void (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))block { + LOCK([_arr enumerateObjectsAtIndexes:s options:opts usingBlock:block]); +} + +- (NSUInteger)indexOfObjectPassingTest:(BOOL (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))predicate { + LOCK(NSUInteger i = [_arr indexOfObjectPassingTest:predicate]); return i; +} + +- (NSUInteger)indexOfObjectWithOptions:(NSEnumerationOptions)opts passingTest:(BOOL (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))predicate { + LOCK(NSUInteger i = [_arr indexOfObjectWithOptions:opts passingTest:predicate]); return i; +} + +- (NSUInteger)indexOfObjectAtIndexes:(NSIndexSet *)s options:(NSEnumerationOptions)opts passingTest:(BOOL (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))predicate { + LOCK(NSUInteger i = [_arr indexOfObjectAtIndexes:s options:opts passingTest:predicate]); return i; +} + +- (NSIndexSet *)indexesOfObjectsPassingTest:(BOOL (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))predicate { + LOCK(NSIndexSet * i = [_arr indexesOfObjectsPassingTest:predicate]); return i; +} + +- (NSIndexSet *)indexesOfObjectsWithOptions:(NSEnumerationOptions)opts passingTest:(BOOL (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))predicate { + LOCK(NSIndexSet * i = [_arr indexesOfObjectsWithOptions:opts passingTest:predicate]); return i; +} + +- (NSIndexSet *)indexesOfObjectsAtIndexes:(NSIndexSet *)s options:(NSEnumerationOptions)opts passingTest:(BOOL (NS_NOESCAPE ^)(id obj, NSUInteger idx, BOOL *stop))predicate { + LOCK(NSIndexSet * i = [_arr indexesOfObjectsAtIndexes:s options:opts passingTest:predicate]); return i; +} + +- (NSArray *)sortedArrayUsingComparator:(NSComparator NS_NOESCAPE)cmptr { + LOCK(NSArray * a = [_arr sortedArrayUsingComparator:cmptr]); return a; +} + +- (NSArray *)sortedArrayWithOptions:(NSSortOptions)opts usingComparator:(NSComparator NS_NOESCAPE)cmptr { + LOCK(NSArray * a = [_arr sortedArrayWithOptions:opts usingComparator:cmptr]); return a; +} + +- (NSUInteger)indexOfObject:(id)obj inSortedRange:(NSRange)r options:(NSBinarySearchingOptions)opts usingComparator:(NSComparator NS_NOESCAPE)cmp { + LOCK(NSUInteger i = [_arr indexOfObject:obj inSortedRange:r options:opts usingComparator:cmp]); return i; +} + +#pragma mark - mutable + +- (void)addObject:(id)anObject { + LOCK([_arr addObject:anObject]); +} + +- (void)insertObject:(id)anObject atIndex:(NSUInteger)index { + LOCK([_arr insertObject:anObject atIndex:index]); +} + +- (void)removeLastObject { + LOCK([_arr removeLastObject]); +} + +- (void)removeObjectAtIndex:(NSUInteger)index { + LOCK([_arr removeObjectAtIndex:index]); +} + +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject { + LOCK([_arr replaceObjectAtIndex:index withObject:anObject]); +} + +- (void)addObjectsFromArray:(NSArray *)otherArray { + LOCK([_arr addObjectsFromArray:otherArray]); +} + +- (void)exchangeObjectAtIndex:(NSUInteger)idx1 withObjectAtIndex:(NSUInteger)idx2 { + LOCK([_arr exchangeObjectAtIndex:idx1 withObjectAtIndex:idx2]); +} + +- (void)removeAllObjects { + LOCK([_arr removeAllObjects]); +} + +- (void)removeObject:(id)anObject inRange:(NSRange)range { + LOCK([_arr removeObject:anObject inRange:range]); +} + +- (void)removeObject:(id)anObject { + LOCK([_arr removeObject:anObject]); +} + +- (void)removeObjectIdenticalTo:(id)anObject inRange:(NSRange)range { + LOCK([_arr removeObjectIdenticalTo:anObject inRange:range]); +} + +- (void)removeObjectIdenticalTo:(id)anObject { + LOCK([_arr removeObjectIdenticalTo:anObject]); +} + +- (void)removeObjectsInArray:(NSArray *)otherArray { + LOCK([_arr removeObjectsInArray:otherArray]); +} + +- (void)removeObjectsInRange:(NSRange)range { + LOCK([_arr removeObjectsInRange:range]); +} + +- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray range:(NSRange)otherRange { + LOCK([_arr replaceObjectsInRange:range withObjectsFromArray:otherArray range:otherRange]); +} + +- (void)replaceObjectsInRange:(NSRange)range withObjectsFromArray:(NSArray *)otherArray { + LOCK([_arr replaceObjectsInRange:range withObjectsFromArray:otherArray]); +} + +- (void)setArray:(NSArray *)otherArray { + LOCK([_arr setArray:otherArray]); +} + +- (void)sortUsingFunction:(NSInteger (NS_NOESCAPE *)(id, id, void *))compare context:(void *)context { + LOCK([_arr sortUsingFunction:compare context:context]); +} + +- (void)sortUsingSelector:(SEL)comparator { + LOCK([_arr sortUsingSelector:comparator]); +} + +- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes { + LOCK([_arr insertObjects:objects atIndexes:indexes]); +} + +- (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { + LOCK([_arr removeObjectsAtIndexes:indexes]); +} + +- (void)replaceObjectsAtIndexes:(NSIndexSet *)indexes withObjects:(NSArray *)objects { + LOCK([_arr replaceObjectsAtIndexes:indexes withObjects:objects]); +} + +- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx { + LOCK([_arr setObject:obj atIndexedSubscript:idx]); +} + +- (void)sortUsingComparator:(NSComparator NS_NOESCAPE)cmptr { + LOCK([_arr sortUsingComparator:cmptr]); +} + +- (void)sortWithOptions:(NSSortOptions)opts usingComparator:(NSComparator NS_NOESCAPE)cmptr { + LOCK([_arr sortWithOptions:opts usingComparator:cmptr]); +} + +- (BOOL)isEqualToArray:(NSArray *)otherArray { + if (otherArray == self) return YES; + if ([otherArray isKindOfClass:TAThreadSafeArray.class]) { + TAThreadSafeArray *other = (id)otherArray; + BOOL isEqual; + dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(other->_lock, DISPATCH_TIME_FOREVER); + isEqual = [_arr isEqualToArray:other->_arr]; + dispatch_semaphore_signal(other->_lock); + dispatch_semaphore_signal(_lock); + return isEqual; + } + return NO; +} + +#pragma mark - protocol + +- (id)copyWithZone:(NSZone *)zone { + return [self mutableCopyWithZone:zone]; +} + +- (id)mutableCopyWithZone:(NSZone *)zone { + LOCK(id copiedDictionary = [[self.class allocWithZone:zone] initWithArray:_arr]); + return copiedDictionary; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id __unsafe_unretained[])stackbuf + count:(NSUInteger)len { + LOCK(NSUInteger count = [_arr countByEnumeratingWithState:state objects:stackbuf count:len]); + return count; +} + +- (BOOL)isEqual:(id)object { + if (object == self) return YES; + + if ([object isKindOfClass:TAThreadSafeArray.class]) { + TAThreadSafeArray *other = object; + BOOL isEqual; + dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(other->_lock, DISPATCH_TIME_FOREVER); + isEqual = [_arr isEqual:other->_arr]; + dispatch_semaphore_signal(other->_lock); + dispatch_semaphore_signal(_lock); + return isEqual; + } + return NO; +} + +- (NSUInteger)hash { + LOCK(NSUInteger hash = [_arr hash]); + return hash; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.m.meta new file mode 100644 index 0000000..629be44 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeArray.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7680060cd89566d4a920168d6bf3b95d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.h new file mode 100644 index 0000000..7b87e9b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.h @@ -0,0 +1,11 @@ +// +// TAThreadSafeDictionary.h +// + +#import + +/// A simple implementation of thread safe mutable dictionary. +/// +@interface TAThreadSafeDictionary : NSMutableDictionary + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.h.meta new file mode 100644 index 0000000..322e043 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b338cdd278371a442bbe683971264b80 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.m new file mode 100644 index 0000000..28fcaa5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.m @@ -0,0 +1,223 @@ +// +// TAThreadSafeDictionary.m +// + +#import "TAThreadSafeDictionary.h" + +#define INIT(...) self = super.init; \ +if (!self) return nil; \ +__VA_ARGS__; \ +if (!_dic) return nil; \ +_lock = dispatch_semaphore_create(1); \ +return self; + + +#define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \ +__VA_ARGS__; \ +dispatch_semaphore_signal(_lock); + + +@implementation TAThreadSafeDictionary { + NSMutableDictionary *_dic; //Subclass a class cluster... + dispatch_semaphore_t _lock; +} + +#pragma mark - init + +- (instancetype)init { + INIT(_dic = [[NSMutableDictionary alloc] init]); +} + +- (instancetype)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys { + INIT(_dic = [[NSMutableDictionary alloc] initWithObjects:objects forKeys:keys]); +} + +- (instancetype)initWithCapacity:(NSUInteger)capacity { + INIT(_dic = [[NSMutableDictionary alloc] initWithCapacity:capacity]); +} + +- (instancetype)initWithObjects:(const id[])objects forKeys:(const id [])keys count:(NSUInteger)cnt { + INIT(_dic = [[NSMutableDictionary alloc] initWithObjects:objects forKeys:keys count:cnt]); +} + +- (instancetype)initWithDictionary:(NSDictionary *)otherDictionary { + INIT(_dic = [[NSMutableDictionary alloc] initWithDictionary:otherDictionary]); +} + +- (instancetype)initWithDictionary:(NSDictionary *)otherDictionary copyItems:(BOOL)flag { + INIT(_dic = [[NSMutableDictionary alloc] initWithDictionary:otherDictionary copyItems:flag]); +} + + +#pragma mark - method + +- (NSUInteger)count { + LOCK(NSUInteger c = _dic.count); return c; +} + +- (id)objectForKey:(id)aKey { + LOCK(id o = [_dic objectForKey:aKey]); return o; +} + +- (NSEnumerator *)keyEnumerator { + LOCK(NSEnumerator * e = [_dic keyEnumerator]); return e; +} + +- (NSArray *)allKeys { + LOCK(NSArray * a = [_dic allKeys]); return a; +} + +- (NSArray *)allKeysForObject:(id)anObject { + LOCK(NSArray * a = [_dic allKeysForObject:anObject]); return a; +} + +- (NSArray *)allValues { + LOCK(NSArray * a = [_dic allValues]); return a; +} + +- (NSString *)description { + LOCK(NSString * d = [_dic description]); return d; +} + +- (NSString *)descriptionInStringsFileFormat { + LOCK(NSString * d = [_dic descriptionInStringsFileFormat]); return d; +} + +- (NSString *)descriptionWithLocale:(id)locale { + LOCK(NSString * d = [_dic descriptionWithLocale:locale]); return d; +} + +- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level { + LOCK(NSString * d = [_dic descriptionWithLocale:locale indent:level]); return d; +} + +- (BOOL)isEqualToDictionary:(NSDictionary *)otherDictionary { + if (otherDictionary == self) return YES; + + if ([otherDictionary isKindOfClass:TAThreadSafeDictionary.class]) { + TAThreadSafeDictionary *other = (id)otherDictionary; + BOOL isEqual; + dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(other->_lock, DISPATCH_TIME_FOREVER); + isEqual = [_dic isEqual:other->_dic]; + dispatch_semaphore_signal(other->_lock); + dispatch_semaphore_signal(_lock); + return isEqual; + } + return NO; +} + +- (NSEnumerator *)objectEnumerator { + LOCK(NSEnumerator * e = [_dic objectEnumerator]); return e; +} + +- (NSArray *)objectsForKeys:(NSArray *)keys notFoundMarker:(id)marker { + LOCK(NSArray * a = [_dic objectsForKeys:keys notFoundMarker:marker]); return a; +} + +- (NSArray *)keysSortedByValueUsingSelector:(SEL)comparator { + LOCK(NSArray * a = [_dic keysSortedByValueUsingSelector:comparator]); return a; +} + +- (void)getObjects:(id __unsafe_unretained[])objects andKeys:(id __unsafe_unretained[])keys { + LOCK([_dic getObjects:objects andKeys:keys]); +} + +- (id)objectForKeyedSubscript:(id)key { + LOCK(id o = [_dic objectForKeyedSubscript:key]); return o; +} + +- (void)enumerateKeysAndObjectsUsingBlock:(void (NS_NOESCAPE ^)(id _Nonnull, id _Nonnull, BOOL * _Nonnull))block { + LOCK([_dic enumerateKeysAndObjectsUsingBlock:block]); +} + +- (void)enumerateKeysAndObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (NS_NOESCAPE ^)(id key, id obj, BOOL *stop))block { + LOCK([_dic enumerateKeysAndObjectsWithOptions:opts usingBlock:block]); +} + +- (NSArray *)keysSortedByValueUsingComparator:(NSComparator NS_NOESCAPE)cmptr { + LOCK(NSArray * a = [_dic keysSortedByValueUsingComparator:cmptr]); return a; +} + +- (NSArray *)keysSortedByValueWithOptions:(NSSortOptions)opts usingComparator:(NSComparator NS_NOESCAPE)cmptr { + LOCK(NSArray * a = [_dic keysSortedByValueWithOptions:opts usingComparator:cmptr]); return a; +} + +- (NSSet *)keysOfEntriesPassingTest:(BOOL (^ NS_NOESCAPE)(id _Nonnull, id _Nonnull, BOOL * _Nonnull))predicate { + LOCK(NSSet * a = [_dic keysOfEntriesPassingTest:predicate]); return a; +} + +- (NSSet *)keysOfEntriesWithOptions:(NSEnumerationOptions)opts passingTest:(BOOL (^ NS_NOESCAPE)(id _Nonnull, id _Nonnull, BOOL * _Nonnull))predicate { + LOCK(NSSet * a = [_dic keysOfEntriesWithOptions:opts passingTest:predicate]); return a; +} + +#pragma mark - mutable + +- (void)removeObjectForKey:(id)aKey { + LOCK([_dic removeObjectForKey:aKey]); +} + +- (void)setObject:(id)anObject forKey:(id )aKey { + LOCK([_dic setObject:anObject forKey:aKey]); +} + +- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary { + LOCK([_dic addEntriesFromDictionary:otherDictionary]); +} + +- (void)removeAllObjects { + LOCK([_dic removeAllObjects]); +} + +- (void)removeObjectsForKeys:(NSArray *)keyArray { + LOCK([_dic removeObjectsForKeys:keyArray]); +} + +- (void)setDictionary:(NSDictionary *)otherDictionary { + LOCK([_dic setDictionary:otherDictionary]); +} + +- (void)setObject:(id)obj forKeyedSubscript:(id )key { + LOCK([_dic setObject:obj forKeyedSubscript:key]); +} + +#pragma mark - protocol + +- (id)copyWithZone:(NSZone *)zone { + return [self mutableCopyWithZone:zone]; +} + +- (id)mutableCopyWithZone:(NSZone *)zone { + LOCK(id copiedDictionary = [[self.class allocWithZone:zone] initWithDictionary:_dic]); + return copiedDictionary; +} + +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id __unsafe_unretained[])stackbuf + count:(NSUInteger)len { + LOCK(NSUInteger count = [_dic countByEnumeratingWithState:state objects:stackbuf count:len]); + return count; +} + +- (BOOL)isEqual:(id)object { + if (object == self) return YES; + + if ([object isKindOfClass:TAThreadSafeDictionary.class]) { + TAThreadSafeDictionary *other = object; + BOOL isEqual; + dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(other->_lock, DISPATCH_TIME_FOREVER); + isEqual = [_dic isEqual:other->_dic]; + dispatch_semaphore_signal(other->_lock); + dispatch_semaphore_signal(_lock); + return isEqual; + } + return NO; +} + +- (NSUInteger)hash { + LOCK(NSUInteger hash = [_dic hash]); + return hash; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.m.meta new file mode 100644 index 0000000..193336b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TAThreadSafeDictionary.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 99c995bf358bfd2478bdd7a9f4113cf4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TDConstant.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TDConstant.h new file mode 100644 index 0000000..f591f31 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TDConstant.h @@ -0,0 +1,210 @@ +// +// TDConstant.h +// ThinkingSDK +// +// Created by LiHuanan on 2020/9/8. +// Copyright © 2020 thinkingdata. All rights reserved. +// + +#import +/** +Debug Mode + +- ThinkingAnalyticsDebugOff : Not enabled by default +*/ +typedef NS_OPTIONS(NSInteger, ThinkingAnalyticsDebugMode) { + /** + Not enabled by default + */ + ThinkingAnalyticsDebugOff = 0, + + /** + Enable DebugOnly Mode, Data is not persisted + */ + ThinkingAnalyticsDebugOnly = 1 << 0, + + /** + Enable Debug Mode,Data will persist + */ + ThinkingAnalyticsDebug = 1 << 1, + + /** + Enable Debug Mode,Data will persist,Equivalent to ThinkingAnalyticsDebug + */ + ThinkingAnalyticsDebugOn = ThinkingAnalyticsDebug, +}; + + +/** + Log Level + + - TDLoggingLevelNone : Not enabled by default + */ +typedef NS_OPTIONS(NSInteger, TDLoggingLevel) { + /** + Not enabled by default + */ + TDLoggingLevelNone = 0, + + /** + Error Log + */ + TDLoggingLevelError = 1 << 0, + + /** + Info Log + */ + TDLoggingLevelInfo = 1 << 1, + + /** + Debug Log + */ + TDLoggingLevelDebug = 1 << 2, +}; + +/** + Https Certificate Verification Mode +*/ +typedef NS_OPTIONS(NSInteger, TDSSLPinningMode) { + /** + The default authentication method will only verify the certificate returned by the server in the system's trusted certificate list + */ + TDSSLPinningModeNone = 0, + + /** + The public key of the verification certificate + */ + TDSSLPinningModePublicKey = 1 << 0, + + /** + Verify all contents of the certificate + */ + TDSSLPinningModeCertificate = 1 << 1 +}; + +/** + Custom HTTPS Authentication +*/ +typedef NSURLSessionAuthChallengeDisposition (^TDURLSessionDidReceiveAuthenticationChallengeBlock)(NSURLSession *_Nullable session, NSURLAuthenticationChallenge *_Nullable challenge, NSURLCredential *_Nullable __autoreleasing *_Nullable credential); + + + +/** + Network Type Enum + + - TDNetworkTypeDefault : 3G、4G、WIFI + */ +typedef NS_OPTIONS(NSInteger, ThinkingAnalyticsNetworkType) { + + /** + 3G、4G、WIFI + */ + TDNetworkTypeDefault = 0, + + /** + only WIFI + */ + TDNetworkTypeOnlyWIFI = 1 << 0, + + /** + 2G、3G、4G、WIFI + */ + TDNetworkTypeALL = 1 << 1, +}; + +/** + Auto-Tracking Enum + + - ThinkingAnalyticsEventTypeNone : auto-tracking is not enabled by default + */ +typedef NS_OPTIONS(NSInteger, ThinkingAnalyticsAutoTrackEventType) { + + /** + auto-tracking is not enabled by default + */ + ThinkingAnalyticsEventTypeNone = 0, + + /* + Active Events + */ + ThinkingAnalyticsEventTypeAppStart = 1 << 0, + + /** + Inactive Events + */ + ThinkingAnalyticsEventTypeAppEnd = 1 << 1, + + /** + Clicked events + */ + ThinkingAnalyticsEventTypeAppClick = 1 << 2, + + /** + View Page Events + */ + ThinkingAnalyticsEventTypeAppViewScreen = 1 << 3, + + /** + Crash Events + */ + ThinkingAnalyticsEventTypeAppViewCrash = 1 << 4, + + /** + Installation Events + */ + ThinkingAnalyticsEventTypeAppInstall = 1 << 5, + /** + All Events + */ + ThinkingAnalyticsEventTypeAll = ThinkingAnalyticsEventTypeAppStart | ThinkingAnalyticsEventTypeAppEnd | ThinkingAnalyticsEventTypeAppClick | ThinkingAnalyticsEventTypeAppInstall | ThinkingAnalyticsEventTypeAppViewCrash | ThinkingAnalyticsEventTypeAppViewScreen + +}; + +typedef NS_OPTIONS(NSInteger, ThinkingNetworkType) { + ThinkingNetworkTypeNONE = 0, + ThinkingNetworkType2G = 1 << 0, + ThinkingNetworkType3G = 1 << 1, + ThinkingNetworkType4G = 1 << 2, + ThinkingNetworkTypeWIFI = 1 << 3, + ThinkingNetworkType5G = 1 << 4, + ThinkingNetworkTypeALL = 0xFF, +}; + + +typedef NS_OPTIONS(NSInteger, TAThirdPartyShareType) { + TAThirdPartyShareTypeNONE = 0, + TAThirdPartyShareTypeAPPSFLYER = 1 << 0, + TAThirdPartyShareTypeIRONSOURCE = 1 << 1, + TAThirdPartyShareTypeADJUST = 1 << 2, + TAThirdPartyShareTypeBRANCH = 1 << 3, + TAThirdPartyShareTypeTOPON = 1 << 4, + TAThirdPartyShareTypeTRACKING = 1 << 5, + TAThirdPartyShareTypeTRADPLUS = 1 << 6, + TAThirdPartyShareTypeAPPLOVIN = 1 << 7, + TAThirdPartyShareTypeKOCHAVA = 1 << 8, + TAThirdPartyShareTypeTALKINGDATA = 1 << 9, + TAThirdPartyShareTypeFIREBASE = 1 << 10, + + + TDThirdPartyShareTypeNONE = TAThirdPartyShareTypeNONE, + TDThirdPartyShareTypeAPPSFLYER = TAThirdPartyShareTypeAPPSFLYER, + TDThirdPartyShareTypeIRONSOURCE = TAThirdPartyShareTypeIRONSOURCE, + TDThirdPartyShareTypeADJUST = TAThirdPartyShareTypeADJUST, + TDThirdPartyShareTypeBRANCH = TAThirdPartyShareTypeBRANCH, + TDThirdPartyShareTypeTOPON = TAThirdPartyShareTypeTOPON, + TDThirdPartyShareTypeTRACKING = TAThirdPartyShareTypeTRACKING, + TDThirdPartyShareTypeTRADPLUS = TAThirdPartyShareTypeTRADPLUS, + +}; + +//MARK: - Data reporting status +typedef NS_ENUM(NSInteger, TATrackStatus) { + /// Suspend reporting + TATrackStatusPause, + /// Stop reporting and clear cache + TATrackStatusStop, + /// Suspend reporting and continue to persist data + TATrackStatusSaveOnly, + /// reset normal + TATrackStatusNormal +}; diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TDConstant.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TDConstant.h.meta new file mode 100644 index 0000000..6a776fd --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Core/TDConstant.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: eccd71fd6b2eb76479ac8578fc97a5dd +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo.meta new file mode 100644 index 0000000..c46f26f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ba5910307a8361644a2a0c733f942dd9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h new file mode 100644 index 0000000..54c6235 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h @@ -0,0 +1,35 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXTERN NSString *const VERSION; + +@interface TDDeviceInfo : NSObject + ++ (TDDeviceInfo *)sharedManager; + + +@property (nonatomic, copy) NSString *uniqueId; +@property (nonatomic, copy) NSString *deviceId; +@property (nonatomic, copy) NSString *appVersion; +@property (nonatomic, readonly) BOOL isFirstOpen; +@property (nonatomic, copy) NSString *libName; +@property (nonatomic, copy) NSString *libVersion; + ++ (NSString *)libVersion; ++ (NSString*)bundleId; + +- (void)td_updateData; +- (NSDictionary *)td_collectProperties; + ++ (NSDate *)td_getInstallTime; + +- (NSDictionary *)getAutomaticData; + ++ (NSString *)currentRadio; + ++ (NSTimeInterval)uptime; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h.meta new file mode 100644 index 0000000..d31613b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9a60930b943f31e4b9e33f854dd218c7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m new file mode 100644 index 0000000..e5c0c40 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m @@ -0,0 +1,481 @@ +#import "TDDeviceInfo.h" +#import +#import +#import + +#if TARGET_OS_IOS +#import +#elif TARGET_OS_OSX +#import +#endif + +#import "TDKeychainHelper.h" +#import "TDPublicConfig.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDFile.h" +#import "TDPresetProperties+TDDisProperties.h" +#import + +#define kTDDyldPropertyNames @[@"TDPerformance"] +#define kTDGetPropertySelName @"getPresetProperties" + +#define kDeviceClass @"XY2HU4AX3JI2JJW5MDhjm6wea2x6ymvm28ylmiyh7jkc8axy9mw3em8w" +#define kCurrentDevice @"0a223h444j555cm666uw77722rh985jrj323ae44y5xn5ll5tm5mD5wm6e8y9m0vm32y46i7a89x0yl32c44ml4ye5a3a5" +#define kIdfv @"hj23kik4343j545dk656ke43434hhn534536jj7676tx323423yyx547657iy7678yxf7654hhl32342im3424ww4235w546ew64645w76ll57rx67yF434hj323ao343aa546rk76l323Vx32y32y32x32e3m43w656m76nxy657k657lmd65y657yx5o323aa34kk45rk76k76lm87" +#define kUUIDStr @"323J342J342K342U657K675A87A87U879H0943AX908IJ214KWD54WW87SX98XY3425At769k93l2l548m7r32xyx76769im3234ww6576n8ax89g98k9l97m1w31242" + +#if TARGET_OS_IOS +static CTTelephonyNetworkInfo *__td_TelephonyNetworkInfo; +#endif + +@interface TDDeviceInfo () + +@property (nonatomic, readwrite) BOOL isFirstOpen; +@property (atomic, strong) NSDictionary *automaticData; + +@end + +@implementation TDDeviceInfo + ++ (void)load { +#if TARGET_OS_IOS + __td_TelephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init]; +#endif +} + + ++ (TDDeviceInfo *)sharedManager { + static dispatch_once_t onceToken; + static TDDeviceInfo *manager; + dispatch_once(&onceToken, ^{ + manager = [[TDDeviceInfo alloc] init]; + }); + return manager; +} + +- (instancetype)init { + self = [super init]; + if (self) { + self.libName = @"iOS"; + self.libVersion = TDPublicConfig.version; + + NSDictionary *deviceInfo = [self getDeviceUniqueId]; + _uniqueId = [deviceInfo objectForKey:@"uniqueId"]; + _deviceId = [deviceInfo objectForKey:@"deviceId"]; + _appVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"]; + + self.automaticData = [self td_collectProperties]; + } + return self; +} + ++ (NSString *)libVersion { + return [self sharedManager].libVersion; +} + +- (void)td_updateData { + self.automaticData = [self td_collectProperties]; +} + +-(NSDictionary *)getAutomaticData { + NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:self.automaticData]; + [dic addEntriesFromDictionary:[TDDeviceInfo getAPMParams]]; + self.automaticData = dic; + return self.automaticData; +} + +- (NSDictionary *)td_collectProperties { + NSMutableDictionary *p = [NSMutableDictionary dictionary]; + + if (![TDPresetProperties disableDeviceId]) { + [p setValue:_deviceId forKey:@"#device_id"]; + } + +#if TARGET_OS_IOS + if (![TDPresetProperties disableCarrier]) { + CTCarrier *carrier = nil; + NSString *carrierName = @""; + #ifdef __IPHONE_12_0 + if (@available(iOS 12.1, *)) { + NSArray *carrierKeysArray = [__td_TelephonyNetworkInfo.serviceSubscriberCellularProviders.allKeys sortedArrayUsingSelector:@selector(compare:)]; + carrier = __td_TelephonyNetworkInfo.serviceSubscriberCellularProviders[carrierKeysArray.firstObject]; + if (!carrier.mobileNetworkCode) { + carrier = __td_TelephonyNetworkInfo.serviceSubscriberCellularProviders[carrierKeysArray.lastObject]; + } + } + #endif + + if (!carrier) { + carrier = [__td_TelephonyNetworkInfo subscriberCellularProvider]; + } + + // System characteristics, when the SIM is not installed, the carrierName also has a value, here additionally add the judgment of whether MCC and MNC have values + // MCC, MNC, and isoCountryCode are nil when no SIM card is installed and not within the cellular service range + if (carrier.carrierName && + carrier.carrierName.length > 0 && + carrier.mobileNetworkCode && + carrier.mobileNetworkCode.length > 0) { + carrierName = carrier.carrierName; + } + [p setValue:carrierName forKey:@"#carrier"]; + } +#endif + + if (![TDPresetProperties disableLibVersion]) { + [p setValue:self.libVersion forKey:@"#lib_version"]; + } + if (![TDPresetProperties disableManufacturer]) { + [p setValue:@"Apple" forKey:@"#manufacturer"]; + } + + +#if TARGET_OS_IOS + if (![TDPresetProperties disableDeviceModel]) { + [p setValue:[self td_iphoneType] forKey:@"#device_model"]; + } + + if (![TDPresetProperties disableLib]) { + [p setValue:self.libName forKey:@"#lib"]; + } + + if (![TDPresetProperties disableOs]) { + [p setValue:@"iOS" forKey:@"#os"]; + } + if (![TDPresetProperties disableOsVersion]) { + UIDevice *device = [UIDevice currentDevice]; + [p setValue:[device systemVersion] forKey:@"#os_version"]; + } + if (![TDPresetProperties disableScreenWidth]) { + CGSize size = [UIScreen mainScreen].bounds.size; + [p setValue:@((NSInteger)size.width) forKey:@"#screen_width"]; + } + if (![TDPresetProperties disableScreenHeight]) { + CGSize size = [UIScreen mainScreen].bounds.size; + [p setValue:@((NSInteger)size.height) forKey:@"#screen_height"]; + } + + if (![TDPresetProperties disableDeviceType]) { + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { + [p setValue:@"iPad" forKey:@"#device_type"]; + } else { + [p setValue:@"iPhone" forKey:@"#device_type"]; + } + } +#endif + +#if TARGET_OS_OSX + + if (![TDPresetProperties disableLib]) { + [p setValue:@"Mac OS" forKey:@"#lib"]; + } + + if (![TDPresetProperties disableOs]) { + [p setValue:@"OSX" forKey:@"#os"]; + } + if (![TDPresetProperties disableOsVersion]) { + NSDictionary *sv = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; + NSString *versionString = [sv objectForKey:@"ProductVersion"]; + [p setValue:versionString forKey:@"#os_version"]; + } +#endif + if (![TDPresetProperties disableSystemLanguage]) { + NSString *preferredLanguages = [[NSLocale preferredLanguages] firstObject]; + if (preferredLanguages && preferredLanguages.length > 0) { + p[@"#system_language"] = [[preferredLanguages componentsSeparatedByString:@"-"] firstObject];; + } + } + [p addEntriesFromDictionary:[TDDeviceInfo getAPMParams]]; + + return [p copy]; +} + ++ (NSString*)bundleId +{ + return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; +} + +- (NSString *)td_iphoneType { + struct utsname systemInfo; + uname(&systemInfo); + NSString *platform = [NSString stringWithCString:systemInfo.machine encoding:NSASCIIStringEncoding]; + if ([platform isEqualToString:@"iPhone1,1"]) return @"iPhone 2G"; + if ([platform isEqualToString:@"iPhone1,2"]) return @"iPhone 3G"; + if ([platform isEqualToString:@"iPhone2,1"]) return @"iPhone 3GS"; + if ([platform isEqualToString:@"iPhone3,1"]) return @"iPhone 4"; + if ([platform isEqualToString:@"iPhone3,2"]) return @"iPhone 4"; + if ([platform isEqualToString:@"iPhone3,3"]) return @"iPhone 4"; + if ([platform isEqualToString:@"iPhone4,1"]) return @"iPhone 4S"; + if ([platform isEqualToString:@"iPhone5,1"]) return @"iPhone 5"; + if ([platform isEqualToString:@"iPhone5,2"]) return @"iPhone 5"; + if ([platform isEqualToString:@"iPhone5,3"]) return @"iPhone 5c"; + if ([platform isEqualToString:@"iPhone5,4"]) return @"iPhone 5c"; + if ([platform isEqualToString:@"iPhone6,1"]) return @"iPhone 5s"; + if ([platform isEqualToString:@"iPhone6,2"]) return @"iPhone 5s"; + if ([platform isEqualToString:@"iPhone7,1"]) return @"iPhone 6 Plus"; + if ([platform isEqualToString:@"iPhone7,2"]) return @"iPhone 6"; + if ([platform isEqualToString:@"iPhone8,1"]) return @"iPhone 6s"; + if ([platform isEqualToString:@"iPhone8,2"]) return @"iPhone 6s Plus"; + if ([platform isEqualToString:@"iPhone8,4"]) return @"iPhone SE"; + if ([platform isEqualToString:@"iPhone9,1"]) return @"iPhone 7"; + if ([platform isEqualToString:@"iPhone9,2"]) return @"iPhone 7 Plus"; + if ([platform isEqualToString:@"iPod1,1"]) return @"iPod Touch 1G"; + if ([platform isEqualToString:@"iPod2,1"]) return @"iPod Touch 2G"; + if ([platform isEqualToString:@"iPod3,1"]) return @"iPod Touch 3G"; + if ([platform isEqualToString:@"iPod4,1"]) return @"iPod Touch 4G"; + if ([platform isEqualToString:@"iPod5,1"]) return @"iPod Touch 5G"; + if ([platform isEqualToString:@"iPad1,1"]) return @"iPad 1G"; + if ([platform isEqualToString:@"iPad2,1"]) return @"iPad 2"; + if ([platform isEqualToString:@"iPad2,2"]) return @"iPad 2"; + if ([platform isEqualToString:@"iPad2,3"]) return @"iPad 2"; + if ([platform isEqualToString:@"iPad2,4"]) return @"iPad 2"; + if ([platform isEqualToString:@"iPad2,5"]) return @"iPad Mini 1G"; + if ([platform isEqualToString:@"iPad2,6"]) return @"iPad Mini 1G"; + if ([platform isEqualToString:@"iPad2,7"]) return @"iPad Mini 1G"; + if ([platform isEqualToString:@"iPad3,1"]) return @"iPad 3"; + if ([platform isEqualToString:@"iPad3,2"]) return @"iPad 3"; + if ([platform isEqualToString:@"iPad3,3"]) return @"iPad 3"; + if ([platform isEqualToString:@"iPad3,4"]) return @"iPad 4"; + if ([platform isEqualToString:@"iPad3,5"]) return @"iPad 4"; + if ([platform isEqualToString:@"iPad3,6"]) return @"iPad 4"; + if ([platform isEqualToString:@"iPad4,1"]) return @"iPad Air"; + if ([platform isEqualToString:@"iPad4,2"]) return @"iPad Air"; + if ([platform isEqualToString:@"iPad4,3"]) return @"iPad Air"; + if ([platform isEqualToString:@"iPad4,4"]) return @"iPad Mini 2G"; + if ([platform isEqualToString:@"iPad4,5"]) return @"iPad Mini 2G"; + if ([platform isEqualToString:@"iPad4,6"]) return @"iPad Mini 2G"; + if ([platform isEqualToString:@"i386"]) return @"iPhone Simulator"; + if ([platform isEqualToString:@"x86_64"]) return @"iPhone Simulator"; + return platform; +} + + + +- (NSDictionary *)getDeviceUniqueId { + + NSString *defaultDistinctId = [self getIdentifier]; + NSString *deviceId; + NSString *uniqueId; + + TDKeychainHelper *wrapper = [[TDKeychainHelper alloc] init]; + NSString *deviceIdKeychain = [wrapper readDeviceId]; + NSString *installTimesKeychain = [wrapper readInstallTimes]; + + BOOL isExistFirstRecord = [[[NSUserDefaults standardUserDefaults] objectForKey:@"thinking_isfirst"] boolValue]; + if (!isExistFirstRecord) { + _isFirstOpen = YES; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"thinking_isfirst"]; + [[NSUserDefaults standardUserDefaults] synchronize]; + } else { + _isFirstOpen = NO; + } + + if (deviceIdKeychain.length == 0 || installTimesKeychain.length == 0) { + [wrapper readOldKeychain]; + deviceIdKeychain = [wrapper getDeviceIdOld]; + installTimesKeychain = [wrapper getInstallTimesOld]; + } + + TDFile *file = [[TDFile alloc] initWithAppid:[ThinkingAnalyticsSDK sharedInstance].appid]; + if (deviceIdKeychain.length == 0 || installTimesKeychain.length == 0) { + deviceIdKeychain = [file unarchiveDeviceId]; + installTimesKeychain = [file unarchiveInstallTimes]; + } + + if (deviceIdKeychain.length == 0 || installTimesKeychain.length == 0) { + deviceId = defaultDistinctId; + installTimesKeychain = @"1"; + } else { + if (!isExistFirstRecord) { + int setup_int = [installTimesKeychain intValue]; + setup_int++; + + installTimesKeychain = [NSString stringWithFormat:@"%d",setup_int]; + } + + deviceId = deviceIdKeychain; + } + + if ([installTimesKeychain isEqualToString:@"1"]) { + uniqueId = deviceId; + } else { + uniqueId = [NSString stringWithFormat:@"%@_%@",deviceId,installTimesKeychain]; + } + + [wrapper saveDeviceId:deviceId]; + [wrapper saveInstallTimes:installTimesKeychain]; + [file archiveDeviceId:deviceId]; + [file archiveInstallTimes:installTimesKeychain]; + return @{@"uniqueId":uniqueId, @"deviceId":deviceId}; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + +- (NSString *)getIdentifier { + NSString *anonymityId = NULL; + + Class deviceCls = NSClassFromString([self dealStringWithRegExp:kDeviceClass]); + if (deviceCls) { + SEL currentDve = NSSelectorFromString([self dealStringWithRegExp:kCurrentDevice]); + SEL idfvor = NSSelectorFromString([self dealStringWithRegExp:kIdfv]); + SEL uuidStr = NSSelectorFromString([self dealStringWithRegExp:kUUIDStr]); + + if ([deviceCls respondsToSelector:currentDve]) { + id cls1 = [deviceCls performSelector:currentDve]; + if (cls1 && [cls1 respondsToSelector:idfvor]) { + id cls2 = [cls1 performSelector:idfvor]; + if (cls2 && [cls2 respondsToSelector:uuidStr]) { + id tempAnonymityId = [cls2 performSelector:uuidStr]; + if ([tempAnonymityId isKindOfClass:[NSString class]]) { + anonymityId = tempAnonymityId; + } + } + } + } + } + + if (!anonymityId) { + anonymityId = [[NSUUID UUID] UUIDString]; + } + + return anonymityId; +} + +#pragma clang diagnostic pop + + +- (NSString *)dealStringWithRegExp:(NSString *)string { + NSRegularExpression *regExp = [[NSRegularExpression alloc]initWithPattern:@"[0-9AXYHJKLMW]" + options:NSRegularExpressionCaseInsensitive + error:nil]; + return [regExp stringByReplacingMatchesInString:string + options:NSMatchingReportProgress + range:NSMakeRange(0, string.length) + withTemplate:@""]; +} + +#if TARGET_OS_IOS + ++ (NSString *)currentRadio { + NSString *networkType = @"NULL"; + + if (!__td_TelephonyNetworkInfo) { + return networkType; + } + + @try { + NSString *currentRadio = nil; + +#ifdef __IPHONE_12_0 + if (@available(iOS 12.0, *)) { + NSDictionary *serviceCurrentRadio = [__td_TelephonyNetworkInfo serviceCurrentRadioAccessTechnology]; + if ([serviceCurrentRadio isKindOfClass:[NSDictionary class]] && serviceCurrentRadio.allValues.count>0) { + currentRadio = serviceCurrentRadio.allValues[0]; + } + } +#endif + if (currentRadio == nil && [__td_TelephonyNetworkInfo.currentRadioAccessTechnology isKindOfClass:[NSString class]]) { + currentRadio = __td_TelephonyNetworkInfo.currentRadioAccessTechnology; + } + + if ([currentRadio isEqualToString:CTRadioAccessTechnologyLTE]) { + networkType = @"4G"; + } else if ([currentRadio isEqualToString:CTRadioAccessTechnologyeHRPD] || + [currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB] || + [currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] || + [currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] || + [currentRadio isEqualToString:CTRadioAccessTechnologyCDMA1x] || + [currentRadio isEqualToString:CTRadioAccessTechnologyHSUPA] || + [currentRadio isEqualToString:CTRadioAccessTechnologyHSDPA] || + [currentRadio isEqualToString:CTRadioAccessTechnologyWCDMA]) { + networkType = @"3G"; + } else if ([currentRadio isEqualToString:CTRadioAccessTechnologyEdge] || + [currentRadio isEqualToString:CTRadioAccessTechnologyGPRS]) { + networkType = @"2G"; + } +#ifdef __IPHONE_14_1 + else if (@available(iOS 14.1, *)) { + if ([currentRadio isKindOfClass:[NSString class]]) { + if([currentRadio isEqualToString:CTRadioAccessTechnologyNRNSA] || + [currentRadio isEqualToString:CTRadioAccessTechnologyNR]) { + networkType = @"5G"; + } + } + } +#endif + } @catch (NSException *exception) { + TDLogError(@"%@: %@", self, exception); + } + + return networkType; +} + +#elif TARGET_OS_OSX ++ (NSString *)currentRadio { + return @"WIFI"; +} +#endif + ++ (NSDate *)td_getInstallTime { + + NSURL* urlToDocumentsFolder = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; + __autoreleasing NSError *error; + NSDate *installDate = [[[NSFileManager defaultManager] attributesOfItemAtPath:urlToDocumentsFolder.path error:&error] objectForKey:NSFileCreationDate]; + if (!error) { + return installDate; + } + return [NSDate date]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + ++ (NSDictionary *)getAPMParams { + NSMutableDictionary *p = [NSMutableDictionary dictionary]; + for (NSString *clsName in kTDDyldPropertyNames) { + Class cls = NSClassFromString(clsName); + SEL sel = NSSelectorFromString(kTDGetPropertySelName); + if (cls && sel && [cls respondsToSelector:sel]) { + NSDictionary *result = [cls performSelector:sel]; +// NSDictionary *result = [NSObject performSelector:sel onTarget:cls withArguments:@[]]; + if ([result isKindOfClass:[NSDictionary class]] && result.allKeys.count > 0) { + [p addEntriesFromDictionary:result]; + } + + } + } + return p; +} + +#pragma clang diagnostic pop + ++ (BOOL)isIPad { +#if TARGET_OS_IOS + return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad); +#elif TARGET_OS_OSX + return NO; +#endif +} + ++ (NSTimeInterval)uptime +{ + struct timeval boottime; + int mib[2] = {CTL_KERN, KERN_BOOTTIME}; + size_t size = sizeof(boottime); + + struct timeval now; + struct timezone tz; + gettimeofday(&now, &tz); + + double uptime = -1; + + if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) + { + uptime = now.tv_sec - boottime.tv_sec; + uptime += (double)(now.tv_usec - boottime.tv_usec) / 1000000.0; + } + return uptime; +} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m.meta new file mode 100644 index 0000000..437f667 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 01250d16ad3f65c41908ee8941191529 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.h new file mode 100644 index 0000000..fc4a5ca --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.h @@ -0,0 +1,20 @@ +// +// TDFPSMonitor.h +// SSAPMSDK +// +// Created by wwango on 2021/9/7. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDFPSMonitor : NSObject + +@property (nonatomic, assign, getter=isEnable) BOOL enable; + +- (NSNumber *)getPFS; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.h.meta new file mode 100644 index 0000000..8e2e5cb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4fc3ed786525c3f4597a19ade7f43499 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.m new file mode 100644 index 0000000..7dc7e66 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.m @@ -0,0 +1,76 @@ +// +// TDFPSMonitor.m +// SSAPMSDK +// +// Created by wwango on 2021/9/7. +// + +#import "TDFPSMonitor.h" +#import +#import "TDWeakProxy.h" + +@interface TDFPSMonitor () { + CADisplayLink *_link; + NSUInteger _count; + NSTimeInterval _lastTime; + int _thinkingdata_fps; +} + +@end + +@implementation TDFPSMonitor + +- (void)setEnable:(BOOL)enable { + _enable = enable; + if (_enable) { + [self startDisplay]; + } else { + [self stopDisplay]; + } +} + +- (NSNumber *)getPFS { + return [NSNumber numberWithInt:[NSString stringWithFormat:@"%d", _thinkingdata_fps].intValue]; +} + +- (void)dealloc { + if (_link) { + [_link invalidate]; + } +} + +- (void)startDisplay { + + if (_link) return; + + _thinkingdata_fps = 60; + _link = [CADisplayLink displayLinkWithTarget:[TDWeakProxy proxyWithTarget:self] selector:@selector(tick:)]; +// _link.preferredFrameRateRange = CAFrameRateRangeMake(60, 120, 120); + [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; +} + +- (void)stopDisplay { + if (_link) { + [_link invalidate]; + _link= nil; + } +} + +- (void)tick:(CADisplayLink *)link { + if (_lastTime == 0) { + _lastTime = link.timestamp; + return; + } + + _count++; + NSTimeInterval delta = link.timestamp - _lastTime; + if (delta < 1.0) return; + _lastTime = link.timestamp; + _thinkingdata_fps = _count / delta; + _count = 0; + +// NSLog(@"@@@@@FPS:%i", _thinkingdata_fps); +} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.m.meta new file mode 100644 index 0000000..bf3ab5c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDFPSMonitor.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: aa4fa74cbdebf634ca82ffea792698ea +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.h new file mode 100644 index 0000000..2fa4e1b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.h @@ -0,0 +1,17 @@ +// +// TDPerformance.h +// ThinkingSDK +// +// Created by wwango on 2021/12/23. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDPerformance : NSObject + +@end + + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.h.meta new file mode 100644 index 0000000..895a3c1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fd598319e2151f847a2b2a281e15e0e1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.m new file mode 100644 index 0000000..e1ff035 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.m @@ -0,0 +1,176 @@ +// +// TDPerformance.m +// ThinkingSDK +// +// Created by wwango on 2021/12/23. +// + +#import "TDPerformance.h" +#import "TDFPSMonitor.h" +#include +#include +#import +#include +#import +#import "TDPresetProperties+TDDisProperties.h" + +typedef TDPresetProperties TDAPMPresetProperty; + +static const NSString *kTDPerformanceRAM = @"#ram"; +static const NSString *kTDPerformanceDISK = @"#disk"; +static const NSString *kTDPerformanceSIM = @"#simulator"; +static const NSString *kTDPerformanceFPS = @"#fps"; + +#define TD_MAIM_INFO_PLIST_DISPRESTPRO_KEY @"TDDisPresetProperties" + +#define TD_PM_UNIT_KB 1024.0 +#define TD_PM_UNIT_MB (1024.0 * TD_PM_UNIT_KB) +#define TD_PM_UNIT_GB (1024.0 * TD_PM_UNIT_MB) + +TDFPSMonitor *fpsMonitor; + +@implementation TDPerformance + ++ (NSDictionary *)getPresetProperties { + + NSMutableDictionary *dic = [NSMutableDictionary dictionary]; + + if (![TDAPMPresetProperty disableRAM]) { + NSString *ram = [NSString stringWithFormat:@"%.1f/%.1f", + [TDPerformance td_pm_func_getFreeMemory]*1.0/TD_PM_UNIT_GB, + [TDPerformance td_pm_func_getRamSize]*1.0/TD_PM_UNIT_GB]; + if (ram && ram.length) { + [dic setObject:ram forKey:kTDPerformanceRAM]; + } + } + + if (![TDAPMPresetProperty disableDisk]) { + NSString *disk = [NSString stringWithFormat:@"%.1f/%.1f", + [TDPerformance td_get_disk_free_size]*1.0/TD_PM_UNIT_GB, + [TDPerformance td_get_storage_size]*1.0/TD_PM_UNIT_GB]; + if (disk && disk.length) { + [dic setObject:disk forKey:kTDPerformanceDISK]; + } + } + + if (![TDAPMPresetProperty disableSimulator]) { + +#ifdef TARGET_OS_IPHONE + #if TARGET_IPHONE_SIMULATOR + [dic setObject:@(YES) forKey:kTDPerformanceSIM]; + #elif TARGET_OS_SIMULATOR + [dic setObject:@(YES) forKey:kTDPerformanceSIM]; + #else + [dic setObject:@(NO) forKey:kTDPerformanceSIM]; + #endif +#else + [dic setObject:@(YES) forKey:kTDPerformanceSIM]; +#endif + } + + if (![TDAPMPresetProperty disableFPS]) { + if (!fpsMonitor) { + fpsMonitor = [[TDFPSMonitor alloc] init]; + [fpsMonitor setEnable:YES]; + [dic setObject:[fpsMonitor getPFS] forKey:kTDPerformanceFPS]; + } else { + [dic setObject:[fpsMonitor getPFS] forKey:kTDPerformanceFPS]; + } + } + return dic; +} + +#pragma mark - memory + ++ (int64_t)td_pm_func_getFreeMemory { + size_t length = 0; + int mib[6] = {0}; + + int pagesize = 0; + mib[0] = CTL_HW; + mib[1] = HW_PAGESIZE; + length = sizeof(pagesize); + if (sysctl(mib, 2, &pagesize, &length, NULL, 0) < 0){ + return -1; + } + mach_msg_type_number_t count = HOST_VM_INFO_COUNT; + vm_statistics_data_t vmstat; + if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count) != KERN_SUCCESS){ + return -1; + } + + int64_t freeMem = vmstat.free_count * pagesize; + int64_t inactiveMem = vmstat.inactive_count * pagesize; + return freeMem + inactiveMem; +} + ++ (int64_t)td_pm_func_getRamSize{ + int mib[2]; + size_t length = 0; + + mib[0] = CTL_HW; + mib[1] = HW_MEMSIZE; + long ram; + length = sizeof(ram); + if (sysctl(mib, 2, &ram, &length, NULL, 0) < 0) { + return -1; + } + return ram; +} + +#pragma mark - disk + ++ (NSDictionary *)td_pm_getFileAttributeDic { + NSError *error; + NSDictionary *directory = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error]; + if (error) { + return nil; + } + return directory; +} + ++ (long long)td_get_disk_free_size { + NSDictionary *directory = [self td_pm_getFileAttributeDic]; + if (directory) { + return [[directory objectForKey:NSFileSystemFreeSize] unsignedLongLongValue]; + } + return -1; +} + ++ (long long)td_get_storage_size { + NSDictionary *directory = [self td_pm_getFileAttributeDic]; + return directory ? ((NSNumber *)[directory objectForKey:NSFileSystemSize]).unsignedLongLongValue:-1; +} + +@end + + +@implementation TDPerformance (PresetProperty) + ++ (NSArray*)disPerformancePresetProperties { + static NSArray *arr; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + arr = (NSArray *)[[[NSBundle mainBundle] infoDictionary] objectForKey:TD_MAIM_INFO_PLIST_DISPRESTPRO_KEY]; + }); + return arr; +} + ++ (BOOL)needFPS { + return ![[self disPerformancePresetProperties] containsObject:kTDPerformanceFPS]; +} + ++ (BOOL)needRAM { + return ![[self disPerformancePresetProperties] containsObject:kTDPerformanceRAM]; +} + ++ (BOOL)needDisk { + return ![[self disPerformancePresetProperties] containsObject:kTDPerformanceDISK]; +} + ++ (BOOL)needSimulator { + return ![[self disPerformancePresetProperties] containsObject:kTDPerformanceSIM]; +} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.m.meta new file mode 100644 index 0000000..470b435 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDPerformance.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 84a08b6191b46604faa7ded801773034 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt.meta new file mode 100644 index 0000000..470a3de --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a3ea848284a4d694fb454fc1a17e279e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h new file mode 100644 index 0000000..f356dd4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h @@ -0,0 +1,11 @@ +// +// TDEncrypt.h +// ThinkingSDK +// +// Created by wwango on 2022/1/27. +// + +#import "TDEncryptAlgorithm.h" +#import "TDEncryptProtocol.h" +#import "TDEncryptManager.h" +#import "TDSecretKey.h" diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h.meta new file mode 100644 index 0000000..6738969 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 007c777ed2dec0f489043d899988eac4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h new file mode 100644 index 0000000..ceea259 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h @@ -0,0 +1,20 @@ +// +// TDEncryptAlgorithm.h +// ThinkingSDK +// +// Created by wwango on 2022/1/27. + + +NS_ASSUME_NONNULL_BEGIN + +@protocol TDEncryptAlgorithm + + +- (nullable NSString *)encryptData:(NSData *)data; + + +- (NSString *)algorithm; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h.meta new file mode 100644 index 0000000..2f83858 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5ea121910391cad4fb7eee7d1448f867 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h new file mode 100644 index 0000000..e659808 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h @@ -0,0 +1,32 @@ +// +// TDEncryptManager.h +// ThinkingSDK +// +// Created by wwango on 2022/1/21. + + +#import + +#import "TDConfig.h" + +@class TDEventRecord; + +NS_ASSUME_NONNULL_BEGIN + +@interface TDEncryptManager : NSObject + + +@property(nonatomic, assign, getter=isValid) BOOL valid; + + +- (instancetype)initWithConfig:(TDConfig *)config; + + +- (void)handleEncryptWithConfig:(NSDictionary *)encryptConfig; + + +- (NSDictionary *)encryptJSONObject:(NSDictionary *)obj; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h.meta new file mode 100644 index 0000000..a923c15 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 75696c9758a99d2438eb226fdc4b52d7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m new file mode 100644 index 0000000..aaa82c7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m @@ -0,0 +1,217 @@ +// +// TDEncryptManager.m +// ThinkingSDK +// +// Created by wwango on 2022/1/21. +// + +#import "TDEncryptManager.h" +#import "TDEncryptProtocol.h" +#import "TDSecretKey.h" +#import "TDRSAEncryptorPlugin.h" +#import "NSData+TDGzip.h" +#import "TDJSONUtil.h" +#import "TDEventRecord.h" +#import "TDLogging.h" + + +@interface TDEncryptManager () + +@property (nonatomic, strong) TDConfig *config; +@property (nonatomic, strong) id encryptor; +@property (nonatomic, copy) NSArray> *encryptors; +@property (nonatomic, copy) NSString *encryptedSymmetricKey; +@property (nonatomic, strong) TDSecretKey *secretKey; + +@end + +@implementation TDEncryptManager + +- (instancetype)initWithConfig:(TDConfig *)config +{ + self = [super init]; + if (self) { + [self updateConfig:config]; + } + return self; +} + +- (void)updateConfig:(TDConfig *)config { + self.config = config; + + + NSMutableArray *encryptors = [NSMutableArray array]; + [encryptors addObject:[TDRSAEncryptorPlugin new]]; + self.encryptors = encryptors; + + + [self updateEncryptor:[self loadCurrentSecretKey]]; +} + +- (void)handleEncryptWithConfig:(NSDictionary *)encryptConfig { + + if (!encryptConfig || ![encryptConfig isKindOfClass:[NSDictionary class]]) { + return; + } + + if (![encryptConfig objectForKey:@"version"]) { + return; + } + + NSInteger version = [[encryptConfig objectForKey:@"version"] integerValue]; + TDSecretKey *secretKey = [[TDSecretKey alloc] initWithVersion:version + publicKey:encryptConfig[@"key"] + asymmetricEncryption:encryptConfig[@"asymmetric"] + symmetricEncryption:encryptConfig[@"symmetric"]]; + + + if (![secretKey isValid]) { + return; + } + + + if (![self encryptorWithSecretKey:secretKey]) { + return; + } + + + [self updateEncryptor:secretKey]; +} + +- (void)updateEncryptor:(TDSecretKey *)obj { + @try { + + TDSecretKey *secretKey = obj; + if (!secretKey.publicKey.length) { + return; + } + + if ([self needUpdateSecretKey:self.secretKey newSecretKey:secretKey]) { + return; + } + + id encryptor = [self filterEncrptor:secretKey]; + if (!encryptor) { + return; + } + + NSString *encryptedSymmetricKey = [encryptor encryptSymmetricKeyWithPublicKey:secretKey.publicKey]; + + if (encryptedSymmetricKey.length) { + + self.secretKey = secretKey; + + self.encryptor = encryptor; + + self.encryptedSymmetricKey = encryptedSymmetricKey; + + TDLogDebug(@"\n****************secretKey****************\n public key: %@ \n encrypted symmetric key: %@\n****************secretKey****************", secretKey.publicKey, encryptedSymmetricKey); + } + } @catch (NSException *exception) { + TDLogError(@"%@ error: %@", self, exception); + } +} + +- (TDSecretKey *)loadCurrentSecretKey { + TDSecretKey *secretKey = self.config.secretKey; + return secretKey; +} + +- (BOOL)needUpdateSecretKey:(TDSecretKey *)oldSecretKey newSecretKey:(TDSecretKey *)newSecretKey { + if (oldSecretKey.version != newSecretKey.version) { + return NO; + } + if (![oldSecretKey.publicKey isEqualToString:newSecretKey.publicKey]) { + return NO; + } + if (![oldSecretKey.symmetricEncryption isEqualToString:newSecretKey.symmetricEncryption]) { + return NO; + } + if (![oldSecretKey.asymmetricEncryption isEqualToString:newSecretKey.asymmetricEncryption]) { + return NO; + } + return YES; +} + +- (id)filterEncrptor:(TDSecretKey *)secretKey { + id encryptor = [self encryptorWithSecretKey:secretKey]; + if (!encryptor) { + NSString *format = @"\n You have used the [%@] key, but the corresponding encryption plugin has not been registered. \n"; + NSString *type = [NSString stringWithFormat:@"%@+%@", secretKey.asymmetricEncryption, secretKey.symmetricEncryption]; + NSString *message = [NSString stringWithFormat:format, type]; + NSAssert(NO, message); + return nil; + } + return encryptor; +} + +- (id)encryptorWithSecretKey:(TDSecretKey *)secretKey { + if (!secretKey) { + return nil; + } + __block id encryptor; + [self.encryptors enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + BOOL isSameAsymmetricType = [[obj asymmetricEncryptType] isEqualToString:secretKey.asymmetricEncryption]; + BOOL isSameSymmetricType = [[obj symmetricEncryptType] isEqualToString:secretKey.symmetricEncryption]; + if (isSameAsymmetricType && isSameSymmetricType) { + encryptor = obj; + *stop = YES; + } + }]; + return encryptor; +} + +- (NSDictionary *)encryptJSONObject:(NSDictionary *)obj { + @try { + if (!obj) { + TDLogDebug(@"Enable encryption but the input obj is invalid!"); + return nil; + } + + if (!self.encryptor) { + TDLogDebug(@"Enable encryption but the secret key is invalid!"); + return nil; + } + + if (![self encryptSymmetricKey]) { + TDLogDebug(@"Enable encryption but encrypt symmetric key is failed!"); + return nil; + } + + + NSData *jsonData = [TDJSONUtil JSONSerializeForObject:obj]; + + + NSString *encryptedString = [self.encryptor encryptEvent:jsonData]; + if (!encryptedString) { + TDLogDebug(@"Enable encryption but encrypted input obj is invalid!"); + return nil; + } + + + NSMutableDictionary *secretObj = [NSMutableDictionary dictionary]; + secretObj[@"pkv"] = @(self.secretKey.version); + secretObj[@"ekey"] = self.encryptedSymmetricKey; + secretObj[@"payload"] = encryptedString; + return [NSDictionary dictionaryWithDictionary:secretObj]; + } @catch (NSException *exception) { + TDLogDebug(@"%@ error: %@", self, exception); + return nil; + } +} + + +- (BOOL)encryptSymmetricKey { + if (self.encryptedSymmetricKey) { + return YES; + } + NSString *publicKey = self.secretKey.publicKey; + self.encryptedSymmetricKey = [self.encryptor encryptSymmetricKeyWithPublicKey:publicKey]; + return self.encryptedSymmetricKey != nil; +} + +- (BOOL)isValid { + return _encryptor ? YES:NO; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m.meta new file mode 100644 index 0000000..6dc0eff --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e1806b2e73b6b0249adf18876a642122 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h new file mode 100644 index 0000000..fad0aa6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h @@ -0,0 +1,26 @@ +// +// TDEncryptProtocol.h +// ThinkingSDK +// +// Created by wwango on 2022/1/27. + + +NS_ASSUME_NONNULL_BEGIN + +@protocol TDEncryptProtocol + + +- (NSString *)symmetricEncryptType; + + +- (NSString *)asymmetricEncryptType; + + +- (NSString *)encryptEvent:(NSData *)event; + + +- (NSString *)encryptSymmetricKeyWithPublicKey:(NSString *)publicKey; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h.meta new file mode 100644 index 0000000..7a4e9bc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 44292de05cdcf0d45956c8e4905d96c0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h new file mode 100644 index 0000000..f5fe539 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h @@ -0,0 +1,37 @@ +// +// TDSecretKey.h +// ThinkingSDK +// +// Created by wwango on 2022/1/21. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDSecretKey : NSObject + +/// Initialize key information +- (instancetype)initWithVersion:(NSUInteger)version publicKey:(NSString *)publicKey; + +/// Initialize key information +/// @param version key version number +/// @param publicKey public key +/// @param asymmetricEncryption asymmetric encryption type +/// @param symmetricEncryption Symmetric encryption type +- (instancetype)initWithVersion:(NSUInteger)version + publicKey:(NSString *)publicKey + asymmetricEncryption:(NSString *)asymmetricEncryption + symmetricEncryption:(NSString *)symmetricEncryption; + +@property (nonatomic, assign, readonly) NSUInteger version; +@property (nonatomic, copy, readonly) NSString *publicKey; +@property (nonatomic, copy, readonly) NSString *symmetricEncryption; +@property (nonatomic, copy, readonly) NSString *asymmetricEncryption; + +/// Whether the key information is available +@property (nonatomic, assign, readonly) BOOL isValid; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h.meta new file mode 100644 index 0000000..8a04252 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 489aec22b994055499b3134076f4a298 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m new file mode 100644 index 0000000..cf97361 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m @@ -0,0 +1,80 @@ +// +// TDSecretKey.m +// ThinkingSDK +// +// Created by wwango on 2022/1/21. +// + +#import "TDSecretKey.h" + +@interface TDSecretKey () + +@property (nonatomic, assign) NSUInteger version; +@property (nonatomic, copy) NSString *publicKey; +@property (nonatomic, copy) NSString *symmetricEncryption; +@property (nonatomic, copy) NSString *asymmetricEncryption; + +@end + +@implementation TDSecretKey + + +- (instancetype)initWithVersion:(NSUInteger)version + publicKey:(NSString *)publicKey { + + return [[TDSecretKey alloc] initWithVersion:version + publicKey:publicKey + asymmetricEncryption:@"RSA" + symmetricEncryption:@"AES"]; +} + +- (instancetype)initWithVersion:(NSUInteger)version + publicKey:(NSString *)publicKey + asymmetricEncryption:(NSString *)asymmetricEncryption + symmetricEncryption:(NSString *)symmetricEncryption { + self = [super init]; + if (self) { + self.version = version; + self.publicKey = publicKey; + self.asymmetricEncryption = asymmetricEncryption; + self.symmetricEncryption = symmetricEncryption; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeInteger:self.version forKey:@"version"]; + [coder encodeObject:self.publicKey forKey:@"publicKey"]; + [coder encodeObject:self.symmetricEncryption forKey:@"symmetricEncrypt"]; + [coder encodeObject:self.asymmetricEncryption forKey:@"asymmetricEncrypt"]; +} + +- (instancetype)initWithCoder:(NSCoder *)coder { + self = [super init]; + if (self) { + self.version = [coder decodeIntegerForKey:@"version"]; + self.publicKey = [coder decodeObjectForKey:@"publicKey"]; + self.symmetricEncryption = [coder decodeObjectForKey:@"symmetricEncrypt"]; + self.asymmetricEncryption = [coder decodeObjectForKey:@"asymmetricEncrypt"]; + } + return self; +} + +- (BOOL)isValid { + if (self.publicKey.length && self.symmetricEncryption.length && self.asymmetricEncryption.length) { + return YES; + } + return NO; +} + +- (id)copyWithZone:(NSZone *)zone { + TDSecretKey *secretKey = [[[self class] allocWithZone:zone] init]; + secretKey.version = self.version; + secretKey.publicKey = [self.publicKey copy]; + secretKey.symmetricEncryption = [self.symmetricEncryption copy]; + secretKey.asymmetricEncryption = [self.asymmetricEncryption copy]; + return secretKey; +} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m.meta new file mode 100644 index 0000000..662a6da --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e2d082a9e23dc1e429fafe875d1dee5e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin.meta new file mode 100644 index 0000000..73bbcfc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a0bfaf5b48186fb42907d7f0f67fb9d7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h new file mode 100644 index 0000000..df59789 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h @@ -0,0 +1,19 @@ +// +// TDAESEncryptor.h +// ThinkingSDK +// +// Created by wwango on 2022/1/21. +// + +#import +#import "TDEncryptAlgorithm.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAESEncryptor : NSObject + +@property (nonatomic, copy, readonly) NSData *key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h.meta new file mode 100644 index 0000000..d3a9ee8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c1bae0f6e28b3a9459f6d015684cc509 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m new file mode 100644 index 0000000..634214a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m @@ -0,0 +1,85 @@ +// +// TDAESEncryptor.m +// ThinkingSDK +// +// Created by wwango on 2022/1/21. +// + +#import "TDAESEncryptor.h" +#import +#import "TDLogging.h" + +@interface TDAESEncryptor () + +@property (nonatomic, copy, readwrite) NSData *key; + +@end + +@implementation TDAESEncryptor + + +- (NSData *)key { + if (!_key) { + NSUInteger length = 16; + NSString *letters = @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + NSMutableString *randomString = [NSMutableString stringWithCapacity:length]; + for (NSUInteger i = 0; i < length; i++) { + [randomString appendFormat: @"%C", [letters characterAtIndex:arc4random_uniform((uint32_t)[letters length])]]; + } + _key = [randomString dataUsingEncoding:NSUTF8StringEncoding]; + } + return _key; +} + + +- (NSString *)algorithm { + return @"AES"; +} + + +- (nullable NSString *)encryptData:(NSData *)obj { + if (!obj) { + return nil; + } + + if (!self.key) { + return nil; + } + + NSData *data = obj; + NSUInteger dataLength = [data length]; + size_t bufferSize = dataLength + kCCBlockSizeAES128; + void *buffer = malloc(bufferSize); + + size_t numBytesEncrypted = 0; + CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, + kCCAlgorithmAES128, + kCCOptionPKCS7Padding | kCCOptionECBMode, + [self.key bytes], + kCCBlockSizeAES128, + nil, + [data bytes], + dataLength, + buffer, + bufferSize, + &numBytesEncrypted); + if (cryptStatus == kCCSuccess) { + + NSData *encryptData = [NSData dataWithBytes:buffer length:numBytesEncrypted]; + NSMutableData *ivEncryptData = [NSMutableData data]; + [ivEncryptData appendData:encryptData]; + + free(buffer); + + NSData *base64EncodeData = [ivEncryptData base64EncodedDataWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]; + NSString *encryptString = [[NSString alloc] initWithData:base64EncodeData encoding:NSUTF8StringEncoding]; + return encryptString; + } else { + free(buffer); + } + return nil; +} + + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m.meta new file mode 100644 index 0000000..5f6bfbc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 59a362c678d5ec041913f12ce41fcfe9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h new file mode 100644 index 0000000..ec1bfe8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h @@ -0,0 +1,19 @@ +// +// TDRSAEncryptor.h +// ThinkingSDK +// +// Created by wwango on 2022/1/21. +// + +#import +#import "TDEncryptAlgorithm.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDRSAEncryptor : NSObject + +@property (nonatomic, copy) NSString *key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h.meta new file mode 100644 index 0000000..ce15a98 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8aa83682ab6fae74ca3f2969d23c276b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m new file mode 100644 index 0000000..031b183 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m @@ -0,0 +1,180 @@ +// +// TDRSAEncryptor.m +// ThinkingSDK +// +// Created by wwango on 2022/1/21. +// + +#import "TDRSAEncryptor.h" + +@implementation TDRSAEncryptor + +- (void)setKey:(NSString *)key { + if (!key) { + return; + } + NSString *publicKeyCopy = [key copy]; + publicKeyCopy = [publicKeyCopy stringByReplacingOccurrencesOfString:@"\r" withString:@""]; + publicKeyCopy = [publicKeyCopy stringByReplacingOccurrencesOfString:@"\n" withString:@""]; + publicKeyCopy = [publicKeyCopy stringByReplacingOccurrencesOfString:@"\t" withString:@""]; + publicKeyCopy = [publicKeyCopy stringByReplacingOccurrencesOfString:@" " withString:@""]; + _key = publicKeyCopy; +} + +- (NSString *)algorithm { + return @"RSA"; +} + +- (NSString *)encryptData:(NSData *)data { + if (!data) { + return nil; + } + + NSString *asymmetricPublicKey = self.key; + if (!asymmetricPublicKey) { + return nil; + } + + SecKeyRef keyRef = [self addPublicKey:asymmetricPublicKey]; + if (!keyRef) { + return nil; + } + + const uint8_t *srcbuf = (const uint8_t *)[data bytes]; + size_t srclen = (size_t)data.length; + + size_t block_size = SecKeyGetBlockSize(keyRef) * sizeof(uint8_t); + void *outbuf = malloc(block_size); + size_t src_block_size = block_size - 11; + + NSMutableData *ret = [[NSMutableData alloc] init]; + for(int idx=0; idx src_block_size) { + data_len = src_block_size; + } + + size_t outlen = block_size; + OSStatus status = noErr; + + status = SecKeyEncrypt(keyRef, + kSecPaddingPKCS1, + srcbuf + idx, + data_len, + outbuf, + &outlen + ); + if (status != 0) { + ret = nil; + break; + }else{ + [ret appendBytes:outbuf length:outlen]; + } + } + free(outbuf); + CFRelease(keyRef); + + return [ret base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn]; +} + +#pragma mark – Private Methods +- (SecKeyRef)addPublicKey:(NSString *)aymmetricPublicKey { + NSString *key = [aymmetricPublicKey copy]; + + NSData *data = [[NSData alloc] initWithBase64EncodedString:key options:NSDataBase64DecodingIgnoreUnknownCharacters]; + data = [self stripPublicKeyHeader:data]; + if (!data) { + return nil; + } + + //a tag to read/write keychain storage + NSString *tag = @"RSAUtil_PubKey"; + NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]]; + + // Delete any old lingering key with the same tag + NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init]; + [publicKey setObject:(__bridge id) kSecClassKey forKey:(__bridge id)kSecClass]; + [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; + [publicKey setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag]; + SecItemDelete((__bridge CFDictionaryRef)publicKey); + + // Add persistent version of the key to system keychain + [publicKey setObject:data forKey:(__bridge id)kSecValueData]; + [publicKey setObject:(__bridge id) kSecAttrKeyClassPublic forKey:(__bridge id) + kSecAttrKeyClass]; + [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id) + kSecReturnPersistentRef]; + + CFTypeRef persistKey = nil; + OSStatus status = SecItemAdd((__bridge CFDictionaryRef)publicKey, &persistKey); + if (persistKey != nil) { + CFRelease(persistKey); + } + if ((status != noErr) && (status != errSecDuplicateItem)) { + return nil; + } + + [publicKey removeObjectForKey:(__bridge id)kSecValueData]; + [publicKey removeObjectForKey:(__bridge id)kSecReturnPersistentRef]; + [publicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef]; + [publicKey setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType]; + + // Now fetch the SecKeyRef version of the key + SecKeyRef keyRef = nil; + status = SecItemCopyMatching((__bridge CFDictionaryRef)publicKey, (CFTypeRef *)&keyRef); + if (status != noErr) { + return nil; + } + return keyRef; +} + +- (NSData *)stripPublicKeyHeader:(NSData *)d_key { + // Skip ASN.1 public key header + if (d_key == nil) { + return(nil); + } + + unsigned long len = [d_key length]; + if (!len) { + return(nil); + } + + unsigned char *c_key = (unsigned char *)[d_key bytes]; + unsigned int idx = 0; + + if (c_key[idx++] != 0x30) { + return(nil); + } + + if (c_key[idx] > 0x80) { + idx += c_key[idx] - 0x80 + 1; + } else { + idx++; + } + + // PKCS #1 rsaEncryption szOID_RSA_RSA + static unsigned char seqiod[] = + { 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00 }; + if (memcmp(&c_key[idx], seqiod, 15)) { + return(nil); + } + idx += 15; + + if (c_key[idx++] != 0x03) { + return(nil); + } + + if (c_key[idx] > 0x80) { + idx += c_key[idx] - 0x80 + 1; + } else { + idx++; + } + if (c_key[idx++] != '\0') { + return(nil); + } + // Now make a new NSData from this buffer + return([NSData dataWithBytes:&c_key[idx] length:len - idx]); +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m.meta new file mode 100644 index 0000000..e0de93d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f264473e7e157d243a1c6841a277a64f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h new file mode 100644 index 0000000..0d88a72 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h @@ -0,0 +1,18 @@ +// +// TDRSAEncryptorPlugin.h +// ThinkingSDK +// +// Created by wwango on 2022/1/21. +// + +#import +#import "TDEncryptProtocol.h" +#import "TDRSAEncryptor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDRSAEncryptorPlugin : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h.meta new file mode 100644 index 0000000..90f8dc8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2f8a2fc7a7a36dc4a8386f0de8dd3936 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m new file mode 100644 index 0000000..97f650e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m @@ -0,0 +1,53 @@ +// +// TDRSAEncryptorPlugin.m +// ThinkingSDK +// +// Created by wwango on 2022/1/21. +// + +#import "TDRSAEncryptorPlugin.h" +#import "TDAESEncryptor.h" +#import "TDRSAEncryptor.h" + +@interface TDRSAEncryptorPlugin () + +@property (nonatomic, strong) TDAESEncryptor *aesEncryptor; +@property (nonatomic, strong) TDRSAEncryptor *rsaEncryptor; + +@end + +@implementation TDRSAEncryptorPlugin + +- (instancetype)init { + self = [super init]; + if (self) { + _aesEncryptor = [[TDAESEncryptor alloc] init]; + _rsaEncryptor = [[TDRSAEncryptor alloc] init]; + } + return self; +} + + +- (NSString *)symmetricEncryptType { + return [_aesEncryptor algorithm]; +} + + +- (NSString *)asymmetricEncryptType { + return [_rsaEncryptor algorithm]; +} + + +- (NSString *)encryptEvent:(NSData *)event { + return [_aesEncryptor encryptData:event]; +} + + +- (NSString *)encryptSymmetricKeyWithPublicKey:(NSString *)publicKey { + if (![_rsaEncryptor.key isEqualToString:publicKey]) { + _rsaEncryptor.key = publicKey; + } + return [_rsaEncryptor encryptData:_aesEncryptor.key]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m.meta new file mode 100644 index 0000000..5ce3efd --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b25354cad37847947a6aeeacd66034c8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker.meta new file mode 100644 index 0000000..ad46b68 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9f887689f12856249b4329baebed1c89 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel.meta new file mode 100644 index 0000000..143c17d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4b1919f01f251cc41860bb9f3da62aa9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.h new file mode 100644 index 0000000..fc416b9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.h @@ -0,0 +1,18 @@ +// +// TABaseEvent+H5.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import "TABaseEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TABaseEvent (H5) +@property (nonatomic, copy) NSString *h5TimeString; +@property (nonatomic, strong) NSNumber *h5ZoneOffSet; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.h.meta new file mode 100644 index 0000000..c0d8aae --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f11317a372fa3bb43a976f24b4df418b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.m new file mode 100644 index 0000000..3df6fa9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.m @@ -0,0 +1,32 @@ +// +// TABaseEvent+H5.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import "TABaseEvent+H5.h" +#import + +static char TA_EVENT_H5_TIME_STRING; +static char TA_EVENT_H5_ZONE_OFF_SET; + +@implementation TABaseEvent (H5) + +- (NSString *)h5TimeString { + return objc_getAssociatedObject(self, &TA_EVENT_H5_TIME_STRING); +} + +- (void)setH5TimeString:(NSString *)h5TimeString { + objc_setAssociatedObject(self, &TA_EVENT_H5_TIME_STRING, h5TimeString, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (NSNumber *)h5ZoneOffSet { + return objc_getAssociatedObject(self, &TA_EVENT_H5_ZONE_OFF_SET); +} + +- (void)setH5ZoneOffSet:(NSNumber *)h5ZoneOffSet { + objc_setAssociatedObject(self, &TA_EVENT_H5_ZONE_OFF_SET, h5ZoneOffSet, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.m.meta new file mode 100644 index 0000000..22863e6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent+H5.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f174d52f029e3624d9eccdf142c3384f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.h new file mode 100644 index 0000000..b890f23 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.h @@ -0,0 +1,83 @@ +// +// TABaseEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import +#import "TAPropertyValidator.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef NSString * kTAEventType; + +typedef NS_OPTIONS(NSUInteger, TAEventType) { + TAEventTypeNone = 0, + TAEventTypeTrack = 1 << 0, + TAEventTypeTrackFirst = 1 << 1, + TAEventTypeTrackUpdate = 1 << 2, + TAEventTypeTrackOverwrite = 1 << 3, + TAEventTypeUserSet = 1 << 4, + TAEventTypeUserUnset = 1 << 5, + TAEventTypeUserAdd = 1 << 6, + TAEventTypeUserDel = 1 << 7, + TAEventTypeUserSetOnce = 1 << 8, + TAEventTypeUserAppend = 1 << 9, + TAEventTypeUserUniqueAppend = 1 << 10, + TAEventTypeAll = 0xFFFFFFFF, +}; + +//extern kTAEventType const kTAEventTypeTrack; +//extern kTAEventType const kTAEventTypeTrackFirst; +//extern kTAEventType const kTAEventTypeTrackUpdate; +//extern kTAEventType const kTAEventTypeTrackOverwrite; +//extern kTAEventType const kTAEventTypeUserSet; +//extern kTAEventType const kTAEventTypeUserUnset; +//extern kTAEventType const kTAEventTypeUserAdd; +//extern kTAEventType const kTAEventTypeUserDel; +//extern kTAEventType const kTAEventTypeUserSetOnce; +//extern kTAEventType const kTAEventTypeUserAppend; +//extern kTAEventType const kTAEventTypeUserUniqueAppend; + +typedef NS_OPTIONS(NSInteger, TAEventTimeValueType) { + TAEventTimeValueTypeNone = 0, + TAEventTimeValueTypeTimeOnly = 1 << 0, + TAEventTimeValueTypeTimeAndZone = 1 << 1, +}; + +@interface TABaseEvent : NSObject +@property (nonatomic, assign) TAEventType eventType; +@property (nonatomic, copy) NSString *uuid; +@property (nonatomic, copy) NSString *accountId; +@property (nonatomic, copy) NSString *distinctId; +@property (nonatomic, strong) NSDate *time; +@property (nonatomic, strong) NSTimeZone *timeZone; +@property (nonatomic, strong, readonly) NSDateFormatter *timeFormatter; + +@property (nonatomic, assign) TAEventTimeValueType timeValueType; +@property (nonatomic, strong) NSMutableDictionary *properties; + +@property (nonatomic, assign) BOOL immediately; + +@property (atomic, assign, getter=isTrackPause) BOOL trackPause; + +@property (nonatomic, assign) BOOL isEnabled; + +@property (atomic, assign) BOOL isOptOut; + +- (instancetype)initWithType:(TAEventType)type; + +- (void)validateWithError:(NSError **)error; + +- (NSMutableDictionary *)jsonObject; + +- (NSMutableDictionary *)formatDateWithDict:(NSDictionary *)dict; + +- (NSString *)eventTypeString; + ++ (TAEventType)typeWithTypeString:(NSString *)typeString; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.h.meta new file mode 100644 index 0000000..1a11817 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3420f9b2d086c2546b50b97d7dff67c7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.m new file mode 100644 index 0000000..fe00edb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.m @@ -0,0 +1,215 @@ +// +// TABaseEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TABaseEvent.h" + +#if __has_include() +#import +#else +#import "TDLogging.h" +#endif + +#import "ThinkingAnalyticsSDKPrivate.h" + +kTAEventType const kTAEventTypeTrack = @"track"; +kTAEventType const kTAEventTypeTrackFirst = @"track_first"; +kTAEventType const kTAEventTypeTrackUpdate = @"track_update"; +kTAEventType const kTAEventTypeTrackOverwrite = @"track_overwrite"; +kTAEventType const kTAEventTypeUserSet = @"user_set"; +kTAEventType const kTAEventTypeUserUnset = @"user_unset"; +kTAEventType const kTAEventTypeUserAdd = @"user_add"; +kTAEventType const kTAEventTypeUserDel = @"user_del"; +kTAEventType const kTAEventTypeUserSetOnce = @"user_setOnce"; +kTAEventType const kTAEventTypeUserAppend = @"user_append"; +kTAEventType const kTAEventTypeUserUniqueAppend = @"user_uniq_append"; + +@interface TABaseEvent () +@property (nonatomic, strong) NSDateFormatter *timeFormatter; + +@end + +@implementation TABaseEvent + +- (instancetype)init +{ + self = [super init]; + if (self) { + + _time = [NSDate date]; + self.timeValueType = TAEventTimeValueTypeNone; + self.uuid = [NSUUID UUID].UUIDString; + } + return self; +} + +- (instancetype)initWithType:(TAEventType)type { + if (self = [self init]) { + self.eventType = type; + } + return self; +} + +- (void)validateWithError:(NSError *__autoreleasing _Nullable *)error { + +} + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + dict[@"#time"] = self.time; + dict[@"#uuid"] = self.uuid; + dict[@"#type"] = [self eventTypeString]; + if (self.accountId) { + dict[@"#account_id"] = self.accountId; + } + if (self.distinctId) { + dict[@"#distinct_id"] = self.distinctId; + } + dict[@"properties"] = self.properties; + return dict; +} + +- (NSMutableDictionary *)formatDateWithDict:(NSDictionary *)dict { + if (dict == nil || ![dict isKindOfClass:NSDictionary.class]) { + return nil; + } + NSMutableDictionary *mutableDict = nil; + if ([dict isKindOfClass:NSMutableDictionary.class]) { + mutableDict = (NSMutableDictionary *)dict; + } else { + mutableDict = [dict mutableCopy]; + } + + NSArray *keys = dict.allKeys; + for (NSInteger i = 0; i < keys.count; i++) { + id value = dict[keys[i]]; + if ([value isKindOfClass:NSDate.class]) { + NSString *newValue = [self.timeFormatter stringFromDate:(NSDate *)value]; + mutableDict[keys[i]] = newValue; + } else if ([value isKindOfClass:NSDictionary.class]) { + NSDictionary *newValue = [self formatDateWithDict:value]; + mutableDict[keys[i]] = newValue; + } + } + return mutableDict; +} + +- (NSString *)eventTypeString { + switch (self.eventType) { + case TAEventTypeTrack: { + return TD_EVENT_TYPE_TRACK; + } break; + case TAEventTypeTrackFirst: { + + return TD_EVENT_TYPE_TRACK; + } break; + case TAEventTypeTrackUpdate: { + return TD_EVENT_TYPE_TRACK_UPDATE; + } break; + case TAEventTypeTrackOverwrite: { + return TD_EVENT_TYPE_TRACK_OVERWRITE; + } break; + case TAEventTypeUserAdd: { + return TD_EVENT_TYPE_USER_ADD; + } break; + case TAEventTypeUserSet: { + return TD_EVENT_TYPE_USER_SET; + } break; + case TAEventTypeUserUnset: { + return TD_EVENT_TYPE_USER_UNSET; + } break; + case TAEventTypeUserAppend: { + return TD_EVENT_TYPE_USER_APPEND; + } break; + case TAEventTypeUserUniqueAppend: { + return TD_EVENT_TYPE_USER_UNIQ_APPEND; + } break; + case TAEventTypeUserDel: { + return TD_EVENT_TYPE_USER_DEL; + } break; + case TAEventTypeUserSetOnce: { + return TD_EVENT_TYPE_USER_SETONCE; + } break; + + default: + return nil; + break; + } +} + ++ (TAEventType)typeWithTypeString:(NSString *)typeString { + if ([typeString isEqualToString:TD_EVENT_TYPE_TRACK]) { + return TAEventTypeTrack; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_TRACK_FIRST]) { + return TAEventTypeTrack; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_TRACK_UPDATE]) { + return TAEventTypeTrackUpdate; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_TRACK_OVERWRITE]) { + return TAEventTypeTrackOverwrite; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_ADD]) { + return TAEventTypeUserAdd; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_DEL]) { + return TAEventTypeUserDel; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_SET]) { + return TAEventTypeUserSet; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_UNSET]) { + return TAEventTypeUserUnset; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_APPEND]) { + return TAEventTypeUserAppend; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_UNIQ_APPEND]) { + return TAEventTypeUserUniqueAppend; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_SETONCE]) { + return TAEventTypeUserSetOnce; + } + return TAEventTypeNone; +} + +//MARK: - Private + +//MARK: - Delegate + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + +} + +//MARK: - Setter & Getter + +- (NSMutableDictionary *)properties { + if (!_properties) { + _properties = [NSMutableDictionary dictionary]; + } + return _properties; +} + +- (void)setTimeZone:(NSTimeZone *)timeZone { + _timeZone = timeZone; + + + self.timeFormatter.timeZone = timeZone ?: [NSTimeZone localTimeZone]; +} + +- (NSDateFormatter *)timeFormatter { + if (!_timeFormatter) { + _timeFormatter = [[NSDateFormatter alloc] init]; + _timeFormatter.dateFormat = kDefaultTimeFormat; + _timeFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + _timeFormatter.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + + _timeFormatter.timeZone = [NSTimeZone localTimeZone]; + } + return _timeFormatter; +} + +- (void)setTime:(NSDate *)time { + + if (time) { + [self willChangeValueForKey:@"time"]; + _time = time; + [self didChangeValueForKey:@"time"]; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.m.meta new file mode 100644 index 0000000..78bf081 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TABaseEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 264d7dc6cbe59cd40b05b87c88789d50 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.h new file mode 100644 index 0000000..63b3910 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.h @@ -0,0 +1,34 @@ +// +// TATrackEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TABaseEvent.h" + +NS_ASSUME_NONNULL_BEGIN + + +@interface TATrackEvent : TABaseEvent +/// eventName +@property (nonatomic, copy) NSString *eventName; +/// Cumulative front activity time +@property (nonatomic, assign) NSTimeInterval foregroundDuration; +/// Cumulative background time +@property (nonatomic, assign) NSTimeInterval backgroundDuration; + +/// Record the boot time node when the event occurred. Used to count the cumulative time of events +@property (nonatomic, assign) NSTimeInterval systemUpTime; + +/// Used to record dynamic public properties, dynamic public properties need to be obtained in the current thread where the event occurs +@property (nonatomic, strong) NSDictionary *dynamicSuperProperties; + +/// Used to document static public properties +@property (nonatomic, strong) NSDictionary *superProperties; + +- (instancetype)initWithName:(NSString *)eventName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.h.meta new file mode 100644 index 0000000..83eb6ab --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 348008e8bc508b5428b825f9975547ec +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.m new file mode 100644 index 0000000..c905bcd --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.m @@ -0,0 +1,75 @@ +// +// TATrackEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TATrackEvent.h" +#import "TDPresetProperties.h" +#import "TDPresetProperties+TDDisProperties.h" +#import "NSDate+TAFormat.h" +#import "TDDeviceInfo.h" + +@implementation TATrackEvent + +- (instancetype)initWithName:(NSString *)eventName { + if (self = [self init]) { + self.eventName = eventName; + } + return self; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.eventType = TAEventTypeTrack; + self.systemUpTime = [TDDeviceInfo uptime]; + } + return self; +} + +- (void)validateWithError:(NSError *__autoreleasing _Nullable *)error { + + [TAPropertyValidator validateEventOrPropertyName:self.eventName withError:error]; +} + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + dict[@"#event_name"] = self.eventName; + + if (![TDPresetProperties disableDuration]) { + if (self.foregroundDuration > 0) { + self.properties[@"#duration"] = @([NSString stringWithFormat:@"%.3f", self.foregroundDuration].floatValue); + } + } + + if (![TDPresetProperties disableBackgroundDuration]) { + if (self.backgroundDuration > 0) { + self.properties[@"#background_duration"] = @([NSString stringWithFormat:@"%.3f", self.backgroundDuration].floatValue); + } + } + + if (self.timeValueType != TAEventTimeValueTypeTimeOnly) { + if (![TDPresetProperties disableZoneOffset]) { + self.properties[@"#zone_offset"] = @([self timeZoneOffset]); + } + } + + return dict; +} + +- (double)timeZoneOffset { + NSTimeZone *tz = self.timeZone ?: [NSTimeZone localTimeZone]; + return [[NSDate date] ta_timeZoneOffset:tz]; +} + +//MARK: - Delegate + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + [TAPropertyValidator validateNormalTrackEventPropertyKey:key value:value error:error]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.m.meta new file mode 100644 index 0000000..b0216a7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 632dc32424717974a807dc4adb9fcf41 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.h new file mode 100644 index 0000000..81bfb99 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.h @@ -0,0 +1,18 @@ +// +// TATrackFirstEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TATrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TATrackFirstEvent : TATrackEvent + +@property (nonatomic, copy) NSString *firstCheckId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.h.meta new file mode 100644 index 0000000..c6ed991 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: cf92f2d9fb4cde247948818a8f9c9cc5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.m new file mode 100644 index 0000000..c226cdf --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.m @@ -0,0 +1,42 @@ +// +// TATrackFirstEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TATrackFirstEvent.h" +#import "TDDeviceInfo.h" + +@implementation TATrackFirstEvent + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.eventType = TAEventTypeTrackFirst; + } + return self; +} + +- (void)validateWithError:(NSError *__autoreleasing _Nullable *)error { + [super validateWithError:error]; + if (*error) { + return; + } + if (self.firstCheckId.length <= 0) { + NSString *errorMsg = @"property 'firstCheckId' cannot be empty which in FirstEvent"; + *error = TAPropertyError(100010, errorMsg); + return; + } +} + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + dict[@"#first_check_id"] = self.firstCheckId ?: [TDDeviceInfo sharedManager].deviceId; + + return dict; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.m.meta new file mode 100644 index 0000000..c298372 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackFirstEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bd17e1c9dfb23764c8c6516849e8c5f4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.h new file mode 100644 index 0000000..69a23f5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.h @@ -0,0 +1,18 @@ +// +// TATrackOverwriteEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TATrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TATrackOverwriteEvent : TATrackEvent + +@property (nonatomic, copy) NSString *eventId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.h.meta new file mode 100644 index 0000000..189434d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b6e101892176f7941a1b09ff739b227c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.m new file mode 100644 index 0000000..a9c740e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.m @@ -0,0 +1,42 @@ +// +// TATrackOverwriteEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TATrackOverwriteEvent.h" + +@implementation TATrackOverwriteEvent + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.eventType = TAEventTypeTrackOverwrite; + } + return self; +} + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + dict[@"#event_id"] = self.eventId; + + return dict; +} + + +- (void)validateWithError:(NSError *__autoreleasing _Nullable *)error { + [super validateWithError:error]; + if (*error) { + return; + } + if (self.eventId.length <= 0) { + NSString *errorMsg = @"property 'eventId' cannot be empty which in OverwriteEvent"; + *error = TAPropertyError(100011, errorMsg); + return; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.m.meta new file mode 100644 index 0000000..c50a399 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackOverwriteEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9533786078363fa428dc28c33206f5b2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.h new file mode 100644 index 0000000..845b3bb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.h @@ -0,0 +1,18 @@ +// +// TATrackUpdateEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TATrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TATrackUpdateEvent : TATrackEvent + +@property (nonatomic, copy) NSString *eventId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.h.meta new file mode 100644 index 0000000..8363298 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d8212c727ff3ae14fb32e3996cc02f01 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.m new file mode 100644 index 0000000..7bd99ae --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.m @@ -0,0 +1,42 @@ +// +// TATrackUpdateEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TATrackUpdateEvent.h" + +@implementation TATrackUpdateEvent + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.eventType = TAEventTypeTrackUpdate; + } + return self; +} + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + dict[@"#event_id"] = self.eventId; + + return dict; +} + + +- (void)validateWithError:(NSError *__autoreleasing _Nullable *)error { + [super validateWithError:error]; + if (*error) { + return; + } + if (self.eventId.length <= 0) { + NSString *errorMsg = @"property 'eventId' cannot be empty which in UpdateEvent"; + *error = TAPropertyError(100012, errorMsg); + return; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.m.meta new file mode 100644 index 0000000..22840b4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TATrackUpdateEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 473fc011a28100a44bc92ba0a638b227 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h new file mode 100644 index 0000000..98a2f4e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h @@ -0,0 +1,12 @@ + +#if __has_include() +#import +#else +#import "TDUpdateEventModel.h" +#endif + +#if __has_include() +#import +#else +#import "TDOverwriteEventModel.h" +#endif diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h.meta new file mode 100644 index 0000000..a96f49b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4f8cc57729aa4ec4eaaae6d8ed602249 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h new file mode 100644 index 0000000..6a4403a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h @@ -0,0 +1,31 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_OPTIONS(NSInteger, TimeValueType) { + TDTimeValueTypeNone = 0, + TDTimeValueTypeTimeOnly = 1 << 0, + TDTimeValueTypeAll = 1 << 1, +}; + +typedef NSString *kEDEventTypeName; + +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_TRACK_FIRST; +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_TRACK_UPDATE; +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_TRACK_OVERWRITE; + +@interface TDEventModel : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@property (nonatomic, copy, readonly) NSString *eventName; +@property (nonatomic, copy, readonly) kEDEventTypeName eventType; // Default is TD_EVENT_TYPE_TRACK + +@property (nonatomic, strong) NSDictionary *properties; + +- (void)configTime:(NSDate *)time timeZone:(NSTimeZone * _Nullable)timeZone; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h.meta new file mode 100644 index 0000000..890c636 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b3e64bb60e0cf8f4e9c76c4e2e3266d8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m new file mode 100644 index 0000000..c4c310e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m @@ -0,0 +1,72 @@ + +#import "TDEventModel.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +kEDEventTypeName const TD_EVENT_TYPE_TRACK_FIRST = @"track_first"; +kEDEventTypeName const TD_EVENT_TYPE_TRACK_UPDATE = @"track_update"; +kEDEventTypeName const TD_EVENT_TYPE_TRACK_OVERWRITE = @"track_overwrite"; + +@interface TDEventModel () + +@property (nonatomic, copy) NSString *eventName; +@property (nonatomic, copy) kEDEventTypeName eventType; + +@end + +@implementation TDEventModel + +- (instancetype)initWithEventName:(NSString *)eventName { + return [self initWithEventName:eventName eventType:TD_EVENT_TYPE_TRACK]; +} + +- (instancetype)initWithEventName:(NSString *)eventName eventType:(kEDEventTypeName)eventType { + if (self = [[[TDEventModel class] alloc] init]) { + self.persist = YES; + self.eventName = eventName ?: @""; + self.eventType = eventType ?: @""; + if ([self.eventType isEqualToString:TD_EVENT_TYPE_TRACK_FIRST]) { + _extraID = [TDDeviceInfo sharedManager].deviceId ?: @""; + } + } + return self; +} + +#pragma mark - Public + +- (void)configTime:(NSDate *)time timeZone:(NSTimeZone *)timeZone { + if (!time || ![time isKindOfClass:[NSDate class]]) { + self.timeValueType = TDTimeValueTypeNone; + } else { + self.time = time; + self.timeZone = timeZone; + + NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init]; + timeFormatter.dateFormat = kDefaultTimeFormat; + timeFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + timeFormatter.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + if (timeZone && [timeZone isKindOfClass:[NSTimeZone class]]) { + self.timeValueType = TDTimeValueTypeAll; + timeFormatter.timeZone = timeZone; + } else { + self.timeValueType = TDTimeValueTypeTimeOnly; + timeFormatter.timeZone = [NSTimeZone localTimeZone]; + } + self.timeString = [timeFormatter stringFromDate:time]; + } +} + +#pragma mark - Setter + +- (void)setExtraID:(NSString *)extraID { + if (extraID.length > 0) { + _extraID = extraID; + } else { + if ([self.eventType isEqualToString:TD_EVENT_TYPE_TRACK_FIRST]) { + TDLogError(@"Invalid firstCheckId. Use device Id"); + } else { + TDLogError(@"Invalid eventId"); + } + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m.meta new file mode 100644 index 0000000..6a46266 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 89bc3274598fdcc4594b2a8dc286c530 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h new file mode 100644 index 0000000..a0d83a9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h @@ -0,0 +1,19 @@ + +#if __has_include() +#import +#else +#import "TDEventModel.h" +#endif + + +NS_ASSUME_NONNULL_BEGIN + +@interface TDFirstEventModel : TDEventModel + +- (instancetype)initWithEventName:(NSString * _Nullable)eventName; + +- (instancetype)initWithEventName:(NSString * _Nullable)eventName firstCheckID:(NSString *)firstCheckID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h.meta new file mode 100644 index 0000000..6e1ea19 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 73d5531a42705ed4ba226579bd7a98b1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m new file mode 100644 index 0000000..11db5a8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m @@ -0,0 +1,17 @@ +#import "TDFirstEventModel.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +@implementation TDFirstEventModel + +- (instancetype)initWithEventName:(NSString *)eventName { + return [self initWithEventName:eventName eventType:TD_EVENT_TYPE_TRACK_FIRST]; +} + +- (instancetype)initWithEventName:(NSString *)eventName firstCheckID:(NSString *)firstCheckID { + if (self = [self initWithEventName:eventName eventType:TD_EVENT_TYPE_TRACK_FIRST]) { + self.extraID = firstCheckID; + } + return self; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m.meta new file mode 100644 index 0000000..659810c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a0692a1a2fa244c488a7169860f116ba +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h new file mode 100644 index 0000000..46a2968 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h @@ -0,0 +1,22 @@ +// +// TDOverwriteEventModel.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#if __has_include() +#import +#else +#import "TDEventModel.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDOverwriteEventModel : TDEventModel + +- (instancetype)initWithEventName:(NSString *)eventName eventID:(NSString *)eventID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h.meta new file mode 100644 index 0000000..0afa9be --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 182d74d42aab04e4481e2f85b6dd21b3 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m new file mode 100644 index 0000000..7f9de48 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m @@ -0,0 +1,20 @@ +// +// TDOverwriteEventModel.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDOverwriteEventModel.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +@implementation TDOverwriteEventModel + +- (instancetype)initWithEventName:(NSString *)eventName eventID:(NSString *)eventID { + if (self = [self initWithEventName:eventName eventType:TD_EVENT_TYPE_TRACK_OVERWRITE]) { + self.extraID = eventID; + } + return self; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m.meta new file mode 100644 index 0000000..24c0512 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8d2e30d9eb2468e46b0a2bec2943ca37 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h new file mode 100644 index 0000000..b356a60 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h @@ -0,0 +1,22 @@ +// +// TDUpdateEventModel.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#if __has_include() +#import +#else +#import "TDEventModel.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDUpdateEventModel : TDEventModel + +- (instancetype)initWithEventName:(NSString *)eventName eventID:(NSString *)eventID; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h.meta new file mode 100644 index 0000000..e433c80 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: dbc2e721110e8d4458e16a8fa6abe060 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m new file mode 100644 index 0000000..16aeca7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m @@ -0,0 +1,20 @@ +// +// TDUpdateEventModel.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUpdateEventModel.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +@implementation TDUpdateEventModel + +- (instancetype)initWithEventName:(NSString *)eventName eventID:(NSString *)eventID { + if (self = [self initWithEventName:eventName eventType:TD_EVENT_TYPE_TRACK_UPDATE]) { + self.extraID = eventID; + } + return self; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m.meta new file mode 100644 index 0000000..ba7da4c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4a4c872a829d6024fb4a734c8ef5a8ed +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty.meta new file mode 100644 index 0000000..2997556 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0e7eea08e7b68714a8325f2273b99e2b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.h new file mode 100644 index 0000000..1625740 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.h @@ -0,0 +1,16 @@ +// +// TAUserEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TABaseEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAUserEvent : TABaseEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.h.meta new file mode 100644 index 0000000..7f6034c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6bb3fb96980c85a41be6e9150daad0d3 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.m new file mode 100644 index 0000000..33fb3dc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.m @@ -0,0 +1,35 @@ +// +// TAUserEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TAUserEvent.h" + +@implementation TAUserEvent + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.timeValueType = TAEventTimeValueTypeNone; + } + return self; +} + +//MARK: - Delegate + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + [TAPropertyValidator validateBaseEventPropertyKey:key value:value error:error]; +} + +//MARK: - Setter & Getter + +- (void)setTime:(NSDate *)time { + [super setTime:time]; + + self.timeValueType = time == nil ? TAEventTimeValueTypeNone : TAEventTimeValueTypeTimeOnly; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.m.meta new file mode 100644 index 0000000..e1b887c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9b2091d83c9e46d48b5ecbcc1303bafb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.h new file mode 100644 index 0000000..b69ec61 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.h @@ -0,0 +1,16 @@ +// +// TAUserEventAdd.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAUserEventAdd : TAUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.h.meta new file mode 100644 index 0000000..e36be3b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 63b7ac8601a8a024cadf13867461fea2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.m new file mode 100644 index 0000000..53556e5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.m @@ -0,0 +1,36 @@ +// +// TAUserEventAdd.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEventAdd.h" + +@implementation TAUserEventAdd + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TAEventTypeUserAdd; + } + return self; +} + +- (void)validateWithError:(NSError *__autoreleasing _Nullable *)error { + +} + +//MARK: - Delegate + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + [super ta_validateKey:key value:value error:error]; + if (*error) { + return; + } + if (![value isKindOfClass:NSNumber.class]) { + NSString *errMsg = [NSString stringWithFormat:@"Property value must be type NSNumber. got: %@ %@. ", [value class], value]; + *error = TAPropertyError(10008, errMsg); + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.m.meta new file mode 100644 index 0000000..68fd058 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAdd.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f189f76c1bda181468232e42d0167df9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.h new file mode 100644 index 0000000..ac04d9e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.h @@ -0,0 +1,16 @@ +// +// TAUserEventAppend.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAUserEventAppend : TAUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.h.meta new file mode 100644 index 0000000..76bed6b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d49e66ef3c986cc4bbeeb74546f4efff +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.m new file mode 100644 index 0000000..52e2ecf --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.m @@ -0,0 +1,32 @@ +// +// TAUserEventAppend.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEventAppend.h" + +@implementation TAUserEventAppend + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TAEventTypeUserAppend; + } + return self; +} + +//MARK: - Delegate + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + [super ta_validateKey:key value:value error:error]; + if (*error) { + return; + } + if (![value isKindOfClass:NSArray.class]) { + NSString *errMsg = [NSString stringWithFormat:@"Property value must be type NSArray. got: %@ %@. ", [value class], value]; + *error = TAPropertyError(10009, errMsg); + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.m.meta new file mode 100644 index 0000000..dd7e5d8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventAppend.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e81e2c06e182e724797941a1649ebfc9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.h new file mode 100644 index 0000000..c0a7e67 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.h @@ -0,0 +1,16 @@ +// +// TAUserEventDelete.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAUserEventDelete : TAUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.h.meta new file mode 100644 index 0000000..e7b954d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 34371ed8d8bc1d046a8d9ce530e7cc5b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.m new file mode 100644 index 0000000..e076f99 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.m @@ -0,0 +1,19 @@ +// +// TAUserEventDelete.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEventDelete.h" + +@implementation TAUserEventDelete + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TAEventTypeUserDel; + } + return self; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.m.meta new file mode 100644 index 0000000..3f4d2c7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventDelete.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b1c8b11b1d25e694daba78204879133f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.h new file mode 100644 index 0000000..932d1a5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.h @@ -0,0 +1,16 @@ +// +// TAUserEventSet.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAUserEventSet : TAUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.h.meta new file mode 100644 index 0000000..6d1fe7c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7fb292f74faffd04aacdd6d6c8fb573d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.m new file mode 100644 index 0000000..66846e1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.m @@ -0,0 +1,19 @@ +// +// TAUserEventSet.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEventSet.h" + +@implementation TAUserEventSet + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TAEventTypeUserSet; + } + return self; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.m.meta new file mode 100644 index 0000000..a0045f3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSet.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ce3f089d0844ad64abe613ad1c9611e5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.h new file mode 100644 index 0000000..7a077ec --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.h @@ -0,0 +1,16 @@ +// +// TAUserEventSetOnce.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAUserEventSetOnce : TAUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.h.meta new file mode 100644 index 0000000..37b403b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c45394852951ac34893a0a996450fb7d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.m new file mode 100644 index 0000000..0829379 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.m @@ -0,0 +1,19 @@ +// +// TAUserEventSetOnce.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEventSetOnce.h" + +@implementation TAUserEventSetOnce + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TAEventTypeUserSetOnce; + } + return self; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.m.meta new file mode 100644 index 0000000..18e8ca5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventSetOnce.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3c47de666b1b16a418729c90dc5176d5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.h new file mode 100644 index 0000000..d5c0d76 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.h @@ -0,0 +1,16 @@ +// +// TAUserEventUniqueAppend.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEventAppend.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAUserEventUniqueAppend : TAUserEventAppend + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.h.meta new file mode 100644 index 0000000..cdd604a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9e0bc42d52b244149b55cc9aa04c0e87 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.m new file mode 100644 index 0000000..b4e7e91 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.m @@ -0,0 +1,19 @@ +// +// TAUserEventUniqueAppend.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEventUniqueAppend.h" + +@implementation TAUserEventUniqueAppend + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TAEventTypeUserUniqueAppend; + } + return self; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.m.meta new file mode 100644 index 0000000..b7a93c5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUniqueAppend.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 961c617f5bbdd1341a9a4f34ffd3761d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.h new file mode 100644 index 0000000..96d53af --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.h @@ -0,0 +1,16 @@ +// +// TAUserEventUnset.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAUserEventUnset : TAUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.h.meta new file mode 100644 index 0000000..0b70d0b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a78029a6eae2e9e479722e1ec2da88a0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.m new file mode 100644 index 0000000..2759d7e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.m @@ -0,0 +1,19 @@ +// +// TAUserEventUnset.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAUserEventUnset.h" + +@implementation TAUserEventUnset + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TAEventTypeUserUnset; + } + return self; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.m.meta new file mode 100644 index 0000000..ea2cae9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserEventUnset.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: adade9d1d8d6f19418abf8b1ba48c9f6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserPropertyHeader.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserPropertyHeader.h new file mode 100644 index 0000000..834297a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserPropertyHeader.h @@ -0,0 +1,19 @@ +// +// TAUserPropertyHeader.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#ifndef TAUserPropertyHeader_h +#define TAUserPropertyHeader_h + +#import "TAUserEventSet.h" +#import "TAUserEventSetOnce.h" +#import "TAUserEventUnset.h" +#import "TAUserEventAdd.h" +#import "TAUserEventDelete.h" +#import "TAUserEventAppend.h" +#import "TAUserEventUniqueAppend.h" + +#endif /* TAUserPropertyHeader_h */ diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserPropertyHeader.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserPropertyHeader.h.meta new file mode 100644 index 0000000..9a99cef --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TAUserPropertyHeader.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0f4c8873703dea740ae30f3254501278 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property.meta new file mode 100644 index 0000000..b4e8ee4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1254ce5bba99c1d45b1845450e781116 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty.meta new file mode 100644 index 0000000..8adb20c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0f80018b6b2b62a46afd80f5d35584b9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.h new file mode 100644 index 0000000..a2ee5cb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.h @@ -0,0 +1,72 @@ +// +// TDPresetProperties+TDDisProperties.h +// ThinkingSDK +// +// Created by wwango on 2021/12/7. +// 不能使用的 + +#if __has_include() +#import +#else +#import "TDPresetProperties.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDPresetProperties (TDDisProperties) + +// - 禁用功能并过滤字段拼接 + +@property(class, nonatomic, readonly) BOOL disableOpsReceiptProperties; +@property(class, nonatomic, readonly) BOOL disableStartReason; +@property(class, nonatomic, readonly) BOOL disableDisk; +@property(class, nonatomic, readonly) BOOL disableRAM; +@property(class, nonatomic, readonly) BOOL disableFPS; +@property(class, nonatomic, readonly) BOOL disableSimulator; +@property(class, nonatomic, readonly) BOOL disableAppVersion; +@property(class, nonatomic, readonly) BOOL disableOsVersion; +@property(class, nonatomic, readonly) BOOL disableManufacturer; +@property(class, nonatomic, readonly) BOOL disableDeviceModel; +@property(class, nonatomic, readonly) BOOL disableScreenHeight; +@property(class, nonatomic, readonly) BOOL disableScreenWidth; +@property(class, nonatomic, readonly) BOOL disableCarrier; +@property(class, nonatomic, readonly) BOOL disableDeviceId; +@property(class, nonatomic, readonly) BOOL disableSystemLanguage; +@property(class, nonatomic, readonly) BOOL disableLib; +@property(class, nonatomic, readonly) BOOL disableLibVersion; +@property(class, nonatomic, readonly) BOOL disableBundleId; +@property(class, nonatomic, readonly) BOOL disableOs; +@property(class, nonatomic, readonly) BOOL disableInstallTime; +@property(class, nonatomic, readonly) BOOL disableDeviceType; +@property(class, nonatomic, readonly) BOOL disableSessionID; +@property(class, nonatomic, readonly) BOOL disableCalibratedTime; + + +// - 只过滤字段 +@property(class, nonatomic, readonly) BOOL disableNetworkType; +@property(class, nonatomic, readonly) BOOL disableZoneOffset; +@property(class, nonatomic, readonly) BOOL disableDuration; +@property(class, nonatomic, readonly) BOOL disableBackgroundDuration; +@property(class, nonatomic, readonly) BOOL disableAppCrashedReason; +@property(class, nonatomic, readonly) BOOL disableResumeFromBackground; +@property(class, nonatomic, readonly) BOOL disableElementId; +@property(class, nonatomic, readonly) BOOL disableElementType; +@property(class, nonatomic, readonly) BOOL disableElementContent; +@property(class, nonatomic, readonly) BOOL disableElementPosition; +@property(class, nonatomic, readonly) BOOL disableElementSelector; +@property(class, nonatomic, readonly) BOOL disableScreenName; +@property(class, nonatomic, readonly) BOOL disableTitle; +@property(class, nonatomic, readonly) BOOL disableUrl; +@property(class, nonatomic, readonly) BOOL disableReferrer; + + +/// 需要过滤的预置属性 ++ (NSArray*)disPresetProperties; + +/// 过滤预置属性 +/// @param dataDic 外层property ++ (void)handleFilterDisPresetProperties:(NSMutableDictionary *)dataDic; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.h.meta new file mode 100644 index 0000000..57c70db --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fa7b9fe20e8169b47a9c2c70b2ddcc6f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.m new file mode 100644 index 0000000..be8a361 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.m @@ -0,0 +1,342 @@ +// +// TDPresetProperties+TDDisProperties.m +// ThinkingSDK +// +// Created by wwango on 2021/12/7. +// + +#import "TDPresetProperties+TDDisProperties.h" + +static BOOL _td_disableOpsReceiptProperties; +static BOOL _td_disableStartReason; +static BOOL _td_disableDisk; +static BOOL _td_disableRAM; +static BOOL _td_disableFPS; +static BOOL _td_disableSimulator; +static BOOL _td_disableAppVersion; +static BOOL _td_disableOsVersion; +static BOOL _td_disableManufacturer; +static BOOL _td_disableDeviceModel; +static BOOL _td_disableScreenHeight; +static BOOL _td_disableScreenWidth; +static BOOL _td_disableCarrier; +static BOOL _td_disableDeviceId; +static BOOL _td_disableSystemLanguage; +static BOOL _td_disableLib; +static BOOL _td_disableLibVersion; +static BOOL _td_disableBundleId; +static BOOL _td_disableOs; +static BOOL _td_disableInstallTime; +static BOOL _td_disableDeviceType; +static BOOL _td_disableSessionID; +static BOOL _td_disableCalibratedTime; + +static BOOL _td_disableNetworkType; +static BOOL _td_disableZoneOffset; +static BOOL _td_disableDuration; +static BOOL _td_disableBackgroundDuration; +static BOOL _td_disableAppCrashedReason; +static BOOL _td_disableResumeFromBackground; +static BOOL _td_disableElementId; +static BOOL _td_disableElementType; +static BOOL _td_disableElementContent; +static BOOL _td_disableElementPosition; +static BOOL _td_disableElementSelector; +static BOOL _td_disableScreenName; +static BOOL _td_disableTitle; +static BOOL _td_disableUrl; +static BOOL _td_disableReferrer; + + +// 推送事件名 +static const NSString *kTDPushInfo = @"ops_push_click"; + +// - 禁用功能并过滤字段拼接 +static const NSString *kTDStartReason = @"#start_reason"; +static const NSString *kTDPerformanceRAM = @"#ram"; +static const NSString *kTDPerformanceDISK = @"#disk"; +static const NSString *kTDPerformanceSIM = @"#simulator"; +static const NSString *kTDPerformanceFPS = @"#fps"; +static const NSString *kTDPresentAppVersion = @"#app_version"; +static const NSString *kTDPresentOsVersion = @"#os_version"; +static const NSString *kTDPresentManufacturer = @"#manufacturer"; +static const NSString *kTDPresentDeviceModel = @"#device_model"; +static const NSString *kTDPresentScreenHeight = @"#screen_height"; +static const NSString *kTDPresentScreenWidth = @"#screen_width"; +static const NSString *kTDPresentCarrier = @"#carrier"; +static const NSString *kTDPresentDeviceId = @"#device_id"; +static const NSString *kTDPresentSystemLanguage = @"#system_language"; +static const NSString *kTDPresentLib = @"#lib"; +static const NSString *kTDPresentLibVersion = @"#lib_version"; +static const NSString *kTDPresentOs = @"#os"; +static const NSString *kTDPresentBundleId = @"#bundle_id"; +static const NSString *kTDPresentInstallTime = @"#install_time"; +static const NSString *kTDPresentDeviceType = @"#device_type"; +static const NSString *kTDPresentSessionID = @"#session_id"; +static const NSString *kTDPresentCalibratedTime = @"#time_calibration"; + + +// - 只过滤字段 +static const NSString *kTDPresentNETWORKTYPE = @"#network_type"; +static const NSString *kTDPresentZONEOFFSET = @"#zone_offset"; +static const NSString *kTDPresentDURATION = @"#duration"; +static const NSString *kTDPresentBACKGROUNDDURATION = @"#background_duration"; +static const NSString *kTDPresentCRASHREASON = @"#app_crashed_reason"; +static const NSString *kTDPresentRESUMEFROMBACKGROUND = @"#resume_from_background"; +static const NSString *kTDPresentELEMENTID = @"#element_id"; +static const NSString *kTDPresentELEMENTTYPE = @"#element_type"; +static const NSString *kTDPresentELEMENTCONTENT = @"#element_content"; +static const NSString *kTDPresentELEMENTPOSITION = @"#element_position"; +static const NSString *kTDPresentELEMENTSELECTOR = @"#element_selector"; +static const NSString *kTDPresentSCREENNAME = @"#screen_name"; +static const NSString *kTDPresentTITLE = @"#title"; +static const NSString *kTDPresentURL = @"#url"; +static const NSString *kTDPresentREFERRER = @"#referrer"; +static const NSString *kTDPresentOpsReceiptProperties = @"#ops_receipt_properties"; + + + +#define TD_MAIM_INFO_PLIST_DISPRESTPRO_KEY @"TDDisPresetProperties" + +@implementation TDPresetProperties (TDDisProperties) + +static NSMutableArray *__td_disPresetProperties; + ++ (NSArray*)disPresetProperties { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + NSArray *disPresetProperties = (NSArray *)[[[NSBundle mainBundle] infoDictionary] objectForKey:TD_MAIM_INFO_PLIST_DISPRESTPRO_KEY]; + + if (disPresetProperties && disPresetProperties.count) { + __td_disPresetProperties = [NSMutableArray arrayWithArray:disPresetProperties]; + + if ([__td_disPresetProperties containsObject:kTDPresentZONEOFFSET]) { + [__td_disPresetProperties removeObject:kTDPresentZONEOFFSET]; + } + + _td_disableStartReason = [__td_disPresetProperties containsObject:kTDStartReason]; + _td_disableDisk = [__td_disPresetProperties containsObject:kTDPerformanceDISK]; + _td_disableRAM = [__td_disPresetProperties containsObject:kTDPerformanceRAM]; + _td_disableFPS = [__td_disPresetProperties containsObject:kTDPerformanceFPS]; + _td_disableSimulator = [__td_disPresetProperties containsObject:kTDPerformanceSIM]; + + _td_disableAppVersion = [__td_disPresetProperties containsObject:kTDPresentAppVersion]; + _td_disableOsVersion = [__td_disPresetProperties containsObject:kTDPresentOsVersion]; + _td_disableManufacturer = [__td_disPresetProperties containsObject:kTDPresentManufacturer]; + _td_disableDeviceModel = [__td_disPresetProperties containsObject:kTDPresentDeviceModel]; + _td_disableScreenHeight = [__td_disPresetProperties containsObject:kTDPresentScreenHeight]; + _td_disableScreenWidth = [__td_disPresetProperties containsObject:kTDPresentScreenWidth]; + _td_disableCarrier = [__td_disPresetProperties containsObject:kTDPresentCarrier]; + _td_disableDeviceId = [__td_disPresetProperties containsObject:kTDPresentDeviceId]; + _td_disableSystemLanguage = [__td_disPresetProperties containsObject:kTDPresentSystemLanguage]; + _td_disableLib = [__td_disPresetProperties containsObject:kTDPresentLib]; + _td_disableLibVersion = [__td_disPresetProperties containsObject:kTDPresentLibVersion]; + _td_disableBundleId = [__td_disPresetProperties containsObject:kTDPresentBundleId]; + _td_disableOs = [__td_disPresetProperties containsObject:kTDPresentOs]; + _td_disableInstallTime = [__td_disPresetProperties containsObject:kTDPresentInstallTime]; + _td_disableDeviceType = [__td_disPresetProperties containsObject:kTDPresentDeviceType]; + //_td_disableSessionID = [__td_disPresetProperties containsObject:kTDPresentSessionID]; + //_td_disableCalibratedTime = [__td_disPresetProperties containsObject:kTDPresentCalibratedTime]; + _td_disableSessionID = YES; + _td_disableCalibratedTime = YES; + + + _td_disableNetworkType = [__td_disPresetProperties containsObject:kTDPresentNETWORKTYPE]; + _td_disableZoneOffset = [__td_disPresetProperties containsObject:kTDPresentZONEOFFSET]; + _td_disableDuration = [__td_disPresetProperties containsObject:kTDPresentDURATION]; + _td_disableBackgroundDuration = [__td_disPresetProperties containsObject:kTDPresentBACKGROUNDDURATION]; + _td_disableAppCrashedReason = [__td_disPresetProperties containsObject:kTDPresentCRASHREASON]; + _td_disableResumeFromBackground = [__td_disPresetProperties containsObject:kTDPresentRESUMEFROMBACKGROUND]; + _td_disableElementId = [__td_disPresetProperties containsObject:kTDPresentELEMENTID]; + _td_disableElementType = [__td_disPresetProperties containsObject:kTDPresentELEMENTTYPE]; + _td_disableElementContent = [__td_disPresetProperties containsObject:kTDPresentELEMENTCONTENT]; + _td_disableElementPosition = [__td_disPresetProperties containsObject:kTDPresentELEMENTPOSITION]; + _td_disableElementSelector = [__td_disPresetProperties containsObject:kTDPresentELEMENTSELECTOR]; + _td_disableScreenName = [__td_disPresetProperties containsObject:kTDPresentSCREENNAME]; + _td_disableTitle = [__td_disPresetProperties containsObject:kTDPresentTITLE]; + _td_disableUrl = [__td_disPresetProperties containsObject:kTDPresentURL]; + _td_disableReferrer = [__td_disPresetProperties containsObject:kTDPresentREFERRER]; + _td_disableOpsReceiptProperties = [__td_disPresetProperties containsObject:kTDPresentOpsReceiptProperties]; + + + } + }); + return __td_disPresetProperties; +} + + ++ (void)handleFilterDisPresetProperties:(NSMutableDictionary *)dataDic +{ + if (!__td_disPresetProperties || !__td_disPresetProperties.count) { + return ; + } + NSArray *propertykeys = dataDic.allKeys; + NSArray *registerkeys = [TDPresetProperties disPresetProperties]; + NSMutableSet *set1 = [NSMutableSet setWithArray:propertykeys]; + NSMutableSet *set2 = [NSMutableSet setWithArray:registerkeys]; + [set1 intersectSet:set2];// 求交集 + if (!set1.allObjects.count) { + return ; + } + [dataDic removeObjectsForKeys:set1.allObjects]; + return ; +} + + ++ (BOOL)disableOpsReceiptProperties { + return _td_disableOpsReceiptProperties; +} + ++ (BOOL)disableStartReason { + return _td_disableStartReason; +} + ++ (BOOL)disableDisk { + return _td_disableDisk; +} + ++ (BOOL)disableRAM { + return _td_disableRAM; +} + ++ (BOOL)disableFPS { + return _td_disableFPS; +} + ++ (BOOL)disableSimulator { + return _td_disableSimulator; +} + + + + ++ (BOOL)disableAppVersion { + return _td_disableAppVersion; +} + ++ (BOOL)disableOsVersion { + return _td_disableOsVersion; +} + ++ (BOOL)disableManufacturer { + return _td_disableManufacturer; +} + ++ (BOOL)disableDeviceId { + return _td_disableDeviceId; +} + ++ (BOOL)disableDeviceModel { + return _td_disableDeviceModel; +} + ++ (BOOL)disableScreenHeight { + return _td_disableScreenHeight; +} + ++ (BOOL)disableScreenWidth { + return _td_disableScreenWidth; +} + ++ (BOOL)disableCarrier { + return _td_disableCarrier; +} + ++ (BOOL)disableSystemLanguage { + return _td_disableSystemLanguage; +} + ++ (BOOL)disableLib { + return _td_disableLib; +} + ++ (BOOL)disableLibVersion { + return _td_disableLibVersion; +} + ++ (BOOL)disableOs { + return _td_disableOs; +} + ++ (BOOL)disableBundleId { + return _td_disableBundleId; +} + ++ (BOOL)disableInstallTime { + return _td_disableInstallTime; +} + ++ (BOOL)disableDeviceType { + return _td_disableDeviceType; +} + ++ (BOOL)disableNetworkType { + return _td_disableNetworkType; +} + ++ (BOOL)disableZoneOffset { + return _td_disableZoneOffset; +} + ++ (BOOL)disableDuration { + return _td_disableDuration; +} + ++ (BOOL)disableBackgroundDuration { + return _td_disableBackgroundDuration; +} + ++ (BOOL)disableAppCrashedReason { + return _td_disableAppCrashedReason; +} + ++ (BOOL)disableResumeFromBackground { + return _td_disableResumeFromBackground; +} + ++ (BOOL)disableElementId { + return _td_disableElementId; +} + ++ (BOOL)disableElementType { + return _td_disableElementType; +} + ++ (BOOL)disableElementContent { + return _td_disableElementContent; +} + ++ (BOOL)disableElementPosition { + return _td_disableElementPosition; +} + ++ (BOOL)disableElementSelector { + return _td_disableElementSelector; +} + ++ (BOOL)disableScreenName { + return _td_disableScreenName; +} + ++ (BOOL)disableTitle { + return _td_disableTitle; +} + ++ (BOOL)disableUrl { + return _td_disableUrl; +} + ++ (BOOL)disableReferrer { + return _td_disableReferrer; +} + ++ (BOOL)disableSessionID { + return _td_disableSessionID; +} + ++ (BOOL)disableCalibratedTime { + return _td_disableCalibratedTime; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.m.meta new file mode 100644 index 0000000..bac4ddf --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties+TDDisProperties.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e86fd2feb4c097446a73f152c1db5837 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.h new file mode 100644 index 0000000..0848f23 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.h @@ -0,0 +1,36 @@ +// +// TDPresetProperties.h +// ThinkingSDK +// +// Created by huangdiao on 2021/5/25. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDPresetProperties : NSObject + +@property (nonatomic, copy, readonly) NSString *bundle_id; +@property (nonatomic, copy, readonly) NSString *carrier; +@property (nonatomic, copy, readonly) NSString *device_id; +@property (nonatomic, copy, readonly) NSString *device_model; +@property (nonatomic, copy, readonly) NSString *manufacturer; +@property (nonatomic, copy, readonly) NSString *network_type; +@property (nonatomic, copy, readonly) NSString *os; +@property (nonatomic, copy, readonly) NSString *os_version; +@property (nonatomic, copy, readonly) NSNumber *screen_height; +@property (nonatomic, copy, readonly) NSNumber *screen_width; +@property (nonatomic, copy, readonly) NSString *system_language; +@property (nonatomic, copy, readonly) NSNumber *zone_offset; +@property (nonatomic, copy, readonly) NSString *install_time; + +/** + * The key of the returned event preset property starts with "#", and it is not recommended to use it directly as the property of the event + */ +- (NSDictionary *)toEventPresetProperties; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.h.meta new file mode 100644 index 0000000..fda9eb8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: be2a7a171219eae44818a7bd33a279b3 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.m new file mode 100644 index 0000000..c15cf88 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.m @@ -0,0 +1,88 @@ +// +// TDPresetProperties.m +// ThinkingSDK +// +// Created by huangdiao on 2021/5/25. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDPresetProperties.h" +#import "TDPresetProperties+TDDisProperties.h" + +@interface TDPresetProperties () + +@property (nonatomic, copy, readwrite) NSString *bundle_id; +@property (nonatomic, copy, readwrite) NSString *carrier; +@property (nonatomic, copy, readwrite) NSString *device_id; +@property (nonatomic, copy, readwrite) NSString *device_model; +@property (nonatomic, copy, readwrite) NSString *manufacturer; +@property (nonatomic, copy, readwrite) NSString *network_type; +@property (nonatomic, copy, readwrite) NSString *os; +@property (nonatomic, copy, readwrite) NSString *os_version; +@property (nonatomic, copy, readwrite) NSNumber *screen_height; +@property (nonatomic, copy, readwrite) NSNumber *screen_width; +@property (nonatomic, copy, readwrite) NSString *system_language; +@property (nonatomic, copy, readwrite) NSNumber *zone_offset; +@property (nonatomic, copy, readwrite) NSString *install_time; + +@property (nonatomic, copy) NSDictionary *presetProperties; + +@end + +@implementation TDPresetProperties + +- (instancetype)initWithDictionary:(NSDictionary *)dict { + self = [super init]; + if (self) { + [self updateValuesWithDictionary:dict]; + } + return self; +} + + + +- (void)updateValuesWithDictionary:(NSDictionary *)dict { + _bundle_id = dict[@"#bundle_id"]?:@""; + _carrier = dict[@"#carrier"]?:@""; + _device_id = dict[@"#device_id"]?:@""; + _device_model = dict[@"#device_model"]?:@""; + _manufacturer = dict[@"#manufacturer"]?:@""; + _network_type = dict[@"#network_type"]?:@""; + _os = dict[@"#os"]?:@""; + _os_version = dict[@"#os_version"]?:@""; + _screen_height = dict[@"#screen_height"]?:@(0); + _screen_width = dict[@"#screen_width"]?:@(0); + _system_language = dict[@"#system_language"]?:@""; + _zone_offset = dict[@"#zone_offset"]?:@(0); + _install_time = dict[@"#install_time"]?:@""; + + _presetProperties = [NSDictionary dictionaryWithDictionary:dict]; + + NSMutableDictionary *updateProperties = [_presetProperties mutableCopy]; + NSArray *propertykeys = updateProperties.allKeys; + NSArray *registerkeys = [TDPresetProperties disPresetProperties]; + NSMutableSet *set1 = [NSMutableSet setWithArray:propertykeys]; + NSMutableSet *set2 = [NSMutableSet setWithArray:registerkeys]; + [set1 intersectSet:set2]; + if (set1.allObjects.count) { + [updateProperties removeObjectsForKeys:set1.allObjects]; + } + + if ([updateProperties.allKeys containsObject:@"#lib"]) { + [updateProperties removeObjectForKey:@"#lib"]; + } + if ([updateProperties.allKeys containsObject:@"#lib_version"]) { + [updateProperties removeObjectForKey:@"#lib_version"]; + } + if ([updateProperties.allKeys containsObject:@"#time_calibration"]) { + [updateProperties removeObjectForKey:@"#time_calibration"]; + } + + _presetProperties = updateProperties; +} + +- (NSDictionary *)toEventPresetProperties { + return [_presetProperties copy]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.m.meta new file mode 100644 index 0000000..fe02925 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PresetProperty/TDPresetProperties.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 63af3e2b94b0a3f428bf23cb7e8a8f68 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins.meta new file mode 100644 index 0000000..58315d6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8956e17887d5e364d87791db095f5edf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.h new file mode 100644 index 0000000..9fc76db --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.h @@ -0,0 +1,23 @@ +// +// TAPresetPropertyPlugin.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#if TARGET_OS_IOS +#import +#endif +#import "TAPropertyPluginManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAPresetPropertyPlugin : NSObject + +@property(nonatomic, copy)NSString *instanceToken; + +@property (nonatomic, strong) NSTimeZone *defaultTimeZone; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.h.meta new file mode 100644 index 0000000..5521e5f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 000e3e0ae2b22e24c8f5a87332343565 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.m new file mode 100644 index 0000000..e2d7801 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.m @@ -0,0 +1,61 @@ +// +// TAPresetPropertyPlugin.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TAPresetPropertyPlugin.h" +#import "TDPresetProperties.h" +#import "TDPresetProperties+TDDisProperties.h" +#import "TDDeviceInfo.h" +#import "TAReachability.h" +#import "NSDate+TAFormat.h" + +@interface TAPresetPropertyPlugin () +@property (nonatomic, strong) NSMutableDictionary *properties; + +@end + +@implementation TAPresetPropertyPlugin + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.properties = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)start { + if (![TDPresetProperties disableAppVersion]) { + self.properties[@"#app_version"] = [TDDeviceInfo sharedManager].appVersion; + } + if (![TDPresetProperties disableBundleId]) { + self.properties[@"#bundle_id"] = [TDDeviceInfo bundleId]; + } + + if (![TDPresetProperties disableInstallTime]) { + NSString *timeString = [[TDDeviceInfo td_getInstallTime] ta_formatWithTimeZone:self.defaultTimeZone formatString: @"yyyy-MM-dd HH:mm:ss.SSS"]; + if (timeString && [timeString isKindOfClass:[NSString class]] && timeString.length){ + self.properties[@"#install_time"] = timeString; + } + } +} + +- (void)asyncGetPropertyCompletion:(TAPropertyPluginCompletion)completion { + NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary]; + + [mutableDict addEntriesFromDictionary:[[TDDeviceInfo sharedManager] getAutomaticData]]; + + if (![TDPresetProperties disableNetworkType]) { + mutableDict[@"#network_type"] = [[TAReachability shareInstance] networkState]; + } + + if (completion) { + completion(mutableDict); + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.m.meta new file mode 100644 index 0000000..e0ae1a5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPresetPropertyPlugin.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0d82a3ca4f3430e48ba011b309387866 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.h new file mode 100644 index 0000000..3107480 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.h @@ -0,0 +1,42 @@ +// +// TAPropertyPluginManager.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import +#import "TABaseEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^TAPropertyPluginCompletion)(NSDictionary *properties); + +@protocol TAPropertyPluginProtocol + +@property(nonatomic, copy)NSString *instanceToken; + +- (NSDictionary *)properties; + +@optional + +- (void)start; + +- (TAEventType)eventTypeFilter; + +- (void)asyncGetPropertyCompletion:(TAPropertyPluginCompletion)completion; + +@end + + +@interface TAPropertyPluginManager : NSObject + +- (void)registerPropertyPlugin:(id)plugin; + +- (NSMutableDictionary *)currentPropertiesForPluginClasses:(NSArray *)classes; + +- (NSMutableDictionary *)propertiesWithEventType:(TAEventType)type; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.h.meta new file mode 100644 index 0000000..bdffa3f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b18a39c791368ed428e8fbc76a89de00 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.m new file mode 100644 index 0000000..7795350 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.m @@ -0,0 +1,127 @@ +// +// TAPropertyPluginManager.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TAPropertyPluginManager.h" + +@interface TAPropertyPluginManager () +@property (nonatomic, strong) NSMutableArray> *plugins; + +@end + + +@implementation TAPropertyPluginManager + +//MARK: - Public Methods + +- (instancetype)init { + self = [super init]; + if (self) { + self.plugins = [NSMutableArray array]; + } + return self; +} + +- (void)registerPropertyPlugin:(id)plugin { + BOOL isResponds = [plugin respondsToSelector:@selector(properties)]; + NSAssert(isResponds, @"properties plugin must implement `- properties` method!"); + if (!isResponds) { + return; + } + + // delete old plugin + for (id object in self.plugins) { + if (object.class == plugin.class) { + [self.plugins removeObject:object]; + break; + } + } + [self.plugins addObject:plugin]; + + + if ([plugin respondsToSelector:@selector(start)]) { + [plugin start]; + } +} + +- (NSMutableDictionary *)currentPropertiesForPluginClasses:(NSArray *)classes { + NSArray *plugins = [self.plugins copy]; + NSMutableArray> *matchResult = [NSMutableArray array]; + + for (id obj in plugins) { + + for (Class cla in classes) { + if ([obj isKindOfClass:cla]) { + [matchResult addObject:obj]; + break; + } + } + } + + NSMutableDictionary *pluginProperties = [self propertiesWithPlugins:matchResult]; + + return pluginProperties; +} + +- (NSMutableDictionary *)propertiesWithEventType:(TAEventType)type { + + NSArray *plugins = [self.plugins copy]; + NSMutableArray> *matchResult = [NSMutableArray array]; + for (id obj in plugins) { + if ([self isMatchedWithPlugin:obj eventType:type]) { + [matchResult addObject:obj]; + } + } + return [self propertiesWithPlugins:matchResult]; +} + +//MARK: - Private Methods + +- (NSMutableDictionary *)propertiesWithPlugins:(NSArray> *)plugins { + NSMutableDictionary *properties = [NSMutableDictionary dictionary]; + + dispatch_semaphore_t semaphore; + for (id plugin in plugins) { + if ([plugin respondsToSelector:@selector(asyncGetPropertyCompletion:)]) { + + semaphore = dispatch_semaphore_create(0); + [plugin asyncGetPropertyCompletion:^(NSDictionary * _Nonnull dict) { + [properties addEntriesFromDictionary:dict]; + dispatch_semaphore_signal(semaphore); + }]; + } + + NSDictionary *pluginProperties = [plugin respondsToSelector:@selector(properties)] ? plugin.properties : nil; + if (pluginProperties) { + [properties addEntriesFromDictionary:pluginProperties]; + } + if (semaphore) { + + dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC))); + } + + semaphore = nil; + } + return properties; +} + +- (BOOL)isMatchedWithPlugin:(id)plugin eventType:(TAEventType)type { + TAEventType eventTypeFilter; + + if (![plugin respondsToSelector:@selector(eventTypeFilter)]) { + // If the plug-in does not implement the type filtering method, it will only be added for track type data by default, including the first event, updateable event, and rewritable event. In addition to user attribute events + eventTypeFilter = TAEventTypeTrack | TAEventTypeTrackFirst | TAEventTypeTrackUpdate | TAEventTypeTrackOverwrite; + } else { + eventTypeFilter = plugin.eventTypeFilter; + } + + if ((eventTypeFilter & type) == type) { + return YES; + } + return NO; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.m.meta new file mode 100644 index 0000000..3bb5f1c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TAPropertyPluginManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c111649cbe2bc7147ae29d6d1c34414c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.h new file mode 100644 index 0000000..0fd05ae --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.h @@ -0,0 +1,19 @@ +// +// TASessionIdManager.h +// ThinkingSDK +// +// Created by Charles on 6.12.22. +// + +#import + + +NS_ASSUME_NONNULL_BEGIN + +@interface TASessionIdManager : NSObject + ++ (instancetype)shareInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.h.meta new file mode 100644 index 0000000..e04a032 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6dbe7ef45b4d88747904e89fa268be52 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.m new file mode 100644 index 0000000..4110b90 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.m @@ -0,0 +1,75 @@ +// +// TASessionIdManager.m +// ThinkingSDK +// +// Created by Charles on 6.12.22. +// + +#if TARGET_OS_IOS +#import +#endif +#import "TASessionIdManager.h" +#import "TAAppLifeCycle.h" +#import "TDAppState.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +@implementation TASessionIdManager + ++ (instancetype)shareInstance { + static dispatch_once_t onceToken; + static id instance = nil; + dispatch_once(&onceToken, ^{ + instance = [[[self class] alloc] init]; + }); + return instance; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + [self registerAppLifeCycleListener]; + } + return self; +} + +- (void)registerAppLifeCycleListener { + if ([TDAppState runningInAppExtension]) { + return; + } + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; +#if TARGET_OS_IOS + + [notificationCenter addObserver:self selector:@selector(appStateWillChangeNotification:) name:kTAAppLifeCycleStateWillChangeNotification object:nil]; + +#endif +} + + +- (void)appStateWillChangeNotification:(NSNotification *)notification { + TAAppLifeCycleState newState = [[notification.userInfo objectForKey:kTAAppLifeCycleNewStateKey] integerValue]; + TAAppLifeCycleState oldState = [[notification.userInfo objectForKey:kTAAppLifeCycleOldStateKey] integerValue]; + + if (oldState == TAAppLifeCycleStateInit) { + return; + } + + if (newState == TAAppLifeCycleStateStart) { + @synchronized ([self class]) { + [self updateSessionId]; + } + } +} + +- (void)updateSessionId { +// NSMutableDictionary *dic = [ThinkingAnalyticsSDK _getAllInstances]; +// for (NSString *instanceToken in dic.allKeys) { +// ThinkingAnalyticsSDK *instance = dic[instanceToken]; +// if ([instance isKindOfClass:[ThinkingAnalyticsSDK class]]) { +// [instance.sessionidPlugin updateSessionId]; +// } +// } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.m.meta new file mode 100644 index 0000000..39c442a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d337a51411d7b0f4e913089deb7a0f89 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.h new file mode 100644 index 0000000..5e500e4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.h @@ -0,0 +1,23 @@ +// +// TASessionIdPropertyPlugin.h +// ThinkingSDK +// +// Created by Charles on 28.11.22. +// + +#if TARGET_OS_IOS +#import +#endif +#import "TAPropertyPluginManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TASessionIdPropertyPlugin : NSObject + +@property(nonatomic, copy)NSString *instanceToken; + +- (void)updateSessionId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.h.meta new file mode 100644 index 0000000..2892d8d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 42e00b90bf099a645987eb01277fb3c7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.m new file mode 100644 index 0000000..759bd8a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.m @@ -0,0 +1,66 @@ +// +// TASessionIdPropertyPlugin.m +// ThinkingSDK +// +// Created by Charles on 28.11.22. +// + +#import "TASessionIdPropertyPlugin.h" +#import "TDPresetProperties.h" +#import "TDPresetProperties+TDDisProperties.h" +#import "TAAppLifeCycle.h" +#import "TDFile.h" +#import "TDAppState.h" + +@interface TASessionIdPropertyPlugin () +@property (nonatomic, strong) NSMutableDictionary *properties; +@property (nonatomic, strong) TDFile *file; +@property (atomic, assign) long long sessionid; +@property (atomic, copy) NSString *sessionidString; +@end + + +@implementation TASessionIdPropertyPlugin + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.properties = [NSMutableDictionary dictionary]; + + } + return self; +} + +- (void)updateSessionId { +#if TARGET_OS_IOS + if (![TDPresetProperties disableSessionID]) { + @synchronized ([self class]) { + self.sessionid ++; + self.sessionidString = [NSString stringWithFormat:@"%@_%lld", [NSUUID UUID].UUIDString, self.sessionid]; + [self.file archiveSessionID:self.sessionid]; + self.properties[@"#session_id"] = self.sessionidString; + } + } +#endif +} + +- (void)start { + if (![TDPresetProperties disableSessionID]) { + @synchronized ([self class]) { + if (!self.file) { + self.file = [[TDFile alloc] initWithAppid:self.instanceToken]; + } + self.sessionid = [self.file unarchiveSessionID]; + [self updateSessionId]; + } + + } +} + +- (void)asyncGetPropertyCompletion:(TAPropertyPluginCompletion)completion { + +} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.m.meta new file mode 100644 index 0000000..ad23f9d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TASessionIdPropertyPlugin.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 720df558845bf294e80b241e86d6ab7f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.h new file mode 100644 index 0000000..f66ee5a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.h @@ -0,0 +1,34 @@ +// +// TAAutoTrackSuperProperty.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import +#import "TDConstant.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAAutoTrackSuperProperty : NSObject + +- (void)registerSuperProperties:(NSDictionary *)properties withType:(ThinkingAnalyticsAutoTrackEventType)type; + +- (NSDictionary *)currentSuperPropertiesWithEventName:(NSString *)eventName; + +- (void)registerDynamicSuperProperties:(NSDictionary *(^)(ThinkingAnalyticsAutoTrackEventType, NSDictionary *))dynamicSuperProperties; + +- (NSDictionary *)obtainDynamicSuperPropertiesWithType:(ThinkingAnalyticsAutoTrackEventType)type currentProperties:(NSDictionary *)properties; + +/// Only used for auto track in Unity3D environment +/// - Parameter dynamicSuperProperties: dynamic properties +- (void)registerAutoTrackDynamicProperties:(NSDictionary *(^ _Nullable)(void))dynamicSuperProperties; + +/// Only used for auto track in Unity3D environment +- (NSDictionary *)obtainAutoTrackDynamicSuperProperties; + +- (void)clearSuperProperties; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.h.meta new file mode 100644 index 0000000..e0e58f7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 18d01d4c971ce524982ecc27bdd39870 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.m new file mode 100644 index 0000000..1c3454f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.m @@ -0,0 +1,121 @@ +// +// TAAutoTrackSuperProperty.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import "TAAutoTrackSuperProperty.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +@interface TAAutoTrackSuperProperty () +@property (atomic, strong) NSMutableDictionary *eventProperties; +@property (nonatomic, copy) NSDictionary *(^dynamicSuperProperties)(ThinkingAnalyticsAutoTrackEventType type, NSDictionary *properties); +/// Only used for auto track in Unity3D environment +@property (nonatomic, copy) NSDictionary *(^autoTrackDynamicSuperProperties)(void); + +@end + +@implementation TAAutoTrackSuperProperty + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.eventProperties = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)registerSuperProperties:(NSDictionary *)properties withType:(ThinkingAnalyticsAutoTrackEventType)type { + NSDictionary *autoTypes = @{ + @(ThinkingAnalyticsEventTypeAppStart) : TD_APP_START_EVENT, + @(ThinkingAnalyticsEventTypeAppEnd) : TD_APP_END_EVENT, + @(ThinkingAnalyticsEventTypeAppClick) : TD_APP_CLICK_EVENT, + @(ThinkingAnalyticsEventTypeAppInstall) : TD_APP_INSTALL_EVENT, + @(ThinkingAnalyticsEventTypeAppViewCrash) : TD_APP_CRASH_EVENT, + @(ThinkingAnalyticsEventTypeAppViewScreen) : TD_APP_VIEW_EVENT + }; + + NSArray *typeKeys = autoTypes.allKeys; + for (NSInteger i = 0; i < typeKeys.count; i++) { + NSNumber *key = typeKeys[i]; + ThinkingAnalyticsAutoTrackEventType eventType = key.integerValue; + if ((type & eventType) == eventType) { + NSString *eventName = autoTypes[key]; + if (properties) { + + NSDictionary *oldProperties = self.eventProperties[eventName]; + if (oldProperties && [oldProperties isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *mutiOldProperties = [oldProperties mutableCopy]; + [mutiOldProperties addEntriesFromDictionary:properties]; + self.eventProperties[eventName] = mutiOldProperties; + } else { + self.eventProperties[eventName] = properties; + } + + + if (eventType == ThinkingAnalyticsEventTypeAppStart) { + NSDictionary *startParam = self.eventProperties[TD_APP_START_EVENT]; + if (startParam && [startParam isKindOfClass:[NSDictionary class]]) { + self.eventProperties[TD_APP_START_BACKGROUND_EVENT] = startParam; + } + } + } + } + } +} + + +- (NSDictionary *)currentSuperPropertiesWithEventName:(NSString *)eventName { + NSDictionary *autoEventProperty = [self.eventProperties objectForKey:eventName]; + + NSDictionary *validProperties = [TAPropertyValidator validateProperties:[autoEventProperty copy]]; + return validProperties; +} + +- (void)registerDynamicSuperProperties:(NSDictionary *(^)(ThinkingAnalyticsAutoTrackEventType, NSDictionary *))dynamicSuperProperties { + @synchronized (self) { + self.dynamicSuperProperties = dynamicSuperProperties; + } +} + +- (NSDictionary *)obtainDynamicSuperPropertiesWithType:(ThinkingAnalyticsAutoTrackEventType)type currentProperties:(NSDictionary *)properties { + @synchronized (self) { + if (self.dynamicSuperProperties) { + NSDictionary *result = self.dynamicSuperProperties(type, properties); + + NSDictionary *validProperties = [TAPropertyValidator validateProperties:[result copy]]; + return validProperties; + } + return nil; + } +} + +- (void)registerAutoTrackDynamicProperties:(NSDictionary * _Nonnull (^)(void))dynamicSuperProperties { + @synchronized (self) { + self.autoTrackDynamicSuperProperties = dynamicSuperProperties; + } +} + +- (NSDictionary *)obtainAutoTrackDynamicSuperProperties { + @synchronized (self) { + if (self.autoTrackDynamicSuperProperties) { + NSDictionary *properties = self.autoTrackDynamicSuperProperties(); + + NSDictionary *validProperties = [TAPropertyValidator validateProperties:[properties copy]]; + return validProperties; + } + return nil; + } +} + +- (void)clearSuperProperties { + self.eventProperties = [@{} mutableCopy]; +} + +//MARK: - Private Methods + + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.m.meta new file mode 100644 index 0000000..4418cd8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TAAutoTrackSuperProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8a0a6059a7aa043478b3f9d5aa546030 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.h new file mode 100644 index 0000000..c53045c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.h @@ -0,0 +1,35 @@ +// +// TASuperProperty.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/10. +// +// Methods related to static public properties of this class are not thread-safe; methods related to dynamic public properties are thread-safe. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TASuperProperty : NSObject + +#pragma mark - UNAVAILABLE +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +- (instancetype)initWithToken:(NSString *)token isLight:(BOOL)isLight; + +- (void)registerSuperProperties:(NSDictionary *)properties; + +- (void)unregisterSuperProperty:(NSString *)property; + +- (void)clearSuperProperties; + +- (NSDictionary *)currentSuperProperties; + +- (void)registerDynamicSuperProperties:(NSDictionary *(^ _Nullable)(void))dynamicSuperProperties; + +- (NSDictionary *)obtainDynamicSuperProperties; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.h.meta new file mode 100644 index 0000000..9591964 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: da3cad822fb23aa418f6587d2dc85768 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.m new file mode 100644 index 0000000..598fc08 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.m @@ -0,0 +1,106 @@ +// +// TASuperProperty.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/10. +// + +#import "TASuperProperty.h" +#import "TAPropertyValidator.h" +#import "TDLogging.h" +#import "TDFile.h" + +@interface TASuperProperty () +///multi-instance identifier +@property (nonatomic, copy) NSString *token; +/// static public property +@property (atomic, strong) NSDictionary *superProperties; +/// dynamic public properties +@property (nonatomic, copy) NSDictionary *(^dynamicSuperProperties)(void); +@property (nonatomic, strong) TDFile *file; +@property (nonatomic, assign) BOOL isLight; + +@end + +@implementation TASuperProperty + +- (instancetype)initWithToken:(NSString *)token isLight:(BOOL)isLight { + if (self = [super init]) { + NSAssert(token.length > 0, @"token cant empty"); + self.token = token; + self.isLight = isLight; + if (!isLight) { + + self.file = [[TDFile alloc] initWithAppid:token]; + self.superProperties = [self.file unarchiveSuperProperties]; + } + } + return self; +} + +- (void)registerSuperProperties:(NSDictionary *)properties { + properties = [properties copy]; + + properties = [TAPropertyValidator validateProperties:properties]; + if (properties.count <= 0) { + TDLogError(@"%@ propertieDict error.", properties); + return; + } + + + NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithDictionary:self.superProperties]; + + [tmp addEntriesFromDictionary:properties]; + self.superProperties = [NSDictionary dictionaryWithDictionary:tmp]; + + + [self.file archiveSuperProperties:self.superProperties]; +} + +- (void)unregisterSuperProperty:(NSString *)property { + NSError *error = nil; + [TAPropertyValidator validateEventOrPropertyName:property withError:&error]; + if (error) { + return; + } + + NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithDictionary:self.superProperties]; + tmp[property] = nil; + self.superProperties = [NSDictionary dictionaryWithDictionary:tmp]; + + [self.file archiveSuperProperties:self.superProperties]; +} + +- (void)clearSuperProperties { + self.superProperties = @{}; + [self.file archiveSuperProperties:self.superProperties]; +} + +- (NSDictionary *)currentSuperProperties { + if (self.superProperties) { + return [TAPropertyValidator validateProperties:[self.superProperties copy]]; + } else { + return @{}; + } +} + +- (void)registerDynamicSuperProperties:(NSDictionary *(^ _Nullable)(void))dynamicSuperProperties { + @synchronized (self) { + self.dynamicSuperProperties = dynamicSuperProperties; + } +} + + +- (NSDictionary *)obtainDynamicSuperProperties { + @synchronized (self) { + if (self.dynamicSuperProperties) { + NSDictionary *properties = self.dynamicSuperProperties(); + + NSDictionary *validProperties = [TAPropertyValidator validateProperties:[properties copy]]; + return validProperties; + } + return nil; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.m.meta new file mode 100644 index 0000000..c73a0b0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TASuperProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0d28f334c0ccb354295c108bd6fced89 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate.meta new file mode 100644 index 0000000..35a506e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4a20a7aa750a48b4f90277c818ca6fa7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.h new file mode 100644 index 0000000..a1b55c7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.h @@ -0,0 +1,17 @@ +// +// NSArray+TAProperty.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TAValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSArray (TAProperty) + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.h.meta new file mode 100644 index 0000000..d752463 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ec613b013ff5aef4e8aa28eaaaedac1c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.m new file mode 100644 index 0000000..070496d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.m @@ -0,0 +1,16 @@ +// +// NSArray+TAProperty.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "NSArray+TAProperty.h" + +@implementation NSArray (TAProperty) + +- (void)ta_validatePropertyValueWithError:(NSError *__autoreleasing _Nullable *)error { + +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.m.meta new file mode 100644 index 0000000..f68975f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TAProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 906fcbde20a55d942bbf71335d6e503a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.h new file mode 100644 index 0000000..7125b53 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.h @@ -0,0 +1,17 @@ +// +// NSDate+TAProperty.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TAValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSDate (TAProperty) + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.h.meta new file mode 100644 index 0000000..f6f7aa5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7815960cdb2892d43a211eabfeb5e693 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.m new file mode 100644 index 0000000..5c0809c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.m @@ -0,0 +1,16 @@ +// +// NSDate+TAProperty.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "NSDate+TAProperty.h" + +@implementation NSDate (TAProperty) + +- (void)ta_validatePropertyValueWithError:(NSError *__autoreleasing _Nullable *)error { + +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.m.meta new file mode 100644 index 0000000..4f25fc9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TAProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 369f72e6c8b3cd443a39b34386a7c4b2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.h new file mode 100644 index 0000000..7a4ad8b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.h @@ -0,0 +1,17 @@ +// +// NSDictionary+TAProperty.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TAValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSDictionary (TAProperty) + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.h.meta new file mode 100644 index 0000000..d377812 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7670f9711b4d48b4e992d002f831e032 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.m new file mode 100644 index 0000000..f7bf0ee --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.m @@ -0,0 +1,16 @@ +// +// NSDictionary+TAProperty.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "NSDictionary+TAProperty.h" + +@implementation NSDictionary (TAProperty) + +- (void)ta_validatePropertyValueWithError:(NSError *__autoreleasing _Nullable *)error { + +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.m.meta new file mode 100644 index 0000000..a84be74 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TAProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a0f6b32af78b7e74aa6e07ce63484fff +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.h new file mode 100644 index 0000000..010aa07 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.h @@ -0,0 +1,17 @@ +// +// NSNumber+TAProperty.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TAValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSNumber (TAProperty) + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.h.meta new file mode 100644 index 0000000..e92b0a9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b2891f0078a828f4fbd278fe48bc60bf +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.m new file mode 100644 index 0000000..89fd3ae --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.m @@ -0,0 +1,20 @@ +// +// NSNumber+TAProperty.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "NSNumber+TAProperty.h" + +@implementation NSNumber (TAProperty) + +- (void)ta_validatePropertyValueWithError:(NSError *__autoreleasing _Nullable *)error { + if ([self doubleValue] > 9999999999999.999 || [self doubleValue] < -9999999999999.999) { + NSString *errorMsg = [NSString stringWithFormat:@"The number value [%@] is invalid.", self]; + TDLogError(errorMsg); + *error = TAPropertyError(10009, errorMsg); + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.m.meta new file mode 100644 index 0000000..198d950 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TAProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 37a2d705dc4fb714d8748c79e8c13bf9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.h new file mode 100644 index 0000000..711dfa4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.h @@ -0,0 +1,17 @@ +// +// NSString+TAProperty.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TAValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSString (TAProperty) + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.h.meta new file mode 100644 index 0000000..502a970 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c5c6c8921f5d1304f8cd9de8249dff8a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.m new file mode 100644 index 0000000..5751a81 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.m @@ -0,0 +1,36 @@ +// +// NSString+TAProperty.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "NSString+TAProperty.h" + + +static NSInteger kTAPropertyNameMaxLength = 50; + +@implementation NSString (TAProperty) + +- (void)ta_validatePropertyKeyWithError:(NSError *__autoreleasing _Nullable *)error { + if (self.length == 0) { + NSString *errorMsg = @"Property key or Event name is empty"; + TDLogError(errorMsg); + *error = TAPropertyError(10003, errorMsg); + return; + } + + if (self.length > kTAPropertyNameMaxLength) { + NSString *errorMsg = [NSString stringWithFormat:@"Property key or Event name %@'s length is longer than %ld", self, kTAPropertyNameMaxLength]; + TDLogError(errorMsg); + *error = TAPropertyError(10006, errorMsg); + return; + } + *error = nil; +} + +- (void)ta_validatePropertyValueWithError:(NSError *__autoreleasing _Nullable *)error { + +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.m.meta new file mode 100644 index 0000000..f2d9596 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TAProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 04eda97ca3c873b43934892ac240713b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.h new file mode 100644 index 0000000..3d8ceb6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.h @@ -0,0 +1,17 @@ +// +// TAPropertyDefaultValidator.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TAValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAPropertyDefaultValidator : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.h.meta new file mode 100644 index 0000000..f3b54a9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b8c942cce0a6c0c41956903b4a222783 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.m new file mode 100644 index 0000000..8335d6c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.m @@ -0,0 +1,17 @@ +// +// TAPropertyDefaultValidator.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TAPropertyDefaultValidator.h" +#import "TAPropertyValidator.h" + +@implementation TAPropertyDefaultValidator + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + [TAPropertyValidator validateBaseEventPropertyKey:key value:value error:error]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.m.meta new file mode 100644 index 0000000..7f59d29 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyDefaultValidator.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 29d0ba676609b7b498d181a120c8957a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.h new file mode 100644 index 0000000..2dc1c7a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.h @@ -0,0 +1,30 @@ +// +// TAPropertyValidator.h +// Adjust +// +// Created by Yangxiongon 2022/6/10. +// + +#import +#import "TAValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAPropertyValidator : NSObject + ++ (void)validateEventOrPropertyName:(NSString *)name withError:(NSError **)error; + ++ (void)validateBaseEventPropertyKey:(NSString *)key value:(NSString *)value error:(NSError **)error; + ++ (void)validateNormalTrackEventPropertyKey:(NSString *)key value:(NSString *)value error:(NSError **)error; + ++ (void)validateAutoTrackEventPropertyKey:(NSString *)key value:(NSString *)value error:(NSError **)error; + + ++ (NSMutableDictionary *)validateProperties:(NSDictionary *)properties; + ++ (NSMutableDictionary *)validateProperties:(NSDictionary *)properties validator:(id)validator; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.h.meta new file mode 100644 index 0000000..63bf93e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5c9bdd8243e62ab469ee2435b4905767 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.m new file mode 100644 index 0000000..c6049b1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.m @@ -0,0 +1,133 @@ +// +// TAPropertyValidator.m +// Adjust +// +// Created by Yangxiongon 2022/6/10. +// + +#import "TAPropertyValidator.h" +#import "NSString+TAProperty.h" +#import "TAPropertyDefaultValidator.h" + +@implementation TAPropertyValidator + +/// Custom attribute name format validation +static NSString *const kTANormalTrackProperNameValidateRegularExpression = @"^[a-zA-Z][a-zA-Z\\d_]*$"; +/// Custom attribute name regularization +static NSRegularExpression *_regexForNormalTrackValidateKey; + +/// Automatic collection, custom attribute name format validation. All automatic collection of custom attributes needs to meet the following rules +static NSString *const kTAAutoTrackProperNameValidateRegularExpression = @"^([a-zA-Z][a-zA-Z\\d_]{0,49}|\\#(resume_from_background|app_crashed_reason|screen_name|referrer|title|url|element_id|element_type|element_content|element_position|background_duration|start_reason))$"; + +static NSRegularExpression *_regexForAutoTrackValidateKey; + ++ (void)validateEventOrPropertyName:(NSString *)name withError:(NSError *__autoreleasing _Nullable *)error { + if (!name) { + NSString *errorMsg = @"Property key or Event name is empty"; + TDLogError(errorMsg); + *error = TAPropertyError(10003, errorMsg); + return; + } + if (![name isKindOfClass:NSString.class]) { + NSString *errorMsg = [NSString stringWithFormat:@"Property key or Event name is not NSString: [%@]", name]; + TDLogError(errorMsg); + *error = TAPropertyError(10007, errorMsg); + return; + } + + [name ta_validatePropertyKeyWithError:error]; +} + ++ (void)validateBaseEventPropertyKey:(NSString *)key value:(NSString *)value error:(NSError **)error { + + if (![key conformsToProtocol:@protocol(TAPropertyKeyValidating)]) { + NSString *errMsg = [NSString stringWithFormat:@"The property KEY must be NSString. got: %@ %@", [key class], key]; + TDLogError(errMsg); + *error = TAPropertyError(10001, errMsg); + return; + } + [(id )key ta_validatePropertyKeyWithError:error]; + if (*error) { + return; + } + + + if (![value conformsToProtocol:@protocol(TAPropertyValueValidating)]) { + NSString *errMsg = [NSString stringWithFormat:@"Property value must be type NSString, NSNumber, NSDate, NSDictionary or NSArray. got: %@ %@. ", [value class], value]; + TDLogError(errMsg); + *error = TAPropertyError(10002, errMsg); + return; + } + [(id )value ta_validatePropertyValueWithError:error]; +} + ++ (void)validateNormalTrackEventPropertyKey:(NSString *)key value:(NSString *)value error:(NSError **)error { + [self validateBaseEventPropertyKey:key value:value error:error]; + if (*error) { + return; + } + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _regexForNormalTrackValidateKey = [NSRegularExpression regularExpressionWithPattern:kTANormalTrackProperNameValidateRegularExpression options:NSRegularExpressionCaseInsensitive error:nil]; + }); + if (!_regexForNormalTrackValidateKey) { + NSString *errorMsg = @"Property Key validate regular expression init failed"; + TDLogError(errorMsg); + *error = TAPropertyError(10004, errorMsg); + return; + } + NSRange range = NSMakeRange(0, key.length); + if ([_regexForNormalTrackValidateKey numberOfMatchesInString:key options:0 range:range] < 1) { + NSString *errorMsg = [NSString stringWithFormat:@"Property Key or Event name: [%@] is invalid.", key]; + TDLogError(errorMsg); + *error = TAPropertyError(10005, errorMsg); + return; + } +} + ++ (void)validateAutoTrackEventPropertyKey:(NSString *)key value:(NSString *)value error:(NSError **)error { + [self validateBaseEventPropertyKey:key value:value error:error]; + if (*error) { + return; + } + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _regexForAutoTrackValidateKey = [NSRegularExpression regularExpressionWithPattern:kTAAutoTrackProperNameValidateRegularExpression options:NSRegularExpressionCaseInsensitive error:nil]; + }); + if (!_regexForAutoTrackValidateKey) { + NSString *errorMsg = @"Property Key validate regular expression init failed"; + TDLogError(errorMsg); + *error = TAPropertyError(10004, errorMsg); + return; + } + NSRange range = NSMakeRange(0, key.length); + if ([_regexForAutoTrackValidateKey numberOfMatchesInString:key options:0 range:range] < 1) { + NSString *errorMsg = [NSString stringWithFormat:@"Property Key or Event name: [%@] is invalid.", key]; + TDLogError(errorMsg); + *error = TAPropertyError(10005, errorMsg); + return; + } +} + ++ (NSMutableDictionary *)validateProperties:(NSDictionary *)properties { + return [self validateProperties:properties validator:[[TAPropertyDefaultValidator alloc] init]]; +} + ++ (NSMutableDictionary *)validateProperties:(NSDictionary *)properties validator:(id)validator { + if (![properties isKindOfClass:[NSDictionary class]] || ![validator conformsToProtocol:@protocol(TAEventPropertyValidating)]) { + return nil; + } + + for (id key in properties) { + NSError *error = nil; + id value = properties[key]; + + + [validator ta_validateKey:key value:value error:&error]; + } + return [properties copy]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.m.meta new file mode 100644 index 0000000..deb0dda --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAPropertyValidator.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d497f9e3d392f73459dff3cfde70eb64 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAValidatorProtocol.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAValidatorProtocol.h new file mode 100644 index 0000000..58eab5a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAValidatorProtocol.h @@ -0,0 +1,45 @@ +// +// TAValidatorProtocol.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#ifndef TAValidatorProtocol_h +#define TAValidatorProtocol_h + +#import + +#if __has_include() +#import +#else +#import "TDLogging.h" +#endif + +#define TAPropertyError(errorCode, errorMsg) \ + [NSError errorWithDomain:@"ThinkingAnalyticsErrorDomain" \ + code:errorCode \ + userInfo:@{NSLocalizedDescriptionKey:errorMsg}] \ + + +@protocol TAPropertyKeyValidating + +- (void)ta_validatePropertyKeyWithError:(NSError **)error; + +@end + +/// The validator protocol of the attribute value, used to verify the attribute value +@protocol TAPropertyValueValidating + +- (void)ta_validatePropertyValueWithError:(NSError **)error; + +@end + +/// The validator protocol of event properties, used to verify the key-value of a certain property +@protocol TAEventPropertyValidating + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError **)error; + +@end + +#endif /* TAValidatorProtocol_h */ diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAValidatorProtocol.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAValidatorProtocol.h.meta new file mode 100644 index 0000000..60be723 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TAValidatorProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 76f89a638c88fa14185e509e0802c8db +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.h new file mode 100644 index 0000000..ed830fb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.h @@ -0,0 +1,45 @@ +// +// TAEventTracker.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import + +#if __has_include() +#import +#else +#import "TDConstant.h" +#endif + +#import "TDSecurityPolicy.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +NS_ASSUME_NONNULL_BEGIN + +@class TAEventTracker; + +@interface TAEventTracker : NSObject + ++ (dispatch_queue_t)td_networkQueue; + +- (instancetype)initWithQueue:(dispatch_queue_t)queue instanceToken:(NSString *)instanceToken; + +- (void)flush; + +- (void)track:(NSDictionary *)event immediately:(BOOL)immediately saveOnly:(BOOL)isSaveOnly; + +- (NSInteger)saveEventsData:(NSDictionary *)data; + +- (void)_asyncWithCompletion:(void(^)(void))completion; + +- (void)syncSendAllData; + +#pragma mark - UNAVAILABLE +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.h.meta new file mode 100644 index 0000000..176f382 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 015adc16d89e65b41957e1a942e13538 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.m new file mode 100644 index 0000000..7b7937a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.m @@ -0,0 +1,305 @@ +// +// TAEventTracker.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import "TAEventTracker.h" +#import "TANetwork.h" +#import "TAReachability.h" +#import "TDEventRecord.h" + +static dispatch_queue_t td_networkQueue; + +@interface TAEventTracker () +@property (atomic, strong) TANetwork *network; +@property (atomic, strong) TDConfig *config; +@property (atomic, strong) dispatch_queue_t queue; +@property (nonatomic, strong) TDSqliteDataQueue *dataQueue; + +@end + +@implementation TAEventTracker + ++ (void)initialize { + static dispatch_once_t ThinkingOnceToken; + dispatch_once(&ThinkingOnceToken, ^{ + NSString *queuelabel = [NSString stringWithFormat:@"cn.thinkingdata.%p", (void *)self]; + NSString *networkLabel = [queuelabel stringByAppendingString:@".network"]; + td_networkQueue = dispatch_queue_create([networkLabel UTF8String], DISPATCH_QUEUE_SERIAL); + }); +} + ++ (dispatch_queue_t)td_networkQueue { + return td_networkQueue; +} + +- (instancetype)initWithQueue:(dispatch_queue_t)queue instanceToken:(nonnull NSString *)instanceToken { + if (self = [self init]) { + self.queue = queue; + self.config = [ThinkingAnalyticsSDK sharedInstanceWithAppid:instanceToken].config; + self.network = [self generateNetworkWithConfig:self.config]; + self.dataQueue = [TDSqliteDataQueue sharedInstanceWithAppid:[self.config getMapInstanceToken]]; + } + return self; +} + +- (TANetwork *)generateNetworkWithConfig:(TDConfig *)config { + TANetwork *network = [[TANetwork alloc] init]; + network.debugMode = config.debugMode; + network.appid = config.appid; + network.sessionDidReceiveAuthenticationChallenge = config.securityPolicy.sessionDidReceiveAuthenticationChallenge; + network.serverURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/sync", config.configureURL]]; + network.serverDebugURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/data_debug", config.configureURL]]; + network.securityPolicy = config.securityPolicy; + return network; +} + +//MARK: - Public + +- (void)track:(NSDictionary *)event immediately:(BOOL)immediately saveOnly:(BOOL)isSaveOnly { + ThinkingAnalyticsDebugMode debugMode = self.config.debugMode; + NSInteger count = 0; + if (debugMode == ThinkingAnalyticsDebugOnly || debugMode == ThinkingAnalyticsDebug) { + + if (isSaveOnly) { + return; + } + TDLogDebug(@"queueing debug data: %@", event); + dispatch_async(self.queue, ^{ + dispatch_async(td_networkQueue, ^{ + [self flushDebugEvent:event]; + }); + }); + // ThinkingAnalyticsDebug Mode After the data is sent, it will still be stored locally, so it is necessary to query the database data to determine whether the number of records is sufficient for uploading + @synchronized (TDSqliteDataQueue.class) { + count = [self.dataQueue sqliteCountForAppid:[self.config getMapInstanceToken]]; + } + } else { + if (immediately) { + + if (isSaveOnly) { + return; + } + TDLogDebug(@"queueing data flush immediately:%@", event); + dispatch_async(self.queue, ^{ + dispatch_async(td_networkQueue, ^{ + [self flushImmediately:event]; + }); + }); + } else { + TDLogDebug(@"queueing data:%@", event); + count = [self saveEventsData:event]; + } + } + if (count >= [self.config.uploadSize integerValue]) { + + if (isSaveOnly) { + return; + } + TDLogDebug(@"flush action, count: %ld, uploadSize: %d",count, [self.config.uploadSize integerValue]); + [self flush]; + } +} + +- (void)flushImmediately:(NSDictionary *)event { + [self.network flushEvents:@[event]]; +} + +- (NSInteger)saveEventsData:(NSDictionary *)data { + NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary:data]; + NSInteger count = 0; + @synchronized (TDSqliteDataQueue.class) { + + if (_config.enableEncrypt) { +#if TARGET_OS_IOS + NSDictionary *encryptData = [[ThinkingAnalyticsSDK sharedInstanceWithAppid:self.config.appid].encryptManager encryptJSONObject:event]; + if (encryptData == nil) { + encryptData = event; + } + count = [self.dataQueue addObject:encryptData withAppid:[self.config getMapInstanceToken]]; +#elif TARGET_OS_OSX + count = [self.dataQueue addObject:event withAppid:[self.config getMapInstanceToken]]; +#endif + } else { + count = [self.dataQueue addObject:event withAppid:[self.config getMapInstanceToken]]; + } + } + return count; +} + +- (void)flushDebugEvent:(NSDictionary *)event { + if (self.config.debugMode == ThinkingAnalyticsDebug || self.config.debugMode == ThinkingAnalyticsDebugOnly) { + int debugResult = [self.network flushDebugEvents:event withAppid:self.config.appid]; + if (debugResult == -1) { + // Downgrade + if (self.config.debugMode == ThinkingAnalyticsDebug) { + dispatch_async(self.queue, ^{ + [self saveEventsData:event]; + }); + + self.config.debugMode = ThinkingAnalyticsDebugOff; + self.network.debugMode = ThinkingAnalyticsDebugOff; + } else if (self.config.debugMode == ThinkingAnalyticsDebugOnly) { + TDLogDebug(@"The data will be discarded due to this device is not allowed to debug:%@", event); + } + } + else if (debugResult == -2) { + TDLogDebug(@"Exception occurred when sending message to Server:%@", event); + if (self.config.debugMode == ThinkingAnalyticsDebug) { + + dispatch_async(self.queue, ^{ + [self saveEventsData:event]; + }); + } + } + } else { + + NSInteger count = [self saveEventsData:event]; + if (count >= [self.config.uploadSize integerValue]) { + [self flush]; + } + } +} + +- (void)flush { + [self _asyncWithCompletion:^{}]; +} + +/// Synchronize data asynchronously (synchronize data in the local database to TA) +/// Need to add this event to the serialQueue queue +/// In some scenarios, event warehousing and sending network requests happen at the same time. Event storage is performed in serialQueue, and data reporting is performed in networkQueue. To ensure that events are stored first, you need to add the reported data operation to serialQueue +- (void)_asyncWithCompletion:(void(^)(void))completion { + + void(^block)(void) = ^{ + dispatch_async(td_networkQueue, ^{ + [self _syncWithSize:kBatchSize completion:completion]; + }); + }; + if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(self.queue)) { + block(); + } else { + dispatch_async(self.queue, block); + } +} + +/// Synchronize data (synchronize the data in the local database to TA) +/// @param size The maximum number of items obtained from the database each time, the default is 50 +/// @param completion synchronous callback +/// This method needs to be performed in networkQueue, and will continue to send network requests until the data in the database is sent +- (void)_syncWithSize:(NSUInteger)size completion:(void(^)(void))completion { + + + NSString *networkType = [[TAReachability shareInstance] networkState]; + if (!([TAReachability convertNetworkType:networkType] & self.config.networkTypePolicy)) { + if (completion) { + completion(); + } + return; + } + + NSArray *recordArray; + NSArray *recodIds; + NSArray *uuids; + @synchronized (TDSqliteDataQueue.class) { + + NSArray *records = [self.dataQueue getFirstRecords:kBatchSize withAppid:[self.config getMapInstanceToken]]; + NSArray *encryptRecords = [self encryptEventRecords:records]; + NSMutableArray *indexs = [[NSMutableArray alloc] initWithCapacity:encryptRecords.count]; + NSMutableArray *recordContents = [[NSMutableArray alloc] initWithCapacity:encryptRecords.count]; + for (TDEventRecord *record in encryptRecords) { + [indexs addObject:record.index]; + [recordContents addObject:record.event]; + } + recodIds = indexs; + recordArray = recordContents; + + + uuids = [self.dataQueue upadteRecordIds:recodIds]; + } + + + if (recordArray.count == 0 || uuids.count == 0) { + if (completion) { + completion(); + } + return; + } + + + + BOOL flushSucc = YES; + while (recordArray.count > 0 && uuids.count > 0 && flushSucc) { + flushSucc = [self.network flushEvents:recordArray]; + if (flushSucc) { + @synchronized (TDSqliteDataQueue.class) { + BOOL ret = [self.dataQueue removeDataWithuids:uuids]; + if (!ret) { + break; + } + + NSArray *records = [self.dataQueue getFirstRecords:kBatchSize withAppid:[self.config getMapInstanceToken]]; + NSArray *encryptRecords = [self encryptEventRecords:records]; + NSMutableArray *indexs = [[NSMutableArray alloc] initWithCapacity:encryptRecords.count]; + NSMutableArray *recordContents = [[NSMutableArray alloc] initWithCapacity:encryptRecords.count]; + for (TDEventRecord *record in encryptRecords) { + [indexs addObject:record.index]; + [recordContents addObject:record.event]; + } + recodIds = indexs; + recordArray = recordContents; + + + uuids = [self.dataQueue upadteRecordIds:recodIds]; + } + } else { + break; + } + } + if (completion) { + completion(); + } +} + +- (NSArray *)encryptEventRecords:(NSArray *)records { +#if TARGET_OS_IOS + NSMutableArray *encryptRecords = [NSMutableArray arrayWithCapacity:records.count]; + + TDEncryptManager *encryptManager = [ThinkingAnalyticsSDK sharedInstanceWithAppid:[self.config getMapInstanceToken]].encryptManager; + + if (self.config.enableEncrypt && encryptManager.isValid) { + for (TDEventRecord *record in records) { + + if (record.encrypted) { + + [encryptRecords addObject:record]; + } else { + + NSDictionary *obj = [encryptManager encryptJSONObject:record.event]; + if (obj) { + [record setSecretObject:obj]; + [encryptRecords addObject:record]; + } else { + [encryptRecords addObject:record]; + } + } + } + return encryptRecords.count == 0 ? records : encryptRecords; + } else { + return records; + } +#elif TARGET_OS_OSX + return records; +#endif +} + +- (void)syncSendAllData { + dispatch_sync(td_networkQueue, ^{}); +} + + +//MARK: - Setter & Getter + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.m.meta new file mode 100644 index 0000000..f7149fd --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TAEventTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fbf6894ef152ec04080091793f287cd9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration.meta new file mode 100644 index 0000000..ec3f542 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c6727a77c7016484a88c3ce4e26cd196 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.h new file mode 100644 index 0000000..a311c81 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.h @@ -0,0 +1,33 @@ +// +// TATrackTimer.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/1. +// Copyright © 2022 thinking. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TATrackTimer : NSObject + +- (void)trackEvent:(NSString *)eventName withSystemUptime:(NSTimeInterval)systemUptime; + +- (void)enterForegroundWithSystemUptime:(NSTimeInterval)systemUptime; + +- (void)enterBackgroundWithSystemUptime:(NSTimeInterval)systemUptime; + +- (NSTimeInterval)foregroundDurationOfEvent:(NSString * _Nonnull)eventName isActive:(BOOL)isActive systemUptime:(NSTimeInterval)systemUptime; + +- (NSTimeInterval)backgroundDurationOfEvent:(NSString * _Nonnull)eventName isActive:(BOOL)isActive systemUptime:(NSTimeInterval)systemUptime; + +- (void)removeEvent:(NSString * _Nonnull)eventName; + +- (BOOL)isExistEvent:(NSString * _Nonnull)eventName; + +- (void)clear; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.h.meta new file mode 100644 index 0000000..d242896 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bc2b13ed52c2fe74289ee877867b72e5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.m new file mode 100644 index 0000000..af2b91e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.m @@ -0,0 +1,125 @@ +// +// TATrackTimer.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/1. +// Copyright © 2022 thinking. All rights reserved. +// + +#import "TATrackTimer.h" +#import "TATrackTimerItem.h" +#import "TAThreadSafeDictionary.h" +#import "TDDeviceInfo.h" + +@interface TATrackTimer () +@property (nonatomic, strong) TAThreadSafeDictionary *events; + +@end + +@implementation TATrackTimer + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.events = [TAThreadSafeDictionary dictionary]; + } + return self; +} + +- (void)trackEvent:(NSString *)eventName withSystemUptime:(NSTimeInterval)systemUptime { + if (!eventName.length) { + return; + } + TATrackTimerItem *item = [[TATrackTimerItem alloc] init]; + item.beginTime = systemUptime ?: [TDDeviceInfo uptime]; + self.events[eventName] = item; +} + +- (void)enterForegroundWithSystemUptime:(NSTimeInterval)systemUptime { + NSArray *keys = [self.events allKeys]; + for (NSString *key in keys) { + TATrackTimerItem *item = self.events[key]; + item.beginTime = systemUptime; + if (item.enterBackgroundTime == 0) { + item.backgroundDuration = 0; + } else { + item.backgroundDuration = systemUptime - item.enterBackgroundTime + item.backgroundDuration; + } + } +} + +- (void)enterBackgroundWithSystemUptime:(NSTimeInterval)systemUptime { + NSArray *keys = [self.events allKeys]; + for (NSString *key in keys) { + TATrackTimerItem *item = self.events[key]; + item.enterBackgroundTime = systemUptime; + item.foregroundDuration = systemUptime - item.beginTime + item.foregroundDuration; + } +} + +- (NSTimeInterval)foregroundDurationOfEvent:(NSString *)eventName isActive:(BOOL)isActive systemUptime:(NSTimeInterval)systemUptime { + if (!eventName.length) { + return 0; + } + TATrackTimerItem *item = self.events[eventName]; + if (!item) { + return 0; + } + + if (isActive) { + NSTimeInterval duration = systemUptime - item.beginTime + item.foregroundDuration; + return [self validateDuration:duration eventName:eventName]; + } else { + return [self validateDuration:item.foregroundDuration eventName:eventName]; + } + +} + +- (NSTimeInterval)backgroundDurationOfEvent:(NSString *)eventName isActive:(BOOL)isActive systemUptime:(NSTimeInterval)systemUptime { + if (!eventName.length) { + return 0; + } + TATrackTimerItem *item = self.events[eventName]; + if (!item) { + return 0; + } + if (isActive) { + return [self validateDuration:item.backgroundDuration eventName:eventName]; + } else { + NSTimeInterval duration = 0; + if (item.enterBackgroundTime == 0) { + duration = 0; + } else { + duration = systemUptime - item.enterBackgroundTime + item.backgroundDuration; + } + return [self validateDuration:duration eventName:eventName]; + } +} + +- (void)removeEvent:(NSString *)eventName { + [self.events removeObjectForKey:eventName]; +} + +- (BOOL)isExistEvent:(NSString *)eventName { + return self.events[eventName] != nil; +} + +- (void)clear { + [self.events removeAllObjects]; +} + +//MARK: - Private Methods + +- (NSTimeInterval)validateDuration:(NSTimeInterval)duration eventName:(NSString *)eventName { + NSInteger max = 3600 * 24; + if (duration >= max) { + return max; + } + + return duration; +} + +@end + + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.m.meta new file mode 100644 index 0000000..2ea1ec5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimer.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 10e8d03ff07d67240bf27d1394d7c4d2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.h new file mode 100644 index 0000000..87931ff --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.h @@ -0,0 +1,25 @@ +// +// TATrackTimerItem.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/1. +// Copyright © 2022 thinking. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TATrackTimerItem : NSObject +/// The moment when the event starts to be recorded (the total time the device has been running) +@property (nonatomic, assign) NSTimeInterval beginTime; +/// Accumulated time in the foreground +@property (nonatomic, assign) NSTimeInterval foregroundDuration; +/// The time the event entered the background (total time the device has been running) +@property (nonatomic, assign) NSTimeInterval enterBackgroundTime; +/// accumulated time in the background +@property (nonatomic, assign) NSTimeInterval backgroundDuration; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.h.meta new file mode 100644 index 0000000..d8145f8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e202e328f1f39c943803f79bdbd42e8d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.m new file mode 100644 index 0000000..e2cee1c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.m @@ -0,0 +1,17 @@ +// +// TATrackTimerItem.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/1. +// Copyright © 2022 thinking. All rights reserved. +// + +#import "TATrackTimerItem.h" + +@implementation TATrackTimerItem + +-(NSString *)description { + return [NSString stringWithFormat:@"beginTime: %lf, foregroundDuration: %lf, enterBackgroundTime: %lf, backgroundDuration: %lf", _beginTime, _foregroundDuration, _enterBackgroundTime, _backgroundDuration];; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.m.meta new file mode 100644 index 0000000..0403a4f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TATrackTimerItem.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 327aca07f69704e4a88cbb6e68727baf +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception.meta new file mode 100644 index 0000000..c834e66 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 151f1fb15584e9c4cb250a0e9617b563 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h new file mode 100644 index 0000000..6919fa8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h @@ -0,0 +1,21 @@ +#import + +#import "ThinkingAnalyticsSDKPrivate.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ThinkingExceptionHandler : NSObject + +@property (nonatomic, strong) NSHashTable *thinkingAnalyticsSDKInstances; + +@property (nonatomic) NSUncaughtExceptionHandler *td_lastExceptionHandler; + +@property (nonatomic, unsafe_unretained) struct sigaction *td_signalHandlers; + ++ (instancetype)sharedHandler; + +- (void)addThinkingInstance:(ThinkingAnalyticsSDK *)instance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h.meta new file mode 100644 index 0000000..1b1715a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9a39cb3f484487c458a50bc328c4ea84 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m new file mode 100644 index 0000000..8c62b34 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m @@ -0,0 +1,179 @@ +#import "ThinkingExceptionHandler.h" + +#include +#include +#import "TDLogging.h" +#import "TDPresetProperties+TDDisProperties.h" + +static NSString * const TDUncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName"; +static NSString * const TDUncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey"; +static int TDSignals[] = {SIGILL, SIGABRT, SIGBUS, SIGSEGV, SIGFPE, SIGPIPE, SIGTRAP}; +static volatile atomic_int_fast32_t TDExceptionCount = 0; +static const atomic_int_fast32_t TDExceptionMaximum = 9; + +@implementation ThinkingExceptionHandler + ++ (instancetype)sharedHandler { + static ThinkingExceptionHandler *gSharedHandler = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gSharedHandler = [[ThinkingExceptionHandler alloc] init]; + }); + return gSharedHandler; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _thinkingAnalyticsSDKInstances = [NSHashTable weakObjectsHashTable]; + _td_signalHandlers = calloc(NSIG, sizeof(struct sigaction)); + [self setupHandlers]; + } + return self; +} + +- (void)setupHandlers { + _td_lastExceptionHandler = NSGetUncaughtExceptionHandler(); + NSSetUncaughtExceptionHandler(&TDHandleException); + + struct sigaction action; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_SIGINFO; + action.sa_sigaction = &TDSignalHandler; + for (int i = 0; i < sizeof(TDSignals) / sizeof(int); i++) { + struct sigaction prev_action; + int err = sigaction(TDSignals[i], &action, &prev_action); + if (err == 0) { + memcpy(_td_signalHandlers + TDSignals[i], &prev_action, sizeof(prev_action)); + } else { + TDLogError(@"Error Signal: %d", TDSignals[i]); + } + } +} + +static void TDHandleException(NSException *exception) { + ThinkingExceptionHandler *handler = [ThinkingExceptionHandler sharedHandler]; + + atomic_int_fast32_t exceptionCount = atomic_fetch_add_explicit(&TDExceptionCount, 1, memory_order_relaxed); + if (exceptionCount <= TDExceptionMaximum) { + [handler td_handleUncaughtException:exception]; + } + if (handler.td_lastExceptionHandler) { + handler.td_lastExceptionHandler(exception); + } +} + +static void TDSignalHandler(int signalNumber, struct __siginfo *info, void *context) { + ThinkingExceptionHandler *handler = [ThinkingExceptionHandler sharedHandler]; + NSMutableDictionary *crashInfo; + NSString *reason; + NSException *exception; + + atomic_int_fast32_t exceptionCount = atomic_fetch_add_explicit(&TDExceptionCount, 1, memory_order_relaxed); + if (exceptionCount <= TDExceptionMaximum) { + [crashInfo setObject:[NSNumber numberWithInt:signalNumber] forKey:TDUncaughtExceptionHandlerSignalKey]; + reason = [NSString stringWithFormat:@"Signal %d was raised.", signalNumber]; + exception = [NSException exceptionWithName:TDUncaughtExceptionHandlerSignalExceptionName reason:reason userInfo:crashInfo]; + [handler td_handleUncaughtException:exception]; + } + + struct sigaction prev_action = handler.td_signalHandlers[signalNumber]; + if (prev_action.sa_handler == SIG_DFL) { + signal(signalNumber, SIG_DFL); + raise(signalNumber); + return; + } + if (prev_action.sa_flags & SA_SIGINFO) { + if (prev_action.sa_sigaction) { + prev_action.sa_sigaction(signalNumber, info, context); + } + } else if (prev_action.sa_handler) { + prev_action.sa_handler(signalNumber); + } +} + + +- (void)td_handleUncaughtException:(NSException *)exception { + NSDate *trackDate = [NSDate date]; + NSDictionary *dic = [self td_getCrashInfo:exception]; + for (ThinkingAnalyticsSDK *instance in self.thinkingAnalyticsSDKInstances) { + TAAutoTrackEvent *crashEvent = [[TAAutoTrackEvent alloc] initWithName:TD_APP_CRASH_EVENT]; + crashEvent.time = trackDate; + [instance autoTrackWithEvent:crashEvent properties:dic]; + + if (![instance isAutoTrackEventTypeIgnored:ThinkingAnalyticsEventTypeAppEnd]) { + TAAutoTrackEvent *appEndEvent = [[TAAutoTrackEvent alloc] initWithName:TD_APP_END_EVENT]; + appEndEvent.time = trackDate; + [instance autoTrackWithEvent:appEndEvent properties:nil]; + } + } + + dispatch_sync([ThinkingAnalyticsSDK td_trackQueue], ^{}); + dispatch_sync([ThinkingAnalyticsSDK td_networkQueue], ^{}); + + NSSetUncaughtExceptionHandler(NULL); + for (int i = 0; i < sizeof(TDSignals) / sizeof(int); i++) { + signal(TDSignals[i], SIG_DFL); + } +} + +- (NSMutableDictionary *)td_getCrashInfo:(NSException *)exception { + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + + + if ([TDPresetProperties disableAppCrashedReason]) { + return properties; + } + + NSString *crashStr; + @try { + if ([exception callStackSymbols]) { + crashStr = [NSString stringWithFormat:@"Exception Reason:%@\nException Stack:%@", [exception reason], [exception callStackSymbols]]; + } else { + NSString *exceptionStack = [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]; + crashStr = [NSString stringWithFormat:@"%@ %@", [exception reason], exceptionStack]; + } + crashStr = [crashStr stringByReplacingOccurrencesOfString:@"\n" withString:@"
"]; + + NSUInteger strLength = [((NSString *)crashStr) lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + NSUInteger strMaxLength = TA_PROPERTY_CRASH_LENGTH_LIMIT; + if (strLength > strMaxLength) { + crashStr = [NSMutableString stringWithString:[self limitString:crashStr withLength:strMaxLength - 1]]; + } + + [properties setValue:crashStr forKey:TD_CRASH_REASON]; + } @catch(NSException *exception) { + TDLogError(@"%@ error: %@", self, exception); + } + return properties; +} + +- (NSString *)limitString:(NSString *)originalString withLength:(NSInteger)length { + NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF8); + NSData *originalData = [originalString dataUsingEncoding:encoding]; + NSData *subData = [originalData subdataWithRange:NSMakeRange(0, length)]; + NSString *limitString = [[NSString alloc] initWithData:subData encoding:encoding]; + + NSInteger index = 1; + while (index <= 3 && !limitString) { + if (length > index) { + subData = [originalData subdataWithRange:NSMakeRange(0, length - index)]; + limitString = [[NSString alloc] initWithData:subData encoding:encoding]; + } + index ++; + } + + if (!limitString) { + return originalString; + } + return limitString; +} + +- (void)addThinkingInstance:(ThinkingAnalyticsSDK *)instance { + NSParameterAssert(instance != nil); + if (![self.thinkingAnalyticsSDKInstances containsObject:instance]) { + [self.thinkingAnalyticsSDKInstances addObject:instance]; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m.meta new file mode 100644 index 0000000..2366843 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 16e4260887094c54f952f05d96813d62 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension.meta new file mode 100644 index 0000000..f5ded45 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3603ec7937e65d8489210a107a96627f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.h new file mode 100644 index 0000000..33e6938 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.h @@ -0,0 +1,45 @@ +// +// TAAppExtensionAnalytic.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/5/25. +// Copyright © 2022 thinking. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// key: event name in App Extension +extern NSString * const kTAAppExtensionEventName; +/// key: event properties in App Extension +extern NSString * const kTAAppExtensionEventProperties; +/// key: event properties +extern NSString * const kTAAppExtensionTime; +/// key: event properties +extern NSString * const kTAAppExtensionEventPropertiesSource; + +@interface TAAppExtensionAnalytic : NSObject + ++ (void)calibrateTime:(NSTimeInterval)timestamp; + ++ (void)calibrateTimeWithNtp:(NSString *)ntpServer; + +/// Initialize an event collection object +/// @param instanceName The unique identifier of the event collection object +/// @param appGroupId share App Group ID ++ (TAAppExtensionAnalytic *)analyticWithInstanceName:(NSString * _Nonnull)instanceName appGroupId:(NSString * _Nonnull)appGroupId; + +/// write event +/// @param eventName eventName +/// @param properties properties +/// @return Whether (YES/NO) write success +- (BOOL)writeEvent:(NSString * _Nonnull)eventName properties:(NSDictionary * _Nullable)properties; + +- (NSArray *)readAllEvents; + +- (BOOL)deleteEvents; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.h.meta new file mode 100644 index 0000000..6fb244a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bddffb692c7f31b41a67692f9bd2cbde +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.m new file mode 100644 index 0000000..9811247 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.m @@ -0,0 +1,222 @@ +// +// TAAppExtensionAnalytic.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/5/25. +// Copyright © 2022 thinking. All rights reserved. +// + +#import "TAAppExtensionAnalytic.h" +#import "TAAppExtensionAnalyticConfig.h" + +#if __has_include() +#import +#else +#import "TDCalibratedTimeWithNTP.h" +#endif + +#if __has_include() +#import +#import +#else +#import "TDCalibratedTime.h" +#import "TDDeviceInfo.h" +#endif + + +NSString * const kTAAppExtensionEventName = @"ta_app_extension_event_name"; +NSString * const kTAAppExtensionEventProperties = @"ta_app_extension_properties"; +NSString * const kTAAppExtensionTime = @"time"; +NSString * const kTAAppExtensionEventPropertiesSource = @"from_app_extension"; + +@interface TAAppExtensionAnalytic() +@property (nonatomic, strong) TAAppExtensionAnalyticConfig *config; + +@end + +void *TAAppExtensionQueueTag = &TAAppExtensionQueueTag; + +static NSMutableDictionary *_instances; + +static dispatch_queue_t _appExtensionQueue; + +static TDCalibratedTime *_calibrateTimeManage; + +@implementation TAAppExtensionAnalytic + +//MARK: - Public Methods + ++ (void)initialize { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _instances = [NSMutableDictionary dictionary]; + + _appExtensionQueue = dispatch_queue_create("cn.thinkingdata.appExtensionQueue", DISPATCH_QUEUE_SERIAL); + dispatch_queue_set_specific(_appExtensionQueue, TAAppExtensionQueueTag, &TAAppExtensionQueueTag, NULL); + }); +} + ++ (TAAppExtensionAnalytic *)analyticWithInstanceName:(NSString * _Nonnull)instanceName appGroupId:(NSString * _Nonnull)appGroupId { + @synchronized (self) { + TAAppExtensionAnalyticConfig *config = [[TAAppExtensionAnalyticConfig alloc] init]; + config.instanceName = instanceName; + config.appGroupId = appGroupId; + TAAppExtensionAnalytic *analytic = [self analyticWithConfig:config]; + return analytic; + } +} + ++ (TAAppExtensionAnalytic *)analyticWithConfig:(TAAppExtensionAnalyticConfig *)config { + @synchronized (self) { + if (![config.instanceName isKindOfClass:NSString.class] || !config.instanceName.length) { + return nil; + } + if (![config.appGroupId isKindOfClass:NSString.class] || !config.appGroupId.length) { + return nil; + } + if (_instances[config.instanceName]) { + return _instances[config.instanceName]; + } else { + TAAppExtensionAnalytic *analytic = [[TAAppExtensionAnalytic alloc] init]; + analytic.config = config; + _instances[config.instanceName] = analytic; + return analytic; + } + } +} + ++ (void)calibrateTime:(NSTimeInterval)timestamp { + _calibrateTimeManage = [TDCalibratedTime sharedInstance]; + [[TDCalibratedTime sharedInstance] recalibrationWithTimeInterval:timestamp/1000.]; +} + ++ (void)calibrateTimeWithNtp:(NSString *)ntpServer { + if ([ntpServer isKindOfClass:[NSString class]] && ntpServer.length > 0) { + _calibrateTimeManage = [TDCalibratedTimeWithNTP sharedInstance]; + [[TDCalibratedTimeWithNTP sharedInstance] recalibrationWithNtps:@[ntpServer]]; + } +} + +- (BOOL)writeEvent:(NSString *)eventName properties:(NSDictionary *)properties { + @try { + if (![eventName isKindOfClass:NSString.class] || !eventName.length) { + return NO; + } + if (properties && ![properties isKindOfClass:NSDictionary.class]) { + return NO; + } + + NSMutableDictionary *mutableProperties = [NSMutableDictionary dictionaryWithDictionary:properties]; + mutableProperties[kTAAppExtensionEventPropertiesSource] = @(true); + + __block BOOL result = NO; + dispatch_block_t block = ^{ + NSDictionary *event = @{ + kTAAppExtensionEventName: eventName, + kTAAppExtensionEventProperties: mutableProperties, + kTAAppExtensionTime: [self calibrateDate:[NSDate date]], + }; + NSString *path = [self filePathForApplicationGroup]; + if(![[NSFileManager defaultManager] fileExistsAtPath:path]) { + NSDictionary *attributes = nil; +#if TARGET_OS_IOS + attributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey]; +#endif + [[NSFileManager defaultManager] createFileAtPath:path contents:nil attributes:attributes]; + } + NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithContentsOfFile:path]; + if (mutableArray.count) { + [mutableArray addObject:event]; + } else { + mutableArray = [NSMutableArray arrayWithObject:event]; + } + NSError *err = NULL; + NSData *data= [NSPropertyListSerialization dataWithPropertyList:mutableArray format:NSPropertyListBinaryFormat_v1_0 options:0 error:&err]; + if (path.length && data.length) { + result = [data writeToFile:path options:NSDataWritingAtomic error:nil]; + } + }; + if (dispatch_get_specific(TAAppExtensionQueueTag)) { + block(); + } else { + dispatch_sync(_appExtensionQueue, block); + } + return result; + } @catch (NSException *exception) { + return NO; + } +} + +- (NSArray *)readAllEvents { + @try { + __block NSArray *dataArray = @[]; + dispatch_block_t block = ^() { + NSString *path = [self filePathForApplicationGroup]; + NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:path]; + dataArray = array; + }; + if (dispatch_get_specific(TAAppExtensionQueueTag)) { + block(); + } else { + dispatch_sync(_appExtensionQueue, block); + } + return dataArray; + } @catch (NSException *exception) { + return @[]; + } +} + +- (BOOL)deleteEvents { + @try { + __block BOOL result = NO; + dispatch_block_t block = ^{ + NSString *path = [self filePathForApplicationGroup]; + NSMutableArray *array = [[NSMutableArray alloc] init]; + NSData *data= [NSPropertyListSerialization dataWithPropertyList:array format:NSPropertyListBinaryFormat_v1_0 options:0 error:nil]; + if (path.length && data.length) { + result = [data writeToFile:path options:NSDataWritingAtomic error:nil]; + } + }; + if (dispatch_get_specific(TAAppExtensionQueueTag)) { + block(); + } else { + dispatch_sync(_appExtensionQueue, block); + } + return result ; + } @catch (NSException *exception) { + return NO; + } +} + +//MARK: - Private Methods + +- (NSString *)filePathForApplicationGroup { + @try { + __block NSString *filePath = nil; + dispatch_block_t block = ^() { + NSString *fileName = [NSString stringWithFormat:@"thinking_data_events_%@.plist", self.config.instanceName]; + NSURL *pathUrl = [[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:self.config.appGroupId] URLByAppendingPathComponent:fileName]; + filePath = pathUrl.path; + }; + if (dispatch_get_specific(TAAppExtensionQueueTag)) { + block(); + } else { + dispatch_sync(_appExtensionQueue, block); + } + return filePath; + } @catch (NSException *exception) { + return nil; + } +} + +- (NSDate *)calibrateDate:(NSDate *)date { + if (_calibrateTimeManage && !_calibrateTimeManage.stopCalibrate) { + NSTimeInterval systemUptime = [TDDeviceInfo uptime]; + NSTimeInterval outTime = systemUptime - _calibrateTimeManage.systemUptime; + NSDate *serverDate = [NSDate dateWithTimeIntervalSince1970:(_calibrateTimeManage.serverTime + outTime)]; + return serverDate; + } + return date; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.m.meta new file mode 100644 index 0000000..9438416 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalytic.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6f3b8c2714e6e26458001e83e24ee74d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.h new file mode 100644 index 0000000..d2f1b92 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.h @@ -0,0 +1,20 @@ +// +// TAAppExtensionAnalyticConfig.h +// Pods +// +// Created by Yangxiongon 2022/5/31. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TAAppExtensionAnalyticConfig : NSObject +/// instance tag +@property (nonatomic, copy) NSString *instanceName; +/// app group identifier +@property (nonatomic, copy) NSString *appGroupId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.h.meta new file mode 100644 index 0000000..254bfc1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6da5f3b897452484cabba3d56e59f750 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.m new file mode 100644 index 0000000..d7ede5c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.m @@ -0,0 +1,12 @@ +// +// TAAppExtensionAnalyticConfig.m +// Pods +// +// Created by Yangxiongon 2022/5/31. +// + +#import "TAAppExtensionAnalyticConfig.h" + +@implementation TAAppExtensionAnalyticConfig + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.m.meta new file mode 100644 index 0000000..49658d3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Extension/TAAppExtensionAnalyticConfig.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d9f8d308c66fdf548b674164230306e2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network.meta new file mode 100644 index 0000000..b29ac7d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8b26a45b6d2bd224197b6922ea58d07e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.h new file mode 100644 index 0000000..e2b34cc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.h @@ -0,0 +1,27 @@ +#import + +#import "ThinkingAnalyticsSDKPrivate.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^TDFlushConfigBlock)(NSDictionary *result, NSError * _Nullable error); + +@interface TANetwork : NSObject + +@property (nonatomic, copy) NSString *appid; +@property (nonatomic, strong) NSURL *serverURL; + +@property (nonatomic, strong) NSURL *serverDebugURL; +@property (nonatomic, assign) ThinkingAnalyticsDebugMode debugMode; +@property (nonatomic, strong) TDSecurityPolicy *securityPolicy; +@property (nonatomic, copy) TDURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; + +- (BOOL)flushEvents:(NSArray *)events; +//- (void)flushEvents:(NSArray *)recordArray completion:(nullable void(^)(BOOL))completion; +- (void)fetchRemoteConfig:(NSString *)appid handler:(TDFlushConfigBlock)handler; +- (int)flushDebugEvents:(NSDictionary *)record withAppid:(NSString *)appid; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.h.meta new file mode 100644 index 0000000..6ced1f2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 43b25c6a08dffb94091064201bb4bbfc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.m new file mode 100644 index 0000000..d9dbf05 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.m @@ -0,0 +1,309 @@ +#import "TANetwork.h" + +#import "NSData+TDGzip.h" +#import "TDJSONUtil.h" +#import "TDLogging.h" +#import "TDSecurityPolicy.h" +#import "TDAppState.h" + +#if TARGET_OS_IOS +#import "TDToastView.h" +#endif + +static NSString *kTAIntegrationType = @"TA-Integration-Type"; +static NSString *kTAIntegrationVersion = @"TA-Integration-Version"; +static NSString *kTAIntegrationCount = @"TA-Integration-Count"; +static NSString *kTAIntegrationExtra = @"TA-Integration-Extra"; +static NSString *kTADatasType = @"TA-Datas-Type"; + +@implementation TANetwork + +- (NSURLSession *)sharedURLSession { + static NSURLSession *sharedSession = nil; + @synchronized(self) { + if (sharedSession == nil) { + NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; + sharedSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil]; + } + } + return sharedSession; +} + +- (NSString *)URLEncode:(NSString *)string { + NSString *encodedString = [string stringByAddingPercentEncodingWithAllowedCharacters:[[NSCharacterSet characterSetWithCharactersInString:@"?!@#$^&%*+,:;='\"`<>()[]{}/\\| "] invertedSet]]; + return encodedString; +} + +- (int)flushDebugEvents:(NSDictionary *)record withAppid:(NSString *)appid { + __block int debugResult = -1; + NSMutableDictionary *recordDic = [record mutableCopy]; + NSMutableDictionary *properties = [[recordDic objectForKey:@"properties"] mutableCopy]; + + if ([ThinkingAnalyticsSDK isTrackEvent:[record objectForKey:@"#type"]]) { + @synchronized ([TDDeviceInfo sharedManager]) { + [properties addEntriesFromDictionary:[[TDDeviceInfo sharedManager] getAutomaticData]]; + } + } + [recordDic setObject:properties forKey:@"properties"]; + NSString *jsonString = [TDJSONUtil JSONStringForObject:recordDic]; + NSMutableURLRequest *request = [self buildDebugRequestWithJSONString:jsonString withAppid:appid withDeviceId:[[[TDDeviceInfo sharedManager] getAutomaticData] objectForKey:@"#device_id"]]; + dispatch_semaphore_t flushSem = dispatch_semaphore_create(0); + + void (^block)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) { + + if (error || ![response isKindOfClass:[NSHTTPURLResponse class]]) { + debugResult = -2; + TDLogError(@"Debug Networking error:%@", error); + [self callbackNetworkErrorWithRequest:jsonString error:error.debugDescription]; + dispatch_semaphore_signal(flushSem); + return; + } + NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *)response; + if ([urlResponse statusCode] == 200) { + NSError *err; + + if (!data) { + return; + } + + NSDictionary *retDic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err]; + if (err) { + TDLogError(@"Debug data json error:%@", err); + debugResult = -2; + } else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:1]]) { + debugResult = 1; + NSArray* errorProperties = [retDic objectForKey:@"errorProperties"]; + NSMutableString *errorStr = [NSMutableString string]; + for (id obj in errorProperties) { + NSString *errorReasons = [obj objectForKey:@"errorReason"]; + NSString *propertyName = [obj objectForKey:@"propertyName"]; + [errorStr appendFormat:@" propertyName:%@ errorReasons:%@\n", propertyName, errorReasons]; + } + TDLogError(@"Debug data error:%@", errorStr); + } else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:2]]) { + debugResult = 2; + NSString *errorReasons = [[retDic objectForKey:@"errorReasons"] componentsJoinedByString:@" "]; + TDLogError(@"Debug data error:%@", errorReasons); + } else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:0]]) { + debugResult = 0; + TDLogDebug(@"Verify data success."); + } else if ([[retDic objectForKey:@"errorLevel"] isEqualToNumber:[NSNumber numberWithInt:-1]]) { + debugResult = -1; + NSString *errorReasons = [[retDic objectForKey:@"errorReasons"] componentsJoinedByString:@" "]; + TDLogError(@"Debug mode error:%@", errorReasons); + } + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (debugResult == 0 || debugResult == 1 || debugResult == 2) { +#if TARGET_OS_IOS + dispatch_async(dispatch_get_main_queue(), ^{ + UIApplication *application = [TDAppState sharedApplication]; + if (![application isKindOfClass:UIApplication.class]) { + return; + } + UIWindow *window = application.keyWindow; + [TDToastView showInWindow:window text:[NSString stringWithFormat:@"The current mode is:%@", self.debugMode == ThinkingAnalyticsDebugOnly ? @"DebugOnly(Data is not persisted) \n The test joint debugging stage is allowed to open \n Please turn off the Debug function before the official launch" : @"Debug"] duration:2.0]; + }); +#endif + } + }); + + @try { + if ([retDic isKindOfClass:[NSDictionary class]]) { + if ([[(NSDictionary *)retDic objectForKey:@"errorLevel"] integerValue] != 0) { + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:retDic options:NSJSONWritingPrettyPrinted error:NULL]; + NSString *string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + [self callbackNetworkErrorWithRequest:jsonString error:string]; + } + } + } @catch (NSException *exception) { + + } + } else { + debugResult = -2; + NSString *urlResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + TDLogError(@"%@", [NSString stringWithFormat:@"Debug %@ network failed with response '%@'.", self, urlResponse]); + [self callbackNetworkErrorWithRequest:jsonString error:urlResponse]; + } + dispatch_semaphore_signal(flushSem); + }; + + NSURLSessionDataTask *task = [[self sharedURLSession] dataTaskWithRequest:request completionHandler:block]; + [task resume]; + + dispatch_semaphore_wait(flushSem, DISPATCH_TIME_FOREVER); + return debugResult; +} + +- (BOOL)flushEvents:(NSArray *)recordArray { + __block BOOL flushSucc = YES; + UInt64 time = [[NSDate date] timeIntervalSince1970] * 1000; + NSDictionary *flushDic = @{ + @"data": recordArray, + @"#app_id": self.appid, + @"#flush_time": @(time), + }; + + __block BOOL isEncrypt; + [recordArray enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if ([obj.allKeys containsObject:@"ekey"]) { + isEncrypt = YES; + *stop = YES; + } + }]; + + NSString *jsonString = [TDJSONUtil JSONStringForObject:flushDic]; + NSMutableURLRequest *request = [self buildRequestWithJSONString:jsonString]; + [request addValue:[TDDeviceInfo sharedManager].libName forHTTPHeaderField:kTAIntegrationType]; + [request addValue:[TDDeviceInfo sharedManager].libVersion forHTTPHeaderField:kTAIntegrationVersion]; + [request addValue:@(recordArray.count).stringValue forHTTPHeaderField:kTAIntegrationCount]; + [request addValue:@"iOS" forHTTPHeaderField:kTAIntegrationExtra]; + if (isEncrypt) { + [request addValue:@"1" forHTTPHeaderField:kTADatasType]; + } +// [request addValue:@"Keep-Alive" forHTTPHeaderField:@"Connection"]; +// [request addValue:@"timeout=15,max=100" forHTTPHeaderField:@"Keep-Alive"]; + + dispatch_semaphore_t flushSem = dispatch_semaphore_create(0); + + void (^block)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) { + if (error || ![response isKindOfClass:[NSHTTPURLResponse class]]) { + flushSucc = NO; + TDLogError(@"Networking error:%@", error); + [self callbackNetworkErrorWithRequest:jsonString error:error.debugDescription]; + dispatch_semaphore_signal(flushSem); + return; + } + + NSHTTPURLResponse *urlResponse = (NSHTTPURLResponse *)response; + if ([urlResponse statusCode] == 200) { + flushSucc = YES; + TDLogDebug(@"flush success sendContent---->:%@",flushDic); + if (!data) { + return; + } + id result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; + TDLogDebug(@"flush success responseData---->%@",result); + + @try { + if ([result isKindOfClass:[NSDictionary class]]) { + if ([[(NSDictionary *)result objectForKey:@"code"] integerValue] != 0) { + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:NULL]; + NSString *string = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + [self callbackNetworkErrorWithRequest:jsonString error:string]; + } + } + } @catch (NSException *exception) { + + } + + } else { + flushSucc = NO; + NSString *urlResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + TDLogError(@"%@", [NSString stringWithFormat:@"%@ network failed with response '%@'.", self, urlResponse]); + [self callbackNetworkErrorWithRequest:jsonString error:urlResponse]; + } + + dispatch_semaphore_signal(flushSem); + }; + + NSURLSessionDataTask *task = [[self sharedURLSession] dataTaskWithRequest:request completionHandler:block]; + [task resume]; + dispatch_semaphore_wait(flushSem, DISPATCH_TIME_FOREVER); + return flushSucc; +} + +- (void)callbackNetworkErrorWithRequest:(NSString *)request error:(NSString *)error { + if (request == nil && error == nil) return; + + ThinkingAnalyticsSDK *tdSDK = [ThinkingAnalyticsSDK sharedInstanceWithAppid:self.appid]; + if (tdSDK.errorCallback) { + NSInteger code = 10001; + NSString *errorMsg = error; + NSString *ext = request; + tdSDK.errorCallback(code, errorMsg, ext); + } +} + +- (NSMutableURLRequest *)buildRequestWithJSONString:(NSString *)jsonString { + + NSData *zippedData = [NSData td_gzipData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]]; + NSString *postBody = [zippedData base64EncodedStringWithOptions:0]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.serverURL]; +// NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://192.168.20.23:8991/sync"]]; + [request setHTTPMethod:@"POST"]; + [request setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]]; + NSString *contentType = [NSString stringWithFormat:@"text/plain"]; + [request addValue:contentType forHTTPHeaderField:@"Content-Type"]; + [request setTimeoutInterval:60.0]; + return request; +} + +- (NSMutableURLRequest *)buildDebugRequestWithJSONString:(NSString *)jsonString withAppid:(NSString *)appid withDeviceId:(NSString *)deviceId { + // dryRun=0, if the verification is passed, it will be put into storage. dryRun=1, no storage + int dryRun = _debugMode == ThinkingAnalyticsDebugOnly ? 1 : 0; + NSString *postData = [NSString stringWithFormat:@"appid=%@&source=client&dryRun=%d&deviceId=%@&data=%@", appid, dryRun, deviceId, [self URLEncode:jsonString]]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.serverDebugURL]; + [request setHTTPMethod:@"POST"]; + request.HTTPBody = [postData dataUsingEncoding:NSUTF8StringEncoding]; + return request; +} + +- (void)fetchRemoteConfig:(NSString *)appid handler:(TDFlushConfigBlock)handler { + void (^block)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) { + if (error || ![response isKindOfClass:[NSHTTPURLResponse class]]) { + TDLogError(@"Fetch remote config network failed:%@", error); + return; + } + NSError *err; + if (!data) { + return; + } + NSDictionary *ret = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err]; + if (err) { + TDLogError(@"Fetch remote config json error:%@", err); + } else if ([ret isKindOfClass:[NSDictionary class]] && [ret[@"code"] isEqualToNumber:[NSNumber numberWithInt:0]]) { + TDLogDebug(@"Fetch remote config for %@ : %@", appid, [ret objectForKey:@"data"]); + handler([ret objectForKey:@"data"], error); + } else { + TDLogError(@"Fetch remote config failed"); + } + }; + NSString *urlStr = [NSString stringWithFormat:@"%@?appid=%@", self.serverURL, appid]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlStr]]; + [request setHTTPMethod:@"Get"]; + NSURLSessionDataTask *task = [[self sharedURLSession] dataTaskWithRequest:request completionHandler:block]; + [task resume]; +} + +#pragma mark - NSURLSessionDelegate +- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler { + NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; + NSURLCredential *credential = nil; + + if (self.sessionDidReceiveAuthenticationChallenge) { + disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential); + } else { + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { + credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + if (credential) { + disposition = NSURLSessionAuthChallengeUseCredential; + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + } else { + disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; + } + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + } + + if (completionHandler) { + completionHandler(disposition, credential); + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.m.meta new file mode 100644 index 0000000..df2ce9d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TANetwork.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d2b161a17028d0d419e63bf51c436166 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.h new file mode 100644 index 0000000..d1b4e13 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.h @@ -0,0 +1,28 @@ +// +// TAReachability.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/1. +// + +#import +#import "TDConstant.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TAReachability : NSObject + ++ (ThinkingNetworkType)convertNetworkType:(NSString *)networkType; + ++ (instancetype)shareInstance; + +- (void)startMonitoring; + +- (void)stopMonitoring; + +- (NSString *)networkState; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.h.meta new file mode 100644 index 0000000..9ac17d3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3bf8629734a44364eb0dd013447864dc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.m new file mode 100644 index 0000000..aecf1e7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.m @@ -0,0 +1,192 @@ +// +// TAReachability.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/1. +// + +#import "TAReachability.h" +#import +#import + +#if __has_include() +#import +#else +#import "TDLogging.h" +#endif + + +@interface TAReachability () +#if TARGET_OS_IOS +@property (atomic, assign) SCNetworkReachabilityRef reachability; +#endif +@property (nonatomic, assign) BOOL isWifi; +@property (nonatomic, assign) BOOL isWwan; + +@end + +@implementation TAReachability + +#if TARGET_OS_IOS +static void ThinkingReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) { + TAReachability *instance = (__bridge TAReachability *)info; + if (instance && [instance isKindOfClass:[TAReachability class]]) { + [instance reachabilityChanged:flags]; + } +} +#endif + +//MARK: - Public Methods + ++ (instancetype)shareInstance { + static dispatch_once_t onceToken; + static TAReachability *reachability = nil; + dispatch_once(&onceToken, ^{ + reachability = [[TAReachability alloc] init]; + }); + return reachability; +} + +#if TARGET_OS_IOS + +- (NSString *)networkState { + if (self.isWifi) { + return @"WIFI"; + } else if (self.isWwan) { + return [self currentRadio]; + } else { + return @"NULL"; + } +} + +- (void)startMonitoring { + [self stopMonitoring]; + + SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL,"thinkingdata.cn"); + self.reachability = reachability; + + if (self.reachability != NULL) { + SCNetworkReachabilityFlags flags; + BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(self.reachability, &flags); + if (didRetrieveFlags) { + self.isWifi = (flags & kSCNetworkReachabilityFlagsReachable) && !(flags & kSCNetworkReachabilityFlagsIsWWAN); + self.isWwan = (flags & kSCNetworkReachabilityFlagsIsWWAN); + } + + SCNetworkReachabilityContext context = {0, (__bridge void *)self, NULL, NULL, NULL}; + if (SCNetworkReachabilitySetCallback(self.reachability, ThinkingReachabilityCallback, &context)) { + if (!SCNetworkReachabilityScheduleWithRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes)) { + SCNetworkReachabilitySetCallback(self.reachability, NULL, NULL); + } + } + } +} + +- (void)stopMonitoring { + if (!self.reachability) { + return; + } + SCNetworkReachabilityUnscheduleFromRunLoop(self.reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); +} + ++ (ThinkingNetworkType)convertNetworkType:(NSString *)networkType { + if ([@"NULL" isEqualToString:networkType]) { + return ThinkingNetworkTypeALL; + } else if ([@"WIFI" isEqualToString:networkType]) { + return ThinkingNetworkTypeWIFI; + } else if ([@"2G" isEqualToString:networkType]) { + return ThinkingNetworkType2G; + } else if ([@"3G" isEqualToString:networkType]) { + return ThinkingNetworkType3G; + } else if ([@"4G" isEqualToString:networkType]) { + return ThinkingNetworkType4G; + }else if([@"5G"isEqualToString:networkType]) + { + return ThinkingNetworkType5G; + } + return ThinkingNetworkTypeNONE; +} + +//MARK: - Private Methods + +- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags { + self.isWifi = (flags & kSCNetworkReachabilityFlagsReachable) && !(flags & kSCNetworkReachabilityFlagsIsWWAN); + self.isWwan = (flags & kSCNetworkReachabilityFlagsIsWWAN); +} + +- (NSString *)currentRadio { + NSString *networkType = @"NULL"; + @try { + static CTTelephonyNetworkInfo *info = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + info = [[CTTelephonyNetworkInfo alloc] init]; + }); + NSString *currentRadio = nil; +#ifdef __IPHONE_12_0 + if (@available(iOS 12.0, *)) { + NSDictionary *serviceCurrentRadio = [info serviceCurrentRadioAccessTechnology]; + if ([serviceCurrentRadio isKindOfClass:[NSDictionary class]] && serviceCurrentRadio.allValues.count>0) { + currentRadio = serviceCurrentRadio.allValues[0]; + } + } +#endif + if (currentRadio == nil && [info.currentRadioAccessTechnology isKindOfClass:[NSString class]]) { + currentRadio = info.currentRadioAccessTechnology; + } + + if ([currentRadio isEqualToString:CTRadioAccessTechnologyLTE]) { + networkType = @"4G"; + } else if ([currentRadio isEqualToString:CTRadioAccessTechnologyeHRPD] || + [currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB] || + [currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] || + [currentRadio isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] || + [currentRadio isEqualToString:CTRadioAccessTechnologyCDMA1x] || + [currentRadio isEqualToString:CTRadioAccessTechnologyHSUPA] || + [currentRadio isEqualToString:CTRadioAccessTechnologyHSDPA] || + [currentRadio isEqualToString:CTRadioAccessTechnologyWCDMA]) { + networkType = @"3G"; + } else if ([currentRadio isEqualToString:CTRadioAccessTechnologyEdge] || + [currentRadio isEqualToString:CTRadioAccessTechnologyGPRS]) { + networkType = @"2G"; + } +#ifdef __IPHONE_14_1 + else if (@available(iOS 14.1, *)) { + if ([currentRadio isKindOfClass:[NSString class]]) { + if([currentRadio isEqualToString:CTRadioAccessTechnologyNRNSA] || + [currentRadio isEqualToString:CTRadioAccessTechnologyNR]) { + networkType = @"5G"; + } + } + } +#endif + } @catch (NSException *exception) { + TDLogError(@"%@: %@", self, exception); + } + + return networkType; +} + +#elif TARGET_OS_OSX + ++ (ThinkingNetworkType)convertNetworkType:(NSString *)networkType { + return ThinkingNetworkTypeWIFI; +} + +- (void)startMonitoring { +} + +- (void)stopMonitoring { +} + +- (NSString *)currentRadio { + return @"WIFI"; +} + +- (NSString *)networkState { + return @"WIFI"; +} + +#endif + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.m.meta new file mode 100644 index 0000000..a1f28a4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TAReachability.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 166a2da05be55d24899e432265dcb5af +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h new file mode 100644 index 0000000..f15a3c6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h @@ -0,0 +1,50 @@ + +/** + Thinks AFNetworking: https://github.com/AFNetworking/AFNetworking + */ +#import + +#if __has_include() +#import +#else +#import "TDConstant.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + + +@interface TDSecurityPolicy: NSObject + +@property (nonatomic, assign) BOOL allowInvalidCertificates; +@property (nonatomic, assign) BOOL validatesDomainName; +@property (nonatomic, copy) TDURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; ++ (instancetype)policyWithPinningMode:(TDSSLPinningMode)pinningMode; ++ (instancetype)defaultPolicy; +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain; + +@end + +#ifndef __Require_Quiet + #define __Require_Quiet(assertion, exceptionLabel) \ + do \ + { \ + if ( __builtin_expect(!(assertion), 0) ) \ + { \ + goto exceptionLabel; \ + } \ + } while ( 0 ) +#endif + +#ifndef __Require_noErr_Quiet + #define __Require_noErr_Quiet(errorCode, exceptionLabel) \ + do \ + { \ + if ( __builtin_expect(0 != (errorCode), 0) ) \ + { \ + goto exceptionLabel; \ + } \ + } while ( 0 ) +#endif + + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h.meta new file mode 100644 index 0000000..fe1abbc --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b6067047a0ad2a043a6724e06a1e7a16 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m new file mode 100644 index 0000000..c0f61af --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m @@ -0,0 +1,248 @@ +#import "TDSecurityPolicy.h" + +#import "TDLogging.h" + +static id TDPublicKeyForCertificate(NSData *certificate) { + id allowedPublicKey = nil; + SecCertificateRef allowedCertificate; + SecPolicyRef policy = nil; + SecTrustRef allowedTrust = nil; + SecTrustResultType result; + + allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); + __Require_Quiet(allowedCertificate != NULL, _out); + + policy = SecPolicyCreateBasicX509(); + __Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out); + __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); + + allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); + +_out: + if (allowedTrust) { + CFRelease(allowedTrust); + } + + if (policy) { + CFRelease(policy); + } + + if (allowedCertificate) { + CFRelease(allowedCertificate); + } + + return allowedPublicKey; +} + +static BOOL TDServerTrustIsValid(SecTrustRef serverTrust) { + BOOL isValid = NO; + SecTrustResultType result; + __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out); + + isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); + +_out: + return isValid; +} + +static NSArray * TDCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { + CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); + NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; + + for (CFIndex i = 0; i < certificateCount; i++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); + [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; + } + + return [NSArray arrayWithArray:trustChain]; +} + +static NSArray * TDPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { + SecPolicyRef policy = SecPolicyCreateBasicX509(); + CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); + NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; + for (CFIndex i = 0; i < certificateCount; i++) { + SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); + + SecCertificateRef someCertificates[] = {certificate}; + CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL); + + SecTrustRef trust; + __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out); + + SecTrustResultType result; + __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out); + + [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; + + _out: + if (trust) { + CFRelease(trust); + } + + if (certificates) { + CFRelease(certificates); + } + + continue; + } + CFRelease(policy); + + return [NSArray arrayWithArray:trustChain]; +} + +static BOOL TDSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) { + return [(__bridge id)key1 isEqual:(__bridge id)key2]; +} + +@interface TDSecurityPolicy () + +@property (nonatomic, assign) TDSSLPinningMode SSLPinningMode; +@property (nonatomic, strong, nullable) NSSet *pinnedCertificates; +@property (readwrite, nonatomic, strong) NSSet *pinnedPublicKeys; + +@end + +@implementation TDSecurityPolicy + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + + self.validatesDomainName = YES; + + return self; +} + ++ (NSSet *)certificatesInBundle:(NSBundle *)bundle { + NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; + + NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]]; + for (NSString *path in paths) { + NSData *certificateData = [NSData dataWithContentsOfFile:path]; + [certificates addObject:certificateData]; + } + + return [NSSet setWithSet:certificates]; +} + ++ (NSSet *)defaultPinnedCertificates { + static NSSet *_defaultPinnedCertificates = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSBundle *bundle = [NSBundle bundleForClass:[self class]]; + _defaultPinnedCertificates = [self certificatesInBundle:bundle]; + }); + + return _defaultPinnedCertificates; +} + ++ (instancetype)defaultPolicy { + TDSecurityPolicy *securityPolicy = [[self alloc] init]; + securityPolicy.SSLPinningMode = TDSSLPinningModeNone; + return securityPolicy; +} + ++ (instancetype)policyWithPinningMode:(TDSSLPinningMode)pinningMode { + return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]]; +} + ++ (instancetype)policyWithPinningMode:(TDSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates { + TDSecurityPolicy *securityPolicy = [[self alloc] init]; + securityPolicy.SSLPinningMode = pinningMode; + [securityPolicy setPinnedCertificates:pinnedCertificates]; + return securityPolicy; +} + +- (void)setPinnedCertificates:(NSSet *)pinnedCertificates { + _pinnedCertificates = pinnedCertificates; + + if (self.pinnedCertificates) { + NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]]; + for (NSData *certificate in self.pinnedCertificates) { + id publicKey = TDPublicKeyForCertificate(certificate); + if (publicKey) { + [mutablePinnedPublicKeys addObject:publicKey]; + } + } + self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys]; + } else { + self.pinnedPublicKeys = nil; + } +} + +- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { + if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == TDSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { + TDLogDebug(@"In order to validate a domain name for self signed certificates, you MUST use pinning."); + return NO; + } + + NSMutableArray *policies = [NSMutableArray array]; + if (self.validatesDomainName) { + [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)]; + } else { + [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()]; + } + + SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); + + if (self.SSLPinningMode == TDSSLPinningModeNone) { + return self.allowInvalidCertificates || TDServerTrustIsValid(serverTrust); + } else if (!TDServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { + return NO; + } + + switch (self.SSLPinningMode) { + case TDSSLPinningModeCertificate: { + NSMutableArray *pinnedCertificates = [NSMutableArray array]; + for (NSData *certificateData in self.pinnedCertificates) { + [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; + } + SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); + + if (!TDServerTrustIsValid(serverTrust)) { + return NO; + } + + NSArray *serverCertificates = TDCertificateTrustChainForServerTrust(serverTrust); + for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) { + if ([self.pinnedCertificates containsObject:trustChainCertificate]) { + return YES; + } + } + return NO; + } + case TDSSLPinningModePublicKey: { + NSUInteger trustedPublicKeyCount = 0; + NSArray *publicKeys = TDPublicKeyTrustChainForServerTrust(serverTrust); + + for (id trustChainPublicKey in publicKeys) { + for (id pinnedPublicKey in self.pinnedPublicKeys) { + if (TDSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { + trustedPublicKeyCount += 1; + } + } + } + return trustedPublicKeyCount > 0; + } + + default: + return NO; + } + + return NO; +} + +#pragma mark - NSCopying +- (instancetype)copyWithZone:(NSZone *)zone { + TDSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init]; + securityPolicy.SSLPinningMode = self.SSLPinningMode; + securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates; + securityPolicy.validatesDomainName = self.validatesDomainName; + securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone]; + return securityPolicy; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m.meta new file mode 100644 index 0000000..a0fb30d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9f66440e92e611647afe05768069d1fd +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router.meta new file mode 100644 index 0000000..b0a7c4e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 90f202fc52065d446a81c2e0f918dcf3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.h new file mode 100644 index 0000000..6273b5b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.h @@ -0,0 +1,38 @@ +// +// TAAnnotation.h +// Pods +// +// Created by wwango on 2022/10/8. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +#ifndef ThinkingModSectName + +#define ThinkingModSectName "ThinkingMods" + +#endif + +#ifndef ThinkingServiceSectName + +#define ThinkingServiceSectName "ThinkingServices" + +#endif + + +#define ThinkingDATA(sectname) __attribute((used, section("__DATA,"#sectname" "))) + + +#define ThinkingMod(name) \ +char * k##name##_mod ThinkingDATA(ThinkingMods) = ""#name""; + +#define ThinkingService(servicename,impl) \ +char * k##servicename##_service ThinkingDATA(ThinkingServices) = "{ \""#servicename"\" : \""#impl"\"}"; + +@interface TAAnnotation : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.h.meta new file mode 100644 index 0000000..95b73f3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: dd856a2ac2f52bb458e7378fa95980a3 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.m new file mode 100644 index 0000000..971c41c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.m @@ -0,0 +1,89 @@ +// +// TAAnnotation.m +// Pods +// +// Created by wwango on 2022/10/8. +// + +#import "TAAnnotation.h" +#include +#include +#include +#include +#import +#import +#include +#import "TAModuleManager.h" +#import "TAServiceManager.h" + +NSArray* _TAReadConfiguration(char *sectionName,const struct mach_header *mhp); +static void dyld_callback(const struct mach_header *mhp, intptr_t vmaddr_slide) +{ + //register mods + NSArray *mods = _TAReadConfiguration(ThinkingModSectName, mhp); + for (NSString *modName in mods) { + Class cls; + if (modName) { + cls = NSClassFromString(modName); + + if (cls) { + [[TAModuleManager sharedManager] registerDynamicModule:cls]; + } + } + } + + //register services + NSArray *services = _TAReadConfiguration(ThinkingServiceSectName,mhp); + for (NSString *map in services) { + NSData *jsonData = [map dataUsingEncoding:NSUTF8StringEncoding]; + NSError *error = nil; + id json = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; + if (!error) { + if ([json isKindOfClass:[NSDictionary class]] && [json allKeys].count) { + + NSString *protocol = [json allKeys][0]; + NSString *clsName = [json allValues][0]; + + if (protocol && clsName) { + [[TAServiceManager sharedManager] registerService:NSProtocolFromString(protocol) implClass:NSClassFromString(clsName)]; + } + + } + } + } +} + +NSArray* _TAReadConfiguration(char *sectionName,const struct mach_header *mhp) +{ + NSMutableArray *configs = [NSMutableArray array]; + unsigned long size = 0; +#ifndef __LP64__ + uintptr_t *memory = (uintptr_t*)getsectiondata(mhp, SEG_DATA, sectionName, &size); +#else + const struct mach_header_64 *mhp64 = (const struct mach_header_64 *)mhp; + uintptr_t *memory = (uintptr_t*)getsectiondata(mhp64, SEG_DATA, sectionName, &size); +#endif + + unsigned long counter = size/sizeof(void*); + for(int idx = 0; idx < counter; ++idx){ + char *string = (char*)memory[idx]; + NSString *str = [NSString stringWithUTF8String:string]; + if(!str)continue; + + NSLog(@"config = %@", str); + if(str) [configs addObject:str]; + } + + return configs; +} + +__attribute__((constructor)) void __ta_init_dyld_addImage() { + _dyld_register_func_for_add_image(dyld_callback); +} + +@implementation TAAnnotation + + +@end + + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.m.meta new file mode 100644 index 0000000..f2f0da4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAAnnotation.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 39fa38ed3ad5f7c4abdcec17e484f316 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.h new file mode 100644 index 0000000..de66e00 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.h @@ -0,0 +1,28 @@ +// +// TAContext.h +// ThinkingSDK.default-Base-Core-Extension-Router-Util-iOS +// +// Created by wwango on 2022/10/7. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TAContext : NSObject + +@property(nonatomic, strong) UIApplication *application; + +@property(nonatomic, strong) NSDictionary *launchOptions; + ++ (instancetype)shareInstance; + +- (void)addServiceWithImplInstance:(id)implInstance serviceName:(NSString *)serviceName; + +- (void)removeServiceWithServiceName:(NSString *)serviceName; + +- (id)getServiceInstanceFromServiceName:(NSString *)serviceName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.h.meta new file mode 100644 index 0000000..1a5dd60 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3fb65c7bc426bea408d42a6fa8d08306 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.m new file mode 100644 index 0000000..20aac5b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.m @@ -0,0 +1,68 @@ +// +// TAContext.m +// ThinkingSDK.default-Base-Core-Extension-Router-Util-iOS +// +// Created by wwango on 2022/10/7. +// + +#import "TAContext.h" + +@interface TAContext() + +@property(nonatomic, strong) NSMutableDictionary *modulesByName; + +@property(nonatomic, strong) NSMutableDictionary *servicesByName; + +@end + + +@implementation TAContext + ++ (instancetype)shareInstance +{ + static dispatch_once_t p; + static id instance = nil; + + dispatch_once(&p, ^{ + instance = [[[self class] alloc] init]; + }); + + return instance; +} + +- (void)addServiceWithImplInstance:(id)implInstance serviceName:(NSString *)serviceName +{ + [[TAContext shareInstance].servicesByName setObject:implInstance forKey:serviceName]; +} + +- (void)removeServiceWithServiceName:(NSString *)serviceName +{ + [[TAContext shareInstance].servicesByName removeObjectForKey:serviceName]; +} + +- (id)getServiceInstanceFromServiceName:(NSString *)serviceName +{ + return [[TAContext shareInstance].servicesByName objectForKey:serviceName]; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.modulesByName = [[NSMutableDictionary alloc] initWithCapacity:1]; + self.servicesByName = [[NSMutableDictionary alloc] initWithCapacity:1]; + } + + return self; +} + +- (instancetype)copyWithZone:(NSZone *)zone { + TAContext *context = [[self.class allocWithZone:zone] init]; + + context.application = self.application; + context.launchOptions = self.launchOptions; + + return context; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.m.meta new file mode 100644 index 0000000..3a7dbc2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAContext.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9e069046b6f695e47a2b4a21fd77e3c1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.h new file mode 100644 index 0000000..6ffeff8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.h @@ -0,0 +1,72 @@ +// +// TAModuleManager.h +// Pods +// +// Created by wwango on 2022/10/8. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, TAModuleLevel) +{ + TAModuleBasic = 0, + TAModuleNormal = 1 +}; + +typedef NS_ENUM(NSInteger, TAModuleEventType) +{ + TAMSetupEvent = 0, + TAMInitEvent, + TAMTearDownEvent, + TAMSplashEvent, + TAMQuickActionEvent, + TAMWillResignActiveEvent, + TAMDidEnterBackgroundEvent, + TAMWillEnterForegroundEvent, + TAMDidBecomeActiveEvent, + TAMWillTerminateEvent, + TAMUnmountEvent, + TAMOpenURLEvent, + TAMDidReceiveMemoryWarningEvent, + TAMDidFailToRegisterForRemoteNotificationsEvent, + TAMDidRegisterForRemoteNotificationsEvent, + TAMDidReceiveRemoteNotificationEvent, + TAMDidReceiveLocalNotificationEvent, + TAMWillPresentNotificationEvent, + TAMDidReceiveNotificationResponseEvent, + TAMWillContinueUserActivityEvent, + TAMContinueUserActivityEvent, + TAMDidFailToContinueUserActivityEvent, + TAMDidUpdateUserActivityEvent, + TAMDidCustomEvent = 1000 + +}; + +@interface TAModuleManager : NSObject + ++ (instancetype)sharedManager; + +// If you do not comply with set Level protocol, the default Normal +- (void)registerDynamicModule:(Class)moduleClass; + +- (void)unRegisterDynamicModule:(Class)moduleClass; + +- (void)loadLocalModules; + +- (void)registedAllModules; + +- (void)registerCustomEvent:(NSInteger)eventType + withModuleInstance:(id)moduleInstance + andSelectorStr:(NSString *)selectorStr; + +- (void)triggerEvent:(NSInteger)eventType; + +- (void)triggerEvent:(NSInteger)eventType + withCustomParam:(NSDictionary *)customParam; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.h.meta new file mode 100644 index 0000000..197efa2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4fc15d6474918924c9a46257e5ae21f0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.m new file mode 100644 index 0000000..bdc89e1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.m @@ -0,0 +1,509 @@ +// +// TAModuleManager.m +// Pods +// +// Created by wwango on 2022/10/8. +// + +#import "TAModuleManager.h" +#import "TAModuleProtocol.h" +#import "TAContext.h" + +#define kTAModuleArrayKey @"moduleClasses" +#define kTAModuleInfoNameKey @"moduleClass" +#define kTAModuleInfoLevelKey @"moduleLevel" +#define kTAModuleInfoPriorityKey @"modulePriority" +#define kTAModuleInfoHasInstantiatedKey @"moduleHasInstantiated" + +static NSString *kTASetupSelector = @"modSetUp:"; +static NSString *kTAInitSelector = @"modInit:"; +static NSString *kTASplashSeletor = @"modSplash:"; +static NSString *kTATearDownSelector = @"modTearDown:"; +static NSString *kTAWillResignActiveSelector = @"modWillResignActive:"; +static NSString *kTADidEnterBackgroundSelector = @"modDidEnterBackground:"; +static NSString *kTAWillEnterForegroundSelector = @"modWillEnterForeground:"; +static NSString *kTADidBecomeActiveSelector = @"modDidBecomeActive:"; +static NSString *kTAWillTerminateSelector = @"modWillTerminate:"; +static NSString *kTAUnmountEventSelector = @"modUnmount:"; +static NSString *kTAQuickActionSelector = @"modQuickAction:"; +static NSString *kTAOpenURLSelector = @"modOpenURL:"; +static NSString *kTADidReceiveMemoryWarningSelector = @"modDidReceiveMemoryWaring:"; +static NSString *kTAFailToRegisterForRemoteNotificationsSelector = @"modDidFailToRegisterForRemoteNotifications:"; +static NSString *kTADidRegisterForRemoteNotificationsSelector = @"modDidRegisterForRemoteNotifications:"; +static NSString *kTADidReceiveRemoteNotificationsSelector = @"modDidReceiveRemoteNotification:"; +static NSString *kTADidReceiveLocalNotificationsSelector = @"modDidReceiveLocalNotification:"; +static NSString *kTAWillPresentNotificationSelector = @"modWillPresentNotification:"; +static NSString *kTADidReceiveNotificationResponseSelector = @"modDidReceiveNotificationResponse:"; +static NSString *kTAWillContinueUserActivitySelector = @"modWillContinueUserActivity:"; +static NSString *kTAContinueUserActivitySelector = @"modContinueUserActivity:"; +static NSString *kTADidUpdateContinueUserActivitySelector = @"modDidUpdateContinueUserActivity:"; +static NSString *kTAFailToContinueUserActivitySelector = @"modDidFailToContinueUserActivity:"; +static NSString *kTAHandleWatchKitExtensionRequestSelector = @"modHandleWatchKitExtensionRequest:"; +static NSString *kTAAppCustomSelector = @"modDidCustomEvent:"; + + +@interface TAModuleManager () + +@property(nonatomic, strong) NSMutableArray *TAModuleDynamicClasses; + +@property(nonatomic, strong) NSMutableArray *TAModuleInfos; +@property(nonatomic, strong) NSMutableArray *TAModules; + +@property(nonatomic, strong) NSMutableDictionary> *> *TAModulesByEvent; +@property(nonatomic, strong) NSMutableDictionary *TASelectorByEvent; + +@end + + +@implementation TAModuleManager + + ++ (instancetype)sharedManager +{ + static id sharedManager = nil; + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + sharedManager = [[TAModuleManager alloc] init]; + }); + return sharedManager; +} + +- (void)loadLocalModules +{ + +} + +- (void)registerDynamicModule:(Class)moduleClass +{ + [self addModuleFromObject:moduleClass]; +} + +- (void)unRegisterDynamicModule:(Class)moduleClass { + if (!moduleClass) { + return; + } + [self.TAModuleInfos filterUsingPredicate:[NSPredicate predicateWithFormat:@"%@!=%@", kTAModuleInfoNameKey, NSStringFromClass(moduleClass)]]; + __block NSInteger index = -1; + [self.TAModules enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if ([obj isKindOfClass:moduleClass]) { + index = idx; + *stop = YES; + } + }]; + if (index >= 0) { + [self.TAModules removeObjectAtIndex:index]; + } + [self.TAModulesByEvent enumerateKeysAndObjectsUsingBlock:^(NSNumber * _Nonnull key, NSMutableArray> * _Nonnull obj, BOOL * _Nonnull stop) { + __block NSInteger index = -1; + [obj enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if ([obj isKindOfClass:moduleClass]) { + index = idx; + *stop = NO; + } + }]; + if (index >= 0) { + [obj removeObjectAtIndex:index]; + } + }]; +} + +- (void)registedAllModules +{ + [self.TAModuleInfos sortUsingComparator:^NSComparisonResult(NSDictionary *module1, NSDictionary *module2) { + NSNumber *module1Level = (NSNumber *)[module1 objectForKey:kTAModuleInfoLevelKey]; + NSNumber *module2Level = (NSNumber *)[module2 objectForKey:kTAModuleInfoLevelKey]; + if (module1Level.integerValue != module2Level.integerValue) { + return module1Level.integerValue > module2Level.integerValue; + } else { + NSNumber *module1Priority = (NSNumber *)[module1 objectForKey:kTAModuleInfoPriorityKey]; + NSNumber *module2Priority = (NSNumber *)[module2 objectForKey:kTAModuleInfoPriorityKey]; + return module1Priority.integerValue < module2Priority.integerValue; + } + }]; + + NSMutableArray *tmpArray = [NSMutableArray array]; + + //module init + [self.TAModuleInfos enumerateObjectsUsingBlock:^(NSDictionary *module, NSUInteger idx, BOOL * _Nonnull stop) { + + NSString *classStr = [module objectForKey:kTAModuleInfoNameKey]; + + Class moduleClass = NSClassFromString(classStr); + BOOL hasInstantiated = ((NSNumber *)[module objectForKey:kTAModuleInfoHasInstantiatedKey]).boolValue; + if (NSStringFromClass(moduleClass) && !hasInstantiated) { + id moduleInstance = [[moduleClass alloc] init]; + [tmpArray addObject:moduleInstance]; + } + + }]; + +// [self.BHModules removeAllObjects]; + + [self.TAModules addObjectsFromArray:tmpArray]; + + [self registerAllSystemEvents]; +} + +- (void)registerCustomEvent:(NSInteger)eventType + withModuleInstance:(id)moduleInstance + andSelectorStr:(NSString *)selectorStr { + if (eventType < 1000) { + return; + } + [self registerEvent:eventType withModuleInstance:moduleInstance andSelectorStr:selectorStr]; +} + +- (void)triggerEvent:(NSInteger)eventType +{ + [self triggerEvent:eventType withCustomParam:nil]; +} + +- (void)triggerEvent:(NSInteger)eventType + withCustomParam:(NSDictionary *)customParam { + [self handleModuleEvent:eventType forTarget:nil withCustomParam:customParam]; +} + +#pragma mark - life loop + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.TAModuleDynamicClasses = [NSMutableArray array]; + } + return self; +} + + +#pragma mark - private + +- (TAModuleLevel)checkModuleLevel:(NSUInteger)level +{ + switch (level) { + case 0: + return TAModuleBasic; + break; + case 1: + return TAModuleNormal; + break; + default: + break; + } + //default normal + return TAModuleNormal; +} + + +- (void)addModuleFromObject:(id)object +{ + Class class; + NSString *moduleName = nil; + + if (object) { + class = object; + moduleName = NSStringFromClass(class); + } else { + return ; + } + + __block BOOL flag = YES; + [self.TAModules enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if ([obj isKindOfClass:class]) { + flag = NO; + *stop = YES; + } + }]; + if (!flag) { + return; + } + + if ([class conformsToProtocol:@protocol(TAModuleProtocol)]) { + NSMutableDictionary *moduleInfo = [NSMutableDictionary dictionary]; + + BOOL responseBasicLevel = [class instancesRespondToSelector:@selector(basicModuleLevel)]; + + int levelInt = 1; + + if (responseBasicLevel) { + levelInt = 0; + } + + [moduleInfo setObject:@(levelInt) forKey:kTAModuleInfoLevelKey]; + if (moduleName) { + [moduleInfo setObject:moduleName forKey:kTAModuleInfoNameKey]; + } + + [self.TAModuleInfos addObject:moduleInfo]; + + id moduleInstance = [[class alloc] init]; + [self.TAModules addObject:moduleInstance]; + [moduleInfo setObject:@(YES) forKey:kTAModuleInfoHasInstantiatedKey]; + [self.TAModules sortUsingComparator:^NSComparisonResult(id moduleInstance1, id moduleInstance2) { + NSNumber *module1Level = @(TAModuleNormal); + NSNumber *module2Level = @(TAModuleNormal); + if ([moduleInstance1 respondsToSelector:@selector(basicModuleLevel)]) { + module1Level = @(TAModuleBasic); + } + if ([moduleInstance2 respondsToSelector:@selector(basicModuleLevel)]) { + module2Level = @(TAModuleBasic); + } + if (module1Level.integerValue != module2Level.integerValue) { + return module1Level.integerValue > module2Level.integerValue; + } else { + NSInteger module1Priority = 0; + NSInteger module2Priority = 0; + if ([moduleInstance1 respondsToSelector:@selector(modulePriority)]) { + module1Priority = [moduleInstance1 modulePriority]; + } + if ([moduleInstance2 respondsToSelector:@selector(modulePriority)]) { + module2Priority = [moduleInstance2 modulePriority]; + } + return module1Priority < module2Priority; + } + }]; + [self registerEventsByModuleInstance:moduleInstance]; + } +} + +- (void)registerAllSystemEvents +{ + [self.TAModules enumerateObjectsUsingBlock:^(id moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) { + [self registerEventsByModuleInstance:moduleInstance]; + }]; +} + +- (void)registerEventsByModuleInstance:(id)moduleInstance +{ + NSArray *events = self.TASelectorByEvent.allKeys; + [events enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + [self registerEvent:obj.integerValue withModuleInstance:moduleInstance andSelectorStr:self.TASelectorByEvent[obj]]; + }]; +} + +- (void)registerEvent:(NSInteger)eventType + withModuleInstance:(id)moduleInstance + andSelectorStr:(NSString *)selectorStr { + SEL selector = NSSelectorFromString(selectorStr); + if (!selector || ![moduleInstance respondsToSelector:selector]) { + return; + } + NSNumber *eventTypeNumber = @(eventType); + if (!self.TASelectorByEvent[eventTypeNumber]) { + [self.TASelectorByEvent setObject:selectorStr forKey:eventTypeNumber]; + } + if (!self.TAModulesByEvent[eventTypeNumber]) { + [self.TAModulesByEvent setObject:@[].mutableCopy forKey:eventTypeNumber]; + } + NSMutableArray *eventModules = [self.TAModulesByEvent objectForKey:eventTypeNumber]; + if (![eventModules containsObject:moduleInstance]) { + [eventModules addObject:moduleInstance]; + [eventModules sortUsingComparator:^NSComparisonResult(id moduleInstance1, id moduleInstance2) { + NSNumber *module1Level = @(TAModuleNormal); + NSNumber *module2Level = @(TAModuleNormal); + if ([moduleInstance1 respondsToSelector:@selector(basicModuleLevel)]) { + module1Level = @(TAModuleBasic); + } + if ([moduleInstance2 respondsToSelector:@selector(basicModuleLevel)]) { + module2Level = @(TAModuleBasic); + } + if (module1Level.integerValue != module2Level.integerValue) { + return module1Level.integerValue > module2Level.integerValue; + } else { + NSInteger module1Priority = 0; + NSInteger module2Priority = 0; + if ([moduleInstance1 respondsToSelector:@selector(modulePriority)]) { + module1Priority = [moduleInstance1 modulePriority]; + } + if ([moduleInstance2 respondsToSelector:@selector(modulePriority)]) { + module2Priority = [moduleInstance2 modulePriority]; + } + return module1Priority < module2Priority; + } + }]; + } +} + + +#pragma mark - property setter or getter +- (NSMutableArray *)TAModuleInfos { + if (!_TAModuleInfos) { + _TAModuleInfos = @[].mutableCopy; + } + return _TAModuleInfos; +} + +- (NSMutableArray *)TAModules +{ + if (!_TAModules) { + _TAModules = [NSMutableArray array]; + } + return _TAModules; +} + +- (NSMutableDictionary> *> *)TAModulesByEvent +{ + if (!_TAModulesByEvent) { + _TAModulesByEvent = @{}.mutableCopy; + } + return _TAModulesByEvent; +} + +- (NSMutableDictionary *)TASelectorByEvent +{ + if (!_TASelectorByEvent) { + _TASelectorByEvent = @{ + @(TAMSetupEvent):kTASetupSelector, + @(TAMInitEvent):kTAInitSelector, + @(TAMTearDownEvent):kTATearDownSelector, + @(TAMSplashEvent):kTASplashSeletor, + @(TAMWillResignActiveEvent):kTAWillResignActiveSelector, + @(TAMDidEnterBackgroundEvent):kTADidEnterBackgroundSelector, + @(TAMWillEnterForegroundEvent):kTAWillEnterForegroundSelector, + @(TAMDidBecomeActiveEvent):kTADidBecomeActiveSelector, + @(TAMWillTerminateEvent):kTAWillTerminateSelector, + @(TAMUnmountEvent):kTAUnmountEventSelector, + @(TAMOpenURLEvent):kTAOpenURLSelector, + @(TAMDidReceiveMemoryWarningEvent):kTADidReceiveMemoryWarningSelector, + + @(TAMDidReceiveRemoteNotificationEvent):kTADidReceiveRemoteNotificationsSelector, + @(TAMWillPresentNotificationEvent):kTAWillPresentNotificationSelector, + @(TAMDidReceiveNotificationResponseEvent):kTADidReceiveNotificationResponseSelector, + + @(TAMDidFailToRegisterForRemoteNotificationsEvent):kTAFailToRegisterForRemoteNotificationsSelector, + @(TAMDidRegisterForRemoteNotificationsEvent):kTADidRegisterForRemoteNotificationsSelector, + + @(TAMDidReceiveLocalNotificationEvent):kTADidReceiveLocalNotificationsSelector, + + @(TAMWillContinueUserActivityEvent):kTAWillContinueUserActivitySelector, + + @(TAMContinueUserActivityEvent):kTAContinueUserActivitySelector, + + @(TAMDidFailToContinueUserActivityEvent):kTAFailToContinueUserActivitySelector, + + @(TAMDidUpdateUserActivityEvent):kTADidUpdateContinueUserActivitySelector, + + @(TAMQuickActionEvent):kTAQuickActionSelector, + @(TAMDidCustomEvent):kTAAppCustomSelector, + }.mutableCopy; + } + return _TASelectorByEvent; +} + +#pragma mark - module protocol +- (void)handleModuleEvent:(NSInteger)eventType + forTarget:(id)target + withCustomParam:(NSDictionary *)customParam +{ + switch (eventType) { + case TAMInitEvent: + //special + [self handleModulesInitEventForTarget:nil withCustomParam :customParam]; + break; + case TAMTearDownEvent: + //special + [self handleModulesTearDownEventForTarget:nil withCustomParam:customParam]; + break; + default: { + NSString *selectorStr = [self.TASelectorByEvent objectForKey:@(eventType)]; + [self handleModuleEvent:eventType forTarget:nil withSeletorStr:selectorStr andCustomParam:customParam]; + } + break; + } + +} + +- (void)handleModulesInitEventForTarget:(id)target + withCustomParam:(NSDictionary *)customParam +{ + TAContext *context = [TAContext shareInstance].copy; + + NSArray> *moduleInstances; + if (target) { + moduleInstances = @[target]; + } else { + moduleInstances = [self.TAModulesByEvent objectForKey:@(TAMInitEvent)]; + } + + [moduleInstances enumerateObjectsUsingBlock:^(id moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) { + __weak __typeof(&*self) wself = self; + void ( ^ bk )(void); + bk = ^(){ + __strong __typeof(&*self) sself = wself; + if (sself) { + if ([moduleInstance respondsToSelector:@selector(modInit:)]) { + [moduleInstance modInit:context]; + } + } + }; + + if ([moduleInstance respondsToSelector:@selector(async)]) { + BOOL async = [moduleInstance async]; + + if (async) { + dispatch_async(dispatch_get_main_queue(), ^{ + bk(); + }); + + } else { + bk(); + } + } else { + bk(); + } + }]; +} + +- (void)handleModulesTearDownEventForTarget:(id)target + withCustomParam:(NSDictionary *)customParam +{ + TAContext *context = [TAContext shareInstance].copy; + + NSArray> *moduleInstances; + if (target) { + moduleInstances = @[target]; + } else { + moduleInstances = [self.TAModulesByEvent objectForKey:@(TAMTearDownEvent)]; + } + + //Reverse Order to unload + for (int i = (int)moduleInstances.count - 1; i >= 0; i--) { + id moduleInstance = [moduleInstances objectAtIndex:i]; + if (moduleInstance && [moduleInstance respondsToSelector:@selector(modTearDown:)]) { + [moduleInstance modTearDown:context]; + } + } +} + + +- (void)handleModuleEvent:(NSInteger)eventType + forTarget:(id)target + withSeletorStr:(NSString *)selectorStr + andCustomParam:(NSDictionary *)customParam +{ + TAContext *context = [TAContext shareInstance].copy; + if (!selectorStr.length) { + selectorStr = [self.TASelectorByEvent objectForKey:@(eventType)]; + } + SEL seletor = NSSelectorFromString(selectorStr); + if (!seletor) { + selectorStr = [self.TASelectorByEvent objectForKey:@(eventType)]; + seletor = NSSelectorFromString(selectorStr); + } + NSArray> *moduleInstances; + if (target) { + moduleInstances = @[target]; + } else { + moduleInstances = [self.TAModulesByEvent objectForKey:@(eventType)]; + } + [moduleInstances enumerateObjectsUsingBlock:^(id moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) { + if ([moduleInstance respondsToSelector:seletor]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [moduleInstance performSelector:seletor withObject:context]; +#pragma clang diagnostic pop + } + }]; +} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.m.meta new file mode 100644 index 0000000..618176d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5780a9102f98b034ca5013e5ccdd638d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleProtocol.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleProtocol.h new file mode 100644 index 0000000..39a8944 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleProtocol.h @@ -0,0 +1,81 @@ +// +// TAModuleProtocol.h +// ThinkingSDK.default-Base-Core-Extension-Router-Util-iOS +// +// Created by wwango on 2022/10/7. +// + +#import + +#define TA_EXPORT_MODULE(isAsync) \ ++ (void)load { [[TAModuleManager sharedManager] registerDynamicModule:[self class]]; } \ +-(BOOL)async { return [[NSString stringWithUTF8String:#isAsync] boolValue];} + +@class TAContext; + + +NS_ASSUME_NONNULL_BEGIN + +@protocol TAModuleProtocol + +@optional + +- (void)basicModuleLevel; + +- (NSInteger)modulePriority; + +- (BOOL)async; + +- (void)modSetUp:(TAContext *)context; + +- (void)modInit:(TAContext *)context; + +- (void)modSplash:(TAContext *)context; + +- (void)modQuickAction:(TAContext *)context; + +- (void)modTearDown:(TAContext *)context; + +- (void)modWillResignActive:(TAContext *)context; + +- (void)modDidEnterBackground:(TAContext *)context; + +- (void)modWillEnterForeground:(TAContext *)context; + +- (void)modDidBecomeActive:(TAContext *)context; + +- (void)modWillTerminate:(TAContext *)context; + +- (void)modUnmount:(TAContext *)context; + +- (void)modOpenURL:(TAContext *)context; + +- (void)modDidReceiveMemoryWaring:(TAContext *)context; + +- (void)modDidFailToRegisterForRemoteNotifications:(TAContext *)context; + +- (void)modDidRegisterForRemoteNotifications:(TAContext *)context; + +- (void)modDidReceiveRemoteNotification:(TAContext *)context; + +- (void)modDidReceiveLocalNotification:(TAContext *)context; + +- (void)modWillPresentNotification:(TAContext *)context; + +- (void)modDidReceiveNotificationResponse:(TAContext *)context; + +- (void)modWillContinueUserActivity:(TAContext *)context; + +- (void)modContinueUserActivity:(TAContext *)context; + +- (void)modDidFailToContinueUserActivity:(TAContext *)context; + +- (void)modDidUpdateContinueUserActivity:(TAContext *)context; + +- (void)modHandleWatchKitExtensionRequest:(TAContext *)context; + +- (void)modDidCustomEvent:(TAContext *)context; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleProtocol.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleProtocol.h.meta new file mode 100644 index 0000000..5b96df8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAModuleProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 808489b244a0e334894811c9401825ef +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.h new file mode 100644 index 0000000..3a5fe48 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.h @@ -0,0 +1,38 @@ +// +// TARouter.h +// Pods +// +// Created by wwango on 2022/10/8. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +static NSString *const TARURLSchemeGlobalKey = @"URLGlobalScheme"; +static NSString *const TARURLHostCallService = @"call.service.thinkingdata"; +static NSString *const TARURLHostRegister = @"register.thinking"; +static NSString *const TARURLSubPathSplitPattern = @"."; +static NSString *const TARURLQueryParamsKey = @"params"; + +typedef void(^TARPathComponentCustomHandler)(NSDictionary *params); + +@interface TARouter : NSObject + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + ++ (instancetype)globalRouter; ++ (instancetype)routerForScheme:(NSString *)scheme; + +//url - > com.thinkingdata://call.service/pathComponentKey.protocolName.selector/...?params={}(value url encode) ++ (BOOL)canOpenURL:(NSURL *)URL; ++ (BOOL)openURL:(NSURL *)URL; ++ (BOOL)openURL:(NSURL *)URL + withParams:(NSDictionary *> *)params; ++ (BOOL)openURL:(NSURL *)URL + withParams:(NSDictionary *> *)params + andThen:(void(^)(NSString *pathComponentKey, id obj, id returnValue))then; +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.h.meta new file mode 100644 index 0000000..95c335a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1f7c9241ac875a84193c88e3627c3b40 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.m new file mode 100644 index 0000000..7463167 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.m @@ -0,0 +1,491 @@ +// +// TARouter.m +// Pods +// +// Created by wwango on 2022/10/8. +// + +#import "TARouter.h" +#import "TAServiceProtocol.h" +#import "TAServiceManager.h" +#import + +@interface NSObject (TARetType) + ++ (id)bh_getReturnFromInv:(NSInvocation *)inv withSig:(NSMethodSignature *)sig; + +@end + +@implementation NSObject (TARetType) + ++ (id)bh_getReturnFromInv:(NSInvocation *)inv withSig:(NSMethodSignature *)sig { + NSUInteger length = [sig methodReturnLength]; + if (length == 0) return nil; + + char *type = (char *)[sig methodReturnType]; + while (*type == 'r' || // const + *type == 'n' || // in + *type == 'N' || // inout + *type == 'o' || // out + *type == 'O' || // bycopy + *type == 'R' || // byref + *type == 'V') { // oneway + type++; // cutoff useless prefix + } + +#define ta_return_with_number(_type_) \ +do { \ +_type_ ret; \ +[inv getReturnValue:&ret]; \ +return @(ret); \ +} while (0) + + switch (*type) { + case 'v': return nil; // void + case 'B': ta_return_with_number(bool); + case 'c': ta_return_with_number(char); + case 'C': ta_return_with_number(unsigned char); + case 's': ta_return_with_number(short); + case 'S': ta_return_with_number(unsigned short); + case 'i': ta_return_with_number(int); + case 'I': ta_return_with_number(unsigned int); + case 'l': ta_return_with_number(int); + case 'L': ta_return_with_number(unsigned int); + case 'q': ta_return_with_number(long long); + case 'Q': ta_return_with_number(unsigned long long); + case 'f': ta_return_with_number(float); + case 'd': ta_return_with_number(double); + case 'D': { // long double + long double ret; + [inv getReturnValue:&ret]; + return [NSNumber numberWithDouble:ret]; + }; + + case '@': { // id + id ret = nil; + [inv getReturnValue:&ret]; + return ret; + }; + + case '#': { // Class + Class ret = nil; + [inv getReturnValue:&ret]; + return ret; + }; + + default: { // struct / union / SEL / void* / unknown + const char *objCType = [sig methodReturnType]; + char *buf = calloc(1, length); + if (!buf) return nil; + [inv getReturnValue:buf]; + NSValue *value = [NSValue valueWithBytes:buf objCType:objCType]; + free(buf); + return value; + }; + } +#undef ta_return_with_number +} + +@end + +static NSString *const TARClassRegex = @"(?<=T@\")(.*)(?=\",)"; + + +typedef NS_ENUM(NSUInteger, TARUsage) { + TARUsageUnknown, + TARUsageCallService, +}; + + +static NSMutableDictionary *routerByScheme = nil; + + +@interface TARPathComponent : NSObject + +@property (nonatomic, copy) NSString *key; +@property (nonatomic, strong) Class mClass; +@property (nonatomic, copy) NSDictionary *params; +@property (nonatomic, copy) TARPathComponentCustomHandler handler; + +@end + +@implementation TARPathComponent + +@end + +static NSString *TARURLGlobalScheme = nil; + +@interface TARouter () + +@property (nonatomic, strong) NSMutableDictionary *pathComponentByKey; +@property (nonatomic, copy) NSString *scheme; + +@end + +@implementation TARouter + +#pragma mark - property init +- (NSMutableDictionary *)pathComponentByKey { + if (!_pathComponentByKey) { + _pathComponentByKey = @{}.mutableCopy; + } + return _pathComponentByKey; +} + +#pragma mark - router init + ++ (instancetype)globalRouter +{ + if (!TARURLGlobalScheme) { + TARURLGlobalScheme = @"com.thinkingdata"; + } + return [self routerForScheme:TARURLGlobalScheme]; +} ++ (instancetype)routerForScheme:(NSString *)scheme +{ + if (!scheme.length) { + return nil; + } + + TARouter *router = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + routerByScheme = @{}.mutableCopy; + }); + + if (!routerByScheme[scheme]) { + router = [[self alloc] init]; + router.scheme = scheme; + [routerByScheme setObject:router forKey:scheme]; + } else { + router = [routerByScheme objectForKey:scheme]; + } + + return router; +} + ++ (void)unRegisterRouterForScheme:(NSString *)scheme +{ + if (!scheme.length) { + return; + } + + [routerByScheme removeObjectForKey:scheme]; +} ++ (void)unRegisterAllRouters +{ + [routerByScheme removeAllObjects]; +} + +- (void)addPathComponent:(NSString *)pathComponentKey + forClass:(Class)mClass +{ + [self addPathComponent:pathComponentKey forClass:mClass handler:nil]; +} +//handler is a custom module or service init function +- (void)addPathComponent:(NSString *)pathComponentKey + forClass:(Class)mClass + handler:(TARPathComponentCustomHandler)handler +{ + TARPathComponent *pathComponent = [[TARPathComponent alloc] init]; + pathComponent.key = pathComponentKey; + pathComponent.mClass = mClass; + pathComponent.handler = handler; + [self.pathComponentByKey setObject:pathComponent forKey:pathComponentKey]; +} +- (void)removePathComponent:(NSString *)pathComponentKey +{ + [self.pathComponentByKey removeObjectForKey:pathComponentKey]; +} + ++ (BOOL)canOpenURL:(NSURL *)URL +{ + if (!URL) { + return NO; + } + NSString *scheme = URL.scheme; + if (!scheme.length) { + return NO; + } + + NSString *host = URL.host; + TARUsage usage = [self usage:host]; + if (usage == TARUsageUnknown) { + return NO; + } + + TARouter *router = [self routerForScheme:scheme]; + + NSArray *pathComponents = URL.pathComponents; + + __block BOOL flag = YES; + + [pathComponents enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSArray * subPaths = [obj componentsSeparatedByString:TARURLSubPathSplitPattern]; + + if ([subPaths.firstObject isEqualToString:@"/"]) { + return; + } + + if (!subPaths.count) { + flag = NO; + *stop = NO; + return; + } + NSString *pathComponentKey = subPaths.firstObject; + + + if (router.pathComponentByKey[pathComponentKey]) { + return; + } + Class mClass = NSClassFromString(pathComponentKey); + if (!mClass) { + flag = NO; + *stop = NO; + return; + } + switch (usage) { + case TARUsageCallService: { + if (subPaths.count < 3) { + flag = NO; + *stop = NO; + return; + } + NSString *protocolStr = subPaths[1]; + NSString *selectorStr = subPaths[2]; + Protocol *protocol = NSProtocolFromString(protocolStr); + SEL selector = NSSelectorFromString(selectorStr); + if (!protocol || + !selector || + ![mClass conformsToProtocol:protocol] || + ![mClass instancesRespondToSelector:selector]) { + flag = NO; + *stop = NO; + return; + } + } break; + default: + break; + } + }]; + + return flag; +} + + ++ (BOOL)openURL:(NSURL *)URL +{ + return [self openURL:URL withParams:nil andThen:nil]; +} ++ (BOOL)openURL:(NSURL *)URL + withParams:(NSDictionary *> *)params +{ + return [self openURL:URL withParams:params andThen:nil]; +} ++ (BOOL)openURL:(NSURL *)URL + withParams:(NSDictionary *> *)params + andThen:(void(^)(NSString *pathComponentKey, id obj, id returnValue))then +{ + if (![self canOpenURL:URL]) { + return NO; + } + + NSString *scheme = URL.scheme; + TARouter *router = [self routerForScheme:scheme]; + + NSString *host = URL.host; + TARUsage usage = [self usage:host]; + + NSDictionary *queryDic = [self queryDicFromURL:URL]; + NSString *paramsJson = [queryDic objectForKey:TARURLQueryParamsKey]; + NSDictionary *> *allURLParams = [self paramsFromJson:paramsJson]; + + NSArray *pathComponents = URL.pathComponents; + + [pathComponents enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if (![obj isEqualToString:@"/"]) { + + NSArray * subPaths = [obj componentsSeparatedByString:TARURLSubPathSplitPattern]; + NSString *pathComponentKey = subPaths.firstObject; + + Class mClass; + TARPathComponentCustomHandler handler; + TARPathComponent *pathComponent = [router.pathComponentByKey objectForKey:pathComponentKey]; + if (pathComponent) { + mClass = pathComponent.mClass; + handler = pathComponent.handler; + } else { + mClass = NSClassFromString(pathComponentKey); + } + + NSDictionary *URLParams = [allURLParams objectForKey:pathComponentKey]; + NSDictionary *funcParams = [params objectForKey:pathComponentKey]; + NSDictionary *finalParams = [self solveURLParams:URLParams withFuncParams:funcParams forClass:usage == TARUsageCallService ? nil : mClass]; + + if (handler) { + handler(finalParams); + return; + } + + NSString *protocolStr; + Protocol *protocol; + if (subPaths.count >= 2) { + protocolStr = subPaths[1]; + protocol = NSProtocolFromString(protocolStr); + } + + id obj; + id returnValue; + + switch (usage) { + case TARUsageCallService: { + NSString *selectorStr = subPaths[2]; + SEL selector = NSSelectorFromString(selectorStr); + obj = [[TAServiceManager sharedManager] createService:protocol]; + returnValue = [self safePerformAction:selector forTarget:obj withParams:finalParams]; + } break; + default: + break; + } + !then?:then(pathComponentKey, obj, returnValue); + } + }]; + + return YES; +} + + +#pragma mark - private ++ (TARUsage)usage:(NSString *)usagePattern +{ + usagePattern = usagePattern.lowercaseString; + if ([usagePattern isEqualToString:TARURLHostCallService]) { + return TARUsageCallService; + } + return TARUsageUnknown; +} + ++ (NSDictionary *)queryDicFromURL:(NSURL *)URL +{ + if (!URL) { + return nil; + } + if ([UIDevice currentDevice].systemVersion.floatValue < 8) { + NSMutableDictionary *dic = @{}.mutableCopy; + NSString *query = URL.query; + NSArray *queryStrs = [query componentsSeparatedByString:@"&"]; + [queryStrs enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + NSArray *keyValue = [obj componentsSeparatedByString:@"="]; + if (keyValue.count >= 2) { + NSString *key = keyValue[0]; + NSString *value = keyValue[1]; + [dic setObject:value forKey:key]; + } + }]; + return dic; + } else { + NSURLComponents *URLComponents = [NSURLComponents componentsWithURL:URL + resolvingAgainstBaseURL:NO]; + NSArray *queryItems = URLComponents.queryItems; + NSMutableDictionary *dic = @{}.mutableCopy; + for (NSURLQueryItem *item in queryItems) { + if (item.name && item.value) { + [dic setObject:item.value forKey:item.name]; + } + } + return dic; + } +} + ++ (NSDictionary *> *)paramsFromJson:(NSString *)json +{ + if (!json.length) { + return nil; + } + NSError *error; + NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error]; + if (error) { + NSLog(@"TARouter [Error] Wrong URL Query Format: \n%@", error.description); + } + return dic; +} + + ++ (NSDictionary *)solveURLParams:(NSDictionary *)URLParams + withFuncParams:(NSDictionary *)funcParams + forClass:(Class)mClass +{ + if (!URLParams) { + URLParams = @{}; + } + NSMutableDictionary *params = URLParams.mutableCopy; + NSArray *funcParamKeys = funcParams.allKeys; + [funcParamKeys enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + [params setObject:funcParams[obj] forKey:obj]; + }]; + if (mClass) { + NSArray *paramKeys = params.allKeys; + [paramKeys enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + objc_property_t prop = class_getProperty(mClass, obj.UTF8String); + if (!prop) { + [params removeObjectForKey:obj]; + } else { + NSString *propAttr = [[NSString alloc] initWithCString:property_getAttributes(prop) encoding:NSUTF8StringEncoding]; + NSRange range = [propAttr rangeOfString:TARClassRegex options:NSRegularExpressionSearch]; + if (range.length != 0) { + NSString *propClassName = [propAttr substringWithRange:range]; + Class propClass = objc_getClass([propClassName UTF8String]); + if ([propClass isSubclassOfClass:[NSString class]] && [params[obj] isKindOfClass:[NSNumber class]]) { + [params setObject:[NSString stringWithFormat:@"%@", params[obj]] forKey:obj]; + } else if ([propClass isSubclassOfClass:[NSNumber class]] && [params[obj] isKindOfClass:[NSString class]]) { + [params setObject:@(((NSString *)params[obj]).doubleValue) forKey:obj]; + } + + } + } + }]; + } + return params; +} + ++ (void)setObject:(id)object + withPropertys:(NSDictionary *)propertys +{ + if (!object) { + return; + } + [propertys enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { + [object setValue:obj forKey:key]; + }]; +} + ++ (id)safePerformAction:(SEL)action + forTarget:(NSObject *)target + withParams:(NSDictionary *)params +{ + NSMethodSignature * sig = [target methodSignatureForSelector:action]; + if (!sig) { return nil; } + NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig]; + if (!inv) { return nil; } + [inv setTarget:target]; + [inv setSelector:action]; + NSArray *keys = params.allKeys; + keys = [keys sortedArrayUsingComparator:^NSComparisonResult(NSString * _Nonnull obj1, NSString * _Nonnull obj2) { + if (obj1.integerValue < obj2.integerValue) { + return NSOrderedAscending; + } else if (obj1.integerValue == obj2.integerValue) { + return NSOrderedSame; + } else { + return NSOrderedDescending; + } + }]; + [keys enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + id value = params[obj]; + [inv setArgument:&value atIndex:idx+2]; + }]; + [inv invoke]; + return [NSObject bh_getReturnFromInv:inv withSig:sig]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.m.meta new file mode 100644 index 0000000..7e40745 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TARouter.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bd6b92a05025faa42917f6e8f81a35e0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.h new file mode 100644 index 0000000..e046367 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.h @@ -0,0 +1,28 @@ +// +// TAServiceManager.h +// ThinkingSDK.default-Base-Core-Extension-Router-Util-iOS +// +// Created by wwango on 2022/10/7. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TAServiceManager : NSObject + +@property (nonatomic, assign) BOOL enableException; + ++ (instancetype)sharedManager; + +- (void)registerLocalServices; + +- (void)registerService:(Protocol *)service implClass:(Class)implClass; + +- (id)createService:(Protocol *)service; +- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName; +- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName shouldCache:(BOOL)shouldCache; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.h.meta new file mode 100644 index 0000000..1f61009 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a16ee9fce88ef584eb22111f2a88a66d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.m new file mode 100644 index 0000000..160279d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.m @@ -0,0 +1,163 @@ +// +// TAServiceManager.m +// ThinkingSDK.default-Base-Core-Extension-Router-Util-iOS +// +// Created by wwango on 2022/10/7. +// + +#import "TAServiceManager.h" +#import "TAContext.h" +#import "TAAnnotation.h" +#import + +static const NSString *kTAService = @"service"; +static const NSString *kTAImpl = @"impl"; + +@interface TAServiceManager() + +@property (nonatomic, strong) NSMutableDictionary *allServicesDict; +@property (nonatomic, strong) NSRecursiveLock *lock; + +@end + +@implementation TAServiceManager + ++ (instancetype)sharedManager +{ + static id sharedManager = nil; + static dispatch_once_t onceToken = 0; + dispatch_once(&onceToken, ^{ + sharedManager = [[self alloc] init]; + }); + return sharedManager; +} + +- (void)registerLocalServices +{ + +} + +- (void)registerService:(Protocol *)service implClass:(Class)implClass +{ + NSParameterAssert(service != nil); + NSParameterAssert(implClass != nil); + + if (![implClass conformsToProtocol:service]) { + if (self.enableException) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ module does not comply with %@ protocol", NSStringFromClass(implClass), NSStringFromProtocol(service)] userInfo:nil]; + } + return; + } + + if ([self checkValidService:service]) { + if (self.enableException) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ protocol has been registed", NSStringFromProtocol(service)] userInfo:nil]; + } + return; + } + + NSString *key = NSStringFromProtocol(service); + NSString *value = NSStringFromClass(implClass); + + if (key.length > 0 && value.length > 0) { + [self.lock lock]; + [self.allServicesDict addEntriesFromDictionary:@{key:value}]; + [self.lock unlock]; + } + +} + +- (id)createService:(Protocol *)service +{ + return [self createService:service withServiceName:nil]; +} + +- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName { + return [self createService:service withServiceName:serviceName shouldCache:YES]; +} + +- (id)createService:(Protocol *)service withServiceName:(NSString *)serviceName shouldCache:(BOOL)shouldCache { + if (!serviceName.length) { + serviceName = NSStringFromProtocol(service); + } + id implInstance = nil; + + if (![self checkValidService:service]) { + if (self.enableException) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:@"%@ protocol does not been registed", NSStringFromProtocol(service)] userInfo:nil]; + } + + } + + NSString *serviceStr = serviceName; + if (shouldCache) { + id protocolImpl = [[TAContext shareInstance] getServiceInstanceFromServiceName:serviceStr]; + if (protocolImpl) { + return protocolImpl; + } + } + + Class implClass = [self serviceImplClass:service]; + if ([[implClass class] respondsToSelector:@selector(singleton)]) { + if ([[implClass class] performSelector:@selector(singleton)]) { + if ([[implClass class] respondsToSelector:@selector(shareInstance)]) + implInstance = [[implClass class] shareInstance]; + else + implInstance = [[implClass alloc] init]; + if (shouldCache) { + [[TAContext shareInstance] addServiceWithImplInstance:implInstance serviceName:serviceStr]; + return implInstance; + } else { + return implInstance; + } + } + } + return [[implClass alloc] init]; +} + +#pragma mark - private +- (Class)serviceImplClass:(Protocol *)service +{ + NSString *serviceImpl = [[self servicesDict] objectForKey:NSStringFromProtocol(service)]; + if (serviceImpl.length > 0) { + return NSClassFromString(serviceImpl); + } + return nil; +} + +- (BOOL)checkValidService:(Protocol *)service +{ + NSString *serviceImpl = [[self servicesDict] objectForKey:NSStringFromProtocol(service)]; + if (serviceImpl.length > 0) { + return YES; + } + return NO; +} + +- (NSMutableDictionary *)allServicesDict +{ + if (!_allServicesDict) { + _allServicesDict = [NSMutableDictionary dictionary]; + } + return _allServicesDict; +} + +- (NSRecursiveLock *)lock +{ + if (!_lock) { + _lock = [[NSRecursiveLock alloc] init]; + } + return _lock; +} + +- (NSDictionary *)servicesDict +{ + [self.lock lock]; + NSDictionary *dict = [self.allServicesDict copy]; + [self.lock unlock]; + return dict; +} + + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.m.meta new file mode 100644 index 0000000..c7f7512 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0ee92fe9f02d19d4fb1a1d800801befd +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceProtocol.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceProtocol.h new file mode 100644 index 0000000..a4a16a6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceProtocol.h @@ -0,0 +1,22 @@ +// +// TAServiceProtocol.h +// ThinkingSDK.default-Base-Core-Extension-Router-Util-iOS +// +// Created by wwango on 2022/10/7. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol TAServiceProtocol + +@optional + ++ (BOOL)singleton; + ++ (id)shareInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceProtocol.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceProtocol.h.meta new file mode 100644 index 0000000..092714c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Router/TAServiceProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 985dd88351bbf664fa7d4d48d3c9596e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store.meta new file mode 100644 index 0000000..330727e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ada66347bc659214d819e894104dbd2f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h new file mode 100644 index 0000000..2f756da --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h @@ -0,0 +1,63 @@ +// +// TDFile.h +// ThinkingSDK +// +// Created by LiHuanan on 2020/9/8. +// Copyright © 2020 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDFile : NSObject + +@property(strong,nonatomic) NSString* appid; + +- (instancetype)initWithAppid:(NSString*)appid; + +- (void)archiveSessionID:(long long)sessionid; +- (long long)unarchiveSessionID ; + +- (void)archiveIdentifyId:(nullable NSString *)identifyId; +- (NSString*)unarchiveIdentifyID ; + +- (void)archiveAccountID:(nullable NSString *)accountID; +- (NSString*)unarchiveAccountID ; + +- (void)archiveUploadSize:(NSNumber *)uploadSize; +- (NSNumber*)unarchiveUploadSize; + +- (void)archiveUploadInterval:(NSNumber *)uploadInterval; +- (NSNumber*)unarchiveUploadInterval; + + +- (void)archiveSuperProperties:(nullable NSDictionary *)superProperties; +- (NSDictionary*)unarchiveSuperProperties; + +- (void)archiveTrackPause:(BOOL)trackPause; +- (BOOL)unarchiveTrackPause; + +- (void)archiveOptOut:(BOOL)optOut; +- (BOOL)unarchiveOptOut; + +- (void)archiveIsEnabled:(BOOL)isEnabled; +- (BOOL)unarchiveEnabled; + +- (void)archiveDeviceId:(NSString *)deviceId; +- (NSString *)unarchiveDeviceId; + +- (void)archiveInstallTimes:(NSString *)installTimes; +- (NSString *)unarchiveInstallTimes; + +- (BOOL)archiveObject:(id)object withFilePath:(NSString *)filePath; + +- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *)filePathString; +// Compatible with old versions +- (NSString*)unarchiveOldLoginId; +// Compatible with old versions +- (void)deleteOldLoginId; + +@end; + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h.meta new file mode 100644 index 0000000..d06eeaf --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d5b7e371a93424242a391968ce5a0c5e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m new file mode 100644 index 0000000..ab6aa51 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m @@ -0,0 +1,283 @@ +// +// TDFile.m +// ThinkingSDK +// +// Created by LiHuanan on 2020/9/8. +// Copyright © 2020 thinkingdata. All rights reserved. +// + +#import "TDFile.h" +#import "TDLogging.h" +#import "TDJSONUtil.h" + +@implementation TDFile + +- (instancetype)initWithAppid:(NSString*)appid +{ + self = [super init]; + if(self) + { + self.appid = appid; + } + return self; +} + +- (void)archiveSessionID:(long long)sessionid { + NSString *filePath = [self sessionIdFilePath]; + if (![self archiveObject:@(sessionid) withFilePath:filePath]) { + TDLogError(@"%@ unable to archive identifyId", self); + } +} +- (long long)unarchiveSessionID { + return [[self unarchiveFromFile:[self sessionIdFilePath] asClass:[NSNumber class]] longLongValue]; +} + + +- (void)archiveIdentifyId:(NSString *)identifyId { + + NSString *filePath = [self identifyIdFilePath]; + if (![self archiveObject:[identifyId copy] withFilePath:filePath]) { + TDLogError(@"%@ unable to archive identifyId", self); + } +} + +- (NSString*)unarchiveIdentifyID { + return [self unarchiveFromFile:[self identifyIdFilePath] asClass:[NSString class]]; +} + +- (NSString*)unarchiveAccountID { + return [self unarchiveFromFile:[self accountIDFilePath] asClass:[NSString class]]; +} + +- (void)archiveUploadSize:(NSNumber *)uploadSize { + NSString *filePath = [self uploadSizeFilePath]; + if (![self archiveObject:uploadSize withFilePath:filePath]) { + TDLogError(@"%@ unable to archive uploadSize", self); + } +} +- (NSNumber*)unarchiveUploadSize { + NSNumber* uploadSize = [self unarchiveFromFile:[self uploadSizeFilePath] asClass:[NSNumber class]]; + if (!uploadSize) { + uploadSize = [NSNumber numberWithInteger:30]; + } + return uploadSize; +} + +- (void)archiveUploadInterval:(NSNumber *)uploadInterval { + NSString *filePath = [self uploadIntervalFilePath]; + if (![self archiveObject:uploadInterval withFilePath:filePath]) { + TDLogError(@"%@ unable to archive uploadInterval", self); + } +} + +- (NSNumber*)unarchiveUploadInterval { + NSNumber* uploadInterval = [self unarchiveFromFile:[self uploadIntervalFilePath] asClass:[NSNumber class]]; + if (!uploadInterval) { + uploadInterval = [NSNumber numberWithInteger:30]; + } + return uploadInterval; +} + +- (void)archiveAccountID:(NSString *)accountID { + NSString *filePath = [self accountIDFilePath]; + if (![self archiveObject:[accountID copy] withFilePath:filePath]) { + TDLogError(@"%@ unable to archive accountID", self); + } +} + +- (void)archiveSuperProperties:(NSDictionary *)superProperties { + NSString *filePath = [self superPropertiesFilePath]; + if (![self archiveObject:[superProperties copy] withFilePath:filePath]) { + TDLogError(@"%@ unable to archive superProperties", self); + } +} + +- (NSDictionary*)unarchiveSuperProperties { + return [self unarchiveFromFile:[self superPropertiesFilePath] asClass:[NSDictionary class]]; +} + +- (void)archiveTrackPause:(BOOL)trackPause { + NSString *filePath = [self trackPauseFilePath]; + if (![self archiveObject:[NSNumber numberWithBool:trackPause] withFilePath:filePath]) { + TDLogError(@"%@ unable to archive trackPause", self); + } +} + +- (BOOL)unarchiveTrackPause { + NSNumber *trackPause = (NSNumber *)[self unarchiveFromFile:[self trackPauseFilePath] asClass:[NSNumber class]]; + return [trackPause boolValue]; +} + +- (void)archiveOptOut:(BOOL)optOut { + NSString *filePath = [self optOutFilePath]; + if (![self archiveObject:[NSNumber numberWithBool:optOut] withFilePath:filePath]) { + TDLogError(@"%@ unable to archive isOptOut", self); + } +} + +- (BOOL)unarchiveOptOut { + NSNumber *optOut = (NSNumber *)[self unarchiveFromFile:[self optOutFilePath] asClass:[NSNumber class]]; + return [optOut boolValue]; +} + +- (void)archiveIsEnabled:(BOOL)isEnabled { + NSString *filePath = [self enabledFilePath]; + if (![self archiveObject:[NSNumber numberWithBool:isEnabled] withFilePath:filePath]) { + TDLogError(@"%@ unable to archive isEnabled", self); + } +} + +- (BOOL)unarchiveEnabled { + NSNumber *enabled = (NSNumber *)[self unarchiveFromFile:[self enabledFilePath] asClass:[NSNumber class]]; + if (enabled == nil) { + return YES; + } else { + return [enabled boolValue]; + } +} + +- (void)archiveDeviceId:(NSString *)deviceId { + NSString *filePath = [self deviceIdFilePath]; + if (![self archiveObject:[deviceId copy] withFilePath:filePath]) { + TDLogError(@"%@ unable to archive deviceId", self); + } +} + +- (NSString *)unarchiveDeviceId { + return [self unarchiveFromFile:[self deviceIdFilePath] asClass:[NSString class]]; +} + +- (void)archiveInstallTimes:(NSString *)installTimes { + NSString *filePath = [self installTimesFilePath]; + if (![self archiveObject:[installTimes copy] withFilePath:filePath]) { + TDLogError(@"%@ unable to archive installTimes", self); + } +} + +- (NSString *)unarchiveInstallTimes { + return [self unarchiveFromFile:[self installTimesFilePath] asClass:[NSString class]]; +} + +- (BOOL)archiveObject:(id)object withFilePath:(NSString *)filePath { + @try { + if (![NSKeyedArchiver archiveRootObject:object toFile:filePath]) { + return NO; + } + } @catch (NSException *exception) { + TDLogError(@"Got exception: %@, reason: %@. You can only send to Thinking values that inherit from NSObject and implement NSCoding.", exception.name, exception.reason); + return NO; + } + + [self addSkipBackupAttributeToItemAtPath:filePath]; + return YES; +} + +- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *)filePathString { + NSURL *URL = [NSURL fileURLWithPath:filePathString]; + assert([[NSFileManager defaultManager] fileExistsAtPath:[URL path]]); + + NSError *error = nil; + BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES] + forKey:NSURLIsExcludedFromBackupKey error:&error]; + if (!success) { + TDLogError(@"Error excluding %@ from backup %@", [URL lastPathComponent], error); + } + return success; +} + +- (id)unarchiveFromFile:(NSString *)filePath asClass:(Class)class { + id unarchivedData = nil; + @try { + unarchivedData = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; + if (![unarchivedData isKindOfClass:class]) { + unarchivedData = nil; + } + } + @catch (NSException *exception) { + TDLogError(@"Error unarchive in %@", filePath); + unarchivedData = nil; + NSError *error = NULL; + BOOL removed = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; + if (!removed) { + TDLogDebug(@"Error remove file in %@, error: %@", filePath, error); + } + } + return unarchivedData; +} + +- (NSString *)superPropertiesFilePath { + return [self persistenceFilePath:@"superProperties"]; +} + +- (NSString *)accountIDFilePath { + return [self persistenceFilePath:@"accountID"]; +} + +- (NSString *)uploadSizeFilePath { + return [self persistenceFilePath:@"uploadSize"]; +} + +- (NSString *)uploadIntervalFilePath { + return [self persistenceFilePath:@"uploadInterval"]; +} + +- (NSString *)identifyIdFilePath { + return [self persistenceFilePath:@"identifyId"]; +} + +- (NSString *)sessionIdFilePath { + return [self persistenceFilePath:@"sessionId"]; +} + +- (NSString *)enabledFilePath { + return [self persistenceFilePath:@"isEnabled"]; +} + +- (NSString *)trackPauseFilePath { + return [self persistenceFilePath:@"trackPause"]; +} + +- (NSString *)optOutFilePath { + return [self persistenceFilePath:@"optOut"]; +} + +- (NSString *)deviceIdFilePath { + return [self persistenceFilePath:@"deviceId"]; +} + +- (NSString *)installTimesFilePath { + return [self persistenceFilePath:@"installTimes"]; +} + +- (NSString *)persistenceFilePath:(NSString *)data{ + NSString *filename = [NSString stringWithFormat:@"thinking-%@-%@.plist", self.appid, data]; + return [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject] + stringByAppendingPathComponent:filename]; +} + +- (NSString*)unarchiveOldLoginId { + return [[NSUserDefaults standardUserDefaults] objectForKey:@"thinkingdata_accountId"]; +} + +- (void)deleteOldLoginId { + [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"thinkingdata_accountId"]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + +- (NSString *)description { + NSMutableDictionary *dic = [NSMutableDictionary dictionary]; + [dic setObject:self.appid forKey:@"appid"]; + [dic setObject:[self unarchiveIdentifyID]?:@"" forKey:@"distincid"]; + [dic setObject:[self unarchiveAccountID]?:@"" forKey:@"accountID"]; + [dic setObject:[self unarchiveUploadSize] forKey:@"uploadSize"]; + [dic setObject:[self unarchiveUploadInterval] forKey:@"uploadInterval"]; + [dic setObject:[self unarchiveSuperProperties]?:@{} forKey:@"superProperties"]; + [dic setObject:[NSNumber numberWithBool:[self unarchiveOptOut] ]forKey:@"optOut"]; + [dic setObject:[NSNumber numberWithBool:[self unarchiveEnabled]] forKey:@"isEnabled"]; + [dic setObject:[NSNumber numberWithBool:[self unarchiveTrackPause]] forKey:@"isTrackPause"]; + [dic setObject:[self unarchiveDeviceId]?:@"" forKey:@"deviceId"]; + [dic setObject:[self unarchiveInstallTimes]?:@"" forKey:@"installTimes"]; + return [TDJSONUtil JSONStringForObject:dic]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m.meta new file mode 100644 index 0000000..5369eb7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8cea36e1d5b36b645ad9fb61e7396876 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h new file mode 100644 index 0000000..99b4daa --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h @@ -0,0 +1,14 @@ +#import + +@interface TDKeychainHelper : NSObject + +- (void)saveDeviceId:(NSString *)string; +- (void)saveInstallTimes:(NSString *)string; +- (void)readOldKeychain; + +- (NSString *)readDeviceId; +- (NSString *)readInstallTimes; +- (NSString *)getInstallTimesOld; +- (NSString *)getDeviceIdOld; + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h.meta new file mode 100644 index 0000000..c24c6a4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 56f4c1da82e73e04e89b68eee21ee250 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m new file mode 100644 index 0000000..17a4a34 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m @@ -0,0 +1,121 @@ +#import "TDKeychainHelper.h" +#import "TDLogging.h" + +static NSString * const TDKeychainService = @"com.thinkingddata.analytics.service"; +static NSString * const TDInstallTimes = @"com.thinkingddata.analytics.installtimes"; +static NSString * const TDDeviceID = @"com.thinkingddata.analytics.deviceid"; + +@interface TDKeychainHelper () + +@property (atomic, strong) NSDictionary *oldKeychain; + +@end + +@implementation TDKeychainHelper + +- (void)saveItem:(NSString *)string forKey:(NSString *)key { + NSData *encodeData = [string dataUsingEncoding:NSUTF8StringEncoding]; + NSString *originPassword = [self dataForKey:key]; + if (originPassword.length > 0) { + NSMutableDictionary *updateAttributes = [NSMutableDictionary dictionary]; + updateAttributes[(__bridge id)kSecValueData] = encodeData; + NSMutableDictionary *query = [self keychainQueryWithAccount:key]; + OSStatus statusCode = SecItemUpdate((__bridge CFDictionaryRef)query,(__bridge CFDictionaryRef)updateAttributes); + if (statusCode != noErr) { + TDLogError(@"Keychain Update Error" ); + } + } else { + NSMutableDictionary *attributes = [self keychainQueryWithAccount:key]; + attributes[(__bridge id)kSecValueData] = encodeData; + OSStatus statusCode = SecItemAdd((__bridge CFDictionaryRef)attributes, nil); + if (statusCode != noErr) { + TDLogError(@"Keychain Add Error"); + } + } +} + +- (void)saveInstallTimes:(NSString *)string { + [self saveItem:string forKey:TDInstallTimes]; +} + +- (void)saveDeviceId:(NSString *)string { + [self saveItem:string forKey:TDDeviceID]; +} + +- (NSString *)dataForKey:(NSString *)key { + NSMutableDictionary *attributes = [self keychainQueryWithAccount:key]; + attributes[(__bridge id)kSecMatchLimit] = (__bridge id)(kSecMatchLimitOne); + attributes[(__bridge id)kSecReturnData] = (__bridge id)(kCFBooleanTrue); + CFTypeRef data = nil; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)attributes,(CFTypeRef *)&data); + if (status != errSecSuccess) { + return nil; + } + NSData *encodeData = [NSData dataWithData:(__bridge NSData *)data]; + if (data) { + CFRelease(data); + } + if (encodeData) { + return [[NSString alloc] initWithData:encodeData encoding:NSUTF8StringEncoding]; + } + + return nil; +} + +- (void)readOldKeychain { + NSMutableDictionary *genericPasswordQuery = [[NSMutableDictionary alloc] init]; + [genericPasswordQuery setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; + [genericPasswordQuery setObject:@"ThinkingdataService" forKey:(__bridge id)kSecAttrGeneric]; + [genericPasswordQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; + [genericPasswordQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes]; + NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery]; + CFTypeRef cfDictionary = NULL; + if (SecItemCopyMatching((__bridge CFDictionaryRef)tempQuery, &cfDictionary) == noErr) { + NSMutableDictionary *returnDict = [NSMutableDictionary dictionaryWithDictionary:(__bridge_transfer NSDictionary *)cfDictionary]; + [returnDict setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; + [returnDict setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; + + OSStatus keychainErr = noErr; + CFTypeRef cfXmlData = NULL; + keychainErr = SecItemCopyMatching((__bridge CFDictionaryRef)returnDict, &cfXmlData); + + if (keychainErr == noErr) { + NSData *xmlData = (__bridge_transfer NSData *)cfXmlData; + NSPropertyListFormat fmt; + NSDictionary *resultsInfo = [NSPropertyListSerialization propertyListWithData:xmlData options:NSPropertyListMutableContainersAndLeaves format:&fmt error:nil]; + self.oldKeychain = resultsInfo; + } + } +} + +- (NSString *)getInstallTimesOld { + if (self.oldKeychain) + return [NSString stringWithFormat:@"%@",[self.oldKeychain objectForKey:@"thinking_setup_index"]]; + return nil; +} + +- (NSString *)getDeviceIdOld { + if (self.oldKeychain) + return [self.oldKeychain objectForKey:@"thinking_device_id"]; + return nil; +} + +- (NSString *)readDeviceId { + NSString *data = [self dataForKey:TDDeviceID]; + return data; +} + +- (NSString *)readInstallTimes { + NSString *data = [self dataForKey:TDInstallTimes]; + return data; +} + +- (NSMutableDictionary *)keychainQueryWithAccount:(NSString *)account { + NSMutableDictionary *query = [NSMutableDictionary dictionary]; + query[(__bridge id)kSecClass] = (__bridge id)kSecClassGenericPassword; + query[(__bridge id)kSecAttrService] = TDKeychainService; + query[(__bridge id)kSecAttrAccount] = account; + return query; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m.meta new file mode 100644 index 0000000..9323e7c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b23cf96728e0d454eb567e2acccc6894 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h new file mode 100644 index 0000000..7b86803 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h @@ -0,0 +1,19 @@ +#import + +@class TDEventRecord; + +@interface TDSqliteDataQueue : NSObject + ++ (TDSqliteDataQueue *)sharedInstanceWithAppid:(NSString *)appid; +- (NSInteger)addObject:(id)obj withAppid:(NSString *)appid; +- (NSArray *)getFirstRecords:(NSUInteger)recordSize withAppid:(NSString *)appid; +- (BOOL)removeFirstRecords:(NSUInteger)recordSize withAppid:(NSString *)appid; +- (void)deleteAll:(NSString *)appid; +- (NSInteger)sqliteCountForAppid:(NSString *)appid; +- (void)addColumnText:(NSString *)columnText; + +- (NSArray *)upadteRecordIds:(NSArray *)recordIds; +- (BOOL)removeDataWithuids:(NSArray *)recordIDs; + +@end + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h.meta new file mode 100644 index 0000000..7258368 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fdb622acdcbf89a4689922948177488e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m new file mode 100644 index 0000000..8ff907a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m @@ -0,0 +1,347 @@ +#import "TDSqliteDataQueue.h" +#import + +#import "TDLogging.h" +#import "TDJSONUtil.h" +#import "TDConfig.h" +#import "TDEventRecord.h" + +@implementation TDSqliteDataQueue { + sqlite3 *_database; + NSInteger _allmessageCount; +} + +- (void) closeDatabase { + sqlite3_close(_database); + sqlite3_shutdown(); +} + +- (void) dealloc { + [self closeDatabase]; +} + ++ (TDSqliteDataQueue *)sharedInstanceWithAppid:(NSString *)appid { + static TDSqliteDataQueue *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSString *filepath = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"TDData-data.plist"]; + sharedInstance = [[self alloc] initWithPath:filepath withAppid:appid]; + TDLogDebug(@"sqlite path:%@", filepath); + }); + return sharedInstance; +} + +- (id)initWithPath:(NSString *)filePath withAppid:(NSString *)appid { + self = [super init]; + if (sqlite3_initialize() != SQLITE_OK) { + return nil; + } + if (sqlite3_open_v2([filePath UTF8String], &_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK ) { + NSString *_sql = @"create table if not exists TDData (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT, appid TEXT, creatAt INTEGER)"; + char *errorMsg; + if (sqlite3_exec(_database, [_sql UTF8String], NULL, NULL, &errorMsg)==SQLITE_OK) { + } else { + return nil; + } + + _allmessageCount = [self sqliteCount]; + + if (![self isExistColumnInTable:@"appid"] || ![self isExistColumnInTable:@"creatAt"]) { + [self addColumn:appid]; + } else if (_allmessageCount > 0) { + [self delExpiredData]; + } + + if (![self isExistColumnInTable:@"uuid"]) { + [self addColumnText:@"uuid"]; + } + + } else { + return nil; + } + return self; +} + +- (void)addColumn:(NSString *)appid { + int epochInterval = [[NSDate date] timeIntervalSince1970]; + NSString *query; + if (appid.length > 0 && [appid isKindOfClass: [NSString class]]) + query = [NSString stringWithFormat:@"alter table TDData add 'appid' TEXT default \"%@\"", appid]; + else + query = [NSString stringWithFormat:@"alter table TDData add 'appid' TEXT"]; + NSString *query2 = [NSString stringWithFormat:@"alter table TDData add 'creatAt' INTEGER default %d ", epochInterval]; + char *errMsg; + @try { + sqlite3_exec(_database, [query UTF8String], NULL, NULL, &errMsg); + sqlite3_exec(_database, [query2 UTF8String], NULL, NULL, &errMsg); + } @catch (NSException *exception) { + TDLogError(@"addColumn: %@", exception); + } +} + +- (void)addColumnText:(NSString *)columnText { + NSString *query = [NSString stringWithFormat:@"alter table TDData add '%@' TEXT", columnText];; + char *errMsg; + @try { + sqlite3_exec(_database, [query UTF8String], NULL, NULL, &errMsg); + } @catch (NSException *exception) { + TDLogError(@"addColumn: %@", exception); + } +} + +- (BOOL)isExistColumnInTable:(NSString *)column { + sqlite3_stmt *statement = nil; + NSString *sql = [NSString stringWithFormat:@"PRAGMA table_info(TDData)"]; + if (sqlite3_prepare_v2(_database, [sql UTF8String], -1, &statement, NULL) != SQLITE_OK ) { + sqlite3_finalize(statement); + return NO; + } + while (sqlite3_step(statement) == SQLITE_ROW) { + NSString *columntem = [[NSString alloc] initWithCString:(char *)sqlite3_column_text(statement, 1) encoding:NSUTF8StringEncoding]; + + if ([column isEqualToString:columntem]) { + sqlite3_finalize(statement); + return YES; + } + } + sqlite3_finalize(statement); + return NO; +} + +- (void)delExpiredData { + NSTimeInterval oneDay = 24*60*60*1; + NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow: -oneDay * [TDConfig expirationDays]]; + int expirationDate = [date timeIntervalSince1970]; + [self removeOldRecords:expirationDate]; +} + +- (NSInteger)addObject:(id)obj withAppid:(NSString *)appid { + NSUInteger maxCacheSize = [TDConfig maxNumEvents]; + if (_allmessageCount >= maxCacheSize) { + [self removeFirstRecords:100 withAppid:nil]; + } + + NSString *jsonStr = [TDJSONUtil JSONStringForObject:obj]; + if (!jsonStr) { + return [self sqliteCountForAppid:appid]; + } + NSTimeInterval epochInterval = [[NSDate date] timeIntervalSince1970]; + NSString *query = @"INSERT INTO TDData(content, appid, creatAt) values(?, ?, ?)"; + sqlite3_stmt *insertStatement; + int rc; + rc = sqlite3_prepare_v2(_database, [query UTF8String],-1, &insertStatement, nil); + if (rc == SQLITE_OK) { + sqlite3_bind_text(insertStatement, 1, [jsonStr UTF8String], -1, SQLITE_TRANSIENT); + sqlite3_bind_text(insertStatement, 2, [appid UTF8String], -1, SQLITE_TRANSIENT); + sqlite3_bind_int(insertStatement, 3, epochInterval); + + rc = sqlite3_step(insertStatement); + if (rc == SQLITE_DONE) { + _allmessageCount ++; + } + } + + sqlite3_finalize(insertStatement); + return [self sqliteCountForAppid:appid]; +} + +- (NSArray *)getFirstRecords:(NSUInteger)recordSize withAppid:(NSString *)appid { + if (_allmessageCount == 0) { + return @[]; + } + + NSMutableArray *records = [[NSMutableArray alloc] init]; + NSString *query = @"SELECT id,content FROM TDData where appid=? ORDER BY id ASC LIMIT ?"; + sqlite3_stmt *stmt = NULL; + int rc = sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL); + if (rc == SQLITE_OK) { + sqlite3_bind_text(stmt, 1, [appid UTF8String], -1, SQLITE_TRANSIENT); + sqlite3_bind_int(stmt, 2, (int)recordSize); + while (sqlite3_step(stmt) == SQLITE_ROW) { + sqlite3_int64 index = sqlite3_column_int64(stmt, 0); + char *jsonChar = (char *)sqlite3_column_text(stmt, 1); + if (!jsonChar) { + continue; + } + + NSData *jsonData = [[NSString stringWithUTF8String:jsonChar] dataUsingEncoding:NSUTF8StringEncoding]; + NSError *err; + if (jsonData) { + NSDictionary *eventDict = [NSJSONSerialization JSONObjectWithData:jsonData + options:NSJSONReadingMutableContainers + error:&err]; + if (!err && [eventDict isKindOfClass:[NSDictionary class]]) { + [records addObject:[[TDEventRecord alloc] initWithIndex:[NSNumber numberWithLongLong:index] content:eventDict]]; + } + } + + } + } + sqlite3_finalize(stmt); + return records; +} + +- (BOOL)removeDataWithuids:(NSArray *)uids { + + if (uids.count == 0) { + return NO; + } + + NSString *query = [NSString stringWithFormat:@"DELETE FROM TDData WHERE uuid IN (%@);", [uids componentsJoinedByString:@","]]; + sqlite3_stmt *stmt; + + if (sqlite3_prepare_v2(_database, query.UTF8String, -1, &stmt, NULL) != SQLITE_OK) { + TDLogError(@"Delete records Error: %s", sqlite3_errmsg(_database)); + return NO; + } + BOOL success = YES; + if (sqlite3_step(stmt) != SQLITE_DONE) { + TDLogError(@"Delete records Error: %s", sqlite3_errmsg(_database)); + success = NO; + } + sqlite3_finalize(stmt); + _allmessageCount = [self sqliteCount]; + return YES; +} + +- (BOOL)removeFirstRecords:(NSUInteger)recordSize withAppid:(NSString *)appid { + NSString *query; + + if (appid.length == 0) { + query = @"DELETE FROM TDData WHERE id IN (SELECT id FROM TDData ORDER BY id ASC LIMIT ?)"; + } else { + query = @"DELETE FROM TDData WHERE id IN (SELECT id FROM TDData where appid=? ORDER BY id ASC LIMIT ?)"; + } + + sqlite3_stmt *stmt = NULL; + int rc = sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL); + + if (rc == SQLITE_OK) { + if (appid.length == 0) { + sqlite3_bind_int(stmt, 1, (int)recordSize); + } else { + sqlite3_bind_text(stmt, 1, [appid UTF8String], -1, SQLITE_TRANSIENT); + sqlite3_bind_int(stmt, 2, (int)recordSize); + } + rc = sqlite3_step(stmt); + if (rc != SQLITE_DONE && rc != SQLITE_OK) { + sqlite3_finalize(stmt); + return NO; + } + } else { + sqlite3_finalize(stmt); + return NO; + } + sqlite3_finalize(stmt); + _allmessageCount = [self sqliteCount]; + return YES; +} + +- (NSArray *)upadteRecordIds:(NSArray *)recordIds { + if (recordIds.count == 0) { + return @[]; + } + NSMutableArray *uids = [NSMutableArray arrayWithCapacity:recordIds.count]; + [recordIds enumerateObjectsUsingBlock:^(NSNumber *recordId, NSUInteger idx, BOOL * _Nonnull stop) { + NSString *uuid = [self rand13NumString]; + NSString *query = [NSString stringWithFormat:@"UPDATE TDData SET uuid = '%@' WHERE id = %lld;", uuid, [recordId longLongValue]]; + if ([self execUpdateSQL:query]) { + [uids addObject:uuid]; + } + }]; + return uids; +} + + + +-(NSString *)rand13NumString +{ + int NUMBER_OF_CHARS = 13; + + char data[NUMBER_OF_CHARS]; + + for (int x = 0; x < NUMBER_OF_CHARS; data[x++] = (char)('1' + (arc4random_uniform(9)))); + + NSString *numString = [[NSString alloc] initWithBytes:data length:NUMBER_OF_CHARS encoding:NSUTF8StringEncoding]; + + return numString; +} + + +- (BOOL)execUpdateSQL:(NSString *)sql { + + sqlite3_stmt *stmt; + if (sqlite3_prepare_v2(_database, sql.UTF8String, -1, &stmt, NULL) != SQLITE_OK) { + TDLogError(@"Update Records Error: %s", sqlite3_errmsg(_database)); + sqlite3_finalize(stmt); + return NO; + } + if (sqlite3_step(stmt) != SQLITE_DONE) { + TDLogError(@"Update Records Error: %s", sqlite3_errmsg(_database)); + sqlite3_finalize(stmt); + return NO; + } + sqlite3_finalize(stmt); + return YES; +} + +- (BOOL)removeOldRecords:(int)timestamp { + NSString *query = @"DELETE FROM TDData WHERE creatAt 0) { + sqlite3_bind_text(stmt, 1, [appid UTF8String], -1, SQLITE_TRANSIENT); + } + if (sqlite3_step(stmt) == SQLITE_ROW) { + count = sqlite3_column_int(stmt, 0); + } + } + + sqlite3_finalize(stmt); + return count; +} + +- (void)deleteAll:(NSString *)appid { + if ([appid isKindOfClass:[NSString class]] && appid.length > 0) { + NSString *query = @"DELETE FROM TDData where appid=? "; + + sqlite3_stmt *stmt = NULL; + int rc = sqlite3_prepare_v2(_database, [query UTF8String], -1, &stmt, NULL); + if (rc == SQLITE_OK) { + sqlite3_bind_text(stmt, 1, [appid UTF8String], -1, SQLITE_TRANSIENT); + sqlite3_step(stmt); + } + sqlite3_finalize(stmt); + + _allmessageCount = [self sqliteCount]; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m.meta new file mode 100644 index 0000000..a466d2e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 712995a22fa5736499cf8ceee5c55c8d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime.meta new file mode 100644 index 0000000..95fa968 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f6e4267f1ce3dd043898f973bd47c36a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle.meta new file mode 100644 index 0000000..ee7e8bb --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2aee577b95b3c0c4782dc787a550e3b3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.h new file mode 100644 index 0000000..679aeea --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.h @@ -0,0 +1,10 @@ +#import + +NS_ASSUME_NONNULL_BEGIN +@interface NSObject (TDSwizzle) + ++ (BOOL)td_swizzleMethod:(SEL)origSel withMethod:(SEL)altSel error:(NSError **)error; ++ (BOOL)td_swizzleClassMethod:(SEL)origSel withClassMethod:(SEL)altSel error:(NSError **)error; + +@end +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.h.meta new file mode 100644 index 0000000..74f43ea --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8833b514bb53bf94d8ab7f9b4eb7a56b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.m new file mode 100644 index 0000000..7f70f72 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.m @@ -0,0 +1,41 @@ +#import "NSObject+TDSwizzle.h" + +#if TARGET_OS_IPHONE +#import +#import +#else +#import +#endif + +@implementation NSObject (TDSwizzle) + ++ (BOOL)td_swizzleMethod:(SEL)origSel withMethod:(SEL)altSel error:(NSError **)error { + Method origMethod = class_getInstanceMethod(self, origSel); + if (!origMethod) { + return NO; + } + + Method altMethod = class_getInstanceMethod(self, altSel); + if (!altMethod) { + return NO; + } + + class_addMethod(self, + origSel, + class_getMethodImplementation(self, origSel), + method_getTypeEncoding(origMethod)); + class_addMethod(self, + altSel, + class_getMethodImplementation(self, altSel), + method_getTypeEncoding(altMethod)); + + method_exchangeImplementations(class_getInstanceMethod(self, origSel), class_getInstanceMethod(self, altSel)); + + return YES; +} + ++ (BOOL)td_swizzleClassMethod:(SEL)origSel withClassMethod:(SEL)altSel error:(NSError **)error { + return [object_getClass((id)self) td_swizzleMethod:origSel withMethod:altSel error:error]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.m.meta new file mode 100644 index 0000000..1a20695 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/NSObject+TDSwizzle.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3d7cb9512d7ca42449e99b51b16ae336 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.h new file mode 100644 index 0000000..48407ce --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.h @@ -0,0 +1,13 @@ +#import + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstrict-prototypes" +typedef void (^swizzleBlock)(); +#pragma clang diagnostic pop + +@interface TDSwizzler : NSObject + ++ (void)swizzleSelector:(SEL)aSelector onClass:(Class)aClass withBlock:(swizzleBlock)block named:(NSString *)aName; ++ (void)unswizzleSelector:(SEL)aSelector onClass:(Class)aClass named:(NSString *)aName; + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.h.meta new file mode 100644 index 0000000..371d31b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7c6adfd84fdf6a947809e7642dd1ea97 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.m new file mode 100644 index 0000000..78a4769 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.m @@ -0,0 +1,241 @@ +#import "TDSwizzler.h" + +#import +#import "TDLogging.h" + +#define MAPTABLE_ID(x) (__bridge id)((void *)x) + +#define MIN_ARGS 2 +#define MAX_ARGS 4 + +@interface TDSwizzle : NSObject + +@property (nonatomic, assign) Class class; +@property (nonatomic, assign) SEL selector; +@property (nonatomic, assign) IMP originalMethod; +@property (nonatomic, assign) uint numArgs; +@property (nonatomic, copy) NSMapTable *blocks; + +- (instancetype)initWithBlock:(swizzleBlock)aBlock + named:(NSString *)aName + forClass:(Class)aClass + selector:(SEL)aSelector + originalMethod:(IMP)aMethod; + +@end + +static NSMapTable *swizzles; + +static void td_swizzledMethod_2(id self, SEL _cmd) { + Method aMethod = class_getInstanceMethod([self class], _cmd); + TDSwizzle *swizzle = (TDSwizzle *)[swizzles objectForKey:MAPTABLE_ID(aMethod)]; + if (swizzle) { + ((void(*)(id, SEL))swizzle.originalMethod)(self, _cmd); + + NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; + swizzleBlock block; + while((block = [blocks nextObject])) { + + block(self, _cmd); + } + } +} + +static void td_swizzledMethod_3(id self, SEL _cmd, id arg) { + Method aMethod = class_getInstanceMethod([self class], _cmd); + TDSwizzle *swizzle = (TDSwizzle *)[swizzles objectForKey:MAPTABLE_ID(aMethod)]; + if (swizzle) { + ((void(*)(id, SEL, id))swizzle.originalMethod)(self, _cmd, arg); + + NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; + swizzleBlock block; + while((block = [blocks nextObject])) { + block(self, _cmd, arg); + } + } +} + +static void td_swizzledMethod_4(id self, SEL _cmd, id arg, id arg2) { + Method aMethod = class_getInstanceMethod([self class], _cmd); + TDSwizzle *swizzle = (TDSwizzle *)[swizzles objectForKey:(__bridge id)((void *)aMethod)]; + if (swizzle) { + ((void(*)(id, SEL, id, id))swizzle.originalMethod)(self, _cmd, arg, arg2); + + NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; + swizzleBlock block; + while((block = [blocks nextObject])) { + block(self, _cmd, arg, arg2); + } + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstrict-prototypes" +static void (*td_swizzledMethods[MAX_ARGS - MIN_ARGS + 1])() = {td_swizzledMethod_2, td_swizzledMethod_3, td_swizzledMethod_4}; +#pragma clang diagnostic pop + +@implementation TDSwizzler + ++ (void)load { + swizzles = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality) + valueOptions:(NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality)]; +} + ++ (TDSwizzle *)swizzleForMethod:(Method)aMethod { + return (TDSwizzle *)[swizzles objectForKey:MAPTABLE_ID(aMethod)]; +} + ++ (void)removeSwizzleForMethod:(Method)aMethod { + [swizzles removeObjectForKey:MAPTABLE_ID(aMethod)]; +} + ++ (void)setSwizzle:(TDSwizzle *)swizzle forMethod:(Method)aMethod { + [swizzles setObject:swizzle forKey:MAPTABLE_ID(aMethod)]; +} + ++ (BOOL)isLocallyDefinedMethod:(Method)aMethod onClass:(Class)aClass { + uint count; + BOOL isLocal = NO; + Method *methods = class_copyMethodList(aClass, &count); + for (NSUInteger i = 0; i < count; i++) { + if (aMethod == methods[i]) { + isLocal = YES; + break; + } + } + free(methods); + return isLocal; +} + ++ (void)swizzleSelector:(SEL)aSelector + onClass:(Class)aClass + withBlock:(swizzleBlock)aBlock + named:(NSString *)aName { + Method aMethod = class_getInstanceMethod(aClass, aSelector); + if (!aMethod) { + TDLogInfo(@"SwizzleException:Cannot find method for %@ on %@",NSStringFromSelector(aSelector), NSStringFromClass(aClass)); + return; + } + + uint numArgs = method_getNumberOfArguments(aMethod); + if (numArgs < MIN_ARGS || numArgs > MAX_ARGS) { + TDLogError(@"Cannot swizzle method with %d args", numArgs); + } + + IMP swizzledMethod = (IMP)td_swizzledMethods[numArgs - 2]; + [TDSwizzler swizzleSelector:aSelector onClass:aClass withBlock:aBlock andSwizzleMethod:swizzledMethod named:aName]; +} + ++ (void)swizzleSelector:(SEL)aSelector + onClass:(Class)aClass + withBlock:(swizzleBlock)aBlock + andSwizzleMethod:(IMP)aSwizzleMethod + named:(NSString *)aName { + Method aMethod = class_getInstanceMethod(aClass, aSelector); + if (!aMethod) { + TDLogError(@"Cannot find method for %@ on %@", NSStringFromSelector(aSelector), NSStringFromClass(aClass)); + } + + BOOL isLocal = [self isLocallyDefinedMethod:aMethod onClass:aClass]; + TDSwizzle *swizzle = [self swizzleForMethod:aMethod]; + + if (isLocal) { + if (!swizzle) { + IMP originalMethod = method_getImplementation(aMethod); + + // Replace the local implementation of this method with the swizzled one + method_setImplementation(aMethod, aSwizzleMethod); + + // Create and add the swizzle + @try { + swizzle = [[TDSwizzle alloc] initWithBlock:aBlock named:aName forClass:aClass selector:aSelector originalMethod:originalMethod]; + } @catch (NSException *exception) { + TDLogError(@"%@ error: %@", self, exception); + } + [self setSwizzle:swizzle forMethod:aMethod]; + } else { + [swizzle.blocks setObject:aBlock forKey:aName]; + } + } else { + IMP originalMethod = swizzle ? swizzle.originalMethod : method_getImplementation(aMethod); + + // Add the swizzle as a new local method on the class. + if (!class_addMethod(aClass, aSelector, aSwizzleMethod, method_getTypeEncoding(aMethod))) { + TDLogError(@"Could not add swizzled for %@::%@, even though it didn't already exist locally", NSStringFromClass(aClass), NSStringFromSelector(aSelector)); + } + // Now re-get the Method, it should be the one we just added. + Method newMethod = class_getInstanceMethod(aClass, aSelector); + if (aMethod == newMethod) { + TDLogError(@"Newly added method for %@::%@ was the same as the old method", NSStringFromClass(aClass), NSStringFromSelector(aSelector)); + } + + TDSwizzle *newSwizzle = [[TDSwizzle alloc] initWithBlock:aBlock named:aName forClass:aClass selector:aSelector originalMethod:originalMethod]; + [self setSwizzle:newSwizzle forMethod:newMethod]; + } +} + ++ (void)unswizzleSelector:(SEL)aSelector onClass:(Class)aClass { + Method aMethod = class_getInstanceMethod(aClass, aSelector); + TDSwizzle *swizzle = [self swizzleForMethod:aMethod]; + if (swizzle) { + method_setImplementation(aMethod, swizzle.originalMethod); + [self removeSwizzleForMethod:aMethod]; + } +} + +/* + Remove the named swizzle from the given class/selector. If aName is nil, remove all + swizzles for this class/selector + */ ++ (void)unswizzleSelector:(SEL)aSelector onClass:(Class)aClass named:(NSString *)aName { + Method aMethod = class_getInstanceMethod(aClass, aSelector); + TDSwizzle *swizzle = [self swizzleForMethod:aMethod]; + if (swizzle) { + if (aName) { + [swizzle.blocks removeObjectForKey:aName]; + } + if (!aName || [swizzle.blocks count] == 0) { + method_setImplementation(aMethod, swizzle.originalMethod); + [self removeSwizzleForMethod:aMethod]; + } + } +} + +@end + + +@implementation TDSwizzle + +- (instancetype)init { + if ((self = [super init])) { + self.blocks = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality) + valueOptions:(NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality)]; + } + return self; +} + +- (instancetype)initWithBlock:(swizzleBlock)aBlock + named:(NSString *)aName + forClass:(Class)aClass + selector:(SEL)aSelector + originalMethod:(IMP)aMethod { + if ((self = [self init])) { + self.class = aClass; + self.selector = aSelector; + self.originalMethod = aMethod; + [self.blocks setObject:aBlock forKey:aName]; + } + return self; +} + +- (NSString *)description { + NSString *descriptors = @""; + NSString *key; + NSEnumerator *keys = [self.blocks keyEnumerator]; + while ((key = [keys nextObject])) { + descriptors = [descriptors stringByAppendingFormat:@"\t%@ : %@\n", key, [self.blocks objectForKey:key]]; + } + return [NSString stringWithFormat:@"Swizzle on %@::%@ [\n%@]", NSStringFromClass(self.class), NSStringFromSelector(self.selector), descriptors]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.m.meta new file mode 100644 index 0000000..bd40e4b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/Swizzle/TDSwizzler.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: cc46d295149f8b84485badd5f3653efa +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.h new file mode 100644 index 0000000..ab653f6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.h @@ -0,0 +1,21 @@ +// +// TDRunTime.h +// ThinkingSDK +// +// Created by wwango on 2021/12/30. +// When used for plug-in, get the class name and parameters through reflection +// This class is not thread-safe, pay attention to multi-threading issues when using it + + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDRunTime : NSObject + +// start reason ++ (NSString *)getAppLaunchReason; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.h.meta new file mode 100644 index 0000000..5c97c81 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6f3e07eb8905a36469677bddca584114 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.m new file mode 100644 index 0000000..be550b7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.m @@ -0,0 +1,46 @@ +// +// TDRunTime.m +// ThinkingSDK +// +// Created by wwango on 2021/12/30. +// + +#import "TDRunTime.h" +#import "TDJSONUtil.h" +#import "TDPresetProperties+TDDisProperties.h" + +@implementation TDRunTime + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + ++ (NSString *)getAppLaunchReason { + // start reason + Class cls = NSClassFromString(@"TDAppLaunchReason"); + id appLaunch = [cls performSelector:@selector(sharedInstance)]; + + if (appLaunch && + [appLaunch respondsToSelector:@selector(appLaunchParams)] && + !TDPresetProperties.disableStartReason) + { + NSDictionary *startReason = [appLaunch performSelector:@selector(appLaunchParams)]; + NSString *url = startReason[@"url"]; + NSDictionary *data = startReason[@"data"]; + if (url.length == 0 && data.allKeys.count == 0) { + return @""; + } else { + if (data.allKeys.count == 0) { + startReason = @{@"url":url, @"data":@""}; + } + NSString *startReasonString = [TDJSONUtil JSONStringForObject:startReason]; + if (startReasonString && startReasonString.length) { + return startReasonString; + } + } + } + return @""; +} + +#pragma clang diagnostic pop + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.m.meta new file mode 100644 index 0000000..5f37026 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/TDRuntime/TDRunTime.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5361b2fccab66184b8f306bd0987c4ef +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util.meta new file mode 100644 index 0000000..0a3c927 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 40876fc7f83f1594d93ebb3ff33151f3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category.meta new file mode 100644 index 0000000..94c588b --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 49d08c4b28281f8449aed67ef920e13e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.h new file mode 100644 index 0000000..f701ea3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.h @@ -0,0 +1,7 @@ +#import + +@interface NSData (TDGzip) + ++ (NSData *)td_gzipData:(NSData *)dataa; + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.h.meta new file mode 100644 index 0000000..3d96714 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 68e858265092fc94d96a167c5ca829b9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.m new file mode 100644 index 0000000..4a11699 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.m @@ -0,0 +1,40 @@ +#import "NSData+TDGzip.h" +#import "zlib.h" +#import "TDLogging.h" + +@implementation NSData (TDGzip) + ++ (NSData *)td_gzipData:(NSData *)dataa { + if (!dataa || [dataa length] == 0) { + TDLogDebug(@"gzip error, return nil "); + return nil; + } + + z_stream zlibStreamStruct; + zlibStreamStruct.zalloc = Z_NULL; + zlibStreamStruct.zfree = Z_NULL; + zlibStreamStruct.opaque = Z_NULL; + zlibStreamStruct.total_out = 0; + zlibStreamStruct.next_in = (Bytef *)[dataa bytes]; + zlibStreamStruct.avail_in = (uInt)[dataa length]; + + int initError = deflateInit2(&zlibStreamStruct, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY); + + if (initError != Z_OK) return nil; + + NSMutableData *gzipData = [NSMutableData dataWithLength:[dataa length] * 1.01 + 21]; + int deflateStatus; + + do { + zlibStreamStruct.next_out = [gzipData mutableBytes] + zlibStreamStruct.total_out; + zlibStreamStruct.avail_out = (uInt)([gzipData length] - zlibStreamStruct.total_out); + deflateStatus = deflate(&zlibStreamStruct, Z_FINISH); + } while (deflateStatus == Z_OK); + + if (deflateStatus != Z_STREAM_END) return nil; + deflateEnd(&zlibStreamStruct); + [gzipData setLength:zlibStreamStruct.total_out]; + return gzipData; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.m.meta new file mode 100644 index 0000000..568adba --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSData+TDGzip.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6718c292ca5678b458ea7a3c9864abd7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.h new file mode 100644 index 0000000..fe51ece --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.h @@ -0,0 +1,25 @@ +// +// NSDate+TAFormat.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSDate (TAFormat) + +/// input the time zone +/// @param timeZone timeZone +- (double)ta_timeZoneOffset:(NSTimeZone *)timeZone; + +/// Format NSDate +/// @param timeZone timeZone +/// @param formatString formatString +- (NSString *)ta_formatWithTimeZone:(NSTimeZone *)timeZone formatString:(NSString *)formatString; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.h.meta new file mode 100644 index 0000000..d31cd33 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6e5c672f9a0263143ad34f226b5ea2df +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.m new file mode 100644 index 0000000..bf4f656 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.m @@ -0,0 +1,29 @@ +// +// NSDate+TAFormat.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import "NSDate+TAFormat.h" + +@implementation NSDate (TAFormat) + +- (double)ta_timeZoneOffset:(NSTimeZone *)timeZone { + if (!timeZone) { + return 0; + } + NSInteger sourceGMTOffset = [timeZone secondsFromGMTForDate:self]; + return (double)(sourceGMTOffset/3600.0); +} + +- (NSString *)ta_formatWithTimeZone:(NSTimeZone *)timeZone formatString:(NSString *)formatString { + NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init]; + timeFormatter.dateFormat = formatString; + timeFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + timeFormatter.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + timeFormatter.timeZone = timeZone; + return [timeFormatter stringFromDate:self]; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.m.meta new file mode 100644 index 0000000..660e694 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDate+TAFormat.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 08e73d7871941cd4dbb7e21affc899d6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h new file mode 100644 index 0000000..56f9f68 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h @@ -0,0 +1,17 @@ +// +// NSDictionary+TDJsonOutput.h +// ThinkingSDK +// +// Created by huangdiao on 2021/3/18. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSDictionary (TDJsonOutput) + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h.meta new file mode 100644 index 0000000..45b6c6d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 13c835183032d844e9cbddccf29c5989 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m new file mode 100644 index 0000000..759620d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m @@ -0,0 +1,30 @@ +// +// NSDictionary+TDJsonOutput.m +// ThinkingSDK +// +// Created by huangdiao on 2021/3/18. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "NSDictionary+TDJsonOutput.h" + +@implementation NSDictionary (TDJsonOutput) + +- (NSString *)descriptionWithLocale:(nullable id)locale { + if ([NSJSONSerialization isValidJSONObject:self]) { + NSString *output = nil; + @try { + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:nil]; + output = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + output = [output stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"]; + } + @catch (NSException *exception) { + output = self.description; + } + return output; + } else { + return self.description; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m.meta new file mode 100644 index 0000000..549e1d5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4a7df9995fb32b44daf31c0e92ccc368 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h new file mode 100644 index 0000000..b60ab65 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h @@ -0,0 +1,19 @@ +// +// NSObject+TDUtil.h +// ThinkingSDK +// +// Created by wwango on 2021/10/18. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSObject (TDUtil) + ++ (id)performSelector:(SEL)selector onTarget:(id)target withArguments:(NSArray *)arguments; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h.meta new file mode 100644 index 0000000..68c994c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d4a371b031eb30c46b1535e9cd79100e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m new file mode 100644 index 0000000..fb161a8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m @@ -0,0 +1,185 @@ +// +// NSObject+TDUtil.m +// ThinkingSDK +// +// Created by wwango on 2021/10/18. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "NSObject+TDUtil.h" +#import + +#if TARGET_OS_IOS +#import +#endif + +@implementation NSObject (TDUtil) + ++ (NSValue *)valueForPrimitivePointer:(void *)pointer objCType:(const char *)type +{ + // CASE marcro inspired by https://www.mikeash.com/pyblog/friday-qa-2013-02-08-lets-build-key-value-coding.html +#define CASE(ctype, selectorpart) \ +if(strcmp(type, @encode(ctype)) == 0) { \ +return [NSNumber numberWith ## selectorpart: *(ctype *)pointer]; \ +} + + CASE(BOOL, Bool); + CASE(unsigned char, UnsignedChar); + CASE(short, Short); + CASE(unsigned short, UnsignedShort); + CASE(int, Int); + CASE(unsigned int, UnsignedInt); + CASE(long, Long); + CASE(unsigned long, UnsignedLong); + CASE(long long, LongLong); + CASE(unsigned long long, UnsignedLongLong); + CASE(float, Float); + CASE(double, Double); + +#undef CASE + + NSValue *value = nil; + @try { + value = [NSValue valueWithBytes:pointer objCType:type]; + } @catch (NSException *exception) { + // Certain type encodings are not supported by valueWithBytes:objCType:. Just fail silently if an exception is thrown. + } + + return value; +} + + ++ (BOOL)isTollFreeBridgedValue:(id)value forCFType:(const char *)typeEncoding +{ + // See https://developer.apple.com/library/ios/documentation/general/conceptual/CocoaEncyclopedia/Toll-FreeBridgin/Toll-FreeBridgin.html +#define CASE(cftype, foundationClass) \ +if(strcmp(typeEncoding, @encode(cftype)) == 0) { \ +return [value isKindOfClass:[foundationClass class]]; \ +} + + CASE(CFArrayRef, NSArray); + CASE(CFAttributedStringRef, NSAttributedString); + CASE(CFCalendarRef, NSCalendar); + CASE(CFCharacterSetRef, NSCharacterSet); + CASE(CFDataRef, NSData); + CASE(CFDateRef, NSDate); + CASE(CFDictionaryRef, NSDictionary); + CASE(CFErrorRef, NSError); + CASE(CFLocaleRef, NSLocale); + CASE(CFMutableArrayRef, NSMutableArray); + CASE(CFMutableAttributedStringRef, NSMutableAttributedString); + CASE(CFMutableCharacterSetRef, NSMutableCharacterSet); + CASE(CFMutableDataRef, NSMutableData); + CASE(CFMutableDictionaryRef, NSMutableDictionary); + CASE(CFMutableSetRef, NSMutableSet); + CASE(CFMutableStringRef, NSMutableString); + CASE(CFNumberRef, NSNumber); + CASE(CFReadStreamRef, NSInputStream); + CASE(CFRunLoopTimerRef, NSTimer); + CASE(CFSetRef, NSSet); + CASE(CFStringRef, NSString); + CASE(CFTimeZoneRef, NSTimeZone); + CASE(CFURLRef, NSURL); + CASE(CFWriteStreamRef, NSOutputStream); + +#undef CASE + + return NO; +} + + ++ (id)performSelector:(SEL)selector onTarget:(id)target withArguments:(NSArray *)arguments{ + + if ([target respondsToSelector:selector]) { + + NSMethodSignature *signature = [target methodSignatureForSelector:selector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + [invocation setTarget:target]; + [invocation setSelector:selector]; + [invocation retainArguments]; + + // Always self and _cmd + NSUInteger numberOfArguments = [signature numberOfArguments]; + for (NSUInteger argumentIndex = 2; argumentIndex < numberOfArguments; argumentIndex++) { + NSUInteger argumentsArrayIndex = argumentIndex - 2; + + id argumentObject = [arguments count] > argumentsArrayIndex ? [arguments objectAtIndex:argumentsArrayIndex] : nil; + + // NSNull in the arguments array can be passed as a placeholder to indicate nil. We only need to set the argument if it will be non-nil. + if (argumentObject && ![argumentObject isKindOfClass:[NSNull class]]) { + const char *typeEncodingCString = [signature getArgumentTypeAtIndex:argumentIndex]; + if (typeEncodingCString[0] == @encode(id)[0] || typeEncodingCString[0] == @encode(Class)[0] || [self isTollFreeBridgedValue:argumentObject forCFType:typeEncodingCString]) { + // Object + [invocation setArgument:&argumentObject atIndex:argumentIndex]; + } else if (strcmp(typeEncodingCString, @encode(CGColorRef)) == 0 +#if TARGET_OS_IOS + && [argumentObject isKindOfClass:[UIColor class]] +#elif TARGET_OS_OSX + && [argumentObject isKindOfClass:[NSColor class]] +#endif + ) { + // Bridging UIColor to CGColorRef + CGColorRef colorRef = [argumentObject CGColor]; + [invocation setArgument:&colorRef atIndex:argumentIndex]; + } else if ([argumentObject isKindOfClass:[NSValue class]]) { + // Primitive boxed in NSValue + NSValue *argumentValue = (NSValue *)argumentObject; + + // Ensure that the type encoding on the NSValue matches the type encoding of the argument in the method signature + if (strcmp([argumentValue objCType], typeEncodingCString) != 0) { + return nil; + } + + NSUInteger bufferSize = 0; + @try { + // NSGetSizeAndAlignment barfs on type encoding for bitfields. + NSGetSizeAndAlignment(typeEncodingCString, &bufferSize, NULL); + } @catch (NSException *exception) { } + + if (bufferSize > 0) { + void *buffer = calloc(bufferSize, 1); + [argumentValue getValue:buffer]; + [invocation setArgument:buffer atIndex:argumentIndex]; + free(buffer); + } + } + } + } + + // Try to invoke the invocation but guard against an exception being thrown. + BOOL successfullyInvoked = NO; + @try { + // Some methods are not fit to be called... + // Looking at you -[UIResponder(UITextInputAdditions) _caretRect] + [invocation invoke]; + successfullyInvoked = YES; + } @catch (NSException *exception) { + // Bummer... + } + + // Retreive the return value and box if necessary. + id returnObject = nil; + if (successfullyInvoked) { + const char *returnType = [signature methodReturnType]; + if (returnType[0] == @encode(id)[0] || returnType[0] == @encode(Class)[0]) { + __unsafe_unretained id objectReturnedFromMethod = nil; + [invocation getReturnValue:&objectReturnedFromMethod]; + returnObject = objectReturnedFromMethod; + } else if (returnType[0] != @encode(void)[0]) { + void *returnValue = malloc([signature methodReturnLength]); + if (returnValue) { + [invocation getReturnValue:returnValue]; + returnObject = [self valueForPrimitivePointer:returnValue objCType:returnType]; + free(returnValue); + } + } + } + + return returnObject; + } + + return nil; +} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m.meta new file mode 100644 index 0000000..d356b06 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a4bcb79d46a613e48a006ce526d0db0e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.h new file mode 100644 index 0000000..b9447d3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.h @@ -0,0 +1,21 @@ +// +// NSString+TDString.h +// ThinkingSDK +// +// Created by wwango on 2021/10/11. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSString (TDString) + +- (NSString *)td_trim; + +- (NSString * _Nullable)ta_formatUrlString; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.h.meta new file mode 100644 index 0000000..3e14961 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ba9a4eb0474c4244bbe5c11e22a04e35 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.m new file mode 100644 index 0000000..5a95c99 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.m @@ -0,0 +1,37 @@ +// +// NSString+TDString.m +// ThinkingSDK +// +// Created by wwango on 2021/10/11. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "NSString+TDString.h" + +@implementation NSString (TDString) + +- (NSString *)td_trim { + NSString *string = [self stringByReplacingOccurrencesOfString:@" " withString:@""]; + string = [string stringByReplacingOccurrencesOfString:@"\r" withString:@""]; + string = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""]; + return string; +} + +- (NSString *)ta_formatUrlString { + NSString *urlString = [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + NSURL *url = [NSURL URLWithString:urlString]; + NSString *scheme = [url scheme]; + NSString *host = [url host]; + NSNumber *port = [url port]; + + if (scheme && scheme.length>0 && host && host.length>0) { + urlString = [NSString stringWithFormat:@"%@://%@", scheme, host]; + if (port && [port stringValue]) { + urlString = [urlString stringByAppendingFormat:@":%@", [port stringValue]]; + } + } + return urlString; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.m.meta new file mode 100644 index 0000000..883d982 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSString+TDString.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 95ad9c3976645de4387c9e40aac59f1a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h new file mode 100644 index 0000000..59eca16 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h @@ -0,0 +1,36 @@ +// +// TDCheck.h +// ThinkingSDK +// +// Created by wwango on 2021/9/10. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +#define TD_CHECK_NIL(_object) (_object == nil || [_object isKindOfClass:[NSNull class]]) + +#define TD_CHECK_CLASS(_object, _class) (!TD_CHECK_NIL(_object) && [_object isKindOfClass:[_class class]]) + +#define TD_CHECK_CLASS_NSString(_object) TD_CHECK_CLASS(_object, [NSString class]) +#define TD_CHECK_CLASS_NSNumber(_object) TD_CHECK_CLASS(_object, [NSNumber class]) +#define TD_CHECK_CLASS_NSArray(_object) TD_CHECK_CLASS(_object, [NSArray class]) +#define TD_CHECK_CLASS_NSData(_object) TD_CHECK_CLASS(_object, [NSData class]) +#define TD_CHECK_CLASS_NSDate(_object) TD_CHECK_CLASS(_object, [NSDate class]) +#define TD_CHECK_CLASS_NSDictionary(_object) TD_CHECK_CLASS(_object, [NSDictionary class]) + +#define TD_Valid_NSString(_object) (TD_CHECK_CLASS_NSString(_object) && (_object.length > 0)) +#define TD_Valid_NSArray(_object) (TD_CHECK_CLASS_NSArray(_object) && (_object.count > 0)) +#define TD_Valid_NSData(_object) (TD_CHECK_CLASS_NSData(_object) && (_object.length > 0)) +#define TD_Valid_NSDictionary(_object) (TD_CHECK_CLASS_NSDictionary(_object) && (_object.allKeys.count > 0)) + +@interface TDCheck : NSObject + ++ (NSDictionary *)td_checkToJSONObjectRecursive:(NSDictionary *)properties timeFormatter:(NSDateFormatter *)timeFormatter; + +@end + + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h.meta new file mode 100644 index 0000000..5801306 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6d59843018677ca4182201466233ff9a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m new file mode 100644 index 0000000..2bb5f8d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m @@ -0,0 +1,68 @@ +// +// TDCheck.m +// ThinkingSDK +// +// Created by wwango on 2021/9/10. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDCheck.h" +#import "TDLogging.h" + +@implementation TDCheck + ++ (NSDictionary *)td_checkToJSONObjectRecursive:(NSDictionary *)properties timeFormatter:(NSDateFormatter *)timeFormatter { + return (NSDictionary *)[self td_checkToObjectRecursive:properties timeFormatter:timeFormatter]; +} + +// Five basic types: list, time, Boolean, value, text, list only supports basic data types +// Advanced data types: object, object group ++ (NSObject *)td_checkToObjectRecursive:(NSObject *)properties timeFormatter:(NSDateFormatter *)timeFormatter { + if (TD_CHECK_NIL(properties)) { + return properties; + } else if (TD_CHECK_CLASS_NSDictionary(properties)) { + NSDictionary *propertyDic = [(NSDictionary *)properties copy]; + NSMutableDictionary *propertiesDic = [NSMutableDictionary dictionaryWithDictionary:propertyDic]; + for (NSString *key in [propertyDic keyEnumerator]) { + NSObject *newValue = [self td_checkToJSONObjectRecursive:propertyDic[key] timeFormatter:timeFormatter]; + propertiesDic[key] = newValue; + } + return propertiesDic; + } else if (TD_CHECK_CLASS_NSArray(properties)) { + NSMutableArray *arrayItem = [(NSArray *)properties mutableCopy]; + for (int i = 0; i < arrayItem.count ; i++) { + id item = [self td_checkToJSONObjectRecursive:arrayItem[i] timeFormatter:timeFormatter]; + if (item) arrayItem[i] = item; + } + return arrayItem; + } else if (TD_CHECK_CLASS_NSDate(properties)) { + NSString *dateStr = [timeFormatter stringFromDate:(NSDate *)properties]; + return dateStr; + } else { + return properties; + } +} + +// old method +//inline static NSDictionary *_td_old_checkToJSONObject(NSDictionary *properties, NSDateFormatter *timeFormatter) { +// NSMutableDictionary *propertiesDic = [NSMutableDictionary dictionaryWithDictionary:properties]; +// for (NSString *key in [properties keyEnumerator]) { +// if ([properties[key] isKindOfClass:[NSDate class]]) { +// NSString *dateStr = [timeFormatter stringFromDate:(NSDate *)properties[key]]; +// propertiesDic[key] = dateStr; +// } else if ([properties[key] isKindOfClass:[NSArray class]]) { +// NSMutableArray *arrayItem = [properties[key] mutableCopy]; +// for (int i = 0; i < arrayItem.count ; i++) { +// if ([arrayItem[i] isKindOfClass:[NSDate class]]) { +// NSString *dateStr = [timeFormatter stringFromDate:(NSDate *)arrayItem[i]]; +// arrayItem[i] = dateStr; +// } +// } +// propertiesDic[key] = arrayItem; +// } +// } +// return propertiesDic; +//} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m.meta new file mode 100644 index 0000000..4bd64e9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9ed93d9e3cf0500498b49536787e0b61 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h new file mode 100644 index 0000000..214daa8 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h @@ -0,0 +1,21 @@ +// +// TDCommonUtil.h +// ThinkingSDK +// +// Created by wwango on 2022/1/11. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCommonUtil : NSObject + ++ (NSString *)string:(NSString *)string; + ++ (NSDictionary *)dictionary:(NSDictionary *)dic; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h.meta new file mode 100644 index 0000000..5bf4ea5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: cd205eb0c6ec32145877ef77f64dcd51 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m new file mode 100644 index 0000000..5116c68 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m @@ -0,0 +1,28 @@ +// +// TDCommonUtil.m +// ThinkingSDK +// +// Created by wwango on 2022/1/11. +// + +#import "TDCommonUtil.h" + +@implementation TDCommonUtil + ++ (NSString *)string:(NSString *)string { + if ([string isKindOfClass:[NSString class]] && string.length) { + return string; + } else { + return @""; + } +} + ++ (NSDictionary *)dictionary:(NSDictionary *)dic { + if (dic && [dic isKindOfClass:[NSDictionary class]] && dic.allKeys.count) { + return dic; + } else { + return @{}; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m.meta new file mode 100644 index 0000000..486d1c3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fb2dd05f9814130458f92dba2dd1a3df +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.h new file mode 100644 index 0000000..2a1fe03 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.h @@ -0,0 +1,13 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDJSONUtil : NSObject + ++ (NSString *)JSONStringForObject:(id)object; + ++ (NSData *)JSONSerializeForObject:(id)object; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.h.meta new file mode 100644 index 0000000..050c5d3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a80322847cfa5ba4b826fd5cb9fd1104 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.m new file mode 100644 index 0000000..2acd8e4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.m @@ -0,0 +1,87 @@ +#import "TDJSONUtil.h" +//#import "TDLogging.h" + +@implementation TDJSONUtil + ++ (NSString *)JSONStringForObject:(id)obj { + NSData *data = [self JSONSerializeForObject:obj]; + if (!data) { + return nil; + } + return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; +} + ++ (NSData *)JSONSerializeForObject:(id)object { + + id obj = [TDJSONUtil JSONSerializableObjectForObject:object]; + NSData *data = nil; + + + + @try { + if ([NSJSONSerialization isValidJSONObject:obj]) { + data = [NSJSONSerialization dataWithJSONObject:obj options:0 error:NULL]; + } + } + @catch (NSException *exception) { + + } + + + return data; +} + ++ (id)JSONSerializableObjectForObject:(id)object { + if ([object isKindOfClass:[NSString class]]) { + return object; + } else if ([object isKindOfClass:[NSNumber class]]) { + + if ([object stringValue] && [[object stringValue] rangeOfString:@"."].location != NSNotFound) { + return [NSDecimalNumber decimalNumberWithDecimal:((NSNumber *)object).decimalValue]; + } + if ([object stringValue] && ([[object stringValue] rangeOfString:@"e"].location != NSNotFound || + [[object stringValue] rangeOfString:@"E"].location != NSNotFound )) { + return [NSDecimalNumber decimalNumberWithDecimal:((NSNumber *)object).decimalValue]; + } + return object; + } else if ([object isKindOfClass:[NSArray class]]) { + NSMutableArray *array = [[NSMutableArray alloc] init]; + for (id obj in (NSArray *)object) { + id convertedObj = [self JSONSerializableObjectForObject:obj]; + [self array:array addObject:convertedObj]; + } + return array; + } else if ([object isKindOfClass:[NSDictionary class]]) { + NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; + [(NSDictionary *)object enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *dictionaryStop) { + [self dictionary:dictionary + setObject:[self JSONSerializableObjectForObject:obj] + forKey:key]; + }]; + return dictionary; + } else if ([object isKindOfClass:[NSArray class]]) { + NSMutableArray *array = [[NSMutableArray alloc] init]; + for (id obj in (NSArray *)object) { + id convertedObj = [self JSONSerializableObjectForObject:obj]; + [self array:array addObject:convertedObj]; + } + object = array; + } + + NSString *s = [object description]; + return s; +} + ++ (void)array:(NSMutableArray *)array addObject:(id)object { + if (object) { + [array addObject:object]; + } +} + ++ (void)dictionary:(NSMutableDictionary *)dictionary setObject:(id)object forKey:(id)key { + if (object && key) { + dictionary[key] = object; + } +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.m.meta new file mode 100644 index 0000000..2fc6f4e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDJSONUtil.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 58724bd460dcacd48bb008ed4decb3a8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h new file mode 100644 index 0000000..5e9a897 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h @@ -0,0 +1,17 @@ +// +// TDUtil.h +// ThinkingSDK +// +// Created by LiHuanan on 2020/9/8. +// Copyright © 2020 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDSDKUtil : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h.meta new file mode 100644 index 0000000..d8ec1ff --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 398a8fe3fb3bae44381b2da177a388fa +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m new file mode 100644 index 0000000..db9c0f5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m @@ -0,0 +1,13 @@ +// +// TDUtil.m +// ThinkingSDK +// +// Created by LiHuanan on 2020/9/8. +// Copyright © 2020 thinkingdata. All rights reserved. +// + +#import "TDSDKUtil.h" + +@implementation TDSDKUtil + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m.meta new file mode 100644 index 0000000..f3296df --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 96c273bee8996874faae20ef59559f58 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h new file mode 100644 index 0000000..2abb6c5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h @@ -0,0 +1,24 @@ +// +// TDWeakProxy.h +// ThinkingSDK +// +// Created by wwango on 2021/9/15. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDWeakProxy : NSProxy + +@property (nullable, nonatomic, weak, readonly) id target; + +- (instancetype)initWithTarget:(id)target; + ++ (instancetype)proxyWithTarget:(id)target; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h.meta new file mode 100644 index 0000000..3e35466 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f5f7a816b14bcea4690d1452ed700482 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m new file mode 100644 index 0000000..81384d2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m @@ -0,0 +1,81 @@ +// +// TDWeakProxy.m +// ThinkingSDK +// +// Created by wwango on 2021/9/15. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDWeakProxy.h" + +@implementation TDWeakProxy + + +- (instancetype)initWithTarget:(id)target { + _target = target; + return self; +} + ++ (instancetype)proxyWithTarget:(id)target { + return [[TDWeakProxy alloc] initWithTarget:target]; +} + +- (id)forwardingTargetForSelector:(SEL)selector { + return _target; +} + +- (void)forwardInvocation:(NSInvocation *)invocation { + void *null = NULL; + [invocation setReturnValue:&null]; +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { + return [NSObject instanceMethodSignatureForSelector:@selector(init)]; +} + +- (BOOL)respondsToSelector:(SEL)aSelector { + return [_target respondsToSelector:aSelector]; +} + +- (BOOL)isEqual:(id)object { + return [_target isEqual:object]; +} + +- (NSUInteger)hash { + return [_target hash]; +} + +- (Class)superclass { + return [_target superclass]; +} + +- (Class)class { + return [_target class]; +} + +- (BOOL)isKindOfClass:(Class)aClass { + return [_target isKindOfClass:aClass]; +} + +- (BOOL)isMemberOfClass:(Class)aClass { + return [_target isMemberOfClass:aClass]; +} + +- (BOOL)conformsToProtocol:(Protocol *)aProtocol { + return [_target conformsToProtocol:aProtocol]; +} + +- (BOOL)isProxy { + return YES; +} + +- (NSString *)description { + return [_target description]; +} + +- (NSString *)debugDescription { + return [_target debugDescription]; +} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m.meta new file mode 100644 index 0000000..96c660a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 21cc253e8a9ac9745bfe2ce47ab30285 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast.meta new file mode 100644 index 0000000..25e14be --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cc51704aecadf9b48a38a3758f7a0101 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.h new file mode 100644 index 0000000..d42b0c5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.h @@ -0,0 +1,11 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDToastView : UIView + ++ (instancetype)showInWindow:(UIWindow *)window text:(NSString *)text duration:(NSTimeInterval)duration; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.h.meta new file mode 100644 index 0000000..012573d --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9691085f8ec08a948947c3905a2f3871 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.m new file mode 100644 index 0000000..f92a475 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.m @@ -0,0 +1,97 @@ +#import "TDToastView.h" + +@implementation TDToastView +{ + UITextView *_textView; +} + +#define TOAST_HORIZONTAL_PADDING 20.0 +#define TOAST_VERTICAL_PADDING 15.0 + +- (instancetype)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + _textView = [[UITextView alloc] initWithFrame:CGRectZero]; + _textView.opaque = NO; + _textView.editable = NO; + _textView.selectable = NO; + _textView.textColor = [UIColor whiteColor]; + _textView.backgroundColor = [UIColor clearColor]; + _textView.userInteractionEnabled = YES; + [self addSubview:_textView]; + self.alpha = 0.9; + self.backgroundColor = [UIColor darkGrayColor]; + self.layer.cornerRadius = 20.0; + self.opaque = NO; + self.userInteractionEnabled = YES; + [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_tapHandler:)]]; + } + return self; +} + ++ (instancetype)showInWindow:(UIWindow *)window text:(NSString *)text duration:(NSTimeInterval)duration { + TDToastView *toast = [[self alloc] initWithFrame:CGRectZero]; + toast.text = text; + [toast showInWindow:window duration:duration]; + return toast; +} + + +- (void)showInWindow:(UIWindow *)window duration:(NSTimeInterval)duration { + if (window == nil) { + return; + } + CGRect windowBounds = CGRectInset(window.bounds, 20.0, 20.0); + CGRect toastBounds = CGRectZero; + toastBounds.size = [self sizeThatFits:windowBounds.size]; + self.bounds = toastBounds; + self.center = CGPointMake(CGRectGetMidX(windowBounds), CGRectGetMidY(windowBounds)); + CGFloat alpha = self.alpha; + self.alpha = 0.0; + [window addSubview:self]; + [UIView animateWithDuration:0.3 animations:^{ + self.alpha = alpha; + } completion:^(BOOL finishedShowing) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self dismiss]; + }); + }]; +} + +- (NSString *)text { + return _textView.text; +} + +- (void)setText:(NSString *)text { + _textView.text = text; +} + +- (void)dismiss { + if (!self.superview) { + return; + } + [UIView animateWithDuration:0.3 animations:^{ + self.alpha = 0.0; + } completion:^(BOOL finishedHiding) { + [self removeFromSuperview]; + }]; +} + +- (void)_tapHandler:(UITapGestureRecognizer *)tapGestureRecognizer { + [self dismiss]; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + _textView.frame = CGRectInset(self.bounds, TOAST_HORIZONTAL_PADDING, TOAST_VERTICAL_PADDING); +} + +- (CGSize)sizeThatFits:(CGSize)size { + CGSize textConstrainedSize = CGSizeMake(size.width - 2 * TOAST_HORIZONTAL_PADDING, + size.height - 2 * TOAST_VERTICAL_PADDING); + CGSize textSize = [_textView sizeThatFits:textConstrainedSize]; + CGFloat width = MIN(size.width, textSize.width + 2 * TOAST_HORIZONTAL_PADDING); + CGFloat height = MIN(size.height, textSize.height + 2 * TOAST_VERTICAL_PADDING); + return CGSizeMake(width, height); +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.m.meta new file mode 100644 index 0000000..4be0c0f --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/Util/Toast/TDToastView.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f05665845b810844482546c77ad1e943 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main.meta new file mode 100644 index 0000000..ae7c864 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 02506238c8c85a240b6a46d12aa27808 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m new file mode 100644 index 0000000..0cb2b74 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m @@ -0,0 +1,119 @@ +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDLogging.h" + +@implementation LightThinkingAnalyticsSDK + +- (instancetype)initWithAPPID:(NSString *)appID withServerURL:(NSString *)serverURL withConfig:(TDConfig *)config { + if (self = [self initLight:appID withServerURL:serverURL withConfig:config]) { + } + + return self; +} + +- (void)login:(NSString *)accountId { + if ([self hasDisabled]) + return; + + if (![accountId isKindOfClass:[NSString class]] || accountId.length == 0) { + TDLogError(@"accountId invald", accountId); + return; + } + + @synchronized (self.accountId) { + self.accountId = accountId; + } +} + +- (void)logout { + if ([self hasDisabled]) + return; + + @synchronized (self.accountId) { + self.accountId = nil; + }; +} + +- (void)identify:(NSString *)distinctId { + if ([self hasDisabled]) + return; + + if (![distinctId isKindOfClass:[NSString class]] || distinctId.length == 0) { + TDLogError(@"identify cannot null"); + return; + } + + @synchronized (self.identifyId) { + self.identifyId = distinctId; + }; +} + +- (NSString *)getDistinctId { + return [self.identifyId copy]; +} + +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType { + return; +} + +- (void)flush { + return; +} + +#pragma mark - EnableTracking +- (void)enableTracking:(BOOL)enabled { + TDLogDebug(@"%@light instance: enableTracking...", self); + self.isEnabled = enabled; +} + +- (void)optOutTracking { + TDLogDebug(@"%@light instance: optOutTracking...", self); + self.isEnabled = NO; +} + +- (void)optOutTrackingAndDeleteUser { + TDLogDebug(@"%@light instance: optOutTrackingAndDeleteUser...", self); + self.isEnabled = NO; +} + +- (void)optInTracking { + TDLogDebug(@"%@light instance: optInTracking...", self); + self.isEnabled = YES; +} + + + +- (void)setTrackStatus: (TATrackStatus)status { + switch (status) { + + case TATrackStatusPause: { + TDLogDebug(@"%@light instance - switchTrackStatus: TATrackStatusStop...", self); + self.isEnabled = NO; + break; + } + + case TATrackStatusStop: { + TDLogDebug(@"%@light instance - switchTrackStatus: TATrackStatusStopAndClean...", self); + self.isEnabled = NO; + break; + } + + case TATrackStatusSaveOnly: { + TDLogDebug(@"%@light instance - switchTrackStatus: TATrackStatusPausePost...", self); + self.trackPause = YES; + break; + } + + case TATrackStatusNormal: { + TDLogDebug(@"%@light instance - switchTrackStatus: TATrackStatusRestartAll...", self); + self.trackPause = NO; + self.isEnabled = YES; + [self flush]; + break; + } + default: + break; + } +} + + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m.meta new file mode 100644 index 0000000..33974d5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1848db7016d10b04bb1e6d7e0ac74a54 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.h new file mode 100644 index 0000000..9cfb65a --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.h @@ -0,0 +1,33 @@ +// +// TDEventRecord.h +// ThinkingSDK +// +// Created by wwango on 2022/1/24. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDEventRecord : NSObject + +// Due to historical reasons, there is no event identifier stored in the database +// Record index when fetching data, update uuid before reporting data, remove data according to uuid after successful reporting +@property (nonatomic, copy) NSString *uuid; +@property (nonatomic, strong) NSNumber *index; + +@property (nonatomic, copy, readonly) NSString *content; +@property (nonatomic, copy, readonly) NSDictionary *event; +@property (nonatomic, assign) BOOL encrypted; +@property (nonatomic, copy, readonly) NSString *ekey; + + +- (instancetype)initWithIndex:(NSNumber *)index content:(NSDictionary *)content; +- (instancetype)initWithContent:(NSDictionary *)content; + +- (void)setSecretObject:(NSDictionary *)obj; + +- (NSString *)flushContent:(NSString *)appid; +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.h.meta new file mode 100644 index 0000000..d46ae79 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d24320f1dee61fa4ca4b012fdad15776 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.m new file mode 100644 index 0000000..bd97a4c --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.m @@ -0,0 +1,97 @@ +// +// TDEventRecord.m +// ThinkingSDK +// +// Created by wwango on 2022/1/24. +// + +#import "TDEventRecord.h" + +#import "TDJSONUtil.h" + +static NSString * const TDEncryptRecordKeyEKey = @"ekey"; +static NSString * const TDEncryptRecordKeyPayload = @"payload"; + +@implementation TDEventRecord { + NSMutableDictionary *_event; +} + + +- (instancetype)initWithEvent:(NSDictionary *)event type:(NSString *)type { + if (self = [super init]) { + + _event = [event mutableCopy]; + _encrypted = _event[TDEncryptRecordKeyEKey] != nil; + } + return self; +} + +- (instancetype)initWithUUID:(NSString *)uuid content:(NSDictionary *)content { + if (self = [super init]) { + _uuid = uuid; + + if (content && [content isKindOfClass:[NSDictionary class]]) { + _event = [NSMutableDictionary dictionaryWithDictionary:content]; + _encrypted = _event[TDEncryptRecordKeyEKey] != nil; + } + } + return self; +} + +- (instancetype)initWithIndex:(NSNumber *)index content:(NSDictionary *)content { + if (self = [super init]) { + _index = index; + + if (content && [content isKindOfClass:[NSDictionary class]]) { + _event = [NSMutableDictionary dictionaryWithDictionary:content]; + _encrypted = _event[TDEncryptRecordKeyEKey] != nil; + } + } + return self; +} + +- (instancetype)initWithContent:(NSDictionary *)content { + if (self = [super init]) { + if (content && [content isKindOfClass:[NSDictionary class]]) { + _event = [NSMutableDictionary dictionaryWithDictionary:content]; + _encrypted = _event[TDEncryptRecordKeyEKey] != nil; + } + } + return self; +} + +- (NSString *)ekey { + return _event[TDEncryptRecordKeyEKey]; +} + +- (void)setSecretObject:(NSDictionary *)obj { + if (!obj || ![obj isKindOfClass:[NSDictionary class]]) { + return; + } + [_event removeAllObjects]; + [_event addEntriesFromDictionary:obj]; + + _encrypted = YES; +} + +- (BOOL)isValid { + return self.event.count > 0; +} + +- (NSString *)content { + return [TDJSONUtil JSONStringForObject:self.event]; +} + +- (NSString *)flushContent:(NSString *)appid { + if (![self isValid]) { + return nil; + } + + UInt64 time = [[NSDate date] timeIntervalSince1970] * 1000; + _event[@"#flush_time"] = @(time); + _event[@"#app_id"] =appid; + + return self.content; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.m.meta new file mode 100644 index 0000000..df8f3da --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/TDEventRecord.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6997f69a90a6b3f419fb56a1addd7a94 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h new file mode 100644 index 0000000..bd693f4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h @@ -0,0 +1,520 @@ +#import + +#if TARGET_OS_IOS +#import + +#if __has_include() +#import +#else +#import "TAAutoTrackPublicHeader.h" +#endif + +#elif TARGET_OS_OSX +#import +#endif + +#if __has_include() +#import +#else +#import "TDFirstEventModel.h" +#endif + +#if __has_include() +#import +#else +#import "TDEditableEventModel.h" +#endif + + +#if __has_include() +#import +#else +#import "TDConfig.h" +#endif + +#if __has_include() +#import +#else +#import "TDPresetProperties.h" +#endif + + +NS_ASSUME_NONNULL_BEGIN + +/** + SDK VERSION = 2.8.6 + ThinkingData API + + ## Initialization + + ```objective-c + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK startWithAppId:@"YOUR_APPID" withUrl:@"YOUR_SERVER_URL"]; + ``` + + ## Track Event + + ```objective-c + instance.track("some_event"); + ``` +or + ```objective-c + [[ThinkingAnalyticsSDK sharedInstanceWithAppid:@"YOUR_APPID"] track:@"some_event"]; + ``` + If you only have one instance in your project, you can also use + ```objective-c + [[ThinkingAnalyticsSDK sharedInstance] track:@"some_event"]; + ``` + ## Detailed Documentation + http://doc.thinkingdata.cn/tgamanual/installation/ios_sdk_installation.html + + */ +@interface ThinkingAnalyticsSDK : NSObject + +#pragma mark - Tracking + +/** + Get default instance + + @return SDK instance + */ ++ (nullable ThinkingAnalyticsSDK *)sharedInstance; + +/** + Get one instance according to appid or instanceName + + @param appid APP ID or instanceName + @return SDK instance + */ ++ (nullable ThinkingAnalyticsSDK *)sharedInstanceWithAppid:(NSString *)appid; + +/** + Initialization method + After the SDK initialization is complete, the saved instance can be obtained through this api + + @param appId appId + @param url server url + @return one instance + */ ++ (ThinkingAnalyticsSDK *)startWithAppId:(NSString *)appId withUrl:(NSString *)url; + +/** + Initialization method + After the SDK initialization is complete, the saved instance can be obtained through this api + + @param config initialization configuration + @return one instance + */ ++ (ThinkingAnalyticsSDK *)startWithConfig:(nullable TDConfig *)config; + +/** + Initialization method + After the SDK initialization is complete, the saved instance can be obtained through this api + + @param appId appId + @param url server url + @param config initialization configuration object + @return one instance + */ ++ (ThinkingAnalyticsSDK *)startWithAppId:(NSString *)appId withUrl:(NSString *)url withConfig:(nullable TDConfig *)config; + + +#pragma mark - Action Track + +/** + Track Events + + @param event event name + */ +- (void)track:(NSString *)event; + + +/** + Track Events + + @param event event name + @param propertieDict event properties + */ +- (void)track:(NSString *)event properties:(nullable NSDictionary *)propertieDict; + +/** + Track Events + + @param event event name + @param propertieDict event properties + @param time event trigger time + */ +- (void)track:(NSString *)event properties:(nullable NSDictionary *)propertieDict time:(NSDate *)time __attribute__((deprecated("please use track:properties:time:timeZone: method"))); + +/** + Track Events + + @param event event name + @param propertieDict event properties + @param time event trigger time + @param timeZone event trigger time time zone + */ +- (void)track:(NSString *)event properties:(nullable NSDictionary *)propertieDict time:(NSDate *)time timeZone:(NSTimeZone *)timeZone; + +/** + Track Events + + @param eventModel event Model + */ +- (void)trackWithEventModel:(TDEventModel *)eventModel; + +/** + Get the events collected in the App Extension and report them + + @param appGroupId The app group id required for data sharing + */ +- (void)trackFromAppExtensionWithAppGroupId:(NSString *)appGroupId; + +#pragma mark - + +/** + Timing Events + Record the event duration, call this method to start the timing, stop the timing when the target event is uploaded, and add the attribute #duration to the event properties, in seconds. + */ +- (void)timeEvent:(NSString *)event; + +/** + Identify + Set the distinct ID to replace the default UUID distinct ID. + */ +- (void)identify:(NSString *)distinctId; + +/** + Get Distinctid + Get a visitor ID: The #distinct_id value in the reported data. + */ +- (NSString *)getDistinctId; + +/** + Get sdk version + */ ++ (NSString *)getSDKVersion; + +/** + Login + Set the account ID. Each setting overrides the previous value. Login events will not be uploaded. + + @param accountId account ID + */ +- (void)login:(NSString *)accountId; + +/** + Logout + Clearing the account ID will not upload user logout events. + */ +- (void)logout; + +/** + User_Set + Sets the user property, replacing the original value with the new value if the property already exists. + + @param properties user properties + */ +- (void)user_set:(NSDictionary *)properties; + +/** + User_Set + + @param properties user properties + @param time event trigger time +*/ +- (void)user_set:(NSDictionary *)properties withTime:(NSDate * _Nullable)time; + +/** + User_Unset + + @param propertyName user properties + */ +- (void)user_unset:(NSString *)propertyName; + +/** + User_Unset + Reset user properties. + + @param propertyName user properties + @param time event trigger time +*/ +- (void)user_unset:(NSString *)propertyName withTime:(NSDate * _Nullable)time; + +/** + User_SetOnce + Sets a single user attribute, ignoring the new attribute value if the attribute already exists. + + @param properties user properties + */ +- (void)user_setOnce:(NSDictionary *)properties; + +/** + User_SetOnce + + @param properties user properties + @param time event trigger time +*/ +- (void)user_setOnce:(NSDictionary *)properties withTime:(NSDate * _Nullable)time; + +/** + User_Add + Adds the numeric type user attributes. + + @param properties user properties + */ +- (void)user_add:(NSDictionary *)properties; + +/** + User_Add + + @param properties user properties + @param time event trigger time +*/ +- (void)user_add:(NSDictionary *)properties withTime:(NSDate * _Nullable)time; + +/** + User_Add + + @param propertyName propertyName + @param propertyValue propertyValue + */ +- (void)user_add:(NSString *)propertyName andPropertyValue:(NSNumber *)propertyValue; + +/** + User_Add + + @param propertyName propertyName + @param propertyValue propertyValue + @param time event trigger time +*/ +- (void)user_add:(NSString *)propertyName andPropertyValue:(NSNumber *)propertyValue withTime:(NSDate * _Nullable)time; + +/** + User_Delete + Delete the user attributes,This operation is not reversible and should be performed with caution. + */ +- (void)user_delete; + +/** + User_Delete + + @param time event trigger time + */ +- (void)user_delete:(NSDate * _Nullable)time; + +/** + User_Append + Append a user attribute of the List type. + + @param properties user properties +*/ +- (void)user_append:(NSDictionary *)properties; + +/** + User_Append + The element appended to the library needs to be done to remove the processing,and then import. + + @param properties user properties + @param time event trigger time +*/ +- (void)user_append:(NSDictionary *)properties withTime:(NSDate * _Nullable)time; + +/** + User_UniqAppend + + @param properties user properties +*/ +- (void)user_uniqAppend:(NSDictionary *)properties; + +/** + User_UniqAppend + + @param properties user properties + @param time event trigger time +*/ +- (void)user_uniqAppend:(NSDictionary *)properties withTime:(NSDate * _Nullable)time; + ++ (void)setCustomerLibInfoWithLibName:(NSString *)libName libVersion:(NSString *)libVersion; + +/** + Static Super Properties + Set the public event attribute, which will be included in every event uploaded after that. The public event properties are saved without setting them each time. + * + */ +- (void)setSuperProperties:(NSDictionary *)properties; + +/** + Unset Super Property + Clears a public event attribute. + */ +- (void)unsetSuperProperty:(NSString *)property; + +/** + Clear Super Properties + Clear all public event attributes. + */ +- (void)clearSuperProperties; + +/** + Get Static Super Properties + Gets the public event properties that have been set. + */ +- (NSDictionary *)currentSuperProperties; + +/** + Dynamic super properties + Set dynamic public properties. Each event uploaded after that will contain a public event attribute. + */ +- (void)registerDynamicSuperProperties:(NSDictionary *(^)(void))dynamicSuperProperties; + + +/** + Dynamic super properties in auto track environment + Set dynamic public properties. Each event uploaded after that will contain a public event attribute. + */ +- (void)setAutoTrackDynamicProperties:(NSDictionary *(^)(void))dynamicSuperProperties; + +/** + Register TD error callback + + @param errorCallback + code = 10001, + ext = "string or json string", + errorMsg = "error" + */ +- (void)registerErrorCallback:(void(^)(NSInteger code, NSString * _Nullable errorMsg, NSString * _Nullable ext))errorCallback; + +/** + Gets prefabricated properties for all events. + */ +- (TDPresetProperties *)getPresetProperties; + +/** + Set the network conditions for uploading. By default, the SDK will set the network conditions as 3G, 4G and Wifi to upload data + */ +- (void)setNetworkType:(ThinkingAnalyticsNetworkType)type; + +#if TARGET_OS_IOS + +/** + Enable Auto-Tracking + + @param eventType Auto-Tracking type + + detailed documentation http://doc.thinkingdata.cn/tgamanual/installation/ios_sdk_installation/ios_sdk_autotrack.html + */ +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType; + +/** + Enable the auto tracking function. + + @param eventType Auto-Tracking type + @param properties properties + */ +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType properties:(NSDictionary *)properties; + +/** + Enable the auto tracking function. + + @param eventType Auto-Tracking type + @param callback callback + In the callback, eventType indicates the type of automatic collection, properties indicates the event properties before storage, and this block can return a dictionary for adding new properties + */ +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType callback:(NSDictionary *(^)(ThinkingAnalyticsAutoTrackEventType eventType, NSDictionary *properties))callback; + +/** + Set and Update the value of a custom property for Auto-Tracking + + @param eventType A list of ThinkingAnalyticsAutoTrackEventType, indicating the types of automatic collection events that need to be enabled + @param properties properties + */ +- (void)setAutoTrackProperties:(ThinkingAnalyticsAutoTrackEventType)eventType properties:(NSDictionary *)properties; + +/** + Ignore the Auto-Tracking of a page + + @param controllers Ignore the name of the UIViewController + */ +- (void)ignoreAutoTrackViewControllers:(NSArray *)controllers; + +/** + Ignore the Auto-Tracking of click event + + @param aClass ignored controls Class + */ +- (void)ignoreViewType:(Class)aClass; + +#endif + +//MARK: - + +/** + Get DeviceId + */ +- (NSString *)getDeviceId; + +/** + H5 is connected with the native APP SDK and used in conjunction with the addWebViewUserAgent interface + + @param webView webView + @param request NSURLRequest request + @return YES:Process this request NO: This request has not been processed + + detailed documentation http://doc.thinkingdata.cn/tgamanual/installation/h5_app_integrate.html + */ +- (BOOL)showUpWebView:(id)webView WithRequest:(NSURLRequest *)request; + +/** + When connecting data with H5, you need to call this interface to configure UserAgent + */ +- (void)addWebViewUserAgent; + +/** + Set Log level + + */ ++ (void)setLogLevel:(TDLoggingLevel)level; + +/** + Empty the cache queue. When this api is called, the data in the current cache queue will attempt to be reported. + If the report succeeds, local cache data will be deleted. + */ +- (void)flush; + +/** + Switch reporting status + + @param status TATrackStatus reporting status + */ +- (void)setTrackStatus: (TATrackStatus)status; + +- (void)enableTracking:(BOOL)enabled DEPRECATED_MSG_ATTRIBUTE("Please use instance method setTrackStatus: TATrackStatusPause"); + +- (void)optOutTracking DEPRECATED_MSG_ATTRIBUTE("Please use instance method setTrackStatus: TATrackStatusStop"); + +- (void)optOutTrackingAndDeleteUser DEPRECATED_MSG_ATTRIBUTE("Please use instance method setTrackStatus: TATrackStatusStop"); + +- (void)optInTracking DEPRECATED_MSG_ATTRIBUTE("Please use instance method setTrackStatus: TATrackStatusNormal"); + +/** + Create a light instance + */ +- (ThinkingAnalyticsSDK *)createLightInstance; + ++ (void)calibrateTimeWithNtp:(NSString *)ntpServer; + ++ (void)calibrateTime:(NSTimeInterval)timestamp; + +- (NSString *)getTimeString:(NSDate *)date; + +#if TARGET_OS_IOS +- (void)enableThirdPartySharing:(TAThirdPartyShareType)type; + +- (void)enableThirdPartySharing:(TAThirdPartyShareType)type customMap:(NSDictionary *)customMap; +#endif + ++ (nullable NSString *)getLocalRegion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h.meta new file mode 100644 index 0000000..c575d2e --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0921e73f1e308ce479c5531a7f1224f1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m new file mode 100644 index 0000000..6dcdf26 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m @@ -0,0 +1,1471 @@ +#import "ThinkingAnalyticsSDKPrivate.h" + +#if TARGET_OS_IOS +#import "TDAutoTrackManager.h" +//#import "TARouter.h" +#import "TDAppLaunchReason.h" +#import "TAPushClickEvent.h" +#endif + +#import "TDCalibratedTimeWithNTP.h" +#import "TDConfig.h" +#import "TDPublicConfig.h" +#import "TDFile.h" +#import "TDCheck.h" +#import "TDJSONUtil.h" +#import "NSString+TDString.h" +#import "TDPresetProperties+TDDisProperties.h" +#import "TDAppState.h" +#import "TDEventRecord.h" +#import "TAAppExtensionAnalytic.h" +#import "TAReachability.h" +#import "TAAppLifeCycle.h" +//#import "TASessionIdPropertyPlugin.h" +//#import "TASessionIdManager.h" + + +#if !__has_feature(objc_arc) +#error The ThinkingSDK library must be compiled with ARC enabled +#endif + +#define td_force_inline __inline__ __attribute__((always_inline)) + +@interface TDPresetProperties (ThinkingAnalytics) + +- (instancetype)initWithDictionary:(NSDictionary *)dict; +- (void)updateValuesWithDictionary:(NSDictionary *)dict; + +@end + +@interface ThinkingAnalyticsSDK () +@property (nonatomic, strong) TAEventTracker *eventTracker; +@property (strong,nonatomic) TDFile *file; + +@end + +@implementation ThinkingAnalyticsSDK + +static NSMutableDictionary *instances; +static NSString *defaultProjectAppid; +static TDCalibratedTime *calibratedTime; +static dispatch_queue_t td_trackQueue; + ++ (nullable ThinkingAnalyticsSDK *)sharedInstance { + if (instances.count == 0) { + TDLogError(@"sharedInstance called before creating a Thinking instance"); + return nil; + } + return instances[defaultProjectAppid]; +} + ++ (ThinkingAnalyticsSDK *)sharedInstanceWithAppid:(NSString *)appid { + appid = appid.td_trim; + if (instances[appid]) { + return instances[appid]; + } else { + TDLogError(@"sharedInstanceWithAppid called before creating a Thinking instance"); + return nil; + } +} + ++ (ThinkingAnalyticsSDK *)startWithAppId:(NSString *)appId withUrl:(NSString *)url withConfig:(TDConfig *)config { + appId = appId.td_trim; + + NSString *name = config.name; + if (name && [name isKindOfClass:[NSString class]] && name.length) { + if (instances[name]) { + return instances[name]; + } else { + return [[self alloc] initWithAppkey:appId withServerURL:url withConfig:config]; + } + } + + if (instances[appId]) { + return instances[appId]; + } else if (![url isKindOfClass:[NSString class]] || url.length == 0) { + return nil; + } + return [[self alloc] initWithAppkey:appId withServerURL:url withConfig:config]; +} + ++ (ThinkingAnalyticsSDK *)startWithAppId:(NSString *)appId withUrl:(NSString *)url { + return [ThinkingAnalyticsSDK startWithAppId:appId withUrl:url withConfig:nil]; +} + + ++ (ThinkingAnalyticsSDK *)startWithConfig:(nullable TDConfig *)config { + return [ThinkingAnalyticsSDK startWithAppId:config.appid withUrl:config.configureURL withConfig:config]; +} + +- (instancetype)init:(NSString *)appID { + if (self = [super init]) { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instances = [NSMutableDictionary dictionary]; + defaultProjectAppid = appID; + }); + } + return self; +} + ++ (void)initialize { + static dispatch_once_t ThinkingOnceToken; + dispatch_once(&ThinkingOnceToken, ^{ + NSString *queuelabel = [NSString stringWithFormat:@"cn.thinkingdata.%p", (void *)self]; + td_trackQueue = dispatch_queue_create([queuelabel UTF8String], DISPATCH_QUEUE_SERIAL); + }); +} + ++ (dispatch_queue_t)td_trackQueue { + return td_trackQueue; +} + +- (instancetype)initLight:(NSString *)appid withServerURL:(NSString *)serverURL withConfig:(TDConfig *)config { + if (self = [self init]) { + serverURL = [serverURL ta_formatUrlString]; + self.isEnabled = YES; + + self.appid = appid; + self.serverURL = serverURL; + self.config = [config copy]; + self.config.appid = appid; + self.config.configureURL = serverURL; + self.config.appid = appid; + + self.superProperty = [[TASuperProperty alloc] initWithToken:[self td_getMapInstanceTag] isLight:YES]; + + self.trackTimer = [[TATrackTimer alloc] init]; + + if (![TDPresetProperties disableNetworkType]) { + [[TAReachability shareInstance] startMonitoring]; + } + + self.file = [[TDFile alloc] initWithAppid:appid]; + + self.dataQueue = [TDSqliteDataQueue sharedInstanceWithAppid:appid]; + if (self.dataQueue == nil) { + TDLogError(@"SqliteException: init SqliteDataQueue failed"); + } + + self.eventTracker = [[TAEventTracker alloc] initWithQueue:td_trackQueue instanceToken:[config getMapInstanceToken]]; + } + return self; +} + +- (instancetype)initWithAppkey:(NSString *)appid withServerURL:(NSString *)serverURL withConfig:(TDConfig *)config { + if (self = [self init:appid]) { + + [TDAppState shareInstance]; + // [TASessionIdManager shareInstance]; + + serverURL = [serverURL ta_formatUrlString]; + self.serverURL = serverURL; + self.appid = appid; + + if (!config) { + config = TDConfig.defaultTDConfig; + } + + _config = [config copy]; + _config.appid = appid; + _config.configureURL = serverURL; + + instances[[self td_getMapInstanceTag]] = self; + + self.file = [[TDFile alloc] initWithAppid:[self td_getMapInstanceTag]]; + [self retrievePersistedData]; + + self.superProperty = [[TASuperProperty alloc] initWithToken:[self td_getMapInstanceTag] isLight:NO]; + + self.propertyPluginManager = [[TAPropertyPluginManager alloc] init]; + TAPresetPropertyPlugin *presetPlugin = [[TAPresetPropertyPlugin alloc] init]; + presetPlugin.defaultTimeZone = config.defaultTimeZone; + [self.propertyPluginManager registerPropertyPlugin:presetPlugin]; + + NSString *instanceName = [self td_getMapInstanceTag]; + + _config.getInstanceName = ^NSString * _Nonnull{ + return instanceName; + }; + + /* remove session plugin + TASessionIdPropertyPlugin *sessionidPlugin = [[TASessionIdPropertyPlugin alloc] init]; + sessionidPlugin.instanceToken = instanceName; + self.sessionidPlugin = sessionidPlugin; + [self.propertyPluginManager registerPropertyPlugin:sessionidPlugin]; + */ +#if TARGET_OS_IOS + + if (_config.enableEncrypt) { + self.encryptManager = [[TDEncryptManager alloc] initWithConfig:config]; + } + + __weak __typeof(self)weakSelf = self; + [_config updateConfig:^(NSDictionary * _Nonnull secretKey) { + if (weakSelf.config.enableEncrypt && secretKey) { + [weakSelf.encryptManager handleEncryptWithConfig:secretKey]; + } + }]; + +#elif TARGET_OS_OSX + [_config updateConfig:^(NSDictionary * _Nonnull secretKey) {}]; +#endif + + self.trackTimer = [[TATrackTimer alloc] init]; + + _ignoredViewControllers = [[NSMutableSet alloc] init]; + _ignoredViewTypeList = [[NSMutableSet alloc] init]; + + self.dataQueue = [TDSqliteDataQueue sharedInstanceWithAppid:[self td_getMapInstanceTag]]; + if (self.dataQueue == nil) { + TDLogError(@"SqliteException: init SqliteDataQueue failed"); + } + + if (![TDPresetProperties disableNetworkType]) { + [[TAReachability shareInstance] startMonitoring]; + } + + self.eventTracker = [[TAEventTracker alloc] initWithQueue:td_trackQueue instanceToken:[_config getMapInstanceToken]]; + + [self startFlushTimer]; + + [TAAppLifeCycle startMonitor]; + + [self registerAppLifeCycleListener]; + +#if TARGET_OS_IOS + NSDictionary* ops = [TDAppLaunchReason getAppPushDic]; + if(ops != nil){ + TAPushClickEvent *pushEvent = [[TAPushClickEvent alloc]initWithName: @"ops_push_click"]; + pushEvent.ops = ops; + [self autoTrackWithEvent:pushEvent properties:@{}]; + [self flush]; + } + [TDAppLaunchReason clearAppPushParams]; +#endif + + if ([self ableMapInstanceTag]) { + TDLogInfo(@"Thinking Analytics %@ SDK %@ instance initialized successfully with mode: %@, Instance Name: %@, APP ID: %@, server url: %@, device ID: %@", [[TDDeviceInfo sharedManager] libName] ,[TDDeviceInfo libVersion], [self modeEnumToString:_config.debugMode], _config.name, appid, serverURL, [self getDeviceId]); + + } else { + TDLogInfo(@"Thinking Analytics %@ SDK %@ instance initialized successfully with mode: %@, APP ID: %@, server url: %@, device ID: %@", [[TDDeviceInfo sharedManager] libName], [TDDeviceInfo libVersion], [self modeEnumToString:_config.debugMode], appid, serverURL, [self getDeviceId]); + + } + + } + return self; +} + +- (void)registerAppLifeCycleListener { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter addObserver:self selector:@selector(appStateWillChangeNotification:) name:kTAAppLifeCycleStateWillChangeNotification object:nil]; + [notificationCenter addObserver:self selector:@selector(appStateDidChangeNotification:) name:kTAAppLifeCycleStateDidChangeNotification object:nil]; +} + +- (NSString*)modeEnumToString:(ThinkingAnalyticsDebugMode)enumVal { + NSArray *modeEnumArray = [[NSArray alloc] initWithObjects:kModeEnumArray]; + return [modeEnumArray objectAtIndex:enumVal]; +} + +- (BOOL)ableMapInstanceTag { + return _config.name && [_config.name isKindOfClass:[NSString class]] && _config.name.length; +} + +- (NSString *)td_getMapInstanceTag { + return [self.config getMapInstanceToken]; +} + +- (NSString *)description { + if ([self ableMapInstanceTag]) { + return [NSString stringWithFormat:@"", (void *)self, _config.name, self.appid, self.serverURL]; + } else { + return [NSString stringWithFormat:@"", (void *)self, self.appid, self.serverURL]; + } +} + ++ (id)sharedUIApplication { + return [TDAppState sharedApplication]; +} + +- (void)setTrackStatus: (TATrackStatus)status { + switch (status) { + case TATrackStatusPause: { + TDLogDebug(@"%@ switchTrackStatus: TATrackStatusPause...", self); + [self enableTracking:NO]; + break; + } + case TATrackStatusStop: { + TDLogDebug(@"%@ switchTrackStatus: TATrackStatusStop...", self); + [self doOptOutTracking]; + break; + } + case TATrackStatusSaveOnly: { + TDLogDebug(@"%@ switchTrackStatus: TATrackStatusSaveOnly...", self); + self.trackPause = YES; + self.isEnabled = YES; + self.isOptOut = NO; + dispatch_async(td_trackQueue, ^{ + [self.file archiveTrackPause:YES]; + [self.file archiveIsEnabled:YES]; + [self.file archiveOptOut:NO]; + }); + break; + } + case TATrackStatusNormal: { + TDLogDebug(@"%@ switchTrackStatus: TATrackStatusNormal...", self); + self.trackPause = NO; + self.isEnabled = YES; + self.isOptOut = NO; + dispatch_async(td_trackQueue, ^{ + [self.file archiveTrackPause:NO]; + [self.file archiveIsEnabled:self.isEnabled]; + [self.file archiveOptOut:NO]; + }); + [self flush]; + break; + } + default: + break; + } +} + +#pragma mark - EnableTracking +- (void)enableTracking:(BOOL)enabled { + self.isEnabled = enabled; + dispatch_async(td_trackQueue, ^{ + [self.file archiveIsEnabled:self.isEnabled]; + }); +} + +- (void)optOutTracking { + TDLogDebug(@"%@ optOutTracking...", self); + [self doOptOutTracking]; +} + +- (void)optOutTrackingAndDeleteUser { + TDLogDebug(@"%@ optOutTrackingAndDeleteUser...", self); + TAUserEventDelete *deleteEvent = [[TAUserEventDelete alloc] init]; + deleteEvent.immediately = YES; + [self asyncUserEventObject:deleteEvent properties:nil isH5:NO]; + + [self doOptOutTracking]; +} + +- (void)optInTracking { + TDLogDebug(@"%@ optInTracking...", self); + self.isOptOut = NO; + dispatch_async(td_trackQueue, ^{ + [self.file archiveOptOut:NO]; + }); +} + +- (BOOL)hasDisabled { + return !self.isEnabled || self.isOptOut; +} + +- (void)doOptOutTracking { + self.isOptOut = YES; + +#if TARGET_OS_IOS + @synchronized (self.autoTrackSuperProperty) { + [self.autoTrackSuperProperty clearSuperProperties]; + } +#endif + + [self.superProperty registerDynamicSuperProperties:nil]; + + void(^block)(void) = ^{ + [self.dataQueue deleteAll:[self td_getMapInstanceTag]]; + [self.trackTimer clear]; + [self.superProperty clearSuperProperties]; + self.identifyId = [TDDeviceInfo sharedManager].uniqueId; + self.accountId = nil; + + [self.file archiveAccountID:nil]; + [self.file archiveIdentifyId:nil]; + [self.file archiveSuperProperties:nil]; + [self.file archiveOptOut:YES]; + }; + if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(td_trackQueue)) { + block(); + } else { + dispatch_async(td_trackQueue, block); + } +} + +#pragma mark - LightInstance +- (ThinkingAnalyticsSDK *)createLightInstance { + ThinkingAnalyticsSDK *lightInstance = [[LightThinkingAnalyticsSDK alloc] initWithAPPID:self.appid withServerURL:self.serverURL withConfig:self.config]; + lightInstance.identifyId = [TDDeviceInfo sharedManager].uniqueId; + lightInstance.propertyPluginManager = self.propertyPluginManager; + return lightInstance; +} + +#pragma mark - Persistence +- (void)retrievePersistedData { + self.accountId = [self.file unarchiveAccountID]; + self.identifyId = [self.file unarchiveIdentifyID]; + self.trackPause = [self.file unarchiveTrackPause]; + self.isEnabled = [self.file unarchiveEnabled]; + self.isOptOut = [self.file unarchiveOptOut]; + self.config.uploadSize = [self.file unarchiveUploadSize]; + self.config.uploadInterval = [self.file unarchiveUploadInterval]; + if (self.identifyId.length == 0) { + self.identifyId = [TDDeviceInfo sharedManager].uniqueId; + } + if (self.accountId.length == 0) { + self.accountId = [self.file unarchiveAccountID]; + [self.file archiveAccountID:self.accountId]; + [self.file deleteOldLoginId]; + } +} + +- (void)deleteAll { + dispatch_async(td_trackQueue, ^{ + @synchronized (TDSqliteDataQueue.class) { + [self.dataQueue deleteAll:[self td_getMapInstanceTag]]; + } + }); +} + +//MARK: - AppLifeCycle + +- (void)appStateWillChangeNotification:(NSNotification *)notification { + TAAppLifeCycleState newState = [[notification.userInfo objectForKey:kTAAppLifeCycleNewStateKey] integerValue]; + + if (newState == TAAppLifeCycleStateEnd) { + [self stopFlushTimer]; + } +} + + +- (void)appStateDidChangeNotification:(NSNotification *)notification { + TAAppLifeCycleState newState = [[notification.userInfo objectForKey:kTAAppLifeCycleNewStateKey] integerValue]; + + if (newState == TAAppLifeCycleStateStart) { + [self startFlushTimer]; + + // 更新时长统计 + NSTimeInterval systemUpTime = [TDDeviceInfo uptime]; + [self.trackTimer enterForegroundWithSystemUptime:systemUpTime]; + } else if (newState == TAAppLifeCycleStateEnd) { + // 更新事件时长统计 + NSTimeInterval systemUpTime = [TDDeviceInfo uptime]; + [self.trackTimer enterBackgroundWithSystemUptime:systemUpTime]; + +#if TARGET_OS_IOS + UIApplication *application = [TDAppState sharedApplication];; + __block UIBackgroundTaskIdentifier backgroundTaskIdentifier = UIBackgroundTaskInvalid; + void (^endBackgroundTask)(void) = ^() { + [application endBackgroundTask:backgroundTaskIdentifier]; + backgroundTaskIdentifier = UIBackgroundTaskInvalid; + }; + backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:endBackgroundTask]; + + [self.eventTracker _asyncWithCompletion:endBackgroundTask]; +#else + [self.eventTracker flush]; +#endif + + } else if (newState == TAAppLifeCycleStateTerminate) { + dispatch_sync(td_trackQueue, ^{}); + [self.eventTracker syncSendAllData]; + } +} + +// MARK: - + +- (void)setNetworkType:(ThinkingAnalyticsNetworkType)type { + if ([self hasDisabled]) + return; + + [self.config setNetworkType:type]; +} + ++ (NSString *)getNetWorkStates { + return [[TAReachability shareInstance] networkState]; +} + +//MARK: - Track + +- (void)track:(NSString *)event { + [self track:event properties:nil]; +} + +- (void)track:(NSString *)event properties:(NSDictionary *)propertiesDict { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + [self track:event properties:propertiesDict time:nil timeZone:nil]; +#pragma clang diagnostic pop +} + +// deprecated +- (void)track:(NSString *)event properties:(NSDictionary *)propertiesDict time:(NSDate *)time { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + [self track:event properties:propertiesDict time:time timeZone:nil]; +#pragma clang diagnostic pop +} + +- (void)track:(NSString *)event properties:(nullable NSDictionary *)properties time:(NSDate *)time timeZone:(NSTimeZone *)timeZone { + TATrackEvent *trackEvent = [[TATrackEvent alloc] initWithName:event]; + // TDLogDebug(@"##### track.systemUpTime: %lf", trackEvent.systemUpTime); + [self configEventTimeValueWithEvent:trackEvent time:time timeZone:timeZone]; + [self handleTimeEvent:trackEvent]; + [self asyncTrackEventObject:trackEvent properties:properties isH5:NO]; +} + +- (void)trackWithEventModel:(TDEventModel *)eventModel { + TATrackEvent *baseEvent = nil; + if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK_FIRST]) { + TATrackFirstEvent *trackEvent = [[TATrackFirstEvent alloc] initWithName:eventModel.eventName]; + [self configEventTimeValueWithEvent:trackEvent time:eventModel.time timeZone:eventModel.timeZone]; + trackEvent.firstCheckId = eventModel.extraID; + baseEvent = trackEvent; + } else if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK_UPDATE]) { + TATrackUpdateEvent *trackEvent = [[TATrackUpdateEvent alloc] initWithName:eventModel.eventName]; + [self configEventTimeValueWithEvent:trackEvent time:eventModel.time timeZone:eventModel.timeZone]; + trackEvent.eventId = eventModel.extraID; + baseEvent = trackEvent; + } else if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK_OVERWRITE]) { + TATrackOverwriteEvent *trackEvent = [[TATrackOverwriteEvent alloc] initWithName:eventModel.eventName]; + [self configEventTimeValueWithEvent:trackEvent time:eventModel.time timeZone:eventModel.timeZone]; + trackEvent.eventId = eventModel.extraID; + baseEvent = trackEvent; + } else if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK]) { + TATrackEvent *trackEvent = [[TATrackEvent alloc] initWithName:eventModel.eventName]; + [self configEventTimeValueWithEvent:trackEvent time:eventModel.time timeZone:eventModel.timeZone]; + baseEvent = trackEvent; + } + [self asyncTrackEventObject:baseEvent properties:eventModel.properties isH5:NO]; +} + +- (void)trackFromAppExtensionWithAppGroupId:(NSString *)appGroupId { + @try { + if (appGroupId == nil || [appGroupId isEqualToString:@""]) { + return; + } + + TAAppExtensionAnalytic *analytic = [TAAppExtensionAnalytic analyticWithInstanceName:[self td_getMapInstanceTag] appGroupId:appGroupId]; + NSArray *eventArray = [analytic readAllEvents]; + if (eventArray) { + for (NSDictionary *dict in eventArray) { + NSString *eventName = dict[kTAAppExtensionEventName]; + NSDictionary *properties = dict[kTAAppExtensionEventProperties]; + NSDate *time = dict[kTAAppExtensionTime]; + // track event + if ([time isKindOfClass:NSDate.class]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + [self track:eventName properties:properties time:time timeZone:nil]; +#pragma clang diagnostic pop + } else { + [self track:eventName properties:properties]; + } + } + [analytic deleteEvents]; + } + } @catch (NSException *exception) { + return; + } +} + +#pragma mark - Private + +- (void)asyncTrackEventObject:(TATrackEvent *)event properties:(NSDictionary *)properties isH5:(BOOL)isH5 { + + event.isEnabled = self.isEnabled; + event.trackPause = self.isTrackPause; + event.isOptOut = self.isOptOut; + event.accountId = self.accountId; + event.distinctId = self.identifyId; + + event.dynamicSuperProperties = [self.superProperty obtainDynamicSuperProperties]; + dispatch_async(td_trackQueue, ^{ + [self trackEvent:event properties:properties isH5:isH5]; + }); +} + +- (void)asyncUserEventObject:(TAUserEvent *)event properties:(NSDictionary *)properties isH5:(BOOL)isH5 { + + event.isEnabled = self.isEnabled; + event.trackPause = self.isTrackPause; + event.isOptOut = self.isOptOut; + event.accountId = self.accountId; + event.distinctId = self.identifyId; + + [self configEventTimeValueWithEvent:event time:event.time timeZone:event.timeZone]; + + dispatch_async(td_trackQueue, ^{ + [self trackUserEvent:event properties:properties isH5:NO]; + }); +} + +- (void)configEventTimeValueWithEvent:(TABaseEvent *)event time:(NSDate *)time timeZone:(NSTimeZone *)timeZone { + event.timeZone = timeZone ?: self.config.defaultTimeZone; + if (time) { + event.time = time; + if (timeZone == nil) { + event.timeValueType = TAEventTimeValueTypeTimeOnly; + } else { + event.timeValueType = TAEventTimeValueTypeTimeAndZone; + } + } else { + if (calibratedTime && !calibratedTime.stopCalibrate) { + NSTimeInterval outTime = [TDDeviceInfo uptime] - calibratedTime.systemUptime; + NSDate *serverDate = [NSDate dateWithTimeIntervalSince1970:(calibratedTime.serverTime + outTime)]; + event.time = serverDate; + event.timeValueType = TAEventTimeValueTypeTimeAndZone; + }else{ + event.timeValueType = TAEventTimeValueTypeNone; + } + } +} + ++ (BOOL)isTrackEvent:(NSString *)eventType { + return [TD_EVENT_TYPE_TRACK isEqualToString:eventType] + || [TD_EVENT_TYPE_TRACK_FIRST isEqualToString:eventType] + || [TD_EVENT_TYPE_TRACK_UPDATE isEqualToString:eventType] + || [TD_EVENT_TYPE_TRACK_OVERWRITE isEqualToString:eventType] + ; +} + +#pragma mark - User + +- (void)user_add:(NSString *)propertyName andPropertyValue:(NSNumber *)propertyValue { + [self user_add:propertyName andPropertyValue:propertyValue withTime:nil]; +} + +- (void)user_add:(NSString *)propertyName andPropertyValue:(NSNumber *)propertyValue withTime:(NSDate *)time { + if (propertyName && propertyValue) { + [self user_add:@{propertyName: propertyValue} withTime:time]; + } +} + +- (void)user_add:(NSDictionary *)properties { + [self user_add:properties withTime:nil]; +} + +- (void)user_add:(NSDictionary *)properties withTime:(NSDate *)time { + TAUserEventAdd *event = [[TAUserEventAdd alloc] init]; + event.time = time; + [self asyncUserEventObject:event properties:properties isH5:NO]; +} + +- (void)user_setOnce:(NSDictionary *)properties { + [self user_setOnce:properties withTime:nil]; +} + +- (void)user_setOnce:(NSDictionary *)properties withTime:(NSDate *)time { + TAUserEventSetOnce *event = [[TAUserEventSetOnce alloc] init]; + event.time = time; + [self asyncUserEventObject:event properties:properties isH5:NO]; +} + +- (void)user_set:(NSDictionary *)properties { + [self user_set:properties withTime:nil]; +} + +- (void)user_set:(NSDictionary *)properties withTime:(NSDate *)time { + TAUserEventSet *event = [[TAUserEventSet alloc] init]; + event.time = time; + [self asyncUserEventObject:event properties:properties isH5:NO]; +} + +- (void)user_unset:(NSString *)propertyName { + [self user_unset:propertyName withTime:nil]; +} + +- (void)user_unset:(NSString *)propertyName withTime:(NSDate *)time { + if ([propertyName isKindOfClass:[NSString class]] && propertyName.length > 0) { + NSDictionary *properties = @{propertyName: @0}; + TAUserEventUnset *event = [[TAUserEventUnset alloc] init]; + event.time = time; + [self asyncUserEventObject:event properties:properties isH5:NO]; + } +} + +- (void)user_delete { + [self user_delete:nil]; +} + +- (void)user_delete:(NSDate *)time { + TAUserEventDelete *event = [[TAUserEventDelete alloc] init]; + event.time = time; + [self asyncUserEventObject:event properties:nil isH5:NO]; +} + +- (void)user_append:(NSDictionary *)properties { + [self user_append:properties withTime:nil]; +} + +- (void)user_append:(NSDictionary *)properties withTime:(NSDate *)time { + TAUserEventAppend *event = [[TAUserEventAppend alloc] init]; + event.time = time; + [self asyncUserEventObject:event properties:properties isH5:NO]; +} + +- (void)user_uniqAppend:(NSDictionary *)properties { + [self user_uniqAppend:properties withTime:nil]; +} + +- (void)user_uniqAppend:(NSDictionary *)properties withTime:(NSDate *)time { + TAUserEventUniqueAppend *event = [[TAUserEventUniqueAppend alloc] init]; + event.time = time; + [self asyncUserEventObject:event properties:properties isH5:NO]; +} + +//MARK: - + ++ (void)setCustomerLibInfoWithLibName:(NSString *)libName libVersion:(NSString *)libVersion { + if (libName.length > 0) { + [TDDeviceInfo sharedManager].libName = libName; + } + if (libVersion.length > 0) { + [TDDeviceInfo sharedManager].libVersion = libVersion; + } + [[TDDeviceInfo sharedManager] td_updateData]; +} + +- (NSString *)getAccountId { + return _accountId; +} + +- (NSString *)getDistinctId { + return [self.identifyId copy]; +} + ++ (NSString *)getSDKVersion { + return TDPublicConfig.version; +} + +- (NSString *)getDeviceId { + return [TDDeviceInfo sharedManager].deviceId; +} + +- (void)registerDynamicSuperProperties:(NSDictionary *(^)(void)) dynamicSuperProperties { + if ([self hasDisabled]) { + return; + } + @synchronized (self.superProperty) { + [self.superProperty registerDynamicSuperProperties:dynamicSuperProperties]; + } +} + +- (void)setAutoTrackDynamicProperties:(NSDictionary * _Nonnull (^)(void))dynamicSuperProperties { + if ([self hasDisabled]) { + return; + } + @synchronized (self.autoTrackSuperProperty) { + [self.autoTrackSuperProperty registerAutoTrackDynamicProperties:dynamicSuperProperties]; + } +} + +- (void)registerErrorCallback:(void (^)(NSInteger, NSString * _Nullable, NSString * _Nullable))errorCallback { + self.errorCallback = errorCallback; +} + +- (void)setSuperProperties:(NSDictionary *)properties { + if ([self hasDisabled]) { + return; + } + + dispatch_async(td_trackQueue, ^{ + [self.superProperty registerSuperProperties:properties]; + }); +} + +- (void)unsetSuperProperty:(NSString *)propertyKey { + if ([self hasDisabled]) { + return; + } + dispatch_async(td_trackQueue, ^{ + [self.superProperty unregisterSuperProperty:propertyKey]; + }); +} + +- (void)clearSuperProperties { + if ([self hasDisabled]) { + return; + } + dispatch_async(td_trackQueue, ^{ + [self.superProperty clearSuperProperties]; + }); +} + +- (NSDictionary *)currentSuperProperties { + return [self.superProperty currentSuperProperties]; +} + +- (TDPresetProperties *)getPresetProperties { + NSMutableDictionary *presetDic = [NSMutableDictionary dictionary]; + + NSDictionary *pluginProperties = [self.propertyPluginManager currentPropertiesForPluginClasses:@[TAPresetPropertyPlugin.class]]; + [presetDic addEntriesFromDictionary:pluginProperties]; + + double offset = [[NSDate date] ta_timeZoneOffset:self.config.defaultTimeZone]; + [presetDic setObject:@(offset) forKey:@"#zone_offset"]; + + if (![TDPresetProperties disableNetworkType]) { + NSString *networkType = [self.class getNetWorkStates]; + [presetDic setObject:networkType?:@"" forKey:@"#network_type"]; + } + + // 将安装时间转为字符串 + if (![TDPresetProperties disableInstallTime]) { + if (presetDic[@"#install_time"] && [presetDic[@"#install_time"] isKindOfClass:[NSDate class]]) { + NSString *install_timeString = [(NSDate *)presetDic[@"#install_time"] ta_formatWithTimeZone:self.config.defaultTimeZone formatString:kDefaultTimeFormat]; + if (install_timeString && install_timeString.length) { + [presetDic setObject:install_timeString forKey:@"#install_time"]; + } + } + } + + static TDPresetProperties *presetProperties = nil; + if (presetProperties == nil) { + presetProperties = [[TDPresetProperties alloc] initWithDictionary:presetDic]; + } else { + @synchronized (instances) { + [presetProperties updateValuesWithDictionary:presetDic]; + } + } + + return presetProperties; +} + +- (void)identify:(NSString *)distinctId { + if (![distinctId isKindOfClass:[NSString class]] || distinctId.length == 0) { + TDLogError(@"identify cannot null"); + return; + } + if ([self hasDisabled]) { + return; + } + self.identifyId = distinctId; + @synchronized (self.file) { + [self.file archiveIdentifyId:distinctId]; + } +} + +- (void)login:(NSString *)accountId { + if (![accountId isKindOfClass:[NSString class]] || accountId.length == 0) { + TDLogError(@"accountId invald", accountId); + return; + } + + if ([self hasDisabled]) { + return; + } + self.accountId = accountId; + @synchronized (self.file) { + [self.file archiveAccountID:accountId]; + } +} + +- (void)logout { + if ([self hasDisabled]) { + return; + } + self.accountId = nil; + @synchronized (self.file) { + [self.file archiveAccountID:nil]; + } +} + +- (void)timeEvent:(NSString *)event { + if ([self hasDisabled]) { + return; + } + NSError *error = nil; + [TAPropertyValidator validateEventOrPropertyName:event withError:&error]; + if (error) { + return; + } + [self.trackTimer trackEvent:event withSystemUptime:[TDDeviceInfo uptime]]; +} + ++ (nullable NSString *)getLocalRegion { + NSString *countryCode = [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode]; + return countryCode; +} + +//MARK: - + +- (void)configBaseEvent:(TABaseEvent *)event isH5:(BOOL)isH5 { + + if (event.timeZone == nil) { + event.timeZone = self.config.defaultTimeZone; + } + + if (event.timeValueType == TAEventTimeValueTypeNone && calibratedTime && !calibratedTime.stopCalibrate) { + NSTimeInterval outTime = [TDDeviceInfo uptime] - calibratedTime.systemUptime; + NSDate *serverDate = [NSDate dateWithTimeIntervalSince1970:(calibratedTime.serverTime + outTime)]; + event.time = serverDate; + } +} + +- (void)trackUserEvent:(TAUserEvent *)event properties:(NSDictionary *)properties isH5:(BOOL)isH5 { + + if (!event.isEnabled || event.isOptOut) { + return; + } + + if ([TDAppState shareInstance].relaunchInBackground && !self.config.trackRelaunchedInBackgroundEvents) { + return; + } + + [self configBaseEvent:event isH5:isH5]; + + [event.properties addEntriesFromDictionary:[TAPropertyValidator validateProperties:properties validator:event]]; + + NSDictionary *jsonObj = [event formatDateWithDict:event.jsonObject]; + + [self.eventTracker track:jsonObj immediately:event.immediately saveOnly:event.isTrackPause]; +} + +- (void)trackEvent:(TATrackEvent *)event properties:(NSDictionary *)properties isH5:(BOOL)isH5 { + + if (!event.isEnabled || event.isOptOut) { + return; + } + + if ([TDAppState shareInstance].relaunchInBackground && !self.config.trackRelaunchedInBackgroundEvents && [event.eventName isEqualToString:TD_APP_START_BACKGROUND_EVENT]) { + return; + } + + [self configBaseEvent:event isH5:isH5]; + + NSError *error = nil; + [event validateWithError:&error]; + if (error) { + return; + } + + if ([self.config.disableEvents containsObject:event.eventName]) { + return; + } + + + if ([TDAppState shareInstance].relaunchInBackground) { + event.properties[@"#relaunched_in_background"] = @YES; + } + + NSMutableDictionary *pluginProperties = [self.propertyPluginManager propertiesWithEventType:event.eventType]; + + [TDPresetProperties handleFilterDisPresetProperties:pluginProperties]; + + NSDictionary *superProperties = [TAPropertyValidator validateProperties:self.superProperty.currentSuperProperties validator:event]; + + NSDictionary *dynamicSuperProperties = [TAPropertyValidator validateProperties:event.dynamicSuperProperties validator:event]; + + NSMutableDictionary *jsonObj = [NSMutableDictionary dictionary]; + + + if (isH5) { + event.properties = [superProperties mutableCopy]; + [event.properties addEntriesFromDictionary:dynamicSuperProperties]; + [event.properties addEntriesFromDictionary:properties]; + [event.properties addEntriesFromDictionary:pluginProperties]; + + jsonObj = event.jsonObject; + + + if (event.h5TimeString) { + jsonObj[@"#time"] = event.h5TimeString; + } + if (event.h5ZoneOffSet) { + jsonObj[@"#zone_offset"] = event.h5ZoneOffSet; + } + } else { + [event.properties addEntriesFromDictionary:pluginProperties]; + + jsonObj = event.jsonObject; + [event.properties addEntriesFromDictionary:superProperties]; + [event.properties addEntriesFromDictionary:dynamicSuperProperties]; +#if TARGET_OS_IOS + if ([event isKindOfClass:[TAAutoTrackEvent class]]) { + TAAutoTrackEvent *autoEvent = (TAAutoTrackEvent *)event; + + NSDictionary *autoSuperProperties = [self.autoTrackSuperProperty currentSuperPropertiesWithEventName:event.eventName]; + + autoSuperProperties = [TAPropertyValidator validateProperties:autoSuperProperties validator:autoEvent]; + + [event.properties addEntriesFromDictionary:autoSuperProperties]; + + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + dispatch_async(dispatch_get_main_queue(), ^{ + NSDictionary *autoDynamicSuperProperties = [self.autoTrackSuperProperty obtainDynamicSuperPropertiesWithType:autoEvent.autoTrackEventType currentProperties:event.properties]; + autoDynamicSuperProperties = [TAPropertyValidator validateProperties:autoDynamicSuperProperties validator:autoEvent]; + [event.properties addEntriesFromDictionary:autoDynamicSuperProperties]; + + dispatch_semaphore_signal(semaphore); + }); + + dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC))); + } +#endif + properties = [TAPropertyValidator validateProperties:properties validator:event]; + [event.properties addEntriesFromDictionary:properties]; + + } + + + jsonObj = [event formatDateWithDict:jsonObj]; + + [self.eventTracker track:jsonObj immediately:event.immediately saveOnly:event.isTrackPause]; +} + + +- (void)flush { + + if ([self hasDisabled]) { + return; + } + + if (self.isTrackPause) { + return; + } + [self.eventTracker flush]; +} + +#pragma mark - Flush control +- (void)startFlushTimer { + [self stopFlushTimer]; + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.config.uploadInterval > 0) { + self.timer = [NSTimer scheduledTimerWithTimeInterval:[self.config.uploadInterval integerValue] + target:self + selector:@selector(flush) + userInfo:nil + repeats:YES]; + } + }); +} + +- (void)stopFlushTimer { + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.timer) { + [self.timer invalidate]; + self.timer = nil; + } + }); +} + +#if TARGET_OS_IOS + +//MARK: - Thired Party + +- (void)enableThirdPartySharing:(TAThirdPartyShareType)type { + [self enableThirdPartySharing:type customMap:@{}]; +} + +- (void)enableThirdPartySharing:(TAThirdPartyShareType)type customMap:(NSDictionary *)customMap { + +#if TARGET_OS_IOS + Class TARouterCls = NSClassFromString(@"TARouter"); + // com.thinkingdata://call.service/TAThirdPartyManager.TAThirdPartyProtocol/...?params={}(value url encode) + NSURL *url = [NSURL URLWithString:@"com.thinkingdata://call.service.thinkingdata/TAThirdPartyManager.TAThirdPartyProtocol.enableThirdPartySharing:instance:property:/"]; + if (TARouterCls && [TARouterCls respondsToSelector:@selector(canOpenURL:)] && [TARouterCls respondsToSelector:@selector(openURL:withParams:)]) { + if ([TARouterCls performSelector:@selector(canOpenURL:) withObject:url]) { + [TARouterCls performSelector:@selector(openURL:withParams:) withObject:url withObject:@{@"TAThirdPartyManager":@{@1:[NSNumber numberWithInteger:type],@2:self,@3:customMap}}]; + } + } + +#endif +} + +//MARK: - Auto Track + +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType { + [self _enableAutoTrack:eventType properties:nil callback:nil]; +} + +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType properties:(NSDictionary *)properties { + [self _enableAutoTrack:eventType properties:properties callback:nil]; +} + +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType callback:(NSDictionary *(^)(ThinkingAnalyticsAutoTrackEventType eventType, NSDictionary *properties))callback { + [self _enableAutoTrack:eventType properties:nil callback:callback]; +} + +- (void)_enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType properties:(NSDictionary *)properties callback:(NSDictionary *(^)(ThinkingAnalyticsAutoTrackEventType eventType, NSDictionary *properties))callback { + [self.autoTrackSuperProperty registerSuperProperties:properties withType:eventType]; + [self.autoTrackSuperProperty registerDynamicSuperProperties:callback]; + + [self _enableAutoTrack:eventType]; +} + +- (void)_enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType { + self.config.autoTrackEventType = eventType; + + + [[TDAutoTrackManager sharedManager] trackWithAppid:[self td_getMapInstanceTag] withOption:eventType]; +} + +- (void)setAutoTrackProperties:(ThinkingAnalyticsAutoTrackEventType)eventType properties:(NSDictionary *)properties { + + if ([self hasDisabled]) { + return; + } + + if (properties == nil) { + return; + } + + @synchronized (self.autoTrackSuperProperty) { + [self.autoTrackSuperProperty registerSuperProperties:[properties copy] withType:eventType]; + } +} + +- (void)ignoreViewType:(Class)aClass { + if ([self hasDisabled]) { + return; + } + @synchronized (self.ignoredViewTypeList) { + [self.ignoredViewTypeList addObject:aClass]; + } +} + +- (BOOL)isViewTypeIgnored:(Class)aClass { + return [_ignoredViewTypeList containsObject:aClass]; +} + +- (BOOL)isAutoTrackEventTypeIgnored:(ThinkingAnalyticsAutoTrackEventType)eventType { + return !(_config.autoTrackEventType & eventType); +} + +- (void)ignoreAutoTrackViewControllers:(NSArray *)controllers { + if ([self hasDisabled]) { + return; + } + + if (controllers == nil || controllers.count == 0) { + return; + } + + @synchronized (self.ignoredViewControllers) { + [self.ignoredViewControllers addObjectsFromArray:controllers]; + } +} + +- (void)autoTrackWithEvent:(TAAutoTrackEvent *)event properties:(NSDictionary *)properties { + TDLogDebug(@"##### autoTrackWithEvent: %@", event.eventName); + [self configEventTimeValueWithEvent:event time:nil timeZone:nil]; + [self handleTimeEvent:event]; + [self asyncAutoTrackEventObject:event properties:properties]; +} + +/// Add event to event queue +- (void)asyncAutoTrackEventObject:(TAAutoTrackEvent *)event properties:(NSDictionary *)properties { + + event.isEnabled = self.isEnabled; + event.trackPause = self.isTrackPause; + event.isOptOut = self.isOptOut; + event.accountId = self.accountId; + event.distinctId = self.identifyId; + + NSDictionary *autoTrackDynamicProperties = [self.autoTrackSuperProperty obtainAutoTrackDynamicSuperProperties]; + NSDictionary *dynamicProperties = [self.superProperty obtainDynamicSuperProperties]; + NSMutableDictionary *unionProperties = [NSMutableDictionary dictionary]; + if (dynamicProperties) { + [unionProperties addEntriesFromDictionary:dynamicProperties]; + } + if (autoTrackDynamicProperties) { + [unionProperties addEntriesFromDictionary:autoTrackDynamicProperties]; + } + event.dynamicSuperProperties = unionProperties; + dispatch_async(td_trackQueue, ^{ + [self trackEvent:event properties:properties isH5:NO]; + }); +} + +- (BOOL)isViewControllerIgnored:(UIViewController *)viewController { + if (viewController == nil) { + return false; + } + NSString *screenName = NSStringFromClass([viewController class]); + if (_ignoredViewControllers != nil && _ignoredViewControllers.count > 0) { + if ([_ignoredViewControllers containsObject:screenName]) { + return true; + } + } + return false; +} + +#endif + +// MARK: - H5 tracking + +- (void)clickFromH5:(NSString *)data { + NSData *jsonData = [data dataUsingEncoding:NSUTF8StringEncoding]; + if (!jsonData) { + return; + } + NSError *err; + NSDictionary *eventDict = [NSJSONSerialization JSONObjectWithData:jsonData + options:NSJSONReadingMutableContainers + error:&err]; + NSString *appid = [eventDict[@"#app_id"] isKindOfClass:[NSString class]] ? eventDict[@"#app_id"] : self.appid; + id dataArr = eventDict[@"data"]; + if (!err && [dataArr isKindOfClass:[NSArray class]]) { + NSDictionary *dataInfo = [dataArr objectAtIndex:0]; + if (dataInfo != nil) { + NSString *type = [dataInfo objectForKey:@"#type"]; + NSString *event_name = [dataInfo objectForKey:@"#event_name"]; + NSString *time = [dataInfo objectForKey:@"#time"]; + NSDictionary *properties = [dataInfo objectForKey:@"properties"]; + + NSString *extraID; + + if ([type isEqualToString:TD_EVENT_TYPE_TRACK]) { + extraID = [dataInfo objectForKey:@"#first_check_id"]; + if (extraID) { + type = TD_EVENT_TYPE_TRACK_FIRST; + } + } else { + extraID = [dataInfo objectForKey:@"#event_id"]; + } + + NSMutableDictionary *dic = [properties mutableCopy]; + [dic removeObjectForKey:@"#account_id"]; + [dic removeObjectForKey:@"#distinct_id"]; + [dic removeObjectForKey:@"#device_id"]; + [dic removeObjectForKey:@"#lib"]; + [dic removeObjectForKey:@"#lib_version"]; + [dic removeObjectForKey:@"#screen_height"]; + [dic removeObjectForKey:@"#screen_width"]; + + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK sharedInstanceWithAppid:appid]; + if (instance) { + dispatch_async(td_trackQueue, ^{ + [instance h5track:event_name + extraID:extraID + properties:dic + type:type + time:time]; + }); + } else { + dispatch_async(td_trackQueue, ^{ + [self h5track:event_name + extraID:extraID + properties:dic + type:type + time:time]; + }); + } + } + } +} + +- (void)h5track:(NSString *)eventName + extraID:(NSString *)extraID + properties:(NSDictionary *)propertieDict + type:(NSString *)type + time:(NSString *)time { + + if ([ThinkingAnalyticsSDK isTrackEvent:type]) { + TATrackEvent *event = nil; + if ([type isEqualToString:TD_EVENT_TYPE_TRACK]) { + TATrackEvent *trackEvent = [[TATrackEvent alloc] initWithName:eventName]; + event = trackEvent; + } else if ([type isEqualToString:TD_EVENT_TYPE_TRACK_FIRST]) { + TATrackFirstEvent *firstEvent = [[TATrackFirstEvent alloc] initWithName:eventName]; + firstEvent.firstCheckId = extraID; + event = firstEvent; + } else if ([type isEqualToString:TD_EVENT_TYPE_TRACK_UPDATE]) { + TATrackUpdateEvent *updateEvent = [[TATrackUpdateEvent alloc] initWithName:eventName]; + updateEvent.eventId = extraID; + event = updateEvent; + } else if ([type isEqualToString:TD_EVENT_TYPE_TRACK_OVERWRITE]) { + TATrackOverwriteEvent *overwriteEvent = [[TATrackOverwriteEvent alloc] initWithName:eventName]; + overwriteEvent.eventId = extraID; + event = overwriteEvent; + } + event.h5TimeString = time; + if ([propertieDict objectForKey:@"#zone_offset"]) { + event.h5ZoneOffSet = [propertieDict objectForKey:@"#zone_offset"]; + } + [self asyncTrackEventObject:event properties:propertieDict isH5:YES]; + } else { + TAUserEvent *event = [[TAUserEvent alloc] initWithType:[TABaseEvent typeWithTypeString:type]]; + [self asyncUserEventObject:event properties:propertieDict isH5:YES]; + } +} + +- (double)getTimezoneOffset:(NSDate *)date timeZone:(NSTimeZone *)timeZone { + return [date ta_timeZoneOffset:timeZone]; +} + +- (BOOL)showUpWebView:(id)webView WithRequest:(NSURLRequest *)request { + if (webView == nil || request == nil || ![request isKindOfClass:NSURLRequest.class]) { + TDLogInfo(@"showUpWebView request error"); + return NO; + } + + NSString *urlStr = request.URL.absoluteString; + if (!urlStr) { + return NO; + } + + if ([urlStr rangeOfString:TA_JS_TRACK_SCHEME].length == 0) { + return NO; + } + + NSString *query = [[request URL] query]; + NSArray *queryItem = [query componentsSeparatedByString:@"="]; + + if (queryItem.count != 2) + return YES; + + NSString *queryValue = [queryItem lastObject]; + if ([urlStr rangeOfString:TA_JS_TRACK_SCHEME].length > 0) { + if ([self hasDisabled]) + return YES; + + NSString *eventData = [queryValue stringByRemovingPercentEncoding]; + if (eventData.length > 0) + [self clickFromH5:eventData]; + } + return YES; +} + +- (void)wkWebViewGetUserAgent:(void (^)(NSString *))completion { + self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectZero]; + [self.wkWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id __nullable userAgent, NSError * __nullable error) { + completion(userAgent); + }]; +} + +- (void)addWebViewUserAgent { + if ([self hasDisabled]) + return; + + void (^setUserAgent)(NSString *userAgent) = ^void (NSString *userAgent) { + if ([userAgent rangeOfString:@"td-sdk-ios"].location == NSNotFound) { + userAgent = [userAgent stringByAppendingString:@" /td-sdk-ios"]; + + NSDictionary *userAgentDic = [[NSDictionary alloc] initWithObjectsAndKeys:userAgent, @"UserAgent", nil]; + [[NSUserDefaults standardUserDefaults] registerDefaults:userAgentDic]; + [[NSUserDefaults standardUserDefaults] synchronize]; + } + }; + + dispatch_block_t getUABlock = ^() { + [self wkWebViewGetUserAgent:^(NSString *userAgent) { + setUserAgent(userAgent); + }]; + }; + + td_dispatch_main_sync_safe(getUABlock); +} + +#pragma mark - Logging ++ (void)setLogLevel:(TDLoggingLevel)level { + [TDLogging sharedInstance].loggingLevel = level; +} + +#pragma mark - Calibrate time + ++ (void)calibrateTime:(NSTimeInterval)timestamp { + calibratedTime = [TDCalibratedTime sharedInstance]; + [[TDCalibratedTime sharedInstance] recalibrationWithTimeInterval:timestamp/1000.]; + //NSLog(@"After version 2.8.4, the external time calibration API is discarded, and the SDK will automatically calibrate"); +} + ++ (void)calibrateTimeWithNtp:(NSString *)ntpServer { + if ([ntpServer isKindOfClass:[NSString class]] && ntpServer.length > 0) { + calibratedTime = [TDCalibratedTimeWithNTP sharedInstance]; + [[TDCalibratedTimeWithNTP sharedInstance] recalibrationWithNtps:@[ntpServer]]; + } + //NSLog(@"After version 2.8.4, the external time calibration API is discarded, and the SDK will automatically calibrate"); +} + +// for UNITY +- (NSString *)getTimeString:(NSDate *)date { + return [date ta_formatWithTimeZone:self.config.defaultTimeZone formatString:kDefaultTimeFormat]; +} + +//MARK: - Private + +- (void)handleTimeEvent:(TATrackEvent *)trackEvent { + + BOOL isTrackDuration = [self.trackTimer isExistEvent:trackEvent.eventName]; + BOOL isEndEvent = [trackEvent.eventName isEqualToString:TD_APP_END_EVENT]; + BOOL isStartEvent = [trackEvent.eventName isEqualToString:TD_APP_START_EVENT]; + BOOL isStateInit = [TAAppLifeCycle shareInstance].state == TAAppLifeCycleStateInit; + + if (isStateInit) { + trackEvent.foregroundDuration = [self.trackTimer foregroundDurationOfEvent:trackEvent.eventName isActive:YES systemUptime:trackEvent.systemUpTime]; + [self.trackTimer removeEvent:trackEvent.eventName]; + + } else if (isStartEvent) { + + trackEvent.backgroundDuration = [self.trackTimer backgroundDurationOfEvent:trackEvent.eventName isActive:NO systemUptime:trackEvent.systemUpTime]; + [self.trackTimer removeEvent:trackEvent.eventName]; + + } else if (isEndEvent) { + + trackEvent.foregroundDuration = [self.trackTimer foregroundDurationOfEvent:trackEvent.eventName isActive:YES systemUptime:trackEvent.systemUpTime]; + [self.trackTimer removeEvent:trackEvent.eventName]; + + } else if (isTrackDuration) { + + + BOOL isActive = [TDAppState shareInstance].isActive; + + trackEvent.foregroundDuration = [self.trackTimer foregroundDurationOfEvent:trackEvent.eventName isActive:isActive systemUptime:trackEvent.systemUpTime]; + + trackEvent.backgroundDuration = [self.trackTimer backgroundDurationOfEvent:trackEvent.eventName isActive:isActive systemUptime:trackEvent.systemUpTime]; + + TDLogDebug(@"#####eventName: %@, foregroundDuration: %d", trackEvent.eventName, trackEvent.foregroundDuration); + TDLogDebug(@"#####eventName: %@, backgroundDuration: %d", trackEvent.eventName, trackEvent.backgroundDuration); + + [self.trackTimer removeEvent:trackEvent.eventName]; + } else { + + if (trackEvent.eventName == TD_APP_END_EVENT) { + return; + } + } +} + ++ (NSMutableDictionary *)_getAllInstances { + return instances; +} + ++ (void)_clearCalibratedTime { + calibratedTime = nil; +} + +- (BOOL)isValidName:(NSString *)name isAutoTrack:(BOOL)isAutoTrack { + return YES; +} + +- (BOOL)checkEventProperties:(NSDictionary *)properties withEventType:(NSString *_Nullable)eventType haveAutoTrackEvents:(BOOL)haveAutoTrackEvents { + return YES; +} + +- (void)flushImmediately:(NSDictionary *)dataDic { + +} + ++ (dispatch_queue_t)td_networkQueue { + return [TAEventTracker td_networkQueue]; +} + +- (NSInteger)saveEventsData:(NSDictionary *)data { + return [self.eventTracker saveEventsData:data]; +} + +// MARK: - getter & setter + +- (TAAutoTrackSuperProperty *)autoTrackSuperProperty { + if (!_autoTrackSuperProperty) { + _autoTrackSuperProperty = [[TAAutoTrackSuperProperty alloc] init]; + } + return _autoTrackSuperProperty; +} + +@end diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m.meta new file mode 100644 index 0000000..103a5be --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 47769b322b27cff4590e6c5763e19b3c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h new file mode 100644 index 0000000..546ba04 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h @@ -0,0 +1,168 @@ +#if __has_include() +#import +#else +#import "ThinkingAnalyticsSDK.h" +#endif + +#import +#import +#import +#import +#import + +#if TARGET_OS_IOS +#import "ThinkingExceptionHandler.h" +#import "TAAutoTrackEvent.h" +#import "TAAutoTrackSuperProperty.h" +#import "TDEncrypt.h" +#endif + +#import "TDLogging.h" +#import "TDDeviceInfo.h" +#import "TDConfig.h" +#import "TDSqliteDataQueue.h" +#import "TDEventModel.h" + +#import "TATrackTimer.h" +#import "TASuperProperty.h" +#import "TATrackEvent.h" +#import "TATrackFirstEvent.h" +#import "TATrackOverwriteEvent.h" +#import "TATrackUpdateEvent.h" +#import "TAUserPropertyHeader.h" +#import "TAPropertyPluginManager.h" +//#import "TASessionIdPropertyPlugin.h" +#import "TAPresetPropertyPlugin.h" +#import "TABaseEvent+H5.h" +#import "NSDate+TAFormat.h" +#import "TAEventTracker.h" +#import "TAAppLifeCycle.h" + +NS_ASSUME_NONNULL_BEGIN + +static NSString * const TD_APP_START_EVENT = @"ta_app_start"; +static NSString * const TD_APP_START_BACKGROUND_EVENT = @"ta_app_bg_start"; +static NSString * const TD_APP_END_EVENT = @"ta_app_end"; +static NSString * const TD_APP_VIEW_EVENT = @"ta_app_view"; +static NSString * const TD_APP_CLICK_EVENT = @"ta_app_click"; +static NSString * const TD_APP_CRASH_EVENT = @"ta_app_crash"; +static NSString * const TD_APP_INSTALL_EVENT = @"ta_app_install"; + +static NSString * const TD_CRASH_REASON = @"#app_crashed_reason"; +static NSString * const TD_RESUME_FROM_BACKGROUND = @"#resume_from_background"; +static NSString * const TD_START_REASON = @"#start_reason"; +static NSString * const TD_BACKGROUND_DURATION = @"#background_duration"; + +static kEDEventTypeName const TD_EVENT_TYPE_TRACK = @"track"; + +static kEDEventTypeName const TD_EVENT_TYPE_USER_DEL = @"user_del"; +static kEDEventTypeName const TD_EVENT_TYPE_USER_ADD = @"user_add"; +static kEDEventTypeName const TD_EVENT_TYPE_USER_SET = @"user_set"; +static kEDEventTypeName const TD_EVENT_TYPE_USER_SETONCE = @"user_setOnce"; +static kEDEventTypeName const TD_EVENT_TYPE_USER_UNSET = @"user_unset"; +static kEDEventTypeName const TD_EVENT_TYPE_USER_APPEND = @"user_append"; +static kEDEventTypeName const TD_EVENT_TYPE_USER_UNIQ_APPEND= @"user_uniq_append"; + +#ifndef td_dispatch_main_sync_safe +#define td_dispatch_main_sync_safe(block)\ +if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(dispatch_get_main_queue())) {\ +block();\ +} else {\ +dispatch_sync(dispatch_get_main_queue(), block);\ +} +#endif + +#define kDefaultTimeFormat @"yyyy-MM-dd HH:mm:ss.SSS" + +static NSUInteger const kBatchSize = 50; +static NSUInteger const TA_PROPERTY_CRASH_LENGTH_LIMIT = 8191*2; +static NSString * const TA_JS_TRACK_SCHEME = @"thinkinganalytics://trackEvent"; + +#define kModeEnumArray @"NORMAL", @"DebugOnly", @"Debug", nil + +@interface ThinkingAnalyticsSDK () + +#if TARGET_OS_IOS +@property (nonatomic, strong) TAAutoTrackSuperProperty *autoTrackSuperProperty; +@property (nonatomic, strong) TDEncryptManager *encryptManager; +@property (strong,nonatomic) id thirdPartyManager; +#endif + +@property (atomic, copy) NSString *appid; +@property (atomic, copy) NSString *serverURL; +@property (atomic, copy, nullable) NSString *accountId; +@property (atomic, copy) NSString *identifyId; +@property (nonatomic, strong) TASuperProperty *superProperty; +@property (nonatomic, strong) TAPropertyPluginManager *propertyPluginManager; +//@property (nonatomic, strong) TASessionIdPropertyPlugin *sessionidPlugin; +@property (nonatomic, strong) TAAppLifeCycle *appLifeCycle; +/// TD error callback +@property (atomic, copy) void(^errorCallback)(NSInteger code, NSString * _Nullable errorMsg, NSString * _Nullable ext); + +@property (atomic, strong) NSMutableSet *ignoredViewTypeList; +@property (atomic, strong) NSMutableSet *ignoredViewControllers; + + +@property (atomic, assign, getter=isTrackPause) BOOL trackPause; +@property (atomic, assign) BOOL isEnabled; +@property (atomic, assign) BOOL isOptOut; + + +@property (nonatomic, strong, nullable) NSTimer *timer; + +@property (nonatomic, strong) TATrackTimer *trackTimer; + +@property (atomic, strong) TDSqliteDataQueue *dataQueue; +@property (nonatomic, copy) TDConfig *config; +@property (nonatomic, strong) WKWebView *wkWebView; + +#if TARGET_OS_IOS +- (void)autoTrackWithEvent:(TAAutoTrackEvent *)event properties:(nullable NSDictionary *)properties; +- (BOOL)isViewControllerIgnored:(UIViewController *)viewController; +- (BOOL)isAutoTrackEventTypeIgnored:(ThinkingAnalyticsAutoTrackEventType)eventType; +- (BOOL)isViewTypeIgnored:(Class)aClass; +#endif + +- (instancetype)initLight:(NSString *)appid withServerURL:(NSString *)serverURL withConfig:(TDConfig *)config; + +- (void)retrievePersistedData; ++ (dispatch_queue_t)td_trackQueue; ++ (dispatch_queue_t)td_networkQueue; ++ (id)sharedUIApplication; +- (NSInteger)saveEventsData:(NSDictionary *)data; +- (void)flushImmediately:(NSDictionary *)dataDic; +- (BOOL)hasDisabled; +- (BOOL)isValidName:(NSString *)name isAutoTrack:(BOOL)isAutoTrack; ++ (BOOL)isTrackEvent:(NSString *)eventType; +- (BOOL)checkEventProperties:(NSDictionary *)properties withEventType:(NSString *_Nullable)eventType haveAutoTrackEvents:(BOOL)haveAutoTrackEvents; +- (void)startFlushTimer; +- (double)getTimezoneOffset:(NSDate *)date timeZone:(NSTimeZone *)timeZone; ++ (NSMutableDictionary *)_getAllInstances; + ++ (NSMutableDictionary *)_getAllInstances; + +@end + +@interface TDEventModel () + +@property (nonatomic, copy) NSString *timeString; +@property (nonatomic, assign) double zoneOffset; +@property (nonatomic, assign) TimeValueType timeValueType; +@property (nonatomic, copy) NSString *extraID; +@property (nonatomic, assign) BOOL persist; +@property (nonatomic, strong) NSDate *time; +@property (nonatomic, strong) NSTimeZone *timeZone; + +- (instancetype)initWithEventName:(NSString * _Nullable)eventName; + +- (instancetype _Nonnull )initWithEventName:(NSString * _Nullable)eventName eventType:(kEDEventTypeName _Nonnull )eventType; + +@end + +@interface LightThinkingAnalyticsSDK : ThinkingAnalyticsSDK + +- (instancetype)initWithAPPID:(NSString *)appID withServerURL:(NSString *)serverURL withConfig:(TDConfig *)config; + +@end + +NS_ASSUME_NONNULL_END diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h.meta new file mode 100644 index 0000000..fbeee00 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 57b9bb9b2e014b242ab0491547002b7c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h new file mode 100644 index 0000000..b7cd833 --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h @@ -0,0 +1,38 @@ + +#import + +#if __has_include() +#import +#else +#import "ThinkingAnalyticsSDK.h" +#endif + +#if __has_include() +#import +#else +#import "TDFirstEventModel.h" +#endif + +#if __has_include() +#import +#else +#import "TDEditableEventModel.h" +#endif + +#if __has_include() +#import +#else +#import "TDConfig.h" +#endif + +#if __has_include() +#import +#else +#import "TDPresetProperties.h" +#endif + +#if __has_include() +#import +#else +#import "TDDeviceInfo.h" +#endif diff --git a/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h.meta b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h.meta new file mode 100644 index 0000000..34facae --- /dev/null +++ b/sdk-intergration/Packages/TA/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e385aa30cd049a34c9063fd5c45028e6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime.meta b/sdk-intergration/Packages/TA/Runtime.meta new file mode 100644 index 0000000..ff08b6d --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d04ab5feb9b027f4fb188506d52dc152 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics.meta new file mode 100644 index 0000000..da98201 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 60b198fb61dd042d5b21af809183759f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception.meta new file mode 100644 index 0000000..6e3b94b --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f347e0b07c5d64926ac64031c3bba013 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception/TDException.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception/TDException.cs new file mode 100644 index 0000000..8c0a8f2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception/TDException.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace ThinkingData.Analytics.TDException +{ + public class TDExceptionHandler + { + + //Whether to exit the program when an exception occurs + public static bool IsQuitWhenException = false; + + //Whether the exception catch has been registered + public static bool IsRegistered = false; + private static TDAutoTrackEventHandler mEventCallback; + private static Dictionary mProperties; + + + public static void SetAutoTrackProperties(Dictionary properties) + { + if (!(mProperties is Dictionary)) + { + mProperties = new Dictionary(); + } + + foreach (var item in properties) + { + if (!mProperties.ContainsKey(item.Key)) + { + mProperties.Add(item.Key, item.Value); + } + } + } + + public static void RegisterTAExceptionHandler(TDAutoTrackEventHandler eventCallback) + { + mEventCallback = eventCallback; + //Register exception handling delegate + try + { + if (!IsRegistered) + { + Application.logMessageReceived += _LogHandler; + AppDomain.CurrentDomain.UnhandledException += _UncaughtExceptionHandler; + IsRegistered = true; + } + } + catch + { + } + } + + public static void RegisterTAExceptionHandler(Dictionary properties) + { + SetAutoTrackProperties(properties); + //Register exception handling delegate + try + { + if (!IsRegistered) + { + Application.logMessageReceived += _LogHandler; + AppDomain.CurrentDomain.UnhandledException += _UncaughtExceptionHandler; + IsRegistered = true; + } + } + catch + { + } + } + + public static void UnregisterTAExceptionHandler () + { + //Clear exception handling delegate + try + { + Application.logMessageReceived -= _LogHandler; + System.AppDomain.CurrentDomain.UnhandledException -= _UncaughtExceptionHandler; + } + catch + { + } + } + + + private static void _LogHandler( string logString, string stackTrace, LogType type ) + { + if( type == LogType.Error || type == LogType.Exception || type == LogType.Assert ) + { + //Report exception event + string reasonStr = "exception_type: " + type.ToString() + "
" + "exception_message: " + logString + "
" + "stack_trace: " + stackTrace + "
" ; + Dictionary properties = new Dictionary(){ + {"#app_crashed_reason", reasonStr} + }; + properties = MergeProperties(properties); + TDAnalytics.Track("ta_app_crash", properties); + + if ( IsQuitWhenException ) + { + Application.Quit(); + } + } + } + + private static void _UncaughtExceptionHandler (object sender, System.UnhandledExceptionEventArgs args) + { + if (args == null || args.ExceptionObject == null) + { + return; + } + + try + { + if (args.ExceptionObject.GetType () != typeof(System.Exception)) + { + return; + } + } + catch + { + return; + } + + System.Exception e = (System.Exception)args.ExceptionObject; + + //Report exception event + string reasonStr = "exception_type: " + e.GetType().Name + "
" + "exception_message: " + e.Message + "
" + "stack_trace: " + e.StackTrace + "
" ; + Dictionary properties = new Dictionary(){ + {"#app_crashed_reason", reasonStr} + }; + properties = MergeProperties(properties); + TDAnalytics.Track("ta_app_crash", properties); + + if ( IsQuitWhenException ) + { + Application.Quit(); + } + } + + private static Dictionary MergeProperties(Dictionary properties) + { + + if (mEventCallback is TDAutoTrackEventHandler) + { + Dictionary callbackProperties = mEventCallback.GetAutoTrackEventProperties((int)TDAutoTrackEventType.AppCrash, properties); + foreach (var item in callbackProperties) + { + if (!properties.ContainsKey(item.Key)) + { + properties.Add(item.Key, item.Value); + } + } + } + + if (mProperties is Dictionary) + { + foreach (var item in mProperties) + { + if (!properties.ContainsKey(item.Key)) + { + properties.Add(item.Key, item.Value); + } + } + } + + return properties; + } + } +} diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception/TDException.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception/TDException.cs.meta new file mode 100644 index 0000000..766222b --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Exception/TDException.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 76f285e49c9584fce99e9fe1536fea17 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources.meta new file mode 100644 index 0000000..7f3bdf2 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4e5d07cff2f9b45d6afa9ab9a879fbff +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources/ta_public_config.xml b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources/ta_public_config.xml new file mode 100644 index 0000000..3f98eff --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources/ta_public_config.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources/ta_public_config.xml.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources/ta_public_config.xml.meta new file mode 100644 index 0000000..d130ea4 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Resources/ta_public_config.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b67db39cd532b4f08b95b2cf5938553f +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.asmdef b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.asmdef new file mode 100644 index 0000000..5ca02cd --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.asmdef @@ -0,0 +1,16 @@ +{ + "name": "ThinkingAnalytics", + "rootNamespace": "", + "references": [ + "GUID:0a958a7eb80a248e1b8bc4553787c209" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.asmdef.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.asmdef.meta new file mode 100644 index 0000000..5415231 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ff4bf6c59d1ac413a8f34ced5be76ecd +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.cs new file mode 100644 index 0000000..2010696 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.cs @@ -0,0 +1,1493 @@ +/* + * + 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:3.0.7 + */ + +/** +\mainpage + +\section main_introduction Introduction + +ThinkingData Analytics SDK for Unity. + +This is the [ThinkingData](https://www.thinkingdata.cn)™ Analytics SDK for Unity. Documentation is available on our help center in the following languages: + +- [English](https://docs.thinkingdata.cn/ta-manual/latest/en/installation/installation_menu/client_sdk/game_engine_sdk_installation/unity_sdk_installation/unity_sdk_installation.html) +- [中文](https://docs.thinkingdata.cn/ta-manual/latest/installation/installation_menu/client_sdk/game_engine_sdk_installation/unity_sdk_installation/unity_sdk_installation.html) + + */ + +#if !(UNITY_5_4_OR_NEWER) +#define DISABLE_TA +#warning "Your Unity version is not supported by us - ThinkingAnalyticsSDK disabled" +#endif + +using System; +using System.Collections.Generic; +using ThinkingData.Analytics.Utils; +using ThinkingData.Analytics.Wrapper; +using UnityEngine; +using ThinkingData.Analytics.TDException; +using UnityEngine.SceneManagement; + +namespace ThinkingData.Analytics +{ + /// + /// ThinkingData Analytics SDK for Unity. + /// + [DisallowMultipleComponent] + public class TDAnalytics : MonoBehaviour + { + #region settings + //[System.Serializable] + //public class TDConfig + //{ + // public string appId; + // public string serverUrl; + // public TDMode mode; + // public TDTimeZone timeZone; + // public string timeZoneId; + // public bool enableEncrypt; // enable data encryption, default is false (iOS/Android only) + // public int encryptVersion; // secret key version (iOS/Android only) + // public string encryptPublicKey; // public secret key (iOS/Android only) + // public TDSSLPinningMode pinningMode; // SSL Pinning mode, default is NONE (iOS/Android only) + // public bool allowInvalidCertificates; // allow invalid certificates, default is false (iOS/Android only) + // public bool validatesDomainName; // enable to verify domain name, default is true (iOS/Android only) + // private string sName; + // public string name { set { if (!string.IsNullOrEmpty(value)) sName = value.Replace(" ", ""); } get { return sName; } } // instances name + + // //public TDConfig(string appId, string serverUrl, TDMode mode = TDMode.Normal, TDTimeZone timeZone = TDTimeZone.Local, string timeZoneId = null, string instanceName = null) + // public TDConfig(string appId, string serverUrl) + // { + // this.appId = appId.Replace(" ", ""); + // this.serverUrl = serverUrl; + // this.mode = TDMode.Normal; + // this.timeZone = TDTimeZone.Local; + // this.timeZoneId = null; + // this.enableEncrypt = false; + // this.encryptVersion = 0; + // this.encryptPublicKey = null; + // this.pinningMode = TDSSLPinningMode.NONE; + // this.allowInvalidCertificates = false; + // this.validatesDomainName = true; + // //if (!string.IsNullOrEmpty(instanceName)) + // //{ + // // instanceName = instanceName.Replace(" ", ""); + // //} + // } + + // //public string GetInstanceName() + // //{ + // // return this.instanceName; + // //} + + // public string getTimeZoneId() + // { + // switch (timeZone) + // { + // case TDTimeZone.UTC: + // return "UTC"; + // case TDTimeZone.Asia_Shanghai: + // return "Asia/Shanghai"; + // case TDTimeZone.Asia_Tokyo: + // return "Asia/Tokyo"; + // case TDTimeZone.America_Los_Angeles: + // return "America/Los_Angeles"; + // case TDTimeZone.America_New_York: + // return "America/New_York"; + // case TDTimeZone.Other: + // return timeZoneId; + // default: + // break; + // } + // return null; + // } + //} + + //public enum TDTimeZone + //{ + // Local, + // UTC, + // Asia_Shanghai, + // Asia_Tokyo, + // America_Los_Angeles, + // America_New_York, + // Other = 100 + //} + + //public enum TDMode + //{ + // Debug = 1, + // DebugOnly = 2, + // Normal = 0 + //} + + //public enum TDNetworkType + //{ + // Wifi = 1, + // All = 0 + //} + + [Header("Configuration")] + [Tooltip("Enable Start SDK Manually")] + public bool startManually = true; + + [Tooltip("Enable Log")] + public bool enableLog = true; + [Tooltip("Sets the Network Type")] + public TDNetworkType networkType = TDNetworkType.All; + + [Header("Project")] + [Tooltip("Project Setting, APP ID is given when the project is created")] + [HideInInspector] + public TDConfig[] configs = new TDConfig[1]; + + #endregion + + /// + /// Whether to enable logs + /// + /// enable logs + public static void EnableLog(bool enable) + { + if (sThinkingData != null) + { + sThinkingData.enableLog = enable; + TDLog.EnableLog(enable); + TDWrapper.EnableLog(enable); + } + } + /// + /// Set custom distinct ID, to replace the distinct ID generated by the system + /// + /// distinct ID + /// project ID (optional) + public static void SetDistinctId(string distinctId, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetDistinctId(distinctId, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { distinctId, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Returns the current distinct ID + /// + /// distinct ID + /// project ID (optional) + public static string GetDistinctId(string appId = "") + { + if (tracking_enabled) + { + return TDWrapper.GetDistinctId(appId); + } + return null; + } + + /// + /// Set account ID. This method does not upload Login events + /// + /// account ID + /// project ID (optional) + public static void Login(string account, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Login(account, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { account, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Clear account ID. This method does not upload Logout events + /// + /// project ID (optional) + public static void Logout(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Logout(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Enable auto-tracking + /// + /// auto-tracking eventType + /// properties for auto-tracking events (optional) + /// project ID (optional) + public static void EnableAutoTrack(TDAutoTrackEventType eventType, Dictionary properties = null, string appId = "") + { + if (tracking_enabled) + { + if (properties == null) + { + properties = new Dictionary(); + } + TDWrapper.EnableAutoTrack(eventType, properties, appId); + if ((eventType & TDAutoTrackEventType.AppCrash) != 0 && !TDPublicConfig.DisableCSharpException) + { + TDExceptionHandler.RegisterTAExceptionHandler(properties); + } + if ((eventType & TDAutoTrackEventType.AppSceneLoad) != 0) + { + SceneManager.sceneLoaded -= TDAnalytics.OnSceneLoaded; + SceneManager.sceneLoaded += TDAnalytics.OnSceneLoaded; + } + if ((eventType & TDAutoTrackEventType.AppSceneUnload) != 0) + { + SceneManager.sceneUnloaded -= TDAnalytics.OnSceneUnloaded; + SceneManager.sceneUnloaded += TDAnalytics.OnSceneUnloaded; + } + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventType, properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Enable auto-tracking + /// + /// auto-tracking eventType + /// callback for auto-tracking events + /// project ID (optional) + public static void EnableAutoTrack(TDAutoTrackEventType eventType, TDAutoTrackEventHandler eventHandler, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.EnableAutoTrack(eventType, eventHandler, appId); + if ((eventType & TDAutoTrackEventType.AppCrash) != 0 && !TDPublicConfig.DisableCSharpException) + { + TDExceptionHandler.RegisterTAExceptionHandler(eventHandler); + } + if ((eventType & TDAutoTrackEventType.AppSceneLoad) != 0) + { + SceneManager.sceneLoaded -= TDAnalytics.OnSceneLoaded; + SceneManager.sceneLoaded += TDAnalytics.OnSceneLoaded; + } + if ((eventType & TDAutoTrackEventType.AppSceneUnload) != 0) + { + SceneManager.sceneUnloaded -= TDAnalytics.OnSceneUnloaded; + SceneManager.sceneUnloaded += TDAnalytics.OnSceneUnloaded; + } + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventType, eventHandler, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + + } + + /// + /// Set properties for auto-tracking events + /// + /// auto-tracking eventType + /// properties for auto-tracking events + /// project ID (optional) + public static void SetAutoTrackProperties(TDAutoTrackEventType eventType, Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetAutoTrackProperties(eventType, properties, appId); + if ((eventType & TDAutoTrackEventType.AppCrash) != 0 && !TDPublicConfig.DisableCSharpException) + { + TDExceptionHandler.SetAutoTrackProperties(properties); + } + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventType, properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Track a Event + /// + /// event name + /// project ID (optional) + public static void Track(string eventName, string appId = "") + { + Track(eventName, null, appId); + } + + /// + /// Track a Event + /// + /// the event name + /// properties for the event + /// project ID (optional) + public static void Track(string eventName, Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Track(eventName, properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventName, properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Track a Event + /// + /// the event name + /// properties for the event + /// date for the event + /// project ID (optional) + //[Obsolete("Method is deprecated, please use Track(string eventName, Dictionary properties, DateTime date, TimeZoneInfo timeZone, string appId = \"\") instead.")] + //public static void Track(string eventName, Dictionary properties, DateTime date, string appId = "") + //{ + // if (tracking_enabled) + // { + // TDWrapper.Track(eventName, properties, date, appId); + // } + // else + // { + // System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + // object[] parameters = new object[] { eventName, properties, date, appId }; + // eventCaches.Add(new Dictionary() { + // { "method", method}, + // { "parameters", parameters} + // }); + // } + //} + + /// + /// Track a Event + /// + /// the event name + /// properties for the event + /// date for the event + /// time zone for the event + /// project ID (optional) + public static void Track(string eventName, Dictionary properties, DateTime time, TimeZoneInfo timeZone, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Track(eventName, properties, time, timeZone, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventName, properties, time, timeZone, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Track a Special Event (First Event/Updatable Event/Overwritable Event) + /// + /// the special event + /// project ID (optional) + public static void Track(TDEventModel eventModel, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Track(eventModel, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventModel, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Quickly track a Special Event + /// + /// the event name, 'SceneView' for scene view event, 'AppClick' for click event + /// event properties + /// + public static void QuickTrack(string eventName, Dictionary properties = null, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.QuickTrack(eventName, properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventName, properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Report events data to TE server immediately + /// + /// project ID (optional) + public static void Flush(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Flush(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Scenes load Delegate + /// + /// the load scene + /// the scene loading mode + public static void OnSceneLoaded(Scene scene, LoadSceneMode mode) + { + if (tracking_enabled) + { + TDWrapper.TrackSceneLoad(scene); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { scene, mode }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Scenes unload Delegate + /// + /// the unload scene + public static void OnSceneUnloaded(Scene scene) + { + if (tracking_enabled) + { + TDWrapper.TrackSceneUnload(scene); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { scene }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Super Properties, refer to properties that would be uploaded by each event + /// + /// super properties for events + /// project ID (optional) + public static void SetSuperProperties(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetSuperProperties(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Delete Property form current Super Properties + /// + /// property name + /// project ID (optional) + public static void UnsetSuperProperty(string property, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UnsetSuperProperty(property, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { property, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Returns current Super Properties + /// + /// current super properties + /// project ID (optional) + public static Dictionary GetSuperProperties(string appId = "") + { + if (tracking_enabled) + { + return TDWrapper.GetSuperProperties(appId); + } + return null; + } + + /// + /// Clear current Super Properties + /// + /// project ID (optional) + public static void ClearSuperProperties(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.ClearSuperProperty(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Returns current Preset Properties + /// + /// current preset properties + /// project ID (optional) + public static TDPresetProperties GetPresetProperties(string appId = "") + { + if (tracking_enabled) + { + Dictionary properties = TDWrapper.GetPresetProperties(appId); + TDPresetProperties presetProperties = new TDPresetProperties(properties); + return presetProperties; + } + return null; + } + + /// + /// Sets the Dynamic Super Properties. + /// + /// dynamic super properties interface + /// project ID (optional) + public static void SetDynamicSuperProperties(TDDynamicSuperPropertiesHandler propertiesHandler, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetDynamicSuperProperties(propertiesHandler, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { propertiesHandler, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Records Event Duration, call TimeEvent to start timing for the Event, call Track to end timing + /// + /// the event name + /// project ID (optional) + public static void TimeEvent(string eventName, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.TimeEvent(eventName, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventName, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Sets User Properties, this will overwrite the original properties value + /// + /// user properties + /// project ID (optional) + public static void UserSet(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserSet(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Sets User Properties, this will overwrite the original properties value + /// + /// user properties + /// date time + /// project ID (optional) + public static void UserSet(Dictionary properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserSet(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties , dateTime, appId}; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Unsets one of User Porperties, this would not create properties that have not been created in TE + /// + /// the user property name + /// project ID (optional) + public static void UserUnset(string property, string appId = "") + { + List properties = new List(); + properties.Add(property); + UserUnset(properties, appId); + } + + + /// + /// Unsets some of User Porperties, this would not create properties that have not been created in TE + /// + /// the user properties name + /// project ID (optional) + public static void UserUnset(List properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserUnset(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + + } + + /// + /// Unsets some of User Porperties, this would not create properties that have not been created in TE + /// + /// the user properties name + /// date time + /// project ID (optional) + public static void UserUnset(List properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserUnset(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, dateTime, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Sets User Properties for Once. This message would be neglected, if such property had been set before + /// + /// user properties + /// project ID (optional) + public static void UserSetOnce(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserSetOnce(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + + } + + /// + /// Sets User Properties for Once. The property would be neglected, if such property had been set before + /// + /// user properties + /// date time + /// project ID (optional) + public static void UserSetOnce(Dictionary properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserSetOnce(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, dateTime,appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + + } + + /// + /// Accumulates the property. If the property has not been set, it would be given a value of 0 before computing. + /// + /// the property name + /// value of the property + /// project ID (optional) + public static void UserAdd(string property, object value, string appId = "") + { + Dictionary properties = new Dictionary() + { + { property, value } + }; + UserAdd(properties, appId); + } + + /// + /// Accumulates the property. If the property has not been set, it would be given a value of 0 before computing. + /// + /// user properties + /// project ID (optional) + public static void UserAdd(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserAdd(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Accumulates the property, type of Number. If the property has not been set, it would be given a value of 0 before computing. + /// + /// user properties + /// date time + /// project ID (optional) + public static void UserAdd(Dictionary properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserAdd(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, dateTime, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Appends the property, type of List. + /// + /// user properties + /// project ID (optional) + public static void UserAppend(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserAppend(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Appends the property, type of List. + /// + /// user properties + /// date time + /// project ID (optional) + public static void UserAppend(Dictionary properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserAppend(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, dateTime, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Appends the property Uniquely, type of List. If the property has been set, it would be neglected + /// + /// user properties + /// project ID (optional) + public static void UserUniqAppend(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserUniqAppend(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Appends the property Uniquely, type of List. If the property has been set, it would be neglected + /// + /// user prpoerties + /// date time + /// project ID (optional) + public static void UserUniqAppend(Dictionary properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserUniqAppend(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, dateTime, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Deletes All Properties for a user, the events triggered by the user are still exist + /// + /// project ID (optional) + public static void UserDelete(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserDelete(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Deletes All Properties for a user, the events triggered by the user are still exist + /// + /// date time + /// project ID (optional) + //public static void UserDelete(DateTime dateTime, string appId = "") + //{ + // if (tracking_enabled) + // { + // TDWrapper.UserDelete(dateTime, appId); + // } + // else + // { + // System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + // object[] parameters = new object[] { dateTime, appId }; + // eventCaches.Add(new Dictionary() { + // { "method", method}, + // { "parameters", parameters} + // }); + // } + //} + + /// + /// Sets Network Type for report date to TE + /// + /// network type, see TDNetworkType + /// project ID (optional) + public static void SetNetworkType(TDNetworkType networkType, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetNetworkType(networkType); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { networkType, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Gets TDAnalytics SDK version + /// + /// SDK version + public static string GetSDKVersion() + { + return TDPublicConfig.LIB_VERSION; + } + + /// + /// Gets the device identifier. + /// + /// The device identifier. + public static string GetDeviceId() + { + if (tracking_enabled) + { + return TDWrapper.GetDeviceId(); + } + return null; + } + + /// + /// Sets Data Report Status + /// + /// data report status, see TDTrackStatus + /// project ID (optional) + public static void SetTrackStatus(TDTrackStatus status, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetTrackStatus(status, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { status, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Stops Report Event Data, and Clear Cache Data (include unreported event data, custom distinct ID, account ID, Super Properties) + /// + /// project ID (optional) + [Obsolete("Method is deprecated, please use SetTrackStatus() instead.")] + public static void OptOutTracking(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.OptOutTracking(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Stops Report Event Data, and Clear Cache Data (include unreported event data, custom distinct ID, account ID, super properties), and Delete User + /// + /// project ID (optional) + [Obsolete("Method is deprecated, please use SetTrackStatus() instead.")] + public static void OptOutTrackingAndDeleteUser(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.OptOutTrackingAndDeleteUser(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Resumes Report Event Data + /// + /// project ID (optional) + [Obsolete("Method is deprecated, please use SetTrackStatus() instead.")] + public static void OptInTracking(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.OptInTracking(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Enable Report Event Data + /// + /// Whether to enable reported data + /// project ID (optional) + [Obsolete("Method is deprecated, please use SetTrackStatus() instead.")] + public static void EnableTracking(bool enabled, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.EnableTracking(enabled, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { enabled, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Creats Light Instance, it has same project ID to main instance, diffent distinct ID, account ID, super properties + /// + /// project ID (optional) + /// light instance token + public static string LightInstance(string appId = "") { + if (tracking_enabled) + { + return TDWrapper.CreateLightInstance(); + } + return null; + } + + /// + /// Calibrate Event Time, calibrated times are used for events after that + /// If both CalibrateTime and CalibrateTimeWithNtp are called, the event time is based on the CalibrateTimeWithNtp result. + /// + /// currnt Unix timestamp, units Ms + public static void CalibrateTime(long timestamp) + { + TDWrapper.CalibrateTime(timestamp); + } + + /// + /// Calibrate Event Time, calibrated times are used for events after that + /// If NTP server is not returns in 3s, the time will not be re-calibrated + /// If both CalibrateTime and CalibrateTimeWithNtp are called, the event time is based on the CalibrateTimeWithNtp result. + /// + /// NTP server, e.g 'time.asia.apple.com' + public static void CalibrateTimeWithNtp(string ntpServer) + { + TDWrapper.CalibrateTimeWithNtp(ntpServer); + } + + /// + /// Cross Platform + /// Share TE account system info to other platforms + /// + /// type of platforms, see TDThirdPartyType + /// properties of platforms + /// project ID (optional) + public static void EnableThirdPartySharing(TDThirdPartyType shareType, Dictionary properties = null, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.EnableThirdPartySharing(shareType, properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { shareType }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Gets the Local Country/Region Code + /// the two-letter code defined in ISO 3166 for the country/region + /// + /// country/region code + public static string GetLocalRegion() + { + return System.Globalization.RegionInfo.CurrentRegion.TwoLetterISORegionName; + } + + /// + /// Start Thinking Analytics SDK + /// + /// project ID + /// project URL + public static void Init(string appId, string serverUrl) + { + TDConfig tDConfig = new TDConfig(appId, serverUrl); + Init(tDConfig); + } + + /// + /// Start Thinking Analytics SDK + /// + /// project setting, see TDConfig + public static void Init(TDConfig config) + { + new GameObject("ThinkingData", typeof(TDAnalytics)); + TDConfig[] configs = new TDConfig[1]; + configs[0] = config; + TDAnalytics.Init(configs); + } + + /// + /// Start Thinking Analytics SDK + /// + /// projects setting, see TDConfig + public static void Init(TDConfig[] configs = null) + { + #if DISABLE_TA + tracking_enabled = false; + #else + tracking_enabled = true; + #endif + + if (tracking_enabled) + { + TDPublicConfig.GetPublicConfig(); + TDLog.EnableLog(sThinkingData.enableLog); + TDWrapper.EnableLog(sThinkingData.enableLog); + TDWrapper.SetVersionInfo(TDPublicConfig.LIB_VERSION); + if (configs == null) + { + configs = sThinkingData.configs; + } + try + { + for (int i = 0; i < configs.Length; i++) + { + TDConfig config = configs[i]; + if (!string.IsNullOrEmpty(config.appId)) + { + config.appId = config.appId.Replace(" ", ""); + TDWrapper.ShareInstance(config, sThinkingData); + TDWrapper.SetNetworkType(sThinkingData.networkType); + if (TDLog.GetEnable()) TDLog.i(string.Format("TDAnalytics SDK initialize success, AppId = {0}, ServerUrl = {1}, Mode = {2}, TimeZone = {3}, DeviceId = {4}, Lib = Unity, LibVersion = {5}{6}", config.appId, config.serverUrl, config.mode, config.timeZone, GetDeviceId(), GetSDKVersion(), (config.name != null ? (", Name = " + config.name) : ""))); + } + } + } + catch (Exception ex) + { + if(TDLog.GetEnable()) TDLog.i("ThinkingAnalytics start Error: " + ex.Message); + } + } + + FlushEventCaches(); + } + + #region internal + private static void FlushEventCaches() + { + List> tmpEventCaches = new List>(eventCaches); + eventCaches.Clear(); + foreach (Dictionary eventCache in tmpEventCaches) + { + if (eventCache.ContainsKey("method") && eventCache.ContainsKey("parameters")) + { + System.Reflection.MethodBase method = (System.Reflection.MethodBase)eventCache["method"]; + object[] parameters = (object[])eventCache["parameters"]; + method.Invoke(null, parameters); + } + } + } + + private void Awake() + { + if (sThinkingData == null) + { + sThinkingData = this; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + return; + } + + if (this.startManually == false) + { + TDAnalytics.Init(); + } + } + + private void Start() + { + } + + private void OnApplicationQuit() + { + //Scene scene = SceneManager.GetActiveScene(); + //if (scene != null) + //{ + // OnSceneUnloaded(scene); + //} + } + + private static TDAnalytics sThinkingData; + private static bool tracking_enabled = false; + private static List> eventCaches = new List>(); + #endregion + } + + /// + /// SDK Configuration information class + /// + [System.Serializable] + public class TDConfig + { + /// + /// Project ID + /// + public string appId; + /// + /// Project URL + /// + public string serverUrl; + /// + /// SDK Mode + /// + public TDMode mode; + /// + /// TIme Zone Type + /// + public TDTimeZone timeZone; + /// + /// Time Zone ID + /// + public string timeZoneId; + /// + /// enable data encryption, default is false (iOS/Android only) + /// + internal bool enableEncrypt; + /// + /// secret key version (iOS/Android only) + /// + internal int encryptVersion; + /// + /// public secret key (iOS/Android only) + /// + internal string encryptPublicKey; + internal string symType; + internal string asymType; + /// + /// SSL Pinning mode, default is NONE (iOS/Android only) + /// + public TDSSLPinningMode pinningMode; + /// + /// allow invalid certificates, default is false (iOS/Android only) + /// + public bool allowInvalidCertificates; + /// + /// enable to verify domain name, default is true (iOS/Android only) + /// + public bool validatesDomainName; + /// + /// SDK instance name, to call SDK quickly + /// + public string name { set { if (!string.IsNullOrEmpty(value)) sName = value.Replace(" ", ""); } get { return sName; } } // instances name + private string sName; + + /// + /// Construct TDConfig instance + /// + /// project ID + /// project URL + public TDConfig(string appId, string serverUrl) + { + this.appId = appId.Replace(" ", ""); + this.serverUrl = serverUrl; + this.mode = TDMode.Normal; + this.timeZone = TDTimeZone.Local; + this.timeZoneId = null; + this.enableEncrypt = false; + this.encryptPublicKey = null; + this.encryptVersion = 0; + this.symType = null; + this.asymType = null; + this.pinningMode = TDSSLPinningMode.NONE; + this.allowInvalidCertificates = false; + this.validatesDomainName = true; + } + + public void EnableEncrypt(string publicKey, int version = 0, string symType = "", string asymType = "") + { + this.enableEncrypt = true; + this.encryptVersion = version; + this.encryptPublicKey = publicKey; + this.symType = symType; + this.asymType = asymType; + } + + /// + /// Get Time Zone Identify Code in SDK + /// + /// Time Zone ID + public string getTimeZoneId() + { + switch (timeZone) + { + case TDTimeZone.UTC: + return "UTC"; + case TDTimeZone.Asia_Shanghai: + return "Asia/Shanghai"; + case TDTimeZone.Asia_Tokyo: + return "Asia/Tokyo"; + case TDTimeZone.America_Los_Angeles: + return "America/Los_Angeles"; + case TDTimeZone.America_New_York: + return "America/New_York"; + case TDTimeZone.Other: + return timeZoneId; + default: + break; + } + return null; + } + } + +} diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.cs.meta new file mode 100644 index 0000000..97dbead --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d7df5c5e5307f460e917e72bf75ec567 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.prefab b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.prefab new file mode 100644 index 0000000..6b69e88 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.prefab @@ -0,0 +1,61 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &3471622134282543401 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 57277738888238098} + - component: {fileID: 1362306111410183020} + m_Layer: 0 + m_Name: TDAnalytics + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &57277738888238098 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3471622134282543401} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1362306111410183020 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3471622134282543401} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d7df5c5e5307f460e917e72bf75ec567, type: 3} + m_Name: + m_EditorClassIdentifier: + startManually: 1 + enableLog: 1 + networkType: 1 + configs: + - appId: 1b1c1fef65e3482bad5c9d0e6a823356 + serverUrl: https://receiver.ta.thinkingdata.cn + mode: 0 + timeZone: 0 + timeZoneId: + enableEncrypt: 0 + encryptVersion: 0 + encryptPublicKey: + pinningMode: 0 + allowInvalidCertificates: 0 + validatesDomainName: 0 diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.prefab.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.prefab.meta new file mode 100644 index 0000000..4413269 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDAnalytics.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 986ad473b78d14e08b1f05d368cb1591 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEnum.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEnum.cs new file mode 100644 index 0000000..7ed5d6e --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEnum.cs @@ -0,0 +1,64 @@ +using System; + +namespace ThinkingData.Analytics +{ + /// + /// Time Zone in SDK options + /// + public enum TDTimeZone + { + Local, + UTC, + Asia_Shanghai, + Asia_Tokyo, + America_Los_Angeles, + America_New_York, + Other = 100 + } + + /// + /// SDK running mode options + /// + public enum TDMode + { + Debug = 1, + DebugOnly = 2, + Normal = 0 + } + + /// + /// Data post options + /// + public enum TDNetworkType + { + Wifi = 2, + All = 1 + } + + /// + /// Auto-tracking Events Type options + /// + [Flags] + public enum TDAutoTrackEventType + { + None = 0, + AppStart = 1 << 0, // reporting when the app enters the foreground (ta_app_start) + AppEnd = 1 << 1, // reporting when the app enters the background (ta_app_end) + AppCrash = 1 << 4, // reporting when an uncaught exception occurs (ta_app_crash) + AppInstall = 1 << 5, // reporting when the app is opened for the first time after installation (ta_app_install) + AppSceneLoad = 1 << 6, // reporting when the scene is loaded in the app (ta_scene_loaded) + AppSceneUnload = 1 << 7, // reporting when the scene is unloaded in the app (ta_scene_loaded) + All = AppStart | AppEnd | AppInstall | AppCrash | AppSceneLoad | AppSceneUnload + } + + /// + /// Data Reporting Status + /// + public enum TDTrackStatus + { + Pause = 1, // pause data reporting + Stop = 2, // stop data reporting, and clear caches + SaveOnly = 3, // data stores in the cache, but not be reported + Normal = 4 // resume data reporting + } +} \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEnum.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEnum.cs.meta new file mode 100644 index 0000000..5fcf5d1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEnum.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d704b4acc93944242809914ff26be411 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEvent.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEvent.cs new file mode 100644 index 0000000..b472f34 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEvent.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; + +namespace ThinkingData.Analytics +{ + /// + /// Special event class for internal use, do not use this class directly. + /// + public abstract class TDEventModel + { + public enum TDEventType + { + First, + Updatable, + Overwritable + } + + public TDEventModel(string eventName) + { + EventName = eventName; + } + + public TDEventType? EventType { get; set; } + public string EventName { get; } + public Dictionary Properties { get; set; } + + private DateTime EventTime { get; set; } + private TimeZoneInfo EventTimeZone { get; set; } + protected string ExtraId { get; set; } + + /// + /// Set date time and timezone for the event + /// + /// date time + /// timezone + public void SetTime(DateTime time, TimeZoneInfo timeZone) + { + EventTime = time; + EventTimeZone = timeZone; + } + + /// + /// Get date time for the event + /// + /// + public DateTime GetEventTime() + { + return EventTime; + } + + /// + /// Get timezone for the event + /// + /// + public TimeZoneInfo GetEventTimeZone() + { + return EventTimeZone; + } + + /// + /// Get identify code for the event + /// + /// + public string GetEventId() + { + return ExtraId; + } + } + + /// + /// First Event Model + /// + public class TDFirstEventModel : TDEventModel + { + /// + /// Construct TDFirstEventModel instance + /// + /// name for the event + public TDFirstEventModel(string eventName) : base(eventName) + { + EventType = TDEventType.First; + } + + /// + /// Construct TDFirstEventModel instance + /// + /// name for the event + /// check ID for the first event + public TDFirstEventModel(string eventName, string firstCheckId) : base(eventName) + { + EventType = TDEventType.First; + ExtraId = firstCheckId; + } + } + + /// + /// Updatable Event Model + /// + public class TDUpdatableEventModel : TDEventModel + { + /// + /// Construct TDUpdatableEventModel instance + /// + /// name for the event + /// ID for the event + public TDUpdatableEventModel(string eventName, string eventId) : base(eventName) + { + EventType = TDEventType.Updatable; + ExtraId = eventId; + } + } + + /// + /// Overwritable Event Model + /// + public class TDOverwritableEventModel : TDEventModel + { + /// + /// Construct TDOverwritableEventModel instance + /// + /// name for the event + /// ID for the event + public TDOverwritableEventModel(string eventName, string eventId) : base(eventName) + { + EventType = TDEventType.Overwritable; + ExtraId = eventId; + } + } + +} \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEvent.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEvent.cs.meta new file mode 100644 index 0000000..77f698f --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDEvent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e80e3a99b3f0414187c8b3e448b4d66 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDInterface.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDInterface.cs new file mode 100644 index 0000000..88f33f1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDInterface.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace ThinkingData.Analytics +{ + /// + /// Dynamic super properties interfaces. + /// + public interface TDDynamicSuperPropertiesHandler + { + /// + /// Dynamically gets event properties + /// + /// event properties + Dictionary GetDynamicSuperProperties(); + } + + /// + /// Auto track event callback interfaces. + /// + public interface TDAutoTrackEventHandler + { + /// + /// Get Auto track event properties + /// + /// auto track event type + /// event properties + /// event properties + Dictionary GetAutoTrackEventProperties(int type, Dictionary properties); + } +} \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDInterface.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDInterface.cs.meta new file mode 100644 index 0000000..96f6ca1 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDInterface.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bba307e0084684dc08b109bd32fb415f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDPresetProperties.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDPresetProperties.cs new file mode 100644 index 0000000..c60dd58 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDPresetProperties.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; + +namespace ThinkingData.Analytics +{ + /// + /// Preset Properties + /// + public class TDPresetProperties + { + /// + /// Construct TDPresetProperties instance + /// + /// preset properties + public TDPresetProperties(Dictionary properties) + { + properties = TDEncodeDate(properties); + mPresetProperties = properties; + } + /// + /// Returns Preset Properties + /// The key starts with "#", it is not recommended to use it directly as a user properties + /// + /// preset properties + public Dictionary ToDictionary() + { + return mPresetProperties; + } + /// + /// Application Version Number + /// + public string AppVersion + { + get { return (string)(mPresetProperties.ContainsKey("#app_version") ? mPresetProperties["#app_version"] : ""); } + } + /// + /// Application Bundle Identify + /// + public string BundleId + { + get { return (string)(mPresetProperties.ContainsKey("#bundle_id") ? mPresetProperties["#bundle_id"] : ""); } + } + /// + /// Device Network Carrier + /// + public string Carrier + { + get { return (string)(mPresetProperties.ContainsKey("#carrier") ? mPresetProperties["#carrier"] : ""); } + } + /// + /// Device Identify + /// + public string DeviceId + { + get { return (string)(mPresetProperties.ContainsKey("#device_id") ? mPresetProperties["#device_id"] : ""); } + } + /// + /// Device Model Name + /// + public string DeviceModel + { + get { return (string)(mPresetProperties.ContainsKey("#device_model") ? mPresetProperties["#device_model"] : ""); } + } + /// + /// Device Hardware Manufacturer + /// + public string Manufacturer + { + get { return (string)(mPresetProperties.ContainsKey("#manufacturer") ? mPresetProperties["#manufacturer"] : ""); } + } + /// + /// Device Network Type + /// + public string NetworkType + { + get { return (string)(mPresetProperties.ContainsKey("#network_type") ? mPresetProperties["#network_type"] : ""); } + } + /// + /// Device System OS Name + /// + public string OS + { + get { return (string)(mPresetProperties.ContainsKey("#os") ? mPresetProperties["#os"] : ""); } + } + /// + /// Device System OS Version Number + /// + public string OSVersion + { + get { return (string)(mPresetProperties.ContainsKey("#os_version") ? mPresetProperties["#os_version"] : ""); } + } + /// + /// Screen Height + /// + public double ScreenHeight + { + get { return Convert.ToDouble(mPresetProperties.ContainsKey("#screen_height") ? mPresetProperties["#screen_height"] : 0); } + } + /// + /// Screen Width + /// + public double ScreenWidth + { + get { return Convert.ToDouble(mPresetProperties.ContainsKey("#screen_width") ? mPresetProperties["#screen_width"] : 0); } + } + /// + /// Device System Language Code + /// + public string SystemLanguage + { + get { return (string)(mPresetProperties.ContainsKey("#system_language") ? mPresetProperties["#system_language"] : ""); } + } + /// + /// Time Zone Offset With UTC + /// + public double ZoneOffset + { + get { return Convert.ToDouble(mPresetProperties.ContainsKey("#zone_offset") ? mPresetProperties["#zone_offset"] : 0); } + } + /// + /// Application Install Time + /// + public string InstallTime + { + get { return (string)(mPresetProperties.ContainsKey("#install_time") ? mPresetProperties["#install_time"] : ""); } + } + /// + /// Device Disk Size + /// + public string Disk + { + get { return (string)(mPresetProperties.ContainsKey("#disk") ? mPresetProperties["#disk"] : ""); } + } + /// + /// Device Ram Size + /// + public string Ram + { + get { return (string)(mPresetProperties.ContainsKey("#ram") ? mPresetProperties["#ram"] : ""); } + } + /// + /// Device FPS + /// + public double Fps + { + get { return Convert.ToDouble(mPresetProperties.ContainsKey("#fps") ? mPresetProperties["#fps"] : 0); } + } + /// + /// Device is an Simulator + /// + public bool Simulator + { + get { return (bool)(mPresetProperties.ContainsKey("#simulator") ? mPresetProperties["#simulator"] : false); } + } + + private Dictionary mPresetProperties { get; set; } + private Dictionary TDEncodeDate(Dictionary properties) + { + Dictionary mProperties = new Dictionary(); + foreach (KeyValuePair kv in properties) + { + if (kv.Value is DateTime) + { + DateTime dateTime = (DateTime)kv.Value; + mProperties.Add(kv.Key, dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture)); + } + else + { + mProperties.Add(kv.Key, kv.Value); + } + } + return mProperties; + } + } +} \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDPresetProperties.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDPresetProperties.cs.meta new file mode 100644 index 0000000..bdf2e3b --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/TDPresetProperties.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 23d7545160a0c4e67be49833c78690e3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/ThinkingAnalyticsAPI.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/ThinkingAnalyticsAPI.cs new file mode 100644 index 0000000..2ec9b0a --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/ThinkingAnalyticsAPI.cs @@ -0,0 +1,1753 @@ +/* + * + 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:3.0.7 + */ +#if !(UNITY_5_4_OR_NEWER) +#define DISABLE_TA +#warning "Your Unity version is not supported by us - ThinkingAnalyticsSDK disabled" +#endif + +using System; +using System.Collections.Generic; +using ThinkingData.Analytics; +using ThinkingData.Analytics.Utils; +using ThinkingData.Analytics.Wrapper; +using UnityEngine; +using ThinkingData.Analytics.TDException; +using UnityEngine.SceneManagement; + +namespace ThinkingAnalytics +{ + [DisallowMultipleComponent] + [Obsolete("ThinkingAnalyticsAPI is deprecated, please use ThinkingData.Analytics instead.")] + public class ThinkingAnalyticsAPI : MonoBehaviour, TDDynamicSuperPropertiesHandler, TDAutoTrackEventHandler + { + #region settings + [System.Serializable] + public struct Token + { + public string appid; + public string serverUrl; + public TAMode mode; + public TATimeZone timeZone; + public string timeZoneId; + public bool enableEncrypt; // enable data encryption, default is false (iOS/Android only) + public int encryptVersion; // secret key version (iOS/Android only) + public string encryptPublicKey; // public secret key (iOS/Android only) + public TDSSLPinningMode pinningMode; // SSL Pinning mode, default is NONE (iOS/Android only) + public bool allowInvalidCertificates; // allow invalid certificates, default is false (iOS/Android only) + public bool validatesDomainName; // enable to verify domain name, default is true (iOS/Android only) + private string instanceName; // instances name + + public Token(string appId, string serverUrl, TAMode mode = TAMode.NORMAL, TATimeZone timeZone = TATimeZone.Local, string timeZoneId = null, string instanceName = null) + { + this.appid = appId.Replace(" ", ""); + this.serverUrl = serverUrl; + this.mode = mode; + this.timeZone = timeZone; + this.timeZoneId = timeZoneId; + this.enableEncrypt = false; + this.encryptVersion = 0; + this.encryptPublicKey = null; + this.pinningMode = TDSSLPinningMode.NONE; + this.allowInvalidCertificates = false; + this.validatesDomainName = true; + if (!string.IsNullOrEmpty(instanceName)) + { + instanceName = instanceName.Replace(" ", ""); + } + this.instanceName = instanceName; + } + + public string GetInstanceName() + { + return this.instanceName; + } + + public string getTimeZoneId() + { +#if UNITY_STANDALONE_WIN + switch (timeZone) + { + case TATimeZone.UTC: + return "UTC"; + case TATimeZone.Asia_Shanghai: + return "China Standard Time"; + case TATimeZone.Asia_Tokyo: + return "Tokyo Standard Time"; + case TATimeZone.America_Los_Angeles: + return "Pacific Standard Time"; + case TATimeZone.America_New_York: + return "Eastern Standard Time"; + case TATimeZone.Other: + return timeZoneId; + default: + break; + } +#else + 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; + } +#endif + return null; + } + + internal TDConfig ToTDConfig() + { + TDConfig config = new TDConfig(this.appid, this.serverUrl); + config.mode = (TDMode)this.mode; + config.timeZone = (TDTimeZone)this.timeZone; + config.timeZoneId = this.timeZoneId; + config.pinningMode = this.pinningMode; + config.allowInvalidCertificates = this.allowInvalidCertificates; + config.validatesDomainName = this.validatesDomainName; + config.name = this.instanceName; + if(this.enableEncrypt){ + config.EnableEncrypt(this.encryptPublicKey, this.encryptVersion); + } + return config; + } + } + + 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("Enable Start SDK Manually")] + public bool startManually = true; + + [Tooltip("Enable Log")] + public bool enableLog = true; + [Tooltip("Sets the Network Type")] + public NetworkType networkType = NetworkType.DEFAULT; + + [Header("Project")] + [Tooltip("Project Setting, APP ID is given when the project is created")] + [HideInInspector] + public Token[] tokens = new Token[1]; + +#endregion + + /// + /// Whether to enable logs + /// + /// enable logs + public static void EnableLog(bool enable, string appId = "") + { + if (sThinkingAnalyticsAPI != null) + { + sThinkingAnalyticsAPI.enableLog = enable; + TDLog.EnableLog(enable); + TDWrapper.EnableLog(enable); + } + } + /// + /// Set custom distinct ID, to replace the distinct ID generated by the system + /// + /// distinct ID + /// project ID (optional) + public static void Identify(string distinctId, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetDistinctId(distinctId, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { distinctId, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Returns the current distinct ID + /// + /// distinct ID + /// project ID (optional) + public static string GetDistinctId(string appId = "") + { + if (tracking_enabled) + { + return TDWrapper.GetDistinctId(appId); + } + return null; + } + + /// + /// Set account ID. This method does not upload Login events + /// + /// account ID + /// project ID (optional) + public static void Login(string account, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Login(account, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { account, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Clear account ID. This method does not upload Logout events + /// + /// project ID (optional) + public static void Logout(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Logout(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Enable auto-tracking + /// + /// auto-tracking events + /// properties for auto-tracking events (optional) + /// project ID (optional) + public static void EnableAutoTrack(AUTO_TRACK_EVENTS events, Dictionary properties = null, string appId = "") + { + if (tracking_enabled) + { + if (properties == null) + { + properties = new Dictionary(); + } + TDWrapper.EnableAutoTrack((TDAutoTrackEventType)events, properties, appId); + if ((events & AUTO_TRACK_EVENTS.AppCrash) != 0 && !TDPublicConfig.DisableCSharpException) + { + TDExceptionHandler.RegisterTAExceptionHandler(properties); + } + if ((events & AUTO_TRACK_EVENTS.AppSceneLoad) != 0) + { + SceneManager.sceneLoaded -= ThinkingAnalyticsAPI.OnSceneLoaded; + SceneManager.sceneLoaded += ThinkingAnalyticsAPI.OnSceneLoaded; + } + if ((events & AUTO_TRACK_EVENTS.AppSceneUnload) != 0) + { + SceneManager.sceneUnloaded -= ThinkingAnalyticsAPI.OnSceneUnloaded; + SceneManager.sceneUnloaded += ThinkingAnalyticsAPI.OnSceneUnloaded; + } + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { events, properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Enable auto-tracking + /// + /// auto-tracking events + /// callback for auto-tracking events (optional) + /// project ID (optional) + public static void EnableAutoTrack(AUTO_TRACK_EVENTS events, IAutoTrackEventCallback eventCallback, string appId = "") + { + if (tracking_enabled) + { + sThinkingAnalyticsAPI._eventCallback = eventCallback; + TDWrapper.EnableAutoTrack((TDAutoTrackEventType)events, sThinkingAnalyticsAPI, appId); + if ((events & AUTO_TRACK_EVENTS.AppCrash) != 0 && !TDPublicConfig.DisableCSharpException) + { + TDExceptionHandler.RegisterTAExceptionHandler(sThinkingAnalyticsAPI); + } + if ((events & AUTO_TRACK_EVENTS.AppSceneLoad) != 0) + { + SceneManager.sceneLoaded -= ThinkingAnalyticsAPI.OnSceneLoaded; + SceneManager.sceneLoaded += ThinkingAnalyticsAPI.OnSceneLoaded; + } + if ((events & AUTO_TRACK_EVENTS.AppSceneUnload) != 0) + { + SceneManager.sceneUnloaded -= ThinkingAnalyticsAPI.OnSceneUnloaded; + SceneManager.sceneUnloaded += ThinkingAnalyticsAPI.OnSceneUnloaded; + } + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { events, eventCallback, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + + } + + /// + /// Set properties for auto-tracking events + /// + /// auto-tracking events + /// properties for auto-tracking events + /// project ID (optional) + public static void SetAutoTrackProperties(AUTO_TRACK_EVENTS events, Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetAutoTrackProperties((TDAutoTrackEventType)events, properties, appId); + if ((events & AUTO_TRACK_EVENTS.AppCrash) != 0 && !TDPublicConfig.DisableCSharpException) + { + TDWrapper.SetAutoTrackProperties((TDAutoTrackEventType)events ,properties, appId); + } + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { events, properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Track a Event + /// + /// event name + /// project ID (optional) + public static void Track(string eventName, string appId = "") + { + Track(eventName, null, appId); + } + + /// + /// Track a Event + /// + /// the event name + /// properties for the event + /// project ID (optional) + public static void Track(string eventName, Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Track(eventName, properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventName, properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Track a Event + /// + /// the event name + /// properties for the event + /// date for the event + /// project ID (optional) + [Obsolete("Method is deprecated, please use Track(string eventName, Dictionary properties, DateTime date, TimeZoneInfo timeZone, string appId = \"\") instead.")] + public static void Track(string eventName, Dictionary properties, DateTime date, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Track(eventName, properties, date, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventName, properties, date, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Track a Event + /// + /// the event name + /// properties for the event + /// date for the event + /// time zone for the event + /// project ID (optional) + public static void Track(string eventName, Dictionary properties, DateTime date, TimeZoneInfo timeZone, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Track(eventName, properties, date, timeZone, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventName, properties, date, timeZone, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Track a Special Event (First Event/Updatable Event/Overwritable Event) + /// + /// the special event + /// project ID (optional) + public static void Track(ThinkingAnalyticsEvent analyticsEvent, string appId = "") + { + if (tracking_enabled) + { + TDEventModel eventModel; + if (analyticsEvent.EventType == ThinkingAnalyticsEvent.Type.UPDATABLE) + { + eventModel = new TDUpdatableEventModel(analyticsEvent.EventName, analyticsEvent.ExtraId); + } + else if (analyticsEvent.EventType == ThinkingAnalyticsEvent.Type.OVERWRITABLE) + { + eventModel = new TDUpdatableEventModel(analyticsEvent.EventName, analyticsEvent.ExtraId); + } + else + { + if (string.IsNullOrEmpty(analyticsEvent.ExtraId)) + { + eventModel = new TDFirstEventModel(analyticsEvent.EventName); + } + else + { + eventModel = new TDFirstEventModel(analyticsEvent.EventName, analyticsEvent.ExtraId); + } + } + eventModel.Properties = analyticsEvent.Properties; + eventModel.SetTime(analyticsEvent.EventTime, analyticsEvent.EventTimeZone); + TDWrapper.Track(eventModel, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { analyticsEvent, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Quickly track a Special Event + /// + /// the event name, 'SceneView' for scene view event, 'AppClick' for click event + /// event properties + /// + public static void QuickTrack(string eventName, Dictionary properties = null, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.QuickTrack(eventName, properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventName, properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Report events data to TE server immediately + /// + /// project ID (optional) + public static void Flush(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.Flush(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Scenes load Delegate + /// + /// the load scene + /// the scene loading mode + public static void OnSceneLoaded(Scene scene, LoadSceneMode mode) + { + if (tracking_enabled) + { + TDWrapper.TrackSceneLoad(scene); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { scene, mode }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Scenes unload Delegate + /// + /// the unload scene + public static void OnSceneUnloaded(Scene scene) + { + if (tracking_enabled) + { + TDWrapper.TrackSceneUnload(scene); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { scene }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Super Properties, refer to properties that would be uploaded by each event + /// + /// super properties for events + /// project ID (optional) + public static void SetSuperProperties(Dictionary superProperties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetSuperProperties(superProperties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { superProperties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Delete Property form current Super Properties + /// + /// property name + /// project ID (optional) + public static void UnsetSuperProperty(string property, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UnsetSuperProperty(property, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { property, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Returns current Super Properties + /// + /// current super properties + /// project ID (optional) + public static Dictionary GetSuperProperties(string appId = "") + { + if (tracking_enabled) + { + return TDWrapper.GetSuperProperties(appId); + } + return null; + } + + /// + /// Clear current Super Properties + /// + /// project ID (optional) + public static void ClearSuperProperties(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.ClearSuperProperty(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Returns current Preset Properties + /// + /// current preset properties + /// project ID (optional) + public static TDPresetProperties GetPresetProperties(string appId = "") + { + if (tracking_enabled) + { + Dictionary properties = TDWrapper.GetPresetProperties(appId); + TDPresetProperties presetProperties = new TDPresetProperties(properties); + return presetProperties; + } + return null; + } + + /// + /// Sets the Dynamic Super Properties. + /// + /// dynamic super properties interface + /// project ID (optional) + public static void SetDynamicSuperProperties(IDynamicSuperProperties dynamicSuperProperties, string appId = "") + { + if (tracking_enabled) + { + sThinkingAnalyticsAPI._dynamicSuperProperties = dynamicSuperProperties; + TDWrapper.SetDynamicSuperProperties(sThinkingAnalyticsAPI, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { dynamicSuperProperties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Records Event Duration, call TimeEvent to start timing for the Event, call Track to end timing + /// + /// the event name + /// project ID (optional) + public static void TimeEvent(string eventName, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.TimeEvent(eventName, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { eventName, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Sets User Properties, this will overwrite the original properties value + /// + /// user properties + /// project ID (optional) + public static void UserSet(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserSet(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Sets User Properties, this will overwrite the original properties value + /// + /// user properties + /// date time + /// project ID (optional) + public static void UserSet(Dictionary properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserSet(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties , dateTime, appId}; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Unsets one of User Porperties, this would not create properties that have not been created in TE + /// + /// the user property name + /// project ID (optional) + public static void UserUnset(string property, string appId = "") + { + List properties = new List(); + properties.Add(property); + UserUnset(properties, appId); + } + + + /// + /// Unsets some of User Porperties, this would not create properties that have not been created in TE + /// + /// the user properties name + /// project ID (optional) + public static void UserUnset(List properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserUnset(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + + } + + /// + /// Unsets some of User Porperties, this would not create properties that have not been created in TE + /// + /// the user properties name + /// date time + /// project ID (optional) + public static void UserUnset(List properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserUnset(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, dateTime, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Sets User Properties for Once. This message would be neglected, if such property had been set before + /// + /// user properties + /// project ID (optional) + public static void UserSetOnce(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserSetOnce(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + + } + + /// + /// Sets User Properties for Once. The property would be neglected, if such property had been set before + /// + /// user properties + /// date time + /// project ID (optional) + public static void UserSetOnce(Dictionary properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserSetOnce(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, dateTime,appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + + } + + /// + /// Accumulates the property. If the property has not been set, it would be given a value of 0 before computing. + /// + /// the property name + /// value of the property + /// project ID (optional) + public static void UserAdd(string property, object value, string appId = "") + { + Dictionary properties = new Dictionary() + { + { property, value } + }; + UserAdd(properties, appId); + } + + /// + /// Accumulates the property. If the property has not been set, it would be given a value of 0 before computing. + /// + /// user properties + /// project ID (optional) + public static void UserAdd(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserAdd(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Accumulates the property, type of Number. If the property has not been set, it would be given a value of 0 before computing. + /// + /// user properties + /// date time + /// project ID (optional) + public static void UserAdd(Dictionary properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserAdd(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, dateTime, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Appends the property, type of List. + /// + /// user properties + /// project ID (optional) + public static void UserAppend(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserAppend(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Appends the property, type of List. + /// + /// user properties + /// date time + /// project ID (optional) + public static void UserAppend(Dictionary properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserAppend(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, dateTime, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Appends the property Uniquely, type of List. If the property has been set, it would be neglected + /// + /// user properties + /// project ID (optional) + public static void UserUniqAppend(Dictionary properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserUniqAppend(properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Appends the property Uniquely, type of List. If the property has been set, it would be neglected + /// + /// user prpoerties + /// date time + /// project ID (optional) + public static void UserUniqAppend(Dictionary properties, DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserUniqAppend(properties, dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { properties, dateTime, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Deletes All Properties for a user, the events triggered by the user are still exist + /// + /// project ID (optional) + public static void UserDelete(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserDelete(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Deletes All Properties for a user, the events triggered by the user are still exist + /// + /// date time + /// project ID (optional) + public static void UserDelete(DateTime dateTime, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserDelete(dateTime, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { dateTime, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Sets Network Type for report date to TE + /// + /// network type, see NetworkType + /// project ID (optional) + public static void SetNetworkType(NetworkType networkType, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetNetworkType((TDNetworkType)networkType); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { networkType, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Gets the device identifier. + /// + /// The device identifier. + public static string GetDeviceId() + { + if (tracking_enabled) + { + return TDWrapper.GetDeviceId(); + } + return null; + } + + /// + /// Sets Data Report Status + /// + /// data report status, see TA_TRACK_STATUS + /// project ID (optional) + public static void SetTrackStatus(TA_TRACK_STATUS status, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.SetTrackStatus((TDTrackStatus)status, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { status, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Stops Report Event Data, and Clear Cache Data (include unreported event data, custom distinct ID, account ID, Super Properties) + /// + /// project ID (optional) + [Obsolete("Method is deprecated, please use SetTrackStatus() instead.")] + public static void OptOutTracking(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.OptOutTracking(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Stops Report Event Data, and Clear Cache Data (include unreported event data, custom distinct ID, account ID, super properties), and Delete User + /// + /// project ID (optional) + [Obsolete("Method is deprecated, please use SetTrackStatus() instead.")] + public static void OptOutTrackingAndDeleteUser(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.OptOutTrackingAndDeleteUser(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Resumes Report Event Data + /// + /// project ID (optional) + [Obsolete("Method is deprecated, please use SetTrackStatus() instead.")] + public static void OptInTracking(string appId = "") + { + if (tracking_enabled) + { + TDWrapper.OptInTracking(appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Enable Report Event Data + /// + /// Whether to enable reported data + /// project ID (optional) + [Obsolete("Method is deprecated, please use SetTrackStatus() instead.")] + public static void EnableTracking(bool enabled, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.EnableTracking(enabled, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { enabled, appId }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Creats Light Instance, it has same project ID to main instance, diffent distinct ID, account ID, super properties + /// + /// project ID (optional) + /// light instance token + public static string CreateLightInstance(string appId = "") { + if (tracking_enabled) + { + return TDWrapper.CreateLightInstance(); + } + return null; + } + + /// + /// Calibrate Event Time, calibrated times are used for events after that + /// + /// currnt Unix timestamp, units Ms + public static void CalibrateTime(long timestamp) + { + TDWrapper.CalibrateTime(timestamp); + } + + /// + /// Calibrate Event Time, calibrated times are used for events after that + /// If NTP server is not returns in 3s, the time will not be re-calibrated + /// + /// NTP server, e.g 'time.asia.apple.com' + public static void CalibrateTimeWithNtp(string ntpServer) + { + TDWrapper.CalibrateTimeWithNtp(ntpServer); + } + + /// + /// Cross Platform + /// Share TE account system info to other platforms + /// + /// type of platforms, see TAThirdPartyShareType + /// properties of platforms + /// project ID (optional) + public static void EnableThirdPartySharing(TAThirdPartyShareType shareType, Dictionary properties = null, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.EnableThirdPartySharing((TDThirdPartyType)shareType, properties, appId); + } + else + { + System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod(); + object[] parameters = new object[] { shareType }; + eventCaches.Add(new Dictionary() { + { "method", method}, + { "parameters", parameters} + }); + } + } + + /// + /// Gets the Local Country/Region Code + /// the two-letter code defined in ISO 3166 for the country/region + /// + /// country/region code + public static string GetLocalRegion() + { + return System.Globalization.RegionInfo.CurrentRegion.TwoLetterISORegionName; + } + + /// + /// Start Thinking Analytics SDK + /// + /// project ID + /// project URL + public static void StartThinkingAnalytics(string appId, string serverUrl) + { + ThinkingAnalyticsAPI.TAMode mode = ThinkingAnalyticsAPI.TAMode.NORMAL; + ThinkingAnalyticsAPI.TATimeZone timeZone = ThinkingAnalyticsAPI.TATimeZone.Local; + ThinkingAnalyticsAPI.Token token = new ThinkingAnalyticsAPI.Token(appId, serverUrl, mode, timeZone); + ThinkingAnalyticsAPI.StartThinkingAnalytics(token); + } + + /// + /// Start Thinking Analytics SDK + /// + /// project setting, see ThinkingAnalyticsAPI.Token + public static void StartThinkingAnalytics(ThinkingAnalyticsAPI.Token token) + { + ThinkingAnalyticsAPI.Token[] tokens = new ThinkingAnalyticsAPI.Token[1]; + tokens[0] = token; + ThinkingAnalyticsAPI.StartThinkingAnalytics(tokens); + } + + /// + /// Start Thinking Analytics SDK + /// + /// projects setting, see ThinkingAnalyticsAPI.Token + public static void StartThinkingAnalytics(Token[] tokens = null) + { +#if DISABLE_TA + tracking_enabled = false; +#else + tracking_enabled = true; +#endif + + if (tracking_enabled) + { + TDPublicConfig.GetPublicConfig(); + TDLog.EnableLog(sThinkingAnalyticsAPI.enableLog); + TDWrapper.EnableLog(sThinkingAnalyticsAPI.enableLog); + TDWrapper.SetVersionInfo(TDPublicConfig.LIB_VERSION); + if (tokens == null) + { + tokens = sThinkingAnalyticsAPI.tokens; + } + try + { + for (int i = 0; i < tokens.Length; i++) + { + Token token = tokens[i]; + if (!string.IsNullOrEmpty(token.appid)) + { + token.appid = token.appid.Replace(" ", ""); + if(TDLog.GetEnable()) TDLog.d("ThinkingAnalytics start with APPID: " + token.appid + ", SERVERURL: " + token.serverUrl + ", MODE: " + token.mode); + TDWrapper.ShareInstance(token.ToTDConfig(), sThinkingAnalyticsAPI); + TDWrapper.SetNetworkType((TDNetworkType)sThinkingAnalyticsAPI.networkType); + } + } + } + catch (Exception ex) + { + if(TDLog.GetEnable()) TDLog.d("ThinkingAnalytics start Error: " + ex.Message); + } + } + + FlushEventCaches(); + } + + #region internal + private static void FlushEventCaches() + { + List> tmpEventCaches = new List>(eventCaches); + eventCaches.Clear(); + foreach (Dictionary eventCache in tmpEventCaches) + { + if (eventCache.ContainsKey("method") && eventCache.ContainsKey("parameters")) + { + System.Reflection.MethodBase method = (System.Reflection.MethodBase)eventCache["method"]; + object[] parameters = (object[])eventCache["parameters"]; + method.Invoke(null, parameters); + } + } + } + + private void Awake() + { + if (sThinkingAnalyticsAPI == null) + { + sThinkingAnalyticsAPI = this; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + return; + } + + if (this.startManually == false) + { + ThinkingAnalyticsAPI.StartThinkingAnalytics(); + } + } + + private void Start() + { + } + + private void OnApplicationQuit() + { + //Scene scene = SceneManager.GetActiveScene(); + //if (scene != null) + //{ + // OnSceneUnloaded(scene); + //} + } + + private static ThinkingAnalyticsAPI sThinkingAnalyticsAPI; + private static bool tracking_enabled = false; + private static List> eventCaches = new List>(); + private IDynamicSuperProperties _dynamicSuperProperties; + private IAutoTrackEventCallback _eventCallback; + #endregion + + /// + /// Dynamically gets event properties + /// + /// event properties + public Dictionary GetDynamicSuperProperties() + { + if (this._dynamicSuperProperties != null) + { + return this._dynamicSuperProperties.GetDynamicSuperProperties(); + } + else + { + return null; + } + } + /// + /// Get Auto track event properties + /// + /// auto track event type + /// event properties + /// event properties + public Dictionary GetAutoTrackEventProperties(int type, Dictionary properties) + { + if (this._eventCallback != null) + { + return this._eventCallback.AutoTrackEventCallback(type, properties); + } + else + { + return null; + } + } + } + + // Auto-tracking Events Type + [Obsolete("AUTO_TRACK_EVENTS is deprecated, please use ThinkingData.Analytics.TDAutoTrackEventType instead.")] + public enum AUTO_TRACK_EVENTS + { + NONE = 0, + AppStart = 1 << 0, // reporting when the app enters the foreground (ta_app_start) + AppEnd = 1 << 1, // reporting when the app enters the background (ta_app_end) + AppCrash = 1 << 4, // reporting when an uncaught exception occurs (ta_app_crash) + AppInstall = 1 << 5, // reporting when the app is opened for the first time after installation (ta_app_install) + AppSceneLoad = 1 << 6, // reporting when the scene is loaded in the app (ta_scene_loaded) + AppSceneUnload = 1 << 7, // reporting when the scene is unloaded in the app (ta_scene_loaded) + ALL = AppStart | AppEnd | AppInstall | AppCrash | AppSceneLoad | AppSceneUnload + } + + // Data Reporting Status + [Obsolete("TA_TRACK_STATUS is deprecated, please use ThinkingData.Analytics.TDTrackStatus instead.")] + public enum TA_TRACK_STATUS + { + PAUSE = 1, // pause data reporting + STOP = 2, // stop data reporting, and clear caches + SAVE_ONLY = 3, // data stores in the cache, but not be reported + NORMAL = 4 // resume data reporting + } + + [Obsolete("TAThirdPartyShareType is deprecated, please use ThinkingData.Analytics.Utils.TDThirdPartyType instead.")] + public enum TAThirdPartyShareType + { + 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 + }; + + /// + /// Dynamic super properties interfaces. + /// + [Obsolete("IDynamicSuperProperties is deprecated, please use ThinkingData.Analytics.TDDynamicSuperPropertiesHandler instead.")] + public interface IDynamicSuperProperties + { + /// + /// Dynamically gets event properties + /// + /// event properties + Dictionary GetDynamicSuperProperties(); + } + + /// + /// Auto track event callback interfaces. + /// + [Obsolete("IAutoTrackEventCallback is deprecated, please use ThinkingData.Analytics.TDAutoTrackEventHandler instead.")] + public interface IAutoTrackEventCallback + { + /// + /// Get Auto track event properties + /// + /// auto track event type + /// event properties + /// event properties + Dictionary AutoTrackEventCallback(int type, Dictionary properties); + } + + /// + /// Special event class for internal use, do not use this class directly. + /// + [Obsolete("ThinkingAnalyticsEvent is deprecated, please use ThinkingData.Analytics.TDEventModel instead.")] + 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; } + public Dictionary Properties { get; } + + public DateTime EventTime { get; set; } + public TimeZoneInfo EventTimeZone { get; set; } + public string ExtraId { get; set; } + } + + /// + /// First Events + /// + [Obsolete("TDFirstEvent is deprecated, please use ThinkingData.Analytics.TDFirstEventModel instead.")] + public class TDFirstEvent : ThinkingAnalyticsEvent + { + public TDFirstEvent(string eventName, Dictionary properties) : base(eventName, properties) + { + EventType = Type.FIRST; + } + + // First Event Check ID. By default, first events ID are device ID. + public void SetFirstCheckId(string firstCheckId) + { + ExtraId = firstCheckId; + } + } + + /// + /// Updatable Events + /// + [Obsolete("TDUpdatableEvent is deprecated, please use ThinkingData.Analytics.TDUpdatableEventModel instead.")] + public class TDUpdatableEvent : ThinkingAnalyticsEvent + { + public TDUpdatableEvent(string eventName, Dictionary properties, string eventId) : base(eventName, properties) + { + EventType = Type.UPDATABLE; + ExtraId = eventId; + } + } + + /// + /// Overwritable Events + /// + [Obsolete("TDOverWritableEvent is deprecated, please use ThinkingData.Analytics.TDOverwritableEventModel instead.")] + public class TDOverWritableEvent : ThinkingAnalyticsEvent + { + public TDOverWritableEvent(string eventName, Dictionary properties, string eventId) : base(eventName, properties) + { + EventType = Type.OVERWRITABLE; + ExtraId = eventId; + } + } + + /// + /// Preset Properties + /// + [Obsolete("TDPresetProperties is deprecated, please use ThinkingData.Analytics.TDPresetProperties instead.")] + public class TDPresetProperties + { + /// + /// Construct TDPresetProperties instance + /// + /// preset properties + public TDPresetProperties(Dictionary properties) + { + properties = TDEncodeDate(properties); + mPresetProperties = properties; + } + /// + /// Returns Preset Properties + /// The key starts with "#", it is not recommended to use it directly as a user properties + /// + /// preset properties + public Dictionary ToEventPresetProperties() + { + return mPresetProperties; + } + /// + /// Application Version Number + /// + public string AppVersion + { + get { return (string)(mPresetProperties.ContainsKey("#app_version") ? mPresetProperties["#app_version"] : ""); } + } + /// + /// Application Bundle Identify + /// + public string BundleId + { + get { return (string)(mPresetProperties.ContainsKey("#bundle_id") ? mPresetProperties["#bundle_id"] : ""); } + } + /// + /// Device Network Carrier + /// + public string Carrier + { + get { return (string)(mPresetProperties.ContainsKey("#carrier") ? mPresetProperties["#carrier"] : ""); } + } + /// + /// Device Identify + /// + public string DeviceId + { + get { return (string)(mPresetProperties.ContainsKey("#device_id") ? mPresetProperties["#device_id"] : ""); } + } + /// + /// Device Model Name + /// + public string DeviceModel + { + get { return (string)(mPresetProperties.ContainsKey("#device_model") ? mPresetProperties["#device_model"] : ""); } + } + /// + /// Device Hardware Manufacturer + /// + public string Manufacturer + { + get { return (string)(mPresetProperties.ContainsKey("#manufacturer") ? mPresetProperties["#manufacturer"] : ""); } + } + /// + /// Device Network Type + /// + public string NetworkType + { + get { return (string)(mPresetProperties.ContainsKey("#network_type") ? mPresetProperties["#network_type"] : ""); } + } + /// + /// Device System OS Name + /// + public string OS + { + get { return (string)(mPresetProperties.ContainsKey("#os") ? mPresetProperties["#os"] : ""); } + } + /// + /// Device System OS Version Number + /// + public string OSVersion + { + get { return (string)(mPresetProperties.ContainsKey("#os_version") ? mPresetProperties["#os_version"] : ""); } + } + /// + /// Screen Height + /// + public double ScreenHeight + { + get { return Convert.ToDouble(mPresetProperties.ContainsKey("#screen_height") ? mPresetProperties["#screen_height"] : 0); } + } + /// + /// Screen Width + /// + public double ScreenWidth + { + get { return Convert.ToDouble(mPresetProperties.ContainsKey("#screen_width") ? mPresetProperties["#screen_width"] : 0); } + } + /// + /// Device System Language Code + /// + public string SystemLanguage + { + get { return (string)(mPresetProperties.ContainsKey("#system_language") ? mPresetProperties["#system_language"] : ""); } + } + /// + /// Time Zone Offset With UTC + /// + public double ZoneOffset + { + get { return Convert.ToDouble(mPresetProperties.ContainsKey("#zone_offset") ? mPresetProperties["#zone_offset"] : 0); } + } + /// + /// Application Install Time + /// + public string InstallTime + { + get { return (string)(mPresetProperties.ContainsKey("#install_time") ? mPresetProperties["#install_time"] : ""); } + } + /// + /// Device Disk Size + /// + public string Disk + { + get { return (string)(mPresetProperties.ContainsKey("#disk") ? mPresetProperties["#disk"] : ""); } + } + /// + /// Device Ram Size + /// + public string Ram + { + get { return (string)(mPresetProperties.ContainsKey("#ram") ? mPresetProperties["#ram"] : ""); } + } + /// + /// Device FPS + /// + public double Fps + { + get { return Convert.ToDouble(mPresetProperties.ContainsKey("#fps") ? mPresetProperties["#fps"] : 0); } + } + /// + /// Device is an Simulator + /// + public bool Simulator + { + get { return (bool)(mPresetProperties.ContainsKey("#simulator") ? mPresetProperties["#simulator"] : false); } + } + + private Dictionary mPresetProperties { get; set; } + private Dictionary TDEncodeDate(Dictionary properties) + { + Dictionary mProperties = new Dictionary(); + foreach (KeyValuePair kv in properties) + { + if (kv.Value is DateTime) + { + DateTime dateTime = (DateTime)kv.Value; + mProperties.Add(kv.Key, dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture)); + } + else + { + mProperties.Add(kv.Key, kv.Value); + } + } + return mProperties; + } + } +} diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/ThinkingAnalyticsAPI.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/ThinkingAnalyticsAPI.cs.meta new file mode 100644 index 0000000..b1c95d0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/ThinkingAnalyticsAPI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3281a13f4ff754b019604465fadd63ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils.meta new file mode 100644 index 0000000..31f6fa6 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: efe5e95f8073844fcbaedf8453895210 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDCommonUtils.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDCommonUtils.cs new file mode 100644 index 0000000..41448dd --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDCommonUtils.cs @@ -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); + } + } +} + diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDCommonUtils.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDCommonUtils.cs.meta new file mode 100644 index 0000000..d0a11be --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDCommonUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 56ef1531e6e06454aa9a6700b418632d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDLog.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDLog.cs new file mode 100644 index 0000000..ae46aa7 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDLog.cs @@ -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); + } + } + } +} \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDLog.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDLog.cs.meta new file mode 100644 index 0000000..68e2f99 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDLog.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9b49f9a69526c4417963783322d7e904 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDMiniJson.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDMiniJson.cs new file mode 100644 index 0000000..d54c573 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDMiniJson.cs @@ -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. + */ + /// + /// 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. + /// + public class TDMiniJson + { + /// + /// Parses the string json into a value + /// + /// A JSON string. + /// An List, a Dictionary, a double, an integer, a string, null, true, or false + public static Dictionary 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 Parse(string jsonString) + { + using (var instance = new Parser(jsonString)) + { + return instance.ParseObject(); + } + } + + public void Dispose() + { + json.Dispose(); + json = null; + } + + Dictionary ParseObject() + { + Dictionary table = new Dictionary(); + + // 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 ParseArray() + { + List array = new List(); + + // 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; + } + } + } + + /// + /// Converts a IDictionary / IList object or a simple type (string, int, etc.) into a JSON string + /// + /// A Dictionary / List + /// A JSON encoded string, or null if object 'json' is not serializable + public static string Serialize(object obj, Func func = null) + { + return Serializer.Serialize(obj, func); + } + + sealed class Serializer + { + StringBuilder builder; + Func func; + + Serializer() + { + builder = new StringBuilder(); + } + + public static string Serialize(object obj, Func 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()); + } + } + } + } +} diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDMiniJson.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDMiniJson.cs.meta new file mode 100644 index 0000000..90e907a --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDMiniJson.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03cd5f56606ff4dd694406f5594f0cf2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPropertiesChecker.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPropertiesChecker.cs new file mode 100644 index 0000000..44e7fb0 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPropertiesChecker.cs @@ -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 propertyNameWhitelist = new List() { "#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(Dictionary properties) + { + if (properties == null) + { + return true; + } + bool ret = true; + foreach(KeyValuePair 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)) { + ret = false; + } + if (IsDictionary(kv.Value) && !CheckProperties(kv.Value as Dictionary)) + { + ret = false; + } + } + return ret; + } + public static bool CheckProperties(List 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)) + { + ret = false; + } + } + return ret; + } + public static bool CheckProperties(List 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 source, Dictionary dest) + { + if (null == source) return; + foreach (KeyValuePair kv in source) + { + if (dest.ContainsKey(kv.Key)) + { + dest[kv.Key] = kv.Value; + } else + { + dest.Add(kv.Key, kv.Value); + } + } + } + } +} + diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPropertiesChecker.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPropertiesChecker.cs.meta new file mode 100644 index 0000000..0d18ac9 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPropertiesChecker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d887a818e74eb4316a839030f930036b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPublicConfig.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPublicConfig.cs new file mode 100644 index 0000000..60150fa --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Utils/TDPublicConfig.cs @@ -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 DisPresetProperties = new List(); + + public static readonly string LIB_VERSION = "3.0.7"; + + public static void GetPublicConfig() + { + TextAsset textAsset = Resources.Load("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 light_instances = null; + private static TimeZoneInfo defaultTimeZone = null; + private static TDTimeZone defaultTDTimeZone = TDTimeZone.Local; + + /// + /// Convert Dictionary object to JSONObject in Java. + /// + /// The JSONObject instance. + /// The Dictionary containing some data + private static AndroidJavaObject getJSONObject(Dictionary data) + { + if (data == null) + { + return null; + } + + string dataString = serilize(data); + + try + { + return new AndroidJavaObject(JSON_CLASS, dataString); + } + catch (Exception e) + { + if(TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + return null; + } + + private static string getTimeString(DateTime dateTime) { + //long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + + //DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + //long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + + //AndroidJavaObject date = new AndroidJavaObject("java.util.Date", currentMillis); + + //return getInstance(default_appId).Call("getTimeString", date); + if (defaultTimeZone == null) + { + return TDCommonUtils.FormatDate(dateTime, defaultTDTimeZone); + } + else { + return TDCommonUtils.FormatDate(dateTime, defaultTimeZone); + } + } + + private static AndroidJavaObject getInstance(string appId) { + using (AndroidJavaObject context = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic("currentActivity")) + { + AndroidJavaObject currentInstance; + + if (string.IsNullOrEmpty(appId)) + { + appId = default_appId; + } + + if (light_instances != null && light_instances.ContainsKey(appId)) + { + currentInstance = light_instances[appId]; + } + else + { + currentInstance = sdkClass.CallStatic("sharedInstance", context, appId); + } + + if (currentInstance == null) + { + currentInstance = sdkClass.CallStatic("sharedInstance", context, default_appId); + } + + return currentInstance; + } + } + + + private static void enableLog(bool enable) { + sdkClass.CallStatic("enableTrackLog", enable); + } + private static void setVersionInfo(string libName, string version) { + sdkClass.CallStatic("setCustomerLibInfo", libName, version); + } + + private static void init(TDConfig token) + { + AndroidJavaObject context = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic("currentActivity"); + // AndroidJavaObject config = null; + // if (!string.IsNullOrEmpty(token.GetInstanceName())) + // { + // config = configClass.CallStatic("getInstance", context, token.appid, token.serverUrl, token.GetInstanceName()); + // if (string.IsNullOrEmpty(default_appId)) default_appId = token.GetInstanceName(); + // } + // else + // { + // config = configClass.CallStatic("getInstance", context, token.appid, token.serverUrl); + // if (string.IsNullOrEmpty(default_appId)) default_appId = token.appid; + // } + // config.Call("setModeInt", (int) token.mode); + + // string timeZoneId = token.getTimeZoneId(); + // if (null != timeZoneId && timeZoneId.Length > 0) + // { + // AndroidJavaObject timeZone = new AndroidJavaClass("java.util.TimeZone").CallStatic("getTimeZone", timeZoneId); + // if (null != timeZone) + // { + // config.Call("setDefaultTimeZone", timeZone); + // } + // } + + // if (token.enableEncrypt == true) + // { + // config.Call("enableEncrypt", true); + // AndroidJavaObject secreteKey = new AndroidJavaObject("cn.thinkingdata.android.encrypt.TDSecreteKey", token.encryptPublicKey, token.encryptVersion, "AES", "RSA"); + // config.Call("setSecretKey", secreteKey); + // } + + // sdkClass.CallStatic("sharedInstance", config); + + if (string.IsNullOrEmpty(default_appId)) default_appId = token.appId; + Dictionary configDic = new Dictionary(); + configDic["appId"] = token.appId; + configDic["serverUrl"] = token.serverUrl; + configDic["mode"] = (int) token.mode; + if (!string.IsNullOrEmpty(token.name)) + { + // if (string.IsNullOrEmpty(default_appId)) default_appId = token.GetInstanceName(); + configDic["instanceName"] = token.name; + } + else + { + // if (string.IsNullOrEmpty(default_appId)) default_appId = token.appid; + } + string timeZoneId = token.getTimeZoneId(); + defaultTDTimeZone = token.timeZone; + if (null != timeZoneId && timeZoneId.Length > 0) + { + configDic["timeZone"] = timeZoneId; + if (defaultTimeZone == null) + { + try + { + defaultTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); + } + catch (Exception) + { + } + } + } + else { + if (defaultTimeZone == null) { + defaultTimeZone = TimeZoneInfo.Local; + } + } + if (token.enableEncrypt == true) + { + configDic["enableEncrypt"] = true; + configDic["secretKey"] = new Dictionary() { + {"publicKey", token.encryptPublicKey}, + {"version", token.encryptVersion}, + {"symmetricEncryption", "AES"}, + {"asymmetricEncryption", "RSA"}, + }; + } + unityAPIInstance.Call("sharedInstance", context, TDMiniJson.Serialize(configDic)); + } + + private static void flush(string appId) + { + getInstance(appId).Call("flush"); + } + + private static AndroidJavaObject getDate(DateTime dateTime) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + return new AndroidJavaObject("java.util.Date", currentMillis); + } + + private static void track(string eventName, Dictionary properties, DateTime dateTime, string appId) + { + AndroidJavaObject date = getDate(dateTime); + AndroidJavaClass tzClass = new AndroidJavaClass("java.util.TimeZone"); + AndroidJavaObject tz = null; + AndroidJavaObject javaProperties = getJSONObject(properties); + try + { + getInstance(appId).Call("track", eventName, javaProperties, date, tz); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (date != null) { + date.Dispose(); + } + if (tzClass != null) { + tzClass.Dispose(); + } + if (javaProperties != null) { + javaProperties.Dispose(); + } + } + } + + private static void track(string eventName, Dictionary properties, DateTime dateTime, TimeZoneInfo timeZone, string appId) + { + AndroidJavaObject date = getDate(dateTime); + AndroidJavaClass tzClass = new AndroidJavaClass("java.util.TimeZone"); + AndroidJavaObject tz = null; + AndroidJavaObject javaProperties = getJSONObject(properties); + if (null != timeZone && null != timeZone.Id && timeZone.Id.Length > 0) + { + if ("Local" == timeZone.Id) { + tz = tzClass.CallStatic("getDefault"); + } + else { + tz = tzClass.CallStatic("getTimeZone", timeZone.Id); + } + } + try + { + getInstance(appId).Call("track", eventName, javaProperties, date, tz); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (date != null) { + date.Dispose(); + } + if (tzClass != null) { + tzClass.Dispose(); + } + if (tz != null) { + tz.Dispose(); + } + if (javaProperties != null) { + javaProperties.Dispose(); + } + } + + } + + private static void trackForAll(string eventName, Dictionary properties) + { + string appId = ""; + track(eventName, properties, appId); + } + + private static void track(TDEventModel taEvent, string appId) + { + AndroidJavaObject javaEvent = null; + AndroidJavaObject javaProperties = getJSONObject(getFinalEventProperties(taEvent.Properties)); + switch (taEvent.EventType) + { + case TDEventModel.TDEventType.First: + javaEvent = new AndroidJavaObject("cn.thinkingdata.analytics.TDFirstEvent", + taEvent.EventName, javaProperties); + + string extraId = taEvent.GetEventId(); + if (!string.IsNullOrEmpty(extraId)) + { + javaEvent.Call("setFirstCheckId", extraId); + } + + break; + case TDEventModel.TDEventType.Updatable: + javaEvent = new AndroidJavaObject("cn.thinkingdata.analytics.TDUpdatableEvent", + taEvent.EventName, javaProperties, taEvent.GetEventId()); + break; + case TDEventModel.TDEventType.Overwritable: + javaEvent = new AndroidJavaObject("cn.thinkingdata.analytics.TDOverWritableEvent", + taEvent.EventName, javaProperties, taEvent.GetEventId()); + break; + } + if (null == javaEvent) { + if(TDLog.GetEnable()) TDLog.w("Unexpected java event object. Returning..."); + return; + } + AndroidJavaObject date = getDate(taEvent.GetEventTime()); + AndroidJavaClass tzClass = new AndroidJavaClass("java.util.TimeZone"); + AndroidJavaObject tz = null; + if (taEvent.GetEventTime() != null && taEvent.GetEventTime() != DateTime.MinValue) { + if (taEvent.GetEventTimeZone() != null) { + if ("Local" == taEvent.GetEventTimeZone().Id) { + tz = tzClass.CallStatic("getDefault"); + } + else { + tz = tzClass.CallStatic("getTimeZone", taEvent.GetEventTimeZone().Id); + } + javaEvent.Call("setEventTime", date, tz); + } + else + { + javaEvent.Call("setEventTime", date); + } + + } + try + { + getInstance(appId).Call("track", javaEvent); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (date != null) { + date.Dispose(); + } + if (tzClass != null) { + tzClass.Dispose(); + } + if (tz != null) { + tz.Dispose(); + } + if (javaProperties != null) { + javaProperties.Dispose(); + } + if (javaEvent != null) { + javaEvent.Dispose(); + } + } + + } + + private static void track(string eventName, Dictionary properties, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + try + { + getInstance(appId).Call("track", eventName, javaProperties); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) { + javaProperties.Dispose(); + } + } + } + + private static void setSuperProperties(Dictionary superProperties, string appId) + { + + AndroidJavaObject javaProperties = getJSONObject(superProperties); + try + { + getInstance(appId).Call("setSuperProperties", getJSONObject(superProperties)); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) { + javaProperties.Dispose(); + } + } + } + + private static void unsetSuperProperty(string superPropertyName, string appId) + { + getInstance(appId).Call("unsetSuperProperty", superPropertyName); + } + + private static void clearSuperProperty(string appId) + { + getInstance(appId).Call("clearSuperProperties"); + } + + private static Dictionary getSuperProperties(string appId) + { + Dictionary result = null; + AndroidJavaObject superPropertyObject = getInstance(appId).Call("getSuperProperties"); + try + { + if (null != superPropertyObject) + { + string superPropertiesString = superPropertyObject.Call("toString"); + result = TDMiniJson.Deserialize(superPropertiesString); + } + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (superPropertyObject != null) { + superPropertyObject.Dispose(); + } + } + return result; + } + + private static Dictionary getPresetProperties(string appId) + { + Dictionary result = null; + AndroidJavaObject presetPropertyObject = getInstance(appId).Call("getPresetProperties").Call("toEventPresetProperties"); + try + { + if (null != presetPropertyObject) + { + string presetPropertiesString = presetPropertyObject.Call("toString"); + result = TDMiniJson.Deserialize(presetPropertiesString); + } + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (presetPropertyObject != null) { + presetPropertyObject.Dispose(); + } + } + return result; + } + + private static void timeEvent(string eventName, string appId) + { + getInstance(appId).Call("timeEvent", eventName); + } + + private static void timeEventForAll(string eventName) + { + getInstance("").Call("timeEvent", eventName); + } + + private static void identify(string uniqueId, string appId) + { + getInstance(appId).Call("identify", uniqueId); + } + + private static string getDistinctId(string appId) + { + return getInstance(appId).Call("getDistinctId"); + } + + private static void login(string uniqueId, string appId) + { + getInstance(appId).Call("login", uniqueId); + } + + private static void userSetOnce(Dictionary properties, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + try + { + getInstance(appId).Call("user_setOnce", javaProperties); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) { + javaProperties.Dispose(); + } + } + } + + private static void userSetOnce(Dictionary properties, DateTime dateTime, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + AndroidJavaObject date = getDate(dateTime); + try + { + getInstance(appId).Call("user_setOnce", javaProperties, date); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) + { + javaProperties.Dispose(); + } + if (date != null) { + date.Dispose(); + } + } + } + + private static void userSet(Dictionary properties, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + try + { + getInstance(appId).Call("user_set", javaProperties); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) + { + javaProperties.Dispose(); + } + } + } + + private static void userSet(Dictionary properties, DateTime dateTime, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + AndroidJavaObject date = getDate(dateTime); + try + { + getInstance(appId).Call("user_set", javaProperties, date); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) + { + javaProperties.Dispose(); + } + if (date != null) + { + date.Dispose(); + } + } + } + + private static void userUnset(List properties, string appId) + { + userUnset(properties, DateTime.Now, appId); + } + + private static void userUnset(List properties, DateTime dateTime, string appId) + { + Dictionary finalProperties = new Dictionary(); + foreach(string s in properties) + { + finalProperties.Add(s, 0); + } + AndroidJavaObject javaProperties = getJSONObject(finalProperties); + AndroidJavaObject date = getDate(dateTime); + try + { + getInstance(appId).Call("user_unset", javaProperties, date); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) + { + javaProperties.Dispose(); + } + if (date != null) + { + date.Dispose(); + } + } + } + + private static void userAdd(Dictionary properties, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + try + { + getInstance(appId).Call("user_add", javaProperties); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) + { + javaProperties.Dispose(); + } + } + + } + + private static void userAdd(Dictionary properties, DateTime dateTime, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + AndroidJavaObject date = getDate(dateTime); + try + { + getInstance(appId).Call("user_add", javaProperties, date); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) + { + javaProperties.Dispose(); + } + if (date != null) + { + date.Dispose(); + } + } + } + + private static void userAppend(Dictionary properties, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + try + { + getInstance(appId).Call("user_append", javaProperties); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) + { + javaProperties.Dispose(); + } + } + } + + private static void userAppend(Dictionary properties, DateTime dateTime, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + AndroidJavaObject date = getDate(dateTime); + try + { + getInstance(appId).Call("user_append", javaProperties, date); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) + { + javaProperties.Dispose(); + } + if (date != null) + { + date.Dispose(); + } + } + } + + private static void userUniqAppend(Dictionary properties, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + try + { + getInstance(appId).Call("user_uniqAppend", javaProperties); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) + { + javaProperties.Dispose(); + } + } + } + + private static void userUniqAppend(Dictionary properties, DateTime dateTime, string appId) + { + AndroidJavaObject javaProperties = getJSONObject(properties); + AndroidJavaObject date = getDate(dateTime); + try + { + getInstance(appId).Call("user_uniqAppend", javaProperties, date); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (javaProperties != null) + { + javaProperties.Dispose(); + } + if (date != null) + { + date.Dispose(); + } + } + } + + private static void userDelete(string appId) + { + getInstance(appId).Call("user_delete"); + } + + private static void userDelete(DateTime dateTime, string appId) + { + AndroidJavaObject date = getDate(dateTime); + try + { + getInstance(appId).Call("user_delete", date); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + finally { + if (date != null) + { + date.Dispose(); + } + } + } + + private static void logout(string appId) + { + getInstance(appId).Call("logout"); + } + + private static string getDeviceId() + { + return getInstance(default_appId).Call("getDeviceId"); + } + + private static void setDynamicSuperProperties(TDDynamicSuperPropertiesHandler dynamicSuperProperties, string appId) + { + DynamicListenerAdapter listenerAdapter = new DynamicListenerAdapter(); + if (string.IsNullOrEmpty(appId)) + { + appId = default_appId; + } + unityAPIInstance.Call("setDynamicSuperPropertiesTrackerListener", appId, listenerAdapter); + } + + private static void setNetworkType(TDNetworkType networkType) { + Dictionary properties = new Dictionary() { }; + switch (networkType) + { + case TDNetworkType.Wifi: + properties["network_type"] = 1; + break; + case TDNetworkType.All: + properties["network_type"] = 2; + break; + default: + properties["network_type"] = 2; + break; + + } + unityAPIInstance.Call("setNetworkType", TDMiniJson.Serialize(properties)); + } + + private static void enableAutoTrack(TDAutoTrackEventType events, Dictionary properties, string appId) + { + if (string.IsNullOrEmpty(appId)) + { + appId = default_appId; + } + Dictionary propertiesNew = new Dictionary() { + { "appId", appId}, + { "autoTrackType", (int)events} + }; + unityAPIInstance.Call("enableAutoTrack", TDMiniJson.Serialize(propertiesNew)); + propertiesNew["properties"] = properties; + unityAPIInstance.Call("setAutoTrackProperties", TDMiniJson.Serialize(propertiesNew)); + } + + private static void enableAutoTrack(TDAutoTrackEventType events, TDAutoTrackEventHandler eventCallback, string appId) + { + AutoTrackListenerAdapter listenerAdapter = new AutoTrackListenerAdapter(); + if (string.IsNullOrEmpty(appId)) + { + appId = default_appId; + } + Dictionary properties = new Dictionary() { + { "appId", appId}, + { "autoTrackType", (int)events} + }; + unityAPIInstance.Call("enableAutoTrack", TDMiniJson.Serialize(properties), listenerAdapter); + } + + private static void setAutoTrackProperties(TDAutoTrackEventType events, Dictionary properties, string appId) + { + if (string.IsNullOrEmpty(appId)) + { + appId = default_appId; + } + Dictionary propertiesNew = new Dictionary() { + { "appId", appId}, + { "autoTrackType", (int)events} + }; + propertiesNew["properties"] = properties; + unityAPIInstance.Call("setAutoTrackProperties", TDMiniJson.Serialize(propertiesNew)); + } + + private static void setTrackStatus(TDTrackStatus status, string appId) + { + AndroidJavaClass javaClass = new AndroidJavaClass("cn.thinkingdata.analytics.ThinkingAnalyticsSDK$TATrackStatus"); + AndroidJavaObject trackStatus; + switch (status) + { + case TDTrackStatus.Pause: + trackStatus = javaClass.GetStatic("PAUSE"); + break; + case TDTrackStatus.Stop: + trackStatus = javaClass.GetStatic("STOP"); + break; + case TDTrackStatus.SaveOnly: + trackStatus = javaClass.GetStatic("SAVE_ONLY"); + break; + case TDTrackStatus.Normal: + default: + trackStatus = javaClass.GetStatic("NORMAL"); + break; + } + getInstance(appId).Call("setTrackStatus", trackStatus); + } + + private static void optOutTracking(string appId) + { + getInstance(appId).Call("optOutTracking"); + } + + private static void optOutTrackingAndDeleteUser(string appId) + { + getInstance(appId).Call("optOutTrackingAndDeleteUser"); + } + + private static void optInTracking(string appId) + { + getInstance(appId).Call("optInTracking"); + } + + private static void enableTracking(bool enabled, string appId) + { + getInstance(appId).Call("enableTracking", enabled); + } + + private static string createLightInstance() + { + string randomID = System.Guid.NewGuid().ToString("N"); + AndroidJavaObject lightInstance = getInstance(default_appId).Call("createLightInstance"); + if (light_instances == null) { + light_instances = new Dictionary(); + } + light_instances.Add(randomID, lightInstance); + return randomID; + } + + private static void calibrateTime(long timestamp) + { + sdkClass.CallStatic("calibrateTime", timestamp); + } + + private static void calibrateTimeWithNtp(string ntpServer) + { + unityAPIInstance.Call("calibrateTimeWithNtp", ntpServer); + } + + private static void enableThirdPartySharing(TDThirdPartyType shareType, Dictionary properties, string appId) + { + Dictionary obj = new Dictionary() { + { "appId", appId }, + { "type", (int)shareType } + }; + obj["properties"] = properties; + unityAPIInstance.Call("enableThirdPartySharing", TDMiniJson.Serialize(obj)); + } + + //dynamic super properties + public interface IDynamicSuperPropertiesTrackerListener + { + string getDynamicSuperPropertiesString(); + } + + private class DynamicListenerAdapter : AndroidJavaProxy { + public DynamicListenerAdapter() : base("cn.thinkingdata.engine.ThinkingAnalyticsUnityAPI$DynamicSuperPropertiesTrackerListener") {} + public string getDynamicSuperPropertiesString() + { + Dictionary ret; + if (TDWrapper.mDynamicSuperProperties != null) { + ret = TDWrapper.mDynamicSuperProperties.GetDynamicSuperProperties(); + } + else { + ret = new Dictionary(); + } + //return TDMiniJson.Serialize(ret); + return serilize(ret); + } + } + + //auto-tracking + public interface IAutoTrackEventTrackerListener + { + string eventCallback(int type, string appId, string properties); + } + + private class AutoTrackListenerAdapter : AndroidJavaProxy { + public AutoTrackListenerAdapter() : base("cn.thinkingdata.engine.ThinkingAnalyticsUnityAPI$AutoTrackEventTrackerListener") {} + string eventCallback(int type, string appId, string properties) + { + Dictionary ret; + if (string.IsNullOrEmpty(appId)) appId = default_appId; + if (TDWrapper.mAutoTrackEventCallbacks.ContainsKey(appId)) + { + Dictionary propertiesDic = TDMiniJson.Deserialize(properties); + ret = TDWrapper.mAutoTrackEventCallbacks[appId].GetAutoTrackEventProperties(type, propertiesDic); + } + else { + ret = new Dictionary(); + } + return serilize(ret); + } + } + } +} +#endif \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDAndroidWrapper.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDAndroidWrapper.cs.meta new file mode 100644 index 0000000..2c9773a --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDAndroidWrapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03113e111863a4fd68539598dc2dbb97 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDPCWrapper.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDPCWrapper.cs new file mode 100644 index 0000000..3c2e10d --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDPCWrapper.cs @@ -0,0 +1,415 @@ +#if ((!(UNITY_IOS) || UNITY_EDITOR) && (!(UNITY_ANDROID) || UNITY_EDITOR)) || TE_DISABLE_ANDROID_JAVA || TE_DISABLE_IOS_OC +using System; +using System.Collections.Generic; +using ThinkingData.Analytics.Utils; +using ThinkingSDK.PC.Main; +using ThinkingSDK.PC.Utils; +using ThinkingSDK.PC.DataModel; +using ThinkingSDK.PC.Config; + +namespace ThinkingData.Analytics.Wrapper +{ + public partial class TDWrapper : TDDynamicSuperPropertiesHandler_PC, TDAutoTrackEventHandler_PC + { + static TDAutoTrackEventHandler mEventCallback; + public Dictionary GetDynamicSuperProperties_PC() + { + if (mDynamicSuperProperties != null) + { + return mDynamicSuperProperties.GetDynamicSuperProperties(); + } + else + { + return new Dictionary(); + } + } + + public Dictionary AutoTrackEventCallback_PC(int type, Dictionary properties) + { + if (mEventCallback != null) + { + return mEventCallback.GetAutoTrackEventProperties(type, properties); + } + else + { + return new Dictionary(); + } + } + + private static void init(TDConfig token) + { + ThinkingSDKConfig config = ThinkingSDKConfig.GetInstance(token.appId, token.serverUrl, token.name); + if (!string.IsNullOrEmpty(token.getTimeZoneId())) + { + try + { + config.SetTimeZone(TimeZoneInfo.FindSystemTimeZoneById(token.getTimeZoneId())); + } + catch (Exception) + { + //if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("TimeZoneInfo set failed : " + e.Message); + } + } + if (token.mode == TDMode.Debug) + { + config.SetMode(Mode.DEBUG); + } + else if (token.mode == TDMode.DebugOnly) + { + config.SetMode(Mode.DEBUG_ONLY); + } + ThinkingPCSDK.Init(token.appId, token.serverUrl, token.name, config, sMono); + } + + private static void identify(string uniqueId, string appId) + { + ThinkingPCSDK.Identifiy(uniqueId, appId); + } + + private static string getDistinctId(string appId) + { + return ThinkingPCSDK.DistinctId(appId); + } + + private static void login(string accountId, string appId) + { + ThinkingPCSDK.Login(accountId, appId); + } + + private static void logout(string appId) + { + ThinkingPCSDK.Logout(appId); + } + + private static void flush(string appId) + { + ThinkingPCSDK.Flush(appId); + } + + private static void setVersionInfo(string lib_name, string lib_version) + { + ThinkingPCSDK.SetLibName(lib_name); + ThinkingPCSDK.SetLibVersion(lib_version); + } + + private static void track(TDEventModel taEvent, string appId) + { + ThinkingSDKEventData eventData = null; + switch (taEvent.EventType) + { + case TDEventModel.TDEventType.First: + { + eventData = new ThinkingSDKFirstEvent(taEvent.EventName); + if (!string.IsNullOrEmpty(taEvent.GetEventId())) + { + ((ThinkingSDKFirstEvent)eventData).SetFirstCheckId(taEvent.GetEventId()); + } + } + break; + case TDEventModel.TDEventType.Updatable: + eventData = new ThinkingSDKUpdateEvent(taEvent.EventName, taEvent.GetEventId()); + break; + case TDEventModel.TDEventType.Overwritable: + eventData = new ThinkingSDKOverWritableEvent(taEvent.EventName, taEvent.GetEventId()); + break; + } + if (mDynamicSuperProperties != null) + { + eventData.SetProperties(mDynamicSuperProperties.GetDynamicSuperProperties()); + } + if (taEvent.Properties != null) + { + eventData.SetProperties(taEvent.Properties); + } + if (taEvent.GetEventTime() != null && taEvent.GetEventTimeZone() != null) + { + eventData.SetEventTime(taEvent.GetEventTime()); + eventData.SetTimeZone(taEvent.GetEventTimeZone()); + } + ThinkingPCSDK.Track(eventData, appId); + } + + private static void track(string eventName, Dictionary properties, string appId) + { + ThinkingPCSDK.Track(eventName, properties, appId); + } + + private static void track(string eventName, Dictionary properties, DateTime dateTime, string appId) + { + ThinkingPCSDK.Track(eventName, properties, dateTime, appId); + } + + private static void track(string eventName, Dictionary properties, DateTime dateTime, TimeZoneInfo timeZone, string appId) + { + ThinkingPCSDK.Track(eventName, properties, dateTime, timeZone, appId); + } + + private static void trackForAll(string eventName, Dictionary properties) + { + ThinkingPCSDK.TrackForAll(eventName, properties); + } + + private static void setSuperProperties(Dictionary superProperties, string appId) + { + ThinkingPCSDK.SetSuperProperties(superProperties, appId); + } + + private static void unsetSuperProperty(string superPropertyName, string appId) + { + ThinkingPCSDK.UnsetSuperProperty(superPropertyName, appId); + } + + private static void clearSuperProperty(string appId) + { + ThinkingPCSDK.ClearSuperProperties(appId); + } + + private static Dictionary getSuperProperties(string appId) + { + return ThinkingPCSDK.SuperProperties(appId); + } + + private static Dictionary getPresetProperties(string appId) + { + return ThinkingPCSDK.PresetProperties(appId); + } + private static void timeEvent(string eventName, string appId) + { + ThinkingPCSDK.TimeEvent(eventName, appId); + } + private static void timeEventForAll(string eventName) + { + ThinkingPCSDK.TimeEventForAll(eventName); + } + + private static void userSet(Dictionary properties, string appId) + { + ThinkingPCSDK.UserSet(properties, appId); + } + + private static void userSet(Dictionary properties, DateTime dateTime, string appId) + { + ThinkingPCSDK.UserSet(properties, dateTime, appId); + } + + private static void userUnset(List properties, string appId) + { + ThinkingPCSDK.UserUnset(properties, appId); + } + + private static void userUnset(List properties, DateTime dateTime, string appId) + { + ThinkingPCSDK.UserUnset(properties, dateTime, appId); + } + + private static void userSetOnce(Dictionary properties, string appId) + { + ThinkingPCSDK.UserSetOnce(properties, appId); + } + + private static void userSetOnce(Dictionary properties, DateTime dateTime, string appId) + { + ThinkingPCSDK.UserSetOnce(properties, dateTime, appId); + } + + private static void userAdd(Dictionary properties, string appId) + { + ThinkingPCSDK.UserAdd(properties, appId); + } + + private static void userAdd(Dictionary properties, DateTime dateTime, string appId) + { + ThinkingPCSDK.UserAdd(properties, dateTime, appId); + } + + private static void userDelete(string appId) + { + ThinkingPCSDK.UserDelete(appId); + } + + private static void userDelete(DateTime dateTime, string appId) + { + ThinkingPCSDK.UserDelete(dateTime, appId); + } + + private static void userAppend(Dictionary properties, string appId) + { + ThinkingPCSDK.UserAppend(properties, appId); + } + + private static void userAppend(Dictionary properties, DateTime dateTime, string appId) + { + ThinkingPCSDK.UserAppend(properties, dateTime, appId); + } + + private static void userUniqAppend(Dictionary properties, string appId) + { + ThinkingPCSDK.UserUniqAppend(properties, appId); + } + + private static void userUniqAppend(Dictionary properties, DateTime dateTime, string appId) + { + ThinkingPCSDK.UserUniqAppend(properties, dateTime, appId); + } + + private static void setNetworkType(TDNetworkType networkType) + { + + } + + private static string getDeviceId() + { + return ThinkingPCSDK.GetDeviceId(); + } + + private static void setDynamicSuperProperties(TDDynamicSuperPropertiesHandler dynamicSuperProperties, string appId) + { + ThinkingPCSDK.SetDynamicSuperProperties(new TDWrapper()); + } + + private static void setTrackStatus(TDTrackStatus status, string appId) + { + ThinkingPCSDK.SetTrackStatus((ThinkingSDK.PC.Main.TDTrackStatus)status, appId); + } + + private static void optOutTracking(string appId) + { + ThinkingPCSDK.OptTracking(false, appId); + } + + private static void optOutTrackingAndDeleteUser(string appId) + { + ThinkingPCSDK.OptTrackingAndDeleteUser(appId); + } + + private static void optInTracking(string appId) + { + ThinkingPCSDK.OptTracking(true, appId); + } + + private static void enableTracking(bool enabled, string appId) + { + ThinkingPCSDK.EnableTracking(enabled); + } + + private static string createLightInstance() + { + return ThinkingPCSDK.CreateLightInstance(); + } + + private static string getTimeString(DateTime dateTime) + { + + return ThinkingPCSDK.TimeString(dateTime); + } + + private static void enableAutoTrack(TDAutoTrackEventType autoTrackEvents, Dictionary properties, string appId) + { + ThinkingSDK.PC.Main.TDAutoTrackEventType pcAutoTrackEvents = ThinkingSDK.PC.Main.TDAutoTrackEventType.None; + if ((autoTrackEvents & TDAutoTrackEventType.AppInstall) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppInstall; + } + if ((autoTrackEvents & TDAutoTrackEventType.AppStart) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppStart; + } + if ((autoTrackEvents & TDAutoTrackEventType.AppEnd) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppEnd; + } + if ((autoTrackEvents & TDAutoTrackEventType.AppCrash) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppCrash; + } + if ((autoTrackEvents & TDAutoTrackEventType.AppSceneLoad) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppSceneLoad; + } + if ((autoTrackEvents & TDAutoTrackEventType.AppSceneUnload) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppSceneUnload; + } + ThinkingPCSDK.EnableAutoTrack(pcAutoTrackEvents, properties, appId); + } + + private static void enableAutoTrack(TDAutoTrackEventType autoTrackEvents, TDAutoTrackEventHandler eventCallback, string appId) + { + ThinkingSDK.PC.Main.TDAutoTrackEventType pcAutoTrackEvents = ThinkingSDK.PC.Main.TDAutoTrackEventType.None; + if ((autoTrackEvents & TDAutoTrackEventType.AppInstall) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppInstall; + } + if ((autoTrackEvents & TDAutoTrackEventType.AppStart) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppStart; + } + if ((autoTrackEvents & TDAutoTrackEventType.AppEnd) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppEnd; + } + if ((autoTrackEvents & TDAutoTrackEventType.AppCrash) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppCrash; + } + if ((autoTrackEvents & TDAutoTrackEventType.AppSceneLoad) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppSceneLoad; + } + if ((autoTrackEvents & TDAutoTrackEventType.AppSceneUnload) != 0) + { + pcAutoTrackEvents = pcAutoTrackEvents | ThinkingSDK.PC.Main.TDAutoTrackEventType.AppSceneUnload; + } + mEventCallback = eventCallback; + ThinkingPCSDK.EnableAutoTrack(pcAutoTrackEvents, new TDWrapper(), appId); + } + + private static void setAutoTrackProperties(TDAutoTrackEventType autoTrackEvents, Dictionary properties, string appId) + { + if ((autoTrackEvents & TDAutoTrackEventType.AppInstall) != 0) + { + ThinkingPCSDK.SetAutoTrackProperties(ThinkingSDK.PC.Main.TDAutoTrackEventType.AppInstall, properties, appId); + } + if ((autoTrackEvents & TDAutoTrackEventType.AppStart) != 0) + { + ThinkingPCSDK.SetAutoTrackProperties(ThinkingSDK.PC.Main.TDAutoTrackEventType.AppStart, properties, appId); + } + if ((autoTrackEvents & TDAutoTrackEventType.AppEnd) != 0) + { + ThinkingPCSDK.SetAutoTrackProperties(ThinkingSDK.PC.Main.TDAutoTrackEventType.AppEnd, properties, appId); + } + if ((autoTrackEvents & TDAutoTrackEventType.AppCrash) != 0) + { + ThinkingPCSDK.SetAutoTrackProperties(ThinkingSDK.PC.Main.TDAutoTrackEventType.AppCrash, properties, appId); + } + if ((autoTrackEvents & TDAutoTrackEventType.AppSceneLoad) != 0) + { + ThinkingPCSDK.SetAutoTrackProperties(ThinkingSDK.PC.Main.TDAutoTrackEventType.AppSceneLoad, properties, appId); + } + if ((autoTrackEvents & TDAutoTrackEventType.AppSceneUnload) != 0) + { + ThinkingPCSDK.SetAutoTrackProperties(ThinkingSDK.PC.Main.TDAutoTrackEventType.AppSceneUnload, properties, appId); + } + } + + private static void enableLog(bool enable) + { + ThinkingPCSDK.EnableLog(enable); + } + private static void calibrateTime(long timestamp) + { + ThinkingPCSDK.CalibrateTime(timestamp); + } + + private static void calibrateTimeWithNtp(string ntpServer) + { + ThinkingPCSDK.CalibrateTimeWithNtp(ntpServer); + } + + private static void enableThirdPartySharing(TDThirdPartyType shareType, Dictionary properties, string appId) + { + if (ThinkingSDKPublicConfig.IsPrintLog()) ThinkingSDKLogger.Print("Sharing data is not support on PC: " + shareType + ", " + properties + ", " + appId); + } + } +} +#endif \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDPCWrapper.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDPCWrapper.cs.meta new file mode 100644 index 0000000..9cad8e3 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDPCWrapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8f9ded317afe443d894852ab82fad3ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDWrapper.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDWrapper.cs new file mode 100644 index 0000000..6861d0f --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDWrapper.cs @@ -0,0 +1,472 @@ +using System; +using System.Collections.Generic; +using ThinkingData.Analytics.Utils; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace ThinkingData.Analytics.Wrapper +{ + public partial class TDWrapper + { + public static MonoBehaviour sMono; + private static TDDynamicSuperPropertiesHandler mDynamicSuperProperties; + private static Dictionary mAutoTrackEventCallbacks = new Dictionary(); + private static Dictionary> mAutoTrackProperties = new Dictionary>(); + private static Dictionary mAutoTrackEventInfos = new Dictionary(); + private static System.Random rnd = new System.Random(); + + private static string default_appId = null; + + // add Dictionary to Dictionary + public static void AddDictionary(Dictionary originalDic, Dictionary subDic) + { + if (originalDic != subDic) + { + foreach (KeyValuePair kv in subDic) + { + originalDic[kv.Key] = kv.Value; + } + } + } + + private static string serilize(Dictionary data) { + return TDMiniJson.Serialize(data, getTimeString); + } + + public static void ShareInstance(TDConfig token, MonoBehaviour mono, bool initRequired = true) + { + sMono = mono; + if (string.IsNullOrEmpty(default_appId)) default_appId = token.appId; + if (initRequired) init(token); + } + + public static void EnableLog(bool enable) + { + enableLog(enable); + } + + public static void SetVersionInfo(string version) + { + setVersionInfo("Unity", version); + } + + public static void SetDistinctId(string uniqueId, string appId) + { + identify(uniqueId, appId); + } + + public static string GetDistinctId(string appId) + { + return getDistinctId(appId); + } + + public static void Login(string accountId, string appId) + { + login(accountId, appId); + } + + public static void Logout(string appId) + { + logout(appId); + } + + public static void EnableAutoTrack(TDAutoTrackEventType events, Dictionary properties, string appId) + { + if (string.IsNullOrEmpty(appId)) appId = default_appId; + UpdateAutoTrackSceneInfos(events, appId); + SetAutoTrackProperties(events, properties, appId); + enableAutoTrack(events, properties, appId); + if ((events & TDAutoTrackEventType.AppSceneLoad) != 0) + { + TrackSceneLoad(SceneManager.GetActiveScene(), appId); + } + } + + public static void EnableAutoTrack(TDAutoTrackEventType events, TDAutoTrackEventHandler eventCallback, string appId) + { + if (string.IsNullOrEmpty(appId)) appId = default_appId; + UpdateAutoTrackSceneInfos(events, appId); + mAutoTrackEventCallbacks[appId] = eventCallback; + //mAutoTrackEventCallback = eventCallback; + enableAutoTrack(events, eventCallback, appId); + if ((events & TDAutoTrackEventType.AppSceneLoad) != 0) + { + TrackSceneLoad(SceneManager.GetActiveScene(), appId); + } + } + + private static string TDAutoTrackEventType_APP_SCENE_LOAD = "AppSceneLoad"; + private static string TDAutoTrackEventType_APP_SCENE_UNLOAD = "AppSceneUnload"; + public static void SetAutoTrackProperties(TDAutoTrackEventType events, Dictionary properties, string appId) + { + if ((events & TDAutoTrackEventType.AppSceneLoad) != 0) + { + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_SCENE_LOAD)) + { + AddDictionary(mAutoTrackProperties[TDAutoTrackEventType_APP_SCENE_LOAD], properties); + } + else + mAutoTrackProperties[TDAutoTrackEventType_APP_SCENE_LOAD] = properties; + } + if ((events & TDAutoTrackEventType.AppSceneUnload) != 0) + { + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_SCENE_UNLOAD)) + { + AddDictionary(mAutoTrackProperties[TDAutoTrackEventType_APP_SCENE_UNLOAD], properties); + } + else + mAutoTrackProperties[TDAutoTrackEventType_APP_SCENE_UNLOAD] = properties; + } + setAutoTrackProperties(events, properties, appId); + } + + public static void TrackSceneLoad(Scene scene, string appId = "") + { + Dictionary properties = new Dictionary() { + { "#scene_name", scene.name }, + { "#scene_path", scene.path } + }; + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_SCENE_LOAD)) + { + AddDictionary(properties, mAutoTrackProperties[TDAutoTrackEventType_APP_SCENE_LOAD]); + } + if (string.IsNullOrEmpty(appId)) + { + foreach (var kv in mAutoTrackEventInfos) + { + Dictionary finalProperties = new Dictionary(properties); + if (mAutoTrackEventCallbacks.ContainsKey(kv.Key)) + { + AddDictionary(finalProperties, mAutoTrackEventCallbacks[kv.Key].GetAutoTrackEventProperties((int)TDAutoTrackEventType.AppSceneLoad, properties)); + } + if ((kv.Value & TDAutoTrackEventType.AppSceneLoad) != 0) + { + Track("ta_scene_loaded", finalProperties, kv.Key); + } + if ((kv.Value & TDAutoTrackEventType.AppSceneUnload) != 0) + { + TimeEvent("ta_scene_unloaded", kv.Key); + } + } + } + else + { + Dictionary finalProperties = new Dictionary(properties); + if (mAutoTrackEventCallbacks.ContainsKey(appId)) + { + AddDictionary(finalProperties, mAutoTrackEventCallbacks[appId].GetAutoTrackEventProperties((int)TDAutoTrackEventType.AppSceneLoad, properties)); + } + Track("ta_scene_loaded", finalProperties, appId); + TimeEvent("ta_scene_unloaded", appId); + } + } + + public static void TrackSceneUnload(Scene scene, string appId = "") + { + Dictionary properties = new Dictionary() { + { "#scene_name", scene.name }, + { "#scene_path", scene.path } + }; + if (mAutoTrackProperties.ContainsKey(TDAutoTrackEventType_APP_SCENE_UNLOAD)) + { + AddDictionary(properties, mAutoTrackProperties[TDAutoTrackEventType_APP_SCENE_UNLOAD]); + } + foreach (var kv in mAutoTrackEventInfos) + { + Dictionary finalProperties = new Dictionary(properties); + if (mAutoTrackEventCallbacks.ContainsKey(kv.Key)) + { + AddDictionary(finalProperties, mAutoTrackEventCallbacks[kv.Key].GetAutoTrackEventProperties((int)TDAutoTrackEventType.AppSceneUnload, properties)); + } + if ((kv.Value & TDAutoTrackEventType.AppSceneUnload) != 0) + { + Track("ta_scene_unloaded", finalProperties, kv.Key); + } + } + } + + private static void UpdateAutoTrackSceneInfos(TDAutoTrackEventType events, string appId = "") + { + if (string.IsNullOrEmpty(appId)) appId = default_appId; + mAutoTrackEventInfos[appId] = events; + } + + private static Dictionary getFinalEventProperties(Dictionary properties) + { + TDPropertiesChecker.CheckProperties(properties); + + if (null != mDynamicSuperProperties) + { + Dictionary finalProperties = new Dictionary(); + TDPropertiesChecker.MergeProperties(mDynamicSuperProperties.GetDynamicSuperProperties(), finalProperties); + TDPropertiesChecker.MergeProperties(properties, finalProperties); + return finalProperties; + } + else + { + return properties; + } + + } + public static void Track(string eventName, Dictionary properties, string appId) + { + TDPropertiesChecker.CheckString(eventName); + track(eventName, getFinalEventProperties(properties), appId); + } + + public static void Track(string eventName, Dictionary properties, DateTime datetime, string appId) + { + TDPropertiesChecker.CheckString(eventName); + track(eventName, getFinalEventProperties(properties), datetime, appId); + } + + public static void Track(string eventName, Dictionary properties, DateTime datetime, TimeZoneInfo timeZone, string appId) + { + TDPropertiesChecker.CheckString(eventName); + track(eventName, getFinalEventProperties(properties), datetime, timeZone, appId); + } + + public static void TrackForAll(string eventName, Dictionary properties) + { + TDPropertiesChecker.CheckString(eventName); + trackForAll(eventName, getFinalEventProperties(properties)); + } + + public static void Track(TDEventModel taEvent, string appId) + { + if (null == taEvent || null == taEvent.EventType) + { + if(TDLog.GetEnable()) TDLog.w("Ignoring invalid TA event"); + return; + } + + if (taEvent.GetEventTime() == null) + { + if(TDLog.GetEnable()) TDLog.w("ppp null..."); + } + TDPropertiesChecker.CheckString(taEvent.EventName); + TDPropertiesChecker.CheckProperties(taEvent.Properties); + track(taEvent, appId); + } + + public static void QuickTrack(string eventName, Dictionary properties, string appId) + { + if ("SceneView" == eventName) + { + if (properties == null) + { + properties = new Dictionary() { }; + } + Scene scene = SceneManager.GetActiveScene(); + if (scene != null) + { + properties.Add("#scene_name", scene.name); + properties.Add("#scene_path", scene.path); + } + Track("ta_scene_view", properties, appId); + } + else if ("AppClick" == eventName) + { + if (properties == null) + { + properties = new Dictionary() { }; + } + Track("ta_app_click", properties, appId); + } + } + + public static void SetSuperProperties(Dictionary superProperties, string appId) + { + TDPropertiesChecker.CheckProperties(superProperties); + setSuperProperties(superProperties, appId); + } + + public static void UnsetSuperProperty(string superPropertyName, string appId) + { + TDPropertiesChecker.CheckString(superPropertyName); + unsetSuperProperty(superPropertyName, appId); + } + + public static void ClearSuperProperty(string appId) + { + clearSuperProperty(appId); + } + + + public static void TimeEvent(string eventName, string appId) + { + TDPropertiesChecker.CheckString(eventName); + timeEvent(eventName, appId); + } + + public static void TimeEventForAll(string eventName) + { + TDPropertiesChecker.CheckString(eventName); + timeEventForAll(eventName); + } + + public static Dictionary GetSuperProperties(string appId) + { + return getSuperProperties(appId); + } + + public static Dictionary GetPresetProperties(string appId) + { + return getPresetProperties(appId); + } + + public static void UserSet(Dictionary properties, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userSet(properties, appId); + } + + public static void UserSet(Dictionary properties, DateTime dateTime, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userSet(properties, dateTime, appId); + } + + public static void UserSetOnce(Dictionary properties, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userSetOnce(properties, appId); + } + + public static void UserSetOnce(Dictionary properties, DateTime dateTime, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userSetOnce(properties, dateTime, appId); + } + + public static void UserUnset(List properties, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userUnset(properties, appId); + } + + public static void UserUnset(List properties, DateTime dateTime, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userUnset(properties, dateTime, appId); + } + + public static void UserAdd(Dictionary properties, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userAdd(properties, appId); + } + + public static void UserAdd(Dictionary properties, DateTime dateTime, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userAdd(properties, dateTime, appId); + } + + public static void UserAppend(Dictionary properties, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userAppend(properties, appId); + } + + public static void UserAppend(Dictionary properties, DateTime dateTime, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userAppend(properties, dateTime, appId); + } + + public static void UserUniqAppend(Dictionary properties, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userUniqAppend(properties, appId); + } + + public static void UserUniqAppend(Dictionary properties, DateTime dateTime, string appId) + { + TDPropertiesChecker.CheckProperties(properties); + userUniqAppend(properties, dateTime, appId); + } + + public static void UserDelete(string appId) + { + userDelete(appId); + } + + public static void UserDelete(DateTime dateTime, string appId) + { + userDelete(dateTime, appId); + } + + public static void Flush(string appId) + { + flush(appId); + } + + public static void SetNetworkType(TDNetworkType networkType) + { + setNetworkType(networkType); + } + + public static string GetDeviceId() + { + return getDeviceId(); + } + + public static void SetDynamicSuperProperties(TDDynamicSuperPropertiesHandler dynamicSuperProperties, string appId) + { + if (!TDPropertiesChecker.CheckProperties(dynamicSuperProperties.GetDynamicSuperProperties())) + { + if(TDLog.GetEnable()) TDLog.d("Cannot set dynamic super properties due to invalid properties."); + } + mDynamicSuperProperties = dynamicSuperProperties; + setDynamicSuperProperties(dynamicSuperProperties, appId); + } + + public static void SetTrackStatus(TDTrackStatus status, string appId) + { + setTrackStatus(status, appId); + } + + public static void OptOutTracking(string appId) + { + optOutTracking(appId); + } + + public static void OptOutTrackingAndDeleteUser(string appId) + { + optOutTrackingAndDeleteUser(appId); + } + + public static void OptInTracking(string appId) + { + optInTracking(appId); + } + + public static void EnableTracking(bool enabled, string appId) + { + enableTracking(enabled, appId); + } + + public static string CreateLightInstance() + { + return createLightInstance(); + } + + public static void CalibrateTime(long timestamp) + { + calibrateTime(timestamp); + } + + public static void CalibrateTimeWithNtp(string ntpServer) + { + calibrateTimeWithNtp(ntpServer); + } + + public static void EnableThirdPartySharing(TDThirdPartyType shareType, Dictionary properties = null, string appId = "") + { + if (null == properties) properties = new Dictionary(); + enableThirdPartySharing(shareType, properties, appId); + } + } +} + diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDWrapper.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDWrapper.cs.meta new file mode 100644 index 0000000..3fc816f --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDWrapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 306f69768d6114f989d144fd22df1b13 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs new file mode 100644 index 0000000..f6aa3f5 --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs @@ -0,0 +1,516 @@ +#if UNITY_IOS && !(UNITY_EDITOR) && !TE_DISABLE_IOS_OC +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using ThinkingData.Analytics.Utils; + +namespace ThinkingData.Analytics.Wrapper +{ + public partial class TDWrapper + { + [DllImport("__Internal")] + private static extern void ta_start(string app_id, string url, int mode, string timezone_id, bool enable_encrypt, int encrypt_version, string encrypt_public_key, int pinning_mode, bool allow_invalid_certificates, bool validates_domain_name, string instance_name); + [DllImport("__Internal")] + private static extern void ta_identify(string app_id, string unique_id); + [DllImport("__Internal")] + private static extern string ta_get_distinct_id(string app_id); + [DllImport("__Internal")] + private static extern void ta_login(string app_id, string account_id); + [DllImport("__Internal")] + private static extern void ta_logout(string app_id); + [DllImport("__Internal")] + private static extern void ta_track(string app_id, string event_name, string properties, long time_stamp_millis, string timezone); + [DllImport("__Internal")] + private static extern void ta_track_event(string app_id, string event_string); + [DllImport("__Internal")] + private static extern void ta_set_super_properties(string app_id, string properties); + [DllImport("__Internal")] + private static extern void ta_unset_super_property(string app_id, string property_name); + [DllImport("__Internal")] + private static extern void ta_clear_super_properties(string app_id); + [DllImport("__Internal")] + private static extern string ta_get_super_properties(string app_id); + [DllImport("__Internal")] + private static extern string ta_get_preset_properties(string app_id); + [DllImport("__Internal")] + private static extern void ta_time_event(string app_id, string event_name); + [DllImport("__Internal")] + private static extern void ta_user_set(string app_id, string properties); + [DllImport("__Internal")] + private static extern void ta_user_set_with_time(string app_id, string properties, long timestamp); + [DllImport("__Internal")] + private static extern void ta_user_unset(string app_id, string properties); + [DllImport("__Internal")] + private static extern void ta_user_unset_with_time(string app_id, string properties, long timestamp); + [DllImport("__Internal")] + private static extern void ta_user_set_once(string app_id, string properties); + [DllImport("__Internal")] + private static extern void ta_user_set_once_with_time(string app_id, string properties, long timestamp); + [DllImport("__Internal")] + private static extern void ta_user_add(string app_id, string properties); + [DllImport("__Internal")] + private static extern void ta_user_add_with_time(string app_id, string properties, long timestamp); + [DllImport("__Internal")] + private static extern void ta_user_delete(string app_id); + [DllImport("__Internal")] + private static extern void ta_user_delete_with_time(string app_id, long timestamp); + [DllImport("__Internal")] + private static extern void ta_user_append(string app_id, string properties); + [DllImport("__Internal")] + private static extern void ta_user_append_with_time(string app_id, string properties, long timestamp); + [DllImport("__Internal")] + private static extern void ta_user_uniq_append(string app_id, string properties); + [DllImport("__Internal")] + private static extern void ta_user_uniq_append_with_time(string app_id, string properties, long timestamp); + [DllImport("__Internal")] + private static extern void ta_flush(string app_id); + [DllImport("__Internal")] + private static extern void ta_set_network_type(int type); + [DllImport("__Internal")] + private static extern void ta_enable_log(bool is_enable); + [DllImport("__Internal")] + private static extern string ta_get_device_id(); + [DllImport("__Internal")] + private static extern void ta_set_dynamic_super_properties(string app_id); + [DllImport("__Internal")] + private static extern void ta_enable_tracking(string app_id, bool enabled); + [DllImport("__Internal")] + private static extern void ta_set_track_status(string app_id, int status); + [DllImport("__Internal")] + private static extern void ta_opt_out_tracking(string app_id); + [DllImport("__Internal")] + private static extern void ta_opt_out_tracking_and_delete_user(string app_id); + [DllImport("__Internal")] + private static extern void ta_opt_in_tracking(string app_id); + [DllImport("__Internal")] + private static extern void ta_create_light_instance(string delegate_token); + [DllImport("__Internal")] + private static extern void ta_enable_autoTrack(string app_id, int events, string properties); + [DllImport("__Internal")] + private static extern void ta_enable_autoTrack_with_callback(string app_id, int events); + [DllImport("__Internal")] + private static extern void ta_set_autoTrack_properties(string app_id, int events, string properties); + [DllImport("__Internal")] + private static extern string ta_get_time_string(long timestamp); + [DllImport("__Internal")] + private static extern void ta_calibrate_time(long timestamp); + [DllImport("__Internal")] + private static extern void ta_calibrate_time_with_ntp(string ntpServer); + [DllImport("__Internal")] + private static extern void ta_config_custom_lib_info(string lib_name, string lib_version); + [DllImport("__Internal")] + private static extern void ta_enable_third_party_sharing(string app_id, int share_type, string properties); + + private static TimeZoneInfo defaultTimeZone = null; + private static TDTimeZone defaultTDTimeZone = TDTimeZone.Local; + + private static void init(TDConfig token) + { + registerRecieveGameCallback(); + ta_start(token.appId, token.serverUrl, (int)token.mode, token.getTimeZoneId(), token.enableEncrypt, token.encryptVersion, token.encryptPublicKey, (int) token.pinningMode, token.allowInvalidCertificates, token.validatesDomainName, token.name); + string timeZoneId = token.getTimeZoneId(); + defaultTDTimeZone = token.timeZone; + if (null != timeZoneId && timeZoneId.Length > 0) + { + if (defaultTimeZone == null) + { + try + { + defaultTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); + } + catch (Exception) + { + } + } + } + else + { + if (defaultTimeZone == null) + { + defaultTimeZone = TimeZoneInfo.Local; + } + } + } + + private static void identify(string uniqueId, string appId) + { + ta_identify(appId, uniqueId); + } + + private static string getDistinctId(string appId) + { + return ta_get_distinct_id(appId); + } + + private static void login(string accountId, string appId) + { + ta_login(appId, accountId); + } + + private static void logout(string appId) + { + ta_logout(appId); + } + + private static void flush(string appId) + { + ta_flush(appId); + } + + private static void enableLog(bool enable) + { + ta_enable_log(enable); + } + + private static void setVersionInfo(string lib_name, string lib_version) { + ta_config_custom_lib_info(lib_name, lib_version); + } + + private static void track(TDEventModel taEvent, string appId) + { + Dictionary finalEvent = new Dictionary(); + string extraId = taEvent.GetEventId(); + switch (taEvent.EventType) + { + case TDEventModel.TDEventType.First: + finalEvent["event_type"] = "track_first"; + break; + case TDEventModel.TDEventType.Updatable: + finalEvent["event_type"] = "track_update"; + break; + case TDEventModel.TDEventType.Overwritable: + finalEvent["event_type"] = "track_overwrite"; + break; + } + + if (!string.IsNullOrEmpty(extraId)) + { + finalEvent["extra_id"] = extraId; + } + + finalEvent["event_name"] = taEvent.EventName; + finalEvent["event_properties"] = taEvent.Properties; + if (taEvent.GetEventTime() != null && taEvent.GetEventTime() != DateTime.MinValue) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(taEvent.GetEventTime()).Ticks; + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + finalEvent["event_time"] = currentMillis; + } + if (taEvent.GetEventTimeZone() != null) + { + finalEvent["event_timezone"] = taEvent.GetEventTimeZone().Id; + } + + ta_track_event(appId, serilize(finalEvent)); + } + + private static void track(string eventName, Dictionary properties, string appId) + { + ta_track(appId, eventName, serilize(properties), 0, ""); + } + + private static void track(string eventName, Dictionary properties, DateTime dateTime, string appId) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + string tz = ""; + ta_track(appId, eventName, serilize(properties), currentMillis, tz); + } + + private static void track(string eventName, Dictionary properties, DateTime dateTime, TimeZoneInfo timeZone, string appId) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + string tz = ""; + if (timeZone != null) + { + tz = timeZone.Id; + } + ta_track(appId, eventName, serilize(properties), currentMillis, tz); + } + + private static void trackForAll(string eventName, Dictionary properties) + { + string appId = ""; + track(eventName, properties, appId); + } + + private static void setSuperProperties(Dictionary superProperties, string appId) + { + ta_set_super_properties(appId, serilize(superProperties)); + } + + private static void unsetSuperProperty(string superPropertyName, string appId) + { + ta_unset_super_property(appId, superPropertyName); + } + + private static void clearSuperProperty(string appId) + { + ta_clear_super_properties(appId); + } + + private static Dictionary getSuperProperties(string appId) + { + string superPropertiesString = ta_get_super_properties(appId); + return TDMiniJson.Deserialize(superPropertiesString); + } + + private static Dictionary getPresetProperties(string appId) + { + string presetPropertiesString = ta_get_preset_properties(appId); + return TDMiniJson.Deserialize(presetPropertiesString); + } + + private static void timeEvent(string eventName, string appId) + { + ta_time_event(appId, eventName); + } + + private static void timeEventForAll(string eventName) + { + ta_time_event("", eventName); + } + + private static void userSet(Dictionary properties, string appId) + { + ta_user_set(appId, serilize(properties)); + } + + private static void userSet(Dictionary properties, DateTime dateTime, string appId) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + ta_user_set_with_time(appId, serilize(properties), currentMillis); + } + + private static void userUnset(List properties, string appId) + { + foreach (string property in properties) + { + ta_user_unset(appId, property); + } + } + + private static void userUnset(List properties, DateTime dateTime, string appId) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + foreach (string property in properties) + { + ta_user_unset_with_time(appId, property, currentMillis); + } + } + + private static void userSetOnce(Dictionary properties, string appId) + { + ta_user_set_once(appId, serilize(properties)); + } + + private static void userSetOnce(Dictionary properties, DateTime dateTime, string appId) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + ta_user_set_once_with_time(appId, serilize(properties), currentMillis); + } + + private static void userAdd(Dictionary properties, string appId) + { + ta_user_add(appId, serilize(properties)); + } + + private static void userAdd(Dictionary properties, DateTime dateTime, string appId) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + ta_user_add_with_time(appId, serilize(properties), currentMillis); + } + + private static void userDelete(string appId) + { + ta_user_delete(appId); + } + + private static void userDelete(DateTime dateTime, string appId) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + ta_user_delete_with_time(appId, currentMillis); + } + + private static void userAppend(Dictionary properties, string appId) + { + ta_user_append(appId, serilize(properties)); + } + + private static void userAppend(Dictionary properties, DateTime dateTime, string appId) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + ta_user_append_with_time(appId, serilize(properties), currentMillis); + } + + private static void userUniqAppend(Dictionary properties, string appId) + { + ta_user_uniq_append(appId, serilize(properties)); + } + + private static void userUniqAppend(Dictionary properties, DateTime dateTime, string appId) + { + long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + ta_user_uniq_append_with_time(appId, serilize(properties), currentMillis); + } + + private static void setNetworkType(TDNetworkType networkType) + { + ta_set_network_type((int)networkType); + } + + private static string getDeviceId() + { + return ta_get_device_id(); + } + + private static void setDynamicSuperProperties(TDDynamicSuperPropertiesHandler dynamicSuperProperties, string appId) + { + ta_set_dynamic_super_properties(appId); + } + + private static void setTrackStatus(TDTrackStatus status, string appId) + { + ta_set_track_status(appId, (int)status); + } + + private static void optOutTracking(string appId) + { + ta_opt_out_tracking(appId); + } + + private static void optOutTrackingAndDeleteUser(string appId) + { + ta_opt_out_tracking_and_delete_user(appId); + } + + private static void optInTracking(string appId) + { + ta_opt_in_tracking(appId); + } + + private static void enableTracking(bool enabled, string appId) + { + ta_enable_tracking(appId, enabled); + } + + private static string createLightInstance() + { + string randomID = System.Guid.NewGuid().ToString("N"); + ta_create_light_instance(randomID); + return randomID; + } + + private static string getTimeString(DateTime dateTime) + { + //long dateTimeTicksUTC = TimeZoneInfo.ConvertTimeToUtc(dateTime).Ticks; + //DateTime dtFrom = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + //long currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + //return ta_get_time_string(currentMillis); + if (defaultTimeZone == null) + { + return TDCommonUtils.FormatDate(dateTime, defaultTDTimeZone); + } + else + { + return TDCommonUtils.FormatDate(dateTime, defaultTimeZone); + } + } + + private static void enableAutoTrack(TDAutoTrackEventType autoTrackEvents, Dictionary properties, string appId) + { + ta_enable_autoTrack(appId, (int)autoTrackEvents, serilize(properties)); + } + + private static void enableAutoTrack(TDAutoTrackEventType autoTrackEvents, TDAutoTrackEventHandler eventCallback, string appId) + { + ta_enable_autoTrack_with_callback(appId, (int)autoTrackEvents); + } + + private static void setAutoTrackProperties(TDAutoTrackEventType autoTrackEvents, Dictionary properties, string appId) + { + ta_set_autoTrack_properties(appId, (int)autoTrackEvents, serilize(properties)); + } + + private static void calibrateTime(long timestamp) + { + ta_calibrate_time(timestamp); + } + + private static void calibrateTimeWithNtp(string ntpServer) + { + ta_calibrate_time_with_ntp(ntpServer); + } + + private static void enableThirdPartySharing(TDThirdPartyType shareType, Dictionary properties, string appId) + { + ta_enable_third_party_sharing(appId, (int) shareType, serilize(properties)); + } + + private static void registerRecieveGameCallback() + { + ResultHandler handler = new ResultHandler(resultHandler); + IntPtr handlerPointer = Marshal.GetFunctionPointerForDelegate(handler); + RegisterRecieveGameCallback(handlerPointer); + } + + [DllImport("__Internal")] + public static extern void RegisterRecieveGameCallback + ( + IntPtr handlerPointer + ); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate string ResultHandler(string type, string jsonData); + + [AOT.MonoPInvokeCallback(typeof(ResultHandler))] + static string resultHandler(string type, string jsonData) + { + if (type == "AutoTrackProperties") + { + Dictionaryproperties = TDMiniJson.Deserialize(jsonData); + string appId = properties["AppID"].ToString(); + int eventType = Convert.ToInt32(properties["EventType"]); + if (!string.IsNullOrEmpty(appId) && mAutoTrackEventCallbacks.ContainsKey(appId)) + { + properties.Remove("EventType"); + properties.Remove("AppID"); + DictionaryautoTrackProperties = mAutoTrackEventCallbacks[appId].GetAutoTrackEventProperties(eventType, properties); + //return TDMiniJson.Serialize(autoTrackProperties); + return serilize(autoTrackProperties); + } + } + else if (type == "DynamicSuperProperties") + { + if (mDynamicSuperProperties != null) + { + DictionarydynamicSuperProperties = mDynamicSuperProperties.GetDynamicSuperProperties(); + //return TDMiniJson.Serialize(dynamicSuperProperties); + return serilize(dynamicSuperProperties); + } + } + return "{}"; + } + } +} +#endif \ No newline at end of file diff --git a/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs.meta b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs.meta new file mode 100644 index 0000000..680368f --- /dev/null +++ b/sdk-intergration/Packages/TA/Runtime/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2049ce2b30da48328f05872adacceed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/TA/package.json b/sdk-intergration/Packages/TA/package.json new file mode 100644 index 0000000..14cf74a --- /dev/null +++ b/sdk-intergration/Packages/TA/package.json @@ -0,0 +1,6 @@ +{ + "name": "ta", + "version": "0.0.1", + "displayName": "TA", + "description": "ThinkingData SDK" +} diff --git a/sdk-intergration/Packages/TA/package.json.meta b/sdk-intergration/Packages/TA/package.json.meta new file mode 100644 index 0000000..b645ed3 --- /dev/null +++ b/sdk-intergration/Packages/TA/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e2c66ff523ccdf2478cce083a1a0d993 +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdk-intergration/Packages/packages-lock.json b/sdk-intergration/Packages/packages-lock.json index 96b6bc7..5caa16b 100644 --- a/sdk-intergration/Packages/packages-lock.json +++ b/sdk-intergration/Packages/packages-lock.json @@ -191,6 +191,12 @@ }, "url": "https://packages.unity.cn" }, + "ta": { + "version": "file:TA", + "depth": 0, + "source": "embedded", + "dependencies": {} + }, "tysdk": { "version": "file:tysdk", "depth": 0,