From 8a11506a7824740096c66802dd01623aa400ef27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E5=B3=B0?= Date: Sat, 30 Aug 2025 17:38:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E6=95=B0SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Editor.meta | 8 + Assets/Editor/TDInspectors.cs | 140 ++ Assets/Editor/TDInspectors.cs.meta | 11 + Assets/Editor/TDPostprocessBuild.cs | 124 ++ Assets/Editor/TDPostprocessBuild.cs.meta | 11 + Assets/GoogleMobileAds/Resources.meta | 8 + .../Resources/GoogleMobileAdsSettings.asset | 22 + .../GoogleMobileAdsSettings.asset.meta | 8 + .../Android/FirebaseApp.androidlib.meta | 32 + .../AndroidManifest.xml | 6 + .../FirebaseApp.androidlib/project.properties | 2 + .../res/values/google-services.xml | 9 + .../FirebaseCrashlytics.androidlib.meta | 32 + .../AndroidManifest.xml | 6 + .../project.properties | 2 + .../res/values/crashlytics_build_id.xml | 1 + .../res/values/crashlytics_unity_version.xml | 1 + Assets/Plugins/Android/TDAnalytics.aar | Bin 0 -> 218256 bytes Assets/Plugins/Android/TDAnalytics.aar.meta | 32 + Assets/Plugins/Android/TDCore.aar | Bin 0 -> 73580 bytes Assets/Plugins/Android/TDCore.aar.meta | 32 + .../Android/ThinkingAnalyticsProxy.java | 411 ++++ .../Android/ThinkingAnalyticsProxy.java.meta | 32 + .../Android/ThinkingSDK-thirdparty.aar | Bin 0 -> 23316 bytes .../Android/ThinkingSDK-thirdparty.aar.meta | 32 + .../Plugins/Android/gradleTemplate.properties | 10 + .../Android/gradleTemplate.properties.meta | 7 + Assets/Plugins/Android/mainTemplate.gradle | 98 + .../Plugins/Android/mainTemplate.gradle.meta | 7 + .../Plugins/Android/settingsTemplate.gradle | 42 + .../Android/settingsTemplate.gradle.meta | 7 + Assets/Plugins/OpenHarmony.meta | 8 + Assets/Plugins/OpenHarmony/NativeBridge.tslib | 6 + .../OpenHarmony/NativeBridge.tslib.meta | 7 + Assets/Plugins/OpenHarmony/TDAnalytics.har | Bin 0 -> 21964 bytes .../Plugins/OpenHarmony/TDAnalytics.har.meta | 7 + .../Plugins/OpenHarmony/TDOpenHarmonyProxy.ts | 273 +++ .../OpenHarmony/TDOpenHarmonyProxy.ts.meta | 7 + Assets/Plugins/PC.meta | 8 + Assets/Plugins/PC/AutoTrack.meta | 8 + .../PC/AutoTrack/ThinkingSDKAutoTrack.cs | 213 ++ .../PC/AutoTrack/ThinkingSDKAutoTrack.cs.meta | 11 + Assets/Plugins/PC/Config.meta | 8 + Assets/Plugins/PC/Config/ThinkingSDKConfig.cs | 192 ++ .../PC/Config/ThinkingSDKConfig.cs.meta | 11 + .../PC/Config/ThinkingSDKPublicConfig.cs | 52 + .../PC/Config/ThinkingSDKPublicConfig.cs.meta | 11 + Assets/Plugins/PC/Constant.meta | 8 + .../PC/Constant/ThinkingSDKConstant.cs | 116 ++ .../PC/Constant/ThinkingSDKConstant.cs.meta | 11 + Assets/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 + Assets/Plugins/PC/Main.meta | 8 + .../PC/Main/LightThinkingSDKInstance.cs | 97 + .../PC/Main/LightThinkingSDKInstance.cs.meta | 11 + Assets/Plugins/PC/Main/ThinkingPCSDK.cs | 362 ++++ Assets/Plugins/PC/Main/ThinkingPCSDK.cs.meta | 11 + Assets/Plugins/PC/Main/ThinkingSDKInstance.cs | 786 ++++++++ .../PC/Main/ThinkingSDKInstance.cs.meta | 11 + Assets/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 + Assets/Plugins/PC/Storage.meta | 8 + Assets/Plugins/PC/Storage/ThinkingSDKFile.cs | 80 + .../PC/Storage/ThinkingSDKFile.cs.meta | 11 + .../Plugins/PC/Storage/ThinkingSDKFileJson.cs | 188 ++ .../PC/Storage/ThinkingSDKFileJson.cs.meta | 11 + Assets/Plugins/PC/TaskManager.meta | 8 + .../Plugins/PC/TaskManager/ThinkingSDKTask.cs | 130 ++ .../PC/TaskManager/ThinkingSDKTask.cs.meta | 11 + Assets/Plugins/PC/ThinkingSDK.asmdef | 3 + Assets/Plugins/PC/ThinkingSDK.asmdef.meta | 7 + Assets/Plugins/PC/Time.meta | 8 + Assets/Plugins/PC/Time/TDTimeout.cs | 44 + Assets/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 + Assets/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 + Assets/Plugins/PC/Utils.meta | 8 + Assets/Plugins/PC/Utils/ThinkingSDKAppInfo.cs | 48 + .../PC/Utils/ThinkingSDKAppInfo.cs.meta | 11 + .../Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs | 229 +++ .../PC/Utils/ThinkingSDKDeviceInfo.cs.meta | 11 + Assets/Plugins/PC/Utils/ThinkingSDKJSON.cs | 630 ++++++ .../Plugins/PC/Utils/ThinkingSDKJSON.cs.meta | 11 + Assets/Plugins/PC/Utils/ThinkingSDKLogger.cs | 23 + .../PC/Utils/ThinkingSDKLogger.cs.meta | 11 + .../Plugins/PC/Utils/ThinkingSDKTimeUtil.cs | 15 + .../PC/Utils/ThinkingSDKTimeUtil.cs.meta | 11 + Assets/Plugins/PC/Utils/ThinkingSDKUtil.cs | 199 ++ .../Plugins/PC/Utils/ThinkingSDKUtil.cs.meta | 11 + .../iOS/Firebase/libFirebaseCppCrashlytics.a | Bin 0 -> 885464 bytes .../Firebase/libFirebaseCppCrashlytics.a.meta | 81 + Assets/Plugins/iOS/TAThirdParty.meta | 8 + .../iOS/TAThirdParty/TAAdjustSyncData.h | 16 + .../iOS/TAThirdParty/TAAdjustSyncData.h.meta | 33 + .../iOS/TAThirdParty/TAAdjustSyncData.m | 39 + .../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 | 181 ++ .../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 | 13 + .../iOS/TAThirdParty/TAThirdParty.h.meta | 33 + .../iOS/TAThirdParty/TAThirdPartyManager.h | 20 + .../TAThirdParty/TAThirdPartyManager.h.meta | 33 + .../iOS/TAThirdParty/TAThirdPartyManager.m | 175 ++ .../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 + Assets/Plugins/iOS/ThinkingAnalytics.m | 520 +++++ Assets/Plugins/iOS/ThinkingAnalytics.m.meta | 33 + Assets/Plugins/iOS/ThinkingDataCore.meta | 8 + .../Plugins/iOS/ThinkingDataCore/Classes.meta | 8 + .../Classes/CalibratedTime.meta | 8 + .../Classes/CalibratedTime/TDCalibratedTime.h | 17 + .../CalibratedTime/TDCalibratedTime.h.meta | 33 + .../Classes/CalibratedTime/TDCalibratedTime.m | 87 + .../CalibratedTime/TDCalibratedTime.m.meta | 33 + .../Classes/CalibratedTime/TDNTPServer.h | 28 + .../Classes/CalibratedTime/TDNTPServer.h.meta | 33 + .../Classes/CalibratedTime/TDNTPServer.m | 211 ++ .../Classes/CalibratedTime/TDNTPServer.m.meta | 33 + .../Classes/CalibratedTime/TDNTPTypes.c | 61 + .../Classes/CalibratedTime/TDNTPTypes.c.meta | 33 + .../Classes/CalibratedTime/TDNTPTypes.h | 51 + .../Classes/CalibratedTime/TDNTPTypes.h.meta | 33 + .../ThinkingDataCore/Classes/Category.meta | 8 + .../Classes/Category/NSData.meta | 8 + .../Classes/Category/NSData/NSData+TDGzip.h | 9 + .../Category/NSData/NSData+TDGzip.h.meta | 33 + .../Classes/Category/NSData/NSData+TDGzip.m | 82 + .../Category/NSData/NSData+TDGzip.m.meta | 33 + .../Classes/Category/NSDate.meta | 8 + .../Classes/Category/NSDate/NSDate+TDCore.h | 22 + .../Category/NSDate/NSDate+TDCore.h.meta | 33 + .../Classes/Category/NSDate/NSDate+TDCore.m | 55 + .../Category/NSDate/NSDate+TDCore.m.meta | 33 + .../Classes/Category/NSDictionary.meta | 8 + .../NSDictionary/NSDictionary+TDCore.h | 17 + .../NSDictionary/NSDictionary+TDCore.h.meta | 33 + .../NSDictionary/NSDictionary+TDCore.m | 12 + .../NSDictionary/NSDictionary+TDCore.m.meta | 33 + .../Classes/Category/NSNumber.meta | 8 + .../Category/NSNumber/NSNumber+TDCore.h | 18 + .../Category/NSNumber/NSNumber+TDCore.h.meta | 33 + .../Category/NSNumber/NSNumber+TDCore.m | 20 + .../Category/NSNumber/NSNumber+TDCore.m.meta | 33 + .../Classes/Category/NSObject.meta | 8 + .../Category/NSObject/NSObject+TDCore.h | 20 + .../Category/NSObject/NSObject+TDCore.h.meta | 33 + .../Category/NSObject/NSObject+TDCore.m | 45 + .../Category/NSObject/NSObject+TDCore.m.meta | 33 + .../Classes/Category/NSString.meta | 8 + .../Category/NSString/NSString+TDCore.h | 28 + .../Category/NSString/NSString+TDCore.h.meta | 33 + .../Category/NSString/NSString+TDCore.m | 71 + .../Category/NSString/NSString+TDCore.m.meta | 33 + .../Classes/Category/NSURL.meta | 8 + .../Classes/Category/NSURL/NSURL+TDCore.h | 18 + .../Category/NSURL/NSURL+TDCore.h.meta | 33 + .../Classes/Category/NSURL/NSURL+TDCore.m | 30 + .../Category/NSURL/NSURL+TDCore.m.meta | 33 + .../ThinkingDataCore/Classes/Database.meta | 8 + .../Classes/Database/TDCoreDatabase.h | 21 + .../Classes/Database/TDCoreDatabase.h.meta | 33 + .../Classes/Database/TDCoreDatabase.m | 92 + .../Classes/Database/TDCoreDatabase.m.meta | 33 + .../ThinkingDataCore/Classes/DeviceInfo.meta | 8 + .../Classes/DeviceInfo/TDCoreDeviceInfo.h | 39 + .../DeviceInfo/TDCoreDeviceInfo.h.meta | 33 + .../Classes/DeviceInfo/TDCoreDeviceInfo.m | 417 ++++ .../DeviceInfo/TDCoreDeviceInfo.m.meta | 33 + .../Classes/DeviceInfo/TDCoreFPSMonitor.h | 19 + .../DeviceInfo/TDCoreFPSMonitor.h.meta | 33 + .../Classes/DeviceInfo/TDCoreFPSMonitor.m | 72 + .../DeviceInfo/TDCoreFPSMonitor.m.meta | 33 + .../DeviceInfo/TDCorePresetDisableConfig.h | 56 + .../TDCorePresetDisableConfig.h.meta | 33 + .../DeviceInfo/TDCorePresetDisableConfig.m | 317 +++ .../TDCorePresetDisableConfig.m.meta | 33 + .../Classes/DeviceInfo/TDCorePresetProperty.h | 22 + .../DeviceInfo/TDCorePresetProperty.h.meta | 33 + .../Classes/DeviceInfo/TDCorePresetProperty.m | 145 ++ .../DeviceInfo/TDCorePresetProperty.m.meta | 33 + .../ThinkingDataCore/Classes/Keychain.meta | 8 + .../Classes/Keychain/TDCoreKeychainHelper.h | 20 + .../Keychain/TDCoreKeychainHelper.h.meta | 33 + .../Classes/Keychain/TDCoreKeychainHelper.m | 34 + .../Keychain/TDCoreKeychainHelper.m.meta | 33 + .../Classes/Keychain/TDKeychainManager.h | 21 + .../Classes/Keychain/TDKeychainManager.h.meta | 33 + .../Classes/Keychain/TDKeychainManager.m | 149 ++ .../Classes/Keychain/TDKeychainManager.m.meta | 33 + .../iOS/ThinkingDataCore/Classes/Log.meta | 8 + .../ThinkingDataCore/Classes/Log/TDCoreLog.h | 22 + .../Classes/Log/TDCoreLog.h.meta | 33 + .../ThinkingDataCore/Classes/Log/TDCoreLog.m | 33 + .../Classes/Log/TDCoreLog.m.meta | 33 + .../Classes/Log/TDLogChannelConsole.h | 17 + .../Classes/Log/TDLogChannelConsole.h.meta | 33 + .../Classes/Log/TDLogChannelConsole.m | 62 + .../Classes/Log/TDLogChannelConsole.m.meta | 33 + .../Classes/Log/TDLogChannelProtocol.h | 20 + .../Classes/Log/TDLogChannelProtocol.h.meta | 33 + .../Classes/Log/TDLogConstant.h | 21 + .../Classes/Log/TDLogConstant.h.meta | 33 + .../Classes/Log/TDLogMessage.h | 22 + .../Classes/Log/TDLogMessage.h.meta | 33 + .../Classes/Log/TDLogMessage.m | 21 + .../Classes/Log/TDLogMessage.m.meta | 33 + .../ThinkingDataCore/Classes/Log/TDOSLog.h | 32 + .../Classes/Log/TDOSLog.h.meta | 33 + .../ThinkingDataCore/Classes/Log/TDOSLog.m | 117 ++ .../Classes/Log/TDOSLog.m.meta | 33 + .../iOS/ThinkingDataCore/Classes/Network.meta | 8 + .../Classes/Network/TDNetworkReachability.h | 26 + .../Network/TDNetworkReachability.h.meta | 33 + .../Classes/Network/TDNetworkReachability.m | 180 ++ .../Network/TDNetworkReachability.m.meta | 33 + .../Classes/NotificationManage.meta | 8 + .../TDNotificationManager+Analytics.h | 63 + .../TDNotificationManager+Analytics.h.meta | 33 + .../TDNotificationManager+Analytics.m | 101 + .../TDNotificationManager+Analytics.m.meta | 33 + .../TDNotificationManager+Core.h | 25 + .../TDNotificationManager+Core.h.meta | 33 + .../TDNotificationManager+Core.m | 24 + .../TDNotificationManager+Core.m.meta | 33 + .../TDNotificationManager+Networking.h | 25 + .../TDNotificationManager+Networking.h.meta | 33 + .../TDNotificationManager+Networking.m | 24 + .../TDNotificationManager+Networking.m.meta | 33 + .../TDNotificationManager+RemoteConfig.h | 42 + .../TDNotificationManager+RemoteConfig.h.meta | 33 + .../TDNotificationManager+RemoteConfig.m | 78 + .../TDNotificationManager+RemoteConfig.m.meta | 33 + .../TDNotificationManager.h | 18 + .../TDNotificationManager.h.meta | 33 + .../TDNotificationManager.m | 16 + .../TDNotificationManager.m.meta | 33 + .../iOS/ThinkingDataCore/Classes/Router.meta | 8 + .../Classes/Router/TDMediator+Analytics.h | 48 + .../Router/TDMediator+Analytics.h.meta | 33 + .../Classes/Router/TDMediator+Analytics.m | 118 ++ .../Router/TDMediator+Analytics.m.meta | 33 + .../Classes/Router/TDMediator+RemoteConfig.h | 39 + .../Router/TDMediator+RemoteConfig.h.meta | 33 + .../Classes/Router/TDMediator+RemoteConfig.m | 85 + .../Router/TDMediator+RemoteConfig.m.meta | 33 + .../Classes/Router/TDMediator+Strategy.h | 30 + .../Classes/Router/TDMediator+Strategy.h.meta | 33 + .../Classes/Router/TDMediator+Strategy.m | 33 + .../Classes/Router/TDMediator+Strategy.m.meta | 33 + .../Classes/Router/TDMediator.h | 32 + .../Classes/Router/TDMediator.h.meta | 33 + .../Classes/Router/TDMediator.m | 289 +++ .../Classes/Router/TDMediator.m.meta | 33 + .../iOS/ThinkingDataCore/Classes/TDApp.h | 35 + .../iOS/ThinkingDataCore/Classes/TDApp.h.meta | 33 + .../iOS/ThinkingDataCore/Classes/TDApp.m | 81 + .../iOS/ThinkingDataCore/Classes/TDApp.m.meta | 33 + .../iOS/ThinkingDataCore/Classes/TDCoreInfo.h | 18 + .../Classes/TDCoreInfo.h.meta | 33 + .../iOS/ThinkingDataCore/Classes/TDCoreInfo.m | 16 + .../Classes/TDCoreInfo.m.meta | 33 + .../iOS/ThinkingDataCore/Classes/TDJSONUtil.h | 17 + .../Classes/TDJSONUtil.h.meta | 33 + .../iOS/ThinkingDataCore/Classes/TDJSONUtil.m | 170 ++ .../Classes/TDJSONUtil.m.meta | 33 + .../ThinkingDataCore/Classes/TDRuntime.meta | 8 + .../Classes/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 + .../Classes/TDRuntime/Swizzle/TDSwizzler.h | 13 + .../TDRuntime/Swizzle/TDSwizzler.h.meta | 33 + .../Classes/TDRuntime/Swizzle/TDSwizzler.m | 240 +++ .../TDRuntime/Swizzle/TDSwizzler.m.meta | 33 + .../Classes/TDRuntime/TDClassHelper.h | 16 + .../Classes/TDRuntime/TDClassHelper.h.meta | 33 + .../Classes/TDRuntime/TDClassHelper.m | 36 + .../Classes/TDRuntime/TDClassHelper.m.meta | 33 + .../Classes/TDRuntime/TDCoreWeakProxy.h | 22 + .../Classes/TDRuntime/TDCoreWeakProxy.h.meta | 33 + .../Classes/TDRuntime/TDCoreWeakProxy.m | 78 + .../Classes/TDRuntime/TDCoreWeakProxy.m.meta | 33 + .../Classes/TDRuntime/TDMethodHelper.h | 24 + .../Classes/TDRuntime/TDMethodHelper.h.meta | 33 + .../Classes/TDRuntime/TDMethodHelper.m | 58 + .../Classes/TDRuntime/TDMethodHelper.m.meta | 33 + .../Classes/TDRuntime/TDNewSwizzle.h | 14 + .../Classes/TDRuntime/TDNewSwizzle.h.meta | 33 + .../Classes/TDRuntime/TDNewSwizzle.m | 155 ++ .../Classes/TDRuntime/TDNewSwizzle.m.meta | 33 + .../iOS/ThinkingDataCore/Classes/TDSettings.h | 36 + .../Classes/TDSettings.h.meta | 33 + .../iOS/ThinkingDataCore/Classes/TDSettings.m | 36 + .../Classes/TDSettings.m.meta | 33 + .../Classes/TDSettingsPrivate.h | 19 + .../Classes/TDSettingsPrivate.h.meta | 33 + .../Classes/ThinkingDataCore.h | 190 ++ .../Classes/ThinkingDataCore.h.meta | 33 + Assets/Plugins/iOS/ThinkingSDK.meta | 8 + Assets/Plugins/iOS/ThinkingSDK/Resources.meta | 8 + .../Resources/PrivacyInfo.xcprivacy | 121 ++ .../Resources/PrivacyInfo.xcprivacy.meta | 7 + Assets/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 | 61 + .../AppPush/TDAppDelegateProxyManager.m.meta | 33 + .../AppPush/TDApplicationDelegateProxy.h | 10 + .../AppPush/TDApplicationDelegateProxy.h.meta | 33 + .../AppPush/TDApplicationDelegateProxy.m | 131 ++ .../AppPush/TDApplicationDelegateProxy.m.meta | 33 + .../TDUNUserNotificationCenterDelegateProxy.h | 16 + ...UserNotificationCenterDelegateProxy.h.meta | 33 + .../TDUNUserNotificationCenterDelegateProxy.m | 78 + ...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/TDAppLaunchReason.h | 31 + .../Source/AppLaunch/TDAppLaunchReason.h.meta | 33 + .../Source/AppLaunch/TDAppLaunchReason.m | 181 ++ .../Source/AppLaunch/TDAppLaunchReason.m.meta | 33 + .../iOS/ThinkingSDK/Source/AppLifeCycle.meta | 8 + .../Source/AppLifeCycle/TDAppLifeCycle.h | 47 + .../Source/AppLifeCycle/TDAppLifeCycle.h.meta | 33 + .../Source/AppLifeCycle/TDAppLifeCycle.m | 248 +++ .../Source/AppLifeCycle/TDAppLifeCycle.m.meta | 33 + .../Source/AppLifeCycle/TDAppState.h | 28 + .../Source/AppLifeCycle/TDAppState.h.meta | 33 + .../Source/AppLifeCycle/TDAppState.m | 46 + .../Source/AppLifeCycle/TDAppState.m.meta | 33 + .../iOS/ThinkingSDK/Source/AutoTrack.meta | 8 + .../Source/AutoTrack/AutoPushPlugin.meta | 8 + .../AutoPushPlugin/TDAutoPushPlugin.h | 18 + .../AutoPushPlugin/TDAutoPushPlugin.h.meta | 33 + .../AutoPushPlugin/TDAutoPushPlugin.m | 189 ++ .../AutoPushPlugin/TDAutoPushPlugin.m.meta | 33 + .../ThinkingSDK/Source/AutoTrack/Event.meta | 8 + .../Source/AutoTrack/Event/TDAppEndEvent.h | 17 + .../AutoTrack/Event/TDAppEndEvent.h.meta | 33 + .../Source/AutoTrack/Event/TDAppEndEvent.m | 28 + .../AutoTrack/Event/TDAppEndEvent.m.meta | 33 + .../Source/AutoTrack/Event/TDAppStartEvent.h | 19 + .../AutoTrack/Event/TDAppStartEvent.h.meta | 33 + .../Source/AutoTrack/Event/TDAppStartEvent.m | 35 + .../AutoTrack/Event/TDAppStartEvent.m.meta | 33 + .../Source/AutoTrack/Event/TDAutoClickEvent.h | 22 + .../AutoTrack/Event/TDAutoClickEvent.h.meta | 33 + .../Source/AutoTrack/Event/TDAutoClickEvent.m | 43 + .../AutoTrack/Event/TDAutoClickEvent.m.meta | 33 + .../AutoTrack/Event/TDAutoPageViewEvent.h | 20 + .../Event/TDAutoPageViewEvent.h.meta | 33 + .../AutoTrack/Event/TDAutoPageViewEvent.m | 36 + .../Event/TDAutoPageViewEvent.m.meta | 33 + .../Source/AutoTrack/Event/TDAutoTrackEvent.h | 27 + .../AutoTrack/Event/TDAutoTrackEvent.h.meta | 33 + .../Source/AutoTrack/Event/TDAutoTrackEvent.m | 63 + .../AutoTrack/Event/TDAutoTrackEvent.m.meta | 33 + .../Source/AutoTrack/Event/TDPushClickEvent.h | 16 + .../AutoTrack/Event/TDPushClickEvent.h.meta | 33 + .../Source/AutoTrack/Event/TDPushClickEvent.m | 18 + .../AutoTrack/Event/TDPushClickEvent.m.meta | 33 + .../Source/AutoTrack/TDAutoTrackConst.h | 32 + .../Source/AutoTrack/TDAutoTrackConst.h.meta | 33 + .../Source/AutoTrack/TDAutoTrackManager.h | 37 + .../AutoTrack/TDAutoTrackManager.h.meta | 33 + .../Source/AutoTrack/TDAutoTrackManager.m | 794 ++++++++ .../AutoTrack/TDAutoTrackManager.m.meta | 33 + .../Source/AutoTrack/TDAutoTrackProtocol.h | 80 + .../AutoTrack/TDAutoTrackProtocol.h.meta | 33 + .../AutoTrack/TDAutoTrackPublicHeader.h | 29 + .../AutoTrack/TDAutoTrackPublicHeader.h.meta | 33 + .../ThinkingSDK/Source/AutoTrack/TDRunTime.h | 21 + .../Source/AutoTrack/TDRunTime.h.meta | 33 + .../ThinkingSDK/Source/AutoTrack/TDRunTime.m | 53 + .../Source/AutoTrack/TDRunTime.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 | 77 + .../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 | 127 ++ .../ThinkingSDK/Source/Config/TDConfig.h.meta | 33 + .../iOS/ThinkingSDK/Source/Config/TDConfig.m | 326 +++ .../ThinkingSDK/Source/Config/TDConfig.m.meta | 33 + .../Source/Config/TDConfigPrivate.h | 33 + .../Source/Config/TDConfigPrivate.h.meta | 33 + .../Source/Config/TDPublicConfig.h | 22 + .../Source/Config/TDPublicConfig.h.meta | 33 + .../Source/Config/TDPublicConfig.m | 62 + .../Source/Config/TDPublicConfig.m.meta | 33 + .../iOS/ThinkingSDK/Source/DeviceInfo.meta | 8 + .../DeviceInfo/TDAnalyticsPresetProperty.h | 18 + .../TDAnalyticsPresetProperty.h.meta | 33 + .../DeviceInfo/TDAnalyticsPresetProperty.m | 58 + .../TDAnalyticsPresetProperty.m.meta | 33 + .../Source/DeviceInfo/TDDeviceInfo.h | 17 + .../Source/DeviceInfo/TDDeviceInfo.h.meta | 33 + .../Source/DeviceInfo/TDDeviceInfo.m | 152 ++ .../Source/DeviceInfo/TDDeviceInfo.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 | 29 + .../Source/Encrypt/TDEncryptManager.h.meta | 33 + .../Source/Encrypt/TDEncryptManager.m | 216 ++ .../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/TDBaseEvent+H5.h | 18 + .../EventModel/TDBaseEvent+H5.h.meta | 33 + .../EventTracker/EventModel/TDBaseEvent+H5.m | 29 + .../EventModel/TDBaseEvent+H5.m.meta | 33 + .../EventTracker/EventModel/TDBaseEvent.h | 85 + .../EventModel/TDBaseEvent.h.meta | 33 + .../EventTracker/EventModel/TDBaseEvent.m | 209 ++ .../EventModel/TDBaseEvent.m.meta | 33 + .../EventModel/TDEditableEventModel.h | 12 + .../EventModel/TDEditableEventModel.h.meta | 33 + .../EventTracker/EventModel/TDEventModel.h | 24 + .../EventModel/TDEventModel.h.meta | 33 + .../EventTracker/EventModel/TDEventModel.m | 52 + .../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 + .../EventTracker/EventModel/TDTrackEvent.h | 34 + .../EventModel/TDTrackEvent.h.meta | 33 + .../EventTracker/EventModel/TDTrackEvent.m | 86 + .../EventModel/TDTrackEvent.m.meta | 33 + .../EventModel/TDTrackFirstEvent.h | 18 + .../EventModel/TDTrackFirstEvent.h.meta | 33 + .../EventModel/TDTrackFirstEvent.m | 48 + .../EventModel/TDTrackFirstEvent.m.meta | 33 + .../EventModel/TDTrackOverwriteEvent.h | 18 + .../EventModel/TDTrackOverwriteEvent.h.meta | 33 + .../EventModel/TDTrackOverwriteEvent.m | 40 + .../EventModel/TDTrackOverwriteEvent.m.meta | 33 + .../EventModel/TDTrackUpdateEvent.h | 18 + .../EventModel/TDTrackUpdateEvent.h.meta | 33 + .../EventModel/TDTrackUpdateEvent.m | 40 + .../EventModel/TDTrackUpdateEvent.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/TDUserEvent.h | 16 + .../UserProperty/TDUserEvent.h.meta | 33 + .../EventModel/UserProperty/TDUserEvent.m | 29 + .../UserProperty/TDUserEvent.m.meta | 33 + .../EventModel/UserProperty/TDUserEventAdd.h | 16 + .../UserProperty/TDUserEventAdd.h.meta | 33 + .../EventModel/UserProperty/TDUserEventAdd.m | 36 + .../UserProperty/TDUserEventAdd.m.meta | 33 + .../UserProperty/TDUserEventAppend.h | 16 + .../UserProperty/TDUserEventAppend.h.meta | 33 + .../UserProperty/TDUserEventAppend.m | 32 + .../UserProperty/TDUserEventAppend.m.meta | 33 + .../UserProperty/TDUserEventDelete.h | 16 + .../UserProperty/TDUserEventDelete.h.meta | 33 + .../UserProperty/TDUserEventDelete.m | 19 + .../UserProperty/TDUserEventDelete.m.meta | 33 + .../EventModel/UserProperty/TDUserEventSet.h | 16 + .../UserProperty/TDUserEventSet.h.meta | 33 + .../EventModel/UserProperty/TDUserEventSet.m | 19 + .../UserProperty/TDUserEventSet.m.meta | 33 + .../UserProperty/TDUserEventSetOnce.h | 16 + .../UserProperty/TDUserEventSetOnce.h.meta | 33 + .../UserProperty/TDUserEventSetOnce.m | 19 + .../UserProperty/TDUserEventSetOnce.m.meta | 33 + .../UserProperty/TDUserEventUniqueAppend.h | 16 + .../TDUserEventUniqueAppend.h.meta | 33 + .../UserProperty/TDUserEventUniqueAppend.m | 19 + .../TDUserEventUniqueAppend.m.meta | 33 + .../UserProperty/TDUserEventUnset.h | 16 + .../UserProperty/TDUserEventUnset.h.meta | 33 + .../UserProperty/TDUserEventUnset.m | 19 + .../UserProperty/TDUserEventUnset.m.meta | 33 + .../UserProperty/TDUserPropertyHeader.h | 19 + .../UserProperty/TDUserPropertyHeader.h.meta | 33 + .../Source/EventTracker/Property.meta | 8 + .../Property/PropertyPlugins.meta | 8 + .../PropertyPlugins/TDPresetPropertyPlugin.h | 21 + .../TDPresetPropertyPlugin.h.meta | 33 + .../PropertyPlugins/TDPresetPropertyPlugin.m | 60 + .../TDPresetPropertyPlugin.m.meta | 33 + .../PropertyPlugins/TDPropertyPluginManager.h | 42 + .../TDPropertyPluginManager.h.meta | 33 + .../PropertyPlugins/TDPropertyPluginManager.m | 125 ++ .../TDPropertyPluginManager.m.meta | 33 + .../PropertyPlugins/TDSessionIdManager.h | 19 + .../PropertyPlugins/TDSessionIdManager.h.meta | 33 + .../PropertyPlugins/TDSessionIdManager.m | 75 + .../PropertyPlugins/TDSessionIdManager.m.meta | 33 + .../TDSessionIdPropertyPlugin.h | 23 + .../TDSessionIdPropertyPlugin.h.meta | 33 + .../TDSessionIdPropertyPlugin.m | 75 + .../TDSessionIdPropertyPlugin.m.meta | 33 + .../Property/TDAutoTrackSuperProperty.h | 34 + .../Property/TDAutoTrackSuperProperty.h.meta | 33 + .../Property/TDAutoTrackSuperProperty.m | 121 ++ .../Property/TDAutoTrackSuperProperty.m.meta | 33 + .../EventTracker/Property/TDSuperProperty.h | 35 + .../Property/TDSuperProperty.h.meta | 33 + .../EventTracker/Property/TDSuperProperty.m | 107 + .../Property/TDSuperProperty.m.meta | 33 + .../EventTracker/Property/Validate.meta | 8 + .../Property/Validate/NSArray+TDProperty.h | 17 + .../Validate/NSArray+TDProperty.h.meta | 33 + .../Property/Validate/NSArray+TDProperty.m | 16 + .../Validate/NSArray+TDProperty.m.meta | 33 + .../Property/Validate/NSDate+TDProperty.h | 17 + .../Validate/NSDate+TDProperty.h.meta | 33 + .../Property/Validate/NSDate+TDProperty.m | 16 + .../Validate/NSDate+TDProperty.m.meta | 33 + .../Validate/NSDictionary+TDProperty.h | 17 + .../Validate/NSDictionary+TDProperty.h.meta | 33 + .../Validate/NSDictionary+TDProperty.m | 16 + .../Validate/NSDictionary+TDProperty.m.meta | 33 + .../Property/Validate/NSNumber+TDProperty.h | 17 + .../Validate/NSNumber+TDProperty.h.meta | 33 + .../Property/Validate/NSNumber+TDProperty.m | 20 + .../Validate/NSNumber+TDProperty.m.meta | 33 + .../Property/Validate/NSString+TDProperty.h | 17 + .../Validate/NSString+TDProperty.h.meta | 33 + .../Property/Validate/NSString+TDProperty.m | 36 + .../Validate/NSString+TDProperty.m.meta | 33 + .../Validate/TDPropertyDefaultValidator.h | 17 + .../TDPropertyDefaultValidator.h.meta | 33 + .../Validate/TDPropertyDefaultValidator.m | 17 + .../TDPropertyDefaultValidator.m.meta | 33 + .../Property/Validate/TDPropertyValidator.h | 30 + .../Validate/TDPropertyValidator.h.meta | 33 + .../Property/Validate/TDPropertyValidator.m | 133 ++ .../Validate/TDPropertyValidator.m.meta | 33 + .../Property/Validate/TDValidatorProtocol.h | 45 + .../Validate/TDValidatorProtocol.h.meta | 33 + .../Source/EventTracker/TDEventTracker.h | 47 + .../Source/EventTracker/TDEventTracker.h.meta | 33 + .../Source/EventTracker/TDEventTracker.m | 352 ++++ .../Source/EventTracker/TDEventTracker.m.meta | 33 + .../Source/EventTracker/TrackDuration.meta | 8 + .../EventTracker/TrackDuration/TDTrackTimer.h | 33 + .../TrackDuration/TDTrackTimer.h.meta | 33 + .../EventTracker/TrackDuration/TDTrackTimer.m | 150 ++ .../TrackDuration/TDTrackTimer.m.meta | 33 + .../TrackDuration/TDTrackTimerItem.h | 25 + .../TrackDuration/TDTrackTimerItem.h.meta | 33 + .../TrackDuration/TDTrackTimerItem.m | 17 + .../TrackDuration/TDTrackTimerItem.m.meta | 33 + .../iOS/ThinkingSDK/Source/Exception.meta | 8 + .../Exception/ThinkingExceptionHandler.h | 15 + .../Exception/ThinkingExceptionHandler.h.meta | 33 + .../Exception/ThinkingExceptionHandler.m | 223 +++ .../Exception/ThinkingExceptionHandler.m.meta | 33 + .../Plugins/iOS/ThinkingSDK/Source/Hook.meta | 8 + .../Source/Hook/NSObject+TDDelegateProxy.h | 18 + .../Hook/NSObject+TDDelegateProxy.h.meta | 33 + .../Source/Hook/NSObject+TDDelegateProxy.m | 43 + .../Hook/NSObject+TDDelegateProxy.m.meta | 33 + .../ThinkingSDK/Source/Hook/TDDelegateProxy.h | 25 + .../Source/Hook/TDDelegateProxy.h.meta | 33 + .../ThinkingSDK/Source/Hook/TDDelegateProxy.m | 210 ++ .../Source/Hook/TDDelegateProxy.m.meta | 33 + .../Source/Hook/TDDelegateProxyObject.h | 34 + .../Source/Hook/TDDelegateProxyObject.h.meta | 33 + .../Source/Hook/TDDelegateProxyObject.m | 56 + .../Source/Hook/TDDelegateProxyObject.m.meta | 33 + .../iOS/ThinkingSDK/Source/Logger.meta | 8 + .../iOS/ThinkingSDK/Source/Logger/TDLogging.h | 27 + .../Source/Logger/TDLogging.h.meta | 33 + .../iOS/ThinkingSDK/Source/Logger/TDLogging.m | 54 + .../Source/Logger/TDLogging.m.meta | 33 + .../iOS/ThinkingSDK/Source/Network.meta | 8 + .../Source/Network/TDAnalyticsNetwork.h | 29 + .../Source/Network/TDAnalyticsNetwork.h.meta | 33 + .../Source/Network/TDAnalyticsNetwork.m | 496 +++++ .../Source/Network/TDAnalyticsNetwork.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 + .../ThinkingSDK/Source/PresetProperty.meta | 8 + .../PresetProperty/TDPresetProperties.h | 79 + .../PresetProperty/TDPresetProperties.h.meta | 33 + .../PresetProperty/TDPresetProperties.m | 146 ++ .../PresetProperty/TDPresetProperties.m.meta | 33 + .../Plugins/iOS/ThinkingSDK/Source/Store.meta | 8 + .../ThinkingSDK/Source/Store/TDEventRecord.h | 33 + .../Source/Store/TDEventRecord.h.meta | 33 + .../ThinkingSDK/Source/Store/TDEventRecord.m | 101 + .../Source/Store/TDEventRecord.m.meta | 33 + .../iOS/ThinkingSDK/Source/Store/TDFile.h | 63 + .../ThinkingSDK/Source/Store/TDFile.h.meta | 33 + .../iOS/ThinkingSDK/Source/Store/TDFile.m | 287 +++ .../ThinkingSDK/Source/Store/TDFile.m.meta | 33 + .../Source/Store/TDKeychainHelper.h | 8 + .../Source/Store/TDKeychainHelper.h.meta | 33 + .../Source/Store/TDKeychainHelper.m | 36 + .../Source/Store/TDKeychainHelper.m.meta | 33 + .../Source/Store/TDSqliteDataQueue.h | 19 + .../Source/Store/TDSqliteDataQueue.h.meta | 33 + .../Source/Store/TDSqliteDataQueue.m | 363 ++++ .../Source/Store/TDSqliteDataQueue.m.meta | 33 + .../Plugins/iOS/ThinkingSDK/Source/Toast.meta | 8 + .../ThinkingSDK/Source/Toast/TDToastView.h | 11 + .../Source/Toast/TDToastView.h.meta | 33 + .../ThinkingSDK/Source/Toast/TDToastView.m | 97 + .../Source/Toast/TDToastView.m.meta | 33 + .../Plugins/iOS/ThinkingSDK/Source/Util.meta | 8 + .../iOS/ThinkingSDK/Source/Util/Category.meta | 8 + .../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 | 187 ++ .../Util/Category/NSObject+TDUtil.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/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 + .../Source/Util/Target_Analytics.h | 36 + .../Source/Util/Target_Analytics.h.meta | 33 + .../Source/Util/Target_Analytics.m | 121 ++ .../Source/Util/Target_Analytics.m.meta | 33 + .../Plugins/iOS/ThinkingSDK/Source/main.meta | 8 + .../Source/main/LightThinkingAnalyticsSDK.m | 90 + .../main/LightThinkingAnalyticsSDK.m.meta | 33 + .../Source/main/TDAnalytics+Multiple.h | 284 +++ .../Source/main/TDAnalytics+Multiple.h.meta | 33 + .../Source/main/TDAnalytics+Multiple.m | 256 +++ .../Source/main/TDAnalytics+Multiple.m.meta | 33 + .../Source/main/TDAnalytics+Private.h | 22 + .../Source/main/TDAnalytics+Private.h.meta | 33 + .../Source/main/TDAnalytics+Private.m | 21 + .../Source/main/TDAnalytics+Private.m.meta | 33 + .../Source/main/TDAnalytics+Public.h | 294 +++ .../Source/main/TDAnalytics+Public.h.meta | 33 + .../Source/main/TDAnalytics+Public.m | 328 +++ .../Source/main/TDAnalytics+Public.m.meta | 33 + .../Source/main/TDAnalytics+ThirdParty.h | 30 + .../Source/main/TDAnalytics+ThirdParty.h.meta | 33 + .../Source/main/TDAnalytics+ThirdParty.m | 73 + .../Source/main/TDAnalytics+ThirdParty.m.meta | 33 + .../Source/main/TDAnalytics+WebView.h | 36 + .../Source/main/TDAnalytics+WebView.h.meta | 33 + .../Source/main/TDAnalytics+WebView.m | 169 ++ .../Source/main/TDAnalytics+WebView.m.meta | 33 + .../iOS/ThinkingSDK/Source/main/TDAnalytics.h | 55 + .../Source/main/TDAnalytics.h.meta | 33 + .../iOS/ThinkingSDK/Source/main/TDAnalytics.m | 12 + .../Source/main/TDAnalytics.m.meta | 33 + .../iOS/ThinkingSDK/Source/main/TDConstant.h | 282 +++ .../ThinkingSDK/Source/main/TDConstant.h.meta | 33 + .../main/ThinkingAnalyticsSDK+OldPublic.h | 463 +++++ .../ThinkingAnalyticsSDK+OldPublic.h.meta | 33 + .../main/ThinkingAnalyticsSDK+OldPublic.m | 315 +++ .../ThinkingAnalyticsSDK+OldPublic.m.meta | 33 + .../Source/main/ThinkingAnalyticsSDK.h | 50 + .../Source/main/ThinkingAnalyticsSDK.h.meta | 33 + .../Source/main/ThinkingAnalyticsSDK.m | 1032 ++++++++++ .../Source/main/ThinkingAnalyticsSDK.m.meta | 33 + .../Source/main/ThinkingAnalyticsSDKPrivate.h | 152 ++ .../main/ThinkingAnalyticsSDKPrivate.h.meta | 33 + .../iOS/ThinkingSDK/Source/main/ThinkingSDK.h | 68 + .../Source/main/ThinkingSDK.h.meta | 33 + .../tvOS/Firebase/libFirebaseCppCrashlytics.a | Bin 0 -> 885592 bytes .../Firebase/libFirebaseCppCrashlytics.a.meta | 81 + Assets/Sample.meta | 8 + Assets/Sample/Assets.meta | 8 + Assets/Sample/Assets/button.png | Bin 0 -> 3926 bytes Assets/Sample/Assets/button.png.meta | 123 ++ Assets/Sample/Assets/button_down.png | Bin 0 -> 3889 bytes Assets/Sample/Assets/button_down.png.meta | 123 ++ Assets/Sample/Assets/button_hover.png | Bin 0 -> 3950 bytes Assets/Sample/Assets/button_hover.png.meta | 123 ++ Assets/Sample/Assets/td_logo.png | Bin 0 -> 18883 bytes Assets/Sample/Assets/td_logo.png.meta | 123 ++ Assets/Sample/Assets/td_skin.guiskin | 1428 ++++++++++++++ Assets/Sample/Assets/td_skin.guiskin.meta | 4 + Assets/Sample/OtherScene.cs | 32 + Assets/Sample/OtherScene.cs.meta | 11 + Assets/Sample/OtherScene.unity | 316 +++ Assets/Sample/OtherScene.unity.meta | 7 + Assets/Sample/TAExample.cs | 384 ++++ Assets/Sample/TAExample.cs.meta | 11 + Assets/Sample/TDAnalyticsDemo.cs | 518 +++++ Assets/Sample/TDAnalyticsDemo.cs.meta | 11 + Assets/Sample/TDAnalyticsDemo.unity | 513 +++++ Assets/Sample/TDAnalyticsDemo.unity.meta | 7 + Assets/Scenes/GameScene.unity | 48 +- .../{ => SDKManager}/FirebaseManager.meta | 0 .../FireBaseAnalyticsManager.cs | 0 .../FireBaseAnalyticsManager.cs.meta | 0 .../FireBaseRemoteConfigManager.cs | 26 +- .../FireBaseRemoteConfigManager.cs.meta | 0 .../FirebaseManager/FireBaseSDKManager.cs | 32 + .../FireBaseSDKManager.cs.meta | 11 + Assets/Script/SDKManager/ShuShuMangage.meta | 8 + .../SDKManager/ShuShuMangage/ShuShuEvent.cs | 38 + .../ShuShuMangage/ShuShuEvent.cs.meta | 3 + .../SDKManager/ShuShuMangage/ShuShuMangage.cs | 21 + .../ShuShuMangage/ShuShuMangage.cs.meta | 11 + Assets/StreamingAssets.meta | 8 + .../google-services-desktop.json | 29 + .../google-services-desktop.json.meta | 7 + Assets/ThinkingAnalytics.meta | 8 + Assets/ThinkingAnalytics/Exception.meta | 8 + .../Exception/TDException.cs | 168 ++ .../Exception/TDException.cs.meta | 11 + Assets/ThinkingAnalytics/Resources.meta | 8 + .../Resources/ta_public_config.xml | 43 + .../Resources/ta_public_config.xml.meta | 7 + Assets/ThinkingAnalytics/TDAnalytics.asmdef | 16 + .../ThinkingAnalytics/TDAnalytics.asmdef.meta | 7 + Assets/ThinkingAnalytics/TDAnalytics.cs | 1617 +++++++++++++++ Assets/ThinkingAnalytics/TDAnalytics.cs.meta | 11 + Assets/ThinkingAnalytics/TDAnalytics.prefab | 61 + .../ThinkingAnalytics/TDAnalytics.prefab.meta | 7 + Assets/ThinkingAnalytics/TDEnum.cs | 64 + Assets/ThinkingAnalytics/TDEnum.cs.meta | 11 + Assets/ThinkingAnalytics/TDEvent.cs | 131 ++ Assets/ThinkingAnalytics/TDEvent.cs.meta | 11 + Assets/ThinkingAnalytics/TDInterface.cs | 30 + Assets/ThinkingAnalytics/TDInterface.cs.meta | 11 + .../ThinkingAnalytics/TDPresetProperties.cs | 175 ++ .../TDPresetProperties.cs.meta | 11 + .../ThinkingAnalytics/ThinkingAnalyticsAPI.cs | 1753 +++++++++++++++++ .../ThinkingAnalyticsAPI.cs.meta | 11 + Assets/ThinkingAnalytics/Utils.meta | 8 + .../ThinkingAnalytics/Utils/TDCommonUtils.cs | 86 + .../Utils/TDCommonUtils.cs.meta | 11 + Assets/ThinkingAnalytics/Utils/TDLog.cs | 51 + Assets/ThinkingAnalytics/Utils/TDLog.cs.meta | 11 + Assets/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 + Assets/ThinkingAnalytics/Wrapper.meta | 8 + .../Wrapper/TDAndroidWrapper.cs | 636 ++++++ .../Wrapper/TDAndroidWrapper.cs.meta | 11 + .../Wrapper/TDOpenHarmonyWrapper.cs | 407 ++++ .../Wrapper/TDOpenHarmonyWrapper.cs.meta | 11 + .../ThinkingAnalytics/Wrapper/TDPCWrapper.cs | 492 +++++ .../Wrapper/TDPCWrapper.cs.meta | 11 + Assets/ThinkingAnalytics/Wrapper/TDWrapper.cs | 507 +++++ .../Wrapper/TDWrapper.cs.meta | 11 + .../ThinkingAnalytics/Wrapper/TDiOSWrapper.cs | 551 ++++++ .../Wrapper/TDiOSWrapper.cs.meta | 11 + Assets/google-services.json | 29 + Assets/google-services.json.meta | 7 + .../Editor/CrashlyticsDependencies.xml | 22 + .../Editor/CrashlyticsDependencies.xml.meta | 11 + .../Editor/Firebase.Crashlytics.Editor.dll | Bin 0 -> 25600 bytes .../Firebase.Crashlytics.Editor.dll.meta | 118 ++ .../Editor/Firebase.Crashlytics.Editor.pdb | Bin 0 -> 9916 bytes .../Firebase.Crashlytics.Editor.pdb.meta | 13 + ...aseCrashlytics_version-13.1.0_manifest.txt | 68 + ...ashlytics_version-13.1.0_manifest.txt.meta | 10 + .../Firebase/Plugins/Firebase.Crashlytics.dll | Bin 0 -> 35328 bytes .../Plugins/Firebase.Crashlytics.dll.meta | 81 + .../Firebase/Plugins/Firebase.Crashlytics.pdb | Bin 0 -> 14116 bytes .../Plugins/Firebase.Crashlytics.pdb.meta | 81 + Assets/rd3/Firebase/Plugins/iOS.meta | 2 +- .../Plugins/iOS/Firebase.Crashlytics.dll | Bin 0 -> 32768 bytes .../Plugins/iOS/Firebase.Crashlytics.dll.meta | 81 + .../Plugins/iOS/Firebase.Crashlytics.pdb | Bin 0 -> 13096 bytes .../Plugins/iOS/Firebase.Crashlytics.pdb.meta | 81 + Assets/rd3/Firebase/m2repository/com.meta | 2 +- .../rd3/Firebase/m2repository/com/google.meta | 2 +- .../m2repository/com/google/firebase.meta | 2 +- .../firebase/firebase-crashlytics-unity.meta | 8 + .../firebase-crashlytics-unity/13.1.0.meta | 8 + .../firebase-crashlytics-unity-13.1.0.pom | 13 + ...firebase-crashlytics-unity-13.1.0.pom.meta | 11 + .../firebase-crashlytics-unity-13.1.0.srcaar | Bin 0 -> 186520 bytes ...ebase-crashlytics-unity-13.1.0.srcaar.meta | 11 + .../maven-metadata.xml | 9 + .../maven-metadata.xml.meta | 11 + .../AndroidResolverDependencies.xml | 66 + ProjectSettings/GvhProjectSettings.xml | 1 + ProjectSettings/ProjectSettings.asset | 2 +- 934 files changed, 52273 insertions(+), 39 deletions(-) create mode 100644 Assets/Editor.meta create mode 100644 Assets/Editor/TDInspectors.cs create mode 100644 Assets/Editor/TDInspectors.cs.meta create mode 100644 Assets/Editor/TDPostprocessBuild.cs create mode 100644 Assets/Editor/TDPostprocessBuild.cs.meta create mode 100644 Assets/GoogleMobileAds/Resources.meta create mode 100644 Assets/GoogleMobileAds/Resources/GoogleMobileAdsSettings.asset create mode 100644 Assets/GoogleMobileAds/Resources/GoogleMobileAdsSettings.asset.meta create mode 100644 Assets/Plugins/Android/FirebaseApp.androidlib.meta create mode 100644 Assets/Plugins/Android/FirebaseApp.androidlib/AndroidManifest.xml create mode 100644 Assets/Plugins/Android/FirebaseApp.androidlib/project.properties create mode 100644 Assets/Plugins/Android/FirebaseApp.androidlib/res/values/google-services.xml create mode 100644 Assets/Plugins/Android/FirebaseCrashlytics.androidlib.meta create mode 100644 Assets/Plugins/Android/FirebaseCrashlytics.androidlib/AndroidManifest.xml create mode 100644 Assets/Plugins/Android/FirebaseCrashlytics.androidlib/project.properties create mode 100644 Assets/Plugins/Android/FirebaseCrashlytics.androidlib/res/values/crashlytics_build_id.xml create mode 100644 Assets/Plugins/Android/FirebaseCrashlytics.androidlib/res/values/crashlytics_unity_version.xml create mode 100644 Assets/Plugins/Android/TDAnalytics.aar create mode 100644 Assets/Plugins/Android/TDAnalytics.aar.meta create mode 100644 Assets/Plugins/Android/TDCore.aar create mode 100644 Assets/Plugins/Android/TDCore.aar.meta create mode 100644 Assets/Plugins/Android/ThinkingAnalyticsProxy.java create mode 100644 Assets/Plugins/Android/ThinkingAnalyticsProxy.java.meta create mode 100644 Assets/Plugins/Android/ThinkingSDK-thirdparty.aar create mode 100644 Assets/Plugins/Android/ThinkingSDK-thirdparty.aar.meta create mode 100644 Assets/Plugins/Android/gradleTemplate.properties create mode 100644 Assets/Plugins/Android/gradleTemplate.properties.meta create mode 100644 Assets/Plugins/Android/mainTemplate.gradle create mode 100644 Assets/Plugins/Android/mainTemplate.gradle.meta create mode 100644 Assets/Plugins/Android/settingsTemplate.gradle create mode 100644 Assets/Plugins/Android/settingsTemplate.gradle.meta create mode 100644 Assets/Plugins/OpenHarmony.meta create mode 100644 Assets/Plugins/OpenHarmony/NativeBridge.tslib create mode 100644 Assets/Plugins/OpenHarmony/NativeBridge.tslib.meta create mode 100644 Assets/Plugins/OpenHarmony/TDAnalytics.har create mode 100644 Assets/Plugins/OpenHarmony/TDAnalytics.har.meta create mode 100644 Assets/Plugins/OpenHarmony/TDOpenHarmonyProxy.ts create mode 100644 Assets/Plugins/OpenHarmony/TDOpenHarmonyProxy.ts.meta create mode 100644 Assets/Plugins/PC.meta create mode 100644 Assets/Plugins/PC/AutoTrack.meta create mode 100644 Assets/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs create mode 100644 Assets/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs.meta create mode 100644 Assets/Plugins/PC/Config.meta create mode 100644 Assets/Plugins/PC/Config/ThinkingSDKConfig.cs create mode 100644 Assets/Plugins/PC/Config/ThinkingSDKConfig.cs.meta create mode 100644 Assets/Plugins/PC/Config/ThinkingSDKPublicConfig.cs create mode 100644 Assets/Plugins/PC/Config/ThinkingSDKPublicConfig.cs.meta create mode 100644 Assets/Plugins/PC/Constant.meta create mode 100644 Assets/Plugins/PC/Constant/ThinkingSDKConstant.cs create mode 100644 Assets/Plugins/PC/Constant/ThinkingSDKConstant.cs.meta create mode 100644 Assets/Plugins/PC/DataModel.meta create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKBaseData.cs create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKBaseData.cs.meta create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKEventData.cs create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKEventData.cs.meta create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs.meta create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs.meta create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs.meta create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKUserData.cs create mode 100644 Assets/Plugins/PC/DataModel/ThinkingSDKUserData.cs.meta create mode 100644 Assets/Plugins/PC/Main.meta create mode 100644 Assets/Plugins/PC/Main/LightThinkingSDKInstance.cs create mode 100644 Assets/Plugins/PC/Main/LightThinkingSDKInstance.cs.meta create mode 100644 Assets/Plugins/PC/Main/ThinkingPCSDK.cs create mode 100644 Assets/Plugins/PC/Main/ThinkingPCSDK.cs.meta create mode 100644 Assets/Plugins/PC/Main/ThinkingSDKInstance.cs create mode 100644 Assets/Plugins/PC/Main/ThinkingSDKInstance.cs.meta create mode 100644 Assets/Plugins/PC/Request.meta create mode 100644 Assets/Plugins/PC/Request/ThinkingSDKBaseRequest.cs create mode 100644 Assets/Plugins/PC/Request/ThinkingSDKBaseRequest.cs.meta create mode 100644 Assets/Plugins/PC/Request/ThinkingSDKDebugRequest.cs create mode 100644 Assets/Plugins/PC/Request/ThinkingSDKDebugRequest.cs.meta create mode 100644 Assets/Plugins/PC/Request/ThinkingSDKNormalRequest.cs create mode 100644 Assets/Plugins/PC/Request/ThinkingSDKNormalRequest.cs.meta create mode 100644 Assets/Plugins/PC/Storage.meta create mode 100644 Assets/Plugins/PC/Storage/ThinkingSDKFile.cs create mode 100644 Assets/Plugins/PC/Storage/ThinkingSDKFile.cs.meta create mode 100644 Assets/Plugins/PC/Storage/ThinkingSDKFileJson.cs create mode 100644 Assets/Plugins/PC/Storage/ThinkingSDKFileJson.cs.meta create mode 100644 Assets/Plugins/PC/TaskManager.meta create mode 100644 Assets/Plugins/PC/TaskManager/ThinkingSDKTask.cs create mode 100644 Assets/Plugins/PC/TaskManager/ThinkingSDKTask.cs.meta create mode 100644 Assets/Plugins/PC/ThinkingSDK.asmdef create mode 100644 Assets/Plugins/PC/ThinkingSDK.asmdef.meta create mode 100644 Assets/Plugins/PC/Time.meta create mode 100644 Assets/Plugins/PC/Time/TDTimeout.cs create mode 100644 Assets/Plugins/PC/Time/TDTimeout.cs.meta create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs.meta create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKDefinedTime.cs create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKDefinedTime.cs.meta create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs.meta create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKTime.cs create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKTime.cs.meta create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs.meta create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKTimeInter.cs create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKTimeInter.cs.meta create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs create mode 100644 Assets/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs.meta create mode 100644 Assets/Plugins/PC/Utils.meta create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKAppInfo.cs create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKAppInfo.cs.meta create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs.meta create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKJSON.cs create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKJSON.cs.meta create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKLogger.cs create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKLogger.cs.meta create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs.meta create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKUtil.cs create mode 100644 Assets/Plugins/PC/Utils/ThinkingSDKUtil.cs.meta create mode 100644 Assets/Plugins/iOS/Firebase/libFirebaseCppCrashlytics.a create mode 100644 Assets/Plugins/iOS/Firebase/libFirebaseCppCrashlytics.a.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAdjustSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAdjustSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAThirdParty.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAThirdParty.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.m.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h create mode 100644 Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h.meta create mode 100644 Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m create mode 100644 Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingAnalytics.m create mode 100644 Assets/Plugins/iOS/ThinkingAnalytics.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.c create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.c.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Database.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelProtocol.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelProtocol.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogConstant.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogConstant.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Network.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.m create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettingsPrivate.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettingsPrivate.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/ThinkingDataCore.h create mode 100644 Assets/Plugins/iOS/ThinkingDataCore/Classes/ThinkingDataCore.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Resources.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Resources/PrivacyInfo.xcprivacy create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Resources/PrivacyInfo.xcprivacy.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackConst.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackConst.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackProtocol.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackProtocol.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackPublicHeader.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackPublicHeader.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfigPrivate.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfigPrivate.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserPropertyHeader.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserPropertyHeader.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDValidatorProtocol.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDValidatorProtocol.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Exception.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Logger.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Network.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Toast.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDConstant.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/TDConstant.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h.meta create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h create mode 100644 Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h.meta create mode 100644 Assets/Plugins/tvOS/Firebase/libFirebaseCppCrashlytics.a create mode 100644 Assets/Plugins/tvOS/Firebase/libFirebaseCppCrashlytics.a.meta create mode 100644 Assets/Sample.meta create mode 100644 Assets/Sample/Assets.meta create mode 100644 Assets/Sample/Assets/button.png create mode 100644 Assets/Sample/Assets/button.png.meta create mode 100644 Assets/Sample/Assets/button_down.png create mode 100644 Assets/Sample/Assets/button_down.png.meta create mode 100644 Assets/Sample/Assets/button_hover.png create mode 100644 Assets/Sample/Assets/button_hover.png.meta create mode 100644 Assets/Sample/Assets/td_logo.png create mode 100644 Assets/Sample/Assets/td_logo.png.meta create mode 100644 Assets/Sample/Assets/td_skin.guiskin create mode 100644 Assets/Sample/Assets/td_skin.guiskin.meta create mode 100644 Assets/Sample/OtherScene.cs create mode 100644 Assets/Sample/OtherScene.cs.meta create mode 100644 Assets/Sample/OtherScene.unity create mode 100644 Assets/Sample/OtherScene.unity.meta create mode 100644 Assets/Sample/TAExample.cs create mode 100644 Assets/Sample/TAExample.cs.meta create mode 100644 Assets/Sample/TDAnalyticsDemo.cs create mode 100644 Assets/Sample/TDAnalyticsDemo.cs.meta create mode 100644 Assets/Sample/TDAnalyticsDemo.unity create mode 100644 Assets/Sample/TDAnalyticsDemo.unity.meta rename Assets/Script/{ => SDKManager}/FirebaseManager.meta (100%) rename Assets/Script/{ => SDKManager}/FirebaseManager/FireBaseAnalyticsManager.cs (100%) rename Assets/Script/{ => SDKManager}/FirebaseManager/FireBaseAnalyticsManager.cs.meta (100%) rename Assets/Script/{ => SDKManager}/FirebaseManager/FireBaseRemoteConfigManager.cs (86%) rename Assets/Script/{ => SDKManager}/FirebaseManager/FireBaseRemoteConfigManager.cs.meta (100%) create mode 100644 Assets/Script/SDKManager/FirebaseManager/FireBaseSDKManager.cs create mode 100644 Assets/Script/SDKManager/FirebaseManager/FireBaseSDKManager.cs.meta create mode 100644 Assets/Script/SDKManager/ShuShuMangage.meta create mode 100644 Assets/Script/SDKManager/ShuShuMangage/ShuShuEvent.cs create mode 100644 Assets/Script/SDKManager/ShuShuMangage/ShuShuEvent.cs.meta create mode 100644 Assets/Script/SDKManager/ShuShuMangage/ShuShuMangage.cs create mode 100644 Assets/Script/SDKManager/ShuShuMangage/ShuShuMangage.cs.meta create mode 100644 Assets/StreamingAssets.meta create mode 100644 Assets/StreamingAssets/google-services-desktop.json create mode 100644 Assets/StreamingAssets/google-services-desktop.json.meta create mode 100644 Assets/ThinkingAnalytics.meta create mode 100644 Assets/ThinkingAnalytics/Exception.meta create mode 100644 Assets/ThinkingAnalytics/Exception/TDException.cs create mode 100644 Assets/ThinkingAnalytics/Exception/TDException.cs.meta create mode 100644 Assets/ThinkingAnalytics/Resources.meta create mode 100644 Assets/ThinkingAnalytics/Resources/ta_public_config.xml create mode 100644 Assets/ThinkingAnalytics/Resources/ta_public_config.xml.meta create mode 100644 Assets/ThinkingAnalytics/TDAnalytics.asmdef create mode 100644 Assets/ThinkingAnalytics/TDAnalytics.asmdef.meta create mode 100644 Assets/ThinkingAnalytics/TDAnalytics.cs create mode 100644 Assets/ThinkingAnalytics/TDAnalytics.cs.meta create mode 100644 Assets/ThinkingAnalytics/TDAnalytics.prefab create mode 100644 Assets/ThinkingAnalytics/TDAnalytics.prefab.meta create mode 100644 Assets/ThinkingAnalytics/TDEnum.cs create mode 100644 Assets/ThinkingAnalytics/TDEnum.cs.meta create mode 100644 Assets/ThinkingAnalytics/TDEvent.cs create mode 100644 Assets/ThinkingAnalytics/TDEvent.cs.meta create mode 100644 Assets/ThinkingAnalytics/TDInterface.cs create mode 100644 Assets/ThinkingAnalytics/TDInterface.cs.meta create mode 100644 Assets/ThinkingAnalytics/TDPresetProperties.cs create mode 100644 Assets/ThinkingAnalytics/TDPresetProperties.cs.meta create mode 100644 Assets/ThinkingAnalytics/ThinkingAnalyticsAPI.cs create mode 100644 Assets/ThinkingAnalytics/ThinkingAnalyticsAPI.cs.meta create mode 100644 Assets/ThinkingAnalytics/Utils.meta create mode 100644 Assets/ThinkingAnalytics/Utils/TDCommonUtils.cs create mode 100644 Assets/ThinkingAnalytics/Utils/TDCommonUtils.cs.meta create mode 100644 Assets/ThinkingAnalytics/Utils/TDLog.cs create mode 100644 Assets/ThinkingAnalytics/Utils/TDLog.cs.meta create mode 100644 Assets/ThinkingAnalytics/Utils/TDMiniJson.cs create mode 100644 Assets/ThinkingAnalytics/Utils/TDMiniJson.cs.meta create mode 100644 Assets/ThinkingAnalytics/Utils/TDPropertiesChecker.cs create mode 100644 Assets/ThinkingAnalytics/Utils/TDPropertiesChecker.cs.meta create mode 100644 Assets/ThinkingAnalytics/Utils/TDPublicConfig.cs create mode 100644 Assets/ThinkingAnalytics/Utils/TDPublicConfig.cs.meta create mode 100644 Assets/ThinkingAnalytics/Wrapper.meta create mode 100644 Assets/ThinkingAnalytics/Wrapper/TDAndroidWrapper.cs create mode 100644 Assets/ThinkingAnalytics/Wrapper/TDAndroidWrapper.cs.meta create mode 100644 Assets/ThinkingAnalytics/Wrapper/TDOpenHarmonyWrapper.cs create mode 100644 Assets/ThinkingAnalytics/Wrapper/TDOpenHarmonyWrapper.cs.meta create mode 100644 Assets/ThinkingAnalytics/Wrapper/TDPCWrapper.cs create mode 100644 Assets/ThinkingAnalytics/Wrapper/TDPCWrapper.cs.meta create mode 100644 Assets/ThinkingAnalytics/Wrapper/TDWrapper.cs create mode 100644 Assets/ThinkingAnalytics/Wrapper/TDWrapper.cs.meta create mode 100644 Assets/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs create mode 100644 Assets/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs.meta create mode 100644 Assets/google-services.json create mode 100644 Assets/google-services.json.meta create mode 100644 Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml create mode 100644 Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml.meta create mode 100644 Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.dll create mode 100644 Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.dll.meta create mode 100644 Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.pdb create mode 100644 Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.pdb.meta create mode 100644 Assets/rd3/Firebase/Editor/FirebaseCrashlytics_version-13.1.0_manifest.txt create mode 100644 Assets/rd3/Firebase/Editor/FirebaseCrashlytics_version-13.1.0_manifest.txt.meta create mode 100644 Assets/rd3/Firebase/Plugins/Firebase.Crashlytics.dll create mode 100644 Assets/rd3/Firebase/Plugins/Firebase.Crashlytics.dll.meta create mode 100644 Assets/rd3/Firebase/Plugins/Firebase.Crashlytics.pdb create mode 100644 Assets/rd3/Firebase/Plugins/Firebase.Crashlytics.pdb.meta create mode 100644 Assets/rd3/Firebase/Plugins/iOS/Firebase.Crashlytics.dll create mode 100644 Assets/rd3/Firebase/Plugins/iOS/Firebase.Crashlytics.dll.meta create mode 100644 Assets/rd3/Firebase/Plugins/iOS/Firebase.Crashlytics.pdb create mode 100644 Assets/rd3/Firebase/Plugins/iOS/Firebase.Crashlytics.pdb.meta create mode 100644 Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity.meta create mode 100644 Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0.meta create mode 100644 Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.pom create mode 100644 Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.pom.meta create mode 100644 Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.srcaar create mode 100644 Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.srcaar.meta create mode 100644 Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/maven-metadata.xml create mode 100644 Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/maven-metadata.xml.meta create mode 100644 ProjectSettings/AndroidResolverDependencies.xml diff --git a/Assets/Editor.meta b/Assets/Editor.meta new file mode 100644 index 0000000..9fa18b1 --- /dev/null +++ b/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1c3596aacf52e4dce91260e35b40c684 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/TDInspectors.cs b/Assets/Editor/TDInspectors.cs new file mode 100644 index 0000000..2f69826 --- /dev/null +++ b/Assets/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/Assets/Editor/TDInspectors.cs.meta b/Assets/Editor/TDInspectors.cs.meta new file mode 100644 index 0000000..d24fd67 --- /dev/null +++ b/Assets/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/Assets/Editor/TDPostprocessBuild.cs b/Assets/Editor/TDPostprocessBuild.cs new file mode 100644 index 0000000..ae5271d --- /dev/null +++ b/Assets/Editor/TDPostprocessBuild.cs @@ -0,0 +1,124 @@ +#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_OPENHARMONY +using System.IO; +using UnityEditor; +using UnityEditor.Callbacks; + +namespace ThinkingData.Analytics.Editors +{ + public class TD_PostProcessBuild + { + [PostProcessBuildAttribute(88)] + public static void OnPostProcessBuild(BuildTarget target, string targetPath) + { + string path = Path.Combine(targetPath, "entry/oh-package.json5"); + string jsonContent = File.ReadAllText(path); + jsonContent = jsonContent.Replace("\"TDAnalytics\"", "\"@thinkingdata/analytics\""); + File.WriteAllText(path, jsonContent); + } + } +} +#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/Assets/Editor/TDPostprocessBuild.cs.meta b/Assets/Editor/TDPostprocessBuild.cs.meta new file mode 100644 index 0000000..340f92a --- /dev/null +++ b/Assets/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/Assets/GoogleMobileAds/Resources.meta b/Assets/GoogleMobileAds/Resources.meta new file mode 100644 index 0000000..62ebf0d --- /dev/null +++ b/Assets/GoogleMobileAds/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6e99afc9a5a55e343b5eb579656b88d4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GoogleMobileAds/Resources/GoogleMobileAdsSettings.asset b/Assets/GoogleMobileAds/Resources/GoogleMobileAdsSettings.asset new file mode 100644 index 0000000..f04c964 --- /dev/null +++ b/Assets/GoogleMobileAds/Resources/GoogleMobileAdsSettings.asset @@ -0,0 +1,22 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a187246822bbb47529482707f3e0eff8, type: 3} + m_Name: GoogleMobileAdsSettings + m_EditorClassIdentifier: + adMobAndroidAppId: + adMobIOSAppId: + enableKotlinXCoroutinesPackagingOption: 1 + enableGradleBuildPreProcessor: 1 + disableOptimizeInitialization: 0 + disableOptimizeAdLoading: 0 + userTrackingUsageDescription: + userLanguage: en diff --git a/Assets/GoogleMobileAds/Resources/GoogleMobileAdsSettings.asset.meta b/Assets/GoogleMobileAds/Resources/GoogleMobileAdsSettings.asset.meta new file mode 100644 index 0000000..4919efd --- /dev/null +++ b/Assets/GoogleMobileAds/Resources/GoogleMobileAdsSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7496f258105db1c44adb5eb28724b41f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Android/FirebaseApp.androidlib.meta b/Assets/Plugins/Android/FirebaseApp.androidlib.meta new file mode 100644 index 0000000..82b7b01 --- /dev/null +++ b/Assets/Plugins/Android/FirebaseApp.androidlib.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: ac5dc49959b8d094ba069fb641fc914d +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/Assets/Plugins/Android/FirebaseApp.androidlib/AndroidManifest.xml b/Assets/Plugins/Android/FirebaseApp.androidlib/AndroidManifest.xml new file mode 100644 index 0000000..fa692b9 --- /dev/null +++ b/Assets/Plugins/Android/FirebaseApp.androidlib/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/Assets/Plugins/Android/FirebaseApp.androidlib/project.properties b/Assets/Plugins/Android/FirebaseApp.androidlib/project.properties new file mode 100644 index 0000000..37b95ed --- /dev/null +++ b/Assets/Plugins/Android/FirebaseApp.androidlib/project.properties @@ -0,0 +1,2 @@ +target=android-9 +android.library=true \ No newline at end of file diff --git a/Assets/Plugins/Android/FirebaseApp.androidlib/res/values/google-services.xml b/Assets/Plugins/Android/FirebaseApp.androidlib/res/values/google-services.xml new file mode 100644 index 0000000..a67beb5 --- /dev/null +++ b/Assets/Plugins/Android/FirebaseApp.androidlib/res/values/google-services.xml @@ -0,0 +1,9 @@ + + + 601745472008 + ag787cd54047-e0ac1-gp.firebasestorage.app + ag787cd54047-e0ac1-gp + AIzaSyCbVg7dxQbatGTDomrKq4AkQAP4aH1vr4Y + AIzaSyCbVg7dxQbatGTDomrKq4AkQAP4aH1vr4Y + 1:601745472008:android:768b7c2f79e2e93725afbd + diff --git a/Assets/Plugins/Android/FirebaseCrashlytics.androidlib.meta b/Assets/Plugins/Android/FirebaseCrashlytics.androidlib.meta new file mode 100644 index 0000000..b363c21 --- /dev/null +++ b/Assets/Plugins/Android/FirebaseCrashlytics.androidlib.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: 0126fce90148f434a8ead1dd4d008662 +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/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/AndroidManifest.xml b/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/AndroidManifest.xml new file mode 100644 index 0000000..5284571 --- /dev/null +++ b/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/project.properties b/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/project.properties new file mode 100644 index 0000000..37b95ed --- /dev/null +++ b/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/project.properties @@ -0,0 +1,2 @@ +target=android-9 +android.library=true \ No newline at end of file diff --git a/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/res/values/crashlytics_build_id.xml b/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/res/values/crashlytics_build_id.xml new file mode 100644 index 0000000..debd291 --- /dev/null +++ b/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/res/values/crashlytics_build_id.xml @@ -0,0 +1 @@ +c1e37c92-6bd2-4451-a40c-88a57f3343f3 diff --git a/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/res/values/crashlytics_unity_version.xml b/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/res/values/crashlytics_unity_version.xml new file mode 100644 index 0000000..21af423 --- /dev/null +++ b/Assets/Plugins/Android/FirebaseCrashlytics.androidlib/res/values/crashlytics_unity_version.xml @@ -0,0 +1 @@ +2022.3.62f1 diff --git a/Assets/Plugins/Android/TDAnalytics.aar b/Assets/Plugins/Android/TDAnalytics.aar new file mode 100644 index 0000000000000000000000000000000000000000..33e6c047cf6cd369604cc89130b72e60b36ee54c GIT binary patch literal 218256 zcmV)SK(fD3O9KQ7000OG0000%0Cu=6B}4=O0Jt6i01N;C0B~||XLVt6WG-}gbmdt~ zZ`(K!zWY}QNDis7t5bnJq$r@+DPlC+8j-U-)6!UBEs+FC`GKPUy+g&4Wm|741s2UA z37Gl5d2l%V=v-NZi_za}0AYC{QOfd|tzRfxQx^C{6Pqv+?ltw4jh6O!Z2hptx7N>5 zLl7`e?uE7&Qx`l58a~7Rj6%fAka`wn8sv(2P{HOR1u!Spd?AUq2ANW|X7VIaFZRTI zf8@a4dWt4O)ObF2Rm94H50Qz!cSV12eYby&||?O#kIR^y4rL_biV3 zv3vo&<#N5)i=N>urqeI33vxp}C~lm3s!voZeZK*wJI3;L1M-`snk+-;!}Bc6Qcscu zYj!E@MsQem(v|M9+{HBXi<-X<7@6oVhe_iSe1oMU#4J1K-DuK6_(UMle-ySe4N)># z=8_ALnu6+8<0&p@(+VRtK3-?ov+TEfhWV2uL4y@7U5WmgD4uzOE$U=?v9>@tCW*Ca5d5K zaI9J2$yqa{eV5yF z-|dGP&oJ$PxAAy^?&-S_ zg5^vfUZu564H^{keKWw$UL8!z>=W|{q9w@EPAX+dTKdqy8#J^~Vm~YMC{`3-1rLFplHDCt$A*0JHuS#Z@ z1)yqYl?`8)Up=^`^L9BmfnNrI8|-eW_Fg8*tyXtQA9(VRMi+G>T;*lmjiFHtSc61F z?R1kOygyZ5DX5|h$$w`Wf-0M+D@T0@M$-xLy5e}6ZjKM2YQc66I=unaFXz%kgOr=3^|vl~maci0EZSYB z@vgZd?-Xbi~>*o$0;TM^|ZIP)h>@3IG5I2mk;8K>&gqv;}qn z000C7000vJ002R5WO8q5WKCgiX=Y_}bS`*pY?YEhZ`&{oh41+lobHwvbcZ2uToh>z z#eft@jP6*_5)%uyBtul%?%!8+oW#T0!+f%+k57D0DsJCw2QL)Zc()XD`CR}x<+X8# zr8vfY_EX%Jv%;b?d*TQQcf3HSgEx99ni%^WR=rcUhsM5Fk`jZKSjvX$4VXa4El25s8Ak#jj0P|gOVI5Kg@x*b;2 zCTrYIw@(+tKg2&s#BiWErEXFxd3jR)mSs>ihM}im4SxWTR=$tv4?C!j5dx(EEICaY z*uyJix8 zYE5k%!O@Yf7VnV6>@9!>qkv?HK(@Cfux$;<}EniHOBfzX1Mf zMNF>)gu4#R=eqC|qGVM1&TTWiJkOVx)0e&{U(oIry2-rD<22#|rdg;ia z7K92PIN^{g3#@O24(9G9M@N8_4GH~rA9DAid}>`xvX$nPO# zJ1sD1U#)A}M0O!*$>uOMlqZ>jL5yzGvM9pwfQj3rkj&4oghzQ@_yZDqWU!Ckt9oGFoU0U6yjnZ*ykjgZYY!{uUC}gT`S0Irj^Qhgz*b%Y zGUvI8tEk&ntc8}-N z_#;(+`J+!Yvl}!L{%pP7Q5P-@6Shy#!apH)G@g>DbW%5--7;}{iE$Q^>BuJ+eorPU zOpy{OqIVg{vMx7=vd4VOxmc+|JDf1q%lqE4*@JF=AHy-F*{u`MoD4mUdSr_yS9TKV z3ILU&>?)Bxe;g>WV*d`us@{GFtXdYHp`RFmG%5lw#;4$&0{hE6FGz*ZJ>mRyYm}o0 z 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∓dBl{{qci4)rmM7=%|dSk#jOXi~rb*5%#F2doFeas;9+clODoy~x?I4?1}&%(v#buODJENcy#N+@`N zk;XZy{M-}>ao}-Aebaft5gyF6)0fg=NVMq&vgk0g)`Y6d(^(i2CaiJd6GLs!eCAT7 zXR z{PNxCzrxZ{XMQ;PV~Y=ZwukC(L)ZW_IS`jQ8X*I~erS@R{OKg$6%52K9&2#*SV&1Vboak^g;_)vkVGicGRioa$#p zYLWjZd;iB)%=a>bH1^bUBE(Z}pTu*s3$P{I2RaTiNCYL~?0gsmeO4lq%&p9E2t< zCzF#V2U;8b{3l%1#UdSh_Yk{)iagB?lM_a*r<~`V2eHPen5I&Qk8Sm;dF8QJIdgCFJSDx&YWH}dIsnb~vs{EpECjGmz zZ^M|(#A4T1koDNXAMK5G)dqi(zcd*M}iG>71U|RlI3!8YJZc~i8~~F@I3UET5IU9 zJ;F<-Go%f?wC1anfWWxq56dj+u~-=5akd@PD`2S?#AbYu1c4VzitDD2xr|>hcx0B` zbaqszd<#_BwC!t*IrQB$85on4T_e9P2b~+ge(%05PX_Lz2pa`sA+(cwM@B@@_hTFN zJ(hGoKr3ap9miQ)(WzBiEb37$kz;glhRezd7j7}{8*Q2zpx)xJ2f!rQ02>I3;;EX` z$o_IIJC`l@W^CwI6~m5zv%1X$QF}!wh=Qy58`xq_k%Q}gur{}R*&?28J5{3=voG9* zM`EA18Wb8ZDN?TyXOqS6pIoBK2AAK|zG_3qww@75p%rV#E8l`2sb@Q-7WCURXi)a7 z;5v>cZUT8ybyN|;ak6?}LhU?G&xGG@2fy|O&5lwtRrw|S8bVCoXKqSKTt@zMubcAU1L(n+qNk2q@%#fLAvCcMe42#=kfa z!#(j6DaC?Rw(XF{7=vyyTEGlpgM)^u#?j)CQUsw_is&g@=I%n#@%|$kx8lU%*dK=b z{)drX=ABM*MN%h|M;kj?4^xvfJGnhTX~THne;Xfsj+@OpGTA1M1CF?dd{GT89XO$G=M>s`J#->nZ6|dVDs3cD^ zegD#7GPj2`ih7bKrX@^T0T#ot(LraRD7>!)jZKr6Ynjlh8=|PIksN0u;B{BhXrHxw_vV=6WFBYzk+5j^#~EV3_^ zYkZXBXl6ENLKX*XESAhZLInL%qeR)%i2D)?!epK2c%Gb3+oqU@P&AFWVe(qVu_=nkrHYtO?Fr6}n=bqkM1#j|2<2dZM?Oa_~h56~gxH?sNz%l+5$<j|~TPFoQyiGzOMQwtYKY(y|{c`eMFQGJko@(eJlG2@s()krd8{4*N2UYx8t#NJR+ zP%~BKyyXUmYZ}IW!yQ|d-kgbJAj9&3*)dRiZBtIkvEYVL%&#Q5iqTVh2~Dq(W93E}!j1%o zI5rqMZov7{D`+jLv!C22{qbl_Vx&B1i}McLD_6feBu4?p;+@z2%~g-vH{eklc0?*7 z`QT$6eysLJ22~1@<@T5jfAB?k0rAO*)DI!)>y{kbv3xnDq&DX|fIoGoq))w(i9+NP zXXH`}QDNlNZfql_)Z3&K7f0;zo3qT)X;2{dgb{C8Ym!lt3k2PrcH28r&7E)ZKft;czAMxnj!M;TrfPr1?j=4sD74wK@5tZWG#J#R%byiQ@Mpj zi^%jBBNRns+hm=+#U^r2p+0(9Wm3E680+cEGN7fFHVJFRX3WiTiq=(rI>nCR=x98s>hdGUQa9OD=%R zN5lPE{%Se$n8fNns_>mi=klLqCtoyJwcSVT00(R7FpIlphiLf*c4&dMK~7|1Lb{!{ zR8>c(ROqm5U8wTh#S)IBw#J}?s!zCw6J&qm{-TL2($@fyD^#_++)9oL`@1mGsFy{_ z;Eu^aMM#dX49x{QzZqA(5WQ4Ad&*gCs6jWNm8iG-o?!v*V_)eKUI$v>Fv5_g875I3(6$ASlAbg>C@A9L9Ae}2uEK`Z(&aST5a>}eqCkF z_-dSa_v!D=5-1Qn7bKv}J~P*fuoMw?zAjdrg*I05ZVH(^XgBX{D!Y+AU27{Zsam?G} z5Ex&tBlJ~|w3V?w1f?|`>O;8bQcA()kNSItu*N-nLaX&*Det|X3ui|0-r=nt?^lW; z;rxfgrb*fZCbG#3lPe0z*lGGH%L|h?#7V2e8Jo+t-=Cpk%V+$WPd={W_Fh82kGc~B z%y*6sG8d^f=}sBV8nMNrGCtWD|3h-C_Sj8OSX#1?>i~NrR~;D$OkYIdO3y=v^_wvC zFHsqfNfbVSJOx$|TtT-?(s)Dep$zGs^th=q=9Walv+_FuWO&pLcBdN38Q4cHBo?u1 zy;h%>A>64eD1-N*e(80S`v!^a{z228h}VXvZ-^$pQe^oGQhDOv7#yL9+JnTtA_cH` z{%DO0NxbNynP2hzku(4FV;PzvZXyToinBkjuWWWmr*cWiTvHNw0aO?=C<@hNmY$(ltdO`T! z7&3JCgskwLk?7M9pi22HSPkQs#{L|-7E%u6p1sggoub#Wc6sf7KY7{ow8DE__?`EH z&cN;=RtGh~&%Gm7yW`s8{eb@Gwe>NmJj)B@pYA`Ys?h(;u1HnrUxr1pR_10Vo+iJ| zM2vp_Ha0S`asCg)?Z5kk1PD`&%e8x&{EKHM+#RfFqHM!T8jVay$`9YT;AX_rxt7h& zF8-a!VCi!U@md(QQ=>;YPQEVZp5Q#~&cm}4o4fND)FX-+RDf@5$bAoUD*)LG)Xa7 z9B-s44R0=qr@F&9xyZ1-w!#mmW~9k=il7;7LG#5lk6FVK#owA-ZJ=18`^^|>HE9|* zOgqq2xj)Wk;6z6U5U%knU*b35lGy6?SyM_Op?MPlMEkdRuDFN5jz)U|!b=u;sAqMd z#%@VdYk%xBq|qZa#|?oeV4XEwvxaQ}cV%%Y(uotQl6KEvYNbSPdrsCImch>0`1;Ed(?Bj%LG`18ka7VAt@9iwvAS z5G6Yz(|ubHVBXfqoQ?fJe?H|huk_~ zk(oSrc1eoLYMqsjkFT`!N5uc!$@0us0XZ`SFIC#&ajX62@5goLgttcO_rv)GP{y4E z_|aY{!f)R?e!CKuj2g}it6%fx!qo^%Iu$k4E!sD=3RG@{Im!n?ZtS)zq2Q=n>R(Uk zri9g|AliYZ976};(V)6+lFh%Bg}dP7GBaE3s{BXchJgT2jwGi=tR{*q2~a~yff4zq z12O2WLET_(Ht`D!f8CG%z#PG{jU*G1mC=YZuWtG4M)T0CGJDiIBbdU8oUN!~Y{JSl z>I{G(h=iC>-3bBq0Mi%R-nfybI^1$JSiE+b;&p|koiz4>GACCUQeaCdf4efO3eWm{ zrOmzW`cg*MgOEul%`CHvgR~oC)+THb5Xu1yJXu5plN39$M=ZfNlJ2GEJQexc^d4B} z(y>g8A?Ug1BG{)EWproK*pUkh5(u?7a>zuy!Qnq(_Ua%Eiw^rN!pTHBMB~ zIR}r5#*EoHkbG8vx7J^&ZQvqDZd0>acu{n})2fATm`>ig&2uAy7T{9Z&AnQa>Rh2q z8Jr?RJ-liM?x|Jtq=f0tIh*zSiD{1)#i96V_ZA^YX@~T()@hBj{9G!7Uk>c#Lr6zG z={Cp~O~6XsBsJ4lT2=Zhc$;PV&%6G*LD|gqmTw_17sRW)FeR}$M1G#$_J(vn+L=)W z*0^L^!CEFM)08@Ed+S$-31f6rDn@_7KCw)6I{ZybNsSqil-syT zFRP6x(e+eeLUGw)#}r^avz5flydbyLk(Wf23DNJ&ouD_5Z-i_uoD-IqMc z+3^m_D6~ByK^JB-H+mItn59z;l>Xj3;e5317{S zn{COCgBG#n&l__~QJS{M#e6}Vl_AGgwsboUyg$K){vdFON$jjA)WqMi@m}RGSOwD` z7M(X$f=a8qdo?$$TVOX0br=Ijs&tH&dd}=#@RseQ5tUKszM?X^fn}KfyKH`1`5oE= zc2n*-p6gTV?Es8Xc`ETJ1()^CmFy%EzFdLcb2#5qz>D-_LJYnI)WrBiws4i9OH$Gk zz~Pzkl`$Qh`RsDl=Dx?V*kL-#TRKAY#d}*3-0Jb;g4JV2XeES8tn4zxHNCQj^J!)u7<2OwLA{gU@0!w6v8`qimreG=(XGcD%HBi;qND1g}K+ z6be$#gW6WrTRN>s_!I(CPJ`Ok!04|AtbuW?!a*75B@}S+$~&1q5(`xQ%~KFF%cLmM z*29F&N6dfW1o`f~ia46z-ohIy=ZePl`>x$-_1O7jcE56b4?mY8)Z2H7tLeQ6cisBr z7_lZhbpU_o6PMUx&J+A|pbi1=EqNTjPj{W+c#$oJh!cQ-{2Oy5AE}Bv*7A||y-Q-) zk#>gf_^LG55!OSc9;#>tvDFiq8KRa8-r^Sw7$2#n``fdUYLYF*atRXdF*W(^9+Gx{ zCiVIEC|n1@^hHi@p{LC7UHC#7?$G^%2d#G2?b!v2qn<^KeCC&zRZ_3W# zjk~9|U*pVqsbglp)YJf80B0Zv*N+J>N?HM0#K0UQWv5_>n<}ov71KJSXvbkPfy;oV z1!)W7UZ`V;DglNsqHKt*0L2@<@=JfPsCVKuv$v6+aU?o3(7B&S`SD+ppAcSNeYIaeKn;-p@5W|U|A!IvZ~Ruf{4LVp1OUjq zHjYXYD+4XVJn>=*VlRkPxKy69!TiK=0_|_NgKO|B&gYG`g*nBmD1qLd!>FfC zGDzX7Af84pj+1TgY3FIr>9w7o&nFHbl-Gy=0q>ODclHXc<_tZcMI z&%-|{zL7GET5ClnPlpzjb86-0;joIv3U2eN@=PN44x?T-`SI^=T_ll?KzYC{`_55C z-&90LM>`#>Mjsep?uR{%dlB_m10V~=4+#GO77_X*sA(Q=&tHejp+`nqFgeSS#U-rn zVB5jtY{M5u*nuTJ4Xz-B0Lp@;YynHNKce+4w<{0=qry?%EY4E zR+-u1%@D@__~D3e&*TC-p$P6k1_Y)-{6r8PRd2 zS<<1a>FcpdRR|n4_D;6F`tqXK4QDWuRaFu$^jr@9qRoz*V*LcD24ha5Z6&0$O+mDr zWAs5blikLdWr5rWB)vatQZOU`!#<8ac`6hPyjTs7fZrUlqigWuDZ%`7JqAHWRMt<^ z7NE(4dZjzwq9r6gPb4G8)zNetO@8h)NUdphTAm2ix|`s5CiLqr{IVJM9u|~>Z;H*O zkoGs&5YVzI8wANVr*(9Jq%|9c*64}G!vYckzLJ0nZa--t#YS|QN=1t2_y8(E)xR=0 zz6?Sxl16x!7BNlj>~0&c&pUGVH;94sAQ^R~4W4Dtu5vp-4)98a9{YI_^%O8GF7KpT z$o>RLDwR=B4c%fHaR$q=GZ}6&&<4%-2f77|d*RWH1lxma=H5#wA#?n%X-5AuwlpG* zH@fUp9d1Vsn%xqyYCzyp9b0`F4hRx<47Bgmr(u3vR>~t0pTrGNhm_511p!_TIpRjx zPgI|8S2x5P;*Mjr>oEfNg3$ek2g(h<#EJeV*tX67GJ+FUOiwViMlEAY{MN`TjBH4$ zf8Qk+??|yIEpPWd3b;uz`7yy*_0Y09Ba%L>K-{{A2iM%-00C1IvQ5D)v<=ROCW8O^ z;K}0;R5*E49cB06G`<*!%RW@{TqwT6v+-==3Z*txp(39r#0wu+q?v+w`M7e;LTwT( zTZ1cJ1jDYsTq`%F3Q>p`f^RJ!hpZ62uyx|HhGfo2-$AK*yNeL~B_jO{eE&V6>mKpB zz8B9o$bVKVUZ9m`9AF?IU*P{vt!(X0&3-eeimEyM>w)oavwyQqmiyP;fA>v}nUM!! zLLBa)ZU{RK$Ec$aOA&_!0Nw591_?=tcBk5DL2q4~>qth$yWgAc_d(p|M{Nxz4!R_uM7n)?ulbM_%P^I8E>%V$w<5KQJyw>nBeR{yYRylRs;`?EoZn8e zp~+;890<0^kKo-vBDPk5aU*lSNPXRQyl7goT}Xw71cv19XXH>Z&zHq@9Kqty72qPH zrJ=WW^S_Hj3kpw3rLo$Sn}bs%w*0jJvV~S8r$k6nx{fk*V9a_zv&hIyi`KjT`ZCHG z`E92%P%~1vF=t92a`7_+PheAK*ckz&7C7JT56I(|Pi>FWke0bLnV5+n)W?5nKd$7K z))y>xT!h`Jdw3tXeD4aFvj5LxPM1vGnF#^}1PTTO#Q5JZd;ibISV&Ro|2iN4Z-)l@ z-xusKvS4671Kdr7CjCGZgv^GdGS9-Rf=~pL#ht&+^g5gH0GMw)e&{G7QF{=l{>(Xc z@qHYG9A{iJXYDh5CtTfMUoR(Ee}1WBz24oK-!~R$Jpul0DbU=ahB4Ex4Y!iU}LhxrQ*@nrdiXiMAN%w$AdBg%+c5;5wbk$uKPx_JoJB=t~JjqiAbK>An8FeF;34m zXA)V&61A0Kb(D9?z;2zdvWx7-;m8S{kS*13UBbQV#yJuM8Ey8*Mfq*Q^~FNKen&5= zGfgpii%d&+;1W+$z;px;H$i*O#lUxEdub zwAij8>odMa6!!z7>e;_IMat6F<^5?78+n+?*XJ5hK;k4;9Y*DO$AV+NQkY3#!Xv zk~6Bs-gNA8d?}XuN3pnBDK;zA%pozVORB?Nw-DozDwGO3x6rU2rNKv^y;JB75kcD| z+`?xGToNHpOELLB(6--VLMbrc0#5$+Yn1~EXr^Ef0J=7E+%O4E5nq_YogLLDt#rU{q?JdS%Mj`?w8hN4i8WZ*(@ z;#r@9Ybxn3^9w94?jS;wq3ySzpOp0@Bl5p&lu%3QUG2NOd%8e+p?@&+K;Dnb(b#Ru$t?nL&tuY~OLnG%U}&`hu8t~(P= zL&1lVWPxm8-N-pdB=hA9`woJGVe~SWW6u6F&6%4PS1{ax+;oR&^yf z?sl(fCb)uzo0m=L(X@4-FC6(<0&9wh37qjjC?nq_*z@*E(N-V3;g4#-pQPbh_|MKE z{&Pgmz;4e;{)tA#KTYI+Dm|qCT>l5c^xs5Z(#X#A_kV;STH)V9aCsLmXY8}d>w$xc z+MpFhNwhEIV<;Mx_9@7-u_nvR(>N&$LVP^H`wHxr#Hqv!3H_W;RZ&sdxwCDc54;Ps zjJ5mQZO3cB|1d=Lw0O?ZPI=$fP6~;8-<4k!Ba$e*KJzf96`~Ax$uoY43%y=8=tVKC zi7U;!)Km`=YSZtaY|i@rgqab%cUSau5TO`fCcF5QNhtb!PG&FR`|XTl8{T7IZl?g# zKvwF5X0esjRW+gOPZTgpz6XO~c`r8F;3D5zFfM=ugsh71gMhRE$v9 zMmrO`jQI5FVaH1Mm$rY`3(Z8{@?6Gcy70;I!BS4TrckP~Qky##aT*2;LA4-V9+)Ov zH?(s#$dWSHGXL7p|KjYOV|@v_ZPC5Yt)$4Gjse$$pQE&Tin8b_80m6KbIW%@1s!Lgf2O3L6p$H#?fn< zsp$sDPT;LT$XTiaf)HsEmgAy>6=!gV4LyHe5_4FUP(ohEp@Ty+1Bpx{c|>-Cz?@Qx zev3GdbQ$yb?Rmj4U9VWRIo0$93_AP@wqxMPbH{Mzk zsugp^(fH$*7qddkkK0Tvw`nNvsZYJ4eT&{B&eilA@p=Vt0$A<*+X6-8f2T7d$Om(% zhym}J?cC3pHRn-o!vOOEHyNBE%y-c5zyraDgJYG#U5*eAIYn)MyUN9$Y?lk!vb066 z0?xI|XhU1jyV$+pOzXzep6;~YPrQ7Cg1gmsyidO}5M2(Ma!3M)n)QlfDRhYTod*ox zRNhryAXt7TZ|p%exaBv^{>sZU+#Ln}M$}4 zl?V1eCm~t=?@irW9$OrRcW4S;M~WaMm4%ch8G~O#2uq4F{tkk6FSWQqRK!YFvbZwuv#4@M)7}C|Jznb}+Q(-$!}&(%HqWo;9k*YY$9#`o-<#Uu_CqVN3n$l$j|Y{ ztBuN0JZOFb#7f@u+^pO@R$sY5M*sa6=`NF{tTEk7NP#AI_r)RjDb`(2Q4ANrHj+L;I? zcErkg^pXnQ(4JQk+SIxh@tnoUp)^-@;jtFbknWNBj2ysBK75cR%pP0FUQB2_n{Y!+ zUIlfDMrHB4XZ^>T|YgWU9R}qRDqZE_~<<%88-^MK_ajg>nob-*+AsYvi*2Zq=5{1xF28I z>@B)epMhCd1byz2*c`)dNDuRTbzdxJltv_H(eQb`4@25o@-n7cr@%$ufhEX|{s35S z?7$F79sJo0>QBGPPkzLVd%mMhPL>I6QU}>2ZBov90jumWi|zwBZv}@ge6Fb1>F)n` z)&2BNYG?feR;u6slWL;{z4`wZf$~vUutH%(;ceKwxoX$i*0NryoThRq*&K*e>MS7; z6)cCKQ=thVm9;ZpG-DP{q(o`>dlVH2@9U3GYF0|m7mn^^$H7Vd>g4J zj*id9PhOawD&<@U#hi_nm{>EMNOnV^DeZIQCT6~8r$L4oSMAhFgaC_DEKJ^ z(c!3LJvZKYC%Knf)2Fk$CDAKl0AuinLgvERR4!$LaMYPh!)8x%$gp2^%-G!`3u;*DzfZ_sZQ7 zbsGb*mT~yIXi`EJnUr`s%$<&O=-@)1x|^Sia9=zqh|aE1{Sys?%SUsAtPdgzM*6$Z z1z9>!<(#~PR;p1iA*z0O9smo;?6Qj`?56H~I?g!V$0meMcl8zPtruL8(=ZA1ngV<9 zM@L0%9=ilGjsZJ#6*aKN9o~m?D>r|IVM?AK*LUoBC&s@0xxu|_fSc9VR)%&976bg8 zhVD{tXcXu+%o)^K!}4SLKPrc{sD$=z{+nGZ;Qy&A97=vvRYOI#@YiyT?;5Zuq7f0Bco3ZH~z+r-Vx zJl9;0?eXxm?e>?Ng6ZDa7q3&zZn4i{ zoohQ&;|YA{GavqPQG9(|Fhm+R=8FJXXkz@W;i03k>&9OOu+d+krSk#TqMrPV+wt0X z4l^qSF~N{K-;VsBmzK7@G}Pdrp^JF@djSWX*xh!wl7_g^OftD1?n*Tt+LGYLFfsd9 z91fJ~l`+jVE(A&Jk}_?)Ch4^{c_|hf!Qq!HuXsE%3=DGBe-BYCmeMzeo&}m0$P;3( zltXJK>yIA0x&tR1qYVJUh$YjVqw#en36#?v4h}gxl9iY`bipj8-HSF%-MF3J&%cGp z_b&0!9F|5%w~RE}5ia)JRPpBWCA*l7MEuFj4hcpZsUyV&msN5&A!O zG5mMa{0~g`KTk4z;%20P84xK@UnkjneD}EwiR8(mn8}%x%|4DOw$+lL z?MP{s3)>nsq8M41W0u|ouC1wFlEgCiMyeWa8WSR!83@do82ljA~vp7*ecRVbstrz+OL90;&; zA2!dnv{PDj4;ka$?v~gXKMCD_@^bGfOF*e@desmGwMG;Y0T~G4_b0eGT@o6%E-|f- zQT^M>@HY(f!g)nqo5BNp5a9cbducCiGBk#o?E4=aXHZlK-2b0(S^l9d%m2BOpPjLj zhl9&MLo1saJDIweN}GE8H?LKkk{S{~;WgGawqCZnEaF201F0z$6iuMA^Y*8JPy-p5 z;lSNoAc37|RuK6+A&RK;U*W@_EH$0wnQX5)UgsN!lgrOTI}QBSNNqhA53x9D=xPsz7tlGVL^Zo5^D$f=lq&@Dgb@-PymVEI9(6_y0E zF|K-4^x;Pl%GMyA5SQgvmsDby!S$ZJ;w--V6*KyW8e<((n9j;8w>X32{5pc% z;}Eq`77;D7txs#uaom@81KA~rkREv##cC^<^YhZZs59m?yO5Zm6D!F+tjIV+o|Ia{Kw>i`K9 z?txBDQRqmtnH;ASWDH;fg=Gw?*cK3B?Vtj)KKc{tmk{q7ZsUPbi+fL1U8|Ml)H+9h z1BNf9T{nHlD*9y|oh1j$e8v-h7_dXn+lkX(jCG=O5v9j@Gu* z=$GMK{RZ(VO00jlgy1kn{X;O4;TlaX#c625^cr{(B5QUx@Eio#g06C(Vbr_}d#QsK zGzd__ISA4Q%AamARJ^wsC*4E6Z3jkM5*)k;kf6ch8;micufKVNCIw?lI?J#7@sPg9 zcPzCIF=Wn=Lk1=Xq@x0wX;?kM_M3DW;o_|GG7)o&8Ns7LLREu0}x!ooPHl zs+}0khPL*hMP3^*Qq+xW&ql?ak{+i%J%DbPDg6xkop?XLj{}ERZwJxlS$?zLNY_?W z*40N-_B~PyoJRC`2X_o;s(uMY1kBj;astQ{2B)&$pf=>x+Mb@y*4`eLP0E?ArkN3K z3orV*8ioxN%bl$iY|^`?2DUv(v88(Wdrg`fyqc(I$=Vn!m#q=R3P%rFNHEd{c@0sx@gdfpO#tQfw7SzxBnQ}IiMg!qtbm+G=@J+DQW>K~o+b#mE z@MQwcM31U?`I_$*7?h=E^44eL$I7|#j1k1KmS@xDH5j+{9qyq9v9{-v^6b}!G0Ag? zv2mNEm1d*$+XwkZ>E9#1l%=&G8)s;Cjbjv1Z-unOf|^c|sa>f_ z4LtF)I)1lt+d*zjNwCWE&oD8@Rrs*bTR)2yHQvv+O6jwhHC_cuP-Atqx>WYoeIL@> zX~wDfV*N`GUUw5{BTyjxd6 zu!a!ZI!(Q@%Ly!6J>n9lNJNXY@?Q&GKXkT8@6-`r6fvDu+#N!JXSu26Phi$wNMU0S z;TB-1npRaNGgzJ*98;1`7pg|LzA5~o0Uk4u7UU-*@*tkYs_rqG=S%dzE0F4n9+(aULhKpvrys(lYsbA2fwr8XWHpVJv<=r%Jg2CHWU~VQE{Y2aq-ck?uVU@XnXZi2+IKh4pwk> zWWip93v*fmjpq1b=0ej{fE;HcnDow8B@WWK8OpW|CN0+_z9E>*zad^t0O^N?=P
0(wDcN)i2p& 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
I%x4RUajyb(=Y{*0^Bx_?V*kD$?{gL9Bt_cM=@n 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

98&r!keGBEvAs>EGQ0Nfojjl^Jt!Z!7%x5MMn@8 zuj;iLw$)eva;Rg{)MJnNJuv=kkf&NUykZ_+!>p}?MiTlo{5Z3+=XYUwY*|v6#CRK4(Do$2hv2ELSDz`YCOuAE4y}bpfy)gw9+(?!NG^n zx>b9S?N)2pResg2)xnlXsxf{uA|H=6e{$SO5M|BZN*zFm<2&a8$X~DJQdJK| z8XhB?uPHCMumI}M6m)CblpqJ+l>Zu}c#`m6;Ve=U?~XPA%a;f2q4nPv&F?lfD1@l8 z&=xp*#nlN(rV%unJ?wlFJe4(*UPczF?_@S>KTu(cWUOfh)Mug`%Sm7(x1bMdNev;2 z68URO(ch~E!US%__K{!&AI)V(lhNZKY{LJx&_No8RJHFH42$a&(Htj*t1E&!*?N~} zHV5Op*#d#Xpt$kH+{esevmX(S74{QD)f!e^m&Y&7B53?hor1z@vDP7f2+8tmHh%41 zsuthMk33!=KYb~P{vKZ-Wm`{o@R+dhxI1O3#LYB=)BZUw_z< z4c2az{C>Ozeez_t$yTaU_KtTN57a`)(`1`jDXMrS5~%gGrf*iN+4>jF2XP`YcH8NS zauNf4OF!zIj%I?t~3rK_fLhV)`dmGzTuWkn?&K$|p`4kP}PI4zWc zj(>}%-*K7<@OXyh3p5GUDa}DyfR^I@oxW}%maRvUbV+Tf2NEC;Y%$P}?M2X*77vb7 z`W^l_^$~{xs)yWy)lW7AoKQ*5{2Cmz&`mSPO16z@)4$i%Jtuwa-agt7Mq&{9T|B9( zZ47P+sGvn<>4dmu1iUy*-VL)xI?;kvtZ`}!L9;dxV?9L_G*ouYT*yr{ezLi=?s8QQ z|1NFuE0N-w@aOzKBz>MCwOUKz=+staCs)%ogcf+P>1ZVxF4Jj4Q&VF6gNnty+mJ+~ zh}MWn73uO_0cx%Xsyg2CT+d7(k68FXqihGMb%MZSgF-5KA`zgC-X)#CG-YR^RQ~Eovot|6NbD7kAkqumiWb^iv|T8;b!)^ zs0mwo=NH^DCIUAv4*OHrL~j8Exl$(pLMsl!wU=*JEe1;?o_&^Uf>5%Jj4)VC-{clA zJm1w!Cv*SCJ0q?=Ow;wpAP=DaS%l!tq6hEM^3d<8j_^d=S|L9j9f zK3D-N6o42=#Ojb9L#*E@kuix{qdD*SQ}?m@QVLJQ%5Qm9EuN>4dsfF6U60F5KY>Y# zV5h58ua%>WOo!>rx0|%e}ES9_-a{l1Cs}NY8_>eU)X2V3Gc7z z8UqV4pdLh1&rFvzRjs2syE;Uru)1W!ZG)8hc!P@R4?t8_S-6GzD5H89fvnX7z%oA5 zZd|GQlQ^*cOTQoNTKNKS0UefZp~_rk&=#0i8+|c1;tEdf1f{0~qz2$`P^{ipx(sr# z7-eayu{fr*Kwg`JU4$|O3P*{X%gCM!!{Mxj^R}^@ugqZ=cdMsWii*K#5BEY5b%<}- z_cTa95eZg=`_@v79s-?J=OsQ4&B-|q4IQ`XXj5Wm!?`=8#WC7z(s`ZenJn4Q>D+-| zB!kCo=^pi;%2@8nq8In5u}xwj9LGNe{D8|SX{orFxG8*-Fs(+?I&rrCu;HR5&zuaW z2s;eHK*QdNDVZq1WC0N%K@`l8lK8X}AD=jX)1X^Y@T`{fNMZh{XxOy}$lmEopfyKn zlrS7Q9^3hok;s}QA#LhgRHWEXA|<;>dzJA6qKu^3?OKMj3Mq4uzH^RodQIpf!}Dvk zyM+cP6=w!+sYTk@b*IyUO2rd+LniloDnX$clX(JaX-^?r$|kXjaXBuMNy|=Bdvl5; zl1xXVGw#g7c)bKAMUjMItsf~I@oBMgYAI=qRM5g0Wy5U~InRy5Q`QBA8img=6XRH| zlnl=9@~XhJkIZoS$mND5XT7Qcc8UxzZXN=9~E9N`MnCVq&ndd-}T%}yQ(@F*z zT6W#+9S0uW=0T-QlaleDl1+yyV+%Fgbe+>Dd<-<1+@l$@XjFZ1Qnd<8QS(Jc0OvSB zm^UIOgvLt`sNtCxBV+uY0)J2liC z-uK}n_(pC%lU-Hpmmf7;I3Z*PXCgx?yaxT4ZF?~@8UaxSNSh+rq6WjI;E9QiTq360 zI8Y^|c)HiPGSF;DO0uwQ6x$y7P69m*zKV^f4Ze(Drh*oqz`1C*g~i|HdL+NlkTsgi zYzzC{NvLU|tst$a5H%9nrtZDiEh?WbDDS+8g}qIAp`X+TEBaX)!{zgL6$^T1mma*n z&~4F*d+I0!jw=LKSHvooMwndceHv({`}oFO<-X!uPsQ+JKxLS&y>dSet+mZBln!`# zrnv^veqnQI58ilxmEF`0ws9qtzr&vHGc`GHo5sHvkCj3kic3&ZH&#BwrJ?azWu@nD?(Z%4ddB)s zOiuXY0-66?_68vtMMWcfXETHUh%YF;ORmYGaNAg|HW(|HJI}XPA2&5M8DBVtqRuud z8}|Ck^RvV@GgZzqC6Q1^(tj37>qSSw`E24bJ10cS^8?d*+OGl~rr&q}Cq{SlV1MOh z|E2!?{p@0?*353%a_vQ>-FhU#ez*p33AC5t*_P4frMo9AUbEjHglNVlB*kOBqxQ~V zJsZeeesHhdvGWia#z*|E^3#jGT`rL~To<4#=cr)^FZDXoknVJ8AGEgaewEgiZ>c(F z)vm`<+)SAyEzQWmt;m`O#hAD#xVaZTcu}O#*j`EekVa8>fF!tECx#*q@$xAkKL9N7 zW)AkNQ(r4RuP!auP!8^EoYxBlhbcdW2!nijP`A<|Zau!gENfF-1cuvK=U|`Mm0Pd@ z98M-4-iZNvY!IM-GDt?Kw#BB>?ODen8;C}ikl;(Cr*D@-aeZvW7owHSz_JsbqC;mg zdvb>Wj>A`{F*KKqIpw~T5DE5;{&zMZt{fSv)TR>}#)2v_Gs60D1PK#oVy-@eX@hWn z%{l5V(Lj`95-IUm+cgVObs+%(0uV+52x0()ZZf`Rkx_1{x$*p=b+e$0S$|VhbaoSK zM9b2pv!lUeLuAqy_OJ%Gog_P1obTz+7LeSWOgnJB2H%5;L`@aRlB_40P!1 z{1!-EWg{iB-iBLIZQ}(VDRH&=pxBx_2u|+V2%%_&9{{-#I!!sHljVlwjl9qW;Q>Z?a}f?IEh#6ILsbMZ*vW254J-)maz;uIa)095XA zp-9@7%&$t;2+RV!_|w53f15abvWMuH_+z}nF#o6^^7l4^oV|^+nc;s}aQ;H;BP*kP zSSzkLV&%Ze9T2k!kK|v#KrKMb&V?387dC+d)c36H6R0-MtJ+!PLbhLHY~M(2gUuo) z*nY!KJ>#9gpSFtsmXL%QCGvR6a_Hhd;*4qcd4D~#0h!trh4{GMnHYH&+bnG<8Z10n z5kxKiPLPi%tLFbozyKt7@FCMddlBwdzah*Qwtms4+f5FpSjS<2E%2bprp+Ly*hstf z{F2)_-+vS$Iwgb))(h;hWwF7kvs{fR(xR{(g|l`zt75wAqF-Le)!0aV%t-rzkQFQw zoVC_51Q)SJytmqC6_Tw4v?xE-~ z*4hRUtojwqsejgPtrrp&)0A_dvAufZpsHa3R1x zEX_7gmkFo&AcSbLk$$!@+wX1CEBN=j%!RgGp&8u#VND*JA!BFsDveToOKBs=R=SlpC6 zIun%vlp7VKO=qX(DbruuLPB2xv{23v`fHoYFaZ!l7{Bu`6f{}D{IZ5HaBOzZzvT$@ zw=Mw+Gfw>%ds=3q?tpqwaO)11aSq|aIwRjj$o1067Hs3%dMhP@^LX8GyWkd`6?n0YYa$qg*4$|D40x^kvN zvaer@PT9xD#LJq$TgyR|sH#KAa(3~GsN8r(h-`r@S|enaNzGF%W1sMQ4%KAy4VPz$ z#+CGCC3WmFU=wYr>{Yb0PD<03_H7ya7=##QOujq)vOAusJ%d|nQo?~_^6+T(wfJ5J z4C_ljmXIe^h$K#QLeXV7-=LbW5pxo$EzLR2_aq2}vBW{kP`+S}VfN%-WwurxY$UKl z$_#BBLh*+$?H6=%Qm~Aa?N4DM%4^nN3#_8LI&BH?02FIic%2SUsd9w7=3p0rF0jj( zuqlHdkamkTG7Oi7VYC@f#!nh8<~Q=3DRp+V=u`B@rIuB~kXkKEZ4qtIuQ?mOJN1b2 zI?slQEZtAlL{vEr%zm;uE=#HwIq!`hzkr_#?hve_kk0b!Y}@LCR~wUl3dN!glk80+ zMAGg&i0*udIz`x=_~3DTD7IiS(|mw-GR>^`(B~j?f_C~P7*_du7x8^@tR&7NNY2z0 z(FB!Y1}%oj*5ly;;(12y5O+L)*(l-=JIK}mO+p--JAYxdFF@7vwKT7 zhI@+oqJiuHmiM3c<=Y{}_6pZ$`QU_+Z0Q7Z0dJf_UDp}v7nQh1QE|r5VFmLm(q%N` zwkRM@>!O;udi_n|i+8*#eai9P<`wt>`M+JH@e5S=94H6~>OX3M8Pi(-+Zo1hng0vp zCO&^jGPGKB)4kZHD!t~&oR>&)KLjKLLrHn`&d4O1znlvG^-BQpRmxp)5Eic|1ZkQ{ zIoHo0c6=w}CG(0Ca%x&<%Ll~O&(|+IvvcbO7)q5{5~z`MqLMbqumTq;MKoJM>*qT_$mehMurJeJ*GpEp{q!23Btx$H$%pL(K=4HT~aQo z7TlmX=7ptSXLMYzIymsS{Q|0HMo++{rQ*6EW#~q8d0?K^3azD{VX@oD^`UwJ=QamR zYWPNi(*uS<+Wg6L@Cen$)k_I|Lp5ubR+F2WAb5oGbBi!66k%%;AnM_eYP{xI_HA3Y z`$#V+mjkEg0Pw);<&!D{*KAiD4skhG!;)^kXp85h>S?=pb`e5IPUS?+sf$|F}ZlenkAx&jC4s5Wg#8(dpS4~qbkYwA&+4urjY%{N`FY#sl7k&|B>Gvh(*Tv zbr+{qAWjCb$-2%t+&>+yaf*0-jHK2DcE~z)^SqnNR3rhrf3>@A^S@1ktNB;f zs@8tGS=Pu36^uY#NXt&jOir%Q#{Wt7TJ|MGo4H!|MvVh3E^SBOk5(O$=Iu}S)&>0W zKZ!@+2zJDBvms`q>y_1KIjHQnsvTU01$g^|s?{4~4Rtf{<0y4}eu}^hn`#xwlL{nN zpVH9XZ`~9WUomKodVR&x;PvYTy~nqB!)C2d0CciD#P^n>P-Pecq4pV`-kgbmG6!Qx zRv&?!JSoz}WrTH|HL?z5L7*8CJBv+cFonR3e4=kACT3d4(z$(n6#UM>;DC}7%W^)B z^2Ml!sNYStjX{zCAS>3#KYvPY@PwUErt_JY|3dc_hAQSfzQ@ z8bug%8e}ew8`R8Sph*OVq)~OV^oMn$K^bSRe8B zwMS2W{0DhS5bo5-6Leo9Cxnapxe?va%}A%TOhJN8jEs`STN`@M4{DK3y?f?~VOt0j zc;EcvFu#*9R$vkRE$$I-KF#(n=qYUQ)P08!X;kOO*RHu4+2Led!_fCKL=ntF@>C(-LyK8q>Ni3GTlk)O0rXv- zPWCPOVak}7k!!J@gUod$4aT4fNrj1`5c?73d+iGvF|3pc4a~J%N<776)& zxli3uY?v572V;TQ>XY9CjDs(ImXA!0OuJ?R@tOC2TXUuJL#AQ%&W`q6@R8CJL86DK zlm<=C+&o{|6*0E>mzy!|U=}8xP#MnzoShb?{2cl2G4wl>n#?O$|NX%bShSVNr*oWc za{Q*)c|RA=e1JPV6Vwxq48fUqE*`Bnr$S_7mUZy+Pz|XO6;*G9>bVOLkrhgSLEn(< zISxypz7-#t=?h_|`K?Jna;iN`2v2-_-y}_FVKx^BfpVrYGqys+>w~cETZ2TfWXl`p z8xA=U6rS<&x7qi%*=53Ywt>mcCsXrub^&X=ek~x|LQGwob2V)9BTvS#dc6(vt;uWg z*6lFl%N}h*21G+nfv}^+xiGJB+;WNQx36iGN}edle?8-W?!usE_j^SVbIpv9AuCZj zNl*iedwYFyNZra*dZxW!2C#>rqKwV)osQ57XM-P=Fq==Zh>$htid>SP|Hds6GetTR z@TZU}`p2o9zb?oBJ-n5vtfh!$gyO9Y^#chlJXenDJ5nwjbPY-YflfGI5)zN)oc<|f z*DuTXpvmA%>l(F)-|!uUst-k)=om20L$j17Z?GRh z5^#Bk3XzzsN<(W0XQkZ5e`lmippiKVzI!>u-jM5Sy8tlW^L~vAlq8)F86GL;wk`W{ z1PAdHYJEEOGm_)E<9eGaU;@rywE@SvENzlbj$$U_N6rWXrtMUOInx+hfI2*dZC38# zi3AZ7W8$5Ve=ZM#cawa7CaY0L@DfGCwv#^T6asf=Ns1JUY#+G*0d>Rt2d36M1I>0G zBn}jvSZY{B>Vl$>bEy_7tV%zFKa^W&JPpL>$s>|GaLTYq3YWFvCL=*>O*pV-MD zF+9rsESCHc=aA+=Eg6B;0||ssata1!OKMG>yMSH8S~OB&_t`)IRnzjj9y;+!N&n%1lohJpT9@GAtNWJh1|64D_uR1fT{uodKm z;P^=`3^a)}BxNlcgCw!;sX|%B&sYM*em(1azaYw}B-^xS- zy@a!`Svo0vc`2sh({UNcP>@1|PE@M+aQdT0hdcElwJ~_+7#KhMXec>uf>R*+se{7{ z;#66M^L|^h<#k~japYmXfDX#JBo4J0-8}Sxv{I9x|ES)RJXI2sOD}n@OhCLRn1OfX&E-AOg0&uhXw`T+ zLT1*|FMB-;a2{{+f077P%~ETcmOkTJ_2P6ihRk|E`k<+7QG+C;+uGo~ZFYo#f7y(g zv^F>dg*bMjVR46l$knxkgk|Img-CI}nMLDBr@YL6H+$Zrer{{JIr*v-G{V>h>z!y% zxeoU^Q=Gu<$Lq@H(4i?15@;Sq6y7bDKuV}hWfBiXk^d@8;f*IAY-@@K(NA)Ete3Q7 z;t2MnYlZWMxIVZ#OQTHoiiHb(LTXx3Sr8fm_7aY`(Tnf`4VihYZp|4J#SC=w*Fby#4%JJ4hn2ayt4?t>g0LzeSb&Meq49YR~`s z8a;kZf{-5}xIEv2tI|YE7%im>3TEiWm%yEvD8ip~w}MqWi94x4T6UUl0~sn0{xArB zTQCe2aZjM9{NwB@rn|iD_2A(I1H@TZ9E68w>G7&iVpj?4o}>1FEldn|BodHI*h-Ur zJXR)Nc5u5g8o+@mdd6UtXdpGtq3^6(YTCYOw3f*JP0z-{z{bEwg9i|R0P=+;3C>e- zEx9QQiW|ao*0Do{28#C_W=$`+OxY^oK^TZa=M-WE0IZ7G+IfhEZ{UMca-&yDzj^HS z&K6Vk)%b1|dzB*8L%axr@~)cNBYl5$P!^ybH06A3^sNlusyV*Ol4N9rIj`Ru_ZE za{A$+Hy<#RZS?Uk<-*!2Kca3QSKy%h?Vh2LH^Oz1iZ=hY!$JLAOPqgpcl5tdw*Gg2 zx=`^#=3inZu=YISgaJgUAb20^Ro>4}MHM&H8fvv9Et328bqUdtkm zBJNL#-uGi-5J`aiF`Jl&>bT}t>)4aYXB;U*xOSLXVI_agh+omikK#e}?khIJn(dLL zK?THPqJyB5f@Q^Hv1&GIhl>c|n%27PfVD#0KO-*{&=bSHCCqYZSsPJ33sDH9fWRoC z5m(Bkr~2f~c^uYyJELFLE;b)bn-(4ixUIk`4Pe-R4(AA%0NeQbd)NfGyi@r#(4sF^ zcVGlRHQiJ6u0uJs#PJ7P?>~l=!@Y9%AgqV%;0!oKVng*nnt>2a;he@{dIx3u$Uh?y zUr_pOuM`v4tu+Rj0#09&rB6(rD9&0UQrRxIar+Hv-RBwG^IEMXhbdmVj>=o6bAC3S zCP)8r_zel5uW!+C*VtHJnWuNTlunRKU%Zg86CB=iPz`2KJz15^jBZs~`@Z5hw{BPSM zGJ}VtfCK@#`vw9+`VR<(KV|cO68S>ae-pt_yA@87(EIcdIfQW3-|`M1f@>Vef{M74Ek8b zQ(t#*F(R%*rDSY0@w{yHxOB_D(*E4LbKwQ4!N6l2A(i2pc@h$$Cyj9DcXpf?lSwN> zJBd+K=TESrjt|{8;w;@~HtM61UXA;ZZ`HL36SJdYCr3|clc;kc4{tL|9GCf%Gf9}* z7+fW9c1g#Pe*IHdtCa)ENvpAt+vtnNqBG**T5Z>7Q8^0>?YZq^sCtGG;^7t^n$q2*2-}S{}`59SdM61L%a@Sjm z*uEmU$L$K}SSv`KW=a=0;w^`sq3k!vA| z?+tXpFNau8{RU6L&$q)ujifB7n61-APJ6C%0~$ey&XGp%tsy?!6vS1(u4o($?)0B{ zeR*UIMJt@h*yj@F|9xsCw%FMsJHRcLFdTh?VbWs6U&ph{Z`g8*efs|7XnpDw0XrDk{#! zsyayFiXOqqp=jN{ByYA*W!FGGx>dTdB93Gdy5z$kA1NVDsPdmC&7$R|_)=QA4gIO}pvv2~Kw zyFjJ)!=vpG-DZ9Yr?u1Q#`9U(4`j>$ph@ufzY3ds&##D*K4J|^sEg;X4`4Ow=1bVB z)#*B*mpHVGmOWZ@J=%9}Mj`j0JZ#i(32*hU_tqG+F^Qt;ZOyOSc_{ z1xJ6P46~L5&jVa&cqw3&iAlS|NQJrlkih^E-B;u%;7K~6TSzxd1c{>|XuI2+yx1~W z`NlpiX*kOVrXbUDBnSHPjAUA)!Ih3UoJtK>+w9h-?c)NbJWf(Quu%q5tU^HOE%Y1i zH(y_(C0dAU!sx6hVaOW-(Xf;jtUxiTlxt*KL0;nMoNUB_5A5!3;QMLRh7Vi@SD)AK zLNPjf^BsE!aFN?&_KT>#>N6hAuL{K&EQ>2OrDVk8a|_pwS-*)aSugWGY(ct-*Nc9Y zZQ3GvPwPDav<=j>h?+C>L{&&csD9+yFun=qJ@boxAl#)ej1{1Wm z3v31nNMr#_u4=?ur9ODC3`z0Bp9HyD<+{z?sX!wS#sTHT^4>cQ#rqt#-jy(FP) zVzvyDr{Ol!m8E?LyAA2+W-tQE1ajIy>kWG;8~ju68R_y~rU7DBq+7R`Wr84e!yD-{ zZ*XXQC1cVPu=R`0P9wy)md6fsP2-xv%8JZD_EFbV2OM&YO>Jmo7UMa3sJ{LSo%_V<0kqMpR9UKX;xr!eEp^!s4yChJgu(L%UazQ7o{C zcK77m&6D%q5@VV)5XM`K>E7UX0?L?cswqab(lz7l^}hHUqf8sJ#T)9IIYhS8??W26 zA#{|tXv00Dj!PV9Xx<_|&1B*A<$ctX&%elu*koEXLL-XnXSj`>Jn-&o?TjVYa`@HjV1UKRRy8D9JN zqNJ2rCSEDJyhnX>MLoLNw+j2}d?y<8K|rc;$Z}skvO*+9_{Qj``AO>>P?;u*V$~R< ziFjz%_SRtsF7YMo!+6umUOn>#`%Lo}Fuj=F@?q(+)y=SVVUduU2`dBL+*3J&%JDrt zxD?!n)DJ2Q9D0yw_q}1)5tY2vOqH~?)?d@6SM+kb@dN&Uhuh#JjICa95RfLge@gX? z{?m{sRMq$koAn{{A{zqzip!!95qA=6EQjG26D)27-V&ml?0f93NI#%M7DxKvNd! zPpq|>2Q^+71j6{OVi?IlV}-Nh@G!IImo^1Te`%MHS(j8S2OBW_kwcK1<_q&)1jZto zIat?Z9VnXUD5QJ56PHz@Kg3ARjjN(qRGI4R8haqjiA3EhQ;^c)oHBHt=?RDw0OrPP z5Zh7Y?|xYePm^?)&T+_hvGCwFHwk60vo`#ml((H8O}RlWoT@Q?9vE3%^D9Kc65W(S zfFSorj-7h_>%5tuY9@-K@)u5Z^#~f8J&f~P1JW$i61NdnXiz`%A<8y0IE%XmK1oLd zYhiU#!}CFh#gzcBFX`@%ip?J2X_C*JH2L6_-nV3A+tMbZ165xpz?a+|#YC`H-EO|+ z5qyF_qB7LY47{`TAj88fRGSSWr7nOQ&CL*X@I_Ly8*skI+6J~PJBM~C)Tc(C{aoWJ zRf9G;)4tEg5;bHxAXp~MN7QItd<84biJxU=8^T(q&Buk4Vk1K^B~RdIC)K{~4?&ia zA4Zn_jqk7=L%oh;EKDjW0tPY0z%w+;^=L?Bez}mCPv))tl#<<1>#F=v5}e-SWYM-* z68&PAJfCBTs6w1^x949av-DBX2D=FU|^*8f{`2u~>R=%6KF+oZr29Mw!l> z@|24B9k?t2>NdjEIFXa9mTV0DEUNe`vMh534z7I^KMYqOy(W{E!ad(pnj7A{Hb@e# zvDr4tj%bleRDs^naGv9BzTg4T9xKXIkWlh^4v!Au4V+WI|0(6TtXSn3H|uGe)KW*p z$dbqr5+B#HJ){Qx$U2V&l6}87y9C<|mZt^P^BVFODWU^jt#Sf5Rr-i}@+1^TUBD^x zIFviiuMlrh7*ENq?;Vt2H)1Psov|vxi4nJocTU&XA%#`6r4SH8z z?s9d{zqKXLN)yzD=I_f_U4PTo9++V;wC<`4J+=Z+?<|7v@>!u? z9FeEAMiX&E$zu==P^(@HTYxVN-<`T=(Bxc>mt)x#`Tj1+7eN)7Jt<2cAyrYO-J^P8 z_@H!VgUIu*E_T8`cQ-nw(QnpdKIksDCL%8PvIOPd9OWmw%V~){v;J~!W6Y>e_+*n4 zr;!`p{!=@unb96T7?M@>!_1>*Mr7wv;b(Jh=Hcc35$LFMAWkam5vWoHg73&ii8!_Z zjAw37}V_FVjRovL+@E0`| zJym<9N`HA9srFsV*&>hyiG-OQQ?fS-FhZuv{(#wdIr<|)!RV8Dv=3A8FlWa(tt$vc zuuJi2O|ja%UWDUNTGBY|S-SK@tN1*TnyX+9FHz-?Q1Eo|Oa?dRhA4z8i-AhsK)=87 z51^Q2IKLdL`2UA+Xzn)SyCQ@b&jz-r1fq>yePD^~H z2M&;aoGiJ|s*Z@~u9*sxKajbqBcOEn7>#FM9W!#$hRimiKoUlXi-D*=`(#cR=d39WSI zibmzx3q?IAt^kMl+QYeZ)6el~`afAfRfA$Jv$cVyq9T!S^tlHU2N}1V>7wA{7;6%A z91c8qG}zQq1EJ9<^XZAzBnKK0I@cUg%LH@ zOTcqs?Rwtv9XO&gjV0m{h3+#op=mrp@FXUVW%%iKViOXsK4k?qd9}Aduyuufv$CpU z@CcINML7to9p-_D(4wSq2a%(84ag6p%u3mdJ*a}{nfGK49mr90L$M-onP87{r3Jqz ze{nX^{0v~Lp@kP2h}#lzcJ^HDg#tOaM?uQ#vls?A34W9zQ1!taLp4BWN+3Lj+{UVQnpbb% z9eO>AK42cdtDc<7r*bd*Z?wY(c*Sjh_6O~rF04IKD5OP5l3T^CD8&h>S%ixWs58+l^vSgTM0XBb;oSjaogta( zu-TlX5&3bUiOy=JhE3Ym5Dtfoz*>(B$crea8#5RoklcLBpYT!Kbj}E)n!$5p&w|#8 zuv=MuF`xLIq@0rjf=O~)DIpAzg*buLkB^OG+)_w~pZV=UjrO!lxJ6JGH|DO_&~Sqs z_1g4vzs{%*7+F^lDJWew!J*^`WA`t0B2(NW=w4>j5_kz5RfphcB&YLuqM$XQ=t9!q z;n7=5D{8=WC<-$zoH=p&(r*&75oh2JRD2~{&J7?`p(ITdxHb1qNZ7O zh5C^KmMiAau5|2g4$lAkW8}r36%XitOG5qc-lLT)zrKJDJxVcg}NQ(W+ksW8n$r zIWxB5Ne|st!s3JYNnR+CMQUKAn4pNo5w^pLcnr$0u5pk_%pkJq%mpVBr<5PP#5;-E zQhgWO*0+7X_s9q$5#ff?g!?G_#2nPjVJ%;ZIutcvFT%y==geGL8-%VI-o$<7?by0~ z?lD`3WPAHtw|3)TjCk?S>K5dmM|%wa-w4pp2OK!M(=qRa%IZ8X6__6kS2$JX3jMx7 z`n>Kb-LzpT;H-#H3;*YHUFQx&IRJxG@$T+kAK)S+;AD;Pbh@;PmpH`W;)DLQUMe~6n z&_pfQ)_Gfa{jfEh%-Zm1AUT#m$B2eH!rH8LK9_vIZ9+t5%s>c*Mo%a%Kl*HpSvrA? zwwDEVvv`^8&vJ;b(=!0_?wR^dV(DbOef$50_$LL~^|$$BMKpi>KHq;$*81oFe+Y|^8&@Qel72OnAv@I-W=_O7 zKH~E_Za(Fmuf3w?94gM0A#q4X9&^=^b_&q#N*+UQPX#9CCIs>ef$Me;9qxAe-G+Yj zAP~$nINVSNWQp%I=8kgu_XUDg8QXOq>39k}i*~W!@!S~kovxV>(WvOi;Ov5B0_(2U zdA@-!qZtCR-^<;E=4^fg()nb46MR({ai5e7aNuRO?su_?%rOcV zS%2Al7aX?o(4Id&x6x|fY-PzC&>e;97QeuP%l`(XYOhtoEbQtxmAHfLN+hr&RFWWr zc#+|fZfSub-LZh;B!@ z(=|T)o;}*T*_`=zZhD8tRQ1n48wvf7^o6Fh=6@RzUK99n$ts^)=i5|RXpahdqrgXw zj{KcyK&StbFU7if@-k5@lBg@7+Y{6~RhJ+|Z1+#fbDG+OD2Cw7=wLkc^5nYebKE@27H3tI3P75k)KUm|U!tR05Ymzf#w!VG;BI2T~#%v{tj z8PtGvxE!=6AcsK8h{|2ncr&(qpCkB8m^F^HX%#;)?y)IfJ_iOMscS!LVYYbgM2NTl zxN=~brQnIB*^sSNP&AX^+gS;17DP#Ju3-z1p4N(#XT0!dnb1xtw?WCuy zeZeiZ@o|@!es5CW-7PzLMe9@1Na>YgKbfFlbX|f)eENw~OuE2uOPK>e7I<2%VD9Q} zgktg9g@OU6q@Ixa#I-Z##7UTJHC+4Nhftbf@)KGYbJ}cmy|JUhYOvmWlzGQ+vLQGn zG04_+3Vo?;JGubKfi!l7YP{%Jk|jQ&=m-5f7kCQui{@^N-011n36@Qfl*m4QTk#eY9|Xr{gWJ#|-{hVAq1!|)czpPe*hyH?r>}Pb z4g@2zHym~Lq0WU`&=j#b)Oq){`2uB5_tCRvep6N@YD;t~OYb4-`KC;1zUEr-k05^L z5juEiu&cM-e}fBz!o?f>IX{+QARzSr-T2dA(zU|YPXEcT{%`qOh_6N8y-^pO&cDh5 z^uS&g5FjN>ViBZA>d!=S&qh|`fP_8?0t6!YJ0_oGdtQ~8Vqz1k`x{v)4%6IMn;jo- z4-b&O!>)ZjF)^$rn-<39Zl;{;%^KFZzsi2BRxCAPM|dPBFF&@Z(mhe#^_($rTQyDL zOpjTFL4QFNX*rJHp4mTL^BTVo^Z3e&Jfa&q0fAcX6g+qiUbNF( zYX0MD?(<$a$|%G``-Nh5T#=Bs*_IO&+@<8JLzBhp9UztRyy9dH5LeMvxr=)l5hYsgxQZln_zhtVoM?u-4Ge(gYFXlK7Zh`%4(+;Q8~OfysNft0+k5B?>`3cP9Al&x3{(QBIH3W&Dcm3n0_;F? z7yqg_Z)C>KEcrdiejexuY|R~mR_qtGa4*ugLoj#4Wj21EHVZNKJ(K{wD5#=9=D9@E zPLW}_@}c7^0{wl~za@g#OBTe?ARxFX|7@|UM*94=w&K=~M)t;f{~6Y6RD*U^Qbqls zP2HcA3bs-c4aJEUtCs}{u=6+lPW9D~OuPrw%+t+*)XngA^S+bSuVJpts`6D~IY+r& zT#K?%N+4hCOrcV(Ql&E73H|Bd_5O81HtcAV)sIT>yLtkx!{xe1lgH)DG&gIEj_Wg% zuhTPZfcw0(2c`|x;>1TomIBTOllki;7;RLtI@h3bddgDbW0`a1(s&~e=e$jdNP_v; zA#>qL)~YwNRyk>CGygo2MS{PJuLcWwZY#6X*%{_?GVX)_pBSOeNFY;feSab%E8w&c zYF<*xFS>j#COXcuxsf3=3<%K>SKfo+0>FMN{c28Bh{+w-bSbFmfPzB5FeM<}TGTV% z)9@qQiIOang`dG%ZY`-qCBxwNzS8mX>|OKm!w&^)~CcTe6R^3f$%8U zTLzL$xG#u5_Qmy^O6cqzFdRp6@pqHD@#iB%Fg2=M!}Oe%o?EFo5tX8>5VrR~nB$Bn zX{!xoiJ_dMhosLchM^k$?CPQRqWRIX2^%q=;=u<tQBS)&V^O` zupLlOEq++bW!&PeWLO;{=6WUKh!M=ag>WF9T9SAT^+ia76eB)E8nj{BBIuK?YyE8j>wr`uz zZJ9lI(TzKc1+_=T68Xcrm^)=d7qeqrip1*DwKf2|fWtktMSeu1R#NQJ8Ls(f>Lp4y zz(o>-c5oc*DN9^=4pm`>ACfz#h@pd;oT)+Te&GOL_DMe*FVppf2(Aho{1rojCfmI; zuEAfGPBWd>pg7e&VV8sVTb?9@E1fi`O0{mTEU2=Th~?SskhjQ>Rb0`z7_fm~PMBW7 zZYxl*T{ShhGD!PtbrgNovufCiG=SC2b5yWQ7GZNEx^Dlf8gMQAtV)Qj}ugqT?bx^)7agUawo= zcSbL*UbsZ;#L)Iq8nFqv(@q%C2s=XS|3le31^M!INuYh&wr$())3$BvwE1h>wr$(C zZQC|Z>;7l%H*sUWhncxgyQ-clcC4t}xmIQt@ped-o{l0{cMaIxpDwYx?i6bE^a@L} z5&Mp(AdFUI=yDGQZALOfr-Xr#OZ!d&h&zv8Q9-sm9r^78cBDme)D6%(GT^L>_A1+T zfQNpdorulId-Sg8D~+yQ+`V62Acp&sc+S|{zq(+=hb?=x-9{id-%ZPaM{Z3u+$7&! zmv556Lq0h}RGsCM$y_E~J4~ENy5E6@Nwf12Sx?r*bFzpcX^q3nDSYdzwqs+V z%VX!g?G>q0@~Y3r`!$ID0 zSx?FC_tucV# zq3(d(8xhwyMeW9?CZ}2eDV8GM9ZL9&TC%Ho#wR7X=- zgpQBQ-`eXQVZdvRJ0gF5N+mTraWbgQEb=w4C;2GkrY_vr-c5TgQqwSWJaVsgi(L4$; z=Bx~vQsyq-Tc2#;Qxql6$%sauV)EX1$M2=w!cz9yS-<(B)#6Nk&A+SOzcETx$`^E< zkgDbSy*Y^1d$bgsW# zB+#;t9xBS)OQY9Y5JjSc0nFyvgWic^F`Huv@#&!5#T*#%RfU4{3kK%qQf0g@gJ z_;F8yyu{}i4r&V&cSS|=`&mV&_v(^AA-+%(s4W@`WHNt8FV?vwanha=MKw$RDH@RD z)vvTmyg@HFR8(pUB}9`xuqCQ8b^G6fr}95k9k6ygMWPo zUhP<=_(n%4U(A}tj^&J)cM7h=|RhQWY-KK(J z2G#4xLn}WddVgaRgi6|DMbGGgPNF2Sb&Bmrjsn!{4SBgD+>;QSn_iY6b7l@8gv-h+ zNbu&9zcRKjHk+lBsXCfw?^pd*4%&^I$3}=xFbE=Ht3||JEuu zD3HAl{f8wW|HHC{{{OdPRRsUDZ=&pM=PhZ+0-}UpeqpUY~FHZ`-U-4A67fcnfh^ zn`wVCTCs-lR9Z%#>!x}Z0j!bpyUVw9Y<;xeN&WvhVU=mRo6pw4!&a{E!OuEHW)k2E zH$FB;UN7|GJwN|xP^)lhbIs%4I^Q4;fW1ui_8EF4Z_RjbShhi<>6So8hk>2Hj35>X z+4?DufMj{nPc%bZk+ZdlI|2`kvFrfUvaS?uB7*`c;>1X-=hv6Dg#Yt*1uVV$BH zqJHZ!jlz9krcQBMtkNOQgw=|KAl!$Cm2b#jd2DRGboyyVB93G!z6ciU%hqlIxq!KVKP`@t(6|r69f}Eyi9lu%Mx(^t zS>JEIsVoWJdy*!l7|?~iMH5+eZ7aUGi&@gcDjG_PZEI*bBnzexr8ndsiRol)z0^%^ z1dcR97-Au$k0_;3~KNPhPxm1V_pf zAT%2pow<986i<7WkRRJ)(ekdx3(L{?f(WX9d+ftfDjjn(;63xNf^ZCgAlI#H#?FOy zw-@_7KX|1W18rGGF$uikrn0WgkMf8J%H{CmTBScmvgj72yUPx`TZr`kH!{o$alxwt z4Fsg{&m-3VvCIwa>>2D`oGchr1Qq^SRGlrY|5ez@>QHVt!)V{D4c!T}DB|ss5`9EN zDK=1$L`3YMhnyqzNkMCp)Ol=H?sRsvmdDH7qzZX+DSE%Dx*7Q_EtRD_Kq=#G5BVuGGBK-Z?{VIJ>C?7E>UBFum4^GY`R+54NDru zq#EH^7L(XNo;PHhQ*suXv-CDKc*>45kOMu8C^-^lmTj@;l(@R<*)y%Btk%g3bGt54 z!oQs$KyQ7vCln!HXP|NkcHC^QUGylS^9ZxXM6Lu$SWHY3zA~yso1#*KLfT6u4c~YdsqO)Wt zvph*u5RrJgTu!2@Q`QR@h_y{4NH04ZMN^VE8pwZIXq((HN z(#l-8w%TH8O|*$!IuR5Rs&L?pdYDf#k_=;HfRqV$6IdZB?G-n*B&AHwKe{}N5+9Lk z;>?r!A@EcwrDK@Wq;UkQ4z~|Hci=5v3pVQa50k-m7^MUlC8zMm8QqEDLM?gYuYA*qV9|(~^>P)_wB>1(CPFVdcW6I1wgRsOnF-MUjtsac2a2}TM1vI~5Z1-8 zn{{_!GETTvu~&n#ArsXPXrZ@Pn7z$Za&&)fAz9&3#E24vpdI{XgHzvtI;<^*X|GvJL6Mq`V?W2bDtp#u(E?#5#iH8Z|QJLOi2 zaNE~OndJ{C)z}W2PxaQ#jd-ndU!JDQC6o}@34eXR*Xznw>Lr_rO??^%(-R-X%n@U3{j^0 z;q!fzv>OI;V5MSKOg|UgU|bkGjw+k}2lc zv7EVh4b&6h`T4Y{j-~=z1ZfJSk0E=+?n<5_9P_0Yc!qGPWv12G*Lti%K$_yT%_*IQtFpjXhNWiHZet!oqdyvF3 zFMSjMZ}dcw7J)Bhx_>2fPH#kLfhne@J5(WkbcRGlvHw(efT$K6hDR{nimbl0*SmLEP0;Y=f17QhJrA_lFX*^4!1;Dmx zJPzr05WHk3WOH6>zW4$kt~{8mk;DQmywrt3{jWdAk!6bWOoDKxQJ3(D;f;zV0rPCM z=&FwT-U2%}pMin=@rZUIT1 z^Vzsn;S6YZts*b(lDs`O__2DbIAb!w7`#E*`?uui?)x{zF7~$$rY<54z7USs{gwx& zEeC-ezL3t5`(Ku4p(z9@+%!-d+g{&FlIjM@Ma@BabIlqI=VBwdc^C&qlACWdWseQy zDqUgJrU9wEK`K-R7yU=O&&*!!9ooE~gKeYUZfRvOSj{ol`8xsUV3i15YVzobvHN-) zagANtGu@>^2nWgtMpyJ6WNyy5qXDr=jj%&}{j;v8_iY2g*sq*26m3pK{gL9Pq_wca zU^yy(P43D=cze1yVr!4@r-dnZaX#5ahY#oX0NDi(Z3pPd8h0Nza)&1A;n}}EjqgIc z>U5Lx33&cymRSpv41aq$dc*SXFY3n}zUtO!4iHkfe!B%h=?wpLVzCI81m#Jm!v@q0 z{E=K`!~)fo&xPEWGC^dW*xhDEtLZ}lJ*NIEJ|~!#pw*_hkm(Rv)Oz45dE1v(v>A~kJ0BfI1muy|8SWl31fRdY$qQ$i{fLOm56A%@C)Zn>>)Y$?wYvhozZe6!0@il)aDB_qUp$5x zgU#2}jJD||ZG@bPsDf#lf7yt(L3xW_Slx_tRLXWCme4>nNpaQFQ$rOF^%QpKVf*ZV z*h-C>BAtYQkAjbeHmgSy0z*1%OL@?QG@EH0C)|lBX$)*Vz;4T;n=gKFcE??!K?ek# zRji>(Dr+1c9UiNedqN8ZT`^I6KKZK|z8Knh3^qo_%&j(LE<8MhTuMrFLt{hLdw?j- ziCDy^Xs{pqK+ZzN^GCpPM~Pz~_Wbcx*xz0^IVDq>zggykKB&*K8fy|R21sE zn^OFgyg9Yo!{a?9nnIl2b$49~G7RWY=|7C^5tx4mI72xF@0%MR8>zwn=os1c$ndAS zml10k+dY^*uNUjQoIeVgF=b|}GwI;qtp6A>>9*AxHI2j|5%Xteo{l}bqaCt+!{t7%gxz6_nju>3Pm3#o0#@hVvArFa0pbv4P z?5+uMZA=Oiqc?kyObgSa!|{R&_{!frEw({&?HT-9m)FhSw*BWgvPvXGRDJEo1dFl1 zP=8oC{a||!Wkej%tBC}zt*&ur4hOp7otbxsz0$5Dw`LzS5RtOPS~|}9{w<%xmjvQd z3kl{69T%~qm@`R=k}Y(sAxtf1EF5<NjXZ@uqT&37(7#X=L|9}T4DwHhUaaZ2HfR}0g6C0A zl^Mw{le5VwVyvwAn<3`q26K{;7TEfi5h`&~AwVhvOKN&WCL&XJ@p{PEb)pX#1!g$j zp-I)QUT%e(sw4ruDJ&7P7I476OfQsNR;_l2rUVO$MjlpZb1u@wV{F&LoDH1_frh$P zlz{k0c8PGxPjkHJn)y1vB0h@5^zmD1*oYP>)lPv@dQ+0d)D+oy&6mxJ!_X3|db`TN zq{?DK3gF=o8NiV^R{vHf^r;z^44tfMyd*?#S1I#mkE86wR?8-yZEX+7VQ~vsNN zaL_iVc4%EDcWF|lFl=)o39MptF|lcCon*gdt=E=vzsQTh?T=|*OQ~_mG4;*=?tdlzk;g`F!RB5-{2(&Le7@=v~&b!Z-UupxMncy2PR6`IbZHM$pHwNHJ!XaJ2yjD=%Pf#GzkI7~-*&_V@Ml-_7@i$X>pPUl%=rgM46Jk-9T$YhHS`7FSU|Z zu>WoD8ub^yc(NK(+AS%E!bU=U@QYujU!^<_0^)Tp+u>lpLuKM3-iYna76pPVkpCqS zohSQDPV_iws#B_0dj0&)6g}k0keB%Syc_zkb@`m+H^xRbJOjR|8(sbWFa+vExd#h~ ziObrw8iw!_k>mlcSDN}(89S_3QU}#GBFVHRz`7+{yC&h4qfu#lZ*cS;3WZ>h!ioPF zk{i8huOg(shq0^B8#*>}D!+dL6PobLQ>f#Q7IAr9U4;Ta01rW}2pyT7`V*Gl>R|B= zHycxq<<4CZBXW88unV{nTI3Sto^!eWLQTT_1GtA1UVdMCaK=T@@TK94``kr$TqYJl zzn|OiyCR%b-DyIu*4ASYQ% zAR=+gYpI%!c4foM$jT+KYI-#k!~3aIp8 z@^pQ@y>5QSncS_qx+kI|n`8+MTVnkhdBt81gf4p+#^_udda-ucbPu=jsOy7@^_|ol>{{Fy|G%O8c*Bl`U#fZ!HZ$za198l#! z=vzGL-%&51o9E8^^23}a9 zN-Nv;u9wwM{+ceG?l&I1n$yh8X+gkFw3=h4NVINMKUt`&Zbw?XDhG1A8)K~Q+#X2} zW{FPb=Sk`+cD+Of>HHptUZI!!Phun15iM~H0w{=gTJmsLVwx}R9B?9iBcBjE!hMVn zZ4@Zrb-#laSW_o=IGUm^s?e7!l-AbfB9x&azC!J_kQ~_(_9dS`vK4PYsDVQad)C-> zZ))N|)ya$x%vaW6CSh?+D=2Wd0(tsD4e;HifqHZLG0$T%ruER43`h&3i4 zQlXm3+#8y==U0qqlqyZ#N?*;7<3TYD;7F#j6bmkVlrI8j2XB2X6!20=x3CqPmS%k$ zh%!M;|Bo)U^T z@_n?Zi1odPcfFPmL<*#83&Ih?5dWOT@u`4iNoNAsE$sjSPYvPa(4a`tZCBVuvy0)t z*SGJdKicCVK4V*xH)VKV+3kU~M<)oc`|b)dXJi!>zJE2gfuDD~G6n2IF@JVy^}~U6 zA=Z&9-`LxYJhJL%iOP$mp>o}~#+B1U#VzKz?^2)+>Mik*#DrI zjFrFGgabo=W8asHfqgJBb#wox-}19444xM9-8W^?9=MlELGg+!c-QJY?cTz127H@51{8YGjq-i6u@s**T4Ta;vNYYBs&n0Boxz zqiE@+Y3$VUW$eo(@aX9axoDdTUE@;jyz3+Qb&n)CdrwhtDOlTJ7jIA$hJ6PRG;1aB zC@PT8WWkK4pU;G>!1e*Hgu@?#SQ(^mQJdsX@>^4shf0=L^_ z(r)Fd_=nH5!u;=Y&VSjQg4MSFBewWQH^Ft03>xlOn4|%m)E6{zvPIVQ@TP>7I4oj!cNcly;mIFs6k<3%f}8sw5aAo9hjZrbCXt0Z>V8XhAtm90 z|7xA@b;oJuD|?sg_4{*{V;96?G!*vpY8|2_C+19z-Q1+jO2drTicHi*dk7t?9}g*9 zbYUy}(lTG3^SugX-8I1+7b)^c>7I6WIGS|&dhYqCB+>Dvs1A==sH1bErO9R+l?w&P z7-;a%woIGlU>4()NA zEdU7Q5VrNn28K15!vZOmN&WHLnS4;}m-%^W-JZjg&z~*STey2Wu@fXaD zZM0nnF6wV;D}@HjE3gK)MS+ySyiiN_TkpI!a9$-WLN7e zvKYZKsW5HHZ6+nibs7>d3sw!=mBF;fGWVU$ooxMyy6jUdF0~zEBRcD&Ik{DS*~%wp z+JsuuP*}I_rNbHU3~J4mrBuRNkk3L-+p{h@vgM3xl!`Ra`DKG%QiVLTg{cL4hzG1i z^HG!K(S#A&CXjB4mzVWhfPG*AcbIl;F=o4~%Er^pG)1lC`Zs#2mQ)jS02)i+Rk6 z%a9!Fp!v@gxGEXC)H5Qy^r|@K4z|r=$bu32LE!eF7W-}6M%o;%UHVB$yY%jsE8ca| zeo3<}OEObdQwmapoG~_%aAg5&A?xFM{l(s2l*>um2CoS6G%9AJY|_C;T;)GJ>@AkR zcqQ68)wr(SgQ~9>!i+KFJ2~b2N=k=U#9Kc4p;hZ=p9zIOg5C2 zE7K4s+2s^N9!pYkN*)7r!91rFF<2BjgR|x)m`0nvp`FoAnPfX6 zE&oB(Mr)f9eV{|w$V9aqk0~U}C6cusLgulGIl~ozch~*FZKqW6UjiBe*!kdtL`~|_ z8e}5H>Fm)u>gS(C!$f8dl@yNXi6(4vO%{`8rC1FIJd17Rj`~h#XT5)`jA|cWOGb%wKHn80A~eH0qdnMKp9Ix_mnesrL~Psdv;|8#U(C$9O&# zL8AoQC<5$S@uP_pV)AxTHa(|P9Y8yI&&QJ>WEIsX_~^XJfo?E%lZRJ$XmAZqtgzTr ziJk75*6*(iY(aPU&h7m3qfWvPbB2&FX(e^9W^IQOs7r!0c9%v1?WKWb@X*^<=<^QZ zqfFl0)I7wxeDrP`nfnwR)G2W#5*gD)#*S)F6jfbOBWJ8?a>g!5HD^!>L#unz|h z{M{gTU(w{dwkMix-PZnm!swS4PYexrr>0e%egb0>O@P5w35nX?d2yaWgfq;W&VU&h z1z{ec&Kd&-_ej-lo3<$1zg!wac)JC5Z!}v3*jXcrN_cQ^i&of$`FIo-ib*P7ZMW3i z5n-b>LJS4zL6k;t;VMX#5}2aQ-e}XJv#X$Vi7X6BRci=yrG|I{b3t331O^UPC|9ra zDKR0gD%~fOdZj|KDcuVj(#4$!YN4|UDXtu<(Wg+m=Ad!V{tll3qh-f65&jwdEp^}y$*CLYeuy<75fc-?*6QItjc;KQ|Y9Bi!}@m5ztLes?!NYjykhu?FGW775tXa? z@ZUA{`2Jh_&MaMn6#o|xP~d-95dTN0v#~QVwPsKek#{w9RCly=HZ-y}{Z9|de<-o7 z|6deziJ6rHWXHR#V)`%p&_NBs`*lU~USE3;^hE?))(-Y1IKD-46~_gfONu%@HWB-32GD(S>Y z+SquV?AdLu)&Ek}cGs9WxAbi&^;Nmfz5)&@It@FjYjiR{$-MP>oyq<>-QE2Gn)BDk zEV^8s1?a+ziLsadNvOv5a9WX|^1tybw}>#I^0y}>(VTV^i!ZJAT7h=4 zQk!6_q{`T&nk;{j+v=YDY+P6ishb zSc{U9V2mGE%aG3V2u+7sjzyS4m6?=fCzut5k)NNBwTDxQwt!h1Sm-mX^sxV0hRqSN z6A;$5Mq{w8nk z!mKL`1&%YiGy6yu(Zfq{gw4d$a@SraHpvskh@=yjb{D`-HXKx=P;E!tDdV?%!lkg+ zPhr80drB%vgMDW5V1Z*pW2y?XD%Qk_sp$AaKyeb zd40}Cw3WoWtNYJu608Ue+<9N!p)WcVr&xG1FXgdgyxzK+xr(P+AJjn|_W7c^DcNW^ z^n; zBZVQL8D{GSS;?Fm`79+?OSOCw&!hO?6a7lZ?v$}Q5MWt_%4RZa2^B2X408Ga`1TLn znMh2*to#Qlv4W>wKRP{T#In(}G-~=ka>Yfevn1mbx+Z5{RYF92!v)B=xy{yOG<#&+ zKaM-BR4Y(ym9XU*C0fDQOlO0p62v1+{fJ(q<>1+cNyrXRs6`nCDrj-!TRJ;tXV57f z8h@aY7X^ox#7r1-Y>9p_A6@_7zKEeKvpzOU1hO!UZPHV1FW{<)u|X{ z$*iiZ_lXnYR`0pRy#iGVm51tjah15DcLp|b|Kd%EL2QJ+;x2wT53Gt^o9Hbo=`B_R-exlbd*plQ?QhT*hZIpY+znb2(VaY?bRM`;ixd#Ju%Y zR?!;e6KNwOsYar^sA$YpWc&4~2Y|`ol8wUFByyQ|m1}|7mI{SrZ#xwt>1bZK|IWin zd}RiIvtKNJ{l3L16X&3e{`}pIQU>}1duAa+j&V1H{6cCB zeu&&Po?qhL#2UkpN5>l)!xKykzfZ`mTZbsPXpVm2JG}ow3I1{Ym7EVDzP(SG?-;=|@T%xudd|Sh;|kN`o>g9nr7v;? zFI!mvQx^hV-zW&ei!i1l7=Q)DM32R&bCr=!pI(q z5hl8bOiwmA9T|tg=#G#9VGNKg^e{O!tXqX$(*g^omojt+-Ep3}Roj!-!FJ*%P%)^Aaqbc1_EmWu z=k0-9{Ne4<8v+cIy3gMmB}v@hg6`aiSmoOka^AgQ@`*=E#@58u^vOTbFXXLO>j_{i zOg#RZ0O#Vip#@hG9vdNnMhJnxDJh5`hM!^Vp3DIFKTLQ8A>$zm_kOui2Fd>( zMXj8-IN6-7eMJD`3_vRF*hasuSD*DWt|GcNLwLM>O(`e6{Psk5d=Vn5f>=_ zM%I#LBsc58+bG^#@8BUrCb{*u69l&S+y{9R`)1mqV-xhp%rY^%+2V4#$#ydD`T6_{ z*9VHve7C**e2&m@oj1XIZQmE*!8jPG~L5-9y^f&JWGqW{mC|V+%-v#B`$b9@j1+^I*1nT%w5( zADQ?tVTA_~sj#%$*F>qnb>|+?D<;NQhLU*ABfX;kx`1*p;)>zeGCMn^1Mq0j?J1%hWSCi3a-f7itCX@RK6Pb2?r)huG0tC^ znoLmZs~)6Q)We7#WTf`#H?|%@i|0E?WjF7`oK(vSgrP%N+FW^mf6l-?v+9= z#o&aKY;6eh`uOcINcafxaEW~_BbB@$$_{bB&-Hrw`M+(FOkJ&7oLE>34elZ6kY{`} zMIdXE#At_!SlB@u=3EidBb^A+t@bhe_v-gfH8809r}9l8{`aoezigK-s@qP=qNqP~ z-Q#dXebv)JCxr{(L99e@3h#B6(gHv>mV%N+;1_YnW|j=yER{p7dngoi3$F1p%#{nJt)Cyb2J^EH%2jX_l)ar-fDpjIQ zmJ&@aZ}p8G442e^xGR<2^`6JlL;{FD%)U9(6~~<}RqeVzPG~pxxU~+R>y0uT9J!Sj zbC^+{pZv4yUF@lyuBSPSe0V^ z9AwA|9D~*EBh*wmrSSt(GUA4d=V+cSL+oh=Ky3<>rF7eBVo+c`PlGsGypq+yggsJ0 zJxX1jrbFjKbr3bPw&=QB-ATF^ml)k8J4e$jSofX@;pWp*|Eg)Y@(FJ9j?UTAFJBg% zjBk|CZMJM707O8$zwZ2&M?7UBWT;hsv5b>)6|EkKlaT$AXLonsV5A?z!CJR%l`@ba zXO#P@f>G9Z6@^U2HcKt1SQ_duR&|Woocgm>(z$w%J}7ATD5|bf17qu%qz9SETo0uK zwQpeTSCZ)z$Op^6=&Hl)As(~Eq+@=zR^G;`58&SeSmzhMB^)e-mS`j~NI}45u*=Ox z82a`XW|X`eDRwY|!ptabqo&en=Vit$FMD0bH;QKEqEB;4?vT`Em*E=ddw_i8awH@^ z?x^@mcZoTZC7v-Hvp~HsPJ9LSIR3=-ZV7C~KvXvM2veGQ5w|0T2vboV5=+mP-L0MbwVo-@Qk;|i${`lK2GXU zcGNZB{xE!KBtwIM1@2J4DM|*1nA@fs_3Q;N=HU*Wjb3jOw>~ceAYYNqNO|0Aaq`;a zlAFoQJA!B6_0aL!`NRaBTl_PZV;1N$6mL%VJRg$rCChyuTU3UTE3f*(WX7$yx4=cd_EWaijx&ZBs<(o*USc6pdDi0~z5 za)@TfaO9<%6N)lP6FdyRk+bdI1(*KH89BBhz7!!bzf!0}^3TGRKzNI2)o8&^LAw^Km*Xy)DCGqN%|-$Pe}WN&RodS z)c1KaPni%*hq|11zpm32kPy|E1g2~s-##s6G0?2!bMCBLzFmDd%;YAtex_Mr?)@kD=}qNHAzn&#G}Q+$jGznm8**TW$LO_mABa zxV>F4bG)5a?QBeI`2tK?j+kH_7fGF<%8k~3{h zX1z&)B{dcswib?(*w);lm~fq;kUWPddDNH*H%qdu1>WMJCCM5i-wtcBJjZYWFTj@+ zd7Fpai2<{m6=uJOZyJG>U~BV^A0iBBphTizI4g{d+&KKc%=UuLLnwAOtzM#ote{`a zf}Wo!>C#sJ4C{ioxL>Y}hRC_IreDyam#2Pnlf{fVX@MkB7b7YND&!~jGg8T<2772d0n(SI?BCb3$y(t7+ zXt{x2XjWJ1DogTAxqeK+Y(oOBDB+XUSWGJ%_1NmYPFjvAPVVmIEZl+`E3!o-0jg!% z`aJGMvN3ve_MYh_Yn9=Q&Vi3c2EYenFS=`g!hV%EIsJ_l^9;jSi*-C_`JxZC+ig%>wCmOhbLQst@Xec z{nen4`zGxsuEDUeq8tEN}jZ$qk8zbY)NHRQfPT}mrYrOoMZ+?iF zq(Z&5!j?O3xa+P*=aDOq1)1}SI8`1%>RJ;>Ha)H|hT-*u3$|RH=TP)e8(03`wQC^` zg{~WBDdf!vC4@RH5|g;GB7_H&IJC7cP>52;QzShId=iT;Di;ErFm9wJ2@+q5)Wv*Q z4tu(%I)T1i#oLf&mIG0cp^zvq7rr!Pfoaf$7$35v8FI?6*!wdL-o%`^d1xkHuAHf~ zhvAK?@uKLZGLfPQBHV8ZDX@N#NfYgUx5m4%w0bw`Qd{h*9BV(Gx3i)U%6)u&LG>r2 zDKL;j+Q97-6?M0#Cxz9$vp$2L->U|cyxdD@*l@uR9k^jgO;j`skej=5u$@fdZHb3~ zh;-8}MxJOUJ`@h&dMabmbc2f;-8sqhrn%Y6gfv%&v!2k$#9BsVfS(0*B@_q$ z`Nc8NpSC_JT9oBylcBT};e|U=f-TlT6J~R9@?dc|jvh%&BXD_#wKPc>QH~u7LhyUm zeztCjE_g2%k`c2=dTL;~j(b6`v0#cjI^F?$Kuaj*njH-;fI|B3-_YSzfq{)uI4346 zx_bWp)5$<#W=+GXAoUV8IX{SH3t++N}k7_An7tJAg+`OjC}5!v|G>{Lv>XJQh#Lf2mZyp69E`O^(bA3*5|5 zNZS%!hp{Q|-S(5!N9^;?oUE2I^@nT5O}bJTrThpbpd&=SvEn@3gdATrJ5uj<#FQ%x z&+Q%_0%_Gg7!jL!%ENnof0aS>_g5PJA&G4bfkLyR_XH-3(z98^P8Q?Ey3Ix5Nc)r< zpR(zf2J^5!`K&w#Ph7Ih*2RFTj=tG%J$s*#;7sJ;=0nj>-@sZ%sVo8vH1ao?ecdHH z9JqS>q)FOAe|_qNM(7%PM+3dBgrcuW+fQP>gjTK@xp7M;%Y=$l=%W6ChaNwDBnY=^ zT&v=5{i(m>&XaSn7>&w!_a;G){k2TZSPtIYHX;yke6}M)7{0=7=d1UbS%|{d)M&8T z{3GFcg;o|8tV=#dNd$$3@Sm=RYhn?e$F~tmz2cB5QrI9=f$i4kkZhm7gz=l*gox83 zqmq+OMhNy}v)_b2p_J_o+hvHcIaK(+Sz=raW%s~j+K?+LVA9(k=~~LyH=a3Wq)H8y#YbyMNpJhZ zy9>8%k~-HVtA+2bO|{)f8(bDKB`fDRekCRS6U;p%tE#lHRdQi!X=5GLgC@y*5pegRM*&iOta?mNesWww=pV zBx((e=-9wE)rRh}8r zC|kC&R8<%#KZ}N7`0xemhOpr4_7VT6VNMLLxGUGa~wh}Koco`7zR)E z6@_7fRE;(>9;xKplQ_#snFe$Mv(w=?$q1ZFsU(xlU3{@pzH#N+IjYk2!a!e}y$DJF zX<{95U`8lPEAJs6<;k85uku>zrSt$6357 z$Lx%WSOv}jkKS=pjueiXcsveU=!PZtVFBX(c1?zZ&P(G$d1pZnvVcC7P}ND%PA(JL z4e#*8!*+PF9mQ(W*B|)O-Q&bflzBN4+q9AMrY!a5__(OHrlk{{c)AshuWRYy>9Gr% zKAs2d^oFyApT*++>_vqb=XE>VYk+UjyTArjeoKr=Qg@-$HWU}RP=_Pqse(0cf1=Xo z&5c|gG@b)rb_%8|92rY_m_wGd;k%9BA|vPhZ=a-q&&{Ce&!;g4xh>n1%}+J7E#uE- z4EQ96&P4)#oI8jK0jtiC5t=*Q;2-jUeJhLh2tM__-WZ6QgFRESSbNCo_UBUrWluc# zw?;R{txNYYC)UWrK_?A@v4T@~6$n1>&zG?~+O5wEtR#SdG;D50;2^=&fnvNA>&&dD z0)Ol^>Pk?;30PRQi!54n&mxuuZ z3@Y0LsG8r70%UAT!re_PiJ-YQ9h6g@69*y;b*8q>uiEHh5fLeLl$^&xqeI+JL9PaT zUqAV4Xk^Wz8WI*5PxRa84a~$>PyReJ_G!cH)s>{-R{Uq)Mz%;h{h`tG*Nr6hARzd= zT^YGbc$KL~^I-Cyz^G*uU<}JNs?1RBXXE_T$Xu6p{G*;qC!a^WH@T#6S?7e%>#bKs z_Vaf6K36WzQ0F^tepjRy%^~0k8a4 z@piSCcgi~vwES$mJU`etEfib-Y`gwG#?L@eZZBITUB5PiHeRoNd~0ubrQvL|RolHI zdT#fW+}_E-K3C7L6-PwL&h#&l4ZNtRrvHbtZ;Gxg+O|!q;@YuOv2EM7ZQC|>Y}>A& zV%rs?q8-~-MX%1i=f2nO`FQ8O`#IOoTCJ@<#vF5w(R-i5g_v*IiLhR0Dq0(TSt*G% z7H&q3*#Ug#4k=fCMdL5|lfP5mgwbQ?$Xx|NksVh5=aaW>lb3S_F6CR2Q2%R~mpi%g zb`Q8lD=^!Pwg1b+wO$Ov){QfNV_dH!ND9f52>SC^Og7t_7>41V`=oOv{|pVaxxQFw z;M%XiKZD?LsWVUJH`IvlRc)tPf{w@oF}+l{_r3+;-P8p+QFyn}T&?C$DB;G?KB`^> zdsd_D&sdq+xESH8K4^&5E#JRZ`9TmZA>7@8i0Rjjd{Ffti@XkLv(d}?b5vf2$iV~Y z_H1g+uJ8pLuA`q-X437q>n0qL+2s6Z(zP!%9A|9^ z&;xFut-a>Aa`;TEKc4R@lG_c&r}WnMUna!y-=NFT*BMP78C)z=4xv7BPbwvj%g1|( zkAthdv-Ax+YRuQLjSMtr$feE(@s}>mOiS!Wsg@phVyw$={6Dh$p;Jd`MfcxlHwW~^ z2ifEGyk;pGGKGfVrmWp2zI6vLo;_PnL-$O`kc#&b($BsUe?-r`OwlU2JV#uhyt$#L z*Z-XMm)dpGn7vbkPaS`m-qn9lviDQ1M9lVu2o9oE^VCJN^zaMn`AE}{m@N5(@!q-v93^;dz1-p1+rJx5jS0i-N~^w zWVdZhcW^-FY=6IA-aM2<0mE<}_hvW9SUo{RQc7!~EooiG+!HNJ(NPKhRX+Q>i>d{b zQg)!iepIGAou~(jVkf5quhAFO!Sj^cgzJUtCJPO%qq%)qv&jw_)#=ww51&K{*Pskg`45q(0G5u6Re;VyCYH&T+~nAjY^P!}2;%x&gQy8{q;BG*O#V!gWcloF9h8h)5THny!}B9kwS^=W5{F| z{&B)qhHnw()bigj{H9=Y9h(1<^unxIg;NPGabDIX zr^+OZNSB84>QXwCHmqC(jN7bCt55;IImn4P(hLn*C;4!EAX-O11+=ai?2tQ#$fxbN zETY;kb-DL0Ef@fIbX+PPtsk^-y5!^W8|<3=)@*tom}Q(yNOb6C&y{NsJJr^spw(*Z{@4Y@Si`_#);wA#0wQfEDlGsK-w)hbm-qFsbUif;6X zX{iC!9FLwiX2tiH#@@%S(^a~QiZ8nfslHSLDn+Jf`g-C_lBIUxD*VgbisOWB@!ncI z@HTCFqcx+K`$Gjrd;t#I?!_BYN2vw3b^kK*2A{2~A`S@#W)Ju8Y$weaO#X|zldHa} zfvSP^sRtvA5+VbFqjQs`Gbbuh)|GC8{A$S>n9x?RL7yyxl{q<-32Rv6zO^pUx7>5R zE8}Aw`k3=rgZQrVXVb-(^4&hI3P$q#0O)nYp}W^1^X=woruT~{`WDkQj|aH|2+m6V zXUTWa5*>?v(=G8LK|>f(8;S5nq1NS4vsU30*Fh2LY5oq7q(h`sxNA(tiBF+Hf{kvA zhs5kC>>>e~n`hqT$C0SRaSLV)H~^ZJWoP>IFvlL_7IXk>@4Q!5Z6hyC+9aYI6izfb z)n#|`5}J}h{semjEbJss%pIF$)<-$iGD-=pDM&4JQAvjD0>rS~+u6HT_u3uXtlP%P$q8NfMEgq1Vd&gdT9@fPYtB5xo`ap#nMvWlEZZ7gmq zV|x!|iW6p;bqu(oQnk}}jAzyMPYDyBt?g4|w+|71L<=8&_9e?o&_mWE&b$;B<^%bA6KnsLZ9@>0=?CSbP>%UZHr_O#+_}m;RgMEUwt^&iueb_R1dWD4Y5f3u_dCUf z)hpT%%(Ud01vY|x6c3w-^5$m_Vy9!Ck;1c8_0nR@A1T9_{xj|71u*)tK_S`M!YNGh z3E`H_KJFlW9frs9>mN&;T)&8qM%{INe>5z^P_<2NGicutTf{0kK{AH**tG>$qse>~ zXV@%(D)%j;E!JgSFxRI=Svdoj8h&KoOoZAbo0KB6>j30AYCD$|N+FOL9g$4koSX?4 zBV;GiR`qVgYqo2xYbQJu3kC!;byY+r;oiX=RxruzuTOR8X8&5%Ew-kgh+;IH$D?I2 z`$k{Qfw5_QFh0VWQBC1ppSYI?6~H(nQ(NC%KW@3i9a*W%HXOV*o77O1QQqRV2;^XZ zfpfEiw@RZ!G1K745w)i@K}VZh*d~oZs0+FB4q*-y1Kpc|U%l)khm}T%70#uyOCnQ& zM)0&S3C6tfQnd+~o@wulD-G|uDjvr$3Z=`*H1*izUCw!T|&xP2CcnC z@p_V`4;vPX4HPp=XivIpvL=-DC&obwqSD!A^(s&vxI2o?<~0p9uv~cs^FNf1H3U&( zIdSTmzD*!Y*wA$Ucrp^;>m{ZZ2AON~6#umRk=-+S2Zo1U@r0~BvIAH2H2411I-hXY zt=XMfw$86fteo2OfFhKd)4iSOP{Kg(+*9udi#EE~1^uHajU>v_J0IE%3h&et?-OBw zfUleaLl!}ipP==a*Ixu-LQ;z4M6y$8l?8M7_irzLEeRe(FA0FijaeuQmQwF8t(=6b zWdeyYvZMRjKz6~>AjwIN_)AFGLf$)T57gettSAMES=7i>2e^ZZW9vp@KE&&rXK9RL z)P~QSvV}dv)Wh_xknImB_|7u-4-x=x zEW{lhvUhT0RkKL)hS2YBNW#|%^!4$z2N1u0wKc~)(L6h16D%|Wc?T(;o9I8eA+HLX zIoQe2`6W)Px&h7sQ^7zF+?mZ;9z;fH`;yRlQNdij?YZxkx9 zq~!}37(e9yNN?Qizw|Kw!*$_5+K)j|x1~jG>K9LG#1UvpC0SVC$Y(9J^MDskdMnev zFViez-VpWQ5J>}L5#PW9k`PBa>4q>V8^+J4bGV-Jc%C*_4Fdf0z%qo=g03!VW&+gQ zs?|AUFR{{O7k?xvVlDz1AzQ<6G^#=J$7+RFD^poD^eOFD(QVpwq}E+|jM787OApE& z=guBtgX9#Uaui2NcUBKN0$5+Mlsgu4s)#%QT2xNqHoDGt(I&?&X#!#CGYPThoe7HD zW-#`5dbOFl>P=5N>%tyr_e_9Kk>zaVTe{?xcffeH73DR{Jsr-xV;UCmjxOfic@)_% zak56O7+)B|KyauLY;X2x*43gtHx_;gouhv3IDqJQS{$2qM_zN! zpE9Gst(XcsMVrDJIQyAH7s!)wzk_qhT#l5R7q(~8C6 zSQle}I>r}pQej2WA@d5~HRgM~o8n@F~0f^L<+H12*)xwoqr&4NsS8ptg<^WPLid|IBUOW|2 zV7b1c7cjUnz6W|+LO6!F8_b@wcU{kVP-;^S#WA7?46Go6xC37q*YE|%Fd~n$=q0{| zV--5pCGZRlt}qePk?KeW@D6DbdEDZCWLTIJY=%(YM7YPa`#o!Gu7vN}8J!edJd<^! zdlL6Dofm?oS)EX{Vdfs8!!sP1AkkbT@crv(qCzaO5E29!m^$>ob9ppnF#10(kEyD5 zD&N)6{OQ3lgei%dyU=Ltq-|he*Mr4Ko6|Jl52m1^Qq;T7^^?L5E~hVnKljEcK12N% z=5}0~;jJzHe0{_IK)M0RVH-)VJ9#9z%265Mv zdwd4&s9-KOaB;D5@udCNAx6M}G{D7>Br*?LEmp5SH~RK>(t3aahufw^9nc&2%)YhP zF=eJ?00Lo%7ZfJ$jGPJR`#1CXcd2A_EK{(tGTrx(l>#Y6ib*VJbr_q<4+lVL#nrL8 zpCMSrmS$LQ0pn~QO^aBay}g`JuXKBLzE4*-QdKnta}seRlc=HY9!DfSXKl>o(C~taV(ij zw%*H+JP5|DRFY^m))h3km_*@=OzRa#)76L41C?7i+~nJ170vYv5k`bfgsa|G?_%G$ zEwa!Y)W^uwKB4C^sK^@DzB8BTXo_mD^i?6CWJhGcj+1#ijTD3_zgvqzSZ;lfJfh7A zXZ>8dJV<|{8Xk|$ZIdt)evbM~8pYFA?faX;I)(E5F0^Wvw0V>cbw)SVl1*@W=@>87 z6o;kCFQ54yZ)Pp({#WgpXAom~=}rck;~yK5wo{zBg!0UmFj}akm$6YtL>IVOUXezF z&3h4us=*iEPc2g>T4Sm9fazKE3a#%R8;+n6vQn1vr%+O5y~-?4^v^G8OPsZgL-KhD z@YSJ{C1-Rym#(dsoxA%OCjFgK`r-d z+J{^njWcsD5l?x-e6HmMt*2w^b6DK?09D5apSKkMARC*z@U{=YMx-ia2*di-u7zp9 zFmz<1fMX|uK;JZ}xISTb8aYA3N8F9*DUu4hZ;N-}aB?1MrmL}O5UAn1DIez_A2mOf ze&JU$xHXUY=Hy^%lTcZ75aLFF45BHu=QV&2{fEl=pQUokVz{PlOkQysH}P@Kj-O$x ziqzeh?}fD`^KLl2r8JfIyv=GS($s#rtp8XLn^Jx~55MJk!;%KVQ!l!);wu$I0HNTW zVh=Ot4E7U&tNkRx(4_)U#LrQ`F{N4x{;+l>O@%gf|HRJu{b0%$K2;x(_^(N&x7gdF z{O|TU3i0plR{q)f%T@i)Q67>of6aXn2xKHJq4oL8TOvtq;UZE}QmjZj$!D{&#)XRk z_tw>1l-R_hYl6DK$XQVofy$hYF-JbYE+LQ;&ijO=$8=8)k`(HvCA$kQY&Oc zlVQ>kdwNn|K1p!JS$&v{VJ@uzQ5Tv8CfP;6=uK{4TzT26cC`D9StRy-{Ec zj7IOd$kqNlmBw((=BQ(S*R{7^2l*1|Lwd@xaIS9{DZU}+B+CGjivBCfpyDZaSbec4 zn(?TxM>nh}Q~xZD?|ACDYL?O4Rx^x+|6Y}PafUG#jHFD~Fm<{6dU|T0r5z8v8BgI4 z%V+sXERwC{r882U9&3nk3>_` z+-Zs$6(y&ipTK3AuJc)+E=wJHM4N#GN^f|BG5GbwjG}R|lcuf0?5YKvxtz+YA`z)G z`HOGf*+QYjeU|2r0KZN-00Khpu2EW^N({%%GtB@^uoUl!;^C}_ai6RC6JX8$@OvKo zelKexKe*MtcF;#5`911nZsXBU!JLGeR$A~ulSGpHST};gY&=vmcH6@^q6Mez(CAL5AitRjPaT$z3|W+8M%bf%F52epOE$0#PL$(!XWy!Wjd``TYCjvU$|*th^MW1ch=6 zJ%tKX9!?qLtlp(2lx7DLqU*cAq#A82TrCHUisc$ncE4n+P+gC8<#^Q!i8u@a!h-ps z$g$w&Zj~4XIm4kSk}OUV3MUQi#fedis8fW{(2RCz(o7&1#R=vU6D+hON__m$#Tl~% zIqa>m1{^6j<{jaeMXZ!X0wg@L3u*AYzHD(quQvS{2n@QUP@7bI0l1*9%ED57+9CG zd6Y2d_64@~)gcen$G0u^lA5LFN~<7bpUbFwh3@56Z*yoC%$q4|knpzBBRYYqEYnDK zrQR|7bFYPbMOfNQ*2u{|?z;Buwv-0OP2@J}XRE^~4W1McHmd zi@-`mnCZ_O409Ngk@&U3){D`(@P~Ni=6Q{`qvK94Ug5bBkrWse4g0kLTBGx%k1O}u z7VF|*9HP#NQ^I|HtD4;VLTX}5OR#Ls^L%%P-FZC zS+j2z`Gj@=2P|!P(olDA&Dd+v)0*3v2d~}zw+hu3{{M0x3Y}CuyZ9GzU;nM6i^cy| zVo}lg$C>-13@t59-JY75xSp6Utiicic{fBi$r!xUa-JjnWR~96BfFb$Xl(07?OHfR z6xEOMh61}dkB%)AqTeeclkbRUi|c5eWh=n%4Z;wY1>o!8!1f@FuHIhs9~YonEpW=Y z>ULI=my&LpbWRjh(#p16`m0Li9Bn&eM$O8yXo7;}1SS$?;bCd0@T`e|<)|%!lTe%Gm2g`dJ z{=9rv9I8dW@4S7CwUBdAuqA8Q#Z3e?jf!_Yt*m!jN#%wtC4Z+%~QlXTcne zsp{JDs`?RN7$>H)9lXC*V|EE&jOT|eiUePX--`8THw1GLR|rdv-4xIK6_iW9SM*K8)!`$}#oy?pDPLF6{+89C61I)$CIXHKE+-#}= z|4=MR=cOG7zySl0I3A)+>2#{n zYS?Zt-bDx)F~F31M>qVw1pbLhavbGVg7DY)ZUIOBR$rv{ zwaDJELp9zHAXY*Xz!G?sz`kmd^GyNk80R;?>pTMam^W?(_`bjyBG>@l_V*uld8Qnx z)qWlJ9E{lxGljL3v=3l_P_@NrTo`Op!kqS(sPkG>_gg5KwVP9FuC0GFN9J+>STs~^ z_1f?wXFFFd5dS0H;6k)FQz+qJ^hGcBpi|hT z+JkUr=Ya1)RFPvCYpBLGUV8e_?ITg0%f?|tiGU04spg@^Er8{d8cjHYsK4}tJ58$T zKJ0$|Bocn1C>V(tu%|IkG=$ZWMRs3rEA`932up^wv-TyeCMq`Ttm@BHmVt{HfBUa` zC#r@nzf!i+0@otp+pjvdYh7sK3?i4#XIt~M&-7*z#7?fo0#S@mSXdUCGP)O=QB(o_ zm#iJPS%nUfCcqx|1x|YtPT@2UEbnzv;kHpE0ZV%Kc0E);kf<|5+$&bBF@(v!V$BO( zjuG3O{m=BS4>TuBPb3jER6uT*c60o9V9F#Ke$^8!u;9Wm+2|B!hn0jf(f}Soe}xb} zMTi~9S%xj_#&#^P)W7KCQ|mNos4kaZY`#eGY0KReTrZte%@D>PkC)!d4 z7>3m<4HPvAaC8vYJa*KtwL$y;i>X&mmTUSX01T{@0t}4)|3lgCKY`z^{mob909~M5 zE}Oi&^kB~T+ZT0wtNA2lh{P`_YlMY`HFDBa6%Xv~CFx~SQua~9LF=xFPyP;uE$%}J#Mem$}^VR$1Oa7C`^vmsD5auP{ zH?T7$A*6VMt5sJMKSFM1>Tof!IPom;BB4ru7e84z8DrflhaUtedvZZURE)R^F+*n@ z1Zaa!l|TJi7g>qp*vEy6HjrQ-bP$-=n5Zx)$eUX1FGiZPwNK+y!HL0w>5!bLMVw7F z<3fXrh`EO&u<$kZ1wkhc9%NT~8~7|4NL2`tCPgf?;=@0W86MYw#knomz&@+}1ND#Y!_aZ-X&%ic5#NOgH_Z z5l5pO3ab^YI2d3-OEMVvcoV@%4DB1KMD-gldcKud<_23Njfgfs1g2l7K*-m<3S0J& z3Q3A|owKNV3lZ8n&Li9`;OvJX5F^yfNoqWyd)*xELD#KlDVsWopGdO$2%xU05C@YMzacp4vQwfPhw?zJmwib86Nj7l_1rTj*9u+m?i>zsaF8o@~f7NnPN;mBT}-c*sZy< z<gR3-e>K`jLv?q7o}CYPQ+6GXVIVSHpbtM_~csOq3

tLfa*>M5H5FZEK%z!F}bJrx_58Y}_)~JXY@Ha=ydc z=22SYF$oL>mo}U*A&(4hJru@#5mzPyxK zW0wWy)8i;-og#%N*4D#VStYVg6*iy$ejCvwfHST}%WWW!ob~8b#s}$A?WCmg?F~PR zbdXDRU2zslef0zf*9ZqB$VDkO)L+lKpi(is&g*ORjf*{d2VI?W1g<5-@I_oFu-Fl` zI<=0pmWra?n8_(87R}tV8QtDom#+5m5-N}z|$)n>BjeVcD5glB$>_jfjmmeUi`>TZ2-{(_o-1oMDw4wXI7>mDU& zde;Y9m?7+dY$}b5*+JstB4b9~=E+05@C$!by})`)x|!ut_l?tGtU|&d`dT=35SWKm z>RB?{q^NGx3#k~L@QcO|W~lEGEFdvoT+|QjXIj)duBdAIG8sEgVhu8}+1NWtKHI|%`%c5IZ#vfqiMo~^(8m%+KC^{;c&booGY(W-oDK@UEQ4nrq z(4r4gfpwOm2BlL)R%8V7DC(yTNi<)4MM(I|U2MyX>xjv?pq41Wdxpuoa6AJ$sI9nc zKp5LZv7@cX)+cNMloD;^pZhGlQu{Er;KgN3K6InK4q?vE-~&-P7*n_`we+5ZET$FI z*CfwOE+-0Od)DS9$#LJV>s#Rc#Rp{wX(NCBNZ>6JJI4$~G!p|92J!UVxPQ&1wSD9G z!VA}HF6=Mz9vJF6TEh7%FSV+49I?rt4ISygo`>2<^o19y*TnlBzwjqg3HFC&7=`9n z8dwPtXVE|kHjdBCINl}{xmhSbX_s>Ys}++>A#|BI2sFg?GUo*86yJn_J4E;=1!A~T zRM>0Q@6u{1~#Ag6@kZB_P!TXF0H|S57pD*Hkh&!` zswyou#ptjxkey;{wmn2jJmeX+_~a%``a`CR4Zkm$a!waO8iOQDZFFWJdT~wiaN@ZP zQ8%wTT0EuoT#DTup%Ze{;RMocXSzk2il}O6aPh<9;#4#vmz?w}4O(jG+(pQo+Cbv? z@d-}^J8Kt3matPNvg8Kp=%eWjEF7a^OMPC83bs7a@Vo-KzB?nY^7e-pL6368$hdQ=pkF&?j; zqnwyuTt>cS+UOUi=`X~|lFs^ADF+QZnVFyNS|wFEtz$smxva_Rj7dG1JGbbC*ERLp z+n1SwxUL)7^o?V)j`KqN#0gJzWlmWvKTLO)Kh>39374IFJjTw9qU*&}ketNu2*08l z6>ug`JVMF1PBPI59+{~`TRG(>rY&u^pmJsV{MPi%dRxX=dS6*F->MPu`)p_#3F=DO zn~DI?mh5p-#YRkL8hz~4OZ+E}!o)%~?Z3Tx9||Hu)H%bzoo(jL#X5wX{+v{&KMI3l zNGEAWH%N*n+D@}|&!4_w4G_`bi)PA~5xEm;PF0)bYl|hR=R{KH}qv*o{P4h%vGA>>uYNqd5HOAtde&Y zqOCF*<}e+)rU#H8M1XLFZlLGA>A3%z_kBSK)MeBj^lONcFYM(6~Frjsl-x z(q|s|{tv3Q+5i+N=<{-;3amfm<{5B-3+FNiQg5c5C3iYCHoBRMZIePEapgZLVETbx z)Z|4bzXh4z!ByFqC-Ynkt(thswjH64u`5)CnD7>5qsd%rYT1|;%@??ktmWJ#Ocu~c zl_kF|xF%j|V*jloaE~gy(CinPWF)1~RXm{L9z1eF_uPehsj-`j9h2of36Qgny-nuv z!;mp1lknfjLk7pc~^QZpYb>1IUG5hUwkB3qF?YxhNq5l~O zZwebBcisqB0dn^9%AHsgY2p<-X6~3b6f{&><5WqGHw{}YLFAm)_tAS2ozix;@}xG}trj;fFrtg}#%+`pr}g z(|I_ zcUO!MvZ3uAE|lXV+`@RQ^+%aFyZC`RSIK0AN-5z9nA>$&^}vb`SySQ6rqOLDl4Amx zKM+EnLqYsp;Z6ZNSWhWGodWi-o}Rdd-l9nkPcr?@7KSGkT2BQ~7n>U3y+^Pf4ERsE zOcuZSE;bdCAT{C%S?n3(Im>#82FxS#C2Ll-ymT&2H0K1J3|0-e|FJDmAn`j@2%KVy z{7v6BuTd2P=Xh{6=XBL*V?k*|#osL`~W?-oR*!EL;k5Hx`ZqlRN#0QH0j7e$JoRQ2@sDsgZ4? z4!Qxi&<(@$s9#*QE3DydQ&79wPSHf}J_zUP109axI~72C`3k+Z1+4v@KzEaHri$dR zrvvPRV-JqRMeG~^OonA+Pg9KXdw%p|Ajh7 zznHEk3kL?~i19yX$^UaqCHEhsIt}d4eAWxO1=|!b6SI)iG>I&7cr>9BpVSsXaf6mH zrIDD{aXW9Kb`BYPI8LMKsT1EbH0X6jibfZ1j#J6O1XvZNvguJ@Ij03K0@QLBZr7;l zEiS3G6wpB3qr_|PQO=d`lTX0bK_v%%z?VMeL&F4*t4nP!7d6HBk;MqxijKqBQ=(F9 zH8tHxN@wB9TUGcmSO-TzSbG`L;>aqscW2T<&njah2h}*QC8RleL@1Gm_Z)Y{NQwy9 z3%VK|rPhWX>vZ@SaKNvStQjoDX1gj3gmK_`G8uVsm|6u|qWqD`^TNZ9C)O2uR-J?hj-Z#P`95PZ0pDJ5XBBW)(TP zIRS*Jq`egSQ%SAG!&2_6HR&RnQph@%wO)-Y{_;|SLjrJEu4#o;)^C(`@8a`pwOHITURb7 zTX(Mz4dn+1k7B%;d&-_1pfol&H>gZKV8I($M%6Psv;dFrqc2;A=_}2+)618A$~Pmq zco(VPvT9U~%T8wMg+VkfWP_k_Y=md~Btd>X?sf&65&3=1$z?3oMWLmY)jmAW8ocF# z#C{C*n6ii*Nk!UrYwb~+uocpIa#ol8W=7S-TP+L|XO0nLV!u&a#s#&$5Su29vo>+G zc$O*AB*E#de4f?mU%(w2S}c?`taE3^3R0ABY4lpeU%aE7OsVyuxi2UObw=A6c0lFl zij65dZ=G|4FAjtxXE*f@Hp0uk%WfV`M{u_f?zX#DbH~h28CuR+jMihMN9LF7-?4a) zkbBx$YDv+GS!zd9+6t**VL6yn!WKoRw->pXTcmNUhekapalUx^P;Ek0$;O?EGS*UR z3nmvw!K*sa=Sk89QOC!Im;A6s({H>bA)#PcC65VWz85jeaF4SgRn(z80;jg=D1$nX zh~Q(R8kGa3_URR3(y2!v1dp z(>79%$nv7P@DE$Ug#=|DEK;^Plvr$@$|BjZba1ZC8c>RAlVKTB*H@19pQu+QDEf94 z;-P@pW0Xf>V(^M!Ecx zK5)0p?ZdqaWBRvC{0W)I?t7^x0l%P~4bja9(hWZ5utF;3k0CPO&?fv85~OoUm*TR{xlM75ikm0aXWey~CeIoliucZ@xg;{=7) zZ2oHvBo#xR8Iqiml{h5Y#p?waeXoJLeHxw)1S2LT*IaHn;r?ab<&;pC;SP+GnJf0U zB3nVM2E@)#D^!Xr%Yp+8T-(;gyEjuRcbZfY;3DI|S^wX{5I7-$_@6=o-{V&{dF2JZ- z{;;c*q}i=fdtW1OSOt}_q>(A<-kcaLKGtN1-)v!>J>(32&aXqC(9eZo9>=Tl-1FUa+0gS;LL^PXd}Re0hIntWyyh3 ziK%(tquYR4^{!xS6XnEASd#iW%WRXzKkR725pG7ol#JhAR!c2nlI7^(dsm;=`{S2J z0L*6`#BVt@ik!pbRS{d}-{MSCu__KhXR6NC(qsy0mO@nO&?kkn z%f>z1`Ar)B!p5AFk?6@OXEPU!Y`v@9CdUNj%AD4%qc9ROfx}i*kkr#hnxLSht3NHU z0Ve#%5tWl6`hJewkzYddxD{^6P_~GIV}pSx=jd{yq4W_6!yCcq=M$2(xfWufyZQ~0 zr$qfe;WOfr8|@T`rsc*1r7>s?E$u4g!SI4{GigB0=Vb033#063N9zSd*^;-iX7YfW zZ}0EbJn?i-;-q0K+eyA<(|o?}OOlnvb^c)ewfnN42N1Q`;`uDDSUPko;okAf*c0_J zrG~osBafRctnL?sr8(zmv|5?VUeV$C7MpjMzFF&|R`xS)(v$Qbg=qM8--X@a!hwv-bo6oO z!+_Q{#-xEnlNA=$KV25O>&1I(_~>hJ3(3Bt%Fo=r95JX?IlSWPN0+yrHUxuD%AVx0 z`OX6-mI^zVZYV+xg#<@OuuqRy9nAsFp=b5ss3Q5@ucO70jmeIN?KIZPVDlxU3$ z{c(i)k%l4d_`M7^Cy47yU7vAKvnI78S1rm4Om82%>o-tfU?0CI1bMy8wmpdYnjJ)R zEe|4dMEv_Q0z??x0g*2RBN2V0PP*I4oJ7~8dC1SS^02-L3^2aPb#S~QDy&2_Mx|eV zWz2?fWzL4^jikZq_Sa&xW!0dzmG8RI2=5Ekkzt=K_h5`IFVNi$ z{e7yTg%Qan(Np{L2bxWsuUtSFA68?cLcUaHYK|($ETD6oYJ<2 zC=b)R*dpWInuRo_Du1N9%y?pTYU--gWs#e~^0pJQx8BqVrn@z`ENTWa4D`uq5-hW} zjMPYO-TQx;e;Kb`-#P{d1JnNs21fON=d;+GID0v|GMN6ei>v&fU0ivcl@@w5>1xk( zF()4r5^<5)48;(I3RpIrd1#hggp2tL_r^}7$iyvG)W{*|fItChzD}ygpe$E{!%40! zv+2n^gWiBQh-)mhU{1b`^*7rwp{+(dqPi*LRLUM{vC8&p8#;+03+X6B6ln6l}sh8S>>8)R^+LzM_ z8^LODQWVmCm1&nD&4qgAb*@BP?V4IXD!^D|~KnWFXoX*A|>g8d# z3FWuRM>fx2uf^*jB%PY~!MhtXD>MpHWRyvM9>2&H$B_wTyJO}RDkO=f!JgbcRkf3W z{{^vN`@7t|H*wevv_Jibvu*_rq(G<_NqQ27Abq`H7Q3XW0lJw~>} zKH!yq`nN0dk4}7CYMna=^uFByYz7foMVT*KVUitK2u5+?oJdplzRwZJuc}Aepl^~V zynXxca1ng!QfmJK2KEmM6!pL9^@upwn_F4@m(W*@>R-GlA$=e;+3H|cd~v=3+RPC* zp(BY3mfRPm>H}zrcY<>4)7?+pTUR%XBNBV}8Z)qr0Rn=b$xdt3DbQjl&Plm1hm(^p z=7;AmA9rU=fmB0S&;9+U=RQ(QG-`53appLMQCfxl%FN*7wLeNv!6mz)nE06*{Z*A|(&mF8&vONh~8Q?uY3t z8@AGvMb++vdfEk)>%hYDR5#wlnA&nP%GJXo2eE#=C-}ag9l823FgE-)`I>7>)^J{ki${Ki5zmH+?@)JFMDfNB+AB1nN?6f zx{YqeG+Q{$0ibSzw+G!u2v{L6q7c>MugNLAvua`^+=f%qr>kuuDBonW9Av0dV{E-?jFQ{|otk%KynR`5H!7D*1v2fu`}}-L%v{H1H^618-&y4l%v9{0zDHUS&VQIubFTd=*XE+u2^0$B7Q=tz$ZWvsB(w1WT!q5M;GM1!JbzJ(f* z!rWb1ewm^uB7m5M%1@^+RjxtK>48Hh0sGV(-jj+Gu4el90T9Jsj|W9j1|_kts6`w` z2u(8N`v%yw!|8?{dpmtw&cod-6kaF1)K`5ssrIW9b7c&Xma#|Zi8M3HzlS!Q5J&>= z&BUlbU%5%G_td{|{1hz0Ja#9!H}J@#ddIB)5FhKi&j@azkdC$ex% z5zip$6qB1`Sr>ojU14_yjF(G~J5KFs%gDisi zaU8(-DpM+{1=CJtySGF>u|{C&k~l)JyvGra+}&>}GH7!QPf^X|jGzW-%RZ5|P^WO| z4*88wp^ZmPDFpUL}qt@%D`3(sJ9xu?sOeVgd5y=?j+%md{8h_`JAC^J2 zUa+;r$!wWwnxhTVD_Np7zHymUvW$M79LT+M$JsaYp(G1`gK&DC#r9agfpUVBx#)cS z_!q%sn{OBCVSl%D`M)q#_nzCkLWUEZ*%7nIzdE!54Rlb?GtbY@8(6YY zL$&Xa2yGEEz&7KbXi>Hdyy&#hqu9m8V_;D*5T>w(qCqCQ4j_U z9M4QdV3B=^J#x+@0%K~(=3wBMi+{H*d8MnQLNv7{dRKa(E{{6Wc75c$7rSO23A*E&D}x#5{BIl571f&f}Qr?^TY7*{qAdrIQN zTnQl%vxoq5^b7yAdFMX1+3@~jKtgoRFF7ngN4ka55s!l2P|@+wm)`;xZ!T>|GCj0V zXA8y>tbG%n;J>=NsL^vR2jMq8Xz+9AQr$ZoETcN@@DacTexZCq^gn5ZbDVEBWP30o zy3|iQ$1U;bb7;}sJ^sokgwgMZ>+*MnsIGqm`|p3e?$3{@-w8a-s=qiGyumR)Vr^Kwl}y4nFSc+H66@zjC|kZrH&cmyL;J zV)~evA%E>3GKGaCj{G=8{E#wNn(+%u6tvq*CvQFKdY$Go;6DPnY&rzI-LnKnYg%ld zow0-1 zxxUWh%{BWEt0$FFJB3XSP5ap9&M`j56|eGGMyxDNYUmUy(P*Z4GNh%;$tcRyGD_u$ z19=a)D5(q(HMlii+AnKWFkiWmyKFUq9Fz4*-=L^;@POR2OL6;_FcKbzWDR(prLf>R zX;=iwpjAfg>xO;(Z^h`NWKZ5$6tm>*90Qhhjz@!>kt#`N=9UrCdb!6+g$3z(xk+k` zvTzNuNr3VqwUDd4^bcLd8}dI`^7!uW(&LPc14`FvQT|p8SyV7`S+lO|w{1r4Sw=O^ z3uD&5jO7~fV)3PkOT(_&&48HE14Xi<#S%&<{0+?(c9bzPl2RclpgpxN^=kx?u|}G5 zEP2uRQ)05KT|V?dti&wwiUejKnAr1>NY?*b4 zL$#`3uYjZKP*pO?iQlgoicauj=1-;d32f#D#(8S}AySc&o~~f4;4!)t zGL~#A&d2u~=nl$>h z>e~4VJRJ>&jMr4top#&=F-?WGY|?g$3dye$Gq~tek^R?fSA>A6I3aBOr)PlD=_8u zshokQBghG4xUPasd2H|NlkzYxN^AB-H8^iiUwjmI7)q6mB72=2bc4!qG(`}dmtlo> z^FI|0cpBM_NYkZbI%=&jBf~UXjXI9{itb>UmV(($QZcmESS9|VJlgs2 z!KCCgG>$Mp!WO?@lSKXHEP!4cePR_KUMOG@-b+0Gs*>rF&{z#VB~~NMU9WET_Y2Bl znt4vl?wbg6VojW$|4wqKCQ=t|l%XJMj($}d#V3f#iGW%LtqW)3N?R8+1??j<-yX_tTZV7Bo1=5Kcc<-&2IG97 zT0fl2S?zuVa3Lgz_S2>u7mXy7&7-Gp3a>1ZP8VabAI9%U#?nR#xUKx0c@9C}&2FC&4#Blv{mJC)d-P%KHVS_wk7BeX($Js;4MQ z#vcdnJ5p?Bu880cF4K3L%PjEw4_Gk*)@_`7vN34sCof=0ob7?6o^)aU_P?<;7XFEv zg@0Tj7U=)gdHDY%aLEZeR{s#VkR780+j?7T&4qVNt(9^K>-zPAB-LMKXhk%FN+Bi&Xd50kC_=gAk{hIu!DoHCc5e< z(StuI5(sC@LPTnHs35Rz~*oQ&6=azUar9S6Qv#KTp26PHpH9aLRW zJc@M&$jiEodQB^;n>8W`z(FU9USoe|2l!t)2oZB;2|u%b&AWe~-*5Oyo@_lQ*b&H; z;zSVb1q;4(#eU}}ga=dfUlzBgTGD*VYmg(YCoa|MBSUr3vw#X#Z@(V)3Z(M~Y;2`Z zS_RTSan1_dhtz83GILPzL`y<~Z93Bj*26+OZ7OG33R*N)L?QZ^{DxLjr<&QZx5svq zY>nv%GSnman3Jy)Y1&?G)7B&l=g3_9C|#T6{j*rz*i<`ebqDG6^kY+htixS@haeU7 zNxB2y_mMKdr;-(8U)ut>6+O&(pm4kZ{YlC!aoP4Sm9*&5M1XNBFA ze&QVYdy2@dWOXXgY)9ei@0;By-X7XGx^y(;czp(0DV}xiuI>X0G{D_E=J=MGNx#Vd zLG5Jp;gxNcoht3)zpZM^^NbE$5P*OZ2>vHhZ$kI4z;Sg*Z=5BMA6|=MjjH7pD0~nt z-HG88)sgoY8f+tt1c7CPgXF792DF1EaV)E*hHcz2G`ON)zyAUe@Pm-1=7%OM7Y~n& z$6)0V{AE2$PGaRr*oKg)c`RTcQTX1;jBn(;67N~F+xGf#^L}|b*5dm*w-RprJHvPe z&t2;`aWKld6`meup+Sw<+%Y8_n|^veYr|7X z_W0^=GdyoVWNm(Ekm09&voheoGl#nEb=iv8z%p*8Sw+UFn6ieroH%B0Gs*fP2 z&=Iv*j#juL3Cm2%NfX^E-7*=lG&*vKo!hI-7|}exr{iPVX|&WA@)bYaRdwa!b%9NY zHPx3+YQ&r|!fXZ-XE7#CWr3nUzX^`)#sHQvvKF;U5oUz_r)e5QR})5=O=&yi+-2#a z)0ruQbm9J3V<;5$^>wfq7joP`#tc5kNSckqpLZiFbYoKYpf*|F$w>O4K1B18*pp4> za*g+CQ>kTxpu&Y7l`U41*p$SY$RPA0C`S>3@fK%zVXU#ldRb*F=RQz6QWQN&rQhb2 z!#}W?r99wq(pkuE9?u`xxJGTI3P3m<&P#;I)SQg}tdzF=UYfD|WI#u?D8g8Yc& zJ{by(1^K(c5(|6+;N0eA`>Vl5g--VkB5)w3@q(uqpijG8KHzXB1gYN?K_X_()n=uxgOM>iMX)btu{otc|wCsQk=allg zt#pwM4<7o%?ZQ`Mfj_Sxd|0PySduR=Ao9ZEgC8lykUcM!BQJD!zr!RruR~ZqWXJ{2 zKvT2X?AiaW^w(cG@yC&K0kgL{FuYv-D_DS`sN$q_Az9k^NQM9v+J#i{Zk;LNZ^ZRg z!Y~9hXPylyIfh`2ZKuC%=@W_ zL&tb}c0-k~-njUHvLc%HAS>))Jd<=VuBoP>Ktp;6#Pkej;-i1X%-jj|smMD>q8LbS zYWJ^VSz(JuB*T_lKqe%{b$@*s89Lp&$a}G%XSy%u(BQ9S7A)a*iRV2at&()kDMO`> z&qvhvCBb*bFRVm9EiJ7-?-OcD&eyE(9SN&~CZPS^oFfQ+m}aT!B$QyzD;)gy~JbV&Ip*jcv4%S@ty zsWwJ|SN8VpoYkY2Jq{#k;^i+=HP)r|og%x@EIY!x7YtCufgBhibbY%8yGT`Evw&%a zqPZ3WrV^VX=MBL_ZDs@8!i`uk-&o(CIN#K7plMzg?ZTTp6hSy!706#C(36+3r`dw< zv|kKK$?{vsR^beRZ+Ks2yWcPm?T4=w0H%#I(r+*yJ@U19cpniyC?D zXA=7=dM=qe+Ax@4@ik%sS=95L*`4>(nzNgJO}7Bp5B}Gx*v(wioPLEUc3=B=+YenxW-a{r9k0-uc44lsAm7s* z1arSLx8)Ol#iwc{Nn2M(NXGEY=Tj)Fy;W-y0>U~>UHERNzIVoSsj{v7 z(HZ(Sl*RDoo#PCjrbKflZZmO^bx)SM2M1bh*7G*ABFwV*F6_ikl=gxwmYRE8wMfwp z*jZ$#Pj$?#O|S-oGX_N%)yGZ9((b7yL$Eygcb?RalNRJm&kv%WBO}-t=)E2i=1VcF zr7!D>o-jt|aIeI6e-r9Af%mGVVpoO@MLBtNjUA$6TOgg^*c+E39*5d-$)*Iij}LGI zD$^*ZMA_?{ILsH6c4@4ZW${;JIV0wMDok#g-;vg^7vEZS^R}<*TD{72cC~rnIGg^K zx4yxR4cFXdOXFu`o!GXLCk|@cg~c+vzP62N)L|-yY>>dbR79m}5u_@(iDQgoj);zA z8Q?Rf;3&OEs}B6bO+2&4c|QV(7aG6~-ak0@4?0?DE}mH+i|zzWrmma8W8MwsJ_uqs zTk1K>!jPM}if#Tt-fdpdJN=q4^rBiq<;`hE+T)4usxrdYjwm@>ZpG#BlAKNy40FNX zp~5%2SK3!wEdt#G(E;}@im7V1UE^l3_IP4IJSb58BQ>e;X7)LuHuRd%;HQ#EzL1fS@B-0| zgN@nSySS?E$iB4q2cyV*K7#}W0y_3pD*w8sKZTvAMa&a?r<^WFq9!gPk)7 zVY#_Z;Cz7@vX$z;Nd7A2&%+&hK-_~g{%Vrs;L4DJCNxzOZ23@he_71u1``e@T$RnF z22HgLx+_VVk+M0D{1ZA10~y>1l1d9pdAsLFKtp(pJ~y6EVvcUsWGCQ7K{N0%bzdy< zv^Nfg6(TG)`uoYP$#XgbYy+c{|3s%q?;Otx zw{U!lu=K0h%-rm^T*x_S$41R;4itovL%gOK!w0=?e6)wkWq6PO&RY^w0Mn6wk7&e1(zx4A{E6u0}pOj2zSYc@&!eRLABV(V;@9uESr#x#JqWJW#uZPJ{8t z5ce=%?|8U8!2Z+&s$I8iTT*Np3D5@tPd>?RxAA2h^kJOTB0+u#cr+Z}Q@nzliN(x(WQ5l2%>0i6n$*)mF;|rkRqYZ8y19WFT&PPtmC2F(BuN;vQ-)LU z2u%F<-JTe>+1226o5RdotJoq-41?aT1=%8jb^EHRN&AdHGAqean zzRA-^^8}b4BvwT%w-H=O?ZVl1bEd#qZoo&$1I`3kSS3<(sxW-)!a_hQP4IJSOplp= zAFidOPElCdTZu$nYsj~=9ViSQ#q86WU+DH1ddSO zKOT%CHu*j9<;z58&|_y+Pmw1J1eie}4=o=}qPs7e?OYn8-Bzgh3OkpbS-xq92U}&G zly>oJW_ws+6Heq$Z8DHwUn*5|PPEBy3p;D(I_Au6cg+Wp&0~yD{V;g2JShotBpY}9 z0(nP&lT{2qY7cT0m~)F*!6K|u*ZaMiSki@CzZBF;!SmH>8euJ4PpK*mQ5U*nVW-4$O>%C z=WU9->P`rZ9k0~e(E3~GgS-7973MalxD;cjPaFtlw@=(B<~ED?8>a2VUr>>+ z_?P7$ur1_5`LC+g!R!B4x(4cR`M0Vu3Dy7B43D$m-hbKV7i<2_1~_8~{;%G6MbJ`a z$@f8`{s;K@Ov)fJyZr)rGVkZQ#h>C}gpp|LYD1hLm>X51j_sw7^ z{(Xo=gQf5$xLGA|VB_w(TQ;PWE2O(EqqWK2fI&+;)@YA`2&!>JD5C3f(LG-m)v-F& z8XT5~i$u|O*9lDtuxmy1{tG=0ddk6a`HOWs@S3Hea%sO(q*P$rK|pLvsS2$0MPz(} zhwPMayTY(v2a^}U#xwFbgwgvI#^TmXjuvEPdDAIP4alm--&(4de<&^L>yW}dH|6Fq z8_5a7mG|NAHKqyesF;p?qLzv>q?6s;y+JpdXORY$5Ds+Kl>JG5FV%gdGlkKV(54DQ z4%%nZ18>!5xUt*jZ{s~%x4lq#%&?MuB;Vs6;kpPyURrHMg9Un2;lljqQ-l}e)%L-% z4LS$Cu>&@T9+r3Qz1U5Lwn7C%?`S~fY9N2^UM$*OVAyTNT?n~fcNjhvpBmmB%eg9q zqYyoMJA@~Jb2z?$j9bWOenhd_>)gcWsCp`C1&otRqegqk>z#ow;kt7C9w6jZsb6B3 zgfmQ$WMF1HXKw~?O~o)EN5eq(=h5r%k>)6QA^R(ivr{g(%q)L+sZn+AsZnQ6GYC81 zO;AtpML~ED!-djvK|(~XPa$|)Zibvw^V*|YVJ-`C@YjgbpnFeg`MI{b3PGbfzf(LE zxeX(SHhdiu4hYnf%Q6N(29#@A^&a2#Wo_`?Lv;?7-G2S#& zb)Tv_C$@GdvQ|_Dc@=shqULWtFK??>jo*;7{{=Ky|q9Gb_ zX$PN&X^Uw^j3EnXc3M6q$HY#16)T02W~A@a&;2f^+sBK4fi#B!9Wm(HqW~d=bIv;u z3VGTuwN;jHHx@^gN<}k`V>0+-*EgsPevsCrZ@~FB#efn~+%t?J`vVitTcE;}hHlV9 zwiN!1vRoP?uraO=<2oMzXF>)$3LxhTq+TP5P$id*pRv`}>FV9a$&<&^|ArhhBGz~% zJRVyBqwl_p>OVlYHoSv(|HjrOt=7|R?4M!>-T%BV^S`%?n>1m)6OWqs z$nbp@;-DJQAn}nTgIOiC`oh;ikTrRsYy`+%5fK*-ZLq}?Gm7+2f~RUa&X(HX*}(Wt zt}i!TRZ!*$DJdf|ou$bJgRa7cS9|)D>Znm*g*;{AcmYjA=KCqTE5khlIFU+YZknTP13xnis^~Wlx_;ZQwf;(3yTCEE58zP<1`AP z$W)*#;XHo1P%b>qiCSKZpZ3*?M*fi&jo}mpb+B+C{xqhCub(w0x*|rWaf?qhja+YPm0z{9=0i}bc2?xAa-5Uq)L5=E(Bw=(!4#{p z2qRmsXeK+TD{snNW=qF|^rFvZo|mR-Cz`9xnKjQ-#!LAX4R2ukHF9di%qN~d&O zZSh`=8-p&e%X13a60JS8j=7mN8n_7?>l2x3{D2rzGh*)4dms7P#{1%s<+REaSVcXIbIHCEck;L$-1Zz$N&v-GmRu`>xalk;C^vo+6`m%GyYM&TLbMua!u1>>1(Ecpr(UrT zS30pj-nf>foJ}~P_ARMWotQ{}TcXZ8LJ^0IgRb%>D(KNBzBZ6HU@F9jCEzheo;w9f z)KH?$qcG6y&4c#5Bs-B@qyvccbtzP5k?0$#SPo9gY&hTQ(Jl-$9XoBFPF98GV8!$j z8ugjpBK~lEx>^~2Ve*p3%Ap|hF|yqy3$XsW;9Qo-E0St(Ae)tIZ|DfWZ4<8@TU_Rg3k%Mg23Rz8J|tvW2|fJ$WB z>$AKBQSyci1sP{ScjYu6?Tnm7o=~}<)v>WMQouv$)Vtm?TP2He!y0t(wkJF@e7!m! z4EIhM18uv5a=EDU$P!|Lj662+}@7u_<*&4 zyB|2Z8o%jzNY6emh5y7wAro2)m%=s4eDg}iq%Id$!F?z!>ybu5Uzg>G;{dLKH!>P4 zT~PLT8s?Ql;!A;xip>j`x`Z6=ULdejCWh+_QwKgmk;eQQidoO29jib-zW41aSA6Gj~J(o(vhHEL_Z96pcPjxL{C?(30SY{n}t*0LX&d_FT^{O3b`zjo0aBsnwN641snL}FVx#T z-aQo!3X?X+=Q%7;J4;PGMSWK(F`uk9V0mXrtXO)rx20c?4!aHc6}@uCMt>=bsj)*! zvg>igL8*`amEudb+ww<6jjf22DO4Fb>A~hbaP&>=GzwIQ*7cuXkBDDNd41Zpz)^Ll zyO1hNpzdsh=ncDX6vivtJO15)TRzz(DeA<%Fku*i2CJ`4VI0Dh&wI!1D^UD)gCg-Q zOt}1&0LBNz{^n^iRyA(Ms22%dSPK&rQR=)tk>;X1^_f0bLRH@!4w7r7iDOWL&Cw7+ zmz1V<^aFuFJ<&EUY++374OW)J9Wm{Z|8TMmGMTiBCu?c-YU1q1qwp|t z;oY-5J&CaXEuS*1f*kQOT8%S3*4X}39d6xgb(+}NWw_+6za1mis770Wt7`ao@b3-n zLFLSN)6!pGzxz>$Zf#XP20KY4Ta4YvgOcW#aigD0Mc_oW+71iV?v75zJ(6Q@qXhs{ zXV`WR($xDJ`+w9e5@B(`#CAL#QXvW0qE#nQHoX62b+0LcUD&h5o0jP(&ZoQ(Q{v>|KKfg&Q2a+jn7J6||r4o=e zqERGR#f5o5E}S(v+U$~U)zKd!2a#1TDpeCz)mWBL@-)r+2=}$tBUQIZum8Pkf1U#a zDx{e@IVvZ`wY$5k*h$x>vObSv+R*Nlyna>b)bv!P5NX4!V@%jA0_msZ&_SxBi%7SW9Oo^{J1Nl7hc zf}jdnqCx`{bQ0%RbURAAE#7vq11rz4&~J9ldF@W74T?d^g}ZQJp+<_~FwmZ-D9S)z z3CY|XK)!Y)6Mh3QaJo7E|gbLZj$zWw(!t zw!H6Z7t8948keyOtFl=qA?xQUGd7Ux8%lzzc`!xe-BrHcsc!-Ccv0YROa#oWrK-Dp z3JOx&x_NNpf`tn-c$K*d4?1F-}K| z{@#ounEl7@-MiGsu;NaDbG|`*;?P2jv!VZlnL?i62ZFxyBd05uQJ!?j`Tq5zgwe%$ z*+|Rd++xt<0%!T=K-rAJxBmv~NtmH~sF1#M51#Qdgu7*X75UeKwm zOlJBkk6b*NR|^Zp({dJwtwf61UfKHZWlA_S#yGH+HUbq;{3&>-2)<%0LV~QAQFErn zy~HH@pG3CeQCw3iAHB$WNbWfOl!|!Jq+Q7Dv6?lXJd62J6Mq{|B`C(Lof|RmC}6{q zCK@`NkUk+fs}PqbhF%sS?z!cATLl9I-vUJE(w*xIocs;HiE?O|-NsSgwxMUp_5n$< zeUrJjjqx~XB$`K09-x08d>||Y@IBxY(SSI_zXz1V0p)>>xAfU)znM{TY%dC#7L=XR zw1I2rWbkhaQ6b7`Wh&qmzkCUsus1h%?Od=OHP3s?2;^-Z+H7`L)LVIFGBx4kBjft{ zGn#P=p?!kURX0A$Xk<1(mpn#Ji#|##=pDjnn`e0)+aKU&NtRsM5K?c7HP)OGeIQPm zzMN$;s+oX4N)fP8eFauKuI{n1Ken3t>BgKgy65HCu;p5Ah(@eGu9zVc-A3ccpIoYP zyVr=3A}e=>?ZJGZZieZ4QcAw^EZS>sdz*bdcY&5ZI&HCe+0b}b#l${0;O2~2ExDz& zK;&0<#N<$T(@So^QZ#J=OZvVax*6F9Up|R0AsY=JJ}+~z^N$ND1CNS-0^i>d%42nT z_j&lCMMi}lGlKE*S3sAZjpFbvyW3(nyC)l~a((^6zKv0deoU7CVLVvhZW-GfQqWNV z_!U&VQELXEXI+{pG^0%4<~AFVEWj@6u@PE#7;YijV?xzX>Ty69O6^p8boph2EdW_K znlOjj(9A-4P^r#ZHA9YBg;9SYN(`CNr`2Y)89)BPu{%Rgtj~!?ujUliTYL@zkt>)t zd?)X=n{NQwu7Co69xED)dHu>Gw*+>yYwpmmPE>0*4OVy^=!yV4C-SZ#(;Vn!aF8DS zmskS?Jyi@hGc!N*6e1&EuBhq(lNEwK(8?^O-wyxH{3P(E4E7-do4r{zWXF`%YNVAp z#1|oVn+Qj%P0tIxQ{I@~+z8V=cZbO5*{5hO-|Pq(dL4jp>fsOs^^RiWurAk!Dx%fU zHbV~e$5zp{45z-|e$=JE7ut?`U&MmfXLi!8cW~oYY0&J*(XMSigrRCMF==+GWOjj` zxNe}VI;ti%6>i(Fnt?thlN|qN)U)_U!VwjZd_Oia@uQ&;1}6{ftCMi{G3{;_+tEsE zXwK*`q2s9}fU;Mz?X=SrAN+HT$ZBD;HMa4~VtW@>J+&-dX4jy0%NalSA`@iDf$Yk5 zV~(tD(*|B0U1h^MvivH_zwlD0s_n|hyPMIqX$wBgz_x`{G^DNqH*V`{)zrJve{E>L z*`&5jdg)^eu14#^Xy9}y0}&htV_dzP^qAg#*;?EbcfeG|+6fKM{^Y6Q zdWziQrTwZT$2QYE<2c4d?B*ctia_|@a^uohRF zC@s<^(2*5&1N^8G1EnE#yOP~=uzi)cbw_0%V9_ngado1cv`dGvk-2fr(0o;V-6x_q zuKRdoV6HT3_@S%$)|2R*o`4d6`*?TwCE5H7bmkGxn!I`I-r(z76vz4*F;c$C=(h?J zxza$CK)kmG;gJCG52E`gBDXx1q`%1WguR3ufC;6l7#2mK(~?wYPHtXhnFpgMq+00_ zt{s_V&sHS2WGsTWFO}+@T(A5Tw0BFyjp~1^n5dguje1ki$sYNPbdNNZ=#;I2k9qz7 zY5NCw#A6?(Ua6ID1;Ob4171*g{#_rXo1K9A*ZV(4%C3^*;PFoRbacx5f118{WczpZ zH1+%6Wmiyf$Nv&~dmVsNp=A|zQNaEKP_tsy{;iAJRC4@buWHC16cBPqb}h&o@J?_nyOTd8u{-UN9>>EV>I?IrHuGC znxD8Hm<9IES?++^T#@bkSve&$a#m`LK&w*%|q~fDKQ>fA6`gh@EsK!{CZOLyi2*5#K zc|C?Gi99qz{ngE9wgXAwuj}ZwMDaq1oj?N#U>SF%LR@(b{8M7LG4X*x=u}yjY6}^A zYf5K&r}INc3nJ20Fj5RlG&yq~X2fO|Wdj?XScHm$gK zsJt+*(01}7@~TL0Dh$}gMF?`~;VTnWttMgTKd*edu{W%-yJQe|HfCoaUI=Y?R#&Ao z5not5L3w}T5JcNlrBi@~T_Q!3$20Ip<1bl{K88^s4lmfC+sbNDq?p2m57aF6*T+H~ zrB_T!pzXDy&je2?Qzjk?etFyf?0yOzdm}odU$NB8R7A_7g3Y{ptGp-0E$o@PK&xKv>Q^KG9+{C&Oi#i`+veyu-;IZ(@oP z;jO`~fV-EgN5^UoLhlA-oTmqJSC8cV)}aBbAE2f@a-pl$D zd-;fue%M7%JcTjY$BdXRX^WUn1T|@cVZgeUtP#LnNW|l1=6T@^{^wvVtbvR~gzLOHdI^~B^X{5k( zmD(=hLVwQ|9n{FBGncMhehP9=p$R-BDA{Gw7_g(L%v&SeN5U*@69I>}15`CSNGfAE zr`2Go!oTlEf2;y^JYYbxx&rs0QTw1Q{}z~KnBm%t&22h6*|7DAzGSEI1Nq+q4OcIi zX+eO1MnV54E!3ROf!OF@HxV1iAv0k3j)OxS9D^79L7oMDVr7-(hXFP+Bo&^Ff+WTI zTz(qA&byepfE9}RBtsTBfbjG08JQV=;Wt7dSuMKSc=pI`xmjq@>-Kqv(MObm`P|ia zdW$A1a3xdbh9uIl661LTPw@O0r z8GMD(L1rXG+{nh~=4R{B=dl4l3=~3<-$nfU$+lFBSFUx0MeRd^JPO^t#xy9HTF4>v z#zD?u>pq9otw@~uKXS6HZ(UkWWlCzEnx(NP&I`u;g$#+(^0gfh&FMQ@k1d>ic@}(I zuYgdXE>xZ5`Wt1oY_y%?QmvdAsRIoba_3h_1H*9W=ajFXWl(gtK*6|8RG%EBc2+tj zZ;{2u$?>=a?UJ@>=DzzKefOzsaZS|9?5`WO(Hb?k*luYA^M*q$oxtm$_OjnvS5x$S zW{*~a3CY6i397Yb0H2Jj>yW9O5MoB{!WB)No5D119N+B9Q9O-jrmE9)SoTM=ka<)F3J628R+u;q6iv|@CB3+ zS_hqt{A#{^QCV#gFt>*G6xI~i0+CTAe0YaOZ-!!?_^C}&l(8IG5%ows(s*P9w&XnnqF#!_@zoGWj}%JjC* z92MLxW9RFnvv~n3njn zuv;7`{0Lg-@_{xmIbrO!RM)pceXbDWRpX4vnT@+Y)ON4SaB7?D8s{`_d#}9!hm&*}hNHHR`3HsOt zPv!zq`4TLODP@3txLlY0gGZ;yxrRfPEP=VJzuMazBejM7xlxuRujwFc%-b}zo1t)g z&xSpbN39%F(Liv2D1o|r3SM;}P@EM}eREiwyyh#ia@ z7x_a!PZ68u$?pMJCHYpCuF6w)K%PTu9d`&qT|lZ%kCS#^f;ABS7Vewl_p^HQHXpY^pX}d zCeiaabVC6Bs9Mb>?6f6!UFDE~LKwFiy^i!(Ot1r(Y!z>! z5o%i}vq{E>O-E;0lawNyVrQ+#<62v;B-s`3zvc9_?8cwOcr^o%-WmFvXQ#3$AM|7P zUd34;8Em{$%s47+mq5(UD+egT11HeNEgh2e6??kH%cH>*MIvn`7@9-_OTBvVDGMMe z79qW|TGRd#fs~7(+GRA_KQfd$So5STwmSoXgcSnO zQYu4=OdY;FDD8&(jg_wRSNm`Sz2NA$G7GSY{j%9L7&bwq{n^EF5`saq(^O51zXZ-y z>R7>K4eUq~`5v)-8dLZ~rZI+!!2^ED*P#m$7gtnfES%N6F7ggDj|1y7us z2}EoN5w!W)28b_HM$ltE{a<>6WOPu$hc$DFIBg*iI#Y!2q8h1gLzDg|JeRbF@@d6l z#bHD%cjl>Xz}t|0$j4}R>h%?(89f(JyvW{$ke{rRg#=jM1#Kn2eGGv+GP~I9@& z*AgyM9M{%^kN6AeaB4xqAjrD3zPq&+jbs{J7CsO!RJ+EZ?*S5}3w820I? ze}vp$xUzb}y7tdFr^!-pIk2jv+*1fRJV9aW%r{iH^#)ha?i{tO8pBr5=r#K7WcS%p zaoYdJER%RQ;PAcl4Az9_oQ!QFbal0UX0Fcm-?JdSEpsC?K+vA`ixx;Ws*kqL0?bC1vo)G)s)rh}iv1H|?-ZmHA?CMDP{e2<+;`t^&%e_OF z_=)I|cz63$ntw@GlP{^gO1RcXlz6Fim3V1@l)yLBo`<0l=bR|wyR>+&ETXGTCH6^f z`=;-dB4Ll6>h>e}#+Qd&Avf0+1?2532*d^iLg9fak$3wM3H$Ac`M6`^-lk0a{);EK zz-V|Yj@k#2vezLJqwdg`QK1i3#-r+jC1OPGE=5pnp7^Ip7Irgs6+hGJ!ag{st90Zl zsKA_c=I6f!Dq>d@@wb8l0foZ;-}_|qfBEDKCu9|rpDmAfjqCqIxjYMyBa7$H4(RoDJZtC3TCY6glj^sd`a9vh0nm432ANEn7m)vDdJ=FSlA7|oCq&7A@MVQ7Ic!~z)6 z5%chEQHu?x|VWs01H zPyi_%UT=cnq^Q5ao-sr{8ZN^YeK6TY36^sAfiOKwDQe#@%;Q57EkWEsF$EP{4V5RU zRK`Y`^ce|A+i0qkBb-i@Cp;7b%p(r3TFX<`H_{fUO{uNsfc(#CGuY7%l1k%J_@J9w zA*H7vQ3=u`)1~DQX0KKWlxa$Myg?zDYm64$8Vpm2awqMl0>}yx5}wIjKvI+)L!YS= z;~Lh=Vqf@V68L?{$M0pRoXoL5xl%})n4O2?rnZT*ruks|$^j4tbYA`q-70^m!gZ#pgU2n02x*;~~c$M%4PJn(Gf)3iy678^yp(@mMv z`#j{>EkYi-DI+%pw!yne0B*|*A|&Y$nREkEfK);zJsQ58iPlw$?1K_ zwM_O}e8*JaRY7c^TUmQEZA93HPD%V>NmY1Sjxl+JWy;9<|E66^($(T8+q-JMBU|kKk18?s804XFmGOL2c;`de~Ap%`o(4bSj3athh~v zt8J&Wmf1)Kc8&bJO>r*!shS9A#~me0e8QDgXsLGpLmibaTg?IN+_v33bBB+Dlw_n> z8^xXtyr?h%UHuR+s)ib6)&W(k1(vB%F!l`+&mrncS-g;k_o&R`E2Qw4m%AR0V}K}Y zJVt(c)nMt6Y=q|d5QMP5jq$R(gXC|qTSOt7>BZ`fgY3cR1sK$pmIBF!nKRV6NP|Gr zUce(9fK=V_rF<$x7{)h-j{6nH3`4uh8M)|@Lx`||*h?5*shQI`J5!b_{7ikby24?g z;0Vzc9z!?B3?$f2JPX$7-))j6v0ig(L7<%`s;<4T zqUMDN9f+>r3yFF{z`J;&p2a|oK`?MeY*{07@U74~y`szSf|ZZFKSvg8(B@wL1_$kj zG)nmRNQ5PuCqC-SFGNg^(#OF3?4m2=U_&0wV$m3;B7i1wpiocM6GbnCl}ThqPZZrf zi)wvi<*d_>{ErU@=JKAKSXJ>?sQjJW<~=uwHpYBeWwg4m!V&USNRI3ZF>=*u>`G4z z6Tp@nN=g9_7R>1M7zqLKG+{LAntht9sKJ4uTFu;m34J@`zww ze*AKFvZoR?oitq6*C{sqhMu3zACOx(JFwGxN52p4bHWQPu|6~pDk?=Hx5Hj`q3uzZ zHIkiqokB0)G>FG?DwVOu9^A_vzORuVhs)Ga`7SI_&?TyXX+PW?hI?_MC{>;v{ zLFr_^SZ_;E#1&+1YSgY*RM#U8gPF>nCM6Yi7Ix!iW8e}h|16}5rXe5<&KVyNmsC;MlWvz87K+v^2Ju zkKMeF5bf$@l=C{|F+KhP7#b3>pNlT!qQ~q?{F6bgtWSx)D_F^Qm9TjdZHXau{@C%e z`vl0H00WNs!w_enjB2F2LL!DxRpXeSGS!GZ-T^1u!&itNEGbo%*-AItiY*@`06Ki`D$A2*#Ej45{ly5k6vLJt?UdS%`YrdeJoba>CTm|c+C>x;`BQ* zLf5EH?yv0p%x-+9bNzlksr-drQv|wPsFuBm@-)gCZA)Wp(|IVU`anlOFm3IZj{jcw zc3QreKWJ+;Hc)WcTg$RoZ{v!Sc_~`9i^0F<%(Opwaa>?aid!wIytp zF~2h;`h>Abp#Y84irkF4E3)O{9;i)6U`ojeWA|PQA3RHRTba%~b!AR=RTWQDeC_jK zVH&G3Z7aL$-bKaF=|!VsJ&*`drAmyVUQNN|VB=uWjq}b##}(AQ3CNj5>W|8~)rT0% zo#uq`iA`MsJA;l<{d@gjfbI(Y2s9w7mSq?Pqs3Wai`32?+$EUmL(IRHa9TgSu-}U6 zjAWd#%AN>E5Z}sR zs_{>c>6i+lTis+_bAMKbtk^q?nBIIgDi{Q&=B9kO`Y=V_0%BF<(Et-W^*)cFSmSs; z@v1IeOhjo0jT8NVN+`PiMnxRjAyz`?si&2hJ=_(9|G=NMljTL0&n9k^9l^h0J70$dJ7T^puA|! zIXF{s1e4cHz7-MuFGkit82SK4@ua3*a)84=AX6#{6h^Efj7^88g(}3GJ@%w4jXx^i zc3HC09l^}Jetz1Lkm*;2%0Gm~5iwObU)!|wq`=SJK3Fy$Xv+uYqdY(b=Is_l7e$$~C71kA%y#K**7+4 z)^^**ww;b`chs?M=ZNMhSCWL2aB*WmMPLDjQb^@8o2%yiB zMVhH=gIPETHOvv;ZnB#iaHEmWphoN#RZ7u%DZP4T-vfFK=6bOj9t(tgPl@nu#vu`J z$Haoqe)9MdR|lVQ^3a!1Stws_i!JE%0$ZR@(=s|jufDSL37&L2Vn26`MUb}Kl|;5T z%FzN`=-eeIZktV19>TunGo^*I@N`*Vl=`8)*I@EX2m?YUi`<@BEKs)Bgk zG-6ir(`$&m_9b<|RxSq!F&%ulb3%C&QynAWSMa>TM0m0)Kx`B9Z0A${v?Tv|!WObG zR1Y9R{`BwY#Bf>sUGjvDaKem5T6xI)8pn!#`nx1Lp4gpSB$ytd0RS7n_fX35vN?XfcVdjoqbC21je2* zEMtzbP2P7^O}^YTS0DxRNON2*A@3Hj&+ZZYMN(!0Qy6a}ogPert_cAZ6dQ941LxCw zWZzV5JU)KVJ$B!zE;X+;2R|)Qioq*ma~vGbG_nXrs3 zeOKnYA_wr~6hw{9E_!m{{gtl>M|h@WOSvLE($`Sn$uYYXuqrew@#HqXfZelcmGW#( zFCbvB(y8oQfimUZn%#_X?4CX)Tu=5<5Y|@zBqxh*kj%rjkaS)d&z`z;+!$d>nt~W4E z(a0E&=EhRRworfDW{S6W0PfrUu+%Ccj$bjBs5)$RN-dk;8GcV0TY&`Web+U+JiYsT z65LiHdO4t1F-2%@a+yeT^USu$wARqq5QL4hTr!H~-KX9h-rrQ=2pKAhNH`dYfRBZL zB`QfMcT1q67f=oqBV{Amqdr0@tzu9_3nHT;}z(8D}seI*R>2QN(%sJ;E_lb?xY1cUr*8{QGSiMqMPaZ zpBt6sF2BzlPsoP+%J~SV&t=ptn!w@MhTt-C+bk~)3*dICXJ(vqN+1)08pOn@0i{ef zj*2v?V_RlfI-h4iy)6q^5hZydmBXUsEguyRHzgumOzaBfj=poL2dOkkiI#<=NvBU> zdsOZiojqk*6g>cJldyic;;OVAx;#m=>5;>;Nyr9b+e|#*?O@8-~!)*_F2lUY;LBV&DD%q{Rt*QpW^qklH{?#qjx@oEugARU9e#3^Q z0w>@ka_cCxU=+};5Lu7yJV0{bjGMa$A969YZT?DEEa{4Q>_uUpgxb2`Ls1B3z*c3T z3;u}<)I1eu?ZK?^8QDE6!p9LQUYPY-T+HiZrbY$so=pc?X=P~#@)?@V;|Uf}apRRE zkax^i3rDxH&-UUun&#Cf*y&r>rQ`_4HK9^GLM}svgqka%cZILd$}P^!VpNPs)ZXo$zLvon-Xk@L&X4pJ$?*(J%46s?a(HR?V<{j< zdT;sW6e*1iG<&7Eg;jiN3!H89%%Eb;D~bhRRsIT{k%@CJoG zBT$cAubBNBd7I2BkQ9a=;}0*i^g5qwj$CgZ2}adJQ;G>Heug-1@P0(7+{~v~sQgD% z#ILY{q%0EC%)LVDN$2gbhI#0+q}gFj!TL_C7D4%YTpqKaS-aO?(;LU%khp7L~h@5a4Vmn_#Fhq~mwQgy z5b%6MXA#T5(i$7A9h1AyDCaTzvF0SoNZ?>CYKlJDLsF{vj)W+hFs=VRw9*qYm~?nHO+Ca$9XC% zu+(0pt?3o-U%3srl{@GLHCIn{Pkq|HdLsmZpwt!Gobs-3TW1cQvWP~OoHRQD9x1sr z1!$S2#@-~oML-eY(|Ul6AyBqx`kUMF_n=X5nrD?3w#NqE$iF|}!Ew;*e1677)JcvK z!{MA2&`A9unB+k0ZkeA5-`hJqZlE!ItimNUV27we;%pvm3?*x+^8KT0CSFMtD0Fe+ z>EbkeB;n)g0QzvTvy)!y)BmjE8s+JS2A_78+azR5AC1lImz*42K&JPas!d@ZKi};3 zwya3(ZmkOm<`=K<0c+*c-^e_&rZF8tfKbo;oeRRDStc3NbJsOK$IYbh5rEfEE~^GK ze^Hi#SYUqmmyU(`d2uo8KauVH|C7}EPxa_xl?(ZQ_W*9Sy#T{32Gj3rwy5lOP!fql zp-U>N#~#2!*bBuq3;ho{U``hCoWAGV)-)Vr1ms;2T!r$IJt}~_*@@3i4$sMCYWmJ^ zzZ`JQel&1F{w=-FGT71u4u>myN_h?bQLkM(tyR=)J5 zjw#(xcjpqJdZr-elvwC`-!reIptjSt~?c94p z6UG*$SAc_K4nvTFTW~SJYP+yw2vfrLju0=<>XjfIaW)A!`Wb9c-WEQ_ZMT~e!4frs zGK?YOQQB6Tio;ox`GHhooa7S%@F{W`yLQFcG*u*6VYYl9Y9FV)J`f^!W*%YYji}-A zQfO+Cv&5xXan*%H#ufG|>?z!zWA=?8DZzf0xw&W__m3D6d4h#itGrQ<#KcS|`J3cs zH~&diUoJLRb8i<&tQEA>U@POku%E+);#Ob+KXG^Jp?20v5J*zP%datNyO0p>sVtRG zH;uLcJ_kiqkoGHJnLd98CIQ;!v+wG`;4aW!G6(R)do1#o38tGDzYj8C4^x!z0%bpqbRkYOLKXVLkh{I5lhW{nFC{1MFW=r}FS!&T}S z*hLJnehwJeA^9*uNgb-RRYTBv5~(9j$JC_&tRlVoPA~!^D&L~;%;O~q#Q$4F%#PMfVpZFui`P zN44#*ROr@%cD$w4=~=De^_pc;daV_DvBrU|tZ7a1;YdPKt661AltM7DmXZr8D?N0e z1-84bqA0#Fg7*ZtJ{yf>8Z-duoik+WU_J>$UUFU2<222ZMhdhO$LgpFfYJltD1WRB z53*uaZEAY*bb$UTS~0=PlZS+4q0oL zmaiZ~1{?9MA5at&D65Q=*NXQ( zCqd5=!p~6YxqSckRsim%Y#dT-c7{-(Z2Dq6j z5EB6h>~*7+Y$5A@L0BO?!d%9ii~a`?ms{`k&ri`$=gWb8^~^!ilrOHo6w6}vlxloZgo<{t z@n2I?mNLWMx#h!V@3@azig_D}eEbzMUPuFLO*)DgIQj{R=E~`NChH1SyEFp*;@n?q z!#vOwP>I-TO3_(5-7&Hc`=v_rdZf!MGv1TpA6N5IxR3i+vR=(sYzXfhg!>*4xR^#% zF6CbnufD0`2eX&R(#9!0!=R7qmOmoUk4JwR2l8SnjD= zCH>#lrChpHyd!%d2IqakM|;?V zN0uP9AnjjN8D);^rDf%3GUQ_irJ_Q{&YH*#INaBbN?*Q{Hl^M??cJ?{&FrX{!ig7( z*rQq;qz`-6)w?T9qyxQ>nt&Ud-uOn@PUGr@z=tG^LIkelb8{E14@AESg~6S*l|?lI zU!;T`XY!jlipI_<77B1s3>BSLg1rUJy70OP!gL&k&m5b^XqNx%gyK4(0g04+ZE zjO@vYDf%8aTqaSG)Z1t}t*#_MdGcyokDrkqENdFD9N~heO;;_iY@F9KsmXNJO=2-U zAfCr3)n@@KtM;@YIHXYP!;mCU?0zJ{WqPLj*Sd9A;&$$#!}9ipZ#P4zGw#QcVP4*2 zK;h$*sq~E5ojTMBn5R&D!Odv(1f=+b1r*ratPfIZLH2cvSL-|J2lkwCZMWXd^nP|I z?K-#Q^&x>@wvhAhx;$V1MJNqxg9bLdEcb^k_PG{}!Ab?QI{_^{IrUJi8r?kIu3ScA z^duEDexB@K^Cfs&T$?UFQ~O17x&6+ZzVUcirKNH8mQeP)v~$jnhmYVNQ#s$fUx0<* ztX!qCCe2lW7K9sS3*yTFmF%eot??@@x6P8;y2eZT>BirUvAS4GP|mi^mf4kaCDAp6 zHH6$ZUVX(Kl$Lzg8BJ$J6HVnu8T&q}fZur%;CNrb8(-k5e-I;Ar(~4#kdY{NbHpk% z1sD_!hDco{%D`ZBXI7(n#F}*JHbFbtL$>O^ zBo$%`Gw>)tOY~LcrY6O99GY*ht4DHeY9hY>#nIfe!eFiHAB<}P`9F2I%$RKcb6Asf zp|GHcGn^~puP0^~PzIygr$>VzWP%PQ<$fv%~Go6qe;)FrGSsI4bwE zFzRM4#RC_#Ns6iTwR`5FXU6jFb0oDE= z;i)prYtz3Gv$0)y=j9U=cAlh#%j0XbwWe}`tB`tJP84Iy!58r`-IW zXAq8vdt(q0T#4-)9l}Al*^0ltc7rCeuEw%Gl;3WIk5ek(K)I;Sx0s{&hiifS)DG;2 zTx2Tn)Ob|S?o%g2Y^V5Al01e8K@@fP2lxSkb$I2L4$BeKYPPtBKHDRM19A(x859%E%oSzJ#_y`LtFr~OcS|3&prvp%yc?b=Y@d>@Pxu z_yVn#_KyOKLjCm?hZTlv&w;}BNcr7!qlG32$}(yMPnY@cd}yvYf3Rw({8@Aq`QrC? z!M$b6Ku>9NXs>Vz!F>uIF}mPuva{CoMd;0~pFmNoZvqv#?JTk}hxG&nk}X=?!ek6$ z$zs!BCnULk^U2?X2T+^u1Tr4;0ijZQ=OfDL_Be7kxgPzgb?+@r z+s)+Me`)8U4sg~Kq3|2e+jZJquD>Y>TDjikFPB7O z7L)JGD5B5`L*~aOcD9b$+FyrsbBUq8=Ryqa2MYEPQ%vLaQ)T+^=}%|zumLA$40e8l zctm+a2=H(1e5REXJwHXnHsVIDsbn|^*r z%?Nr7Q{ZmBgnG^VidQEY-~+r8cL+MhysJxS{;0~P!zH0aLC3>K-lmY8O`v?5mODc` z&c!t!a29T13FO7p(;g&9K`*ix3Ud$}S3{RLos``%)=({`aZ>BZ(;M~v`!3*Oy>`LE zXp}sgP4QrmZ0XHqovjmOrqCiSsn1hdnPYm>Ohw=AFrK2HWN^Vu4=?=DlBFu$%dUP< zBa1b5j3s2L9ZM_WEW|ftDztLwjuKlCY$SBb!O(=3|YsXzk+~7%h?wwW*Fl6HX$~Q#*dIPY#YKD^jxG2RYni=Z48%TL}uK0+d-l;)Mtt$8_UwtY8V6BoiJbm};M$SktL_!;%;32b>c zH*d^IjV_4y5!eZcKRBfNa`GS28^5GNNG&lV7JN;(PU7{J4H9Qrj^J&ZaNzaLnemL& zh8tGHc}dD=K)Z8{3DfEDJ7@O^scK3~l6sdc#_^MfRFbqCa$xi2ly@a9+=J&!!f}b8 z%=o;#b`D&gD{L0jYNT3jMnyATFQKAB&Qt=p1 zf$@#j!esN1#c@GCzm5Z3k`JqfhkDklq4rRm|Cm~df;q!KB%k*otdNj~!@|jci?OFt z>#W$*p|9|W_<$U0E8gV|33n!`$t?E*Xxw#2n0`1|H7&|xRw-xqZumAChc{&XhFdP% z8ZSaWF}Aa14BkY;^Gm6INB85=`B)G#-AZQLAGb_^v%sPm(hj$*GtJe`lXWdUIls?M zafwW@JHyz@0EfhfXr0=M(4=P8RZv(V$?p#?H9{8{@{Snh1|A(EJ#=*qu9#+*z2>25 z8-+wbfvZly$sMb+E9uOBOgYbu*3jX;*yXX3`aU}Vhy&l?&?Y-?U*H2cu_tO&hPX!Y^$3Hqwzmf0R>cy9BT8*%-uhwf$x8Dvt|5WJr>^tdj%C1jN#k^ zsMKg!bPQLxNq9>9HH$$(lF1h{3wB%H(g3tl!_|m>$c+-;lbB~@B6)vaE5czLCe$T~?y!*&HmWzK#=C|* zALc}<>5OsrVE`O$-axd#90&Z$9eG~NI*HO-&KL2_P1*G>`;|;~lgxO#4CoBVoksU) z0oVJ8fctXRaegvBJgS?Ek60&9rZK+Eeu!h3a%HFK11~I4yO&AeV308wou2Yzy8V<{ zA&u=lfAc+PzQ?rM$=lPF9Z<)x>n;b*Aye^GvXx=OCvVGh2NqDiiSy01oxQvjMuDi0SZ3){r6FnRMxA;bKC0aYkh7E5}xJOaD;FJ)?hs^ZX ztB+Cm%^scKk>ixa&FdDJvLD!5^XIt6cD!1O-D!W!Ognt2P z9F|n86gagajcVQ>jP-yb(sIcyJG%wj)=o!v6&j`;rrTiqTSPco0mBdsIyR)-S;vok zu5XMHz@W2zfVQ@_i{$b3&2{_9_Cwdn<;-utZ}2~zw+w-PyL~Mo-Kap^(TtRTAT=Se z!#rOD>Aq9s1ySrEX?F)Umsk7T%@I`BXav#Ly$5~5)FAYu&895+jWSS@=?t|iaP#je zN&!QSf%;Azd4nLxFh~ebNcOF_T<1;`o8{IDdPlX6NROolY|boZ607RdS)kXz{FSVc zj5{%x59ASoGu5W1hz>Zy!qIY;y>>V!zfR-!(-sM&P)MdY1GOUV_#rL$0`5Td_o(a} zW4J|8upJ%)J3_*?m+f6QEMwb3qT^q9M)|@}@eqJ`<$)QRZ;mk)Cq-)$whbmamhh0v zCaGB@?)i7-7TmQFj7i(h-lKEoUW&z)CWRXPP{WVwVJFN7B^vUrLL>EmK2$AujW)y- z{WV*;z5WT$6E#LZ8DoG6aqc@Admz1a&nVDt;2k%hMZm($q`UiPb%c^eyr=}q#w`f&{sL~O z3`Q~`0Gg_*)5RUL=n(S)1N$QX$Itr2{v3KGdmFp3jc&RyI2V@5qTN_vweUy>irXLB zhZ1}$T~Ns0f%-%q;BL-{A|~N892}u{9ISt!^kda&GvoQA<0#x)U|rpxhU#_V@p}*( zOck-QQ_&Qk+^JWD=mYZI)hrx?Dmd&Mlsy^LV~}v1Eb?pJemm4VQxXIKa-eoLC;v_p zV()}3bX}|+pxF*bewq;lNs;idz?zarD&36&V0uM@c6JMqgRn=*$5C-GpAcYfQ7ctH z=Q~mi61%>kT(kf>v-gUUGX$@CJzPV_J(@QgD#@EniOIB=kAk#k8ltR&rY{!yqf1;b z2F-$vSoyBwNAT@%*-S)$HD;Gu*~Nw{-H2M3ilMKD<`neUG@}l-J|8U@MdkCm#?HK} ze9gvwbikg-9xyft`I(#Z#>vZz?;83RcI*CkYUY8-d)aOEj_qw#83>#}VNWi_)JKtm z*4jT{&eB!6sSEuji5c*?5`v|2S2 z;K8A=f9YNKApDDz5edkHk{>JsT$M~({_ZUCES6r$WVSF-Yad1yO8bK=3ootWOL&8u zvnOOvyq4KXs}L(k;bQO&3*#-FZkCD@Eu7 z%|D{UC@*h_aoNfgcnL0UX1}nR9B`bJvJjhROYvT_M@kHxcubKw=zqI55@^>ECJ{OFGyN6sI&_-s?s{`l!O%Zd z_vjyxOn#o8A{D=D8uy(|)54L!>eaM8zPen^Tc~7*WI>y{YYZdHL=NB#wC^GZR)iZ_ zjUP7jUj@=M8pJd=t-Jj!u->erYc*l#@0Wvh#;q`;rvwlMCZ(E|dFB~X3C8U$bE_R8 zB(ufG-NJyY;Cx&?2cS30C0G^&+requkr-QdY}ROT%1KblR@?uU9O1IX*rokMd)%1b z)vv|P=#B^YIW!Qeb>hMWsCD|n`Jdnt^|-b0YI5-59Wt40T@Avn)N7)scRUB#wH zjQgq@n<(-xGh;w)1O-Rdi-HLBGr#*0OL-)u(O)sUb1ocCt*`WDk;%WyVU!BrC_rjnu;kGB|>$0GkNz06K3Ytu?c)7;+rv%v^iceWdm0Ssh zMBE95Khl_~lE;$lKEKRHa5Ci#JiVXumdXfsV*8=i7TCq7BLE^sqh$WbZUvV@4zTId zqcFo$TD4`~vM_Z(gz89@w(nVFkS0>f9UVz6Gc`XQdE^)+vABjX zs00YeS|*fW*DKhZP=s`xb+Kr*;cf}1S&c~CwT#K^tr}){|MFCm_FmywvV6GrqcioG zolvYIN2}I#?2xp9d;bn4v|MG1Ixj6!qT#2v^NuUZGr!Dn2!y6V#ZpsEfQBxk9mz~* zu5y@GjU?d~`OO_3k4g(IS=^5-j*(r^Xzb)GOT!{P>xv}C8y{Tb1Rw*5`VwjBdmJfx9yrhIVTHNFQl*ZqD?%V!%PgN zQEhgKcMuj2$qbwdNjSvk^T?I|=KVlyo+iuOls*IhK$-}33EBaa*Ba<`s`H_zPs7Qq zgfF|;WtdTA2_)W^Ff&dyzrY`s?G6^oX3=j@PJg8?$bv-Q_ywj+jkV{pPO7n$NU7m1 zBy@=^xK25}eY+7e9yl`z05OS)fUJQ1R56DyR#(*~rRo*M%P6TeC7gVGd?5YF?wR5| zOR-L=y+J0xfvtcpWe9c=U(QrE*?z$##x#f$RhdM0KddlbCQH^urt>(8cnE6`+gx@$ ze4ar*EAR0uqlc3>p|3#E2O~C?1gDlrj5>1tqO23j9m`rGtP$quadF`ke?J>?W#>^! zLkRXSqr_bY-zI|*!w;|WebNwGVZnku%G0V|uxE}e*rBt|eae=$TX?hR0*Y-%aNdwS zbOXX@Mwl9375DbB?HD30`geuNT1Z@3DhQy6=~&;=RUVV&^guqSOMYn8*PbPxgFFkUF9QK^Xg zUXX~wewK(c(k)Q$iAr7av@N(hlRV3<|4x|vCYZy4M_%s(d|K7~PT#hx{FK#<$;lvO zUV-cu7p0;g|che+s}}Y zY#XCNhxigm;I@x$ru{Cw)4G~|NbR(~@SY^KJll$6vhW=~pKymz5Y6@%-LVF5KOP!@ z8VM6-h|InEv7qje^TV{2W}tnsLzFI3DY4apQR(dlL@bDNI;TA4!fArhN9G1)5A)<% zBcT#bx{il>t0}HaG2~TKa_Tq9oRQCoB<9DzoQ)V!hXm__fq?ix{C}<0SpSz^%70i5 zi@?KMHzzX*i8@xVrnKlp`~WpJq85h~qhayKX0s{18gMIFTN8_p|26@$@cB2Nx+r&= zLF_@7g!Fdr8y@GI%k=k)=h$43X}vAMNNznGlU9iOOVlNbLDk zWKG+23|c;GR}SB^UBNVAB3ZQU$?7Aw%WMnaMG&_Z8|&CGlr%^wnHNI+%UA_kux(uT z70cz2%WfN%M|#fp7{Jg<;g*DfP?&!CcGd|CzS21J>GB0`A}dQa!CaL|`)H*jXW*;( zc^c-nh*H{XL*6)rBjrO!en*FCi>$U5(M8zW=57Vhc&eUd$#QPEjdKr!GFx*5a>>ENnLgJ# zUG;&%E5!!cLfh>Ia>gxU1Id4OJ7>9N`Kh~pg7hC0lG0 z!vn{~;Z1o%oAHNPWUB3FF>Q#|agzU>V@*34EpFO4N(@&FIj*qYSrkk-Akmzs`eiz0 ze!@J)F>#~9dJT*eL5=77DJ`Zl1J7J>-}f8LTsPCgh4X0(JD)9p!$VYKcefm^e-S!@r{LWIv(8jWxrF>`2+Q+sXQKR4Tfmw zoOaO|UZe}*Io{-8TuxYHs>=3EYv^d9hE?=izP)1)CX5xPBQa;PQ?&W3_fI*?()4X4 zWP-mtXHMjg5JI$;1D23eMXCClPNQZd*JAK?A6?W=oWuTsvV7|#QC0T>vwmi6vB9x- z{V|S-W9j|}m5`VxEgn!O@1__Oh2Li}=}~@3=6MLiGI!k5cl7}O>+BX~FN4g*4KEea2d}DRTc0fo>b8eg)I+5-{cgXVud58?r zad7@|MTw?l^pvsW{%uvWnY@fCGz!!$go~8FR?;)Ejz4u2SlGY5E#azs-$p1N^HKpK zUFN1a+5PF~&o=UkiDuPy*-1sx-jGi&c!LYuBZ(7cA}FeTD8L%%N38@i&5bXNa>+pj zbn0h{?#P^oUHTA;tbQH!EIr<0P7`A4bgRo(0}}yZ-dfn!z9%fG$FK>J(^*J~pNg&)+_X$fo@sdi|I^gLqqk*$Sw@)arPwI$(I;gG0+BU7_) zT1)g0s@)M3F0=6Yv%7VUb2B$hBGktNRBuYG$ot3`{uJNtC$T&Ri=t?VjXYxF-6&s9 zW?|~wo~Hwk#vlZ)W)lWr0`UY}(a^a}6wxZa#IFV@{(CD5>UVVEkb%zJb#17_q5*CX zmD`f0T4UJf;~>QPBU&0Q1+$K{A@?1=JlB@cksJa$Cd-05eri~!E=Mfg{Sw;AF|?y! zGrhSHA?5N#Sn9~n^V*HGHc#tD_dxo3-9W`9(kn>%)&z1=eY!b<0opgvx!FB0xcjHr z)wnXI{ZB|AN!%cNJ>$#cePVOrRv%ta9T>HOxeilUJ@FT&Dx>DeB!_Y&zo3g^82-Z( z*jn=IJW`j)03T&f@cN(+E!4)w)ug!ICt@qK`_`{Ol%8;ANKu+UVTwn&t700MKtwNt z;Mso}2e{#iA%7r(fP_(lfYANFF^cH_MJNC9>UV2F`=Fm-{+^KmLURryWFFxpcmP<0 ze;C3HG#CRs2U*<^q&6E}QjjMSoPVZslbawnJQLY2q_FG#Qa%XcJPV>EJlpoZ;a8SU z>~VKbFnxDv$@qd3badb8{@&Pm-{#f#Js3&_G2|Br|3Sn(TW|T>^T$ti`cN^o7;$xE zu|Vb5V<#~<86#QRS{h;$<4SOFWkY5x;)rDXYiuA(d5I5qlb0lcJ_B^Po5&9o-IbXp zvII0T%BGf3vt&j~p6^Wn1 z11(#3D#@bR@Guk|O2j06v-EWO-@7puwFrl#aa62~UXfc;URIZQOJ z^rdWmb`uLN_v@qq>7ps^5Wi)u>Vv+c!aCh_8W)<%kmRp|P_SHe@BIn_x8^XQ4$4QO z#<>sr&{!=kxt&Z%&2~d{+q$U;`YG?_h0{bp%c#Uc%IJSPt(Z~75dnQ=h%AOKCMwF;@J z0+{51>0QERcKBMC9d8E_ih=q*kidjz)Y3`UDJkeH^&nUcKMO)sVpo)nmbm!lAsdY( z6`^qbApTa^G({)7X-nGz+cjxHr6nrxgUj`d3RU>}ZN0hK`f65KyKV!eMdPI`g+__z z6i$?^381RMZ@{D>mzEhWLfu*+I9O$Xw6-f8V!wEf zHD)L|=o_~d?~h;q{Bum(U+bpGBQyn>$`7J9xGk~<9+J5pjRU$&IRWDR?P!ViIbNIA(%_ z3A*?$+htUgOkDPS$U(CpjEOmt+jgRJkLcK!sn|~w36WeToUj4)7#bs7c_j4>43Hg8 z!e%!Fy+^VgfvhqwYg4b++4T<=k|t3svl0t^Nw<*+`XMFqGO`Ct#RtW%xVltU(+gsM z;`@%%6=)52b+oABljtfZwZLYbj4c*a#jg*#jdnEPZQpeHLGH4?-#qIUu2q==bGym!Cur``!oBmL@qlh?L0IADpN(C3 zd5>sM?#-*K?k{!fod2P+mSi*B7i_I+Fr zdl$D1>jYIAB9#((a~Q0=Y|bne70P{Y_;CM`Kxj9gaA z{iNJ9`QC<=*+YiuEY-Oe#63mW_#SNePT#DY|C)n=c&Y5k{T9!UT$npZJ@qiqjUV<` z4N5Odji>?n4qCzWi~uwKvC(8KEE#uO2=cu=w*902!$^aCm^2GdDT^3Fw7PzTUIdz} zG=nj$LB3Bn8vGcS`uVmF0~Sx&TkJ~eSE&49(c{w4XetE^Jc3p@Cg{oOpfh3&5eK`>*oi)tUHHX{Hx)(s$~TJl<@>wYFZ5L(inWaexIGKr?p z$96{#l_O^w|L00OG&4ficq_xBgw$fbdMs!=WJ$it9}!I52-8|whRTQ=FM$lKX0BRa zMh~xAv6p7oYiruMtbPC29Agq0=+cBG1GMGF*ymw&^iR7)0S$Q+*fy=q{P-k{(`a6l zuwgR1^>V~L^5pWCGu4xe!aNDQcyP4Z&cA;NivYj5iSSWo3o94$T4fRiluTwBi+3O0 z$`yXqF4K@q4SQDG5;E9XdA68tW)=Cd=WD*#4S{m3M(j>^%{eM8EBC?IW!skeukE?J zb3C>*zj;ACUa+3tPTskL@U69H=x;I(=2?lk2n!W zU<0Mh{9u1K-DVI(lYbjsnxf(HrcUI;G=HiFc9>Y^s{3`vhfFNFDQhAq^`9*OMHejm zmu*O)00a+&7s~K3S=ddQ7(^-9mbTeT+r&}LB1ojs z_0-gV-Bd)Xko*O(l$7=rSWdXAZLsSr5t!+#hDvkjpjF^3Uzov`ol}m4C+}qsz9!cx z_WAy#!y}NHv}}T7{=Ggo2B$8sSIwrPsqqT8IpbvU;WEnzVDO|aPjfhfVQBZ*`Vc|Y zk6vL8wC1QUcf1`XRSRhw?bV&;}~_h;Uk9lOG-MTEmRIb|>d-gNFb zOM0`VVaB#`H}XgOQG&h4APjRCxu`2^?8=$KJJ4^87Pl~(FjS3I+bsfxD0jQP7`G%a zYZ*~EduXOJ!^9GC@OP0JUh>e1=kc@!EoD2^_n3nJE*7b1717&yqn?{K zmDO8keOc(jN=7?r2C$Wxw}Ae_ineXu1sjxTD&;UdQTXNZo9M^ysUoh}`2{{N;f`Z< z!a(O(kZ;G^QketS{JWp#1f0a2+jjmP8XdbWJHVggGuLpuACA}ci73Ud_r+A@6*+LY* zgV`*=MEk)qB)et2e5goHMwL$aVO!2m@}8-~)QnN8{6L?~M4q`tBN_1nJ?45?+7ebG zksf3EU5y>kyF04SJ=?4iu8m>CVl6XFvY|>5$2~lH1k3N#N9Wk}rHs5X6omEXz(d2= z$fgc%7je28LrtF4&%ltWJl8-KL%1qHM4Zgj^kKysS zL|bHV7^;Pgz~e3t)MZqhvv|-DUYmkz$iQaGtQZa3P6G_3ki=~N;`sQ$EvNVS4Ja=u zWJdCVQXy*6(2P%K>Ua_8pIHh()ha{z2*d-}J@8y+a44_1vSbfy|27c>G943mpdBv~ z1lRU}>`Vt-&+8t%dJi{Fd3@#H=~{4M`6Xylvt#+?Xi~Fb`4uz84dUHz+f&JEfpKnF zYMKPDz`?=6?-XUH%i(`wdT};=W#|8O6uH<48-ZlS=G=aH&)MwGH#` z+cOkd@-t#ERp9;)XXg~2+1Bj)Bpr9sv28mYy+OyeZQD*dwr$&fW81cEbj-8Xe!jiW z+3V)p``pZn`OK?XqsFLF^{aom<^3p5qoTvK^oZWIfNY=WwKQRL%xdjBubSM@i{^5L z)IUz^XQM!9rxsM(E$shsE+xyh!G&Ve&N;<{QoV(HT<0QD(CUn#m*Cgd_e(pT!?#Wy z;G0v_WE@$f`{RTAeqE>=`!xyA|ECwM;X0-ZuD|ly2mPfEpzN0T@25S&rYvQg?BEw_ z<=EKtaesPA)qO7&UccnzVhIfGhrYJNx+LrHgF1l5EIMC*f;j+kuGdI%MUP8u1sRmR;TcP;}#Bf*x*)EB*7$wIH7f0X_eUvyA_I327^iuoM6&G6h zP8nYwgSRz-n+Vu2H8NkWZyEA^^!{%`6#E}ZYhWuAsI9OChpT8tIbv6{)4#pxX5TLQEtj|)Z&jNYU_Uj zn%KAJTPL~%UeTNwhIZy`TCK1#s0;9i>ffnJKsNl==QSQ-V$ABc@&wBXKRtq1jl!96uBjxO`pe#Ny z0&IZWJyl<195;4Whu5#BdrF^pr%8Je4rU2!+7Ck~oh1v7y~d?wjwFq?A-X`*N?H*P zp6%DL#m(-)T|^Z~=l^()bs9#~=x3*srKvp14EMJ*;!M~TNJ(!m{dJ_?`@oodI|+ps z?|bp$|L%~WFJ=p@uJtZ^Y%^Cc#^^0qA`H-kQ|ydvaLHY1nWNZ_!vk6aqs`5tmto@T zaJ^M~vHQjKZ`&v$rF-de@`Ym)?635+HM%BiduwNuzp0+|72^0Cxlyc(jG|m^@k{zn zGs+?PEIoSJocr%mkHi!SPe>BF@duo7P}s1`aF4`! zL;3>NPnSt|q{9HELO%T-rr}`Xz>DYQ7k0Zd^Q$7v(U&lXwyW=DkC%R+I72K7$~@GZ zb>=FJ{3rvsF|b@$Bucko_Gj~o_6EIP^D?cSZe!hI*9T?L^yG`KC%g0G!yQm&jV+4k z7Qhdo%lODpZo6Iv3i>W&n92zC)g$N;Ubz&&BlBdk&zsdk;x!ZPwK#$3t)}W+#qc#$tv=hApQ}G@Co9Z zcpE)B+*B>FZgDJN%wzI{5r+7; zT+&*^x*spB(>O-mO(6PiJ4fu0r!y?yXTXIm9}O;-81h}no|;?U4BfTxzTD_aFSz~@ z+~&C@9d@1rqj+iL@h+iYDs+L$LJUmU-rPBD{RonnU03^ln05U^|1YRp5`j(*o7$#q z0p2K7dK0E7Eyy4A_6nU{$NN)XrFGfT7q&Ce^7T^p(Q-U6)!^aeOEL$pXL4!3+B$B!` z!x9OW&aMFBy=bj|lx`=Si6c^XnTEefiUr)cue)UrJ9d;X8HxsVAH_aL86|I5di7mk zOn3)s2abOcnK9 z)J&)p$cFmPvAKTKym}F|-f4mKmT|Ah-%GF)gG|BM3p?kxnqr!rl@^kf)7|v}asw9w ze)8_+`$v(qVO9<2j;kR{KDpG4W-LkmK&cVG=zxn^+2JHSE}}Vll%okh3MvaJ3c#g* z8bQqn$_}KWC-`ey&d|qVkFRYlH#~*zBhhlydt~oI9@fXQmetv4ru$GvH52H+K+0Y-ex<=-_1Qdv9bI9SG=$e{qH%g ze;K<-*;)}t8P$hgyj?#*AsiZ2lTwpbTucr0JG5ApTq0X2n1VpXgNb7TEp7Y6738q} zg{yCvu6aeCp~?Ho)V-+s^yK%lHSu2wX`N5mo>Pun*$r=J)7xJljA7c4pU=0i6EW$# z7X+#3PtwCOR{-_ANsGV3guvt(*bPZ+2>lE9L2ucNN&9gWm`lQ(jbkWr7RfetcoINd zLR(1R19YI#zM-Na3(A`8-(iB*PpeIa)W>Y|G z%Z{$(RkYxm9qxWBVU2rfNp{?I%C^5wzD);8d~fZUt;6PSdbh_e%U5FIJALCctS*5TFlG9%N0=^!!$h8Ep zxR4PIPF#0c$qYD^`!hm;D>l!0-#bZbBlqq@an(*Mue}eEye@IBO`Bb3+^vu2085fG zlsWKEpKcvb&U&z3s*j{j$*+8?eN1H}LI2Rpm5ie;nXnlRiLwqoPprHI7lcaPJqwodPn;vzRbu!veE8N^b{T z$2S5L?{F7orwZ!Vz+jWDTI$SrwiL|-0mScuSEIRf?-t`oxPy~$zn}gcT?59!i1KM^`wFPN2l6w%Dbn|32 zK+3&_XR397fLCeX!3)!P2T}IxcpRN91hBn+v*7lQ{G8pkJA^r?$*Qbba^f;CLcs8s zWpYz$zZ_~>y9Dt^3#?{tyzgNO&M`dTj3@Jw6Mju|8n$d>r=$;aqWVPcr7)H%-X8{v zLnB3}Y6|9^dDmS#soXi!of1=KlO(fOczy&;fT$U#w@(Sxt=C+t1(vwJu)A9B@ zDTg7QDM(Yv%0roHSA%%#^udy)$aMaa#d?~nD7e+NFAA)Qu6z0ZI=&)NlU$b-&BrCu z=JSV9zUAZSg!Dba497=Wnarwa4*K+dJ$)X{BHD>7-YrZACf(-QU9HbmR9W3$rWaZ0 zTN2kz`rUWLy=H$n@in>9`^G2_B$2>uyhfeE#mWuH&wn)X@_+J%Q2upRApZ~Kb^oO+ zUllto@r?;_h~jhIrD_cWk;qN%giTFef<}khUk*u&0e{%4ErTsXJKlc9VJSUQ9WJ_HaD-@i^| z%70TD6tuN5G5=3tKs6XI)MMu_KJ5mLsMtp60NM<}Awm~2x{#>49!x)Q%C=gSU+DWY zvsbm)D}oaf4l8~f(E%tF(d&gF^CYaDW{PWI`edOB>&^-ZKfBpEfjtdJ&PQA<ifX=64HmI-fB%VimhWPf=?3l~Afu z!UclaXv-kzRRn<-x_S`KQsRJ-?%PVlND+^p$E^0vL(1sI;Ss+eL!aIRma zViqvuMdYyDp&k&Ap=H9+KL!gnrP960h)5SFJ54g>vSL{eHqLc)>Ak|T3ulf%Dr`2S z>I~KUChtSb);)53!fRdd%N~pdxPRI-+~;k;pm$I+#dDQ6NbtZc??X#Fa*^o!(Fnx| z%7mU+*|`w0k_zN~B4)_6xLP1JV|{PZT6;5MEL-OvH}FmK>lHeG84GE0+Q4T*e)Us| zUtCB%CR^Q{?Bm3<(KZJ(QCLSYgh(LX{V-2Pk|+Xqb8)r+ZUh_0L%- z<;TohO0_ifO1Kq1px3GWDos53lbeayidS!&5El-5aeG&Uzx!mBE@aDHJ>2M)f+b0j zYCAm|%1fWE0AW>s4@fx26KhiyYl~^P#C=EW$fz1_KE)=8!9jX?J@#sBb)L+0sNnc5 z+6ld%8;)j%q~e1BrOi*Qd_$6nn8u;6+!erK%cX0ulpHQEWRdIFS)rf4WHX7KaR0-R z(V8hfxHAc+1r`A(U{@GU)QusXGM7XT$5cub`Npo!a)TUybLU3j}GAbRo9DBut*Csdw18uS7z$BlxW9mErJUeZFs+YE2R=u=p zU%*SG0f!*U?}3EA*nHwEk3S5xU#GqkbMuzvtCqGR^YxF$6yYzSP-(j6iw#c0AXE&L zKubCvVa@D?D4Gbw)MwoZd>pG^c8>065*Liqy{yri2Fg4Z>+az4uo#h0DW-hV88q_^ zZaqj=;yhL?3d1S9Lw5WB(RpX~Nvb9Nnixq<<04{i_MDt{WAfKMHpqhx_usw|K>B1M z*#REO+08~sa^xHQ)DRB!O|JdR{rr!MlN-NFz(HU!kb|S~f%%UqyL9#D?fO;wpyl+p zS+E*N3~De{4A@ox32+84$j}h<0#ZMs!|B|_Cc1L-$5-F9(blt8ozdDeS5uTPTui!4 z_Vle#8u;Yu7TtqBVIcVd*R9N4;t>`_pUI8f2h{GfdE_xPdiP2Z6t0QDH$ z6Lkhc1a~{r%1&G}-J}~lwtj^>H9+RBst_6M^O{aKJl}$R_)Guk?Xs1NjnnJn!;z*W zf8U!ufSZ;ul>goUAKC#U8)t@aFP|g=%`T|@!3zch`o1nF=%MF|9aq|fW3BJg9{fAh zc?yABLPL*lc&huiufd`SbR^-0u_nvqu>&pGbpvJJXl&T z&&tNaOU&x4=%c|RgC1!#CpIP{CZou^dtrY({mwGcTV0Q~)@^*UTzXC~1d2Eooh()J zvGJ+G%KxAhDWwl&>8}=aQg2FDl*>z3sN2rN#_Fn>!Ryi)i@@{NDEzL#yi>#pk$0Vb z>}>x55C0gVd=@*63Bd_5W}URe2{_Msw!25Rq7$z4&t9$-Ff4Q8ZeyVb)VUMC4xpc5eaDe_9E^kK0zF=#w6YTPF zj(+xn{otp;1l5o+(*q1>!XqzT_L4F-qRV+nFLzpV$-b!I(7siQMp^7BzVAneOFD^l z1PH@3>@0gt_tp?Qn~%||qNwuPIR{nES*rT(6Z|5*+AFR80v5clsbyVs_kc+~6hPR7 zbEMHw*lb%qR{V~a;@RYydLSy#H9cISz-eT>4pU$ABxsu#-|EskR1it*cV;oaUYLP`xs=J+}Ij2)PXUU^NPT8u! z6^i*WD@Ku1Fwer6NE%$d_n)9)>tzLZ2ih4^uPzN`g*7yGMC7SgU~|KRite&RWG>zb zI+9Z|)?hf3g}tEJ*0o!cl#G{%BQj3A@kl7yPk{AZhe3#!iG?6r37F4>Xdu4YZRcpA z%~hynG8u28r(jtl)~qYGJsu5EQVxXmc6(C|Hs91;IrtE$=(V)_AbRkMuTwJ-P~$Iq zAISNPt>frQal8S%Ss_eE#BYfhl8M$zAGzf%Cbd4A41a;x&QyN`0CZlMf60f%wqd%;0s_-4?7 z;U5@|aF1M=#t3;L_}=4ue#5Wy`*OgBJv99H<=m8AW3%0bxrhQ?@!I2H0eI|l<`1}ZRdSsndvO@N73=Ae(=r*>F5Ll_ee3JbV zo=?T!D@m3}r?NyXb8MB9XBe1eG6G!Dy4ix_YJHJ0bS%oT%`D5G`-DF`2htVlz}eMM zBv)?-fbn=DkvJ0pQCQf{b$XlnR%`kd?U zuGqlQUc)yWz%u3b1y0ARXA(}QdSnM}Y$xIn zt7EL2;r9CH=Lheb5v1S#;JO_tpo=_gabH5aHFc~@&|ibk(0GAPgt);6%DmSbbO>yNFUxR)*1 zcFM^E{yY2Uu(0jU4(hJ@D0i3pEW{UsrUTovruq&1d(L;A6Ondg#M7M!^}sJvgjDZO z4fJ>Pbz`>YaFp{6|D&t=)UfY%QK2T$htqqpj{3xX3BqRg0M=i|N>}~}tuU4ad=tc4 z1r~ijHGb&@`&lliL4Mso8uSJA9ZE{IMekcVPvx8oU1`u|md)K#=94Y3DiVC!vEZEL z(_CaNXbG*@>e}_X9@1YpGH4yvjLghQUKuCPIwXoeYtwh=82xk!x0*_b%uESVVXQ)L zGi?^K%EM?tZkmz6&|g`cE*@XNPR)IczDW*uR9n?@{}GX1G3A%AqR_qndOUm5EoFH) zG2Q^r{YQ7gd?+9s$E>=#dhfy8$u|zC{X%tSDAOH`-cxsn+S(*BW!ei_V$fYFQFjDM zMedROtTlspO=00ecUtE>FMr3@sMEl5>Idiao+flJc`1!$)a798($udtd2pDV9KVGu zL5&>=MV0KJ2Rj2NJp>?lx=?7)TeYD1iPx@ww}b!I1M`FI%uFNkLAyH5#+7qx3x%jr zfuxamvY4{>K7{X}7VCmJcFc?&QW9rgVoI@hj9+h zY|*Cft^Nc_&gw-<)9srLM1H~RY!V%v(R3y)Rvn$z9gk^HkH%Ejc|-Ll4+K01q&4l% zg_oKcLxomVsG<*9?YcxRy%`?5_owwAdCO93?Dg&=}|SbT<_kf|w|7`aSzP zlyWU?_Wf#W>HCgc|2u+zWIN6{;#}gsfq+mz{kzQg-=e7hN|r_`X*;e9q4H#`)K!&N zC9q0c#3zS?e4JwYTAaP|x4U{@ahhIlZSsA6Ub=#)-lBc;y*rqlb4}m9a-q$DN`oO{K#? zPiMA0+>6;`H)?mEYs_gqZl2D)!{rM6u-3!sqQ4*Fjv?xc-PnE8YH6KcSjGYLK^tHi zrg|D%z}Z6%VbLSV$6}WRV-t1F4=n3(sP0CewkDUgclpE|HQz`i>bjGG5)IjCM5ApO z%*+Yarb(ljGG+flJ;oH7XU^_%hhUOondqqOp1lKSHw6AV818TlYdOid%%6S|R(|HS z4X0bS{LT=~jyY9AGhLzaoqLQj=}o{9RslGI`0(utf_f`)j!Mf!j8$09elz+8vv@8o zA?bu6MX4jmJdzcVa94;cTB%rtTx_E8h{9#ig4e23aV2q#zdv|5oNMrs zOK6FRcz74ZX?1MrEnmLT9d$JoM}V+^bp>#?rIr;~|77g%Z=5FQ(K!akizl2dgW94K zLBax54!I{dv}Tsd6oYT42nH+++K}udleD~md57kmZ4M5@s??4lz7!9BqejS@;Ymbk zbjw*=wZ1^~KJ0c)XQnBYm8nYY2h{lp&k}84X9N|{Eh5iYFP}gg>D>QVe zx9FIq=l!;uy@Eo`_Et*g&?YdA(|@Yb6XlZR%I4NztVRcjzEQEddh0Wlv$)DO4OJdT zccRL7!t+D2P_a-TOiOf4RyjBWCqK2Xn70bED_}ipc<{IaQ4C3S4_4~bNlT5w-BZ9= zV<~;GsE%;0*CilM;QMujkvl9Ef>)o2bXs@Pu&T=>toI=rIew~0E9dkN8@NX(R}2Oc0_rH(cGgAmY6^eFPy0`fZPWnkAB`=Ezs@rDz9u z_l&cOnsH-#L(N$@w=95CE0C^60qVSb3)5{$RPX7s?IJ-zK<2v8u(sJ)mW75Bh7&^j(;;~%%8(}*IkBj_sN|t`p8Rnm zq4dY#{f$7DU3vcPqKEN#;@WGQv!A=0n-}ysngPto+q?dDI$=|$P*B2Y`8mr*&^<$^ zQb1V6okl04l3`sUdp#ohfh_}7qiIi=u3Ip!21n~naBN7ULDjR&V4U3R$=cc5*>JM` zECliQZwBeu#wO0?;>FxRFlN+G>Bh$1nh!mNW;KXW{7F>O#+ZQ^ewPis9o?lIbD5q9)vISVsV37s{0h2;-guEZ$ z3+){$!_1Oq%`S5|=JDz|8r*I_h-tzp={ve<44-PQgIQ$wlR&+Sy5HUOl`!q1-w(8- z)(zq7{#Or+Xx!+PBwG^>9B~-AN?yH-y4#)V4 z*Iv-`SOnuh&CdSBKVRg7-l>y?!f?40rer8nMU6MG|J}H;Zu@eje{I;j(Enf6a>IX= zyTDyg=8*lCn#J0y%>>2jQ8fmBuRtpyQPPDeOH^!G*~DA3wcG2ao^m|qJg24kR0tXl zBYgh$xthMgP#V6yD5ld$g~M_=y5f22;6C1Poa%gf;p_ea)q^yK{5+Xm+K?@`VNVYp z<>_x`hHs}`$UK8qv{NlLZyMT+)Issse88zrudtg(GjCF#WOZXPOcZ}guTCkU@w=+F z(*h&fO1iLevC;TYhcpD~PqZ=DnXEkvR;F`|vE0}@5}cmVu6hp7@|~ojt%*8ECWZ@x z@=CEJZz*3hIrW?2f%$+Fgxa9nu6G+nMK%hya^)o*z?5n4qv6u3!Qj4np%>(B-7+p6 zeD?)8qPQAB8m0;kO6U>8G~#&RQ+eW~mBcjA%hbO$@+6K$ZoNhhoOs268^1xv^0{EE z_(M_Cr`~}hOh7-thLdp6iMY zhA6To<*f6GE%!f;{dwvWK1jGV%{3%hi+l%Xnh-*ybE!uaJY6Sc6Frs@i34|qVxTV= zDMFp_aF{B0$pv)>?>9AYTl_zatMp zQ)g~Hhpb`+8k`niB3}gGSg>_6)%jln+JO40V6a&NVLAbn9>`%kYGADM{6$b@Aw!&* z6sJ!@QN;c_=}?O6>j|<<=|16g+qvBQH%uKd5!S-Vf_(lMM-w%evX_RHK}wHIr;$M( zgqS-JQMzV{W|W@K35?g5hWA+Yap)#%O19sZx8xj!tZOa}i5B~uHEQ6znHpc=G6rn) zEfHcAvp|S#L50>pguzw8J>nqoN^lTPB9l2veP)|vD(vEo3eI*UMtmj#}$HWg(6`P$o|k_YJ>^W zIztI=>7y&yvanPZ1AV@^mTHOMWmd7UNeV(0wa2Qp`T>Ch$mOL+ynsRl&0BQG>rBV( z*6ZviAMe*cOuyB&jCbDN2qt~w>e2yp!-szJ_u+uawyf6VTkGW`Yk=Yit6RI-Pbkr@EjC{s%L}`Eypw^vdbEELey&h1K*OLn|`AHwVdl8{Q${ZGtm}bDJ<1)2% zz=A}|C}|Oq>}e6Mm8UvnXY+Gs=@Hge`6XELD{k?q+IIDtt2zp%4IFLnlyo3Vj(k>C zFc2}}Gni^_+&`Jm{}W|uJ47v?c&SEjypZtmBfAEV0be&|%&_d6)WPtPMKP}66XGm^ zKIYxUvK&|CdfZCqmF;Ny8FiF!lX83Y2AUfs+Lds!Ebo#^VZG5lt*Z>4d#82b+=(mbcIu4=aVe85(I zMG(eBZ&~>uQscozCAErco5l`lScOSx(siO+632BFAXoi(H`^Ihk=3YK9;n^M8qpsf znCL7~>e!+IhH(Aa2&`-0eA}FKzO{!H0?}7*rId{XFjfzwxq4a*)7fgDR2%6sNTPgNt-Foi)GOoU~;ro9c6xD9yv}S@efA3T9V^ zYb?>nq;d$^^oN^@MXpK1GdTXdV~6Rio>N$bdt!8)-Glbq_oJ@eF$&a(pjyTw+DOi* z$1~H3rtbY$Rod(s3AH1~;2IhZ^`9=Dbs7OXZ4>JBJ4s)L)z-mM$=&W>6OO8sEdN#w_xYi@Y`p{z3CG`owqSihOc~55kXk!v z{2h~vkP?Q|#Td2CMjZghbeDmO%!Cb}^C3Uff|nk*1C`J;>OTGFh;!PLGbN|H>l5S# zbsF<|bo6z@Q{8$YqpjDvQ@{8us+@d!oNjUkSTri`o})?StgDDn<+2%n`~=iD7jIRU zTy$qCQVj!ClrN_7gm@M<(IOLVJH5Jl*ob`Y{4LtS|C8mh%Lf0Dn?YDN?N52SdsowJyK*h`0(wawE{Ree-dpyGd+ZbAKN+-2{UN@jerB2QW+O^%`kU4;( zHI2sh04;bgV=L$p&%U5a9j-{N!?YgxmFcgW0};|UIDZRPsk41K+AU|qr7e83a_J4E z4OrrEdFokd6YCg#ilsrN3v`IlAy3r@tCsirUOF02?T!kH2!+ll0ROXn%4>6OMqB3y zVkh~$fmcE=N`$QhFa~NW90u~s-vVg)9_zo50Rw|C(qr<#rCr#u%q(4FTzY-AHFwUeMX4$mXKjv^?rz?2R~y@T0kl3(634~rE46|B z$2U+RMltpem^@1Lwnyk}gJ+f@dc+UtIpe%}B7aQ_)|AVXyWyIX%{cCSi_-nOG z{_j^hzq6C=e+)4eb}_bbk}`L6GPe28S;j?j<1%1OsGSeJH%dnOd(HNFF#SSN!961W zsii{5yHFCx%&1(KMjB$FaR`pPP|xy1dxm5<{o40i*`5v_9$S1m+aNn&)Zi-)FG{|} zTEk(n6R=h9Y9V4ctioXvgjI7)EW89HIqB+FW`+ZWz0}ggd?sNG0>hmL+}s3JKu4)4 z6#EtR%W@UI5(afb@ZY~eDte-W(qYkpChC7vkz=UXp}_gN zcxH%mI!S`P7Pi+tn5dS>GcsvFJbi5KyV6e1H&OcSWjV;K#bX5o|LUqSr5e^Gnmq)5 z$FB?r7Cz~1OKZxxm<~SRqUO4JA8!@p_aZx1?Mii4I_+_4luvSPn1LdsS_@l`fWsUW;pXwgw{-fl$@oSCXW8H!BDl0A$ zrQ0CgG2hnZXez7q$>(Y6fyOqA2TBj{*_WHeG26NS`Dg zm6^@TEUH@%{18a+P+=a|w>G9yoYTZ3vvL!&G<{LoH49mDkL8JpPbuCi9M~}|6}-h@ z)}IAf=FYm?^4U(PK+LmaYRPnTRXUbyJITq>O$#THhzxy2t1C!RK>i_aQ;Ft+dMnRfq5|0KBTJ;>lTGTiws6YLL#lqC-)z{?$zo&DOnp>jEdG8 zPv?k?t)ifMtqCkWt zSQL`WN|Ztf3OMGbK}DxyZChfwd}OoL#%g)o!n&G4C&TAq+2cE$>t5Ofb*u*25UPb^ z_4nhOsY^}uyuZ($JLI?_i4-Cev1*B&ET6TsMc!{j=yG-nq3O17x#|;~Hq1(ML*)^rWAOg zM8Euccsbey@uExEg-s*Vf|#&7Y%KP?b^MATGS{Z6qf~?e7A+nbS$>f_*M&E*3KlOE zndzv8W<~N*&iO1~kZhXDCF`VI0PbzXL{O#%p5AN-?*!e!{;tMmC0eBJdaiyW;+wx! z#V;8}Cr??nV>lKowM3DA zO~HDbyT-WO75kKsJZj(e8Vxk4vPg{)3v@6Uc}n@CRYFAuncauq*XgRg((r5bz9S$? zsua6*Cr@+mcq9`r!*khQl0{~w710R-RWW@cF~wCabJ{D&fHTd;I>)>l4AqXAcR?e@ z@W+_}gU5*yVD5+D_^6_M13Of7*N6Q&UbSKs2uw7MhZq!7Or|d+Zt7^C%6L+Of;p4*G+-L;flNqpA$ZY5tA@@_s)e0s zk@8Rc7;SQX3U9C+REcaoH9`C=nw}Y?j`(Qf^m{5VjnsRrv9Ww{CYH-VMPVG?VY^EO zjjbzb)Xe-q7<}ZIGsS0rsFGXZVC!*x(~6Y_6xj%#X`np?1hZ)xrwu1p3D*nS-EZ@v z#e!z@(w5;T=Sfy>-GXm#bS&pX%T(K?JMtS?78vC#sEM{a8Tr3_BlI6GJQB~u(-lYG zqbWOr9py-?Ft*Xc9=W?Hw3vm0L=%6pPa%RH3a(GR-I>o)0B=7a*3u;jI3*WRBR%C1 z=^zWRIjGFkdm{EO6ADtyva!q7R>0U4oP0tZ1kmW9X!MF+!P*$5Zy%zaU`k$ z`;hl}L7DvVE^?0w)myHhGU=sqi`KQWLl5xDldv#s$d)zcbn6I-)36M8no)pLU2`75 zmP3g$e<;@GoOl+l-^qx^#*jt5TJDdQ_{S@(z}eo()6;!ZdhDx((t>P{FXWzX+KsZg zJG*xhlFg4OeMf_u>vZvjEdM*Og;|g7$F!Iuc{bSD%r8G5)qE>#UPg$h9-N!G`=}N9 z1=UAe4VQ!iK?U8C1y}4Z*c7K$k(;O58sW&*xY+ArFdLqj&9F`)-F94;fnqx%C;5>q z_|DLEECcX2tiw5H))Re>80D}jrhA#a8!DwB6hojVZs+c+od8_>P;OJ=yo03hfbLaI zDtHN%H>@Rb!bq^WS`Uvj#DS+dH#dZ>kRB*P=yiguAxdw5;VY{JiNMpSiNd3u^-CNW z?-CAr9|6JezM)%vY@+PR>9I`R&Ds)4LR9+>N)o#{A0ZTRATGvhtGF6%N(T zdPyx8DJ1KQ7ihucVP4>MP`;gHkgb(V?*lSym{5em`5nyzQMZETl`65%pzf|}pN}yZ z%034mnjmujox_LOFDGjMc;C$!xf{!`;`eqIk;Ly$dM+6+$2cZu(moZjxJdYB4a;QX6R8 z!PEhm%mg=346?4sWzw$bZi#oCPomqL=a>O@wV+=|20xL#t+ECltSSZwE#uvI+J*P% zm+0dv50jb35L5~AYU=2wE5A==R_a8LV{UqeA{4I=!NZS&>%FUNY?%#|V< zUl`0RAufHsBMDdX3>PC|nCI;*GAq1`V!u&lSe&G(c~|6UBx{8E#&foS`I1-Wue+W2 zCc$6-vzE|I7Zp4V0|IjW*DJvPcrdGivyGFvwXuMMxsjA$$$EVb`mii=oZIc$f; z=D(41=ZMwTiM>hTQSDYwgNXY1cvqIUsgQZ$~0H?Y}%X+ z&u9Y>^#tvHEJTIX*M%fZo^HH-IN+>#ACsETAE7V3ye;}pac+N1J$-idJKExLLh50S z8^pR^T4=TwjNn{+F9Thb3rMeEI8h8QkLZ*573W+uqP{?PPH)??cezO7?IG`+f;)p| zDnD|4mLN#KDB4KVp`)veQF5u;$HkP_S(K!zT3t!KW)5Kp(hpm6#MY)4929q>tSnFUe9V zebu;ZOF2ubhL!w5U?5h@bO6z$k%P;ectftdx@1P_?F_oOJ}#%TMrzq*C->0$b~-Q_ zV=2{)JS=o`-l5!O`QH(Q+YA=^1A%*m(>x0JiL@)U$oUa zknb-w$iWi|Hcd2kN$|+%32zb^#U_F2_0olPB9$q|P#Fj6Y)4-Li%GIbW^qKWGLF(rL0RTIwsO4c_? zV0!P7YGhlk3FSIGa(ScGg0xOo8P?SR5llDo^2!aH5{niaOkAKP7P=I@dy8j9;5k-u zvW&-MDu@H+LG=T+MYjdq!cD1~aX);iajiq#xB6;DrMMo$qF+|X=6GlBcC3u3aY-n& zBJfH$uwmSIKOZyaSn)TUEbF2(qKO@&)5MS|sjcdlD^5H{=g1D`Ua3U$Pdg`9%mjaE zh_&mjTwl!wq3C&7VLe6l2I@@36Wp-Us6{{_7c5tWn~>~hJVAa-_Qpue+jNc*(wnuDd4@>ls^sdirWbE zGuAZPGiO}(c{0UjE8~7G{UA zl_L%M2`cd_IWPH)cnCwKRpT@o_j{H<%iB?|pgM(}-aYOhj`?)nSa@Tl!lmDwd zu2LxPnUPa>5$9E42;^F<>}S2^j}{tDlJKJ_*A|AI2sUniqH=+PBH;)1TVqJix+{2^ z@T1uSUh1E^M20ey&B*u#9)q4?l@LD{X`tAU%kXp$!+E1Ah9>RzrMSaP-das`3;2sy zF``!{MhJZ=9(;+%Q8KlHHI*L=i^%EIhuUY}|KfhPZW+&NNKTO1HGUK2`$q>DC;F zgpbJGa-+DrNOZ~=qBow*FbY-;x65Zv0-rxO)PhawrYxRng)IP|KMc>4HHr?H^&!M- z`-hIPXx+pNqocQCf!?oV31xq9-jh>;wwWqvrOcU==+;gGLS4;*UUiM_tZHhTi<)Zl zs;Y{r+GfJ}9wfjf{dNQrlJJWvT@ zY;Q2GLp(|Rg9l9>=+e)uRYxFMpvEM|H;%9~Mrpy7Av@cL)qgg1l#r^v8)+bodd?{f z5r;a@9IR5u9o{?@6*7ZAC{1Ax;N$k2;zzv>873kK=KIzjr@HGf^5bWCR|am@UVlc& zej>l(Sd~9BB_$0?VjGpQklY!JKF6WGRfK`4Y11LN%a45OZ3kR{x$mt0MmFdgZ&=Vv zGUgS&kWSa2vONev{o1)E;rj@a^Ui5pDQh5RsNVJuf#P zwDoW=ph5n9hta2xk@c?LQ7Zk9;an!h9Q0HCXHD$Y`}l_&U9c`b1%++yFG^V^wV?gw zYDyozz~NePOSfGFK6Dc_$P zpO?j5^&KC0+_~Q0nK?aYW@i+QMJG@V;&lQy-d)?~$4fKAb_+y8LfGuggxhNOh1=?( z4D{J~(MBV}?nEMwNcoaEaOgu-8}asow!OQuA8fVmx>!@w5v&Pp=ikEL<DUJ`tU`PmIwu2zhP?_*uKX0J^BvxSKJ43iFi*IQ6?u*iI_lZ9S+hA;vC_^lQu{+-Gx?>paEW3mz8P zg42QZI#Y=67z_l0uD?pU&p@Lqh0nPNr=w`a$~wg`9~e`NYa^I)z(rWX|0F;ziH`1- z^5$sN77war#iw|+9Wr@QDXdy*Q2JmA!3Ys;;?9B&!E=oTak1}154+EN8HinVmNa`Z z!tZ^oRHoG4&wku~Et}rrJJ{YvCL5;=GaL+z$8S9MOwcf5 zVIoj{Q{$9j%!?3nhfIOGiC*f)LEVhLH}czNZy$-6gtL&XonXe@L8`ph+W5hdAf{`bDaeW3Y-y0E{0xWK9}SL%TiMITW$79|4Wm@G$2=Inc1?Q255!s=xe zr`K)tp7^PHrAOmRmCO{XNVnL7QT=zSC>vesAL@Gog`C&?2ucb` zqidcotR&y6(lQJ&k$xqOE*cW4gn#=L?hQQdloVq3fDk)SX+yt@h?Mi`Tp!zKXLS7L z<(Kr*6B=bj`yP*m-)i+1^G6OpxXs8qt|Y@XNTK{$t zv`8mN%_OSdc!%Um5L##zyGXlXQ#e=0#O?27viWAc<6kXibM|q~EN>8hBNjL_=1*8c zCh-s6owb-e{7Uro>U2r7^a_0OXUo@zx}TO!%J6a&N!FU`9q$oIiXT1<(=Q@G=gDv` zTU;?UM8xnY7(8k=n7z&Z&0S=fY48N>tl~0TEL#wuYhws7FxP+gE>U_dnZF9mnrwX{6t(Q z$>B}3NRrsFq3=wK_R&aqVrkXAu09Xs$T&k5P@W5Rv>`DsCSeYVq6l?feNiZY5~ zEYF6T29TSv%~oRD>Dhy z@`bnxq})d{DY*`iw ziFm*1!F!r#vf9t%rjWfskRm^=1=F<*BCm_#66ltjS;66n;6&n*Vf~t+7DT&og|^_Y zoe~WHpyQAt>~CkzNB2|=Uft`;K}QiQ7%I(BN#%)vX%0wBClT&Bu#CU|?S|^B51K;0 z<%SWDZ=DKIu6U^aZN zdXW>xy-JGbHw*L0urW#g$sSR4nXF39lhxcT@=1(dmO3LEybbNqKAv9DrdQzjwm^rYIF(aBlePnEO03(+4P7aY+ z{l=X)s{W^M?~KQ*e#3^TDQA)J^i1^;&;VJN;qMCZEb-dDm`+W>XY6s2u6dQL9j)C% zSd@6InWi1!)PM*!;E@yx%-!ejS6=f`WJ(J4j@OR**GZ_31h?bcePb3R%3Se z3(_zcM4u-|Gj~L^**$6?xN81zr>m8pmFg+I(^q%>Q%v1Cg^h!u=ZlY@YL)0ERG!*a zsx@2a*5(S?(Z!%?At}XhvTyHA-3lCot$pUd{kXFAwOzq%|MiZfwm#x`xN+D5E%dA} zx*{tTJI~=Q5I>2#1_n#|t8H9;BXZawdkZJ+2pd)TJAc@Qy3}juy12I$9V}wwsHp)| z5!>EjFUX5KQMx$;dq+v~gI|-EnXoV48}53dB&mC}Id6GUE|mx3@+?4L+eQ3U@x#Q? ziNnd=)rvz)QpM5D+QQn*#Le2#LBYYo@!#ud{8h))2t?y!-D8SxkssjavN+7h5y~(! z1r#wq?X3xsso+crm(Fy^-dzS!`u9JzB-QY-5(*>Jbo+SrS^i~-d6aXHNufy z@>_V}=f~Aj!N-V}!7c@Cwq_M9u6HH}1ESQ{k2(h<7jnO|qjtlDS@-jG>3hkx3KWhqxqbtyp9*Nwknjq<>^P)g4s*B*dEvZVIEF-Rtlg{Lu8YBCn_ukS@P#_!StyfV?U~ua$gL83`t+XSe}iKjCe^{A6q{dVkCH?X1#H7(Jv9N1sAM>PzWsYw6JYH5Tp~Ke; zJ5DMII(;I4#K>e~2WR1P_U+9BJ3Ez4RRot^p-w(~w?lmQ?FATtiR%831ykP%cf*M> zOa&_a0w(XPC}q+nt9+2L;>*C?K*21mLORXMe4_fKN`Mpp{oFF4|Hz}YQDKD?G0-w! z#Gp|jyxo%N7^672$15@uIM*dbJUbSO?Qg}wv0y|@iC(Ql zk~32Yx-0RGveOSN=2r;ipXJ@o+HLqxs_^H&};YkG=KhaIaPCjxBJH8g&?c#9f)e;_~`Y^N| z9*NV%usg-=XMo@?xS17zX-p;^jKS<~ z!3y9Cn15)~XQUwT&8@PK`7tePzNyHr+juhH?MPc^St&Z{<1&{gufj};mX^Bsq{1VHI{+;JooyQ zt|G>S4qqMMFD3dkW(M2jSv;T}&oFI_LQpSo`f8C~S~puo?a`RYJ{_qP96Uymt7)2{ zb6tNkVNXT<;>vXLeG97*H(f3Mf=0?iwQ9AmZqh_ak(}n!7_`Cq1PWQEQ~JdyiMIM! zu|Xgt&Q7PDs{0|hyZY72sv|ddk2Ol?#u1bw$eZKKiDDLlT^|+EYf=_|opBEuIvNSz zF+r(ymfyBhdHdXlm{B$?zfi;@eIcVTr!+3HUZ^@JX~0+~$uW**vwu0I$)*7`RYM+c z>zZ$4o$Iv8nmlFdd*dLD#v?zR`#A{_fhJEKzC;SmQ50FVR`t4|LG-G{el9xq2+St# z+gAL1J*;o6)M%NNOqS6Y0pDdSDH-A8_}1w;kflrm2uDr zaXs1)FJX@1dxrj1)!P5ADPz9f6jd~H;%N3Dh11xcu2ORonmDDgX3U^8w{uh;V`>%; z@9SYP3#&mNb5r$l^z1;=j@x4&U&lPHD|M-sfn<5cgj@m4XFiX~3%s8e} z*hmj2oM+93>xg=1#LIJ`4%vS5S!Bz|ArGz;%?-PTcs`aAF-OrY5gCxI-BL*kT&vE&|&b_=T8ItSX2RxrWs$l0-N<*N`ffgVPu_ZSy9hk+2~&;GX}6J2a_g~WXi%sfo|4cdv^&rc z$IfLrSjUN7Qyr1xs=Yr*uTc3ZIl`_qDtbMX+2dlip%xHgAfu6cZkAw)dXq!pE|bYT3ElH_C*`Xs zvurr-k6!Dd)Z8DUKvAbCQN2(0x%3mYmXN^chOCv}%`4x*PPcSY5vH#IiCOY5SGVRr z?hQ-U+_3rQ++Jfof4N?@-dLIX7;mplo(35%y*5)miX4J}T;-Vg)S%tFNg|nb;lmQy zcI54+ox;{KCyLI-pI$$1{x)4{>CxhMypPxh{|04Yd3mYZN3}cHgd~zT#@!4Dxo>J8 zkC53UN%z}P2e0{^USC&E&e>085i!L+FOAHo!(ur25YFQ@>rqNzgv0H9qxKC|f%=#Ri$vEuAy)YS87mw@=U9*E;?A*pL zP1zJlD1&DuE}T6_yfkM_Z%mHTK4=T$f2m&g95Cw+4K6VtUb8gGMai?V{AfdRO!T1& z_wDmrp9^+ocul^ieEAp_y?2Y=E_0-TR?i>rWOh7?wUNm*QVmDFs`MsHl~&jbvB9zux7&vR6DIzshwgmv7bv< z&oP*Ld(ltvO|I2JX1=2Z>+}w2)J|9P=B&yjsG|&3{k0aahpM+&8 zNgL-?1Eq1hT9d|cZr$kxcF$Q>{R5q@2S{)jMdCEsOA7st`a_B>yLD=a!WOqhLVr{)N-Vw{#QS*-HX@9@+{Gj^pat$M=3KC!GISal?%CA|DeEVpDV?n zVUY6^{;d>rJ{9MlM8Yhq<}J6Rjb#Fvo;^3BA>rc1UIPTpG^`P1OA;beT`8Z*!7#4K zdX$bjq7Ix-#~;TzUyy_qkxGW}F3u*$Jp3}z=4+F{s@!Uqp>ma|8~)lig#ria_WO~B zDF@-BnkMuKanwP$hOeZVgi5%(^Xl@|0R2{6RRvW#oyEw5 zO;kqD#VlOW?o7IC8EqnlGol@%#GQLrZ)`Gp>rde7yne&&mVoItUcqZq-BsuukZ!dN zlQS|h3>)^2W`xM?)~Lx6&XJT}ftB$#ayLAz$fi!Y;?$Ly$qyVC6&rtI97F{;!ED%H z9`U9m?L9RDU9L(+hJ>nOu+OG&NiL<_waMSeObY~(b z6O(x*iMo_Xq`FpTL@#z#W#%A#Wo7?>9d*PZC$?Vz6~k+mS%eK~buD7U@hz(7jxbIb zk1(9d@2-ouCgjy?Et}`Rtf!H+iWE2`v1jV0&#$?`wiTy#+h$h;RdYa^)MqOa z$Js~A+tU1*_(FBggmuZ-Ck}06jDYsM;e;znqRB#$BPJnagtmWgg@Y^Pes@Y2+O8tC>4_e^ z5&W`ofPSRYLa2daf2&m~gUf2#>gFe@r`37oVz1EXmK9lcPjzSlT++^rG07gYwKwz# zB{h=0VdZ;wpphyL=3oY6!wEkS0jV(OuN;i|zu)`F(?h+b^?6*(Qeu-6Lk^ZWNUjsp z5{b)!s}8XpQI3%-wgYwCA6)kewo$z(yYCNCYAadTd2zj-FFC^8sUz%V*X#>`^-<`nm(Hh~R` z^|&;H7{^4}{@td2GLU^ZZd?FLcbQRzP46m5wpKqfJ;J)?sv4R7S46#gfsD^7$CbVW zY;MIIi6m$^uE55Qrsq#*doJhsi4JQu?6t9lT`g8e8sirIfQjrot079j!iF7sA{KAz z=2w4pb!}T#xWqR$tYBN_n@yYI2s~7As&5wj)B&y1I%&mBnt$VM~<*2i3zP8d+)^<$E>U$k9 zySlfIhAb^7xXMGlu;TKK>vQ_8o*}Ulau}X*I_*Xze9HX1amN_C$xom@3Mof3P821~ zR~Qb0mgGTlMY=kC9VFrF*jHlnzJ$$IOj+@E$hDan5BBYRdaHw78D4kv_8IZ}n=edV z9ev(><|zFt`!Qc%4x`jp^W!+|pfahgFYFyR(XrhH0hZ=2pY+iv;;rh$U<3Zq!cLV# zHxT2^Yz<+;;cH$E53N_3c5JFL6xN1MH@a0&oV<5hx|rRJ3zDn)vU_20N1LjTh4arB(&mi#8IPQLe8i4@J6ITZI{J4|AonTiqWhg5{E zb&1A@zbLQ3@@42Y-L0ygDoR;3pP|eTa!z`#E{Y;RsxDk#^jb~;rz{Hn^Rh zTHb5J{FNJJI}vxk=Y~yTDeIhJkJb(n8@Fs{@Uw@#sS4NK(9QYwmph}1^CdX#s4?vP za+Gc+~=mx7EI7qPOb zCE|wi`k2aw>6-eggi3n&_(rLmw3*<$_tY!d%;a|1cT3$+_}5X9vKu%8PRF9;uOvF% zW2GR%Z^4|vvk4!+9#&Qo8`e&JFIT9`o_bDrxzy$9nMvPc;>gu`D`}E?F_IUfhIAVO zDvBMQqWpCqn?JCV@yc(wWnt8eB$(7sWxQFYcwvxJO0g7_IjazE@0K*%RmF&y!(1lF z_!;|E;1*@=V=H$1v1Q5J!G$C(4dDocu6y2Dv9n5BwPOY>4`B8s5fVCAjdQDNc<}i2 zSWhHXqvZ^hbxHh03AmaXF=wd0RFknV+EtE)FTdnp%|D|^RWhfG6=NZrGnFCly3Fh{+dp%Bn8XqQqCLi@v9v zES6`v`AteRcnc(r-S-P@1mk%c9Yaz5!a2sBA)p4&iLKd z8g+hA)_gAW-p#K8iVMz>qF0;5u*dH6wiF?8EqYoU`3Ck9eY+!#H>R|06zRg^9*65J zv4g!Y@0=+9l!z3xp@c`5TsQG9HZl2ohnAoQ8YR7No-0?~D)qAsLW0f~u7w*>a0=z$ z1dbYGzbl>vFnqAMf?3rPwt$eb{qU9)tU9a zS0?i~6XaAjn|9`QFPrlvX?7%JUZqQO=D*Xfm9bl#<46-WsHqO`gtZzbY2mp{6T9cM z^HiYCIOEI8DE07~6i4C#B}a`zm%@XEmpC8bjF?<*^RCw&RCPzwNU0T# z=wk{CCvd%MXU|utaEnY%{eY9aO8ce7YLA4yV(7Kfp^cVwlk&3#6fM@gr5F>#z`|Bu z2a%Ewo`O~HQFvo%9}ep#XX@@%^S6ZcrxE4dQctd6cHYyQtW@!C?azLXd1G6R`&lWu zq)!?yMwogTNq3o7dF+N*tFBf>Z}+3|P@A9;sb-w{n(?cf+J*h@eQ2dkgrPL|uq6=% zR2j0>rJWashxli)jtoRiEzyW6nk(fzHVMAQ-Eca|iDmT?82f^cxUxh0orH>6M_HL? zoH&LJ|5NlxqlPBS!~3wjDSM21xee<@?Gm(Fx=&Oi6hAMKl?;+=usO;+Eea}2o}r6z z8EDF*<(-Q_8FVCl`9@pITEccfV9J_uP(G7AD{VCTn*zRY$fVwmj0wL#hD9h(NYH-7 zmQJNxI5A%(wlCovZMr&f{C2-)p2$;S<*{ZX;c%_3E_>xGU!Q!&4rF_&kJ1xQri8Zk z&}3x?A5Gu)=HN&N4EcOOAfhBmb1jb7wHP+PCG_2=>rXPL28BH_8rvMkX%^u08ZwFs+}Zd@yK)v%kxK6m%Uo=FQP z=>tdk0o(mr`G*@PI>|hw%^zv$Qx5_ac9)(G8)Z4an84?ck*IJSVx>%OpuaZT<%wS8 zgVUNvG^t-*)bg~(^jrQ-J`cY~#R>;v`viNmQF`!b#xMIE<^Abzt79X;i^XpWnC+=Y z4o;q^Fn)fg%U!Ry%hJB)H=O!aVsIVxc;m#O;c7J1hi_@2rFw$5+@=njnM%1%TTtKA z>4k{ZiD2itd_1K+A%4w#5Mt74)HIjMV$z{q!RWU|Bc4JuQbp#rfG=20vl5S^@ z?}avr2Q=?KbxJB}n=osKme|Hj@aWqyz`*OwWJlTN@CumAsIs$BOe0nv3W~F=9v}=L zQAT(~i!+=0hKTd!w#Si$XXm*eRLk3l#8PQ~>C2x`BaqYQTsI~_ua?tqJkn!L*%S2d z?(R9Pv9toM1f@|`KQG34H3Wrwo)w}TTD#bBSlmSZEqX)nAhY8ImqJCyDby^Ldr z?UQ?jz>%_E#7;&rQRzua!9h^$&|a#OlVSZK$kA-~O5lR>_>oRa1QYWe@wIY`mvRHW6R=#oap>G{`^vQG6$>Jn_tQB2QZ} zW;f5Tsy*HHejDqF?iKC5xv;w9Z9cNImsOaN3Ohs5Cl5&`N={T+v$%(X9)*30xZNCI z#=Cyv=x4}}K^+_SaN-(M6^~xbYd!dB6^gHtaMHHxDZEMzgQn%mWD~Q-ij`KkiAqlD zjpq@@SB*{V%TbH2k>oIS+w@YZKP=Q}vynWjHhZ&ejKH;q{08gHm!w=-HeYJ^Jw3C@ z@S}|sgXBWCvC}8wrDNmanm*dp#v$4)i7%{6w99QOha86u z9)=)cYQ0hEzvYdUWE z!DYc2X}1Gvt5*@S^RNnD`Yy&cSJ7|qvTa-)jO`h#y4D=8la*iLrxzXQUSJKM-><6K zrLa`rp%2O!?O~1`4ZK3WsaZA9d|ISgHIHF$R?K`|+RCHBik*`BnX8+0%vWJ?uI+_q z?0uc@ZoW#4L+=^eS*&v+BCZ`J;FRM{wqR<))5SHAVkIccwIwi8vLz_X`+_!Efzyi4 zyt-uKgd%>;fySfAV;Kb>NGbF-cyF5`8D+xn2IW5h`27j7@ldoGXZ zuf}hwCie`|TL7Y~6rYHoaUqWmMWg)=Kt%qFRynp;UEHWe3%E zk#=H0!SaHvi_ky;&pscc=k0FUzM&x|F636i9IE}1cyA^;IdOR1*3|;L+MLehtj!Gm zqEtWpoVom$WNi}V%=yH!%39P&Y6*MCB&WCYeO~Eg?s1oWD<+E{X+7Yj;E8;ko38d2 zt|gtlT`HB;K;a6ZLxE7JPod;Itc})k4pwNUTR}(Ta$(2uie%_kPCU!{(?m4Y5n_VJ z1(pOx*>_m)r5LFl;GN3Ov+h6a=(k&{cZBgq^Ww0#xi}=`Lfu7mSHL&lcNJ&PQ3$I5YSKT2qtiWia9$P4hMEWpr&5=8m zOCqtW7(^;132VrYnflU+uUolw(R%dplR*5qy@8qixi#F`wJQ^AXNEF$Hc3<0C!w6{ZiL1-P4bRT({wq|qC>>BHp;64*c(Sj{gb zxmWe5Z^uJhAQ={|(faz6!bb)Hhci_$N^vYiXahV^W(E)X^SF#=Wp9({Id?c`Ki)y7 zY=FUKMnY})@H(i(I=CaY>h?U*y8vU%dcw_|M+B?pOm>W|9n`V}9>b+*js?Pmv(oYe zlzGSmVmGMN4orl~wk!9qhvp+$YNog=>b(A#;jexe1IutHp$UZ|0bzIObJ32PrvO}L zNW%;ku8lj#bLCgAVjt@*4BsD3R*K1uWyVeVHnqe*kKXA%@)@nO{J`)z(yz?z+n$9d zhhy(jG07I^QWb0W+ixx#lja1Eq^+N8wsao|1aMl>-25((xSjXzDYFiz7O|yOF$VnA zNUF#8U&XAz%_f>QT;s|d@>v|K=e*hr`*3VtM6}MB|82p$*8RIKXP9)~hy~F;Y}>#} zayD_a7*i7E!deQt7`^s_8^RW7(a?M%J|-6Ooy1E zRw>}GujW0_d-DYqo}vUj@b@S@dC3-IoWA1Z!ci-fVckr=o}FIT9oD(aLtdlii#^!q zb-gHs>CFPK`)CicA+a~pnEU**C|#Y8L-*dzFeJw!TGFf?we{2Ll{pcv>o_6%p&u2> znxN|(Cr)_qfeiBTtXyEiQjtP+SWB^}sTWLF>+ue4BqG~mUT-@EVAl<82P=33dDs;uR1%<)n&ODvjGj}bVLo{#3&B;2BS^ytu)lpl0`%IBGJOBPvjX;oT!$2{d~ zBqw!`?{SwgIcJn|-zT4^r6PJcPHfsN(@_YFg_`JdsPHIiTJt6iy7RangM!;UN5TpG zV&YiuWtgH34I_r$$$rjqLgcGD&$F)|%Hh;PeWxp$K&Vc6UZ_m1F0z(o`gJ4WgiDST z6_wWAh%ExK1EUtz9cwZxuRBjT@=_#y5+0lAJm`0@WEVHg?7uUE zyXg2v(2(=PR@Xtetv6Hi_r66I5ew=scckVYm2hI4m^g6jDW{Bm>N~U4W=1WqfVn+( z4XJOTciE$Iv&^sWnnmNQ)I!rDlN*H5xuBy7=I;trXU`t(YTdTL>OA;t=vJ-Q)*837 zCw#=*Z@B36Wf%SQFcKhM_t!p^kiuJwEtacKq<`+=vVmv}(!eHIMq)n-hF zryok)9(9-tIHeaSQE(((vw1(67ZcL0#*pUnOxq+)O<3XDRNO7bTbLqUl;7f_-4I$@ z&&aiV4z_mRN8;>o5DKfs3cDP-M1~0J4oGn~Qel%?UQ3sLpQT+UbLL7=DluT{nR-*o znOgQ++t415TDhpuwzQ?6cS{Uf>Eplxiu#mZl`qQaU#w_n{iIHViaybmP->34F&hLW z(K1my_#nihTbJ&^GA-*v*wCBv0#`v{IDS{*1qHWoepCS++FYd6(XCM!-H}|_`^r3` zMvJ-(SRW%COFW}x-ETi1A&Z2+hAR;g!#&|5%WZkxWMQ}Wv+P}-2Q{FmU7ej(x217c zgJ2c=UeZr*h*gA;Di2>`36#*^nU($aNHi2gCKQ{uBgbI!#X&w>^~z|5meBN$4mw2? zud;QqEOXK3)AVsXom_PVm9H2gYcZZYk=2%YqEqiwvFo#55DaWo8zkFVkVgh^=NhEJ zIxFXYPBv5379^zExeZF~CoBuNh4?&p23?1)c)dqR(rYf>R97v^upMTf{;n{_+c$4T zhY5=2xMyXtOTLA#D17-&FDfhE2K>X;00A%JB+o;(gKMG%+t-~A?`doyTknGOU<<5y z&AvR4d7j;b!k>^3v889QB9MdJz#DJ?`V{eKk%u#tIoQWKUV+_g-L!}M>TNNLN6bNj zMksn{iKMjpz9A$T+4uUV58f!Mr51Hr;AhyTBg;q4GtbXnZ}seSw_#!buJEz~rIo^% zH+sN-(s>}4)k-n!PG zsrmQ2VYNvP`b11jC`>`N>S}#x5j0jYxojc1!y!T4+s65o(!wuh>^*94#w4^ZR;H1d zVTiX;ftvCnzsN>znv1<25VhPAO+Wm;xP};@EkYYnS$X=!GiCqE&5fuV$3|uI!hP>w zz%8-hve=FkM{{0XQ+ym&DraR+jxL}Tty7s|8)2a$Oy0L*z3{lvAa25o(5ut62`NtvYfC1J0|jYZY2SBvgz8rIkK;4Y!J~HU z-zh?RfN(rSgB;qlMX{#$^@-ZM5-qLa2@TR4_|Hldkd%WCg%c%$aD?B}&}S*=`UrWe zEI+z)SK&rg=#&P{{LV>-`U;h%x6J}sSpio77yipvxx?{SLIUp<-Me+SQv3Zh2VTmV zv^$~48u6?DO)B1zg>1(axKh{5?bF$(9|o{c+UJ zR-Ero`8XMRkaHrOElxlW4A!Z`jFe3yO~{!k?KiphPZ89VU;)MYp=z&|_c_*po?;{z z7&aL2_Io>I{~Q+phwc|2$vuUIfvNgOi+?nJ_+OnsF#9jse?7m%^LrudD8pukd}}$h{S!ZWXtPey5HWc375S}?p<3t8 z5qlbY_$yI%MMnNg3^RAMJgJcIK~36RW3gpq@NzLG(gDHWkWkI5P<;0xs7PJe!S&72 z*u4Un-Qbm)O%cR=tY?d`4BKLd&w?Mk0y677JjKD>`w+(!iTSBNhHxxtZ&uNgBpo6F zOI`u1rVvMa1a=Ndqo5|^=Z0FUXwq)E?$07X%91*x~c?FmccI?<;ZdiygudXv-;U6139vyo$!}6oXFW$$lfSRfRNUr zc@nd6g3HN$xz}+?P=%2>%VTG^+_Ev==q^-DJ6#mI9wY_6u6K3o+nZ`+um6|JvE9B_RrpHjS>$rQfy4IKR$ z0XI#kawC?;JXS%;otK+I=5H_s81K8|`(11oKpNUnam4=Dn4}Ei2}8*9`Zu_(+%7k> z$th9F>t&}VVx!o|9rM!$$(Ike#(rNeag1T^Sy0AK-RBEc zW%M%wsyDmuXs*fwX_p$>aGem=nVlr1IpM773OTf;p3&mTnDvdzSEwFS3>J7Zbq8%z zP{gDVnWk4zHgQRkmY(o<5Mu{NeM#&Z-eGwhs5ho-;RcTNb88>vcDqO|wxeJ(6zW0G zP>i{Pi_XMqKzX@i`^+ki7HH9$;p-v7l-#`kS$2n;pn%eY2IZvI_JI-cFI`KzsX^SK zC!wE&EqJND&5EN!(KeE_C;PfP=-0MJwFTn?a$Htp2Ea!cT1s^!_xl!!P4_&xN?Y@r zyJk%nlVP&CqgL{CdWjKAyXzD3=BP=(ew&VPh>%o0pvq!wRKCo%2FewLO(QyKSB-fE zMLLMFfy?RPSY;3UtFbbsnG(A=$_kls;)qyC3CBiwA8lHnVY>!a4f++6-^0^*!7ChT zehd-|G;}oqn~gM_EDZ4FdXFJyT~x~K7hE&F;tF!){5&eSaPjvI$IO@UrBzIqwfvaW zm3W4GC1+^${`l|+2I3>9bLd)K0Nu{2r_|cUqiGm&a0|vrb`Uo)G^gJRz8Gh<84^HO z%_|EhB1=WdvaWO#FEWd#8)6sK@#^}HHX)*{P;|Qv?WZtasS5A4yOns`gu#O(X6maC zSbH|ajfJ0DD6W~(9WlZCTd4l5mkC|uijt$c+$rQXUbAAidmlpoxRWLSHNyABihAO` zU1B4umUacf(}zA)0?}QcTR>kAQ9#+2BCwB-w2f0j2#K83p=#T)X2pOiAAIL8_Fo<1 zg^kc?<0{6u?Cto{EySymDL7VBeAUiv&oOU*?rkF~$-o;d_Pnp)?)*v>)fV}r0HO~) zoN^SDW>VJcrWC%k*N^CNrFs2|nYiW*e}Pj9!?f&JYeL!BE^+)JtG@QOH(XaL;Z%Br zrxAkLjs>ALKCa1 zug+tiqM+pJndz)XqE4Def94mbS#9wKLhQ<@%68;TKfdm%!U9j)K_@LLld>I=4CN0vpmq(?X-Q<$&8(gzgV0yY4uzwRAPw+gDhHtD8W*&2w`n;x{+S3mpnm zM6E6nYtI?qE$Ru$n5mH@n9^}NHK|1{OilXxr3aSKI8}Y=P^PZc>GNF!YHtXVMW}Wc z=mV8?Ss+Ll>biZmK4nGT^4IX?@R)ZXmc4*ETj%2MgQMXu^!H?EqvRw43}BP%z0gPg z05S6rq+j?#7}qYzJZ;YN5?nLsw9G>fH z=UTU|2FVp$V^L47*`f{-nm!m~f3_@unrH_*YZH9qMh{q^;Gp)RLX8(B+#6B|O@8j# zS%{k>ZUmrPY|o5xMd<~Q<@!@Oy`ZPP0?}u5f&J^|kc_a}8Ux<&7>a}&bQr=h3v@~f z3Z>@>363GiOFSRZ)JgdYq80MvMM(g1K@+R`g!D<-KN+O2ik~ z_1nsz#{d@<0b4W~QY)I?ifMEZ2P~AIGMNv7T80x+<<&8zn|z z`Lu51I;*s?b<$qQ9x*X>kP(Mr!dmnw4nb-6Rs;u;AP1uWlR_ODR)_OU?V`OKe;1ZC zMRPY9J>Vr(0TlI1y#JJgOAtc8`0iE)4it=vbGe{VgFlhi7qEb+8ZCgct>FkERh7C; z`rMXU@74j2JNUV^l|fQ9i$QGHpsz{BW2)?$4><_mJAu}Ys@B}DGXGN~Quopei}$3A zqn%}J!0(uZah+nT4asJP`7IbzJg{557B3dN4Vc_x71OaD5Bd7_z{6BmO&O9+>oza^688l`lkSZd zF1M05&QZ-3=!#<9H3pt;t^h|pnt=^VG2o>JX`sH7vzPR2t_LgI3Ghc^#iQKwfWDSMH$(GclOx{YId7-e{4z9e z&*-Q1-5y^$PBS#J&*pP2)Hb@e(qe7CN#KZOPR%FpMmcSWV)@hI{i4S>BU-Xa2t&QT zref5W&0APM-SFO`T&a7g(KLusyp;AFgI{_*32(`CCun9_q=26)hP0%sY<9UTNbS6{ zx~Iv2JzG3r^iVR|@LIV~$Lj@d1{B>W0RL#Fd=7sn+rl`t!hseEpx;h7Z7caCcs@sU zR*K==to#A=YIj&?qIHC&5b53;{Nj@AS+Jm5qwI)!++J7$Larb#hdA=@~ml3c5iguX8_H`t!^_63oM2$SOR!_h=5G=K@S!Q}&!^ z*3U$)plgNR<2?5-U&z-+4A2J&;1I9rH|wHrRl<*%0~s70*}iZ$&8 zkUp7VrL~|@tuP6dR()Y=3%9x$lKgppnKed;Q`vF62~cq0yynn1`pOzJYeR`4En}6I}eM2w$asjAgSidNk4fZbOUv27BOa6y`ocK=SO@(A@K%aZTbF*4nFeu7TTWpd4- z(Dfn#$=SP1V0Z&*gQulhphvGjC;U5S~y5YEJQ#A z0AMfx04(??0D%8<1ponl8Rm=Ag#iCQM)Xfiq-S8ne}PiwdI~D|5GdcL%)g<3Fm$3W z1s2_Y27iebv%cJ<)&;+MgSyv?iByw1^2{%{BUE%NCf?l3lln1sLHzRCyh^meeV>$% z?|Ls@60f+WF;@C4AwTn6Lx5bh$IXPOp1GG9zZJ@hS8PG1CN@X^ ziS*cBVuo9K?YCmp@wl5{{h^^J&MGBlnP&e(4m2SLc)T7j_?fg(Fu1tREj^2I6b^7mZ&Q#s%^HIsG?EykZqJ zk`EVv0D#oLC~Q8(BFHB!*x%=tFRf48yl s-^9PO?MD~?Z0+~x{bAqhPIiL)6-!4fl;3CmDB&Mnd{N8!j{xBR0Jd@EwEzGB literal 0 HcmV?d00001 diff --git a/Assets/Plugins/Android/TDAnalytics.aar.meta b/Assets/Plugins/Android/TDAnalytics.aar.meta new file mode 100644 index 0000000..7abadc3 --- /dev/null +++ b/Assets/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/Assets/Plugins/Android/TDCore.aar b/Assets/Plugins/Android/TDCore.aar new file mode 100644 index 0000000000000000000000000000000000000000..72e3ff73da62ed74f914baf7ccc492c14e9be3d8 GIT binary patch literal 73580 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 0 && !TextUtils.isEmpty(publicKey)) { + tdConfig.enableEncrypt(version, publicKey); + } + TDAnalytics.init(tdConfig); + } catch (Exception ignore) { + + } + } + + public static void track(String eventName, String properties, long time, String timeZoneId, String appId) { + try { + ThinkingAnalyticsSDK instance = TDAnalyticsAPI.getInstance(appId); + if (null != instance) { + JSONObject pJson = null; + try { + pJson = new JSONObject(properties); + } catch (JSONException ignore) { + } + if (time < 0 || TextUtils.isEmpty(timeZoneId)) { + instance.track(eventName, pJson); + } else { + TimeZone timeZone = null; + if (TextUtils.equals(timeZoneId, "Local")) { + timeZone = TimeZone.getDefault(); + } else { + timeZone = TimeZone.getTimeZone(timeZoneId); + } + instance.track(eventName, pJson, new Date(time), timeZone); + } + } + } catch (Exception ignore) { + } + } + + public static void trackEvent(int type, String eventName, String properties, String eventId, long time, String timeZoneId, String appId) { + try { + Date date = null; + if (time > 0) { + date = new Date(time); + } + TimeZone timeZone = null; + if (TextUtils.equals(timeZoneId, "Local")) { + timeZone = TimeZone.getDefault(); + } else { + timeZone = TimeZone.getTimeZone(timeZoneId); + } + JSONObject pJson = null; + try { + pJson = new JSONObject(properties); + } catch (JSONException ignore) { + } + if (type == 0) { + TDFirstEvent firstEvent = new TDFirstEvent(eventName, pJson); + firstEvent.setFirstCheckId(eventId); + firstEvent.setEventTime(date, timeZone); + TDAnalyticsAPI.track(firstEvent, appId); + } else if (type == 1) { + TDUpdatableEvent updatableEvent = new TDUpdatableEvent(eventName, pJson, eventId); + updatableEvent.setEventTime(date, timeZone); + TDAnalyticsAPI.track(updatableEvent, appId); + } else if (type == 2) { + TDOverWritableEvent overWritableEvent = new TDOverWritableEvent(eventName, pJson, eventId); + overWritableEvent.setEventTime(date, timeZone); + TDAnalyticsAPI.track(overWritableEvent, appId); + } + } catch (Exception ignore) { + } + } + + + public static void timeEvent(String eventName, String appId) { + TDAnalyticsAPI.timeEvent(eventName, appId); + } + + public static void login(String accountId, String appId) { + TDAnalyticsAPI.login(accountId, appId); + } + + public static void logout(String appId) { + TDAnalyticsAPI.logout(appId); + } + + public static void identify(String distinctId, String appId) { + TDAnalyticsAPI.setDistinctId(distinctId, appId); + } + + public static String getDistinctId(String appId) { + return TDAnalyticsAPI.getDistinctId(appId); + } + + public static void userSet(String properties, long time, String appId) { + try { + ThinkingAnalyticsSDK instance = TDAnalyticsAPI.getInstance(appId); + if (null != instance) { + Date date = null; + if (time > 0) { + date = new Date(time); + } + instance.user_set(new JSONObject(properties), date); + } + } catch (Exception ignore) { + } + } + + public static void userUnset(String properties, long time, String appId) { + try { + ThinkingAnalyticsSDK instance = TDAnalyticsAPI.getInstance(appId); + if (null != instance) { + Date date = null; + if (time > 0) { + date = new Date(time); + } + instance.user_unset(new JSONObject(properties), date); + } + } catch (Exception ignore) { + } + } + + public static void userSetOnce(String properties, long time, String appId) { + try { + ThinkingAnalyticsSDK instance = TDAnalyticsAPI.getInstance(appId); + if (null != instance) { + Date date = null; + if (time > 0) { + date = new Date(time); + } + instance.user_setOnce(new JSONObject(properties), date); + } + } catch (Exception ignore) { + + } + } + + public static void userAdd(String properties, long time, String appId) { + try { + ThinkingAnalyticsSDK instance = TDAnalyticsAPI.getInstance(appId); + if (null != instance) { + Date date = null; + if (time > 0) { + date = new Date(time); + } + instance.user_add(new JSONObject(properties), date); + } + } catch (Exception ignore) { + + } + } + + public static void userDel(long time, String appId) { + try { + ThinkingAnalyticsSDK instance = TDAnalyticsAPI.getInstance(appId); + if (null != instance) { + Date date = null; + if (time > 0) { + date = new Date(time); + } + instance.user_delete(date); + } + } catch (Exception ignore) { + } + } + + public static void userAppend(String properties, long time, String appId) { + try { + ThinkingAnalyticsSDK instance = TDAnalyticsAPI.getInstance(appId); + if (null != instance) { + Date date = null; + if (time > 0) { + date = new Date(time); + } + instance.user_append(new JSONObject(properties), date); + } + } catch (Exception ignore) { + } + } + + public static void userUniqAppend(String properties, long time, String appId) { + try { + ThinkingAnalyticsSDK instance = TDAnalyticsAPI.getInstance(appId); + if (null != instance) { + Date date = null; + if (time > 0) { + date = new Date(time); + } + instance.user_uniqAppend(new JSONObject(properties), date); + } + } catch (Exception ignore) { + } + } + + public static void setSuperProperties(String properties, String appId) { + try { + TDAnalyticsAPI.setSuperProperties(new JSONObject(properties), appId); + } catch (Exception ignore) { + } + } + + public static void unsetSuperProperty(String property, String appId) { + TDAnalyticsAPI.unsetSuperProperty(property, appId); + } + + public static void clearSuperProperties(String appId) { + TDAnalyticsAPI.clearSuperProperties(appId); + } + + public static String getSuperProperties(String appId) { + return TDAnalyticsAPI.getSuperProperties(appId).toString(); + } + + public static String getPresetProperties(String appId) { + return TDAnalyticsAPI.getPresetProperties(appId).toEventPresetProperties().toString(); + } + + public static void flush(String appId) { + TDAnalyticsAPI.flush(appId); + } + + public static String getDeviceId() { + return TDAnalytics.getDeviceId(); + } + + public static void setTrackStatus(int status, String appId) { + TDAnalytics.TDTrackStatus trackStatus = TDAnalytics.TDTrackStatus.NORMAL; + if (status == 1) { + trackStatus = TDAnalytics.TDTrackStatus.PAUSE; + } else if (status == 2) { + trackStatus = TDAnalytics.TDTrackStatus.STOP; + } else if (status == 3) { + trackStatus = TDAnalytics.TDTrackStatus.SAVE_ONLY; + } + TDAnalyticsAPI.setTrackStatus(trackStatus, appId); + } + + public static String createLightInstance(String appId) { + return TDAnalyticsAPI.lightInstance(appId); + } + + public static void setNetworkType(int type, String appId) { + if (type == 0 || type == 2) { + TDAnalyticsAPI.setNetworkType(TDAnalytics.TDNetworkType.ALL, appId); + } else if (type == 1) { + TDAnalyticsAPI.setNetworkType(TDAnalytics.TDNetworkType.WIFI, appId); + } + } + + public static void enableThirdPartySharing(int types, String params, String appId) { + Map maps = new HashMap<>(); + try { + JSONObject json = new JSONObject(params); + for (Iterator it = json.keys(); it.hasNext(); ) { + String key = it.next(); + maps.put(key, json.opt(key)); + } + } catch (JSONException ignore) { + } + if (maps.isEmpty()) { + TDAnalyticsAPI.enableThirdPartySharing(types, appId); + } else { + TDAnalyticsAPI.enableThirdPartySharing(types, maps, appId); + } + } + + public static void setDynamicSuperPropertiesTrackerListener(String appId, DynamicSuperPropertiesTrackerListener listener) { + ThinkingAnalyticsSDK ta = TDAnalyticsAPI.getInstance(appId); + if (null == ta) return; + ta.setAutoTrackDynamicProperties(new ThinkingAnalyticsSDK.AutoTrackDynamicProperties() { + @Override + public JSONObject getAutoTrackDynamicProperties() { + try { + String pStr = listener.getDynamicSuperPropertiesString(); + if (pStr != null) { + return new JSONObject(pStr); + } + } catch (JSONException e) { + e.printStackTrace(); + } + return new JSONObject(); + } + }); + } + + public static void enableAutoTrack(int types, String properties, String appId) { + JSONObject json = null; + try { + json = new JSONObject(properties); + } catch (JSONException ignore) { + } + TDAnalyticsAPI.enableAutoTrack(types, json, appId); + } + + public static void setAutoTrackProperties(int types, String properties, String appId) { + JSONObject json = null; + try { + json = new JSONObject(properties); + } catch (JSONException ignore) { + } + if (json == null) return; + ThinkingAnalyticsSDK instance = TDAnalyticsAPI.getInstance(appId); + List eventTypeList = new ArrayList<>(); + if ((types & TDAnalytics.TDAutoTrackEventType.APP_START) > 0) { + eventTypeList.add(ThinkingAnalyticsSDK.AutoTrackEventType.APP_START); + } + if ((types & TDAnalytics.TDAutoTrackEventType.APP_END) > 0) { + eventTypeList.add(ThinkingAnalyticsSDK.AutoTrackEventType.APP_END); + } + + if ((types & TDAnalytics.TDAutoTrackEventType.APP_INSTALL) > 0) { + eventTypeList.add(ThinkingAnalyticsSDK.AutoTrackEventType.APP_INSTALL); + } + if ((types & TDAnalytics.TDAutoTrackEventType.APP_CRASH) > 0) { + eventTypeList.add(ThinkingAnalyticsSDK.AutoTrackEventType.APP_CRASH); + } + instance.setAutoTrackProperties(eventTypeList, json); + } + + public static void enableAutoTrack(int types, AutoTrackEventTrackerListener listener, String appId) { + TDAnalyticsAPI.enableAutoTrack(types, new TDAnalytics.TDAutoTrackEventHandler() { + @Override + public JSONObject getAutoTrackEventProperties(int i, JSONObject jsonObject) { + try { + String name = appId; + if (TextUtils.isEmpty(name)) { + name = TDAnalytics.instance.mConfig.getName(); + } + String eStr = listener.eventCallback(i, name, jsonObject.toString()); + if (eStr != null) { + return new JSONObject(eStr); + } + } catch (JSONException e) { + e.printStackTrace(); + } + return new JSONObject(); + } + }, appId); + } + + public interface DynamicSuperPropertiesTrackerListener { + String getDynamicSuperPropertiesString(); + } + + public interface AutoTrackEventTrackerListener { + /** + * Callback event name and current properties and get dynamic properties + * + * @return dynamic properties String + */ + String eventCallback(int type, String appId, String properties); + } +} diff --git a/Assets/Plugins/Android/ThinkingAnalyticsProxy.java.meta b/Assets/Plugins/Android/ThinkingAnalyticsProxy.java.meta new file mode 100644 index 0000000..c97f5e1 --- /dev/null +++ b/Assets/Plugins/Android/ThinkingAnalyticsProxy.java.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: 2eaec3210b47f473c8e023bc9410f6e2 +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/Assets/Plugins/Android/ThinkingSDK-thirdparty.aar b/Assets/Plugins/Android/ThinkingSDK-thirdparty.aar new file mode 100644 index 0000000000000000000000000000000000000000..a1c7c4e0ff2275ff440b9c733968318cda266dd4 GIT binary patch literal 23316 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 literal 0 HcmV?d00001 diff --git a/Assets/Plugins/Android/ThinkingSDK-thirdparty.aar.meta b/Assets/Plugins/Android/ThinkingSDK-thirdparty.aar.meta new file mode 100644 index 0000000..8b74aaa --- /dev/null +++ b/Assets/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/Assets/Plugins/Android/gradleTemplate.properties b/Assets/Plugins/Android/gradleTemplate.properties new file mode 100644 index 0000000..4cfad19 --- /dev/null +++ b/Assets/Plugins/Android/gradleTemplate.properties @@ -0,0 +1,10 @@ +org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M +org.gradle.parallel=true +unityStreamingAssets=**STREAMING_ASSETS** +# Android Resolver Properties Start +android.useAndroidX=true +android.enableJetifier=true +# Android Resolver Properties End +**ADDITIONAL_PROPERTIES** + +android.jetifier.ignorelist=annotation-experimental-1.4.0.aar diff --git a/Assets/Plugins/Android/gradleTemplate.properties.meta b/Assets/Plugins/Android/gradleTemplate.properties.meta new file mode 100644 index 0000000..0ae1473 --- /dev/null +++ b/Assets/Plugins/Android/gradleTemplate.properties.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d19bf1415603c584ebc742afa3d23b8e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Android/mainTemplate.gradle b/Assets/Plugins/Android/mainTemplate.gradle new file mode 100644 index 0000000..5ed1629 --- /dev/null +++ b/Assets/Plugins/Android/mainTemplate.gradle @@ -0,0 +1,98 @@ +apply plugin: 'com.android.library' +**APPLY_PLUGINS** + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) +// Android Resolver Dependencies Start + implementation 'androidx.annotation:annotation:1.2.0' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/kwai/Editor/Dependencies.xml:8 + implementation 'androidx.appcompat:appcompat:1.6.1' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/kwai/Editor/Dependencies.xml:6 + implementation 'androidx.browser:browser:1.4.0' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/Editor/Dependencies.xml:4 + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' // Assets/GoogleMobileAds/Editor/GoogleMobileAdsDependencies.xml:12 + implementation 'androidx.lifecycle:lifecycle-process:2.6.2' // Assets/GoogleMobileAds/Editor/GoogleMobileAdsDependencies.xml:17 + implementation 'androidx.media3:media3-exoplayer:1.0.0-alpha01' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/kwai/Editor/Dependencies.xml:5 + // implementation 'androidx.recyclerview:recyclerview:1.1.0' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/gtm/Editor/Dependencies.xml:7 + implementation 'androidx.recyclerview:recyclerview:1.2.1' // Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml:9 + implementation 'com.applovin.mediation:bigoads-adapter:5.5.1.2' // Assets/MaxSdk/Mediation/BigoAds/Editor/Dependencies.xml:4 + implementation 'com.applovin.mediation:bytedance-adapter:7.5.0.3.0' // Assets/MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml:8 + implementation 'com.applovin.mediation:fyber-adapter:8.3.8.0' // Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml:4 + implementation 'com.applovin.mediation:google-adapter:[24.5.0.0]' // Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml:5 + implementation 'com.applovin.mediation:google-ad-manager-adapter:[24.5.0.0]' // Assets/MaxSdk/Mediation/GoogleAdManager/Editor/Dependencies.xml:5 + implementation 'com.applovin.mediation:mintegral-adapter:16.9.91.0' // Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml:8 + implementation 'com.applovin.mediation:moloco-adapter:4.0.0.0' // Assets/MaxSdk/Mediation/Moloco/Editor/Dependencies.xml:4 + implementation 'com.applovin.mediation:unityads-adapter:4.16.1.0' // Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml:4 + implementation 'com.applovin.mediation:vungle-adapter:7.5.1.0' // Assets/MaxSdk/Mediation/Vungle/Editor/Dependencies.xml:4 + implementation 'com.applovin:applovin-sdk:13.3.1' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/applovin/Editor/Dependencies.xml:3 + // implementation 'com.bigossp:bigo-ads:5.0.0' // Assets/BigoSDK/Editor/Dependencies.xml:11 + implementation 'com.bigossp:bigo-ads:5.3.0' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/bigo/Editor/Dependencies.xml:3 + implementation 'com.fyber:marketplace-sdk:8.3.7' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/fyber/Editor/Dependencies.xml:3 + // implementation 'com.google.android.gms:play-services-ads:24.4.0' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/admob/Editor/Dependencies.xml:3 + implementation 'com.google.android.gms:play-services-ads:24.5.0' // Assets/GoogleMobileAds/Editor/GoogleMobileAdsDependencies.xml:7 + // implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/vungle/Editor/Dependencies.xml:5 + implementation 'com.google.android.gms:play-services-ads-identifier:18.2.0' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/pangle/Editor/Dependencies.xml:7 + implementation 'com.google.android.gms:play-services-base:18.7.2' // Assets/rd3/Firebase/Editor/AppDependencies.xml:17 + implementation 'com.google.android.gms:play-services-basement:18.1.0' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/vungle/Editor/Dependencies.xml:4 + implementation 'com.google.android.material:material:1.2.1' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/kwai/Editor/Dependencies.xml:7 + implementation 'com.google.android.ump:user-messaging-platform:3.2.0' // Assets/GoogleMobileAds/Editor/GoogleUmpDependencies.xml:7 + implementation 'com.google.firebase:firebase-analytics:23.0.0' // Assets/rd3/Firebase/Editor/RemoteConfigDependencies.xml:15 + implementation 'com.google.firebase:firebase-analytics-unity:13.1.0' // Assets/rd3/Firebase/Editor/AnalyticsDependencies.xml:18 + implementation 'com.google.firebase:firebase-app-unity:13.1.0' // Assets/rd3/Firebase/Editor/AppDependencies.xml:22 + implementation 'com.google.firebase:firebase-common:22.0.0' // Assets/rd3/Firebase/Editor/AppDependencies.xml:13 + implementation 'com.google.firebase:firebase-config:23.0.0' // Assets/rd3/Firebase/Editor/RemoteConfigDependencies.xml:13 + implementation 'com.google.firebase:firebase-config-unity:13.1.0' // Assets/rd3/Firebase/Editor/RemoteConfigDependencies.xml:20 + implementation 'com.google.firebase:firebase-crashlytics-ndk:20.0.0' // Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml:13 + implementation 'com.google.firebase:firebase-crashlytics-unity:13.1.0' // Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml:20 + implementation 'com.mbridge.msdk.oversea:mbridge_android_sdk:16.9.71' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/gtm/Editor/Dependencies.xml:6 + implementation 'com.pangle.global:pag-sdk:7.2.0.6' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/pangle/Editor/Dependencies.xml:6 + implementation 'com.unity3d.ads:unity-ads:4.14.0' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/unityads/Editor/Dependencies.xml:3 + implementation 'com.vungle:vungle-ads:7.5.0' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/vungle/Editor/Dependencies.xml:3 + implementation 'io.github.kwainetwork:adApi:1.2.19' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/kwai/Editor/Dependencies.xml:3 + implementation 'io.github.kwainetwork:adImpl:1.2.19' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/kwai/Editor/Dependencies.xml:4 + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10' // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/kwai/Editor/Dependencies.xml:9 +// Android Resolver Dependencies End +**DEPS**} + +// Android Resolver Exclusions Start +android { + packagingOptions { + exclude ('/lib/armeabi/*' + '*') + exclude ('/lib/mips/*' + '*') + exclude ('/lib/mips64/*' + '*') + exclude ('/lib/x86/*' + '*') + exclude ('/lib/x86_64/*' + '*') + } +} +// Android Resolver Exclusions End +android { + namespace "com.unity3d.player" + ndkPath "**NDKPATH**" + compileSdkVersion **APIVERSION** + buildToolsVersion '**BUILDTOOLS**' + + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + defaultConfig { + minSdkVersion **MINSDKVERSION** + targetSdkVersion **TARGETSDKVERSION** + ndk { + abiFilters **ABIFILTERS** + } + versionCode **VERSIONCODE** + versionName '**VERSIONNAME**' + consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD** + } + + lintOptions { + abortOnError false + } + + aaptOptions { + noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ') + ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~" + }**PACKAGING_OPTIONS** +} +**IL_CPP_BUILD_SETUP** +**SOURCE_BUILD_SETUP** +**EXTERNAL_SOURCES** diff --git a/Assets/Plugins/Android/mainTemplate.gradle.meta b/Assets/Plugins/Android/mainTemplate.gradle.meta new file mode 100644 index 0000000..54243cf --- /dev/null +++ b/Assets/Plugins/Android/mainTemplate.gradle.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d0ce803953bb6b144b9dced384d6d358 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Android/settingsTemplate.gradle b/Assets/Plugins/Android/settingsTemplate.gradle new file mode 100644 index 0000000..815e48e --- /dev/null +++ b/Assets/Plugins/Android/settingsTemplate.gradle @@ -0,0 +1,42 @@ +pluginManagement { + repositories { + **ARTIFACTORYREPOSITORY** + gradlePluginPortal() + google() + mavenCentral() + } +} + +include ':launcher', ':unityLibrary' +**INCLUDES** + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + repositories { + **ARTIFACTORYREPOSITORY** + google() + mavenCentral() +// Android Resolver Repos Start + def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/") + maven { + url "https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea" // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/gtm/Editor/Dependencies.xml:5, Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml:8 + } + maven { + url "https://artifact.bytedance.com/repository/pangle" // Assets/ThinkupTpnPlugin/AnyThinkAds/Plugins/Android/NonChina/mediation/pangle/Editor/Dependencies.xml:5, Assets/MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml:8 + } + maven { + url "https://repo1.maven.org/maven2/" // Assets/BigoSDK/Editor/Dependencies.xml:11 + } + maven { + url "https://maven.google.com/" // Assets/GoogleMobileAds/Editor/GoogleMobileAdsDependencies.xml:7, Assets/GoogleMobileAds/Editor/GoogleMobileAdsDependencies.xml:12, Assets/GoogleMobileAds/Editor/GoogleMobileAdsDependencies.xml:17, Assets/GoogleMobileAds/Editor/GoogleUmpDependencies.xml:7 + } + maven { + url (unityProjectPath + "/Assets/Firebase/m2repository") // Assets/rd3/Firebase/Editor/AnalyticsDependencies.xml:18, Assets/rd3/Firebase/Editor/AppDependencies.xml:22, Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml:20, Assets/rd3/Firebase/Editor/RemoteConfigDependencies.xml:20 + } + mavenLocal() +// Android Resolver Repos End + flatDir { + dirs "${project(':unityLibrary').projectDir}/libs" + } + } +} diff --git a/Assets/Plugins/Android/settingsTemplate.gradle.meta b/Assets/Plugins/Android/settingsTemplate.gradle.meta new file mode 100644 index 0000000..05bba80 --- /dev/null +++ b/Assets/Plugins/Android/settingsTemplate.gradle.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b7bd8993c4f1e6242a9cfbe9355ae1d0 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/OpenHarmony.meta b/Assets/Plugins/OpenHarmony.meta new file mode 100644 index 0000000..c827a59 --- /dev/null +++ b/Assets/Plugins/OpenHarmony.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 15e16f29589354e96963bf14c4beda9f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/OpenHarmony/NativeBridge.tslib b/Assets/Plugins/OpenHarmony/NativeBridge.tslib new file mode 100644 index 0000000..6aa30f6 --- /dev/null +++ b/Assets/Plugins/OpenHarmony/NativeBridge.tslib @@ -0,0 +1,6 @@ +import { TDOpenHarmonyProxy } from "./TDOpenHarmonyProxy.ts"; +export function RegisterNativeBridge(){ + var register = { } + register["TDOpenHarmonyProxy"] = TDOpenHarmonyProxy; + return register +} diff --git a/Assets/Plugins/OpenHarmony/NativeBridge.tslib.meta b/Assets/Plugins/OpenHarmony/NativeBridge.tslib.meta new file mode 100644 index 0000000..425318d --- /dev/null +++ b/Assets/Plugins/OpenHarmony/NativeBridge.tslib.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 15b10eae4ecca486aa372841269de824 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/OpenHarmony/TDAnalytics.har b/Assets/Plugins/OpenHarmony/TDAnalytics.har new file mode 100644 index 0000000000000000000000000000000000000000..a1180b67dc55047e3192df04741164ac893e406a GIT binary patch literal 21964 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 0) { + config.enableEncrypt(version, publicKey) + } + TDAnalytics.initWithConfig(globalThis.context, config) + } + + static enableLog(enable: boolean) { + TDAnalytics.enableLog(enable) + } + + static setLibraryInfo(libName: string, libVersion: string) { + TDAnalytics.setCustomerLibInfo(libName, libVersion) + } + + static setDistinctId(distinctId: string, appId: string) { + TDAnalytics.setDistinctId(distinctId, appId) + } + + static getDistinctId(appId: string): string { + return TDAnalytics.getDistinctId(appId) + } + + static login(accountId: string, appId: string) { + TDAnalytics.login(accountId, appId) + } + + static logout(appId: string) { + TDAnalytics.logout(appId) + } + + static track(eventName: string, properties: string, timeStamp: number, timeZoneId: string, appId: string) { + try { + let time: Date = null; + let timeZone: I18n.TimeZone = null; + if (timeStamp > 0) { + time = new Date(timeStamp) + if (timeZoneId) { + if (timeZoneId === 'Local') { + timeZone = I18n.getTimeZone() + } else { + timeZone = I18n.getTimeZone(timeZoneId) + } + } + } + TDAnalytics.track({ + eventName: eventName, + properties: this.parseJsonStrict(properties), + time: time, + timeZone: timeZone + }, appId) + } catch (e) { + } + } + + static trackEvent(eventType: number, eventName: string, properties: string, eventId: string, timeStamp: number, + timezoneId: string, appId: string) { + try { + let time: Date = null; + let timeZone: I18n.TimeZone = null; + if (timeStamp > 0) { + time = new Date(timeStamp); + if (timezoneId) { + if (timezoneId == 'Local') { + timeZone = I18n.getTimeZone() + } else { + timeZone = I18n.getTimeZone(timezoneId) + } + } + } + if (eventType == 1) { + TDAnalytics.trackFirst({ + eventName: eventName, + properties: this.parseJsonStrict(properties), + firstCheckId: eventId, + time: time, + timeZone: timeZone + }, appId) + } else if (eventType == 2) { + TDAnalytics.trackUpdate({ + eventName: eventName, + properties: this.parseJsonStrict(properties), + eventId: eventId, + time: time, + timeZone: timeZone + }, appId) + } else if (eventType == 3) { + TDAnalytics.trackOverwrite({ + eventName: eventName, + properties: this.parseJsonStrict(properties), + eventId: eventId, + time: time, + timeZone: timeZone + }, appId) + } + } catch (e) { + } + } + + + static setSuperProperties(superProperties: string, appId: string) { + try { + TDAnalytics.setSuperProperties(this.parseJsonStrict(superProperties), appId) + } catch (e) { + } + } + + static unsetSuperProperty(property: string, appId: string) { + TDAnalytics.unsetSuperProperty(property, appId) + } + + static clearSuperProperties(appId: string) { + TDAnalytics.clearSuperProperties(appId) + } + + static getSuperProperties(appId: string): string { + return TDAnalytics.getSuperProperties(appId) + } + + static getPresetProperties(appId: string): string { + return TDAnalytics.getPresetProperties(appId) + } + + static flush(appId: string) { + TDAnalytics.flush(appId) + } + + static userSet(properties: string, timeStamp: number, appId: string) { + try { + let time: Date = null; + if (timeStamp > 0) { + time = new Date(timeStamp) + } + TDAnalytics.userSet({ + properties: this.parseJsonStrict(properties), + time: time + }, appId) + } catch (e) { + } + } + + static userSetOnce(properties: string, timeStamp: number, appId: string) { + try { + let time: Date = null; + if (timeStamp > 0) { + time = new Date(timeStamp) + } + TDAnalytics.userSetOnce({ + properties: this.parseJsonStrict(properties), + time: time + }, appId) + } catch (e) { + } + } + + static userUnset(property: string, timeStamp: number, appId: string) { + let time: Date = null; + if (timeStamp > 0) { + time = new Date(timeStamp) + } + TDAnalytics.userUnset({ + property: property, + time: time + }, appId) + } + + static userAdd(properties: string, timeStamp: number, appId: string) { + try { + let time: Date = null; + if (timeStamp > 0) { + time = new Date(timeStamp) + } + TDAnalytics.userAdd({ + properties: this.parseJsonStrict(properties), + time: time + }, appId) + } catch (e) { + } + } + + static userAppend(properties: string, timeStamp: number, appId: string) { + try { + let time: Date = null; + if (timeStamp > 0) { + time = new Date(timeStamp) + } + TDAnalytics.userAppend({ + properties: this.parseJsonStrict(properties), + time: time + }, appId) + } catch (e) { + } + } + + static userUniqAppend(properties: string, timeStamp: number, appId: string) { + try { + let time: Date = null; + if (timeStamp > 0) { + time = new Date(timeStamp) + } + TDAnalytics.userUniqAppend({ + properties: this.parseJsonStrict(properties), + time: time + }, appId) + } catch (e) { + } + } + + + static userDelete(timeStamp: number, appId: string) { + let time: Date = null; + if (timeStamp > 0) { + time = new Date(timeStamp) + } + TDAnalytics.userDelete({ + time: time + }, appId) + } + + static getDeviceId(): string { + return TDAnalytics.getDeviceId() + } + + static setNetWorkType(type: number) { + if (type == 2) { + TDAnalytics.setNetworkType(TDNetworkType.WIFI) + } else { + TDAnalytics.setNetworkType(TDNetworkType.ALL) + } + } + + static enableAutoTrack(autoTypes: number, appId: string) { + TDAnalytics.enableAutoTrack(globalThis.context, autoTypes, null, appId) + } + + static timeEvent(eventName: string, appId: string) { + TDAnalytics.timeEvent(eventName, appId) + } + + static calibrateTime(timestamp: number) { + TDAnalytics.calibrateTime(timestamp) + } + + private static parseJsonStrict(jsonString: string): object { + try { + const parsed = JSON.parse(jsonString); + if (typeof parsed !== 'object' || parsed === null) { + return {}; + } + return parsed; + } catch (error) { + return {}; + } + } + +} \ No newline at end of file diff --git a/Assets/Plugins/OpenHarmony/TDOpenHarmonyProxy.ts.meta b/Assets/Plugins/OpenHarmony/TDOpenHarmonyProxy.ts.meta new file mode 100644 index 0000000..215c1eb --- /dev/null +++ b/Assets/Plugins/OpenHarmony/TDOpenHarmonyProxy.ts.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c7ac184e05351426197404368d11c8d2 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC.meta b/Assets/Plugins/PC.meta new file mode 100644 index 0000000..bbe9ed9 --- /dev/null +++ b/Assets/Plugins/PC.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b5e64871c9af447029fea9ebb8a01507 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/AutoTrack.meta b/Assets/Plugins/PC/AutoTrack.meta new file mode 100644 index 0000000..ca4fbe3 --- /dev/null +++ b/Assets/Plugins/PC/AutoTrack.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eefcaefb9c6534ae78d8e6d4345a9531 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs b/Assets/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs new file mode 100644 index 0000000..5aceb94 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs.meta b/Assets/Plugins/PC/AutoTrack/ThinkingSDKAutoTrack.cs.meta new file mode 100644 index 0000000..5401f4e --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Config.meta b/Assets/Plugins/PC/Config.meta new file mode 100644 index 0000000..0d08ab0 --- /dev/null +++ b/Assets/Plugins/PC/Config.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 255aaff7c590d43c0893234eb5279770 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/Config/ThinkingSDKConfig.cs b/Assets/Plugins/PC/Config/ThinkingSDKConfig.cs new file mode 100644 index 0000000..fc04f3f --- /dev/null +++ b/Assets/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 + { + if (result != null && result["code"] != null + && int.Parse(result["code"].ToString()) == 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/Assets/Plugins/PC/Config/ThinkingSDKConfig.cs.meta b/Assets/Plugins/PC/Config/ThinkingSDKConfig.cs.meta new file mode 100644 index 0000000..243dc8d --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Config/ThinkingSDKPublicConfig.cs b/Assets/Plugins/PC/Config/ThinkingSDKPublicConfig.cs new file mode 100644 index 0000000..9c95ea9 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Config/ThinkingSDKPublicConfig.cs.meta b/Assets/Plugins/PC/Config/ThinkingSDKPublicConfig.cs.meta new file mode 100644 index 0000000..d514b1a --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Constant.meta b/Assets/Plugins/PC/Constant.meta new file mode 100644 index 0000000..416a115 --- /dev/null +++ b/Assets/Plugins/PC/Constant.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e5cb18d25c15d4fca82ba5c10c117b9e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/Constant/ThinkingSDKConstant.cs b/Assets/Plugins/PC/Constant/ThinkingSDKConstant.cs new file mode 100644 index 0000000..679f81e --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Constant/ThinkingSDKConstant.cs.meta b/Assets/Plugins/PC/Constant/ThinkingSDKConstant.cs.meta new file mode 100644 index 0000000..0271e3a --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel.meta b/Assets/Plugins/PC/DataModel.meta new file mode 100644 index 0000000..48cac20 --- /dev/null +++ b/Assets/Plugins/PC/DataModel.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a138ac43e601b433093a111aff909263 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/DataModel/ThinkingSDKBaseData.cs b/Assets/Plugins/PC/DataModel/ThinkingSDKBaseData.cs new file mode 100644 index 0000000..4fceb96 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKBaseData.cs.meta b/Assets/Plugins/PC/DataModel/ThinkingSDKBaseData.cs.meta new file mode 100644 index 0000000..bf4ed94 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKEventData.cs b/Assets/Plugins/PC/DataModel/ThinkingSDKEventData.cs new file mode 100644 index 0000000..3d90731 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKEventData.cs.meta b/Assets/Plugins/PC/DataModel/ThinkingSDKEventData.cs.meta new file mode 100644 index 0000000..f3b1fa0 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs b/Assets/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs new file mode 100644 index 0000000..b4b0669 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs.meta b/Assets/Plugins/PC/DataModel/ThinkingSDKFirstEvent.cs.meta new file mode 100644 index 0000000..e177423 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs b/Assets/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs new file mode 100644 index 0000000..ec2c1b2 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs.meta b/Assets/Plugins/PC/DataModel/ThinkingSDKOverWritableEvent.cs.meta new file mode 100644 index 0000000..b4ba599 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs b/Assets/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs new file mode 100644 index 0000000..da8139c --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs.meta b/Assets/Plugins/PC/DataModel/ThinkingSDKUpdateEvent.cs.meta new file mode 100644 index 0000000..a6ac63d --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKUserData.cs b/Assets/Plugins/PC/DataModel/ThinkingSDKUserData.cs new file mode 100644 index 0000000..652721d --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/DataModel/ThinkingSDKUserData.cs.meta b/Assets/Plugins/PC/DataModel/ThinkingSDKUserData.cs.meta new file mode 100644 index 0000000..74880bb --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Main.meta b/Assets/Plugins/PC/Main.meta new file mode 100644 index 0000000..7a6a436 --- /dev/null +++ b/Assets/Plugins/PC/Main.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3166be662c1c1445387662fcb7118be5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/Main/LightThinkingSDKInstance.cs b/Assets/Plugins/PC/Main/LightThinkingSDKInstance.cs new file mode 100644 index 0000000..b5e421a --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Main/LightThinkingSDKInstance.cs.meta b/Assets/Plugins/PC/Main/LightThinkingSDKInstance.cs.meta new file mode 100644 index 0000000..9fba05a --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Main/ThinkingPCSDK.cs b/Assets/Plugins/PC/Main/ThinkingPCSDK.cs new file mode 100644 index 0000000..21b038f --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Main/ThinkingPCSDK.cs.meta b/Assets/Plugins/PC/Main/ThinkingPCSDK.cs.meta new file mode 100644 index 0000000..0cb66c0 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Main/ThinkingSDKInstance.cs b/Assets/Plugins/PC/Main/ThinkingSDKInstance.cs new file mode 100644 index 0000000..fdf463c --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Main/ThinkingSDKInstance.cs.meta b/Assets/Plugins/PC/Main/ThinkingSDKInstance.cs.meta new file mode 100644 index 0000000..02bd125 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Request.meta b/Assets/Plugins/PC/Request.meta new file mode 100644 index 0000000..4d9cce4 --- /dev/null +++ b/Assets/Plugins/PC/Request.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: db5e3ec02eff54121adead1f781264b7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/Request/ThinkingSDKBaseRequest.cs b/Assets/Plugins/PC/Request/ThinkingSDKBaseRequest.cs new file mode 100644 index 0000000..9a67146 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Request/ThinkingSDKBaseRequest.cs.meta b/Assets/Plugins/PC/Request/ThinkingSDKBaseRequest.cs.meta new file mode 100644 index 0000000..c52a4c1 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Request/ThinkingSDKDebugRequest.cs b/Assets/Plugins/PC/Request/ThinkingSDKDebugRequest.cs new file mode 100644 index 0000000..8aeb9b2 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Request/ThinkingSDKDebugRequest.cs.meta b/Assets/Plugins/PC/Request/ThinkingSDKDebugRequest.cs.meta new file mode 100644 index 0000000..e8c9592 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Request/ThinkingSDKNormalRequest.cs b/Assets/Plugins/PC/Request/ThinkingSDKNormalRequest.cs new file mode 100644 index 0000000..082d158 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Request/ThinkingSDKNormalRequest.cs.meta b/Assets/Plugins/PC/Request/ThinkingSDKNormalRequest.cs.meta new file mode 100644 index 0000000..b1ff204 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Storage.meta b/Assets/Plugins/PC/Storage.meta new file mode 100644 index 0000000..56bae73 --- /dev/null +++ b/Assets/Plugins/PC/Storage.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 21f73e209611b40d3a1c87e9418529b2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/Storage/ThinkingSDKFile.cs b/Assets/Plugins/PC/Storage/ThinkingSDKFile.cs new file mode 100644 index 0000000..caca170 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Storage/ThinkingSDKFile.cs.meta b/Assets/Plugins/PC/Storage/ThinkingSDKFile.cs.meta new file mode 100644 index 0000000..8ad1e33 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Storage/ThinkingSDKFileJson.cs b/Assets/Plugins/PC/Storage/ThinkingSDKFileJson.cs new file mode 100644 index 0000000..18e9cd7 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Storage/ThinkingSDKFileJson.cs.meta b/Assets/Plugins/PC/Storage/ThinkingSDKFileJson.cs.meta new file mode 100644 index 0000000..b08277c --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/TaskManager.meta b/Assets/Plugins/PC/TaskManager.meta new file mode 100644 index 0000000..bd2f83f --- /dev/null +++ b/Assets/Plugins/PC/TaskManager.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 372921461f6fe4648827f1a91a1759e1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/TaskManager/ThinkingSDKTask.cs b/Assets/Plugins/PC/TaskManager/ThinkingSDKTask.cs new file mode 100644 index 0000000..3d1380e --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/TaskManager/ThinkingSDKTask.cs.meta b/Assets/Plugins/PC/TaskManager/ThinkingSDKTask.cs.meta new file mode 100644 index 0000000..ce3d9c5 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/ThinkingSDK.asmdef b/Assets/Plugins/PC/ThinkingSDK.asmdef new file mode 100644 index 0000000..4c20b5e --- /dev/null +++ b/Assets/Plugins/PC/ThinkingSDK.asmdef @@ -0,0 +1,3 @@ +{ + "name": "ThinkingSDK" +} diff --git a/Assets/Plugins/PC/ThinkingSDK.asmdef.meta b/Assets/Plugins/PC/ThinkingSDK.asmdef.meta new file mode 100644 index 0000000..a78dc3e --- /dev/null +++ b/Assets/Plugins/PC/ThinkingSDK.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0a958a7eb80a248e1b8bc4553787c209 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/Time.meta b/Assets/Plugins/PC/Time.meta new file mode 100644 index 0000000..ee5221a --- /dev/null +++ b/Assets/Plugins/PC/Time.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c859b53e53b824a2aa36430c980862cf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/Time/TDTimeout.cs b/Assets/Plugins/PC/Time/TDTimeout.cs new file mode 100644 index 0000000..3c2de2c --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/TDTimeout.cs.meta b/Assets/Plugins/PC/Time/TDTimeout.cs.meta new file mode 100644 index 0000000..81367bd --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs b/Assets/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs new file mode 100644 index 0000000..cd24363 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs.meta b/Assets/Plugins/PC/Time/ThinkingSDKCalibratedTime.cs.meta new file mode 100644 index 0000000..4d4dc14 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKDefinedTime.cs b/Assets/Plugins/PC/Time/ThinkingSDKDefinedTime.cs new file mode 100644 index 0000000..654a19e --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKDefinedTime.cs.meta b/Assets/Plugins/PC/Time/ThinkingSDKDefinedTime.cs.meta new file mode 100644 index 0000000..7cdf3fa --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs b/Assets/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs new file mode 100644 index 0000000..647b7c6 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs.meta b/Assets/Plugins/PC/Time/ThinkingSDKNTPCalibration.cs.meta new file mode 100644 index 0000000..09d2677 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKTime.cs b/Assets/Plugins/PC/Time/ThinkingSDKTime.cs new file mode 100644 index 0000000..4f15c55 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKTime.cs.meta b/Assets/Plugins/PC/Time/ThinkingSDKTime.cs.meta new file mode 100644 index 0000000..97d6856 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs b/Assets/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs new file mode 100644 index 0000000..a4096a0 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs.meta b/Assets/Plugins/PC/Time/ThinkingSDKTimeCalibration.cs.meta new file mode 100644 index 0000000..202b812 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKTimeInter.cs b/Assets/Plugins/PC/Time/ThinkingSDKTimeInter.cs new file mode 100644 index 0000000..82c17fa --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKTimeInter.cs.meta b/Assets/Plugins/PC/Time/ThinkingSDKTimeInter.cs.meta new file mode 100644 index 0000000..1671847 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs b/Assets/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs new file mode 100644 index 0000000..d3da381 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs.meta b/Assets/Plugins/PC/Time/ThinkingSDKTimestampCalibration.cs.meta new file mode 100644 index 0000000..f2bbbca --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils.meta b/Assets/Plugins/PC/Utils.meta new file mode 100644 index 0000000..570e5c6 --- /dev/null +++ b/Assets/Plugins/PC/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0ff15ea6a25e04a79b1289cb0664ef16 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PC/Utils/ThinkingSDKAppInfo.cs b/Assets/Plugins/PC/Utils/ThinkingSDKAppInfo.cs new file mode 100644 index 0000000..12b4eb5 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKAppInfo.cs.meta b/Assets/Plugins/PC/Utils/ThinkingSDKAppInfo.cs.meta new file mode 100644 index 0000000..eee0a1f --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs b/Assets/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs new file mode 100644 index 0000000..153ced7 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs.meta b/Assets/Plugins/PC/Utils/ThinkingSDKDeviceInfo.cs.meta new file mode 100644 index 0000000..e9931ec --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKJSON.cs b/Assets/Plugins/PC/Utils/ThinkingSDKJSON.cs new file mode 100644 index 0000000..b3d844f --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKJSON.cs.meta b/Assets/Plugins/PC/Utils/ThinkingSDKJSON.cs.meta new file mode 100644 index 0000000..de5cd85 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKLogger.cs b/Assets/Plugins/PC/Utils/ThinkingSDKLogger.cs new file mode 100644 index 0000000..aaebf35 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKLogger.cs.meta b/Assets/Plugins/PC/Utils/ThinkingSDKLogger.cs.meta new file mode 100644 index 0000000..4de59a8 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs b/Assets/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs new file mode 100644 index 0000000..2bbb844 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs.meta b/Assets/Plugins/PC/Utils/ThinkingSDKTimeUtil.cs.meta new file mode 100644 index 0000000..8371099 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKUtil.cs b/Assets/Plugins/PC/Utils/ThinkingSDKUtil.cs new file mode 100644 index 0000000..4a47bf1 --- /dev/null +++ b/Assets/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/Assets/Plugins/PC/Utils/ThinkingSDKUtil.cs.meta b/Assets/Plugins/PC/Utils/ThinkingSDKUtil.cs.meta new file mode 100644 index 0000000..5e83b32 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/Firebase/libFirebaseCppCrashlytics.a b/Assets/Plugins/iOS/Firebase/libFirebaseCppCrashlytics.a new file mode 100644 index 0000000000000000000000000000000000000000..27a9c31307ea3638b821098c2210352bd6a292ea GIT binary patch literal 885464 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

Nl?~2}kacPN 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�Lj<_f2(xn!GFadp{nBMJ06sm3a)%D9sN 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

7MI3H@88}7$XKpM^m0x$K!NG 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 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 selectorV4 = NSSelectorFromString(@"addSessionCallbackParameter:value:"); + SEL selectorV5 = NSSelectorFromString(@"addGlobalCallbackParameter:forKey:"); + if (cls != nil) { + if ([cls respondsToSelector:selectorV4]) { + [cls performSelector:selectorV4 withObject:TA_ACCOUNT_ID withObject:accountID]; + [cls performSelector:selectorV4 withObject:TA_DISTINCT_ID withObject:distinctId]; + } else if ([cls respondsToSelector:selectorV5]) { + [cls performSelector:selectorV5 withObject:accountID withObject:TA_ACCOUNT_ID]; + [cls performSelector:selectorV5 withObject:distinctId withObject:TA_DISTINCT_ID]; + } + } +} +#pragma clang diagnostic pop + +@end diff --git a/Assets/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m.meta new file mode 100644 index 0000000..3733857 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAAdjustSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5eb03a70bd3e04ffaa297aa9944119ab +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h b/Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h new file mode 100644 index 0000000..74e9db6 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h.meta b/Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h.meta new file mode 100644 index 0000000..d193490 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bef7b8a1ee5a94c378327f8cc1fbd2c2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m b/Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m new file mode 100644 index 0000000..a155bfb --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m.meta new file mode 100644 index 0000000..462e5bd --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAAppLovinSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6acd01cb07ff941398f5ac76b64d668b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h b/Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h new file mode 100644 index 0000000..3e2e4da --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h.meta b/Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h.meta new file mode 100644 index 0000000..4f45d34 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b808938d5a4ef44cd9417fcaa09304a4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m b/Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m new file mode 100644 index 0000000..75b7171 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m.meta new file mode 100644 index 0000000..701459b --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAAppsFlyerSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b373ccef5fc814dd3b5cca3544ef6d2e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.h b/Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.h new file mode 100644 index 0000000..99e9b02 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.h.meta b/Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.h.meta new file mode 100644 index 0000000..d3bdabb --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 65771a2840d484c718e6166b779b5d9b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.m b/Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.m new file mode 100644 index 0000000..b276be8 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.m.meta new file mode 100644 index 0000000..d1fe240 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TABaseSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 517862af209ce4ad4bd8002a327fed73 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.h b/Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.h new file mode 100644 index 0000000..0d11db3 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.h.meta b/Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.h.meta new file mode 100644 index 0000000..adf2d92 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ca22a7263ae5145cba856e4bd480b2bb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.m b/Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.m new file mode 100644 index 0000000..c810dd0 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.m.meta new file mode 100644 index 0000000..f0dcbb7 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TABranchSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 14d24e07c10e04914806b65a10266790 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h b/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h new file mode 100644 index 0000000..346faca --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h.meta b/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h.meta new file mode 100644 index 0000000..2ddc6b2 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 05884726aff3f4d0a8705c380924ac0f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m b/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m new file mode 100644 index 0000000..668090b --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m @@ -0,0 +1,181 @@ +// +// TAFirebaseSyncData.m +// ThinkingSDK.default-Base-Core-Extension-Util-iOS +// +// Created by wwango on 2022/9/28. +// + +#import "TAFirebaseSyncData.h" +#import +//#import + +@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]; +// } + + [self registTDMethods]; +} + + +#pragma mark - Firebase Analytics + +//// 原始方法实现 +//void (*ori_method_logEventWithOrigin_IMP)(id, SEL, id, BOOL, id, id); +//// 交换方法实现 +//void method_td_logEventWithOrigin_IMP(id self, SEL _cmd, id orgin, BOOL isPublicEvent, id name, id parameters) { +// NSLog(@"This is a dynamic method: [%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); +// if(ori_method_logEventWithOrigin_IMP) { +// ori_method_logEventWithOrigin_IMP(self, _cmd, orgin, isPublicEvent, name, parameters); +// } +//} + +//// 原始方法实现 +//void (*ori_method_queueOperationWithBlock_IMP)(id, SEL, id); +//// 交换方法实现 +//void method_td_queueOperationWithBlock_IMP(id self, SEL _cmd, id block) { +// NSLog(@"This is a dynamic method: [%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); +// if(ori_method_queueOperationWithBlock_IMP) { +// ori_method_queueOperationWithBlock_IMP(self, _cmd, block); +// } +//} + +// 原始方法实现 +void (*ori_method_notifyEventListeners_IMP)(id, SEL, id); +// 交换方法实现 +void method_td_notifyEventListeners_IMP(id self, SEL _cmd, id notify) { + NSString *name = [notify valueForKey:@"_name"]; + //NSString *origin = [notify valueForKey:@"_origin"]; + NSDictionary *parameters = [notify valueForKey:@"_parameters"]; + [NSClassFromString(@"TDAnalytics") performSelector:NSSelectorFromString(@"track:properties:") withObject:name withObject:parameters]; + if(ori_method_notifyEventListeners_IMP) { + ori_method_notifyEventListeners_IMP(self, _cmd, notify); + } +} + + + +// 原始方法实现 +void (*ori_method_setUserPropertyString_IMP)(id, SEL, id, id); +// 交换方法实现 +void method_td_setUserPropertyString_IMP(id self, SEL _cmd, id value, id name) { + [NSClassFromString(@"TDAnalytics") performSelector:NSSelectorFromString(@"setSuperProperties:") withObject:[NSDictionary dictionaryWithObject:value forKey:name]]; + if(ori_method_setUserPropertyString_IMP) { + ori_method_setUserPropertyString_IMP(self, _cmd, value, name); + } +} + +// 原始方法实现 +void (*ori_method_setUserID_IMP)(id, SEL, id); +// 交换方法实现 +void method_td_setUserID_IMP(id self, SEL _cmd, id userId) { + [NSClassFromString(@"TDAnalytics") performSelector:NSSelectorFromString(@"setSuperProperties:") withObject:[NSDictionary dictionaryWithObject:userId forKey:@"userId"]]; + if(ori_method_setUserID_IMP) { + ori_method_setUserID_IMP(self, _cmd, userId); + } +} + + +// 原始方法实现 +void (*ori_method_setDefaultEventParameters_IMP)(id, SEL, id); +// 交换方法实现 +void method_td_setDefaultEventParameters_IMP(id self, SEL _cmd, id parameters) { + [NSClassFromString(@"TDAnalytics") performSelector:NSSelectorFromString(@"setSuperProperties:") withObject:parameters]; + if(ori_method_setDefaultEventParameters_IMP) { + ori_method_setDefaultEventParameters_IMP(self, _cmd, parameters); + } +} + +- (void)registTDMethods { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + // APMAnalytics method exchange + Class desClass_APMAnalytics = objc_getClass("APMAnalytics"); + Class metaClass_APMAnalytics = object_getClass(desClass_APMAnalytics); + bool ret = NO; + //ret = class_addMethod(metaClass_APMAnalytics, NSSelectorFromString(@"td_logEventWithOrigin:isPublicEvent:name:parameters:"), (IMP)(method_td_logEventWithOrigin_IMP), "v@:@B@@"); + //if (ret) { + // Method method_logEventWithOrigin = class_getClassMethod(metaClass_APMAnalytics, NSSelectorFromString(@"logEventWithOrigin:isPublicEvent:name:parameters:")); + // Method method_td_logEventWithOrigin = class_getClassMethod(metaClass_APMAnalytics, NSSelectorFromString(@"td_logEventWithOrigin:isPublicEvent:name:parameters:")); + // //保存原始实现 + // ori_method_logEventWithOrigin_IMP = (void(*)(id, SEL, id, BOOL, id, id))method_getImplementation(method_logEventWithOrigin); + // //交换方法实现 + // method_exchangeImplementations(method_logEventWithOrigin, method_td_logEventWithOrigin); + //} + + //ret = class_addMethod(metaClass_APMAnalytics, NSSelectorFromString(@"td_queueOperationWithBlock:"), (IMP)(method_td_queueOperationWithBlock_IMP), "v@:@"); + //if (ret) { + // Method method_queueOperationWithBlock = class_getClassMethod(metaClass_APMAnalytics, NSSelectorFromString(@"queueOperationWithBlock:")); + // Method method_td_queueOperationWithBlock = class_getClassMethod(metaClass_APMAnalytics, NSSelectorFromString(@"td_queueOperationWithBlock:")); + // //保存原始实现 + // ori_method_queueOperationWithBlock_IMP = (void(*)(id, SEL, id))method_getImplementation(method_queueOperationWithBlock); + // //交换方法实现 + // method_exchangeImplementations(method_queueOperationWithBlock, method_td_queueOperationWithBlock); + //} + + ret = class_addMethod(metaClass_APMAnalytics, NSSelectorFromString(@"td_notifyEventListeners:"), (IMP)(method_td_notifyEventListeners_IMP), "v@:@"); + if (ret) { + Method method_notifyEventListeners = class_getClassMethod(metaClass_APMAnalytics, NSSelectorFromString(@"notifyEventListeners:")); + Method method_td_notifyEventListeners = class_getClassMethod(metaClass_APMAnalytics, NSSelectorFromString(@"td_notifyEventListeners:")); + //保存原始实现 + ori_method_notifyEventListeners_IMP = (void(*)(id, SEL, id))method_getImplementation(method_notifyEventListeners); + //交换方法实现 + method_exchangeImplementations(method_notifyEventListeners, method_td_notifyEventListeners); + } + + + // FIRAnalytics method exchange + Class desClass_FIRAnalytics = objc_getClass("FIRAnalytics"); + Class metaClass_FIRAnalytics = object_getClass(desClass_FIRAnalytics); + ret = class_addMethod(metaClass_FIRAnalytics, NSSelectorFromString(@"td_setUserPropertyString:forName:"), (IMP)(method_td_setUserPropertyString_IMP), "v@:@@"); + if (ret) { + Method method_setUserPropertyString = class_getClassMethod(metaClass_FIRAnalytics, NSSelectorFromString(@"setUserPropertyString:forName:")); + Method method_td_setUserPropertyString = class_getClassMethod(metaClass_FIRAnalytics, NSSelectorFromString(@"td_setUserPropertyString:forName:")); + //保存原始实现 + ori_method_setUserPropertyString_IMP = (void(*)(id, SEL, id, id))method_getImplementation(method_setUserPropertyString); + //交换方法实现 + method_exchangeImplementations(method_setUserPropertyString, method_td_setUserPropertyString); + } + + + ret = class_addMethod(metaClass_FIRAnalytics, NSSelectorFromString(@"td_setUserID:"), (IMP)(method_td_setUserID_IMP), "v@:@"); + if (ret) { + Method method_setUserID = class_getClassMethod(metaClass_FIRAnalytics, NSSelectorFromString(@"setUserID:")); + Method method_td_setUserID = class_getClassMethod(metaClass_FIRAnalytics, NSSelectorFromString(@"td_setUserID:")); + //保存原始实现 + ori_method_setUserID_IMP = (void(*)(id, SEL, id))method_getImplementation(method_setUserID); + //交换方法实现 + method_exchangeImplementations(method_setUserID, method_td_setUserID); + } + + + ret = class_addMethod(metaClass_FIRAnalytics, NSSelectorFromString(@"td_setDefaultEventParameters:"), (IMP)(method_td_setDefaultEventParameters_IMP), "v@:@"); + if (ret) { + Method method_setDefaultEventParameters = class_getClassMethod(metaClass_FIRAnalytics, NSSelectorFromString(@"setDefaultEventParameters:")); + Method method_td_setDefaultEventParameters = class_getClassMethod(metaClass_FIRAnalytics, NSSelectorFromString(@"td_setDefaultEventParameters:")); + //保存原始实现 + ori_method_setDefaultEventParameters_IMP = (void(*)(id, SEL, id))method_getImplementation(method_setDefaultEventParameters); + //交换方法实现 + method_exchangeImplementations(method_setDefaultEventParameters, method_td_setDefaultEventParameters); + } + }); +} + +#pragma clang diagnostic pop + +@end diff --git a/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m.meta new file mode 100644 index 0000000..19ec555 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAFirebaseSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f55372f8b0d004744a0d5601f3bdf1e5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h b/Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h new file mode 100644 index 0000000..e89d705 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h.meta b/Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h.meta new file mode 100644 index 0000000..2a8fcc8 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7fccbd96cdae8450181ef923883295d9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m b/Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m new file mode 100644 index 0000000..99c24f8 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m.meta new file mode 100644 index 0000000..ad8bfa8 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAIronSourceSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 50723cb7b78a24331b43bba7ee34caf4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h b/Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h new file mode 100644 index 0000000..ada1929 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h.meta b/Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h.meta new file mode 100644 index 0000000..771248a --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5908d2c27b80a489dae892a870b74de1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m b/Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m new file mode 100644 index 0000000..29a1fc2 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m.meta new file mode 100644 index 0000000..a0d4ea4 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAKochavaSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9dfff7df542cd401fa7592b206725a02 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.h b/Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.h new file mode 100644 index 0000000..516410d --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.h.meta b/Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.h.meta new file mode 100644 index 0000000..a55686a --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e64632bcc60a04ad78c1baa60002266c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.m b/Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.m new file mode 100644 index 0000000..d26258e --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.m.meta new file mode 100644 index 0000000..5182f8a --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAReYunSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fb3609527c6d545288b56199264a3800 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAThirdParty.h b/Assets/Plugins/iOS/TAThirdParty/TAThirdParty.h new file mode 100644 index 0000000..6df5b8e --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAThirdParty.h @@ -0,0 +1,13 @@ +// +// TAThirdParty.h +// TAThirdParty +// +// Created by Charles on 9.1.23. +// + +#import + +// version = 0.3.5 +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Assets/Plugins/iOS/TAThirdParty/TAThirdParty.h.meta b/Assets/Plugins/iOS/TAThirdParty/TAThirdParty.h.meta new file mode 100644 index 0000000..9e9c6b8 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAThirdParty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 97f6ec784b7384c92ac47dcd94bf5d5a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h b/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h new file mode 100644 index 0000000..01bd1c6 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h.meta b/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h.meta new file mode 100644 index 0000000..bd46834 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 17cf558a565af4f988f6a4a875217e78 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m b/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m new file mode 100644 index 0000000..7e07b03 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m @@ -0,0 +1,175 @@ +// +// 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, TDThirdPartyType) { + TDThirdPartyTypeNone = 0, + TDThirdPartyTypeAppsFlyer = 1 << 0, + TDThirdPartyTypeIronSource = 1 << 1, + TDThirdPartyTypeAdjust = 1 << 2, + TDThirdPartyTypeBranch = 1 << 3, + TDThirdPartyTypeTopOn = 1 << 4, + TDThirdPartyTypeTracking = 1 << 5, + TDThirdPartyTypeTradPlus = 1 << 6, + TDThirdPartyTypeAppLovin = 1 << 7, + TDThirdPartyTypeKochava = 1 << 8, + TDThirdPartyTypeTalkingData = 1 << 9, + TDThirdPartyTypeFirebase = 1 << 10, +}; + +static NSMutableDictionary *_thirdPartyManagerMap; + +static NSString * const KEY_THIRD_PARTY_CLASS_NAME = @"libClass"; +static NSString * const KEY_TA_PLUGIN_CLASS_NAME = @"taThirdClass"; +static NSString * const KEY_ERROR_MESSAGE = @"errorMes"; + +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 *)type instance:(id)instance property:(NSDictionary *)property +{ + NSArray *thirdPartyList = [self _getThridInfoWithType:type]; + + for (NSInteger i = 0; i < thirdPartyList.count; i++) { + NSDictionary *info = thirdPartyList[i]; + + NSString *libClass = info[KEY_THIRD_PARTY_CLASS_NAME]; + NSString *taThirdClass = info[KEY_TA_PLUGIN_CLASS_NAME]; + NSString *errorMes = info[KEY_ERROR_MESSAGE]; + + if (!NSClassFromString(libClass)) { + NSLog(@"[ThinkingData][Error] %@", 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(@"[ThinkingData][Info] %@ , SyncThirdData Success", NSClassFromString(libClass)); + } + } +} + +- (NSArray *)_getThridInfoWithType:(NSNumber *)type { + NSInteger typeNum = type.integerValue; + + NSMutableArray *mutableArray = [NSMutableArray array]; + + if (typeNum & TDThirdPartyTypeAppsFlyer) { + [mutableArray addObject:@{ + KEY_THIRD_PARTY_CLASS_NAME: @"AppsFlyerLib", + KEY_TA_PLUGIN_CLASS_NAME: @"TAAppsFlyerSyncData", + KEY_ERROR_MESSAGE: @"AppsFlyer Data synchronization exception: not installed AppsFlyer SDK" + }]; + } + + if (typeNum & TDThirdPartyTypeIronSource) { + [mutableArray addObject:@{ + KEY_THIRD_PARTY_CLASS_NAME: @"IronSource", + KEY_TA_PLUGIN_CLASS_NAME:@"TAIronSourceSyncData", + KEY_ERROR_MESSAGE: @"IronSource Data synchronization exception: not installed IronSource SDK" + }]; + } + + if (typeNum & TDThirdPartyTypeAdjust) { + [mutableArray addObject:@{ + KEY_THIRD_PARTY_CLASS_NAME: @"Adjust", + KEY_TA_PLUGIN_CLASS_NAME:@"TAAdjustSyncData", + KEY_ERROR_MESSAGE: @"Adjust Data synchronization exception: not installed Adjust SDK" + }]; + } + + if (typeNum & TDThirdPartyTypeBranch) { + [mutableArray addObject:@{ + KEY_THIRD_PARTY_CLASS_NAME: @"Branch", + KEY_TA_PLUGIN_CLASS_NAME:@"TABranchSyncData", + KEY_ERROR_MESSAGE: @"Branch Data synchronization exception: not installed Branch SDK" + }]; + } + + if (typeNum & TDThirdPartyTypeTopOn) { + [mutableArray addObject:@{ + KEY_THIRD_PARTY_CLASS_NAME: @"ATAPI", + KEY_TA_PLUGIN_CLASS_NAME:@"TATopOnSyncData", + KEY_ERROR_MESSAGE: @"TopOn Data synchronization exception: not installed TopOn SDK" + }]; + } + + if (typeNum & TDThirdPartyTypeTracking) { + [mutableArray addObject:@{ + KEY_THIRD_PARTY_CLASS_NAME: @"Tracking", + KEY_TA_PLUGIN_CLASS_NAME:@"TAReYunSyncData", + KEY_ERROR_MESSAGE: @"ReYun Data synchronization exception: Data synchronization exception: not installed SDK" + }]; + } + + if (typeNum & TDThirdPartyTypeTradPlus) { + [mutableArray addObject:@{ + KEY_THIRD_PARTY_CLASS_NAME: @"TradPlus", + KEY_TA_PLUGIN_CLASS_NAME:@"TATradPlusSyncData", + KEY_ERROR_MESSAGE: @"TradPlus Data synchronization exception: not installed TradPlus SDK" + }]; + } + + if (typeNum & TDThirdPartyTypeAppLovin) { + [mutableArray addObject:@{ + KEY_THIRD_PARTY_CLASS_NAME: @"ALSdk", + KEY_TA_PLUGIN_CLASS_NAME:@"TAAppLovinSyncData", + KEY_ERROR_MESSAGE: @"AppLovin Data synchronization exception: not installed AppLovin SDK" + }]; + } + + if (typeNum & TDThirdPartyTypeKochava) { + [mutableArray addObject:@{ + KEY_THIRD_PARTY_CLASS_NAME: @"KVATracker", + KEY_TA_PLUGIN_CLASS_NAME:@"TAKochavaSyncData", + KEY_ERROR_MESSAGE: @"Kochava Data synchronization exception: not installed Kochava SDK" + }]; + } + + if (typeNum & TDThirdPartyTypeFirebase) { + [mutableArray addObject:@{ + KEY_THIRD_PARTY_CLASS_NAME: @"FIRAnalytics", + KEY_TA_PLUGIN_CLASS_NAME:@"TAFirebaseSyncData", + KEY_ERROR_MESSAGE: @"FIREBASE Data synchronization exception: not installed FIRAnalytics SDK" + }]; + } + + return [mutableArray copy]; +} + +@end diff --git a/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m.meta b/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m.meta new file mode 100644 index 0000000..e7583c8 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAThirdPartyManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6a1080195ae5b4aea9bc7c1589134f3f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h b/Assets/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h new file mode 100644 index 0000000..3f78b17 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h.meta b/Assets/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h.meta new file mode 100644 index 0000000..c0c3540 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TAThirdPartySyncProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d31a489f12eb54653b5cf6444e4245d6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.h b/Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.h new file mode 100644 index 0000000..e3eb68b --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.h.meta b/Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.h.meta new file mode 100644 index 0000000..c512ac3 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9cb377c463bc14281934790c36a376c0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.m b/Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.m new file mode 100644 index 0000000..b383cd6 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.m.meta new file mode 100644 index 0000000..8020cdd --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TATopOnSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: dbca3c50b30be4ef88f32c10856288c8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h b/Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h new file mode 100644 index 0000000..5d94ce6 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h.meta b/Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h.meta new file mode 100644 index 0000000..3152340 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f0682c1aaffe14280b29c0bd11d0954e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m b/Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m new file mode 100644 index 0000000..1603792 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m.meta b/Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m.meta new file mode 100644 index 0000000..d64d6f4 --- /dev/null +++ b/Assets/Plugins/iOS/TAThirdParty/TATradPlusSyncData.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e00d06dbb27e94407b943499f2d08daf +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingAnalytics.m b/Assets/Plugins/iOS/ThinkingAnalytics.m new file mode 100644 index 0000000..4ea1cea --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingAnalytics.m @@ -0,0 +1,520 @@ +#if __has_include() +#import +#import +#else +#import "ThinkingSDK.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]); +} + +const char *ta_get_account_id(const char *app_id) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSString *account_id = [TDAnalytics getAccountIdWithAppId:app_id_string]; + return ta_strdup([account_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, int type,const char *event_name,const char *properties,const char *event_id,long long time_stamp_millis, const char *timezone) { + NSString *app_id_string = app_id != NULL ? [NSString stringWithUTF8String:app_id] : nil; + NSString *event_name_string = event_name != NULL ? [NSString stringWithUTF8String:event_name] : nil; + NSString *event_id_string = event_id != NULL ? [NSString stringWithUTF8String:event_id] : nil; + TDEventModel *eventModel; + if(type == 0){ + eventModel = [[TDFirstEventModel alloc] initWithEventName:event_name_string firstCheckID:event_id_string]; + }else if(type == 1){ + eventModel = [[TDUpdateEventModel alloc] initWithEventName:event_name_string eventID:event_id_string]; + }else if(type == 2){ + eventModel = [[TDOverwriteEventModel alloc] initWithEventName:event_name_string eventID:event_id_string]; + } + if(eventModel == NULL) return; + NSDictionary *evevent_properties = nil; + ta_convertToDictionary(properties, &evevent_properties); + eventModel.properties = evevent_properties; + NSString *timezoneString = timezone != NULL ? [NSString stringWithUTF8String:timezone] : nil; + NSTimeZone *tz; + if ([@"Local" isEqualToString:timezoneString]) { + tz = [NSTimeZone localTimeZone]; + } else { + tz = [NSTimeZone timeZoneWithName:timezoneString]; + } + if (time_stamp_millis > 0) { + NSDate *date = [NSDate dateWithTimeIntervalSince1970:time_stamp_millis/1000.0]; + [eventModel configTime:date timeZone:tz]; + } + [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/Assets/Plugins/iOS/ThinkingAnalytics.m.meta b/Assets/Plugins/iOS/ThinkingAnalytics.m.meta new file mode 100644 index 0000000..3fff6fd --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingAnalytics.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4a3a74316bafb412290585db3ac5a04e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore.meta b/Assets/Plugins/iOS/ThinkingDataCore.meta new file mode 100644 index 0000000..578f28d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dc9ce7e195a034b4383e3fc6dac4de33 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes.meta new file mode 100644 index 0000000..4973e99 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 00c57f8b45eef4696976079b9f2a9b9d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime.meta new file mode 100644 index 0000000..7d07547 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4a43ba7904865402dab9870feb41b38f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.h new file mode 100644 index 0000000..d86eaf4 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.h @@ -0,0 +1,17 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCalibratedTime : NSObject +@property (atomic, assign, readonly) BOOL hasBeenCalibrated; + ++ (instancetype)sharedInstance; + ++ (NSDate *)now; + +- (void)recalibrationWithTimeInterval:(NSTimeInterval)timestamp; +- (void)recalibrationWithNtps:(NSArray *)ntpServers; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.h.meta new file mode 100644 index 0000000..bcc019a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8c2f861958ac24d76bcd736e04612643 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.m new file mode 100644 index 0000000..94bd8be --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.m @@ -0,0 +1,87 @@ +#import "TDCalibratedTime.h" +#import "TDCoreDeviceInfo.h" +#import "TDNTPServer.h" +#import "TDNotificationManager+Core.h" +#import "TDCoreLog.h" + +@interface TDCalibratedTime () +@property (atomic, assign) NSTimeInterval deviceBootTime; +@property (atomic, assign) NSTimeInterval serverTime; +@property (atomic, assign) BOOL hasBeenCalibrated; + +@end + + +@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.hasBeenCalibrated = NO; + } + return self; +} + ++ (NSDate *)now { + NSDate *date = nil; + TDCalibratedTime *calibrated = [TDCalibratedTime sharedInstance]; + if (calibrated.hasBeenCalibrated) { + NSTimeInterval outTime = [TDCoreDeviceInfo bootTime] - calibrated.deviceBootTime; + date = [NSDate dateWithTimeIntervalSince1970:(calibrated.serverTime + outTime)]; + } else { + date = [NSDate date]; + } + return date; +} + +- (void)recalibrationWithTimeInterval:(NSTimeInterval)timestamp { + if (timestamp > 0) { + NSTimeInterval nowInterval = [[NSDate date] timeIntervalSince1970]; + TDCORELOG(@"SDK calibrateTime success. Timestamp(%lf), diff = %lfms", timestamp * 1000, (timestamp - nowInterval) * 1000); + self.hasBeenCalibrated = YES; + self.serverTime = timestamp; + self.deviceBootTime = [TDCoreDeviceInfo bootTime]; + [TDNotificationManager postCoreNotificationCalibratedTimeSuccess:[TDCalibratedTime now]]; + } +} + +- (void)recalibrationWithNtps:(NSArray *)ntpServers { + dispatch_async(dispatch_get_main_queue(), ^{ + [self startNtp:ntpServers]; + }); +} + +- (void)startNtp:(NSArray *)ntpServerHost { + NSError *err; + for (NSString *host in ntpServerHost) { + if (!([host isKindOfClass:[NSString class]] && host.length > 0)) { + continue; + } + + err = nil; + TDNTPServer *server = [[TDNTPServer alloc] initWithHostname:host port:123]; + NSTimeInterval offset = [server dateWithError:&err]; + [server disconnect]; + + if (err) { + TDCORELOG(@"ntp failed. host: %@ error: %@", host, err); + } else { + TDCORELOG(@"SDK calibrateTime success. NTP(%@), diff = %lfms", host, (NSTimeInterval)(offset * 1000)); + self.deviceBootTime = [TDCoreDeviceInfo bootTime]; + self.serverTime = [[NSDate dateWithTimeIntervalSinceNow:offset] timeIntervalSince1970]; + self.hasBeenCalibrated = YES; + [TDNotificationManager postCoreNotificationCalibratedTimeSuccess:[TDCalibratedTime now]]; + break; + } + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.m.meta new file mode 100644 index 0000000..3fa3387 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDCalibratedTime.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 90f87ceef86a64eb39462916cd27385f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.h new file mode 100644 index 0000000..fbae756 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/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/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.h.meta new file mode 100644 index 0000000..45a7d60 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f8035ad4199a142fe9b8abb4cb1a5a38 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.m new file mode 100644 index 0000000..f9492d8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.m @@ -0,0 +1,211 @@ +#import "TDNTPServer.h" +#import "TDNTPTypes.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(void) { + 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; + return YES; + } +} + +- (NSTimeInterval)dateWithError:(NSError *__autoreleasing _Nullable *_Nullable)error { + @synchronized (self) { + return isfinite(_offset) || [self syncWithError:error] ? _offset : 0; + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.m.meta new file mode 100644 index 0000000..137fbaf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPServer.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 06c07a9cbd7e6406c94518066eb75005 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.c b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.c new file mode 100644 index 0000000..d09df8b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/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/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.c.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.c.meta new file mode 100644 index 0000000..b2558eb --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.c.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ac2ad1998c80d45f2ac718ddf4883224 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.h new file mode 100644 index 0000000..cab6a44 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/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/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.h.meta new file mode 100644 index 0000000..4389162 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/CalibratedTime/TDNTPTypes.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 18f02ba86c91f4567b793beb5b04b96f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category.meta new file mode 100644 index 0000000..de618d5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0b5be36e8a2f94699b5a6bfe5519acd3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData.meta new file mode 100644 index 0000000..a2a4c37 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 89219b76e4d614e66a9a62e15056467c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.h new file mode 100644 index 0000000..1ad6f18 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.h @@ -0,0 +1,9 @@ +#import + +@interface NSData (TDGzip) + ++ (NSData *)td_gzipData:(NSData *)dataa; + ++ (NSData *)td_gunzipData:(NSData *)data; + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.h.meta new file mode 100644 index 0000000..6f04029 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 250dc854c27ee411e85b664779a0ef8b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.m new file mode 100644 index 0000000..b43e083 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.m @@ -0,0 +1,82 @@ +#import "NSData+TDGzip.h" +#import +#import "TDCoreLog.h" + +@implementation NSData (TDGzip) + ++ (NSData *)td_gzipData:(NSData *)dataa { + if (!dataa || [dataa length] == 0) { + TDCORELOG(@"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; +} + ++ (NSData *)td_gunzipData:(NSData *)compressedData { + if ([compressedData length] == 0) { + return compressedData; + } + NSUInteger full_length = [compressedData length]; + NSUInteger half_length = [compressedData length] / 2; + NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length]; + BOOL done = NO; + int status; + z_stream strm; + strm.next_in = (Bytef *)[compressedData bytes]; + strm.avail_in = (uint)[compressedData length]; + strm.total_out = 0; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + if (inflateInit2(&strm, (15+32)) != Z_OK) return nil; + while (!done) { + // Make sure we have enough room and reset the lengths. + if (strm.total_out >= [decompressed length]) { + [decompressed increaseLengthBy: half_length]; + } + // chadeltu 加了(Bytef *) + strm.next_out = (Bytef *)[decompressed mutableBytes] + strm.total_out; + strm.avail_out = (uint)[decompressed length] - (uint)(strm.total_out); + // Inflate another chunk. + status = inflate (&strm, Z_SYNC_FLUSH); + if (status == Z_STREAM_END) { + done = YES; + } else if (status != Z_OK) { + break; + } + } + if (inflateEnd (&strm) != Z_OK) return nil; + // Set real length. + if (done) { + [decompressed setLength: strm.total_out]; + return [NSData dataWithData: decompressed]; + } else { + return nil; + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.m.meta new file mode 100644 index 0000000..ec16761 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSData/NSData+TDGzip.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 043f08397cddf4cf3be299df2a6306e7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate.meta new file mode 100644 index 0000000..4a74a23 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 415d0262871ef428d836be26dfb73bae +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.h new file mode 100644 index 0000000..bfc55a0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.h @@ -0,0 +1,22 @@ +// +// NSDate+TDCore.h +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/3/13. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSDate (TDCore) + ++ (nullable NSDate *)td_dateWithString:(nonnull NSString *)dateString formatter:(nullable NSString *)formatter timeZone:(nullable NSTimeZone *)timeZone; + +- (double)td_timeZoneOffset:(NSTimeZone *)timeZone; + +- (NSString *)td_formatWithTimeZone:(NSTimeZone *)timeZone formatString:(NSString *)formatString; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.h.meta new file mode 100644 index 0000000..2cc499d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f390bd929a9b6486e8c29e0765f64d5f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.m new file mode 100644 index 0000000..ffb2ba6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.m @@ -0,0 +1,55 @@ +// +// NSDate+TDCore.m +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/3/13. +// + +#import "NSDate+TDCore.h" +#import "NSString+TDCore.h" + +@implementation NSDate (TDCore) + ++ (NSDate *)td_dateWithString:(NSString *)dateString formatter:(NSString *)formatter timeZone:(nullable NSTimeZone *)timeZone { + if ([NSString td_isEmpty:dateString]) { + return nil; + } + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + [dateFormatter setTimeZone:timeZone]; + NSDate *date = nil; + if (![NSString td_isEmpty:formatter]) { + [dateFormatter setDateFormat:formatter]; + date = [dateFormatter dateFromString:dateString]; + } + if (!date) { + [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm"]; + date = [dateFormatter dateFromString:dateString]; + } + if (!date) { + [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; + date = [dateFormatter dateFromString:dateString]; + } + if (!date) { + [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; + date = [dateFormatter dateFromString:dateString]; + } + return date; +} + +- (double)td_timeZoneOffset:(NSTimeZone *)timeZone { + if (!timeZone) { + return 0; + } + NSInteger sourceGMTOffset = [timeZone secondsFromGMTForDate:self]; + return (double)(sourceGMTOffset/3600.0); +} + +- (NSString *)td_formatWithTimeZone:(NSTimeZone *)timeZone formatString:(NSString *)formatString { + NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init]; + timeFormatter.dateFormat = formatString; + timeFormatter.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + timeFormatter.timeZone = timeZone; + return [timeFormatter stringFromDate:self]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.m.meta new file mode 100644 index 0000000..db3f49a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDate/NSDate+TDCore.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3b7fd59f9c46f4069ac9b50f3817a82e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary.meta new file mode 100644 index 0000000..095ad06 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dbdf0181d841d4e09b52692a864a163d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.h new file mode 100644 index 0000000..5077390 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.h @@ -0,0 +1,17 @@ +// +// NSDictionary+TDCore.h +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/3/14. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSDictionary (TDCore) + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.h.meta new file mode 100644 index 0000000..19083ba --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c0c6aa0f7b8d248bea475ed0f1eaa79a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.m new file mode 100644 index 0000000..caf41b0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.m @@ -0,0 +1,12 @@ +// +// NSDictionary+TDCore.m +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/3/14. +// + +#import "NSDictionary+TDCore.h" + +@implementation NSDictionary (TDCore) + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.m.meta new file mode 100644 index 0000000..8573b53 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSDictionary/NSDictionary+TDCore.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9d16a1aeb5773473996d37b09ea03390 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber.meta new file mode 100644 index 0000000..3f38ecf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 337a597cf28a34bab862b501fe4760a7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.h new file mode 100644 index 0000000..e82d07b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.h @@ -0,0 +1,18 @@ +// +// NSNumber+TDCore.h +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/7/24. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSNumber (TDCore) + +- (BOOL)td_isBool; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.h.meta new file mode 100644 index 0000000..3cc1672 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f07b8f2c0543c4f268d5e6613300f0f1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.m new file mode 100644 index 0000000..2607862 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.m @@ -0,0 +1,20 @@ +// +// NSNumber+TDCore.m +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/7/24. +// + +#import "NSNumber+TDCore.h" + +@implementation NSNumber (TDCore) + +- (BOOL)td_isBool { + const char *type = [self objCType]; + if (strcmp(type, "c") == 0 && ([self isEqualToNumber:@YES] || [self isEqualToNumber:@NO])) { + return YES; + } + return NO; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.m.meta new file mode 100644 index 0000000..f2646db --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSNumber/NSNumber+TDCore.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 33818e5f49cef4d6a8cba12f8444a3ca +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject.meta new file mode 100644 index 0000000..c9b5b40 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 09bd2089163174d488265abcd4374fb4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.h new file mode 100644 index 0000000..a6a6012 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.h @@ -0,0 +1,20 @@ +// +// NSObject+TDCore.h +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/3/13. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSObject (TDCore) + +- (nullable instancetype)td_filterNull; +- (nullable NSString *)td_string; +- (nullable NSNumber *)td_number; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.h.meta new file mode 100644 index 0000000..7b1e58a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 90748d10279ff43bebc11eb1ca6d6d84 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.m new file mode 100644 index 0000000..0c2fb7a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.m @@ -0,0 +1,45 @@ +// +// NSObject+TDCore.m +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/3/13. +// + +#import "NSObject+TDCore.h" + +@implementation NSObject (TDCore) + +- (instancetype)td_filterNull { + if ([self isKindOfClass:NSNull.class]) { + return nil; + } + return self; +} + +- (NSString *)td_string { + NSObject *target = [self td_filterNull]; + if ([target isKindOfClass:NSString.class]) { + return (NSString *)target; + } + if ([target isKindOfClass:NSNumber.class]) { + return [NSString stringWithFormat:@"%@", target]; + } + return nil; +} + +- (NSNumber *)td_number { + NSObject *target = [self td_filterNull]; + if ([target isKindOfClass:NSString.class]) { + NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; + formatter.numberStyle = NSNumberFormatterDecimalStyle; + NSNumber *number = [formatter numberFromString:(NSString *)target]; + return number; + } + if ([target isKindOfClass:NSNumber.class]) { + return (NSNumber *)target; + } + return nil; +} + + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.m.meta new file mode 100644 index 0000000..0974971 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSObject/NSObject+TDCore.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ff9148ad56bcb4b368da4b4527a1cea1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString.meta new file mode 100644 index 0000000..aef009a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a21e12f3026b44ef3b93d5bb48cbd1f6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.h new file mode 100644 index 0000000..1767b2c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.h @@ -0,0 +1,28 @@ +// +// NSString+TDCore.h +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/3/12. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSString (TDCore) + ++ (BOOL)td_isEmpty:(NSString *)str; + +- (nullable id)td_jsonObject; + +- (NSString *)td_trim; + ++ (nullable NSString *)td_jsonStringWithJsonObject:(id)jsonObj; + ++ (BOOL)td_isEqualWithString1:(nullable NSString *)string1 string2:(nullable NSString *)string2; + +- (NSString *)td_sha256AndBase64; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.h.meta new file mode 100644 index 0000000..3d3b22c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e7d7a60dbd110438aaf23abaede6603e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.m new file mode 100644 index 0000000..daf3877 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.m @@ -0,0 +1,71 @@ +// +// NSString+TDCore.m +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/3/12. +// + +#import "NSString+TDCore.h" +#import "TDJSONUtil.h" +#import + +@implementation NSString (TDCore) + ++ (BOOL)td_isEmpty:(NSString *)str { + if (str == nil) { + return YES; + } else { + if ([str isKindOfClass:NSString.class]) { + return str.length <= 0; + } else { + return YES; + } + } +} + +- (id)td_jsonObject { + NSData *jsonData = [self dataUsingEncoding:NSUTF8StringEncoding]; + return [TDJSONUtil jsonForData:jsonData]; +} + +- (NSString *)td_trim { + NSString *string = [self stringByReplacingOccurrencesOfString:@" " withString:@""]; + string = [string stringByReplacingOccurrencesOfString:@"\r" withString:@""]; + string = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""]; + return string; +} + ++ (NSString *)td_jsonStringWithJsonObject:(id)jsonObj { + if ([jsonObj isKindOfClass:NSArray.class] || [jsonObj isKindOfClass:NSDictionary.class]) { + @try { + NSError *jsonSeralizeError = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObj options:NSJSONWritingPrettyPrinted error:&jsonSeralizeError]; + if (jsonSeralizeError == nil && jsonData != nil) { + NSString *str = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + return str; + } + } @catch (NSException *exception) { + return nil; + } + } + return nil; +} + ++ (BOOL)td_isEqualWithString1:(NSString *)string1 string2:(NSString *)string2 { + if (string1 == nil && string2 == nil) { + return YES; + } else if ([string1 isEqualToString:string2]) { + return YES; + } + return NO; +} + +- (NSString *)td_sha256AndBase64 { + NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding]; + uint8_t digest[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(data.bytes, (CC_LONG)data.length, digest); + NSData *output = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH]; + return [output base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.m.meta new file mode 100644 index 0000000..c992dec --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSString/NSString+TDCore.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 436015ca91e1b4b2e9f24086708dee8a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL.meta new file mode 100644 index 0000000..c4ec003 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e890e9aae12964f8a8010a887558ed5d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.h new file mode 100644 index 0000000..1c12e5d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.h @@ -0,0 +1,18 @@ +// +// NSURL+TDCore.h +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/5/27. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSURL (TDCore) + ++ (NSString *)td_baseUrlStringWithString:(NSString *)urlString; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.h.meta new file mode 100644 index 0000000..1bba781 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: cbfa02ae02dbe4b27ae637627ca00279 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.m new file mode 100644 index 0000000..5a4157f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.m @@ -0,0 +1,30 @@ +// +// NSURL+TDCore.m +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/5/27. +// + +#import "NSURL+TDCore.h" + +@implementation NSURL (TDCore) + ++ (NSString *)td_baseUrlStringWithString:(NSString *)urlString { + NSString *formatString = [urlString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + NSURL *url = [NSURL URLWithString:formatString]; + NSString *scheme = [url scheme]; + NSString *host = [url host]; + NSNumber *port = [url port]; + + if (scheme && scheme.length > 0 && host && host.length > 0) { + formatString = [NSString stringWithFormat:@"%@://%@", scheme, host]; + if (port && [port stringValue]) { + formatString = [formatString stringByAppendingFormat:@":%@", [port stringValue]]; + } + } + return formatString; +} + + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.m.meta new file mode 100644 index 0000000..a4f29ae --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Category/NSURL/NSURL+TDCore.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: eb909be83eecc4518a575faa8c9f0e10 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database.meta new file mode 100644 index 0000000..1fd0f08 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 01f60e644d49441d6b44973a1da98f3d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.h new file mode 100644 index 0000000..068c417 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.h @@ -0,0 +1,21 @@ +// +// TDCoreDatabase.h +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/3/14. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCoreDatabase : NSObject + ++ (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt; + ++ (NSString *)stringForColumnIndex:(int)columnIdx inStatement:(sqlite3_stmt *)pStmt; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.h.meta new file mode 100644 index 0000000..0f8fdf1 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 36f88fea675524447950a6779f19adb7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.m new file mode 100644 index 0000000..c8011b2 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.m @@ -0,0 +1,92 @@ +// +// TDCoreDatabase.m +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/3/14. +// + +#import "TDCoreDatabase.h" + +@implementation TDCoreDatabase + ++ (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt { + if ((!obj) || ((NSNull *)obj == [NSNull null])) { + sqlite3_bind_null(pStmt, idx); + } + + // FIXME - someday check the return codes on these binds. + else if ([obj isKindOfClass:[NSData class]]) { + const void *bytes = [obj bytes]; + if (!bytes) { + // it's an empty NSData object, aka [NSData data]. + // Don't pass a NULL pointer, or sqlite will bind a SQL null instead of a blob. + bytes = ""; + } + sqlite3_bind_blob(pStmt, idx, bytes, (int)[obj length], SQLITE_TRANSIENT); + } + else if ([obj isKindOfClass:[NSDate class]]) { + sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]); + } + else if ([obj isKindOfClass:[NSNumber class]]) { + + if (strcmp([obj objCType], @encode(char)) == 0) { + sqlite3_bind_int(pStmt, idx, [obj charValue]); + } + else if (strcmp([obj objCType], @encode(unsigned char)) == 0) { + sqlite3_bind_int(pStmt, idx, [obj unsignedCharValue]); + } + else if (strcmp([obj objCType], @encode(short)) == 0) { + sqlite3_bind_int(pStmt, idx, [obj shortValue]); + } + else if (strcmp([obj objCType], @encode(unsigned short)) == 0) { + sqlite3_bind_int(pStmt, idx, [obj unsignedShortValue]); + } + else if (strcmp([obj objCType], @encode(int)) == 0) { + sqlite3_bind_int(pStmt, idx, [obj intValue]); + } + else if (strcmp([obj objCType], @encode(unsigned int)) == 0) { + sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedIntValue]); + } + else if (strcmp([obj objCType], @encode(long)) == 0) { + sqlite3_bind_int64(pStmt, idx, [obj longValue]); + } + else if (strcmp([obj objCType], @encode(unsigned long)) == 0) { + sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongValue]); + } + else if (strcmp([obj objCType], @encode(long long)) == 0) { + sqlite3_bind_int64(pStmt, idx, [obj longLongValue]); + } + else if (strcmp([obj objCType], @encode(unsigned long long)) == 0) { + sqlite3_bind_int64(pStmt, idx, (long long)[obj unsignedLongLongValue]); + } + else if (strcmp([obj objCType], @encode(float)) == 0) { + sqlite3_bind_double(pStmt, idx, [obj floatValue]); + } + else if (strcmp([obj objCType], @encode(double)) == 0) { + sqlite3_bind_double(pStmt, idx, [obj doubleValue]); + } + else if (strcmp([obj objCType], @encode(BOOL)) == 0) { + sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0)); + } + else { + sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_TRANSIENT); + } + } + else { + sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_TRANSIENT); + } +} + ++ (NSString *)stringForColumnIndex:(int)columnIdx inStatement:(sqlite3_stmt *)pStmt { + if (sqlite3_column_type(pStmt, columnIdx) == SQLITE_NULL || (columnIdx < 0) || columnIdx >= sqlite3_column_count(pStmt)) { + return nil; + } + const char *c = (const char *)sqlite3_column_text(pStmt, columnIdx); + if (!c) { + // null row. + return nil; + } + return [NSString stringWithUTF8String:c]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.m.meta new file mode 100644 index 0000000..ab080bc --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Database/TDCoreDatabase.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 30390506a2f3444a2ab1fbb114438c6d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo.meta new file mode 100644 index 0000000..cab1f69 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 415368d8b93784505985b8f697e6199c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.h new file mode 100644 index 0000000..8447398 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.h @@ -0,0 +1,39 @@ +// +// TDCoreDeviceInfo.h +// Pods +// +// Created by 杨雄 on 2024/4/23. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCoreDeviceInfo : NSObject + ++ (NSTimeInterval)bootTime; ++ (NSString *)manufacturer; ++ (nullable NSString *)systemLanguage; ++ (NSString *)bundleId; ++ (NSString *)deviceId; ++ (NSDate *)installTime; ++ (NSString *)ram; ++ (NSString *)disk; ++ (NSString *)appVersion; ++ (NSString *)os; ++ (NSString *)osVersion; ++ (NSString *)deviceModel; ++ (NSString *)deviceType; ++ (BOOL)isSimulator; + +#if TARGET_OS_IOS ++ (NSNumber *)fps; ++ (NSNumber *)screenWidth; ++ (NSNumber *)screenHeight; ++ (NSString *)networkType; ++ (nullable NSString *)carrier; +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.h.meta new file mode 100644 index 0000000..8cc3889 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0b0729b1f4eea40e9a71dab018e636ef +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.m new file mode 100644 index 0000000..88f6d47 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.m @@ -0,0 +1,417 @@ +// +// TDCoreDeviceInfo.m +// Pods +// +// Created by 杨雄 on 2024/4/23. +// + +#import "TDCoreDeviceInfo.h" +#import +#include +#import "TDCoreKeychainHelper.h" +#import + +#if TARGET_OS_WATCH +#import +#endif + +#if TARGET_OS_IOS || TARGET_OS_VISION +#import +#endif + +#if TARGET_OS_IOS +#import "TDNetworkReachability.h" +#import "TDCoreFPSMonitor.h" +#endif + +@implementation TDCoreDeviceInfo + +#if TARGET_OS_IOS ++ (void)load { + [[TDNetworkReachability shareInstance] startMonitoring]; +} +#endif + ++ (NSTimeInterval)bootTime { + 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; +} + ++ (NSString *)manufacturer { + return @"Apple"; +} + ++ (nullable NSString *)systemLanguage { + NSString *preferredLanguages = [[NSLocale preferredLanguages] firstObject]; + if (preferredLanguages && preferredLanguages.length > 0) { + return [[preferredLanguages componentsSeparatedByString:@"-"] firstObject];; + } + return nil; +} + ++ (NSString *)bundleId { + return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"]; +} + +#if (TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_VISION || TARGET_OS_TV) + ++ (NSString *)deviceId { + NSString *deviceId = nil; + @synchronized (self) { + NSString *keyDeviceId = @"thinking_data_device_id"; + deviceId = [TDCoreKeychainHelper readDeviceId]; + if (!([deviceId isKindOfClass:NSString.class] && deviceId.length > 0)) { + deviceId = [[NSUserDefaults standardUserDefaults] stringForKey:keyDeviceId]; + } + if (!deviceId) { + deviceId = [self defaultIdentifier]; + } + [[NSUserDefaults standardUserDefaults] setObject:deviceId forKey:keyDeviceId]; + [TDCoreKeychainHelper saveDeviceId:deviceId]; + } + return deviceId; +} + +#define kDeviceClass @"XY2HU4AX3JI2JJW5MDhjm6wea2x6ymvm28ylmiyh7jkc8axy9mw3em8w" +#define kCurrentDevice @"0a223h444j555cm666uw77722rh985jrj323ae44y5xn5ll5tm5mD5wm6e8y9m0vm32y46i7a89x0yl32c44ml4ye5a3a5" +#define kIdfv @"hj23kik4343j545dk656ke43434hhn534536jj7676tx323423yyx547657iy7678yxf7654hhl32342im3424ww4235w546ew64645w76ll57rx67yF434hj323ao343aa546rk76l323Vx32y32y32x32e3m43w656m76nxy657k657lmd65y657yx5o323aa34kk45rk76k76lm87" +#define kUUIDStr @"323J342J342K342U657K675A87A87U879H0943AX908IJ214KWD54WW87SX98XY3425At769k93l2l548m7r32xyx76769im3234ww6576n8ax89g98k9l97m1w31242" ++ (NSString *)defaultIdentifier { + 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]); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + 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; + } + } + } + } +#pragma clang diagnostic pop + } + if (!anonymityId) { + anonymityId = [[NSUUID UUID] UUIDString]; + } + return anonymityId; +} + +#elif TARGET_OS_OSX + ++ (NSString *)deviceId { + NSString *keyDeviceId = @"thinking_data_device_id"; + NSString *deviceId = [[NSUserDefaults standardUserDefaults] stringForKey:keyDeviceId]; + if (!deviceId) { + deviceId = [self getSystemSerialNumber]; + if (deviceId == nil) { + deviceId = [[NSUUID UUID] UUIDString]; + } + [[NSUserDefaults standardUserDefaults] setObject:deviceId forKey:keyDeviceId]; + } + return deviceId; +} + ++ (nullable NSString *)getSystemSerialNumber { + io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")); + if (platformExpert) { + CFTypeRef serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0); + IOObjectRelease(platformExpert); + if (serialNumberAsCFString) { + NSString *serialNumber = (__bridge_transfer NSString *)serialNumberAsCFString; + return serialNumber; + } + } + return nil; +} + +#endif + ++ (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:@""]; +} + ++ (NSDate *)installTime { + NSURL *urlToDocumentsFolder = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; + NSError *error = nil; + NSDate *installDate = [[[NSFileManager defaultManager] attributesOfItemAtPath:urlToDocumentsFolder.path error:&error] objectForKey:NSFileCreationDate]; + if (!error) { + return installDate; + } + return [NSDate date]; +} + +#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) ++ (NSString *)ram { + NSString *ram = [NSString stringWithFormat:@"%.1f/%.1f", [self td_pm_func_getFreeMemory]*1.0/TD_PM_UNIT_GB, [self td_pm_func_getRamSize]*1.0/TD_PM_UNIT_GB]; + return ram; +} ++ (NSString *)disk { + NSString *disk = [NSString stringWithFormat:@"%.1f/%.1f", [self td_get_disk_free_size]*1.0/TD_PM_UNIT_GB, [self td_get_storage_size]*1.0/TD_PM_UNIT_GB]; + return disk; +} + ++ (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; +} + ++ (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; +} + ++ (NSString *)appVersion { + return [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"]; +} + ++ (BOOL)isSimulator { + BOOL result = NO; +#if TARGET_IPHONE_SIMULATOR + result = YES; +#elif TARGET_OS_SIMULATOR + result = YES; +#else + result = NO; +#endif + return result; +} + +#if TARGET_OS_IOS + ++ (NSNumber *)fps { + static TDCoreFPSMonitor *fpsMonitor = nil; + if (!fpsMonitor) { + fpsMonitor = [[TDCoreFPSMonitor alloc] init]; + [fpsMonitor setEnable:YES]; + } + return [fpsMonitor getPFS]; +} + ++ (NSNumber *)screenWidth { + CGSize size = [UIScreen mainScreen].bounds.size; + return @((NSInteger)size.width); +} + ++ (NSNumber *)screenHeight { + CGSize size = [UIScreen mainScreen].bounds.size; + return @((NSInteger)size.height); +} + ++ (NSString *)networkType { + return [[TDNetworkReachability shareInstance] networkState]; +} + ++ (nullable NSString *)carrier { + return [[TDNetworkReachability shareInstance] carrier]; +} +#endif + +/* ========================================================================================================== + Warning: The following code cannot be modified, otherwise it will result in inaccurate data analysis!!! + ============================================================================================================ + */ ++ (NSString *)deviceModel { +#if (TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_VISION || TARGET_OS_TV) + 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; +#elif TARGET_OS_OSX + struct utsname systemInfo; + uname(&systemInfo); + NSString *platform = [NSString stringWithCString:systemInfo.machine encoding:NSASCIIStringEncoding]; + return platform; +#endif +} + ++ (NSString *)os { +#if TARGET_OS_IOS + return @"iOS"; +#elif TARGET_OS_WATCH + return [WKInterfaceDevice currentDevice].systemName; +#elif TARGET_OS_TV + return @"tvOS"; +#elif TARGET_OS_VISION + return [UIDevice currentDevice].systemName; +#elif TARGET_OS_OSX + return @"OSX"; +#endif +} + ++ (NSString *)osVersion { +#if TARGET_OS_IOS || TARGET_OS_VISION || TARGET_OS_TV + return [[UIDevice currentDevice] systemVersion]; +#elif TARGET_OS_WATCH + return [WKInterfaceDevice currentDevice].systemVersion; +#elif TARGET_OS_OSX + NSDictionary *sv = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; + NSString *versionString = [sv objectForKey:@"ProductVersion"]; + return versionString; +#endif + return @""; +} + ++ (NSString *)deviceType { +#if TARGET_OS_IOS + NSString *typeName = @"unknown"; + switch ([[UIDevice currentDevice] userInterfaceIdiom]) { + case UIUserInterfaceIdiomPad: { + typeName = @"iPad"; + } break; + case UIUserInterfaceIdiomPhone: { + typeName = @"iPhone"; + } break; + case UIUserInterfaceIdiomTV: { + typeName = @"TV"; + } break; + case UIUserInterfaceIdiomCarPlay: { + typeName = @"CarPlay"; + } break; +#ifdef __IPHONE_14_0 + case UIUserInterfaceIdiomMac: { + typeName = @"Mac"; + } break; +#endif +#ifdef __IPHONE_17_0 + case UIUserInterfaceIdiomVision: { + typeName = @"Vision"; + } break; +#endif + default: + break; + } + return typeName; +#elif TARGET_OS_OSX + return @"Mac"; +#elif TARGET_OS_WATCH + return @"AppleWatch"; +#elif TARGET_OS_VISION + return @"VisionPro"; +#elif TARGET_OS_TV + return @"AppleTV"; +#endif + return @""; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.m.meta new file mode 100644 index 0000000..93f9117 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreDeviceInfo.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 98760b88a7a5f46128e9d1846136dbd9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.h new file mode 100644 index 0000000..f42ad3c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.h @@ -0,0 +1,19 @@ +// +// TDCoreFPSMonitor.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/24. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCoreFPSMonitor : NSObject +@property (nonatomic, assign, getter=isEnable) BOOL enable; + +- (NSNumber *)getPFS; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.h.meta new file mode 100644 index 0000000..238191b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 56117c2f6c43f4a3bbe88ef03e338924 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.m new file mode 100644 index 0000000..702b706 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.m @@ -0,0 +1,72 @@ +// +// TDCoreFPSMonitor.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/24. +// + +#import "TDCoreFPSMonitor.h" +#import +#import "TDCoreWeakProxy.h" + +@interface TDCoreFPSMonitor () +@property (nonatomic, strong) CADisplayLink *link; +@property (nonatomic, assign) NSUInteger count; +@property (nonatomic, assign) NSTimeInterval lastTime; +@property (nonatomic, assign) int thinkingdata_fps; + +@end + +@implementation TDCoreFPSMonitor + +- (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:[TDCoreWeakProxy 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; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.m.meta new file mode 100644 index 0000000..22f129e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCoreFPSMonitor.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 02553d30dbe3a497592fe37e155561c5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.h new file mode 100644 index 0000000..c16ad62 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.h @@ -0,0 +1,56 @@ +// +// TDCorePresetDisableConfig.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/25. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCorePresetDisableConfig : NSObject + +@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; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.h.meta new file mode 100644 index 0000000..3e3829e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4852319f96f674a85bc9d9d6eb0b8d37 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.m new file mode 100644 index 0000000..aab1478 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.m @@ -0,0 +1,317 @@ +// +// TDCorePresetDisableConfig.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/25. +// + +#import "TDCorePresetDisableConfig.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 *kTDPresentOpsReceiptProperties = @"#ops_receipt_properties"; +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"; + +#define TD_MAIM_INFO_PLIST_DISPRESTPRO_KEY @"TDDisPresetProperties" + +@implementation TDCorePresetDisableConfig + +static NSMutableArray *__td_disPresetProperties; + ++ (void)initialize { + [self loadDisPresetProperties]; +} + ++ (NSArray *)loadDisPresetProperties { + 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; +} + ++ (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/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.m.meta new file mode 100644 index 0000000..2cd1f72 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetDisableConfig.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4d45d78e5506a4395a0eb7656ce51256 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.h new file mode 100644 index 0000000..e4188e5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.h @@ -0,0 +1,22 @@ +// +// TDCorePresetProperty.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/26. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCorePresetProperty : NSObject + ++ (NSDictionary *)staticProperties; + ++ (NSDictionary *)dynamicProperties; + ++ (NSDictionary *)allPresetProperties; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.h.meta new file mode 100644 index 0000000..881e9cd --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 01aef1e4616454ee7be4d15184c63ccc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.m new file mode 100644 index 0000000..0c4015e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.m @@ -0,0 +1,145 @@ +// +// TDCorePresetProperty.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/26. +// + +#import "TDCorePresetProperty.h" +#import "TDCoreDeviceInfo.h" +#import "TDCorePresetDisableConfig.h" + +@implementation TDCorePresetProperty + ++ (NSDictionary *)staticProperties { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + if (![TDCorePresetDisableConfig disableBundleId]) { + NSString *value = [TDCoreDeviceInfo bundleId]; + if (value) { + dict[@"#bundle_id"] = value; + } + } + if (![TDCorePresetDisableConfig disableInstallTime]) { + NSDate *value = [TDCoreDeviceInfo installTime]; + if (value) { + dict[@"#install_time"] = value; + } + } + if (![TDCorePresetDisableConfig disableDeviceId]) { + NSString *value = [TDCoreDeviceInfo deviceId]; + if (value) { + dict[@"#device_id"] = value; + } + } + if (![TDCorePresetDisableConfig disableAppVersion]) { + NSString *value = [TDCoreDeviceInfo appVersion]; + if (value) { + dict[@"#app_version"] = value; + } + } + if (![TDCorePresetDisableConfig disableOs]) { + NSString *value = [TDCoreDeviceInfo os]; + if (value) { + dict[@"#os"] = value; + } + } + if (![TDCorePresetDisableConfig disableOsVersion]) { + NSString *value = [TDCoreDeviceInfo osVersion]; + if (value) { + dict[@"#os_version"] = value; + } + } + if (![TDCorePresetDisableConfig disableDeviceModel]) { + NSString *value = [TDCoreDeviceInfo deviceModel]; + if (value) { + dict[@"#device_model"] = value; + } + } + if (![TDCorePresetDisableConfig disableDeviceType]) { + NSString *value = [TDCoreDeviceInfo deviceType]; + if (value) { + dict[@"#device_type"] = value; + } + } + if (![TDCorePresetDisableConfig disableManufacturer]) { + NSString *value = [TDCoreDeviceInfo manufacturer]; + if (value) { + dict[@"#manufacturer"] = value; + } + } + if (![TDCorePresetDisableConfig disableSimulator]) { + dict[@"#simulator"] = [TDCoreDeviceInfo isSimulator] ? @(YES) : @(NO); + } +#if TARGET_OS_IOS + + if (![TDCorePresetDisableConfig disableScreenWidth]) { + NSNumber *value = [TDCoreDeviceInfo screenWidth]; + if (value) { + dict[@"#screen_width"] = value; + } + } + if (![TDCorePresetDisableConfig disableScreenHeight]) { + NSNumber *value = [TDCoreDeviceInfo screenHeight]; + if (value) { + dict[@"#screen_height"] = value; + } + } +#endif + return dict; +} + ++ (NSDictionary *)dynamicProperties { + NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary]; + + if (![TDCorePresetDisableConfig disableRAM]) { + NSString *value = [TDCoreDeviceInfo ram]; + if (value) { + mutableDict[@"#ram"] = value; + } + } + if (![TDCorePresetDisableConfig disableDisk]) { + NSString *value = [TDCoreDeviceInfo disk]; + if (value) { + mutableDict[@"#disk"] = value; + } + } + if (![TDCorePresetDisableConfig disableSystemLanguage]) { + NSString *value = [TDCoreDeviceInfo systemLanguage]; + if (value) { + mutableDict[@"#system_language"] = value; + } + } +#if TARGET_OS_IOS + if (![TDCorePresetDisableConfig disableCarrier]) { + NSString *value = [TDCoreDeviceInfo carrier]; + if (value) { + mutableDict[@"#carrier"] = value; + } + } + if (![TDCorePresetDisableConfig disableNetworkType]) { + NSString *value = [TDCoreDeviceInfo networkType]; + if (value) { + mutableDict[@"#network_type"] = value; + } + } + if (![TDCorePresetDisableConfig disableFPS]) { + NSNumber *value = [TDCoreDeviceInfo fps]; + if (value) { + mutableDict[@"#fps"] = value; + } + } +#endif + return mutableDict; +} + ++ (NSDictionary *)allPresetProperties { + NSDictionary *staticDict = [self staticProperties]; + NSDictionary *dynamicDict = [self dynamicProperties]; + + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + [dict addEntriesFromDictionary:staticDict]; + [dict addEntriesFromDictionary:dynamicDict]; + return dict; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.m.meta new file mode 100644 index 0000000..1064c4e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/DeviceInfo/TDCorePresetProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 518d2743a150b42d88db013fb0311fa5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain.meta new file mode 100644 index 0000000..ef39dda --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4e836d9003e714cf1bd690fd380a447a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.h new file mode 100644 index 0000000..20df298 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.h @@ -0,0 +1,20 @@ +// +// TDCoreKeychainHelper.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/24. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCoreKeychainHelper : NSObject + ++ (void)saveDeviceId:(NSString *)string; + ++ (NSString *)readDeviceId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.h.meta new file mode 100644 index 0000000..5df7c3f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f09db69b8fe434562848fb6bf12d5695 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.m new file mode 100644 index 0000000..8999e8f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.m @@ -0,0 +1,34 @@ +// +// TDCoreKeychainHelper.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/24. +// + +#import "TDCoreKeychainHelper.h" +#import "TDKeychainManager.h" + +static NSString * const kTDDeviceIDOld = @"com.thinkingddata.analytics.deviceid"; +static NSString * const kTDDeviceIDNew = @"com.thinkingddata.analytics.deviceid_1"; + +@implementation TDCoreKeychainHelper + ++ (void)saveDeviceId:(NSString *)string { + [TDKeychainManager saveItem:string forKey:kTDDeviceIDNew]; + + // Compatibility handles the case of jumping back and forth between old and new SDK versions + [TDKeychainManager oldSaveItem:string forKey:kTDDeviceIDOld]; +} + ++ (NSString *)readDeviceId { + NSString *data = [TDKeychainManager itemForKey:kTDDeviceIDNew]; + if (data == nil) { + data = [TDKeychainManager oldItemForKey:kTDDeviceIDOld]; + if (data) { + [TDKeychainManager saveItem:data forKey:kTDDeviceIDNew]; + } + } + return data; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.m.meta new file mode 100644 index 0000000..4da5635 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDCoreKeychainHelper.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 23267f5a227e440279b4d3f360848880 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.h new file mode 100644 index 0000000..9c5e5f7 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.h @@ -0,0 +1,21 @@ +// +// TDKeychainManager.h +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/1/23. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDKeychainManager : NSObject ++ (void)saveItem:(nonnull NSString *)value forKey:(nonnull NSString *)key; ++ (void)oldSaveItem:(nonnull NSString *)value forKey:(nonnull NSString *)key; ++ (nullable NSString *)itemForKey:(nonnull NSString *)key; ++ (BOOL)deleteItemWithKey:(nonnull NSString *)key; ++ (nullable NSString *)oldItemForKey:(NSString *)key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.h.meta new file mode 100644 index 0000000..b83a705 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ea66356f7b752448da1f6da14a70b0bc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.m new file mode 100644 index 0000000..16365fe --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.m @@ -0,0 +1,149 @@ +// +// TDKeychainManager.m +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/1/23. +// + +#import "TDKeychainManager.h" +#import "TDOSLog.h" + +static NSString * const TDKeychainService = @"com.thinkingddata.analytics.service"; + +@interface TDKeychainManager () + +@end + +@implementation TDKeychainManager + ++ (void)saveItem:(nonnull NSString *)value forKey:(nonnull NSString *)key { + if (!key || !value) { + return; + } + NSData *encodeData = [value dataUsingEncoding:NSUTF8StringEncoding]; + @synchronized (self) { + NSString *originPassword = [self itemForKey: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) { + [TDOSLog logMessage:@"Keychain Update Error" prefix:@"TDCore" type:TDLogTypeError asynchronous:NO]; + } + } else { + NSMutableDictionary *attributes = [self keychainQueryWithAccount:key]; + attributes[(__bridge id)kSecValueData] = encodeData; + OSStatus statusCode = SecItemAdd((__bridge CFDictionaryRef)attributes, nil); + if (statusCode != noErr) { + [TDOSLog logMessage:@"Keychain Add Error" prefix:@"TDCore" type:TDLogTypeError asynchronous:NO]; + } + } + } +} + ++ (void)oldSaveItem:(nonnull NSString *)value forKey:(nonnull NSString *)key { + if (!key || !value) { + return; + } + NSData *encodeData = [value dataUsingEncoding:NSUTF8StringEncoding]; + @synchronized (self) { + NSString *originPassword = [self oldItemForKey:key]; + if (originPassword.length > 0) { + NSMutableDictionary *updateAttributes = [NSMutableDictionary dictionary]; + updateAttributes[(__bridge id)kSecValueData] = encodeData; + NSMutableDictionary *query = [self oldKeychainQueryWithAccount:key]; + OSStatus statusCode = SecItemUpdate((__bridge CFDictionaryRef)query,(__bridge CFDictionaryRef)updateAttributes); + if (statusCode != noErr) { + [TDOSLog logMessage:@"Keychain Update Error" prefix:@"TDCore" type:TDLogTypeError asynchronous:NO]; + } + } else { + NSMutableDictionary *attributes = [self oldKeychainQueryWithAccount:key]; + attributes[(__bridge id)kSecValueData] = encodeData; + OSStatus statusCode = SecItemAdd((__bridge CFDictionaryRef)attributes, nil); + if (statusCode != noErr) { + [TDOSLog logMessage:@"Keychain Add Error" prefix:@"TDCore" type:TDLogTypeError asynchronous:NO]; + } + } + } +} + ++ (nullable NSString *)itemForKey:(NSString *)key { + if (!key) { + return nil; + } + NSMutableDictionary *attributes = [self keychainQueryWithAccount:key]; + attributes[(__bridge id)kSecMatchLimit] = (__bridge id)(kSecMatchLimitOne); + attributes[(__bridge id)kSecReturnData] = (__bridge id)(kCFBooleanTrue); + NSString *result = nil; + @synchronized (self) { + CFTypeRef data = nil; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)attributes,(CFTypeRef *)&data); + if (status == errSecSuccess) { + NSData *encodeData = [NSData dataWithData:(__bridge NSData *)data]; + if (data) { + CFRelease(data); + } + if (encodeData) { + result = [[NSString alloc] initWithData:encodeData encoding:NSUTF8StringEncoding]; + } + } + } + return result; +} + ++ (nullable NSString *)oldItemForKey:(NSString *)key { + if (!key) { + return nil; + } + NSMutableDictionary *attributes = [self oldKeychainQueryWithAccount:key]; + attributes[(__bridge id)kSecMatchLimit] = (__bridge id)(kSecMatchLimitOne); + attributes[(__bridge id)kSecReturnData] = (__bridge id)(kCFBooleanTrue); + NSString *result = nil; + @synchronized (self) { + CFTypeRef data = nil; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)attributes,(CFTypeRef *)&data); + if (status == errSecSuccess) { + NSData *encodeData = [NSData dataWithData:(__bridge NSData *)data]; + if (data) { + CFRelease(data); + } + if (encodeData) { + result = [[NSString alloc] initWithData:encodeData encoding:NSUTF8StringEncoding]; + } + } + } + return result; +} + ++ (BOOL)deleteItemWithKey:(nonnull NSString *)key { + if (!key) { + return NO; + } + NSMutableDictionary *query = [self keychainQueryWithAccount:key]; + BOOL result = NO; + @synchronized (self) { + OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query); + result = (status == errSecSuccess); + } + return result; +} + ++ (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; + query[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlock; + return query; +} + ++ (NSMutableDictionary *)oldKeychainQueryWithAccount:(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/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.m.meta new file mode 100644 index 0000000..29dc888 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Keychain/TDKeychainManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c874dd0d4a6a441dfa75ff9c52fccc71 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log.meta new file mode 100644 index 0000000..ec6afd8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6e4ed24660e144ff9a6c9eeefa27b193 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.h new file mode 100644 index 0000000..5b9ebcf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.h @@ -0,0 +1,22 @@ +// +// TDCoreLog.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/7/17. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +#define TDCORELOG(format, ...) [TDCoreLog printLog:(format), ##__VA_ARGS__] + +@interface TDCoreLog : NSObject + ++ (void)enableLog:(BOOL)enable; + ++ (void)printLog:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.h.meta new file mode 100644 index 0000000..be2b8ab --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 540badeeb1b654ce5b8152bfc867a074 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.m new file mode 100644 index 0000000..01609b5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.m @@ -0,0 +1,33 @@ +// +// TDCoreLog.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/7/17. +// + +#import "TDCoreLog.h" +#import "TDOSLog.h" + +static BOOL _logOn = YES; + +@implementation TDCoreLog + ++ (void)enableLog:(BOOL)enable { + _logOn = enable; +} + ++ (void)printLog:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2) { + if (_logOn == YES) { + if (format) { + va_list args; + va_start(args, format); + NSString *output = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + + NSString *prefix = @"TDCore"; + [TDOSLog logMessage:output prefix:prefix type:TDLogTypeInfo asynchronous:YES]; + } + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.m.meta new file mode 100644 index 0000000..7151b8f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDCoreLog.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: cb3ca0d5a513643c0ab3ef79ffc773fe +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.h new file mode 100644 index 0000000..7ae72da --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.h @@ -0,0 +1,17 @@ +// +// TDLogChannelConsole.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/1/22. +// + +#import +#import "TDLogChannelProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDLogChannelConsole : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.h.meta new file mode 100644 index 0000000..9eccbc9 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: cb390acbdcf6d4214895f4dd81d23657 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.m new file mode 100644 index 0000000..c9bee94 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.m @@ -0,0 +1,62 @@ +// +// TDLogChannelConsole.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/1/22. +// + +#import "TDLogChannelConsole.h" +#import + +@interface TDLogChannelConsole () +@property (strong, nonatomic) os_log_t logger; + +@end + +@implementation TDLogChannelConsole + +- (instancetype)init +{ + self = [super init]; + if (self) { +#ifdef __IPHONE_10_0 + self.logger = os_log_create("cn.thinkingdata.analytics.log", "ThinkingData"); +#endif + } + return self; +} + +- (void)printMessage:(NSString *)message type:(TDLogType)type { + if (message == nil) { + return; + } + +#ifdef __IPHONE_10_0 + if (@available(iOS 10.0, *)) { + const char *msg = [message UTF8String]; + os_log_t logger = self.logger; + switch (type) { + case TDLogTypeDebug: + os_log_debug(logger, "%{public}s", msg); + break; + case TDLogTypeInfo: + os_log_info(logger, "%{public}s", msg); + break; + case TDLogTypeWarning: + os_log_error(logger, "%{public}s", msg); + break; + case TDLogTypeError: + os_log_error(logger, "%{public}s", msg); + break; + case TDLogTypeOff: + default: + break; + } + } +#else + NSLog(@"%@", message); +#endif +} + + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.m.meta new file mode 100644 index 0000000..c69fb76 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelConsole.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 648394274314f4bd5bc062ae6b6bb6f4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelProtocol.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelProtocol.h new file mode 100644 index 0000000..499a524 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelProtocol.h @@ -0,0 +1,20 @@ +// +// TDLogChannelProtocol.h +// Pods +// +// Created by 杨雄 on 2024/1/22. +// + +#ifndef TDLogChannelProtocol_h +#define TDLogChannelProtocol_h + +#import +#import "TDLogConstant.h" + +@protocol TDLogChannleProtocol + +- (void)printMessage:(NSString *)message type:(TDLogType)type; + +@end + +#endif /* TDLogChannelProtocol_h */ diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelProtocol.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelProtocol.h.meta new file mode 100644 index 0000000..0d7c82a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogChannelProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4d09e21e074214737a1156d0f799d7b7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogConstant.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogConstant.h new file mode 100644 index 0000000..34eda2a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogConstant.h @@ -0,0 +1,21 @@ +// +// TDLogConstant.h +// Pods +// +// Created by 杨雄 on 2024/1/22. +// + +#ifndef TDLogConstant_h +#define TDLogConstant_h + +#import + +typedef NS_ENUM(NSUInteger, TDLogType) { + TDLogTypeOff = 0, + TDLogTypeError = 1 << 0, + TDLogTypeWarning = 1 << 1, + TDLogTypeInfo = 1 << 2, + TDLogTypeDebug = 1 << 3, +}; + +#endif /* TDLogConstant_h */ diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogConstant.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogConstant.h.meta new file mode 100644 index 0000000..916f501 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogConstant.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 76d3e619d0dd1413aa666c1dd25f9aa4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.h new file mode 100644 index 0000000..93af964 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.h @@ -0,0 +1,22 @@ +// +// TDLogMessage.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/1/22. +// + +#import +#import "TDLogConstant.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDLogMessage : NSObject +@property (nonatomic, copy) NSString *prefix; +@property (nonatomic, copy) NSString *message; +@property (nonatomic, assign) TDLogType type; + +- (TDLogMessage *)initWithMessage:(NSString *)message prefix:(nullable NSString *)prefix type:(TDLogType)type; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.h.meta new file mode 100644 index 0000000..3e35a26 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a17b2cd01a7304682bc8baabfb55f265 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.m new file mode 100644 index 0000000..2b22e0c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.m @@ -0,0 +1,21 @@ +// +// TDLogMessage.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/1/22. +// + +#import "TDLogMessage.h" + +@implementation TDLogMessage + +- (TDLogMessage *)initWithMessage:(NSString *)message prefix:(NSString *)prefix type:(TDLogType)type { + if (self = [super init]) { + self.prefix = prefix; + self.message = message; + self.type = type; + } + return self; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.m.meta new file mode 100644 index 0000000..840619d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDLogMessage.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9251627353083411db8d7bca9d8f8146 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.h new file mode 100644 index 0000000..e0f6cef --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.h @@ -0,0 +1,32 @@ +// +// TDOSLog.h +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/1/22. +// + +#import + +#if __has_include() +#import +#else +#import "TDLogChannelProtocol.h" +#endif + +#if __has_include() +#import +#else +#import "TDLogConstant.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDOSLog : NSObject + ++ (void)addLogConsumer:(id)consumer; + ++ (void)logMessage:(NSString *)message prefix:(nullable NSString *)prefix type:(TDLogType)type asynchronous:(BOOL)asynchronous; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.h.meta new file mode 100644 index 0000000..97aa235 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 92906c2bd1b4046979de56b5c6cc91bc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.m new file mode 100644 index 0000000..a1a935d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.m @@ -0,0 +1,117 @@ +// +// TDOSLog.m +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/1/22. +// + +#import "TDOSLog.h" +#import "TDLogChannelConsole.h" +#import "TDLogMessage.h" + +#ifndef DDLOG_MAX_QUEUE_SIZE + #define DDLOG_MAX_QUEUE_SIZE 1000 +#endif + +static dispatch_queue_t g_loggingQueue; +static dispatch_semaphore_t g_queueSemaphore; +static void *const GlobalLoggingQueueIdentityKey = (void *)&GlobalLoggingQueueIdentityKey; + +@interface TDOSLog () +@property (atomic, strong) NSMutableArray> *logConsumers; +@property (nonatomic, strong) dispatch_queue_t consoleLogQueue; +@property (nonatomic, strong) NSDateFormatter *dateFormatter; +@end + +@implementation TDOSLog + ++ (instancetype)sharedInstance { + static id sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + ++ (void)initialize { + static dispatch_once_t TDLogOnceToken; + dispatch_once(&TDLogOnceToken, ^{ + g_loggingQueue = dispatch_queue_create("cn.thinking.log", NULL); + + void *nonNullValue = GlobalLoggingQueueIdentityKey; + dispatch_queue_set_specific(g_loggingQueue, GlobalLoggingQueueIdentityKey, nonNullValue, NULL); + g_queueSemaphore = dispatch_semaphore_create(DDLOG_MAX_QUEUE_SIZE); + }); +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.logConsumers = [NSMutableArray array]; + [self.logConsumers addObject:[[TDLogChannelConsole alloc] init]]; + + self.dateFormatter = [[NSDateFormatter alloc] init]; + [self.dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; + + const char *loggerQueueName = [@"cn.thinkingdata.analytics.osLogger" UTF8String]; + dispatch_queue_t loggerQueue = dispatch_queue_create(loggerQueueName, NULL); + self.consoleLogQueue = loggerQueue; + } + return self; +} + +// MARK: - Public method + ++ (void)addLogConsumer:(id)consumer { + dispatch_async(g_loggingQueue, ^{ + if ([consumer conformsToProtocol:@protocol(TDLogChannleProtocol)]) { + [[TDOSLog sharedInstance].logConsumers addObject:consumer]; + } + }); +} + ++ (void)logMessage:(NSString *)message prefix:(NSString *)prefix type:(TDLogType)type asynchronous:(BOOL)asynchronous { + TDLogMessage *logMessage = [[TDLogMessage alloc] initWithMessage:message prefix:prefix type:type]; + [self.sharedInstance queueLogMessage:logMessage asynchronously:asynchronous]; +} + +//MARK: - Private method + +- (void)queueLogMessage:(TDLogMessage *)logMessage asynchronously:(BOOL)asyncFlag { + dispatch_block_t logBlock = ^{ + dispatch_semaphore_wait(g_queueSemaphore, DISPATCH_TIME_FOREVER); + @autoreleasepool { + [self lt_log:logMessage]; + } + dispatch_semaphore_signal(g_queueSemaphore); + }; + + if (asyncFlag) { + dispatch_async(g_loggingQueue, logBlock); + } else if (dispatch_get_specific(GlobalLoggingQueueIdentityKey)) { + logBlock(); + } else { + dispatch_sync(g_loggingQueue, logBlock); + } +} + +- (void)lt_log:(TDLogMessage *)logMessage { + NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), + @"This method should only be run on the logging thread/queue"); + + NSMutableString *logText = [[NSMutableString alloc] init]; + NSString *timeString = [self.dateFormatter stringFromDate:[NSDate date]]; + [logText appendFormat:@"[%@]", timeString]; + if (logMessage.prefix) { + [logText appendFormat:@"[%@]", logMessage.prefix]; + } + [logText appendFormat:@" %@", logMessage.message]; + + for (id consumer in self.logConsumers) { + [consumer printMessage:logText type:logMessage.type]; + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.m.meta new file mode 100644 index 0000000..5b7337c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Log/TDOSLog.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e9865184e38294362b713291b48f06b7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network.meta new file mode 100644 index 0000000..9b8d86e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2f04524b7ddbe4f50b01e03a4b14893a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.h new file mode 100644 index 0000000..21093ab --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.h @@ -0,0 +1,26 @@ +// +// TDNetworkReachability.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/1/15. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDNetworkReachability : NSObject + ++ (instancetype)shareInstance; + +- (void)startMonitoring; + +- (void)stopMonitoring; + +- (NSString *)networkState; + +- (nullable NSString *)carrier; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.h.meta new file mode 100644 index 0000000..10cc8c4 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 232bba0170c5e4894aac72664982367f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.m new file mode 100644 index 0000000..7f4d4e7 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.m @@ -0,0 +1,180 @@ +// +// TDNetworkReachability.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/1/15. +// + +#import "TDNetworkReachability.h" +#import +#import +#import +#import "TDNotificationManager+Networking.h" + +@interface TDNetworkReachability () +@property (atomic, assign) SCNetworkReachabilityRef reachability; +@property (nonatomic, assign) BOOL isWifi; +@property (nonatomic, assign) BOOL isWwan; +@property (nonatomic, strong) CTTelephonyNetworkInfo *td_TelephonyNetworkInfo; + +@end + +@implementation TDNetworkReachability + +#if TARGET_OS_IOS +static void ThinkingReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) { + TDNetworkReachability *instance = (__bridge TDNetworkReachability *)info; + if (instance && [instance isKindOfClass:[TDNetworkReachability class]]) { + [instance reachabilityChanged:flags]; + [TDNotificationManager postNetworkStatusChanged:[instance networkState]]; + } +} +#endif + +//MARK: - Public Methods + ++ (instancetype)shareInstance { + static dispatch_once_t onceToken; + static TDNetworkReachability *reachability = nil; + dispatch_once(&onceToken, ^{ + reachability = [[TDNetworkReachability alloc] init]; + }); + return reachability; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.td_TelephonyNetworkInfo = [[CTTelephonyNetworkInfo alloc] init]; + } + return self; +} + +- (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); +} + +//MARK: - Private Methods + +- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags { + self.isWifi = (flags & kSCNetworkReachabilityFlagsReachable) && !(flags & kSCNetworkReachabilityFlagsIsWWAN); + self.isWwan = (flags & kSCNetworkReachabilityFlagsIsWWAN); +} + +- (NSString *)currentRadio { + NSString *networkType = @"NULL"; + @try { + NSString *currentRadio = nil; +#ifdef __IPHONE_12_0 + if (@available(iOS 12.0, *)) { + NSDictionary *serviceCurrentRadio = [self.td_TelephonyNetworkInfo serviceCurrentRadioAccessTechnology]; + if ([serviceCurrentRadio isKindOfClass:[NSDictionary class]] && serviceCurrentRadio.allValues.count>0) { + currentRadio = serviceCurrentRadio.allValues[0]; + } + } +#endif + if (currentRadio == nil && [self.td_TelephonyNetworkInfo.currentRadioAccessTechnology isKindOfClass:[NSString class]]) { + currentRadio = self.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; +} + +- (nullable NSString *)carrier { +#ifdef __IPHONE_16_0 + return nil; +#else + CTCarrier *carrier = nil; + NSString *carrierName = @""; +#ifdef __IPHONE_12_0 + if (@available(iOS 12.0, *)) { + NSArray *carrierKeysArray = [self.td_TelephonyNetworkInfo.serviceSubscriberCellularProviders.allKeys sortedArrayUsingSelector:@selector(compare:)]; + carrier = self.td_TelephonyNetworkInfo.serviceSubscriberCellularProviders[carrierKeysArray.firstObject]; + if (!carrier.mobileNetworkCode) { + carrier = self.td_TelephonyNetworkInfo.serviceSubscriberCellularProviders[carrierKeysArray.lastObject]; + } + } +#endif + if (!carrier) { + carrier = [self.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; + } + return carrierName; +#endif +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.m.meta new file mode 100644 index 0000000..3b30514 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Network/TDNetworkReachability.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 94d9f886cb2114d6db9f5881a5bc6ed0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage.meta new file mode 100644 index 0000000..75cb000 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7e20b11921d6b4135899c0c8c7f122ee +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.h new file mode 100644 index 0000000..5fdf305 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.h @@ -0,0 +1,63 @@ +// +// TDNotificationManager+Analytics.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/1/12. +// + +#if __has_include() +#import +#else +#import "TDNotificationManager.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +/// init +extern NSString * const kAnalyticsNotificationNameInit; +// login +extern NSString * const kAnalyticsNotificationNameLogin; +// logout +extern NSString * const kAnalyticsNotificationNameLogout; +// set distinct id +extern NSString * const kAnalyticsNotificationNameSetDistinctId; +// app install event +extern NSString * const kAnalyticsNotificationNameAppInstall; +// track event +extern NSString * const kAnalyticsNotificationNameTrack; + +// params +extern NSString * const kAnalyticsNotificationParamsAppId; +extern NSString * const kAnalyticsNotificationParamsServerUrl; +extern NSString * const kAnalyticsNotificationParamsAccountId; +extern NSString * const kAnalyticsNotificationParamsDistinctId; +extern NSString * const kAnalyticsNotificationParamsEvent; + +@interface TDNotificationManager (Analytics) + +/// analytics init event +/// Observer will receive data: e.g. {"appId": "", "serverUrl": ""} ++ (void)postAnalyticsInitEventWithAppId:(nonnull NSString *)appId serverUrl:(nonnull NSString *)serverUrl; + +/// analytics login event +/// Observer will receive data: e.g. {"appId": "", "accountId": "", "distinctId": ""} ++ (void)postAnalyticsLoginEventWithAppId:(nonnull NSString *)appId accountId:(nonnull NSString *)accountId distinctId:(nonnull NSString *)distinctId; + +/// analytics logout event +/// Observer will receive data: e.g. {"appId": "", "distinctId": ""} ++ (void)postAnalyticsLogoutEventWithAppId:(nonnull NSString *)appId distinctId:(nonnull NSString *)distinctId; + +/// analytics set distinct id event +/// Observer will receive data: e.g. {"appId": "", "accountId": "", "distinctId": ""} ++ (void)postAnalyticsSetDistinctIdEventWithAppId:(nonnull NSString *)appId accountId:(nullable NSString *)accountId distinctId:(nonnull NSString *)distinctId; + +/// analytics app install event ++ (void)postAnalyticsAppInstallEventWithAppId:(NSString *)appId; + +/// analytics track event +/// Observer will receive data: e.g. {"appId": "", "event": ""} ++ (void)postAnalyticsTrackWithAppId:(nonnull NSString *)appId event:(nonnull NSDictionary *)event; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.h.meta new file mode 100644 index 0000000..21ae223 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5da19ae19dfaf4e589bf7453079d580c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.m new file mode 100644 index 0000000..8c7f205 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.m @@ -0,0 +1,101 @@ +// +// TDNotificationManager+Analytics.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/1/12. +// + +#import "TDNotificationManager+Analytics.h" + +NSString * const kAnalyticsNotificationNameInit = @"kAnalyticsNotificationNameInit"; +NSString * const kAnalyticsNotificationNameLogin = @"kAnalyticsNotificationNameLogin"; +NSString * const kAnalyticsNotificationNameLogout = @"kAnalyticsNotificationNameLogout"; +NSString * const kAnalyticsNotificationNameSetDistinctId = @"kAnalyticsNotificationNameSetDistinctId"; +NSString * const kAnalyticsNotificationNameAppInstall = @"kAnalyticsNotificationNameAppInstall"; +NSString * const kAnalyticsNotificationNameTrack = @"kAnalyticsNotificationNameTrack"; + +NSString * const kAnalyticsNotificationParamsAppId = @"appId"; +NSString * const kAnalyticsNotificationParamsServerUrl = @"serverUrl"; +NSString * const kAnalyticsNotificationParamsAccountId = @"accountId"; +NSString * const kAnalyticsNotificationParamsDistinctId = @"distinctId"; +NSString * const kAnalyticsNotificationParamsEvent = @"event"; + + +@implementation TDNotificationManager (Analytics) + ++ (void)postAnalyticsInitEventWithAppId:(NSString *)appId serverUrl:(NSString *)serverUrl { + if (appId.length == 0 || serverUrl.length == 0) { + return; + } + NSDictionary *userInfo = @{ + kAnalyticsNotificationParamsAppId: appId, + kAnalyticsNotificationParamsServerUrl: serverUrl + }; + [self postNotificationName:kAnalyticsNotificationNameInit object:nil userInfo:userInfo]; +} + ++ (void)postAnalyticsLoginEventWithAppId:(NSString *)appId accountId:(NSString *)accountId distinctId:(NSString *)distinctId { + if (appId.length == 0 || accountId.length == 0) { + return; + } + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:@{ + kAnalyticsNotificationParamsAppId: appId, + kAnalyticsNotificationParamsAccountId: accountId, + }]; + if (distinctId.length) { + userInfo[kAnalyticsNotificationParamsDistinctId] = distinctId; + } + [self postNotificationName:kAnalyticsNotificationNameLogin object:nil userInfo:userInfo]; +} + ++ (void)postAnalyticsLogoutEventWithAppId:(NSString *)appId distinctId:(NSString *)distinctId { + if (appId.length == 0) { + return; + } + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:@{ + kAnalyticsNotificationParamsAppId: appId, + }]; + if (distinctId.length) { + userInfo[kAnalyticsNotificationParamsDistinctId] = distinctId; + } + + [self postNotificationName:kAnalyticsNotificationNameLogout object:nil userInfo:userInfo]; +} + ++ (void)postAnalyticsSetDistinctIdEventWithAppId:(NSString *)appId accountId:(NSString *)accountId distinctId:(NSString *)distinctId { + if (appId.length == 0 || distinctId.length == 0) { + return; + } + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:@{ + kAnalyticsNotificationParamsAppId: appId, + kAnalyticsNotificationParamsDistinctId: distinctId, + }]; + if (accountId.length) { + userInfo[kAnalyticsNotificationParamsAccountId] = accountId; + } + + [self postNotificationName:kAnalyticsNotificationNameSetDistinctId object:nil userInfo:userInfo]; +} + ++ (void)postAnalyticsAppInstallEventWithAppId:(NSString *)appId { + if (appId.length == 0) { + return; + } + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:@{ + kAnalyticsNotificationParamsAppId: appId, + }]; + [self postNotificationName:kAnalyticsNotificationNameAppInstall object:nil userInfo:userInfo]; +} + ++ (void)postAnalyticsTrackWithAppId:(NSString *)appId event:(NSDictionary *)event { + if (appId.length == 0 || event.count == 0) { + return; + } + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:@{ + kAnalyticsNotificationParamsAppId: appId, + kAnalyticsNotificationParamsEvent: event, + }]; + [self postNotificationName:kAnalyticsNotificationNameTrack object:nil userInfo:userInfo]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.m.meta new file mode 100644 index 0000000..4aea6d1 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Analytics.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 73e405d08084948cdb5b992f92d751f1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.h new file mode 100644 index 0000000..15e255e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.h @@ -0,0 +1,25 @@ +// +// TDNotificationManager+Core.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/6/27. +// + +#if __has_include() +#import +#else +#import "TDNotificationManager.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +extern NSString * const kCoreNotificationNameCalibratedTimeSuccess; +extern NSString * const kCoreNotificationParamsCalibratedTimeNow; + +@interface TDNotificationManager (Core) + ++ (void)postCoreNotificationCalibratedTimeSuccess:(NSDate *)now; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.h.meta new file mode 100644 index 0000000..b408784 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 72056eed9d556463bb4cf7066654cd3d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.m new file mode 100644 index 0000000..96afdd4 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.m @@ -0,0 +1,24 @@ +// +// TDNotificationManager+Core.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/6/27. +// + +#import "TDNotificationManager+Core.h" + +NSString * const kCoreNotificationNameCalibratedTimeSuccess = @"kCoreNotificationNameCalibratedTimeSuccess"; +NSString * const kCoreNotificationParamsCalibratedTimeNow = @"now"; + +@implementation TDNotificationManager (Core) + ++ (void)postCoreNotificationCalibratedTimeSuccess:(NSDate *)now { + if (![now isKindOfClass:NSDate.class]) { + return; + } + [self postNotificationName:kCoreNotificationNameCalibratedTimeSuccess object:nil userInfo:@{ + kCoreNotificationParamsCalibratedTimeNow: now + }]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.m.meta new file mode 100644 index 0000000..bb61440 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Core.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3157b4a348b8e466b9b0e1efe26fcae6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.h new file mode 100644 index 0000000..1c453bb --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.h @@ -0,0 +1,25 @@ +// +// TDNotificationManager+Networking.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/1/15. +// + +#if __has_include() +#import +#else +#import "TDNotificationManager.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +extern NSString * const kNetworkNotificationNameStatusChange; +extern NSString * const kNetworkNotificationParamsNetworkType; + +@interface TDNotificationManager (Networking) + ++ (void)postNetworkStatusChanged:(NSString *)networkStatus; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.h.meta new file mode 100644 index 0000000..d911015 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 47fd1e3d199b24b04bd6f24e8e1a9b61 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.m new file mode 100644 index 0000000..1fbef97 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.m @@ -0,0 +1,24 @@ +// +// TDNotificationManager+Networking.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/1/15. +// + +#import "TDNotificationManager+Networking.h" + +NSString * const kNetworkNotificationNameStatusChange = @"kNetworkNotificationNameStatusChange"; +NSString * const kNetworkNotificationParamsNetworkType = @"network"; + +@implementation TDNotificationManager (Networking) + ++ (void)postNetworkStatusChanged:(NSString *)networkStatus { + if (networkStatus.length == 0) { + return; + } + [self postNotificationName:kNetworkNotificationNameStatusChange object:nil userInfo:@{ + kNetworkNotificationParamsNetworkType: networkStatus + }]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.m.meta new file mode 100644 index 0000000..f4cd03b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+Networking.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1d9b5e7f0230b4d3dbee64255faf24c8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.h new file mode 100644 index 0000000..9606a6c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.h @@ -0,0 +1,42 @@ +// +// TDNotificationManager+RemoteConfig.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/3/2. +// + +#if __has_include() +#import +#else +#import "TDNotificationManager.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +extern NSString * const kRemoteConfigNotificationNameTemplateWillStop; +extern NSString * const kRemoteConfigNotificationNameTemplateFetchSuccess; +extern NSString * const kRemoteConfigNotificationNameTemplateFetchFailed; +extern NSString * const kRemoteConfigNotificationParamAppId; +extern NSString * const kRemoteConfigNotificationParamTemplateCode; +extern NSString * const kRemoteConfigNotificationParamClientUserId; +extern NSString * const kRemoteConfigNotificationParamTemplateInfo; +extern NSString * const kRemoteConfigNotificationParamErrorCode; +extern NSString * const kRemoteConfigNotificationParamIsDebug; + +extern NSString * const kRemoteConfigNotificationNameSystemConfigFetchSuccess; +extern NSString * const kRemoteConfigNotificationParamSystemConfig; + +@interface TDNotificationManager (RemoteConfig) + ++ (void)postRemoteConfigWillStopTemplateCode:(nonnull NSString *)templateCode appId:(nonnull NSString *)appId clientUserId:(nonnull NSString *)clientUserId; + ++ (void)postRemoteConfigFetchTemplateSuccessIsDebug:(BOOL)isDebug templateCode:(nonnull NSString *)templateCode appId:(nonnull NSString *)appId clientUserId:(nonnull NSString *)clientUserId templateInfo:(nonnull NSDictionary *)templateInfo; + ++ (void)postRemoteConfigFetchTemplateFailedIsDebug:(BOOL)isDebug templateCode:(nonnull NSString *)templateCode appId:(nonnull NSString *)appId clientUserId:(nonnull NSString *)clientUserId errorCode:(NSNumber *)errorCode; + ++ (void)postRemoteConfigFetchSystemConfigSuccessWithAppId:(nonnull NSString *)appId systemConfig:(nullable NSDictionary *)systemConfig; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.h.meta new file mode 100644 index 0000000..9e32820 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8e985713547844520bae1181966c0aed +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.m new file mode 100644 index 0000000..5f63c8f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.m @@ -0,0 +1,78 @@ +// +// TDNotificationManager+RemoteConfig.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/3/2. +// + +#import "TDNotificationManager+RemoteConfig.h" + +NSString * const kRemoteConfigNotificationNameTemplateWillStop = @"kRemoteConfigNotificationNameTemplateWillStop"; +NSString * const kRemoteConfigNotificationNameTemplateFetchSuccess = @"kRemoteConfigNotificationNameTemplateFetchSuccess"; +NSString * const kRemoteConfigNotificationNameTemplateFetchFailed = @"kRemoteConfigNotificationNameTemplateFetchFailed"; +NSString * const kRemoteConfigNotificationParamAppId = @"appId"; +NSString * const kRemoteConfigNotificationParamTemplateCode = @"templateCode"; +NSString * const kRemoteConfigNotificationParamClientUserId = @"clientUserId"; +NSString * const kRemoteConfigNotificationParamTemplateInfo = @"templateInfo"; +NSString * const kRemoteConfigNotificationParamErrorCode = @"errorCode"; +NSString * const kRemoteConfigNotificationParamIsDebug = @"isDebug"; + +NSString * const kRemoteConfigNotificationNameSystemConfigFetchSuccess = @"kRemoteConfigNotificationNameSystemConfigFetchSuccess"; +NSString * const kRemoteConfigNotificationParamSystemConfig = @"kRemoteConfigNotificationParamSystemConfig"; + +@implementation TDNotificationManager (RemoteConfig) + ++ (void)postRemoteConfigWillStopTemplateCode:(NSString *)templateCode appId:(NSString *)appId clientUserId:(NSString *)clientUserId { + if (templateCode.length == 0 || appId.length == 0 || clientUserId.length == 0) { + return; + } + NSDictionary *userInfo = @{ + kRemoteConfigNotificationParamAppId: appId, + kRemoteConfigNotificationParamTemplateCode: templateCode, + kRemoteConfigNotificationParamClientUserId: clientUserId + }; + [self postNotificationName:kRemoteConfigNotificationNameTemplateWillStop object:nil userInfo:userInfo]; +} + ++ (void)postRemoteConfigFetchTemplateSuccessIsDebug:(BOOL)isDebug templateCode:(NSString *)templateCode appId:(NSString *)appId clientUserId:(NSString *)clientUserId templateInfo:(NSDictionary *)templateInfo { + if (templateCode.length == 0 || appId.length == 0 || clientUserId.length == 0 || templateInfo.count == 0) { + return; + } + NSDictionary *userInfo = @{ + kRemoteConfigNotificationParamAppId: appId, + kRemoteConfigNotificationParamTemplateCode: templateCode, + kRemoteConfigNotificationParamClientUserId: clientUserId, + kRemoteConfigNotificationParamTemplateInfo: templateInfo, + kRemoteConfigNotificationParamIsDebug: @(isDebug), + }; + [self postNotificationName:kRemoteConfigNotificationNameTemplateFetchSuccess object:nil userInfo:userInfo]; +} + ++ (void)postRemoteConfigFetchTemplateFailedIsDebug:(BOOL)isDebug templateCode:(NSString *)templateCode appId:(NSString *)appId clientUserId:(NSString *)clientUserId errorCode:(NSNumber *)errorCode { + if (templateCode.length == 0 || appId.length == 0 || clientUserId.length == 0 || errorCode == nil) { + return; + } + NSDictionary *userInfo = @{ + kRemoteConfigNotificationParamAppId: appId, + kRemoteConfigNotificationParamTemplateCode: templateCode, + kRemoteConfigNotificationParamClientUserId: clientUserId, + kRemoteConfigNotificationParamErrorCode: errorCode, + kRemoteConfigNotificationParamIsDebug: @(isDebug), + }; + [self postNotificationName:kRemoteConfigNotificationNameTemplateFetchFailed object:nil userInfo:userInfo]; +} + ++ (void)postRemoteConfigFetchSystemConfigSuccessWithAppId:(NSString *)appId systemConfig:(nullable NSDictionary *)systemConfig { + if (appId.length == 0) { + return; + } + NSMutableDictionary *userInfo = [@{ + kRemoteConfigNotificationParamAppId: appId, + } mutableCopy]; + if ([systemConfig isKindOfClass:NSDictionary.class]) { + userInfo[kRemoteConfigNotificationParamSystemConfig] = systemConfig; + } + [self postNotificationName:kRemoteConfigNotificationNameSystemConfigFetchSuccess object:nil userInfo:userInfo]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.m.meta new file mode 100644 index 0000000..7588048 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager+RemoteConfig.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 00da302b023aa425480f00bb781ae32b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.h new file mode 100644 index 0000000..a9b7576 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.h @@ -0,0 +1,18 @@ +// +// TDNotificationManager.h +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/1/12. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDNotificationManager : NSObject + ++ (void)postNotificationName:(nonnull NSString *)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.h.meta new file mode 100644 index 0000000..58268f3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d85a39f4be406485fbbf889af81cf5dc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.m new file mode 100644 index 0000000..cbabd52 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.m @@ -0,0 +1,16 @@ +// +// TDNotificationManager.m +// Pods-DevelopProgram +// +// Created by 杨雄 on 2024/1/12. +// + +#import "TDNotificationManager.h" + +@implementation TDNotificationManager + ++ (void)postNotificationName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo { + [[NSNotificationCenter defaultCenter] postNotificationName:name object:object userInfo:userInfo]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.m.meta new file mode 100644 index 0000000..4fc2182 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/NotificationManage/TDNotificationManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: aa7107ac4deee4cd2af0bc5c04521c4d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router.meta new file mode 100644 index 0000000..17d63dd --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 46a327f0a2cb94432b1db1cfb4047bd4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.h new file mode 100644 index 0000000..d13db7a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.h @@ -0,0 +1,48 @@ +// +// TDMediator+Analytics.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/3/6. +// + +#if __has_include() +#import +#else +#import "TDMediator.h" +#endif + +#if __has_include() +#import +#else +#import "TDSettings.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +extern NSString * const kTDMediatorTargetAnalytics; + +@interface TDMediator (Analytics) + +- (void)tdAnalyticsInitWithSettings:(nullable TDSettings *)settings; + +- (nullable NSString *)tdAnalyticsGetAccountIdWithAppId:(nullable NSString *)appId; + +- (nullable NSString *)tdAnalyticsGetDistinctIdWithAppId:(nullable NSString *)appId; + +- (void)tdAnalyticsTrackEvent:(nonnull NSString *)eventName properties:(nullable NSDictionary *)properties appId:(nullable NSString *)appId; + +- (void)tdAnalyticsUserSetProperties:(nonnull NSDictionary *)properties appId:(nullable NSString *)appId; + +- (nullable NSDictionary *)tdAnalyticsGetPresetPropertiesWithAppId:(nullable NSString *)appId; + +- (void)tdAnalyticsTrackDebugEvent:(nonnull NSString *)eventName properties:(nullable NSDictionary *)properties appId:(nullable NSString *)appId; + +- (BOOL)tdAnalyticsGetEnableAutoPushWithAppId:(nullable NSString *)appId; + +- (NSArray *)tdAnalyticsGetAllAppIds; + +- (nullable NSString *)tdAnalyticsGetSDKVersion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.h.meta new file mode 100644 index 0000000..d32f3f6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ce32dad599f8d4f3bbff213ed9c9750b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.m new file mode 100644 index 0000000..eca78b5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.m @@ -0,0 +1,118 @@ +// +// TDMediator+Analytics.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/3/6. +// + +#import "TDMediator+Analytics.h" + +NSString * const kTDMediatorTargetAnalytics = @"Analytics"; + +NSString * const kTDMediatorTargetAnalyticsActionNativeInit = @"nativeInitWithParams"; +NSString * const kTDMediatorTargetAnalyticsActionNativeGetAccountId = @"nativeGetAccountIdWithParams"; +NSString * const kTDMediatorTargetAnalyticsActionNativeGetDistinctId = @"nativeGetDistinctIdWithParams"; +NSString * const kTDMediatorTargetAnalyticsActionNativeGetPresetProperties = @"nativeGetPresetPropertiesWithParams"; +NSString * const kTDMediatorTargetAnalyticsActionNativeTrackEvent = @"nativeTrackEventWithParams"; +NSString * const kTDMediatorTargetAnalyticsActionNativeUserSet = @"nativeUserSetWithParams"; +NSString * const kTDMediatorTargetAnalyticsActionNativeTrackDebugEvent = @"nativeTrackDebugEventWithParams"; +NSString * const kTDMediatorTargetAnalyticsActionNativeGetEnableAutoPush = @"nativeGetEnableAutoPushWithParams"; +NSString * const kTDMediatorTargetAnalyticsActionNativeGetAllAppIds = @"nativeGetAllAppIdsWithParams"; +NSString * const kTDMediatorTargetAnalyticsActionNativeGetSDKVersion = @"nativeGetSDKVersionWithParams"; + +@implementation TDMediator (Analytics) + +- (void)tdAnalyticsInitWithSettings:(TDSettings *)settings { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (settings) { + params[@"settings"] = settings; + } + [[TDMediator sharedInstance] performTarget:kTDMediatorTargetAnalytics action:kTDMediatorTargetAnalyticsActionNativeInit params:params shouldCacheTarget:NO]; +} + +- (nullable NSString *)tdAnalyticsGetAccountIdWithAppId:(nullable NSString *)appId { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId) { + params[@"appId"] = appId; + } + NSString *accountId = [[TDMediator sharedInstance] performTarget:kTDMediatorTargetAnalytics action:kTDMediatorTargetAnalyticsActionNativeGetAccountId params:params shouldCacheTarget:NO]; + return accountId; +} + +- (nullable NSString *)tdAnalyticsGetDistinctIdWithAppId:(nullable NSString *)appId { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId) { + params[@"appId"] = appId; + } + NSString *distinctId = [[TDMediator sharedInstance] performTarget:kTDMediatorTargetAnalytics action:kTDMediatorTargetAnalyticsActionNativeGetDistinctId params:params shouldCacheTarget:NO]; + return distinctId; +} + +- (void)tdAnalyticsTrackEvent:(nonnull NSString *)eventName properties:(nullable NSDictionary *)properties appId:(nullable NSString *)appId { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId) { + params[@"appId"] = appId; + } + if (eventName) { + params[@"eventName"] = eventName; + } + if (properties) { + params[@"properties"] = properties; + } + [[TDMediator sharedInstance] performTarget:kTDMediatorTargetAnalytics action:kTDMediatorTargetAnalyticsActionNativeTrackEvent params:params shouldCacheTarget:NO needModuleReady:YES]; +} + +- (void)tdAnalyticsUserSetProperties:(nonnull NSDictionary *)properties appId:(nullable NSString *)appId { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId) { + params[@"appId"] = appId; + } + if (properties) { + params[@"properties"] = properties; + } + [[TDMediator sharedInstance] performTarget:kTDMediatorTargetAnalytics action:kTDMediatorTargetAnalyticsActionNativeUserSet params:params shouldCacheTarget:NO needModuleReady:YES]; +} + +- (nullable NSDictionary *)tdAnalyticsGetPresetPropertiesWithAppId:(NSString *)appId { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId) { + params[@"appId"] = appId; + } + NSDictionary *dict = [[TDMediator sharedInstance] performTarget:kTDMediatorTargetAnalytics action:kTDMediatorTargetAnalyticsActionNativeGetPresetProperties params:params shouldCacheTarget:NO]; + return dict; +} + +- (void)tdAnalyticsTrackDebugEvent:(NSString *)eventName properties:(NSDictionary *)properties appId:(NSString *)appId { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId) { + params[@"appId"] = appId; + } + if (eventName) { + params[@"eventName"] = eventName; + } + if (properties) { + params[@"properties"] = properties; + } + [[TDMediator sharedInstance] performTarget:kTDMediatorTargetAnalytics action:kTDMediatorTargetAnalyticsActionNativeTrackDebugEvent params:params shouldCacheTarget:NO needModuleReady:YES]; +} + +- (BOOL)tdAnalyticsGetEnableAutoPushWithAppId:(NSString *)appId { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId) { + params[@"appId"] = appId; + } + BOOL enableAutoPush = [[[TDMediator sharedInstance] performTarget:kTDMediatorTargetAnalytics action:kTDMediatorTargetAnalyticsActionNativeGetEnableAutoPush params:params shouldCacheTarget:NO] boolValue]; + return enableAutoPush; +} + +- (NSArray *)tdAnalyticsGetAllAppIds { + NSArray *appIds = [[TDMediator sharedInstance] performTarget:kTDMediatorTargetAnalytics action:kTDMediatorTargetAnalyticsActionNativeGetAllAppIds params:nil shouldCacheTarget:NO]; + return appIds; +} + +- (nullable NSString *)tdAnalyticsGetSDKVersion { + NSString *version = [[TDMediator sharedInstance] performTarget:kTDMediatorTargetAnalytics action:kTDMediatorTargetAnalyticsActionNativeGetSDKVersion params:nil shouldCacheTarget:NO]; + return version; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.m.meta new file mode 100644 index 0000000..904ee0b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Analytics.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: de92d976bf5b745ab83a18249cdc4c61 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.h new file mode 100644 index 0000000..9eceecd --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.h @@ -0,0 +1,39 @@ +// +// TDMediator+RemoteConfig.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/3/6. +// + +#if __has_include() +#import +#else +#import "TDMediator.h" +#endif + +#if __has_include() +#import +#else +#import "TDSettings.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDMediator (RemoteConfig) + +- (void)tdRemoteConfigInitWithSettings:(nonnull TDSettings *)settings; + +- (nullable NSString *)tdRemoteConfigGetClientUserIdWithAccountId:(nullable NSString *)accountId distinctId:(nullable NSString *)distinctId appId:(nonnull NSString *)appId; + +- (nullable NSDictionary *)tdRemoteConfigGetApplyValueWithAppId:(nonnull NSString *)appId templateCode:(nullable NSString *)code; + +- (nullable NSDictionary *)tdRemoteConfigGetSystemConfigWithAppId:(nonnull NSString *)appId; + +- (BOOL)tdRemoteConfigIsSDKCloseWithAppId:(nonnull NSString *)appId; + +- (void)tdRemoteConfigFetchTemplateWithAppId:(nonnull NSString *)appId templateCode:(nullable NSString *)code; + +@end + + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.h.meta new file mode 100644 index 0000000..31b5a18 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9f7b2aeb76f62465f920e18fcb4be56d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.m new file mode 100644 index 0000000..5bcbb58 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.m @@ -0,0 +1,85 @@ +// +// TDMediator+RemoteConfig.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/3/6. +// + +#import "TDMediator+RemoteConfig.h" + +NSString * const kTDMediatorTargetRemoteConfig = @"RemoteConfig"; + +NSString * const kTDMediatorTargetRemoteConfigActionNativeInit = @"nativeInitWithParams"; +NSString * const kTDMediatorTargetRemoteConfigActionNativeGetClientUserId = @"nativeGetClientUserIdWithParams"; +NSString * const kTDMediatorTargetRemoteConfigActionNativeGetApplyValue = @"nativeGetApplyValueWithParams"; +NSString * const kTDMediatorTargetRemoteConfigActionNativeGetSystemConfig = @"nativeGetSystemConfigWithParams"; +NSString * const kTDMediatorTargetRemoteConfigActionNativeIsSDKClose = @"nativeIsSDKCloseWithParams"; +NSString * const kTDMediatorTargetRemoteConfigActionNativeFetchTemplateInfo = @"nativeFetchTemplateInfoWithParams"; + +@implementation TDMediator (RemoteConfig) + +- (void)tdRemoteConfigInitWithSettings:(TDSettings *)settings { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (settings) { + params[@"settings"] = settings; + } + [[TDMediator sharedInstance] performTarget:kTDMediatorTargetRemoteConfig action:kTDMediatorTargetRemoteConfigActionNativeInit params:params shouldCacheTarget:NO]; +} + +- (NSString *)tdRemoteConfigGetClientUserIdWithAccountId:(NSString *)accountId distinctId:(NSString *)distinctId appId:(nonnull NSString *)appId { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (accountId.length) { + params[@"accountId"] = accountId; + } + if (distinctId.length) { + params[@"distinctId"] = distinctId; + } + if (appId.length) { + params[@"appId"] = appId; + } + NSString *userId = [[TDMediator sharedInstance] performTarget:kTDMediatorTargetRemoteConfig action:kTDMediatorTargetRemoteConfigActionNativeGetClientUserId params:params shouldCacheTarget:NO]; + return userId; +} + +- (NSDictionary *)tdRemoteConfigGetApplyValueWithAppId:(NSString *)appId templateCode:(NSString *)code { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId.length) { + params[@"appId"] = appId; + } + if (code.length) { + params[@"templateCode"] = code; + } + NSDictionary *dict = [[TDMediator sharedInstance] performTarget:kTDMediatorTargetRemoteConfig action:kTDMediatorTargetRemoteConfigActionNativeGetApplyValue params:params shouldCacheTarget:NO]; + return dict; +} + +- (NSDictionary *)tdRemoteConfigGetSystemConfigWithAppId:(NSString *)appId { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId.length) { + params[@"appId"] = appId; + } + NSDictionary *dict = [[TDMediator sharedInstance] performTarget:kTDMediatorTargetRemoteConfig action:kTDMediatorTargetRemoteConfigActionNativeGetSystemConfig params:params shouldCacheTarget:NO]; + return dict; +} + +- (BOOL)tdRemoteConfigIsSDKCloseWithAppId:(NSString *)appId { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId.length) { + params[@"appId"] = appId; + } + NSNumber *value = [[TDMediator sharedInstance] performTarget:kTDMediatorTargetRemoteConfig action:kTDMediatorTargetRemoteConfigActionNativeIsSDKClose params:params shouldCacheTarget:NO]; + return value.boolValue; +} + +- (void)tdRemoteConfigFetchTemplateWithAppId:(NSString *)appId templateCode:(NSString *)code { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (appId.length) { + params[@"appId"] = appId; + } + if (code.length) { + params[@"templateCode"] = code; + } + [[TDMediator sharedInstance] performTarget:kTDMediatorTargetRemoteConfig action:kTDMediatorTargetRemoteConfigActionNativeFetchTemplateInfo params:params shouldCacheTarget:NO]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.m.meta new file mode 100644 index 0000000..704c5a1 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+RemoteConfig.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: afd38c24134d54299b338eefab26b31d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.h new file mode 100644 index 0000000..c04075d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.h @@ -0,0 +1,30 @@ +// +// TDMediator+Strategy.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/21. +// + +#if __has_include() +#import +#else +#import "TDMediator.h" +#endif + +#if __has_include() +#import +#else +#import "TDSettings.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDMediator (Strategy) + +- (void)tdStrategyInitWithSettings:(nullable TDSettings *)settings; + +- (nullable NSString *)tdStrategyGetSDKVersion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.h.meta new file mode 100644 index 0000000..3fdb841 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8221ed81aecc44d019af92a93708a73c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.m new file mode 100644 index 0000000..dbb05b9 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.m @@ -0,0 +1,33 @@ +// +// TDMediator+Strategy.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/21. +// + +#import "TDMediator+Strategy.h" + +NSString * const kTDMediatorTargetStrategy = @"Strategy"; + +NSString * const kTDMediatorTargetStrategyActionNativeInit = @"nativeInitWithParams"; +NSString * const kTDMediatorTargetStrategyActionNativeGetSDKVersion = @"nativeGetSDKVersionWithParams"; + +@implementation TDMediator (Strategy) + +- (void)tdStrategyInitWithSettings:(TDSettings *)settings { + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + if (settings) { + params[@"settings"] = settings; + } + [[TDMediator sharedInstance] performTarget:kTDMediatorTargetStrategy action:kTDMediatorTargetStrategyActionNativeInit params:params shouldCacheTarget:NO]; +} + +- (NSString *)tdStrategyGetSDKVersion { + NSString *version = [[TDMediator sharedInstance] performTarget:kTDMediatorTargetStrategy action:kTDMediatorTargetStrategyActionNativeGetSDKVersion params:nil shouldCacheTarget:NO]; + if ([version isKindOfClass:NSString.class]) { + return version; + } + return nil; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.m.meta new file mode 100644 index 0000000..23391be --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator+Strategy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f40cfaf9c3c9f4aeab983e8a41bcaba6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.h new file mode 100644 index 0000000..e9e71d5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.h @@ -0,0 +1,32 @@ +// +// TDMediator.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/3/6. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +extern NSString * _Nonnull const kTDMediatorParamsKeySwiftTargetModuleName; + +@interface TDMediator : NSObject + ++ (instancetype)sharedInstance; + +- (void)registerSuccessWithTargetName:(nonnull NSString *)targetName; + +- (id _Nullable)performActionWithUrl:(NSURL * _Nullable)url completion:(void(^_Nullable)(NSDictionary * _Nullable info))completion; + +- (id _Nullable)performTarget:(NSString * _Nonnull)targetName action:(NSString * _Nonnull)actionName params:(NSDictionary * _Nullable)params shouldCacheTarget:(BOOL)shouldCacheTarget; + +- (id _Nullable)performTarget:(NSString * _Nonnull)targetName action:(NSString * _Nonnull)actionName params:(NSDictionary * _Nullable)params shouldCacheTarget:(BOOL)shouldCacheTarget needModuleReady:(BOOL)needModuleReady; + +- (void)releaseCachedTargetWithFullTargetName:(NSString * _Nullable)fullTargetName; + +- (BOOL)check:(NSString * _Nullable)targetName moduleName:(NSString * _Nullable)moduleName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.h.meta new file mode 100644 index 0000000..542d315 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6e84f72bcd5974d1e9a335308e8b69eb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.m new file mode 100644 index 0000000..564892a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.m @@ -0,0 +1,289 @@ +// +// TDMediator.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/3/6. +// + +#import "TDMediator.h" +#import "TDCoreLog.h" + +NSString * const kTDMediatorParamsKeySwiftTargetModuleName = @"kTDMediatorParamsKeySwiftTargetModuleName"; + +@interface TDMediatorPerformInstance : NSObject +@property (nonatomic, copy) NSString *targetName; +@property (nonatomic, copy) NSString *actionName; +@property (nonatomic, strong) NSDictionary *params; + +@end + +@implementation TDMediatorPerformInstance +@end + + +@interface TDMediator () +@property (nonatomic, strong) NSMutableDictionary *cachedTarget; +@property (nonatomic, strong) NSMutableArray *registerTargetNames; +@property (nonatomic, strong) NSMutableDictionary *> *cachedPerformInstance; + +@end + +@implementation TDMediator + ++ (instancetype)sharedInstance { + static TDMediator *mediator; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + mediator = [[TDMediator alloc] init]; + }); + return mediator; +} + +- (instancetype)init { + self = [super init]; + if (self) { + self.cachedTarget = [[NSMutableDictionary alloc] init]; + self.registerTargetNames = [NSMutableArray array]; + self.cachedPerformInstance = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)registerSuccessWithTargetName:(NSString *)targetName { + if ([targetName isKindOfClass:NSString.class] && targetName.length > 0) { + @synchronized (self) { + [self.registerTargetNames addObject:targetName]; + NSMutableArray *cached = self.cachedPerformInstance[targetName]; + for (TDMediatorPerformInstance *instance in cached) { + [self performTarget:instance.targetName action:instance.actionName params:instance.params shouldCacheTarget:NO]; + } + self.cachedPerformInstance[targetName] = [NSMutableArray array]; + } + } +} + +/// call by url +/// - Parameters: +/// - url: scheme://[target]/[action]?[params] e.g. aaa://targetA/actionB?id=1234 +/// - completion: completion description +- (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary * _Nullable))completion { + if (url == nil||![url isKindOfClass:[NSURL class]]) { + return nil; + } + + NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; + NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:url.absoluteString]; + [urlComponents.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if (obj.value&&obj.name) { + [params setObject:obj.value forKey:obj.name]; + } + }]; + + // refuse calls apis which only for local through remote url + NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""]; + if ([actionName hasPrefix:@"native"]) { + return @(NO); + } + + id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO]; + if (completion) { + if (result) { + completion(@{@"result":result}); + } else { + completion(nil); + } + } + return result; +} + +- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget { + return [self performTarget:targetName action:actionName params:params shouldCacheTarget:shouldCacheTarget needModuleReady:NO]; +} + +- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget needModuleReady:(BOOL)needModuleReady { + if (targetName == nil || actionName == nil) { + return nil; + } + + if (needModuleReady) { + @synchronized (self) { + if (![self.registerTargetNames containsObject:targetName]) { + NSMutableArray *cached = self.cachedPerformInstance[targetName]; + if (!cached) { + cached = [NSMutableArray array]; + self.cachedPerformInstance[targetName] = cached; + } + TDMediatorPerformInstance *instance = [[TDMediatorPerformInstance alloc] init]; + instance.targetName = targetName; + instance.actionName = actionName; + instance.params = params; + if (cached.count < 100) { + [cached addObject:instance]; + [TDCoreLog printLog:@"Router cache successed. target: %@, action: %@, params: %@", targetName, actionName, params]; + } else { + [TDCoreLog printLog:@"Router Cache fulled. Drop it. target: %@, action: %@, params: %@", targetName, actionName, params]; + } + return nil; + } + } + } + + NSString *swiftModuleName = params[kTDMediatorParamsKeySwiftTargetModuleName]; + + // generate target + NSString *targetClassString = nil; + if (swiftModuleName.length > 0) { + targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName]; + } else { + targetClassString = [NSString stringWithFormat:@"Target_%@", targetName]; + } + NSObject *target = [self safeFetchCachedTarget:targetClassString]; + if (target == nil) { + Class targetClass = NSClassFromString(targetClassString); + target = [[targetClass alloc] init]; + } + + // generate action + NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName]; + SEL action = NSSelectorFromString(actionString); + + if (target == nil) { + // process no target-action calling + [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params]; + return nil; + } + + if (shouldCacheTarget) { + [self safeSetCachedTarget:target key:targetClassString]; + } + + if ([target respondsToSelector:action]) { + return [self safePerformAction:action target:target params:params]; + } else { + // call default function "notFound:" in target when action is not found + SEL action = NSSelectorFromString(@"notFound:"); + if ([target respondsToSelector:action]) { + return [self safePerformAction:action target:target params:params]; + } else { + // process no target-action calling + [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params]; + @synchronized (self) { + [self.cachedTarget removeObjectForKey:targetClassString]; + } + return nil; + } + } +} + +/// release cache +/// - Parameter fullTargetName: OC: e.g. Target_XXX. Swift: e.g. XXXModule.Target_YYY +- (void)releaseCachedTargetWithFullTargetName:(NSString *)fullTargetName { + if (fullTargetName == nil) { + return; + } + @synchronized (self) { + [self.cachedTarget removeObjectForKey:fullTargetName]; + } +} + +- (BOOL)check:(NSString * _Nullable)targetName moduleName:(NSString * _Nullable)moduleName{ + if (moduleName.length > 0) { + return NSClassFromString([NSString stringWithFormat:@"%@.Target_%@", moduleName, targetName]) != nil; + } else { + return NSClassFromString([NSString stringWithFormat:@"Target_%@", targetName]) != nil; + } +} + +- (void)NoTargetActionResponseWithTargetString:(NSString *)targetString selectorString:(NSString *)selectorString originParams:(NSDictionary *)originParams { + SEL action = NSSelectorFromString(@"Action_response:"); + NSObject *target = [[NSClassFromString(@"Target_NoTargetAction") alloc] init]; + + NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; + params[@"originParams"] = originParams; + params[@"targetString"] = targetString; + params[@"selectorString"] = selectorString; + + [self safePerformAction:action target:target params:params]; +} + +- (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params { + NSMethodSignature *methodSig = [target methodSignatureForSelector:action]; + if (methodSig == nil) { + return nil; + } + const char *retType = [methodSig methodReturnType]; + + if (strcmp(retType, @encode(void)) == 0) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; + [invocation setArgument:¶ms atIndex:2]; + [invocation setSelector:action]; + [invocation setTarget:target]; + [invocation invoke]; + return nil; + } + + if (strcmp(retType, @encode(NSInteger)) == 0) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; + [invocation setArgument:¶ms atIndex:2]; + [invocation setSelector:action]; + [invocation setTarget:target]; + [invocation invoke]; + NSInteger result = 0; + [invocation getReturnValue:&result]; + return @(result); + } + + if (strcmp(retType, @encode(BOOL)) == 0) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; + [invocation setArgument:¶ms atIndex:2]; + [invocation setSelector:action]; + [invocation setTarget:target]; + [invocation invoke]; + BOOL result = 0; + [invocation getReturnValue:&result]; + return @(result); + } + + if (strcmp(retType, @encode(CGFloat)) == 0) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; + [invocation setArgument:¶ms atIndex:2]; + [invocation setSelector:action]; + [invocation setTarget:target]; + [invocation invoke]; + CGFloat result = 0; + [invocation getReturnValue:&result]; + return @(result); + } + + if (strcmp(retType, @encode(NSUInteger)) == 0) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; + [invocation setArgument:¶ms atIndex:2]; + [invocation setSelector:action]; + [invocation setTarget:target]; + [invocation invoke]; + NSUInteger result = 0; + [invocation getReturnValue:&result]; + return @(result); + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + return [target performSelector:action withObject:params]; +#pragma clang diagnostic pop +} + +#pragma mark - getters and setters + +- (NSObject *)safeFetchCachedTarget:(NSString *)key { + @synchronized (self) { + return self.cachedTarget[key]; + } +} + +- (void)safeSetCachedTarget:(NSObject *)target key:(NSString *)key { + @synchronized (self) { + self.cachedTarget[key] = target; + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.m.meta new file mode 100644 index 0000000..dc7d87d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/Router/TDMediator.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 66dbbf5977104487e80cdd049af4d62e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.h new file mode 100644 index 0000000..c3db0e0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.h @@ -0,0 +1,35 @@ +// +// TDApp.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/9/9. +// + +#import + +#if __has_include() +#import +#else +#import "TDSettings.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDApp : NSObject + +/// Initializes from a local file, named 'td_settings.json' ++ (void)start; + +/// Initializes with app id and server url +/// - Parameters: +/// - appId: The app id of your TE project +/// - serverUrl: The server url of your TE project ++ (void)startWithAppId:(NSString *)appId serverUrl:(NSString *)serverUrl; + +/// Initializes with SDK config +/// - Parameter settings: More specific profile ++ (void)startWithSetting:(TDSettings *)settings; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.h.meta new file mode 100644 index 0000000..409ca8e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 77e496978beff4f1282dc47e174c00a5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.m new file mode 100644 index 0000000..813801b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.m @@ -0,0 +1,81 @@ +// +// TDApp.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/9/9. +// + +#import "TDApp.h" +#import "TDJSONUtil.h" +#import "TDMediator+Analytics.h" +#import "TDMediator+RemoteConfig.h" +#import "TDMediator+Strategy.h" +#import "TDSettingsPrivate.h" +#import "TDCoreLog.h" +#import "NSString+TDCore.h" +#import "NSUrl+TDCore.h" + +@implementation TDApp + ++ (void)start { + NSString *path = [[NSBundle mainBundle] pathForResource:@"td_settings" ofType:@"json"]; + if ([[NSFileManager defaultManager] fileExistsAtPath:path]) { + NSData *data = [NSData dataWithContentsOfFile:path]; + NSArray *array = [TDJSONUtil jsonForData:data]; + if ([array isKindOfClass:NSArray.class]) { + for (NSDictionary *projectInfo in array) { + if ([projectInfo isKindOfClass:NSDictionary.class]) { + TDSettings *settings = [[TDSettings alloc] initWithDictionary:projectInfo]; + [self startWithSetting:settings]; + } + } + } else { + TDCORELOG(@"td_settings.json format is error"); + } + } else { + TDCORELOG(@"td_settings.json is not found"); + } +} + ++ (void)startWithAppId:(NSString *)appId serverUrl:(NSString *)serverUrl { + TDSettings *settings = [[TDSettings alloc] init]; + settings.appId = appId; + settings.serverUrl = serverUrl; + [TDApp startWithSetting:settings]; +} + ++ (void)startWithSetting:(TDSettings *)settings { + // TDCore 日志打印开关 + [TDCoreLog enableLog:settings.enableLog]; + + NSString *appId = nil; + if ([settings.appId isKindOfClass:NSString.class]) { + appId = [settings.appId td_trim]; + } + if (appId.length == 0) { + TDCORELOG(@"The app id is invalid"); + return; + } else { + settings.appId = appId; + } + + NSString *serverUrl = nil; + if ([settings.serverUrl isKindOfClass:NSString.class]) { + serverUrl = [NSURL td_baseUrlStringWithString:settings.serverUrl]; + } + if (serverUrl.length == 0) { + TDCORELOG(@"The server url is invalid"); + return; + } else { + settings.serverUrl = serverUrl; + } + + // 初始化采集SDK + [[TDMediator sharedInstance] tdAnalyticsInitWithSettings:settings]; + // 初始化RemoteConfig SDK + [[TDMediator sharedInstance] tdRemoteConfigInitWithSettings:settings]; + // 初始化策略SDK + [[TDMediator sharedInstance] tdStrategyInitWithSettings:settings]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.m.meta new file mode 100644 index 0000000..b5b374d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDApp.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 165f9a7e1c0184b33995a2c6777d9b2c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.h new file mode 100644 index 0000000..64f00b0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.h @@ -0,0 +1,18 @@ +// +// TDCoreInfo.h +// ThinkingDataCore +// +// Created by 杨雄 on 2023/9/15. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCoreInfo : NSObject + ++ (NSString *)version; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.h.meta new file mode 100644 index 0000000..9c55085 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ae92e38198a86472babcb1379dba240b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.m new file mode 100644 index 0000000..b6491a8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.m @@ -0,0 +1,16 @@ +// +// TDCoreInfo.m +// ThinkingDataCore +// +// Created by 杨雄 on 2023/9/15. +// + +#import "TDCoreInfo.h" + +@implementation TDCoreInfo + ++ (NSString *)version { + return @"1.2.2"; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.m.meta new file mode 100644 index 0000000..908e004 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDCoreInfo.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a2ab9250aec7f4b4988a2f3681f595b6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.h new file mode 100644 index 0000000..c3e087c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.h @@ -0,0 +1,17 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDJSONUtil : NSObject + ++ (NSString *)JSONStringForObject:(id)object; + ++ (NSData *)JSONSerializeForObject:(id)object; + ++ (nullable id)jsonForData:(nonnull NSData *)data; + ++ (nullable NSMutableDictionary *)formatDateWithFormatter:(nonnull NSDateFormatter *)dateFormatter dict:(NSDictionary *)dict; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.h.meta new file mode 100644 index 0000000..609a237 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: dcb73703e86044804b77901c0f38bfab +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.m new file mode 100644 index 0000000..6878602 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.m @@ -0,0 +1,170 @@ +#import "TDJSONUtil.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]) { + NSError *error = nil; + data = [NSJSONSerialization dataWithJSONObject:obj options:NSJSONWritingFragmentsAllowed error:&error]; + if (error != nil) { + return nil; + } + } + } + @catch (NSException *exception) { + + } + return data; +} + ++ (nullable id)jsonForData:(NSData *)data { + if (![data isKindOfClass:NSData.class]) { + return nil; + } + @try { + NSError *jsonSeralizeError = nil; + id json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonSeralizeError]; + if (jsonSeralizeError == nil) { + return json; + } + } @catch (NSException *exception) { + // + } + return nil; +} + ++ (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; + } +} + ++ (nullable NSMutableDictionary *)formatDateWithFormatter:(nonnull NSDateFormatter *)dateFormatter dict:(NSDictionary *)dict { + if (![dict isKindOfClass:NSDictionary.class] || ![dateFormatter isKindOfClass:NSDateFormatter.class]) { + return nil; + } + NSMutableDictionary *mutableDict = nil; + if ([dict isKindOfClass:NSMutableDictionary.class]) { + mutableDict = (NSMutableDictionary *)dict; + } else { + mutableDict = [dict mutableCopy]; + } + + NSArray *keys = mutableDict.allKeys; + for (NSString *key in keys) { + id value = dict[key]; + + // 处理字典 + if ([value isKindOfClass:[NSDictionary class]]) { + NSDictionary *newDict = [self formatDateWithFormatter:dateFormatter dict:value]; + mutableDict[key] = newDict; + } + // 处理数组 + else if ([value isKindOfClass:[NSArray class]]) { + NSMutableArray *newArray = [self formatArrayWithFormatter:dateFormatter array:value]; + mutableDict[key] = newArray; + } + // 处理集合 + else if ([value isKindOfClass:[NSSet class]]) { + NSSet *setData = value; + NSArray *newArray = [self formatArrayWithFormatter:dateFormatter array:setData.allObjects]; + mutableDict[key] = newArray; + } + // 处理日期 + else if ([value isKindOfClass:[NSDate class]]) { + NSString *newValue = [dateFormatter stringFromDate:(NSDate *)value]; + mutableDict[key] = newValue; + } + + } + return mutableDict; +} + + ++ (NSMutableArray *)formatArrayWithFormatter:(nonnull NSDateFormatter *)dateFormatter array:(NSArray *)array { + NSMutableArray *mutableArray = nil; + if ([array isKindOfClass:[NSMutableArray class]]) { + mutableArray = (NSMutableArray *)array; + } else { + mutableArray = [array mutableCopy]; + } + + for (int i = 0; i < mutableArray.count; i++) { + id value = mutableArray[i]; + if ([value isKindOfClass:[NSDate class]]) { + NSString *newValue = [dateFormatter stringFromDate:(NSDate *)value]; + mutableArray[i] = newValue; + } else if ([value isKindOfClass:[NSDictionary class]]) { + NSDictionary *newDict = [self formatDateWithFormatter:dateFormatter dict:value]; + mutableArray[i] = newDict; + } else if ([value isKindOfClass:[NSArray class]]) { + NSArray *newArray = [self formatArrayWithFormatter:dateFormatter array:value]; + mutableArray[i] = newArray; + } else if ([value isKindOfClass:[NSSet class]]) { + NSSet *setData = value; + NSArray *newArray = [self formatArrayWithFormatter:dateFormatter array:setData.allObjects]; + mutableArray[i] = newArray; + } + } + return mutableArray; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.m.meta new file mode 100644 index 0000000..a297102 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDJSONUtil.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 493815f68ee2c410bb4576ca8d351557 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime.meta new file mode 100644 index 0000000..a8a5919 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f7f69a4d3619d480992f8158ddbe7764 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle.meta new file mode 100644 index 0000000..e49339a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 75b00bb7e5ca04813934b3580a551b14 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.h new file mode 100644 index 0000000..679aeea --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.h.meta new file mode 100644 index 0000000..83936be --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3de7db2dbee2b4f1cb70bf1133c9cf26 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.m new file mode 100644 index 0000000..7f70f72 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.m.meta new file mode 100644 index 0000000..0d3ddb6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/NSObject+TDSwizzle.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 28d46c2bdd871409081f8f1e9b0693fc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.h new file mode 100644 index 0000000..48407ce --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.h.meta new file mode 100644 index 0000000..36419bf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 70accc43706e14e57840c87b5ca8b5c6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.m new file mode 100644 index 0000000..9616f17 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.m @@ -0,0 +1,240 @@ +#import "TDSwizzler.h" +#import +#import "TDCoreLog.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) { + TDCORELOG(@"Cannot find method for %@ on %@",NSStringFromSelector(aSelector), NSStringFromClass(aClass)); + return; + } + + uint numArgs = method_getNumberOfArguments(aMethod); + if (numArgs < MIN_ARGS || numArgs > MAX_ARGS) { + TDCORELOG(@"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) { + TDCORELOG(@"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) { + TDCORELOG(@"%@ 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))) { + TDCORELOG(@"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) { + TDCORELOG(@"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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.m.meta new file mode 100644 index 0000000..b5500d9 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/Swizzle/TDSwizzler.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9b483538036f74d7795d6d8b163e8914 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.h new file mode 100644 index 0000000..48b0055 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.h.meta new file mode 100644 index 0000000..3c0b3b8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 69c6a43721dd64eab8976fe5696ccb12 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.m new file mode 100644 index 0000000..4c7c01a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.m.meta new file mode 100644 index 0000000..2de1f82 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDClassHelper.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2c9862bca358a4cb498509a3cd3b7498 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.h new file mode 100644 index 0000000..bf26960 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.h @@ -0,0 +1,22 @@ +// +// TDCoreWeakProxy.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/24. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDCoreWeakProxy : NSProxy + +@property (nullable, nonatomic, weak, readonly) id target; + +- (instancetype)initWithTarget:(id)target; + ++ (instancetype)proxyWithTarget:(id)target; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.h.meta new file mode 100644 index 0000000..036f476 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 89e1d852d143d458da80377a78a7ab53 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.m new file mode 100644 index 0000000..b496f7f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.m @@ -0,0 +1,78 @@ +// +// TDCoreWeakProxy.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/24. +// + +#import "TDCoreWeakProxy.h" + +@implementation TDCoreWeakProxy + +- (instancetype)initWithTarget:(id)target { + _target = target; + return self; +} + ++ (instancetype)proxyWithTarget:(id)target { + return [[self 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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.m.meta new file mode 100644 index 0000000..f41ad79 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDCoreWeakProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a8977a7d9480a4dd189e1ae33999f673 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.h new file mode 100644 index 0000000..68a6211 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.h.meta new file mode 100644 index 0000000..a91abb3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 661c5f1df62ae4f9aab882fcfd683853 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.m new file mode 100644 index 0000000..bd5d213 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.m @@ -0,0 +1,58 @@ + +#import "TDMethodHelper.h" +#import +#import "TDNewSwizzle.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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.m.meta new file mode 100644 index 0000000..64d86b6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDMethodHelper.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8f09d284d6fbc4e0aaa8a4fb07b94d75 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.h new file mode 100644 index 0000000..cb3c0ff --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.h.meta new file mode 100644 index 0000000..b24e70f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3c22c4c0fc8b848db9696b6c6a1b7c97 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.m new file mode 100644 index 0000000..219b7d2 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.m @@ -0,0 +1,155 @@ + +#import "TDNewSwizzle.h" + +#if (TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_VISION || TARGET_OS_TV) + #import + #import +#elif TARGET_OS_OSX + #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/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.m.meta new file mode 100644 index 0000000..2039597 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDRuntime/TDNewSwizzle.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f8cf632a0323645d7a47f3c099651cb9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.h new file mode 100644 index 0000000..ad7a468 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.h @@ -0,0 +1,36 @@ +// +// TDSettings.h +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/21. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, TDSDKMode) { + TDSDKModeNomal = 0, + TDSDKModeDebug, + TDSDKModeDebugOnly, +}; + +@interface TDSettings : NSObject + +@property (nonatomic, copy) NSString *appId; +@property (nonatomic, copy) NSString *serverUrl; +@property (nonatomic, copy) NSString *instanceName; +@property (nonatomic, assign) TDSDKMode mode; +@property (nonatomic, assign) BOOL enableLog; +/// 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; +@property (nonatomic, assign) NSInteger encryptVersion; +@property (nonatomic, copy) NSString *encryptKey; +@property (nonatomic, assign) BOOL enableAutoPush; +@property (nonatomic, assign) BOOL enableAutoCalibrated; +@property (nonatomic, strong) NSDictionary *rccFetchParams; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.h.meta new file mode 100644 index 0000000..afd0643 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c4b7868bdd79b4ce7bbdb2d70e930110 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.m b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.m new file mode 100644 index 0000000..412469f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.m @@ -0,0 +1,36 @@ +// +// TDSettings.m +// ThinkingDataCore +// +// Created by 杨雄 on 2024/5/21. +// + +#import "TDSettings.h" +#import "NSObject+TDCore.h" + +@implementation TDSettings + +- (instancetype)initWithDictionary:(NSDictionary *)dict { + if (self = [self init]) { + self.appId = [dict[@"appId"] td_string]; + self.serverUrl = [dict[@"serverUrl"] td_string]; + NSNumber *timezoneOffset = [dict[@"defaultTimeZone"] td_number]; + if (timezoneOffset) { + self.defaultTimeZone = [NSTimeZone timeZoneForSecondsFromGMT:timezoneOffset.doubleValue * 3600]; + } + self.enableLog = [dict[@"enableLog"] td_number].boolValue; + self.enableAutoCalibrated = [dict[@"enableAutoCalibrated"] td_number].boolValue; + self.enableAutoPush = [dict[@"enableAutoPush"] td_number].boolValue; + self.encryptKey = [dict[@"encryptKey"] td_string]; + self.encryptVersion = [dict[@"encryptVersion"] td_number].integerValue; + self.mode = [dict[@"mode"] td_number].integerValue; + + NSDictionary *rccFetchParams = dict[@"rccFetchParams"]; + if ([rccFetchParams isKindOfClass:NSDictionary.class]) { + self.rccFetchParams = rccFetchParams; + } + } + return self; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.m.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.m.meta new file mode 100644 index 0000000..857715a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettings.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1dc60b2db11f84574ab73c3552e48bd2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettingsPrivate.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettingsPrivate.h new file mode 100644 index 0000000..b0528cf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettingsPrivate.h @@ -0,0 +1,19 @@ +// +// TDSettingsPrivate.h +// Pods +// +// Created by 杨雄 on 2024/6/20. +// + +#ifndef TDSettingsPrivate_h +#define TDSettingsPrivate_h + +#import + +@interface TDSettings() + +- (instancetype)initWithDictionary:(NSDictionary *)dict; + +@end + +#endif /* TDSettingsPrivate_h */ diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettingsPrivate.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettingsPrivate.h.meta new file mode 100644 index 0000000..2b6688b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/TDSettingsPrivate.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 43830f5bd7e454fec95773da747c55b9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/ThinkingDataCore.h b/Assets/Plugins/iOS/ThinkingDataCore/Classes/ThinkingDataCore.h new file mode 100644 index 0000000..4f0e3d7 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/ThinkingDataCore.h @@ -0,0 +1,190 @@ +// +// ThinkingDataCore.h +// ThinkingDataCore +// +// Created by Hale on 2023/7/24. +// + +#import + +// In this header, you should import all the public headers of your framework using statements like #import + +#if __has_include() +#import +#else +#import "TDCoreInfo.h" +#endif + +#if __has_include() +#import +#else +#import "TDJSONUtil.h" +#endif + +#if __has_include() +#import +#else +#import "NSData+TDGzip.h" +#endif + +#if __has_include() +#import +#else +#import "NSDate+TDCore.h" +#endif + +#if __has_include() +#import +#else +#import "TDNewSwizzle.h" +#endif + +#if __has_include() +#import +#else +#import "TDClassHelper.h" +#endif + +#if __has_include() +#import +#else +#import "TDMethodHelper.h" +#endif + +#if __has_include() +#import +#else +#import "NSObject+TDSwizzle.h" +#endif + +#if __has_include() +#import +#else +#import "TDSwizzler.h" +#endif + +#if __has_include() +#import +#else +#import "TDOSLog.h" +#endif + +#if __has_include() +#import +#else +#import "NSString+TDCore.h" +#endif + +#if __has_include() +#import +#else +#import "TDApp.h" +#endif + +#if __has_include() +#import +#else +#import "TDSettings.h" +#endif + +#if __has_include() +#import +#else +#import "TDLogChannelProtocol.h" +#endif + +#if __has_include() +#import +#else +#import "TDLogConstant.h" +#endif + +#if __has_include() +#import +#else +#import "TDNotificationManager+Core.h" +#endif + +#if __has_include() +#import +#else +#import "TDNotificationManager+Analytics.h" +#endif + +#if __has_include() +#import +#else +#import "TDNotificationManager+Networking.h" +#endif + +#if __has_include() +#import +#else +#import "TDNotificationManager+RemoteConfig.h" +#endif + +#if __has_include() +#import +#else +#import "TDCoreDatabase.h" +#endif + +#if __has_include() +#import +#else +#import "TDCalibratedTime.h" +#endif + +#if __has_include() +#import +#else +#import "TDCoreDeviceInfo.h" +#endif + +#if __has_include() +#import +#else +#import "NSObject+TDCore.h" +#endif + +#if __has_include() +#import +#else +#import "NSURL+TDCore.h" +#endif + +#if __has_include() +#import +#else +#import "NSNumber+TDCore.h" +#endif + +#if __has_include() +#import +#else +#import "TDMediator+RemoteConfig.h" +#endif + +#if __has_include() +#import +#else +#import "TDMediator+Analytics.h" +#endif + +#if __has_include() +#import +#else +#import "TDMediator+Strategy.h" +#endif + +#if __has_include() +#import +#else +#import "TDCorePresetProperty.h" +#endif + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif diff --git a/Assets/Plugins/iOS/ThinkingDataCore/Classes/ThinkingDataCore.h.meta b/Assets/Plugins/iOS/ThinkingDataCore/Classes/ThinkingDataCore.h.meta new file mode 100644 index 0000000..44d3df7 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingDataCore/Classes/ThinkingDataCore.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: aa73c5c79638f4d95b0dd8f8b4ee212e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK.meta b/Assets/Plugins/iOS/ThinkingSDK.meta new file mode 100644 index 0000000..20bd8ff --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4dd9e7986fe964179ac6733ba24e44fe +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Resources.meta b/Assets/Plugins/iOS/ThinkingSDK/Resources.meta new file mode 100644 index 0000000..d06ca2c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 43f5c01752f5b4a53809f5f68136b6e9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Resources/PrivacyInfo.xcprivacy b/Assets/Plugins/iOS/ThinkingSDK/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 0000000..28ebcf5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,121 @@ + + + + + NSPrivacyTracking + + NSPrivacyCollectedDataTypes + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeProductInteraction + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeCrashData + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypePerformanceData + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeCoarseLocation + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeUserID + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyCollectedDataType + NSPrivacyCollectedDataTypeDeviceID + NSPrivacyCollectedDataTypeLinked + + NSPrivacyCollectedDataTypeTracking + + NSPrivacyCollectedDataTypePurposes + + NSPrivacyCollectedDataTypePurposeAnalytics + + + + NSPrivacyTrackingDomains + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + 1C8F.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryDiskSpace + NSPrivacyAccessedAPITypeReasons + + E174.1 + 7D9E.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + + diff --git a/Assets/Plugins/iOS/ThinkingSDK/Resources/PrivacyInfo.xcprivacy.meta b/Assets/Plugins/iOS/ThinkingSDK/Resources/PrivacyInfo.xcprivacy.meta new file mode 100644 index 0000000..7533243 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Resources/PrivacyInfo.xcprivacy.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bf4c1d023317e4cde85c42a9f1f83ada +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source.meta b/Assets/Plugins/iOS/ThinkingSDK/Source.meta new file mode 100644 index 0000000..60de287 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b010dd865139b4ea8b8fe65c815bf3da +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch.meta new file mode 100644 index 0000000..d76daa7 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e3aa65556739d487581dcbc33f001e23 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush.meta new file mode 100644 index 0000000..38a9943 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0f4211ddaee63483da2cf08a3b9dbed6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h new file mode 100644 index 0000000..a46db5f --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h.meta new file mode 100644 index 0000000..e574b64 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d606670f7a7c24e1d86b03c1fc6251ac +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m new file mode 100644 index 0000000..98548ba --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m.meta new file mode 100644 index 0000000..0281b8c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAPPPushParams.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 08ddbde4b0bb44a2781691f28d329404 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h new file mode 100644 index 0000000..3960b15 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h.meta new file mode 100644 index 0000000..983e9d2 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0ad802e76f93c418c9419315b8a3d275 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m new file mode 100644 index 0000000..d1d6c0a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m @@ -0,0 +1,61 @@ + +#import "TDAppDelegateProxyManager.h" +#import "TDApplicationDelegateProxy.h" +#import "UIApplication+TDPushClick.h" +#import "TDLogging.h" + +#if __has_include() +#import +#else +#import "TDMethodHelper.h" +#endif + +#if __has_include() +#import +#else +#import "TDNewSwizzle.h" +#endif + +#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 resolveOptionalSelectorsForDelegate:[UNUserNotificationCenter currentNotificationCenter].delegate]; + [TDUNUserNotificationCenterDelegateProxy proxyDelegate:[UNUserNotificationCenter currentNotificationCenter].delegate selectors:[NSSet setWithArray:@[@"userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:",@"userNotificationCenter:willPresentNotification:withCompletionHandler:"]]]; + } + NSError *error = NULL; + [UNUserNotificationCenter td_new_swizzleMethod:@selector(setDelegate:) withMethod:@selector(thinkingdata_setDelegate:) error:&error]; + if (error) { + TDLogInfo(@"proxy notification delegate error: %@", error); + } + } + }); +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m.meta new file mode 100644 index 0000000..0366a4a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDAppDelegateProxyManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ed11ab70d6d6943019d6d3995eeaadc2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h new file mode 100644 index 0000000..1b1a54c --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h.meta new file mode 100644 index 0000000..b4fe0cb --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 47013b120f73c4885845dbb7dd840189 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m new file mode 100644 index 0000000..b3e68e8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m @@ -0,0 +1,131 @@ + +#import "TDApplicationDelegateProxy.h" +#import "NSObject+TDDelegateProxy.h" +#import "UIApplication+TDPushClick.h" +#import +#import "TDAppLaunchReason.h" +#import "TDCommonUtil.h" +#import "TDLogging.h" + +#if __has_include() +#import +#else +#import "TDClassHelper.h" +#endif + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + + +@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 API_DEPRECATED("Use UserNotifications Framework's -[UNUserNotificationCenterDelegate willPresentNotification:withCompletionHandler:] or -[UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:]", ios(4.0, 10.0)) { + 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 (![TDCorePresetDisableConfig 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 (![TDCorePresetDisableConfig 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 (![TDCorePresetDisableConfig 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 API_AVAILABLE(ios(9.0)){ + SEL selector = @selector(application:performActionForShortcutItem:completionHandler:); + [TDApplicationDelegateProxy invokeWithTarget:self selector:selector, application, shortcutItem, completionHandler]; + if (![TDCorePresetDisableConfig 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) { + TDLogInfo(@"iOS version >= 10.0, callback for %@ was ignored.", @"application:didReceiveRemoteNotification:fetchCompletionHandler:"); + return; + } + + if (application.applicationState != UIApplicationStateInactive) { + return; + } + + if (![TDCorePresetDisableConfig disableStartReason]) { + [[TDAppLaunchReason sharedInstance] clearAppLaunchParams]; + [TDAppLaunchReason sharedInstance].appLaunchParams = @{@"url": @"", @"data": [TDCommonUtil dictionary:userInfo]}; + } + +} + ++ (void)trackEventWithTarget:(NSObject *)target application:(UIApplication *)application localNotification:(UILocalNotification *)notification API_DEPRECATED("Use UserNotifications Framework's -[UNUserNotificationCenterDelegate willPresentNotification:withCompletionHandler:] or -[UNUserNotificationCenterDelegate didReceiveNotificationResponse:withCompletionHandler:]", ios(4.0, 10.0)){ + + if (target != application.delegate) { + return; + } + + BOOL isValidPushClick = NO; + if (application.applicationState == UIApplicationStateInactive) { + isValidPushClick = YES; + } + + if (!isValidPushClick) { + return; + } + + if (![TDCorePresetDisableConfig 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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m.meta new file mode 100644 index 0000000..9994901 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDApplicationDelegateProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b2eec12b437e64f6886ebe51d6680b83 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h new file mode 100644 index 0000000..94be8fc --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h.meta new file mode 100644 index 0000000..68c4018 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: dd9eda5b6572748ecaa3313c288c9e16 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m new file mode 100644 index 0000000..90143d0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m @@ -0,0 +1,78 @@ + +#import "TDUNUserNotificationCenterDelegateProxy.h" + +#if __has_include() +#import +#else +#import "TDClassHelper.h" +#endif + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +#import "NSObject+TDDelegateProxy.h" +#import +#import "TDAPPPushParams.h" +#import "TDAppLaunchReason.h" +#import "TDCommonUtil.h" +#import "ThinkingAnalyticsSDK.h" +#import "ThinkingAnalyticsSDKPrivate.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)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler API_AVAILABLE(ios(10.0)){ + SEL selector = @selector(userNotificationCenter:willPresentNotification:withCompletionHandler:); + [TDUNUserNotificationCenterDelegateProxy invokeWithTarget:self selector:selector, center, notification, completionHandler]; + [TDUNUserNotificationCenterDelegateProxy trackEventWithTarget:self notificationCenter:center notification:notification]; +} + ++ (void)trackEventWithTarget:(NSObject *)target notificationCenter:(UNUserNotificationCenter *)center notification:(UNNotification *)notification API_AVAILABLE(ios(10.0)){ + +} + ++ (void)trackEventWithTarget:(NSObject *)target notificationCenter:(UNUserNotificationCenter *)center notificationResponse:(UNNotificationResponse *)response API_AVAILABLE(ios(10.0)){ + + if (target != center.delegate) { + return; + } + + if (![TDCorePresetDisableConfig 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 ([ThinkingAnalyticsSDK defaultInstance].config.enableAutoPush) { + @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:", @"userNotificationCenter:willPresentNotification:withCompletionHandler:"]]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m.meta new file mode 100644 index 0000000..21f49a8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/TDUNUserNotificationCenterDelegateProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a8c262d70355c46f38a4fce70eb95cc5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h new file mode 100644 index 0000000..373abc9 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h.meta new file mode 100644 index 0000000..4f9c082 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b039553b9411c4e30b4fc767b7c2714d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m new file mode 100644 index 0000000..0e24334 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m.meta new file mode 100644 index 0000000..8df4fd6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UIApplication+TDPushClick.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 17463cb6f209545bcbc20aad5129804d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h new file mode 100644 index 0000000..3b0adc8 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h.meta new file mode 100644 index 0000000..85d1588 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: dd78c300cef5f4507a13b638ce74c1e5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m new file mode 100644 index 0000000..c69d6b3 --- /dev/null +++ b/Assets/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:", @"userNotificationCenter:willPresentNotification:withCompletionHandler:"]]]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m.meta new file mode 100644 index 0000000..b9fff43 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/AppPush/UNUserNotificationCenter+TDPushClick.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 73166a65f020c46e8a3c53cfa301c5f2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h new file mode 100644 index 0000000..e7b6240 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h.meta new file mode 100644 index 0000000..3658010 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f37b994396cdc4d7780bcb516fcc6d51 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m new file mode 100644 index 0000000..f833549 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m @@ -0,0 +1,181 @@ +// +// TDAppLaunchReason.m +// ThinkingSDK +// +// Created by wwango on 2021/11/17. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDAppLaunchReason.h" +#import +#import "TDCommonUtil.h" +#import "TDAppState.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDAppDelegateProxyManager.h" +#import "TDPushClickEvent.h" + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +@implementation TDAppLaunchReason + ++ (void)load { + [[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]; + TDPushClickEvent *pushEvent = [[TDPushClickEvent alloc]initWithName: @"te_ops_push_click"]; + pushEvent.ops = opsReceiptProperties; + [instance autoTrackWithEvent:pushEvent properties:@{}]; + [instance innerFlush]; + } + } + } + } + } @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 ([ThinkingAnalyticsSDK defaultInstance].config.enableAutoPush && launchOptions) { + NSDictionary *remoteNotification = [launchOptions objectForKey:@"UIApplicationLaunchOptionsRemoteNotificationKey"]; + [TDAppLaunchReason td_ops_push_click:remoteNotification]; + } + + // 记录冷启动启动原因 + if (![TDCorePresetDisableConfig 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 (![TDCorePresetDisableConfig disableStartReason]) { + [[TDAppDelegateProxyManager defaultManager] proxyNotifications]; + } + + if (![TDCorePresetDisableConfig 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 @{}; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + 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; + } +#pragma clang diagnostic pop + + return launchOptions; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m.meta new file mode 100644 index 0000000..449f7f5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLaunch/TDAppLaunchReason.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2153a11937bd248f793246e3f848aaab +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle.meta new file mode 100644 index 0000000..22b708e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 20131090d388c4844a66a22ba19bcf58 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.h new file mode 100644 index 0000000..31cc68b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.h @@ -0,0 +1,47 @@ +// +// TDAppLifeCycle.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/28. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// APP life cycle +typedef NS_ENUM(NSUInteger, TDAppLifeCycleState) { + TDAppLifeCycleStateInit = 1, // init status + TDAppLifeCycleStateBackgroundStart, + TDAppLifeCycleStateStart, + TDAppLifeCycleStateEnd, + TDAppLifeCycleStateTerminate, +}; + +/// 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 kTDAppLifeCycleNewStateKey and kTDAppLifeCycleOldStateKey +extern NSNotificationName const kTDAppLifeCycleStateWillChangeNotification; + +/// When the life cycle status changes, this notification will be sent +/// object: The object is the current lifecycle object +/// userInfo: Contains two keys kTDAppLifeCycleNewStateKey and kTDAppLifeCycleOldStateKey +extern NSNotificationName const kTDAppLifeCycleStateDidChangeNotification; + +/// In the status change notification, get the new status +extern NSString * const kTDAppLifeCycleNewStateKey; + +/// In the status change notification, get the status before the change +extern NSString * const kTDAppLifeCycleOldStateKey; + +@interface TDAppLifeCycle : NSObject + +@property (nonatomic, assign, readonly) TDAppLifeCycleState state; + ++ (void)startMonitor; + ++ (instancetype)shareInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.h.meta new file mode 100644 index 0000000..ffd5aba --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2e9c1fbbacf0f42f2872451f2d22002d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.m new file mode 100644 index 0000000..26ea67b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.m @@ -0,0 +1,248 @@ +// +// TDAppLifeCycle.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/28. +// + +#import "TDAppLifeCycle.h" +#import "TDAppState.h" + +#if TARGET_OS_IOS +#import +#elif TARGET_OS_OSX +#import +#endif + +#if __has_include() +#import +#else +#import "TDLogging.h" +#endif + +NSNotificationName const kTDAppLifeCycleStateWillChangeNotification = @"cn.thinkingdata.TDAppLifeCycleStateWillChange"; +NSNotificationName const kTDAppLifeCycleStateDidChangeNotification = @"cn.thinkingdata.TDAppLifeCycleStateDidChange"; +NSString * const kTDAppLifeCycleNewStateKey = @"new"; +NSString * const kTDAppLifeCycleOldStateKey = @"old"; + + +@interface TDAppLifeCycle () +/// status +@property (nonatomic, assign) TDAppLifeCycleState state; + +@end + +@implementation TDAppLifeCycle + ++ (void)startMonitor { + [TDAppLifeCycle shareInstance]; +} + ++ (instancetype)shareInstance { + static dispatch_once_t onceToken; + static TDAppLifeCycle *appLifeCycle = nil; + dispatch_once(&onceToken, ^{ + appLifeCycle = [[TDAppLifeCycle alloc] init]; + }); + return appLifeCycle; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _state = TDAppLifeCycleStateInit; + [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 ? TDAppLifeCycleStateBackgroundStart : TDAppLifeCycleStateStart; + }; + + 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 ? TDAppLifeCycleStateBackgroundStart : TDAppLifeCycleStateStart; +} + +- (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 = TDAppLifeCycleStateStart; +} + +#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 = TDAppLifeCycleStateEnd; +} + +#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 = TDAppLifeCycleStateEnd; +} +#endif + +- (void)applicationWillTerminate:(NSNotification *)notification { + TDLogDebug(@"application will terminate"); + + self.state = TDAppLifeCycleStateTerminate; +} + +//MARK: - Setter + +- (void)setState:(TDAppLifeCycleState)state { + + if (_state == state) { + return; + } + + [TDAppState shareInstance].isActive = (state == TDAppLifeCycleStateStart); + + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:2]; + userInfo[kTDAppLifeCycleNewStateKey] = @(state); + userInfo[kTDAppLifeCycleOldStateKey] = @(_state); + + [[NSNotificationCenter defaultCenter] postNotificationName:kTDAppLifeCycleStateWillChangeNotification object:self userInfo:userInfo]; + + _state = state; + + [[NSNotificationCenter defaultCenter] postNotificationName:kTDAppLifeCycleStateDidChangeNotification object:self userInfo:userInfo]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.m.meta new file mode 100644 index 0000000..964549e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppLifeCycle.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c2b0cf29c0ac24442b70af08fba39f7f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h new file mode 100644 index 0000000..c1053ac --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h.meta new file mode 100644 index 0000000..501b9ec --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bfe06ac7bb20d44f4b38427283e52402 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m new file mode 100644 index 0000000..bd613c3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m @@ -0,0 +1,46 @@ +// +// 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 + +@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/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m.meta new file mode 100644 index 0000000..eb2d9d0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AppLifeCycle/TDAppState.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 439283b9eaf2e4d26883e97a319af143 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack.meta new file mode 100644 index 0000000..db0bd64 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d1b5729d7c0b24bddbf39df25636523b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin.meta new file mode 100644 index 0000000..7e08465 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d28aab82ad3e74dbfba611830073cc1f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.h new file mode 100644 index 0000000..ca0756e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.h @@ -0,0 +1,18 @@ +// +// TDAutoPushPlugin.h +// Pods +// +// Created by 廖德生 on 2024/08/26. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAutoPushPlugin : NSObject + ++ (void)enableLog:(BOOL)enable; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.h.meta new file mode 100644 index 0000000..b471411 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: da9a0ad6e1e7e4ac5a30de33a53f43a0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.m new file mode 100644 index 0000000..ce762b0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.m @@ -0,0 +1,189 @@ +// +// TDAutoPushPlugin.m +// TDAutoPushPlugin.m +// Pods +// +// Created by 廖德生 on 2024/08/26. +// + +#import "TDAutoPushPlugin.h" +#import +#import + +#if __has_include() +#import +#else +#import "ThinkingDataCore.h" +#endif + +static BOOL _logOn = YES; +static NSString * _fcm_token = nil; +static NSString * _apns_token = nil; +static NSString * _jpush_token = nil; +static NSString * const TD_FCM_TOKEN = @"fcm_token"; +static NSString * const TD_APNS_TOKEN = @"apns_token"; +static NSString * const TD_JPUSH_TOKEN = @"jiguang_id"; +static NSMutableSet * _pushAppIds; + +@interface TDAutoPushPlugin () + +@end + +@implementation TDAutoPushPlugin + ++ (void)load { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [TDAutoPushPlugin monitorFIRMessagingToken]; + [TDAutoPushPlugin monitorJPUSHServiceToken]; + [TDAutoPushPlugin registerAnalyticsListener]; + [TDAutoPushPlugin registerAppLifeCycleListener]; + _pushAppIds = [NSMutableSet set]; + }); +} + ++ (void)enableLog:(BOOL)enable { + _logOn = enable; +} + +void (*td_fcm_imp_original)(id, SEL, id); +void td_fcm_imp_final(id self, SEL _cmd, id token) { + [TDAutoPushPlugin printLog:@"FCM token: %@", token]; + _fcm_token = token; + for (NSString *appId in _pushAppIds) { + [TDAutoPushPlugin tdUserSetValue:token forKey:TD_FCM_TOKEN appid:appId]; + } + if(td_fcm_imp_original) { + td_fcm_imp_original(self, _cmd, token); + } +} + ++ (void)monitorFIRMessagingToken { + Class desClass_FIRMessaging = objc_getClass("FIRMessaging"); + bool ret = NO; + ret = class_addMethod(desClass_FIRMessaging, NSSelectorFromString(@"td_updateDefaultFCMToken:"), (IMP)(td_fcm_imp_final), "v@:@"); + if (ret) { + Method method_original = class_getInstanceMethod(desClass_FIRMessaging, NSSelectorFromString(@"updateDefaultFCMToken:")); + Method method_final = class_getInstanceMethod(desClass_FIRMessaging, NSSelectorFromString(@"td_updateDefaultFCMToken:")); + td_fcm_imp_original = (void(*)(id, SEL, id))method_getImplementation(method_original); + method_exchangeImplementations(method_original, method_final); + } +} + ++ (void)monitorJPUSHServiceToken { + Class desClass = NSClassFromString(@"JPUSHService"); + void(^_handler)(int resCode, NSString *registrationID) = ^(int resCode, NSString *registrationID) { + [TDAutoPushPlugin printLog:@"JPush registrationID: %@, resCode: %d", registrationID, resCode]; + _jpush_token = registrationID; + for (NSString *appId in _pushAppIds) { + [TDAutoPushPlugin tdUserSetValue:registrationID forKey:TD_JPUSH_TOKEN appid:appId]; + } + }; + SEL _sel = NSSelectorFromString(@"registrationIDCompletionHandler:"); + if ([desClass respondsToSelector:_sel]) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [desClass performSelector:_sel withObject:_handler]; +#pragma clang diagnostic pop + } +} + ++ (void)tdUserSetValue:(NSString *)value forKey:(NSString *)key appid:(NSString *)appId { + if (value != nil && value.length > 0) { + if ([[TDMediator sharedInstance] tdAnalyticsGetEnableAutoPushWithAppId: appId]) { + [[TDMediator sharedInstance] tdAnalyticsUserSetProperties:@{key: value} appId:appId]; + } + } +} + ++ (void)registerAnalyticsListener { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pushTokenNotification:) name:kAnalyticsNotificationNameInit object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pushTokenNotification:) name:kAnalyticsNotificationNameLogin object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pushTokenNotification:) name:kAnalyticsNotificationNameSetDistinctId object:nil]; +} + ++ (void)pushTokenNotification:(NSNotification *)notification { + NSDictionary *userInfo = notification.userInfo; + NSString *appId = userInfo[kAnalyticsNotificationParamsAppId]; + if (_fcm_token.length) { + [TDAutoPushPlugin tdUserSetValue:_fcm_token forKey:TD_FCM_TOKEN appid:appId]; + } else { + [_pushAppIds addObject:appId]; + } + + if (_jpush_token.length) { + [TDAutoPushPlugin tdUserSetValue:_jpush_token forKey:TD_JPUSH_TOKEN appid:appId]; + } else { + [_pushAppIds addObject:appId]; + } + + if (_apns_token.length) { + [TDAutoPushPlugin tdUserSetValue:_apns_token forKey:TD_APNS_TOKEN appid:appId]; + } else { + [_pushAppIds addObject:appId]; + } +} + ++ (void)registerAppLifeCycleListener { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(td_applicationDidFinishLaunching:) + name:UIApplicationDidFinishLaunchingNotification + object:nil]; +} + +static void (*original_didRegisterForRemoteNotificationsWithDeviceToken)(id, SEL, UIApplication *, NSData *); ++ (void)td_applicationDidFinishLaunching:(NSNotification *)notification { + Class delegateClass = [UIApplication sharedApplication].delegate.class; + + SEL originalSelector = @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:); + SEL swizzledSelector = @selector(td_application:didRegisterForRemoteNotificationsWithDeviceToken:); + + Method originalMethod = class_getInstanceMethod(delegateClass, originalSelector); + // 保存原有方法的实现 + original_didRegisterForRemoteNotificationsWithDeviceToken = (void (*)(id, SEL, UIApplication *, NSData *))method_getImplementation(originalMethod); + + Method swizzledMethod = class_getClassMethod([self class], swizzledSelector); + method_exchangeImplementations(originalMethod, swizzledMethod); +} + ++ (void)td_application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + NSString *tokenStr = [TDAutoPushPlugin formatDeviceTokenToHexStr:deviceToken]; + [TDAutoPushPlugin printLog:@"apns token: %@", tokenStr]; + _apns_token = tokenStr; + for (NSString *appId in _pushAppIds) { + [TDAutoPushPlugin tdUserSetValue:tokenStr forKey:TD_APNS_TOKEN appid:appId]; + } + original_didRegisterForRemoteNotificationsWithDeviceToken(self, _cmd, application, deviceToken); +} + ++ (NSString *)formatDeviceTokenToHexStr:(NSData *)deviceToken { + NSString *tokenStr; + if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 13.0) { + const unsigned *tokenBytes = [deviceToken bytes]; + tokenStr = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x", + ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]), + ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]), + ntohl(tokenBytes[6]), ntohl(tokenBytes[7])]; + } else { + tokenStr = [[deviceToken description] stringByReplacingOccurrencesOfString:@"<" withString:@""]; + tokenStr = [tokenStr stringByReplacingOccurrencesOfString:@">" withString:@""]; + tokenStr = [tokenStr stringByReplacingOccurrencesOfString:@" " withString:@""]; + } + return tokenStr; +} + ++ (void)printLog:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2) { + if (_logOn == YES) { + if (format) { + va_list args; + va_start(args, format); + NSString *output = [[NSString alloc] initWithFormat:format arguments:args]; + va_end(args); + + NSString *prefix = @"TDAutoPushPlugin"; + [TDOSLog logMessage:output prefix:prefix type:TDLogTypeInfo asynchronous:YES]; + } + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.m.meta new file mode 100644 index 0000000..112be99 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/AutoPushPlugin/TDAutoPushPlugin.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7d25bd808566149d399161cc855bd5dc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event.meta new file mode 100644 index 0000000..66b5b3d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 60e81b49c2ab941b48f1746f0b7f409d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.h new file mode 100644 index 0000000..36c9718 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.h @@ -0,0 +1,17 @@ +// +// TDAppEndEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/17. +// + +#import "TDAutoTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAppEndEvent : TDAutoTrackEvent +@property (nonatomic, copy) NSString *screenName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.h.meta new file mode 100644 index 0000000..628c470 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 81d81a9a5ad354dcd957258ac3ed0fba +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.m new file mode 100644 index 0000000..3f8f6d5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.m @@ -0,0 +1,28 @@ +// +// TDAppEndEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/17. +// + +#import "TDAppEndEvent.h" + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +@implementation TDAppEndEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + if (![TDCorePresetDisableConfig disableScreenName]) { + self.properties[@"#screen_name"] = self.screenName ?: @""; + } + + return dict; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.m.meta new file mode 100644 index 0000000..d713015 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppEndEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3ad91764cd4c84b3cb4d504bd508b9e4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.h new file mode 100644 index 0000000..2ba3263 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.h @@ -0,0 +1,19 @@ +// +// TDAppStartEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/17. +// + +#import "TDAutoTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAppStartEvent : TDAutoTrackEvent +@property (nonatomic, copy) NSString *startReason; +@property (nonatomic, assign) BOOL resumeFromBackground; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.h.meta new file mode 100644 index 0000000..48c32c8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ed6c573fbb49b4b6ead0f48b1cd38739 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.m new file mode 100644 index 0000000..563626e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.m @@ -0,0 +1,35 @@ +// +// TDAppStartEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/17. +// + +#import "TDAppStartEvent.h" +#import + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +static NSString * const TD_RESUME_FROM_BACKGROUND = @"#resume_from_background"; +static NSString * const TD_START_REASON = @"#start_reason"; + +@implementation TDAppStartEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + if (![TDCorePresetDisableConfig disableResumeFromBackground]) { + self.properties[TD_RESUME_FROM_BACKGROUND] = @(self.resumeFromBackground); + } + if (![TDCorePresetDisableConfig disableStartReason]) { + self.properties[TD_START_REASON] = self.startReason; + } + + return dict; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.m.meta new file mode 100644 index 0000000..793b960 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAppStartEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 938284553f4844198b92206428da5dac +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.h new file mode 100644 index 0000000..6d65e6e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.h @@ -0,0 +1,22 @@ +// +// TDAutoClickEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TDAutoTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAutoClickEvent : TDAutoTrackEvent +@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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.h.meta new file mode 100644 index 0000000..63f3128 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0993f6afd7ac045cf937be6f152bca5f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.m new file mode 100644 index 0000000..f99c97b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.m @@ -0,0 +1,43 @@ +// +// TDAutoClickEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TDAutoClickEvent.h" + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +@implementation TDAutoClickEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + if (![TDCorePresetDisableConfig disableScreenName]) { + self.properties[@"#screen_name"] = self.screenName; + } + if (![TDCorePresetDisableConfig disableElementId]) { + self.properties[@"#element_id"] = self.elementId; + } + if (![TDCorePresetDisableConfig disableElementType]) { + self.properties[@"#element_type"] = self.elementType; + } + if (![TDCorePresetDisableConfig disableElementContent]) { + self.properties[@"#element_content"] = self.elementContent; + } + if (![TDCorePresetDisableConfig disableElementPosition]) { + self.properties[@"#element_position"] = self.elementPosition; + } + if (![TDCorePresetDisableConfig disableTitle]) { + self.properties[@"#title"] = self.pageTitle; + } + + return dict; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.m.meta new file mode 100644 index 0000000..7b7c59f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoClickEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 63f08b37fb6e244eebb84b7c018a1461 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.h new file mode 100644 index 0000000..971ab96 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.h @@ -0,0 +1,20 @@ +// +// TDAutoPageViewEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TDAutoTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAutoPageViewEvent : TDAutoTrackEvent +@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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.h.meta new file mode 100644 index 0000000..629c22f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a8c18e34dc9cb402eab1b55cb07a9280 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.m new file mode 100644 index 0000000..afcc061 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.m @@ -0,0 +1,36 @@ +// +// TDAutoPageViewEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TDAutoPageViewEvent.h" + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +@implementation TDAutoPageViewEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + if (![TDCorePresetDisableConfig disableScreenName]) { + self.properties[@"#screen_name"] = self.screenName; + } + if (![TDCorePresetDisableConfig disableTitle]) { + self.properties[@"#title"] = self.pageTitle; + } + if (![TDCorePresetDisableConfig disableUrl]) { + self.properties[@"#url"] = self.pageUrl; + } + if (![TDCorePresetDisableConfig disableReferrer]) { + self.properties[@"#referrer"] = self.referrer; + } + return dict; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.m.meta new file mode 100644 index 0000000..8583fb6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoPageViewEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d6f6c0e19e8cf4bde97b9f6eec13ff87 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.h new file mode 100644 index 0000000..50b984e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.h @@ -0,0 +1,27 @@ +// +// TDAutoTrackEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TDTrackEvent.h" +#import "TDConstant.h" +#import "TDAutoTrackConst.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAutoTrackEvent : TDTrackEvent + +/// 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 +- (TDAutoTrackEventType)autoTrackEventType; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.h.meta new file mode 100644 index 0000000..47669fd --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4fe43ec71d6ef4d8390b31b12db67986 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.m new file mode 100644 index 0000000..9336e0c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.m @@ -0,0 +1,63 @@ +// +// TDAutoTrackEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/15. +// + +#import "TDAutoTrackEvent.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +@implementation TDAutoTrackEvent + +- (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 (![TDCorePresetDisableConfig disableDuration]) { + if (self.foregroundDuration > minDuration) { + self.properties[@"#duration"] = @([NSString stringWithFormat:@"%.3f", self.foregroundDuration].floatValue); + } + } + if (![TDCorePresetDisableConfig disableBackgroundDuration]) { + if (self.backgroundDuration > minDuration) { + self.properties[TD_BACKGROUND_DURATION] = @([NSString stringWithFormat:@"%.3f", self.backgroundDuration].floatValue); + } + } + + return dict; +} + +- (TDAutoTrackEventType)autoTrackEventType { + if ([self.eventName isEqualToString:TD_APP_START_EVENT]) { + return TDAutoTrackEventTypeAppStart; + } else if ([self.eventName isEqualToString:TD_APP_START_BACKGROUND_EVENT]) { + return TDAutoTrackEventTypeAppStart; + } else if ([self.eventName isEqualToString:TD_APP_END_EVENT]) { + return TDAutoTrackEventTypeAppEnd; + } else if ([self.eventName isEqualToString:TD_APP_VIEW_EVENT]) { + return TDAutoTrackEventTypeAppViewScreen; + } else if ([self.eventName isEqualToString:TD_APP_CLICK_EVENT]) { + return TDAutoTrackEventTypeAppClick; + } else if ([self.eventName isEqualToString:TD_APP_CRASH_EVENT]) { + return TDAutoTrackEventTypeAppViewCrash; + } else if ([self.eventName isEqualToString:TD_APP_INSTALL_EVENT]) { + return TDAutoTrackEventTypeAppInstall; + } else { + return TDAutoTrackEventTypeNone; + } +} + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + [TDPropertyValidator validateAutoTrackEventPropertyKey:key value:value error:error]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.m.meta new file mode 100644 index 0000000..c4ab0f9 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDAutoTrackEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 45745a7908a4e401494b13bf3dd00329 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.h new file mode 100644 index 0000000..f3def1a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.h @@ -0,0 +1,16 @@ +// +// TAPushClickEvent.h +// ThinkingSDK +// +// Created by liulongbing on 2023/5/31. +// +#import "TDAutoTrackEvent.h" +NS_ASSUME_NONNULL_BEGIN + +@interface TDPushClickEvent : TDAutoTrackEvent + +@property (nonatomic, strong) NSDictionary *ops; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.h.meta new file mode 100644 index 0000000..8385545 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 93b8a7c7ab3054230aa69278b0e877c7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.m new file mode 100644 index 0000000..56d7546 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.m @@ -0,0 +1,18 @@ +// +// TAPushClickEvent.m +// ThinkingSDK +// +// Created by liulongbing on 2023/5/31. +// + +#import "TDPushClickEvent.h" + +@implementation TDPushClickEvent + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + self.properties[@"#ops_receipt_properties"] = self.ops; + return dict; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.m.meta new file mode 100644 index 0000000..f905bf4 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Event/TDPushClickEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9162f41571f16414fba1baab7b4d1929 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackConst.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackConst.h new file mode 100644 index 0000000..f1ccf59 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackConst.h @@ -0,0 +1,32 @@ +// +// TDAutoTrackConst.h +// Pods +// +// Created by 杨雄 on 2023/7/23. +// + +#ifndef TDAutoTrackConst_h +#define TDAutoTrackConst_h + +#import + +typedef NS_OPTIONS(NSInteger, TDAutoTrackEventType) { + TDAutoTrackEventTypeNone = 0, + TDAutoTrackEventTypeAppStart = 1 << 0, + TDAutoTrackEventTypeAppEnd = 1 << 1, + TDAutoTrackEventTypeAppClick = 1 << 2, + TDAutoTrackEventTypeAppViewScreen = 1 << 3, + TDAutoTrackEventTypeAppViewCrash = 1 << 4, + TDAutoTrackEventTypeAppInstall = 1 << 5, + TDAutoTrackEventTypeAll = TDAutoTrackEventTypeAppStart | TDAutoTrackEventTypeAppEnd | TDAutoTrackEventTypeAppClick | TDAutoTrackEventTypeAppInstall | TDAutoTrackEventTypeAppViewCrash | TDAutoTrackEventTypeAppViewScreen +}; + +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"; + +#endif /* TDAutoTrackConst_h */ diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackConst.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackConst.h.meta new file mode 100644 index 0000000..9768fc2 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackConst.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bb155137bd66d45fdb2e1e5762541c79 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h new file mode 100644 index 0000000..4700d23 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h @@ -0,0 +1,37 @@ +#import "ThinkingAnalyticsSDK.h" +#import "TDColdStartTracker.h" +#import "TDHotStartTracker.h" +#import "TDInstallTracker.h" +#import "TDAutoTrackConst.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:(TDAutoTrackEventType)type; + +- (void)viewControlWillAppear:(UIViewController *)controller; + +- (void)trackWithEvent:(TDAutoTrackEvent *)event withProperties:(NSDictionary *)properties; + ++ (UIViewController *)topPresentedViewController; + +#pragma mark - UNAVAILABLE +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +@end + diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h.meta new file mode 100644 index 0000000..7d42a49 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1625f60723f8542a69466be0231920f5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m new file mode 100644 index 0000000..29edc8a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m @@ -0,0 +1,794 @@ +#import "TDAutoTrackManager.h" + +#import "UIViewController+AutoTrack.h" +#import "UIApplication+AutoTrack.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDPublicConfig.h" +#import "TDAutoClickEvent.h" +#import "TDAutoPageViewEvent.h" +#import "TDAppLifeCycle.h" +#import "TDAppState.h" +#import "TDRunTime.h" +#import "TDAppStartEvent.h" +#import "TDAppEndEvent.h" +#import "TDAppEndTracker.h" +#import "TDColdStartTracker.h" +#import "TDInstallTracker.h" +#import "TDAppState.h" + +#if __has_include() +#import +#else +#import "TDJSONUtil.h" +#endif + +#if __has_include() +#import +#else +#import "NSObject+TDSwizzle.h" +#endif + +#if __has_include() +#import +#else +#import "TDSwizzler.h" +#endif + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +#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 *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]; + + [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]; + + [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]; + + [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) { + + TDAutoTrackEventType type = (TDAutoTrackEventType)[self.autoTrackOptions[appid] integerValue]; + + if (type & ThinkingAnalyticsEventTypeAppClick) { + + + + + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK instanceWithAppid:appid]; + NSMutableDictionary *trackProperties = [properties mutableCopy]; + + NSMutableDictionary *finalProperties = [customProperties mutableCopy]; + + if ([instance innerIsViewTypeIgnored:[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]; + + [finalProperties addEntriesFromDictionary:properties]; + } + } + + if (propertyWithAppid) { + NSDictionary *autoTrackproperties = [propertyWithAppid objectForKey:appid]; + if ([autoTrackproperties isKindOfClass:[NSDictionary class]]) { + [trackProperties addEntriesFromDictionary:autoTrackproperties]; + + [finalProperties addEntriesFromDictionary:autoTrackproperties]; + } + } + + TDAutoClickEvent *clickEvent = [[TDAutoClickEvent alloc] initWithName:TD_APP_CLICK_EVENT]; + clickEvent.time = trackDate; + clickEvent.elementId = elementId; + clickEvent.elementType = elementType; + clickEvent.elementContent = elementContent; + clickEvent.elementPosition = elementPosition; + clickEvent.pageTitle = elementPageTitle; + clickEvent.screenName = elementScreenName; + + [instance autoTrackWithEvent:clickEvent properties:finalProperties]; + } + } +} + +- (void)trackWithAppid:(NSString *)appid withOption:(TDAutoTrackEventType)type { + TD_LOCK(self.trackOptionLock); + self.autoTrackOptions[appid] = @(type); + TD_UNLOCK(self.trackOptionLock); + + if (type & ThinkingAnalyticsEventTypeAppClick || type & ThinkingAnalyticsEventTypeAppViewScreen) { + [self swizzleVC]; + } + + if (type & ThinkingAnalyticsEventTypeAppInstall) { + TDAutoTrackEvent *event = [[TDAutoTrackEvent 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 instanceWithAppid:appid]; + [instance innerTimeEvent: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; + TDAppStartEvent *event = [[TDAppStartEvent alloc] initWithName:eventName]; + event.resumeFromBackground = NO; + if (![TDCorePresetDisableConfig 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) { + [ThinkingExceptionHandler start]; + } + + TDLogInfo(@"enable auto track: %li", type); +} + +- (void)trackWithEvent:(TDAutoTrackEvent *)event withProperties:(NSDictionary *)properties { + for (NSString *appid in self.autoTrackOptions.allKeys) { + TDAutoTrackEventType type = (TDAutoTrackEventType)[self.autoTrackOptions[appid] integerValue]; + if (type & event.autoTrackEventType) { + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK instanceWithAppid:appid]; + [instance autoTrackWithEvent:event properties:properties]; + } + } +} + +- (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:(TDAutoTrackEventType)eventType { + BOOL isIgnored = YES; + for (NSString *appid in self.autoTrackOptions) { + TDAutoTrackEventType type = (TDAutoTrackEventType)[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 *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]; + + [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) { + TDAutoTrackEventType type = [self.autoTrackOptions[appid] integerValue]; + if (type & ThinkingAnalyticsEventTypeAppViewScreen) { + + + + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK instanceWithAppid:appid]; + NSMutableDictionary *trackProperties = [properties mutableCopy]; + + NSMutableDictionary *finalProperties = [customProperties mutableCopy]; + + if ([instance isViewControllerIgnored:controller] + || [instance innerIsViewTypeIgnored:[controller class]]) { + continue; + } + + if (autoTrackerAppidDic && [autoTrackerAppidDic objectForKey:appid]) { + NSDictionary *dic = [autoTrackerAppidDic objectForKey:appid]; + if ([dic isKindOfClass:[NSDictionary class]]) { + [trackProperties addEntriesFromDictionary:dic]; + + [finalProperties addEntriesFromDictionary:dic]; + } + } + + if (screenAutoTrackerAppidDic && [screenAutoTrackerAppidDic objectForKey:appid]) { + NSString *screenUrl = [screenAutoTrackerAppidDic objectForKey:appid]; + [trackProperties setValue:screenUrl forKey:TD_EVENT_PROPERTY_URL_PROPERTY]; + + pageUrl = screenUrl; + } + + TDAutoPageViewEvent *pageEvent = [[TDAutoPageViewEvent 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:finalProperties]; + } + } +} + +- (BOOL)shouldTrackViewContrller:(Class)aClass { + return ![TDPublicConfig.controllers containsObject:NSStringFromClass(aClass)]; +} + +- (TDAutoTrackEventType)autoTrackOptionForAppid:(NSString *)appid { + return (TDAutoTrackEventType)[[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:kTDAppLifeCycleStateWillChangeNotification object:nil]; +} + +- (void)appStateWillChangeNotification:(NSNotification *)notification { + TDAppLifeCycleState newState = [[notification.userInfo objectForKey:kTDAppLifeCycleNewStateKey] integerValue]; + TDAppLifeCycleState oldState = [[notification.userInfo objectForKey:kTDAppLifeCycleOldStateKey] integerValue]; + + if (newState == TDAppLifeCycleStateStart) { + for (NSString *appid in self.autoTrackOptions.allKeys) { + TDAutoTrackEventType type = (TDAutoTrackEventType)[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 != TDAppLifeCycleStateInit) { + NSString *eventName = [TDAppState shareInstance].relaunchInBackground ? TD_APP_START_BACKGROUND_EVENT : TD_APP_START_EVENT; + TDAppStartEvent *event = [[TDAppStartEvent alloc] initWithName:eventName]; + event.resumeFromBackground = YES; + + if (![TDCorePresetDisableConfig 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 instanceWithAppid:appid]; + [instance innerTimeEvent:TD_APP_END_EVENT]; + } + } + } else if (newState == TDAppLifeCycleStateEnd) { + for (NSString *appid in self.autoTrackOptions) { + TDAutoTrackEventType type = (TDAutoTrackEventType)[self.autoTrackOptions[appid] integerValue]; + if (type & ThinkingAnalyticsEventTypeAppEnd) { + TDAppEndEvent *event = [[TDAppEndEvent 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 instanceWithAppid:appid]; + [instance innerTimeEvent: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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m.meta new file mode 100644 index 0000000..c0a1b5d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4df3c68e4c10242c4a93e8087f0cc5bd +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackProtocol.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackProtocol.h new file mode 100644 index 0000000..5ba79a5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackProtocol.h @@ -0,0 +1,80 @@ +// +// TDAutoTrackProtocol.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#ifndef TDAutoTrackProtocol_h +#define TDAutoTrackProtocol_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 /* TDAutoTrackProtocol_h */ diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackProtocol.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackProtocol.h.meta new file mode 100644 index 0000000..d43c453 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2a15bdeebde374a98944129726bc5717 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackPublicHeader.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackPublicHeader.h new file mode 100644 index 0000000..f93e611 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackPublicHeader.h @@ -0,0 +1,29 @@ +// +// TDAutoTrackPublicHeader.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#ifndef TDAutoTrackPublicHeader_h +#define TDAutoTrackPublicHeader_h + +#if __has_include() +#import +#else +#import "TDAutoTrackProtocol.h" +#endif + +#if __has_include() +#import +#else +#import "UIView+ThinkingAnalytics.h" +#endif + +#if __has_include() +#import +#else +#import "TDAutoTrackConst.h" +#endif + +#endif /* TDAutoTrackPublicHeader_h */ diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackPublicHeader.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackPublicHeader.h.meta new file mode 100644 index 0000000..31f84b4 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDAutoTrackPublicHeader.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4194a7b3dfc4941a1b12956a2fee33d4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.h new file mode 100644 index 0000000..ab653f6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.h.meta new file mode 100644 index 0000000..48213bf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a6c0e615e93964aa4b486dc9627fcb49 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.m new file mode 100644 index 0000000..7adba66 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.m @@ -0,0 +1,53 @@ +// +// TDRunTime.m +// ThinkingSDK +// +// Created by wwango on 2021/12/30. +// + +#import "TDRunTime.h" + +#if __has_include() +#import +#else +#import "TDJSONUtil.h" +#endif + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +@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)] && ![TDCorePresetDisableConfig 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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.m.meta new file mode 100644 index 0000000..dc33029 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/TDRunTime.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: da6f2608c3dcc4a92b3e15110e509538 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker.meta new file mode 100644 index 0000000..7b1ccad --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c350cba01555c41e4aceb6039c8408a8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h new file mode 100644 index 0000000..b8787ad --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h.meta new file mode 100644 index 0000000..a415e45 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c086add947134424b809df8a51900ab1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m new file mode 100644 index 0000000..7618737 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m.meta new file mode 100644 index 0000000..55c1e5d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAppEndTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4d35cb2477a7d4d7cb3de90ac7b73eca +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h new file mode 100644 index 0000000..471beba --- /dev/null +++ b/Assets/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 "TDAutoTrackEvent.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:(TDAutoTrackEvent *)event params:(nullable NSDictionary *)params; + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h.meta new file mode 100644 index 0000000..ce106e8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: eb845cd4c31074234baa5452aa01b957 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m new file mode 100644 index 0000000..c68d03d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m @@ -0,0 +1,77 @@ +// +// TDAutoTracker.m +// ThinkingSDK +// +// Created by wwango on 2021/10/13. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDAutoTracker.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +#if __has_include() +#import +#else +#import "ThinkingDataCore.h" +#endif + +@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:(TDAutoTrackEvent *)event params:(NSDictionary *)params { + if ([self canTrackWithInstanceToken:instanceName]) { + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK instanceWithAppid: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 innerFlush]; + + if ([[self class] isEqual:NSClassFromString(@"TDInstallTracker")]) { + [TDNotificationManager postAnalyticsAppInstallEventWithAppId:instanceName]; + } + } +} + +- (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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m.meta new file mode 100644 index 0000000..be5a54e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDAutoTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 37c4139007b304431bc20e515eacb5bb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h new file mode 100644 index 0000000..9b76e5c --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h.meta new file mode 100644 index 0000000..430716a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1d55b13ee71a645fb9ac64cf57507008 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m new file mode 100644 index 0000000..04fc57a --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m.meta new file mode 100644 index 0000000..9b8c06b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDColdStartTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f877656a50550492bac50b5751b362a5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h new file mode 100644 index 0000000..4323db2 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h.meta new file mode 100644 index 0000000..e4279ea --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4fb20614614b44d0ca7cc5cca29271d1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m new file mode 100644 index 0000000..53cecc0 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m.meta new file mode 100644 index 0000000..8c53d0f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDHotStartTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ddf70b375e3c245f9a3484f2def07096 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h new file mode 100644 index 0000000..f9f6aeb --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h.meta new file mode 100644 index 0000000..17f4ba7 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b5418cddfc5c94976b671523079f3d99 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m new file mode 100644 index 0000000..26d31cb --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m.meta new file mode 100644 index 0000000..2b9d7c9 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/Tracker/TDInstallTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 60a777c3a36f044dca34b330d75949f1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit.meta new file mode 100644 index 0000000..84f1be0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 110fe4351dde84adaaceb4d3d1a724bc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h new file mode 100644 index 0000000..6560494 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h.meta new file mode 100644 index 0000000..0c684c3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4231e3dad819f4cbcba8a5f9f21ec1fb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m new file mode 100644 index 0000000..1d3640f --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m.meta new file mode 100644 index 0000000..f548302 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIApplication+AutoTrack.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 894a13b5eda684890a9d7cac8ae3126e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h new file mode 100644 index 0000000..30d41cc --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h.meta new file mode 100644 index 0000000..981fd57 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a5a8c330e1377497dba3d444335c5d1d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m new file mode 100644 index 0000000..6cb6509 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m.meta new file mode 100644 index 0000000..1d00c2c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIView+ThinkingAnalytics.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 386fae234a3a846a98321d5e551238b5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h new file mode 100644 index 0000000..4e2e17c --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h.meta new file mode 100644 index 0000000..39215b8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 529ad0356001a49ae855e1c55175fba5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m new file mode 100644 index 0000000..f563f69 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m.meta new file mode 100644 index 0000000..0db0e20 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/AutoTrack/UIKit/UIViewController+AutoTrack.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: efb2847493fa54184af815adecc38259 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Config.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Config.meta new file mode 100644 index 0000000..18325ff --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Config.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c12da08287e3048dabcda0f70108dd46 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h new file mode 100644 index 0000000..d6588ae --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h @@ -0,0 +1,127 @@ +#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 + +/// app id +@property (atomic, copy) NSString *appid; + +/// server url +@property (atomic, copy) NSString *serverUrl; + +/// SDK mode +@property (nonatomic, assign) TDMode mode; + +/// 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; + +/// SDK instance name +@property (nonatomic, copy) NSString *name; + +/// Set the network environment for reporting data +@property (nonatomic, assign) TDReportingNetworkType reportingNetworkType; + +/// 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; + +/// instance Token +@property (atomic, copy) NSString *(^getInstanceName)(void); + +/// 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; + +/// app launchOptions +@property (nonatomic, copy) NSDictionary *launchOptions; + +/// Initialize and configure the certificate verification policy +@property (nonatomic, strong) TDSecurityPolicy *securityPolicy; + +/// share data with App Extension +@property (nonatomic, copy) NSString *appGroupName; + +@property (nonatomic, assign) BOOL enableAutoPush; + +/// Enable the automatic time calibration function +@property (nonatomic, assign) BOOL enableAutoCalibrated; + +/// server url +@property (nonatomic, copy) NSString *configureURL DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with property: serverUrl"); + +#if TARGET_OS_IOS +/// enable encryption +@property (nonatomic, assign) BOOL enableEncrypt DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: -enableEncryptWithVersion:publicKey:"); +/// Get local key configuration +@property (nonatomic, strong) TDSecretKey *secretKey DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: -enableEncryptWithVersion:publicKey:"); +#endif +/** + Debug Mode +*/ +@property (nonatomic, assign) ThinkingAnalyticsDebugMode debugMode DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with property: mode"); +/** + Network environment for data transmission + */ +@property (assign, nonatomic) ThinkingNetworkType networkTypePolicy DEPRECATED_MSG_ATTRIBUTE("Deprecated. don't need this property"); +/** + Set automatic burying type + */ +@property (assign, nonatomic) ThinkingAnalyticsAutoTrackEventType autoTrackEventType DEPRECATED_MSG_ATTRIBUTE("Deprecated. don't need this property"); +/** + 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"); + +- (void)setNetworkType:(ThinkingAnalyticsNetworkType)type DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: -setUploadNetworkType:"); +- (void)updateConfig:(void(^)(NSDictionary *dict))block DEPRECATED_MSG_ATTRIBUTE("Deprecated"); +- (NSString *)getMapInstanceToken DEPRECATED_MSG_ATTRIBUTE("Deprecated"); ++ (TDConfig *)defaultTDConfig DEPRECATED_MSG_ATTRIBUTE("Deprecated"); + +/// Initialize the SDK config file +/// @param appId project app Id +/// @param serverUrl Thinking Engine receiver url +- (instancetype)initWithAppId:(NSString *)appId serverUrl:(NSString *)serverUrl; + +/// enable encrypt +/// @param version version of the encryption configuration file +/// @param publicKey public key +- (void)enableEncryptWithVersion:(NSUInteger)version publicKey:(NSString *)publicKey; + +/// enable DNS parse. Must close ATS in info.plist. +/// @param services DNS service list +- (void)enableDNSServcie:(NSArray *)services; + +@end +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h.meta new file mode 100644 index 0000000..620544f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 138e749d506714c15bc9b7134cc64ee2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m new file mode 100644 index 0000000..9369b06 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m @@ -0,0 +1,326 @@ +#import "TDConfig.h" + +#import "TDAnalyticsNetwork.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDSecurityPolicy.h" +#import "TDFile.h" +#import "TDConfigPrivate.h" + +#if __has_include() +#import +#else +#import "TDCalibratedTime.h" +#endif +#if __has_include() +#import +#else +#import "NSString+TDCore.h" +#endif + +#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 NSDictionary *configInfo; + +@interface TDConfig () +@property (nonatomic, assign) ThinkingNetworkType innerNetworkType; + +@end + +@implementation TDConfig + +TDSDKSETTINGS_PLIST_SETTING_IMPL(NSNumber, ThinkingSDKMaxCacheSize, _maxNumEventsNumber, _setMaxNumEventsNumber, @10000, NO); +TDSDKSETTINGS_PLIST_SETTING_IMPL(NSNumber, ThinkingSDKExpirationDays, _expirationDaysNumber, _setExpirationDaysNumber, @10, NO); + +- (instancetype)init { + self = [super init]; + if (self) { + self.reportingNetworkType = TDReportingNetworkTypeALL; + self.mode = TDModeNormal; + + _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; + _serverUrl = serverUrl; + } + return self; +} + +- (void)enableEncryptWithVersion:(NSUInteger)version publicKey:(NSString *)publicKey { +#if TARGET_OS_IOS + if ([publicKey isKindOfClass:NSString.class] && publicKey.length > 0) { + self.innerEnableEncrypt = YES; + self.innerSecretKey = [[TDSecretKey alloc] initWithVersion:version publicKey:publicKey]; + } else { + self.innerEnableEncrypt = NO; + } +#endif +} + +- (void)enableDNSServcie:(NSArray *)services { + // check DNS service list + if (!services || services.count <= 0) { + TDLogDebug(@"Enable DNS service error: Service is empty"); + return; + } + NSArray *dNSServices = @[TDDNSServiceCloudFlare, TDDNSServiceCloudALi, TDDNSServiceCloudGoogle]; + + NSMutableArray *filterServices = [NSMutableArray array]; + for (TDDNSService obj in services) { + if ([obj isKindOfClass:NSString.class] && [dNSServices containsObject:obj]) { + [filterServices addObject:obj]; + } + } + if (filterServices.count > 0) { + TDLogDebug(@"Enable DNS service. Server url list is: %@", filterServices); + self.dnsServices = filterServices; + [TDAnalyticsNetwork enableDNSServcie:filterServices]; + } else { + TDLogDebug(@"Enable DNS service error: Service url authentication failed"); + } +} + +- (void)setName:(NSString *)name { + _name = name.td_trim; +} + +#pragma mark - NSCopying +- (id)copyWithZone:(NSZone *)zone { + TDConfig *config = [[[self class] allocWithZone:zone] init]; + config.trackRelaunchedInBackgroundEvents = self.trackRelaunchedInBackgroundEvents; + config.innerNetworkType = self.innerNetworkType; + config.launchOptions = [self.launchOptions copyWithZone:zone]; + config.mode = self.mode; + config.reportingNetworkType = self.reportingNetworkType; + config.securityPolicy = [self.securityPolicy copyWithZone:zone]; + config.defaultTimeZone = [self.defaultTimeZone copyWithZone:zone]; + config.name = [self.name copy]; + config.appGroupName = [self.appGroupName copy]; + config.serverUrl = [self.serverUrl copy]; + config.enableAutoPush = self.enableAutoPush; + config.dnsServices = self.dnsServices; +#if TARGET_OS_IOS + config.innerSecretKey = [self.innerSecretKey copyWithZone:zone]; + config.innerEnableEncrypt = self.innerEnableEncrypt; +#endif + + return config; +} + +#pragma mark - SETTINGS + +- (void)setReportingNetworkType:(TDReportingNetworkType)reportingNetworkType { + switch (reportingNetworkType) { + case TDReportingNetworkTypeWIFI: { + self.innerNetworkType = ThinkingNetworkTypeWIFI; + } break; + case TDReportingNetworkTypeALL: { + self.innerNetworkType = ThinkingNetworkTypeALL; + } break; + default: { + self.innerNetworkType = ThinkingNetworkTypeALL; + } break; + } +} + +//MARK: - private + +- (ThinkingNetworkType)getNetworkType { + return self.innerNetworkType; +} + +- (void)innerUpdateConfig:(void (^)(NSDictionary *))block { + NSString *serverUrlStr = [NSString stringWithFormat:@"%@/config",self.serverUrl]; + TDAnalyticsNetwork *network = [[TDAnalyticsNetwork 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 (self.enableAutoCalibrated) { + NSNumber *serverTimestampNum = result[@"server_timestamp"]; + if ([serverTimestampNum isKindOfClass:NSNumber.class]) { + NSTimeInterval serverTimestamp = [serverTimestampNum doubleValue] * 0.001; + [[TDCalibratedTime sharedInstance] recalibrationWithTimeInterval:serverTimestamp]; + } + } + 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 instanceWithAppid: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)innerUpdateIPMap { + if (self.dnsServices.count <= 0) { + return; + } + NSString *serverUrlStr = [NSString stringWithFormat:@"%@/sync", self.serverUrl]; + TDAnalyticsNetwork *network = [[TDAnalyticsNetwork alloc] init]; + network.serverURL = [NSURL URLWithString:serverUrlStr]; + network.securityPolicy = self.securityPolicy; + [network fetchIPMap]; +} + +- (NSString *)innerGetMapInstanceToken { + if (self.name && [self.name isKindOfClass:[NSString class]] && self.name.length) { + return self.name; + } else { + return self.appid; + } +} + +//MARK: - Deprecated: public + ++ (TDConfig *)defaultTDConfig DEPRECATED_MSG_ATTRIBUTE("Deprecated"){ + static dispatch_once_t onceToken; + static TDConfig * _defaultTDConfig; + dispatch_once(&onceToken, ^{ + _defaultTDConfig = [TDConfig new]; + }); + return _defaultTDConfig; +} + +- (NSString *)getMapInstanceToken DEPRECATED_MSG_ATTRIBUTE("Deprecated"){ + return [self innerGetMapInstanceToken]; +} + +- (void)updateConfig:(void (^)(NSDictionary *))block DEPRECATED_MSG_ATTRIBUTE("Deprecated"){ + [self innerUpdateConfig:block]; +} + +- (void)setNetworkType:(ThinkingAnalyticsNetworkType)type DEPRECATED_MSG_ATTRIBUTE("Deprecated"){ + switch (type) { + case TDNetworkTypeOnlyWIFI: { + self.reportingNetworkType = TDReportingNetworkTypeWIFI; + } break; + case TDNetworkTypeALL: { + self.reportingNetworkType = TDReportingNetworkTypeALL; + } break; + default: { + self.innerNetworkType = ThinkingNetworkTypeALL; + } break; + } +} + +//MARK: - Deprecated: setter & geter + +- (void)setConfigureURL:(NSString *)configureURL { + self.serverUrl = configureURL; +} + +- (NSString *)configureURL { + return self.serverUrl; +} + +#if TARGET_OS_IOS +- (void)setSecretKey:(TDSecretKey *)secretKey { + _secretKey = secretKey; + + [self enableEncryptWithVersion:secretKey.version publicKey:secretKey.publicKey]; +} + +- (void)setEnableEncrypt:(BOOL)enableEncrypt { + _enableEncrypt = enableEncrypt; + self.innerEnableEncrypt = enableEncrypt; +} +#endif + +- (void)setNetworkTypePolicy:(ThinkingNetworkType)networkTypePolicy DEPRECATED_MSG_ATTRIBUTE("Deprecated"){ + _networkTypePolicy = networkTypePolicy; + self.innerNetworkType = networkTypePolicy; +} + +- (void)setDebugMode:(ThinkingAnalyticsDebugMode)debugMode DEPRECATED_MSG_ATTRIBUTE("Deprecated"){ + _debugMode = debugMode; + self.mode = (TDMode)debugMode; +} + ++ (NSInteger)maxNumEvents DEPRECATED_MSG_ATTRIBUTE("Deprecated"){ + NSInteger maxNumEvents = [self _maxNumEventsNumber].integerValue; + if (maxNumEvents < 5000) { + maxNumEvents = 5000; + } + return maxNumEvents; +} + ++ (void)setMaxNumEvents:(NSInteger)maxNumEventsNumber DEPRECATED_MSG_ATTRIBUTE("Deprecated"){ + [self _setMaxNumEventsNumber:@(maxNumEventsNumber)]; +} + ++ (NSInteger)expirationDays DEPRECATED_MSG_ATTRIBUTE("Deprecated"){ + NSInteger maxNumEvents = [self _expirationDaysNumber].integerValue; + return maxNumEvents >= 0 ? maxNumEvents : 10; +} + ++ (void)setExpirationDays:(NSInteger)expirationDays DEPRECATED_MSG_ATTRIBUTE("Deprecated"){ + [self _setExpirationDaysNumber:@(expirationDays)]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m.meta new file mode 100644 index 0000000..01743a2 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfig.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8af41e5499deb452491c8aa99ae7e698 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfigPrivate.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfigPrivate.h new file mode 100644 index 0000000..ee10971 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfigPrivate.h @@ -0,0 +1,33 @@ +// +// TDConfigPrivate.h +// Pods +// +// Created by 杨雄 on 2023/8/15. +// + +#if __has_include() +#import +#else +#import "TDConfig.h" +#endif + +#ifndef TDConfigPrivate_h +#define TDConfigPrivate_h + +@interface TDConfig () +@property (atomic, assign) BOOL innerEnableEncrypt; +/// DNS service url for fetching ips +@property (atomic, copy) NSArray *dnsServices; + +#if TARGET_OS_IOS +@property (nonatomic, strong) TDSecretKey *innerSecretKey; +#endif + +- (ThinkingNetworkType)getNetworkType; +- (void)innerUpdateConfig:(void(^)(NSDictionary *dict))block; +- (NSString *)innerGetMapInstanceToken; +- (void)innerUpdateIPMap; + +@end + +#endif /* TDConfigPrivate_h */ diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfigPrivate.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfigPrivate.h.meta new file mode 100644 index 0000000..91ae349 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDConfigPrivate.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 05e72c394e15442e2985410cef03f8ce +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h new file mode 100644 index 0000000..37de6ab --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h.meta new file mode 100644 index 0000000..067724a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d19be503ed4684ca2a2419354befb2f7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m new file mode 100644 index 0000000..1807f85 --- /dev/null +++ b/Assets/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 @"3.1.1"; +} +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m.meta new file mode 100644 index 0000000..60d8b6b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Config/TDPublicConfig.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2ce9b91a7899b48d08e6a4657935e1a9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo.meta new file mode 100644 index 0000000..e1fa090 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 573f9b30d37104af0b1e33df00a0c526 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.h b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.h new file mode 100644 index 0000000..6625233 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.h @@ -0,0 +1,18 @@ +// +// TDAnalyticsPresetProperty.h +// ThinkingSDK +// +// Created by 杨雄 on 2024/5/27. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAnalyticsPresetProperty : NSObject + ++ (NSDictionary *)propertiesWithAppId:(NSString *)appId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.h.meta new file mode 100644 index 0000000..7dc341b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: cfdf928c8073849d0accf16a69e4ccd6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.m b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.m new file mode 100644 index 0000000..0032c1f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.m @@ -0,0 +1,58 @@ +// +// TDAnalyticsPresetProperty.m +// ThinkingSDK +// +// Created by 杨雄 on 2024/5/27. +// + +#import "TDAnalyticsPresetProperty.h" +#import "TDDeviceInfo.h" + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +#if __has_include() +#import +#else +#import "NSDate+TDCore.h" +#endif + +#if __has_include() +#import +#else +#import "NSString+TDCore.h" +#endif + +#import "ThinkingAnalyticsSDKPrivate.h" + +@implementation TDAnalyticsPresetProperty + ++ (NSDictionary *)propertiesWithAppId:(NSString *)appId { + NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary]; + + if (![TDCorePresetDisableConfig disableLib]) { + NSString *value = [[TDDeviceInfo sharedManager] libName]; + if (value) { + mutableDict[@"#lib"] = value; + } + } + if (![TDCorePresetDisableConfig disableLibVersion]) { + NSString *value = [[TDDeviceInfo sharedManager] libVersion]; + if (value) { + mutableDict[@"#lib_version"] = value; + } + } + + if (![NSString td_isEmpty:appId]) { + ThinkingAnalyticsSDK *sdk = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + double offset = [[NSDate date] td_timeZoneOffset:sdk.config.defaultTimeZone ?: [NSTimeZone localTimeZone]]; + [mutableDict setObject:@(offset) forKey:@"#zone_offset"]; + } + + return mutableDict; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.m.meta new file mode 100644 index 0000000..239f6e3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDAnalyticsPresetProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 067caa9474be64ef0a3c32d8703f8c7a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h new file mode 100644 index 0000000..8218973 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h @@ -0,0 +1,17 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXTERN NSString *const VERSION; + +@interface TDDeviceInfo : NSObject +@property (nonatomic, copy, readonly) NSString *uniqueId; +@property (nonatomic, assign, readonly) BOOL isFirstOpen; +@property (nonatomic, copy) NSString *libName; +@property (nonatomic, copy) NSString *libVersion; + ++ (TDDeviceInfo *)sharedManager; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h.meta new file mode 100644 index 0000000..06b82ab --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7de6737a81af64127a0e9e26437c140f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m new file mode 100644 index 0000000..df45b5f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m @@ -0,0 +1,152 @@ +#import "TDDeviceInfo.h" +#import "TDPublicConfig.h" +#import "TDKeychainHelper.h" +#import "TDFile.h" +#import "TDKeychainHelper.h" + +#if __has_include() +#import +#else +#import "TDCoreDeviceInfo.h" +#endif + +#if __has_include() +#import +#else +#import "TDCoreKeychainHelper.h" +#endif + +#import "ThinkingAnalyticsSDKPrivate.h" + + +@interface TDDeviceInfo () +@property (nonatomic, copy, readwrite) NSString *uniqueId; +@property (nonatomic, assign, readwrite) BOOL isFirstOpen; + +@end + +@implementation TDDeviceInfo + ++ (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]; + } + return self; +} + ++ (NSString *)libVersion { + return [self sharedManager].libVersion; +} + +- (NSString *)uniqueId { + static dispatch_once_t onceToken; + static NSString *uniqueId = nil; + dispatch_once(&onceToken, ^{ + uniqueId = [self getDeviceUniqueId]; + }); + return uniqueId; +} + + +#if TARGET_OS_OSX + +- (nullable NSString *)getSystemSerialNumber { + io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")); + if (platformExpert) { + CFTypeRef serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0); + IOObjectRelease(platformExpert); + if (serialNumberAsCFString) { + NSString *serialNumber = (__bridge_transfer NSString *)serialNumberAsCFString; + return serialNumber; + } + } + return nil; +} + +- (NSString *)getDeviceUniqueId { + NSString *keyExistFirstRecord = @"thinking_isfirst"; + BOOL isExistFirstRecord = [[[NSUserDefaults standardUserDefaults] objectForKey:keyExistFirstRecord] boolValue]; + if (!isExistFirstRecord) { + self.isFirstOpen = YES; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:keyExistFirstRecord]; + } else { + self.isFirstOpen = NO; + } + + NSString *keyDefaultDistinctId = @"thinking_data_default_distinct_id"; + NSString *defaultDistinctId = [[NSUserDefaults standardUserDefaults] stringForKey:keyDefaultDistinctId]; + if (!defaultDistinctId) { + defaultDistinctId = [TDCoreDeviceInfo deviceId]; + [[NSUserDefaults standardUserDefaults] setObject:defaultDistinctId forKey:keyDefaultDistinctId]; + } + + return defaultDistinctId; +} + +#endif + +#if TARGET_OS_IOS + +- (NSString *)getDeviceUniqueId { + NSString *uniqueId = nil; + @synchronized (self) { + NSString *deviceId = [TDCoreDeviceInfo deviceId]; + NSString *installTimes = [TDKeychainHelper readInstallTimes]; + BOOL isExistFirstRecord = [[[NSUserDefaults standardUserDefaults] objectForKey:@"thinking_isfirst"] boolValue]; + if (!isExistFirstRecord) { + self.isFirstOpen = YES; + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"thinking_isfirst"]; + } else { + self.isFirstOpen = NO; + } + + TDFile *file = [[TDFile alloc] initWithAppid:[ThinkingAnalyticsSDK defaultInstance].config.appid]; + if (deviceId.length == 0) { + deviceId = [file unarchiveDeviceId]; + if (deviceId.length > 0) { + [TDCoreKeychainHelper saveDeviceId:deviceId]; + } + } + if (installTimes.length == 0) { + installTimes = [file unarchiveInstallTimes]; + if (installTimes.length > 0) { + [TDKeychainHelper saveInstallTimes:installTimes]; + } + } + if (installTimes.length == 0) { + installTimes = @"1"; + [file archiveInstallTimes:installTimes]; + [TDKeychainHelper saveInstallTimes:installTimes]; + } else { + if (!isExistFirstRecord) { + int setup_int = [installTimes intValue]; + setup_int++; + installTimes = [NSString stringWithFormat:@"%d", setup_int]; + [file archiveInstallTimes:installTimes]; + [TDKeychainHelper saveInstallTimes:installTimes]; + } + } + + if ([installTimes isEqualToString:@"1"]) { + uniqueId = deviceId; + } else { + uniqueId = [NSString stringWithFormat:@"%@_%@",deviceId, installTimes]; + } + } + return uniqueId; +} + +#endif + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m.meta new file mode 100644 index 0000000..c5f3021 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/DeviceInfo/TDDeviceInfo.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 744d610cea1594481a55109a212558f7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt.meta new file mode 100644 index 0000000..515ddfb --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: baf6ca963286f4fe49728f2419026900 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h new file mode 100644 index 0000000..f356dd4 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h.meta new file mode 100644 index 0000000..604529a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncrypt.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 99ffee2b45dd4439c8b49b22c8899d70 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h new file mode 100644 index 0000000..ceea259 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h.meta new file mode 100644 index 0000000..772f02b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptAlgorithm.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: be6654071274442269e91d5fd5a7fb7c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h new file mode 100644 index 0000000..e3eaa9a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h @@ -0,0 +1,29 @@ +// +// TDEncryptManager.h +// ThinkingSDK +// +// Created by wwango on 2022/1/21. + + +#import + +@class TDSecretKey; + +NS_ASSUME_NONNULL_BEGIN + +@interface TDEncryptManager : NSObject + + +@property(nonatomic, assign, getter=isValid) BOOL valid; + + +- (instancetype)initWithSecretKey:(TDSecretKey *)secretKey; + +- (void)handleEncryptWithConfig:(NSDictionary *)encryptConfig; + + +- (NSDictionary *)encryptJSONObject:(NSDictionary *)obj; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h.meta new file mode 100644 index 0000000..095e796 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ecffce0fb694b4e81a9a0e64f8584b98 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m new file mode 100644 index 0000000..1b91336 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m @@ -0,0 +1,216 @@ +// +// TDEncryptManager.m +// ThinkingSDK +// +// Created by wwango on 2022/1/21. +// + +#import "TDEncryptManager.h" +#import "TDEncryptProtocol.h" +#import "TDSecretKey.h" +#import "TDRSAEncryptorPlugin.h" +#if __has_include() +#import +#else +#import "NSData+TDGzip.h" +#endif +#if __has_include() +#import +#else +#import "TDJSONUtil.h" +#endif +#import "TDLogging.h" + +@interface TDEncryptManager () +@property (nonatomic, strong) id encryptor; +@property (nonatomic, copy) NSArray> *encryptors; +@property (nonatomic, copy) NSString *encryptedSymmetricKey; +@property (nonatomic, strong) TDSecretKey *secretKey; +@property (nonatomic, strong) TDSecretKey *customSecretKey; + +@end + +@implementation TDEncryptManager + +- (instancetype)initWithSecretKey:(TDSecretKey *)secretKey { + self = [self init]; + if (self) { + self.customSecretKey = secretKey; + [self updateEncryptor:secretKey]; + } + return self; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + NSMutableArray *encryptors = [NSMutableArray array]; + [encryptors addObject:[TDRSAEncryptorPlugin new]]; + self.encryptors = encryptors; + } + return self; +} + +- (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); + } +} + +- (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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m.meta new file mode 100644 index 0000000..18d6f5d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1ad3a2338631944cd87df838012e7751 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h new file mode 100644 index 0000000..fad0aa6 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h.meta new file mode 100644 index 0000000..91e05af --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDEncryptProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0bb0d612c5fc140eeb484ac00d791b63 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h new file mode 100644 index 0000000..f5fe539 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h.meta new file mode 100644 index 0000000..66ed025 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ec629b9835a264c5d91241e2774be8d8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m new file mode 100644 index 0000000..cf97361 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m.meta new file mode 100644 index 0000000..1e658b5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/TDSecretKey.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 03c8b384def5542ae8bd608d4b3968ed +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin.meta new file mode 100644 index 0000000..524faa6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a51f5c933df844bfaa6e322b24b3ed9e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h new file mode 100644 index 0000000..df59789 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h.meta new file mode 100644 index 0000000..d531101 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9d1974cff31e245d4a80d7890c19ca79 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m new file mode 100644 index 0000000..634214a --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m.meta new file mode 100644 index 0000000..9057c28 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDAESEncryptor.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3356f39043a754358aac9d671c6355d0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h new file mode 100644 index 0000000..ec1bfe8 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h.meta new file mode 100644 index 0000000..c1b532d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 31a27833643c74455bef05a2933ee2eb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m new file mode 100644 index 0000000..e00c6fe --- /dev/null +++ b/Assets/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_1"; + 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]; + [publicKey setObject:(__bridge id) kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge id)kSecAttrAccessible]; + OSStatus status = 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; + 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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m.meta new file mode 100644 index 0000000..fec4d44 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptor.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9bee54e226cda4595abb93c0ebbe11df +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h new file mode 100644 index 0000000..0d88a72 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h.meta new file mode 100644 index 0000000..657c24f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a6d6b82caa9fc42fa81c28fee8c4ad79 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m new file mode 100644 index 0000000..97f650e --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m.meta new file mode 100644 index 0000000..0e7eebb --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Encrypt/plugin/TDRSAEncryptorPlugin.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d4d26e5bdde0c4282bf6539cf6df7001 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker.meta new file mode 100644 index 0000000..379345f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d39d3850ca6b34272af89f0692e73180 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel.meta new file mode 100644 index 0000000..d4770b9 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9560727ea3c7d4990b4106ce6735a5ff +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.h new file mode 100644 index 0000000..4216013 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.h @@ -0,0 +1,18 @@ +// +// TDBaseEvent+H5.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import "TDBaseEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDBaseEvent (H5) +@property (nonatomic, copy) NSString *h5TimeString; +@property (nonatomic, strong) NSNumber *h5ZoneOffSet; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.h.meta new file mode 100644 index 0000000..13f539c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ad6332a385abb409fac530fd09e031c1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.m new file mode 100644 index 0000000..2a742d1 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.m @@ -0,0 +1,29 @@ +// +// TDBaseEvent+H5.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import "TDBaseEvent+H5.h" +#import + +@implementation TDBaseEvent (H5) + +- (NSString *)h5TimeString { + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setH5TimeString:(NSString *)h5TimeString { + objc_setAssociatedObject(self, @selector(h5TimeString), h5TimeString, OBJC_ASSOCIATION_COPY_NONATOMIC); +} + +- (NSNumber *)h5ZoneOffSet { + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setH5ZoneOffSet:(NSNumber *)h5ZoneOffSet { + objc_setAssociatedObject(self, @selector(h5ZoneOffSet), h5ZoneOffSet, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.m.meta new file mode 100644 index 0000000..1d4851a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent+H5.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f301ae1267df64970b42d12c61a9b8f8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.h new file mode 100644 index 0000000..341d3a4 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.h @@ -0,0 +1,85 @@ +// +// TDBaseEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import +#import "TDPropertyValidator.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef NSString * kTDEventType; + +typedef NS_OPTIONS(NSUInteger, TDEventType) { + TDEventTypeNone = 0, + TDEventTypeTrack = 1 << 0, + TDEventTypeTrackFirst = 1 << 1, + TDEventTypeTrackUpdate = 1 << 2, + TDEventTypeTrackOverwrite = 1 << 3, + TDEventTypeUserSet = 1 << 4, + TDEventTypeUserUnset = 1 << 5, + TDEventTypeUserAdd = 1 << 6, + TDEventTypeUserDel = 1 << 7, + TDEventTypeUserSetOnce = 1 << 8, + TDEventTypeUserAppend = 1 << 9, + TDEventTypeUserUniqueAppend = 1 << 10, + TDEventTypeAll = 0xFFFFFFFF, +}; + +FOUNDATION_EXTERN NSString * const TD_BACKGROUND_DURATION; + +typedef NSString *kEDEventTypeName; + +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_TRACK; +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_USER_DEL; +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_USER_ADD; +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_USER_SET; +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_USER_SETONCE; +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_USER_UNSET; +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_USER_APPEND; +FOUNDATION_EXTERN kEDEventTypeName const TD_EVENT_TYPE_USER_UNIQ_APPEND; + +typedef NS_OPTIONS(NSInteger, TDEventTimeValueType) { + TDEventTimeValueTypeNone = 0, + TDEventTimeValueTypeTimeOnly = 1 << 0, + TDEventTimeValueTypeTimeAndZone = 1 << 1, +}; + +@interface TDBaseEvent : NSObject +@property (nonatomic, assign) TDEventType 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) TDEventTimeValueType timeValueType; +@property (nonatomic, strong) NSMutableDictionary *properties; + +@property (nonatomic, assign) BOOL immediately; +@property (nonatomic, assign) BOOL isDebug; + +@property (atomic, assign, getter=isTrackPause) BOOL trackPause; + +@property (nonatomic, assign) BOOL isEnabled; + +@property (atomic, assign) BOOL isOptOut; + +- (instancetype)initWithType:(TDEventType)type; + +- (void)validateWithError:(NSError **)error; + +- (NSMutableDictionary *)jsonObject; + +- (NSMutableDictionary *)formatDateWithDict:(NSDictionary *)dict; + +- (NSString *)eventTypeString; + ++ (TDEventType)typeWithTypeString:(NSString *)typeString; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.h.meta new file mode 100644 index 0000000..8f9a423 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: afaae7e08bd134bc79ee77ede44b33ef +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.m new file mode 100644 index 0000000..5fc0596 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.m @@ -0,0 +1,209 @@ +// +// TDBaseEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDBaseEvent.h" + +#if __has_include() +#import +#else +#import "TDJSONUtil.h" +#endif + +#import "TDLogging.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +NSString * const TD_BACKGROUND_DURATION = @"#background_duration"; + +kEDEventTypeName const TD_EVENT_TYPE_TRACK = @"track"; +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"; +kEDEventTypeName const TD_EVENT_TYPE_USER_DEL = @"user_del"; +kEDEventTypeName const TD_EVENT_TYPE_USER_ADD = @"user_add"; +kEDEventTypeName const TD_EVENT_TYPE_USER_SET = @"user_set"; +kEDEventTypeName const TD_EVENT_TYPE_USER_SETONCE = @"user_setOnce"; +kEDEventTypeName const TD_EVENT_TYPE_USER_UNSET = @"user_unset"; +kEDEventTypeName const TD_EVENT_TYPE_USER_APPEND = @"user_append"; +kEDEventTypeName const TD_EVENT_TYPE_USER_UNIQ_APPEND= @"user_uniq_append"; + +@interface TDBaseEvent () +@property (nonatomic, strong) NSDateFormatter *timeFormatter; + +@end + +@implementation TDBaseEvent + +- (instancetype)init +{ + self = [super init]; + if (self) { + _time = [NSDate date]; + self.timeValueType = TDEventTimeValueTypeNone; + self.uuid = [NSUUID UUID].UUIDString; + } + return self; +} + +- (instancetype)initWithType:(TDEventType)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; + } + return [TDJSONUtil formatDateWithFormatter:self.h5ZoneOffSet ? self.h5TimeFormatter : self.timeFormatter dict:dict]; +} + +- (NSString *)eventTypeString { + switch (self.eventType) { + case TDEventTypeTrack: { + return TD_EVENT_TYPE_TRACK; + } break; + case TDEventTypeTrackFirst: { + + return TD_EVENT_TYPE_TRACK; + } break; + case TDEventTypeTrackUpdate: { + return TD_EVENT_TYPE_TRACK_UPDATE; + } break; + case TDEventTypeTrackOverwrite: { + return TD_EVENT_TYPE_TRACK_OVERWRITE; + } break; + case TDEventTypeUserAdd: { + return TD_EVENT_TYPE_USER_ADD; + } break; + case TDEventTypeUserSet: { + return TD_EVENT_TYPE_USER_SET; + } break; + case TDEventTypeUserUnset: { + return TD_EVENT_TYPE_USER_UNSET; + } break; + case TDEventTypeUserAppend: { + return TD_EVENT_TYPE_USER_APPEND; + } break; + case TDEventTypeUserUniqueAppend: { + return TD_EVENT_TYPE_USER_UNIQ_APPEND; + } break; + case TDEventTypeUserDel: { + return TD_EVENT_TYPE_USER_DEL; + } break; + case TDEventTypeUserSetOnce: { + return TD_EVENT_TYPE_USER_SETONCE; + } break; + + default: + return nil; + break; + } +} + ++ (TDEventType)typeWithTypeString:(NSString *)typeString { + if ([typeString isEqualToString:TD_EVENT_TYPE_TRACK]) { + return TDEventTypeTrack; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_TRACK_FIRST]) { + return TDEventTypeTrack; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_TRACK_UPDATE]) { + return TDEventTypeTrackUpdate; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_TRACK_OVERWRITE]) { + return TDEventTypeTrackOverwrite; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_ADD]) { + return TDEventTypeUserAdd; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_DEL]) { + return TDEventTypeUserDel; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_SET]) { + return TDEventTypeUserSet; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_UNSET]) { + return TDEventTypeUserUnset; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_APPEND]) { + return TDEventTypeUserAppend; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_UNIQ_APPEND]) { + return TDEventTypeUserUniqueAppend; + } else if ([typeString isEqualToString:TD_EVENT_TYPE_USER_SETONCE]) { + return TDEventTypeUserSetOnce; + } + return TDEventTypeNone; +} + +//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; +} + +- (NSDateFormatter *)h5TimeFormatter { + double timeZoneNumber = [self.h5ZoneOffSet doubleValue]; + NSString *prefix = timeZoneNumber >= 0 ? @"UTC+" : @"UTC-"; + int hours = (int)fabs(timeZoneNumber); + int minutes = (int)((fabs(timeZoneNumber) - hours) * 60); + NSString *minutesStr = minutes == 0 ? @":00" : [NSString stringWithFormat:@":%02d", minutes]; + NSString *result = [NSString stringWithFormat:@"%@%d%@", prefix, hours, minutesStr]; + self.timeFormatter.timeZone = [NSTimeZone timeZoneWithName: result]; + return self.timeFormatter; +} + +- (void)setTime:(NSDate *)time { + + if (time) { + [self willChangeValueForKey:@"time"]; + _time = time; + [self didChangeValueForKey:@"time"]; + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.m.meta new file mode 100644 index 0000000..5f6893e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDBaseEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1b8d7f99e856c45c78b217ed83737cd2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h new file mode 100644 index 0000000..98a2f4e --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h.meta new file mode 100644 index 0000000..752db9c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEditableEventModel.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fba4ca753c9594b94b225ba0619faecc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h new file mode 100644 index 0000000..473256e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h @@ -0,0 +1,24 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +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; +@property (nonatomic, strong) NSDictionary *properties; + +- (void)configTime:(NSDate *)time timeZone:(NSTimeZone *)timeZone; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h.meta new file mode 100644 index 0000000..4cbaa8c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7d7f726d9fabe4abe85bfba590a4d099 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m new file mode 100644 index 0000000..532e14b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m @@ -0,0 +1,52 @@ + +#import "TDEventModel.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +#if __has_include() +#import +#else +#import "TDCoreDeviceInfo.h" +#endif + +@interface TDEventModel () + +@property (nonatomic, copy) NSString *eventName; +@property (nonatomic, copy) kEDEventTypeName eventType; + +@end + +@implementation TDEventModel + +- (instancetype)initWithEventName:(NSString *)eventName eventType:(kEDEventTypeName)eventType { + if (self = [[[TDEventModel class] alloc] init]) { + self.eventName = eventName ?: @""; + self.eventType = eventType ?: @""; + if ([self.eventType isEqualToString:TD_EVENT_TYPE_TRACK_FIRST]) { + _extraID = [TDCoreDeviceInfo deviceId] ?: @""; + } + } + return self; +} + +#pragma mark - Public + +- (void)configTime:(NSDate *)time timeZone:(NSTimeZone *)timeZone { + self.time = time; + self.timeZone = timeZone; +} + +#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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m.meta new file mode 100644 index 0000000..9b89236 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDEventModel.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3de42dea3b4f54381b502e54cf5584a5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h new file mode 100644 index 0000000..a0d83a9 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h.meta new file mode 100644 index 0000000..f864186 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 97a7f26110d42427091d0ecc77a0cef1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m new file mode 100644 index 0000000..11db5a8 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m.meta new file mode 100644 index 0000000..f95b4c1 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDFirstEventModel.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ef9d0018898d449ac852a035e9776780 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h new file mode 100644 index 0000000..46a2968 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h.meta new file mode 100644 index 0000000..03be643 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5dc01cb9b7f4e42f9b63a0d9d768368f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m new file mode 100644 index 0000000..7f9de48 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m.meta new file mode 100644 index 0000000..473857a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDOverwriteEventModel.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 30daf01e45573481881a7ae94d3b89a1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.h new file mode 100644 index 0000000..3ff2191 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.h @@ -0,0 +1,34 @@ +// +// TATrackEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDBaseEvent.h" + +NS_ASSUME_NONNULL_BEGIN + + +@interface TDTrackEvent : TDBaseEvent +/// 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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.h.meta new file mode 100644 index 0000000..5dfbbdc --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a4f0e76fae39a4d928ce3b4519ab1518 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.m new file mode 100644 index 0000000..4ce6194 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.m @@ -0,0 +1,86 @@ +// +// TATrackEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDTrackEvent.h" +#import "TDPresetProperties.h" + +#if __has_include() +#import +#else +#import "NSDate+TDCore.h" +#endif + +#if __has_include() +#import +#else +#import "TDCoreDeviceInfo.h" +#endif + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +@implementation TDTrackEvent + +- (instancetype)initWithName:(NSString *)eventName { + if (self = [self init]) { + self.eventName = eventName; + } + return self; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.eventType = TDEventTypeTrack; + self.systemUpTime = [TDCoreDeviceInfo bootTime]; + } + return self; +} + +- (void)validateWithError:(NSError *__autoreleasing _Nullable *)error { + + [TDPropertyValidator validateEventOrPropertyName:self.eventName withError:error]; +} + +- (NSMutableDictionary *)jsonObject { + NSMutableDictionary *dict = [super jsonObject]; + + dict[@"#event_name"] = self.eventName; + + if (![TDCorePresetDisableConfig disableDuration]) { + if (self.foregroundDuration > 0) { + self.properties[@"#duration"] = @([NSString stringWithFormat:@"%.3f", self.foregroundDuration].floatValue); + } + } + + if (![TDCorePresetDisableConfig disableBackgroundDuration]) { + if (self.backgroundDuration > 0) { + self.properties[TD_BACKGROUND_DURATION] = @([NSString stringWithFormat:@"%.3f", self.backgroundDuration].floatValue); + } + } + + self.properties[@"#zone_offset"] = @([self timeZoneOffset]); + + return dict; +} + +- (double)timeZoneOffset { + NSTimeZone *tz = self.timeZone ?: [NSTimeZone localTimeZone]; + return [[NSDate date] td_timeZoneOffset:tz]; +} + +//MARK: - Delegate + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + [TDPropertyValidator validateNormalTrackEventPropertyKey:key value:value error:error]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.m.meta new file mode 100644 index 0000000..df24782 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2612df3a7727940ceb0ccaf54e7c39e6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.h new file mode 100644 index 0000000..b179c63 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.h @@ -0,0 +1,18 @@ +// +// TATrackFirstEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDTrackFirstEvent : TDTrackEvent + +@property (nonatomic, copy) NSString *firstCheckId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.h.meta new file mode 100644 index 0000000..89fd8a3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 082ed5175f8a940cba9a16ec7c0ecc86 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.m new file mode 100644 index 0000000..d3c9561 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.m @@ -0,0 +1,48 @@ +// +// TATrackFirstEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDTrackFirstEvent.h" + +#if __has_include() +#import +#else +#import "TDCoreDeviceInfo.h" +#endif + + +@implementation TDTrackFirstEvent + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.eventType = TDEventTypeTrackFirst; + } + 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 ?: [TDCoreDeviceInfo deviceId]; + + return dict; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.m.meta new file mode 100644 index 0000000..4e44d90 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackFirstEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c1b41633104d5440b9e4ccdf9142868b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.h new file mode 100644 index 0000000..895fc61 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.h @@ -0,0 +1,18 @@ +// +// TATrackOverwriteEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDTrackOverwriteEvent : TDTrackEvent + +@property (nonatomic, copy) NSString *eventId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.h.meta new file mode 100644 index 0000000..96d82d0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b990dd3d951fe45a39e4ef860d751f0e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.m new file mode 100644 index 0000000..6205ae0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.m @@ -0,0 +1,40 @@ +// +// TATrackOverwriteEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDTrackOverwriteEvent.h" + +@implementation TDTrackOverwriteEvent + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.eventType = TDEventTypeTrackOverwrite; + } + 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) { + TDLogError(@"property 'eventId' cannot be empty which in OverwriteEvent"); + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.m.meta new file mode 100644 index 0000000..e0a233c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackOverwriteEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4414e47d72d0747ad85645f8f9904cdb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.h new file mode 100644 index 0000000..a40cc64 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.h @@ -0,0 +1,18 @@ +// +// TATrackUpdateEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDTrackEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDTrackUpdateEvent : TDTrackEvent + +@property (nonatomic, copy) NSString *eventId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.h.meta new file mode 100644 index 0000000..e51742f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2a4e6a92783804592a341df6acf7d256 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.m new file mode 100644 index 0000000..67bd78f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.m @@ -0,0 +1,40 @@ +// +// TATrackUpdateEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDTrackUpdateEvent.h" + +@implementation TDTrackUpdateEvent + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.eventType = TDEventTypeTrackUpdate; + } + 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) { + TDLogError(@"property 'eventId' cannot be empty which in UpdateEvent"); + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.m.meta new file mode 100644 index 0000000..bb28d90 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDTrackUpdateEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 44092bc189b3646a89357b879cfc9918 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h new file mode 100644 index 0000000..b356a60 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h.meta new file mode 100644 index 0000000..dbfff14 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bab971596eecc447e9515c9c200beeea +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m new file mode 100644 index 0000000..16aeca7 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m.meta new file mode 100644 index 0000000..b4c198c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/TDUpdateEventModel.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5a53fbef3fbd443c6b986f13fb2a9b76 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty.meta new file mode 100644 index 0000000..587ba89 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3cfc2ce7ca8134bceae9d8f4c9a620ab +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.h new file mode 100644 index 0000000..d893cdf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.h @@ -0,0 +1,16 @@ +// +// TAUserEvent.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDBaseEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDUserEvent : TDBaseEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.h.meta new file mode 100644 index 0000000..ded37e9 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 33351f8746690438b9f1ee615980ad7e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.m new file mode 100644 index 0000000..0935ed4 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.m @@ -0,0 +1,29 @@ +// +// TAUserEvent.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDUserEvent.h" + +@implementation TDUserEvent + +- (instancetype)init +{ + self = [super init]; + if (self) { + + } + return self; +} + +//MARK: - Delegate + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + [TDPropertyValidator validateBaseEventPropertyKey:key value:value error:error]; +} + +//MARK: - Setter & Getter + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.m.meta new file mode 100644 index 0000000..0118d4b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEvent.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 410475b18466c4e7e9c9fdc92a00b00e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.h new file mode 100644 index 0000000..e9d2962 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.h @@ -0,0 +1,16 @@ +// +// TAUserEventAdd.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDUserEventAdd : TDUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.h.meta new file mode 100644 index 0000000..ea81d80 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7415330e4e5124333ad47418f85722ac +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.m new file mode 100644 index 0000000..cd96ebc --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.m @@ -0,0 +1,36 @@ +// +// TAUserEventAdd.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEventAdd.h" + +@implementation TDUserEventAdd + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TDEventTypeUserAdd; + } + 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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.m.meta new file mode 100644 index 0000000..76e68b3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAdd.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 680c661f9405d4b67bc86129e17a98a0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.h new file mode 100644 index 0000000..515887f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.h @@ -0,0 +1,16 @@ +// +// TAUserEventAppend.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDUserEventAppend : TDUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.h.meta new file mode 100644 index 0000000..3c08605 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7c0bdc64c7f214279a569e642cd28961 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.m new file mode 100644 index 0000000..f981d46 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.m @@ -0,0 +1,32 @@ +// +// TAUserEventAppend.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEventAppend.h" + +@implementation TDUserEventAppend + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TDEventTypeUserAppend; + } + 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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.m.meta new file mode 100644 index 0000000..dc100b7 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventAppend.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4967adff1152f42f88274d5f55668bcf +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.h new file mode 100644 index 0000000..c9a5c9c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.h @@ -0,0 +1,16 @@ +// +// TAUserEventDelete.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDUserEventDelete : TDUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.h.meta new file mode 100644 index 0000000..3a5768c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 79a3dcbc3c69c45b7a0c67e92d5ae79c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.m new file mode 100644 index 0000000..10904b2 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.m @@ -0,0 +1,19 @@ +// +// TAUserEventDelete.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEventDelete.h" + +@implementation TDUserEventDelete + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TDEventTypeUserDel; + } + return self; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.m.meta new file mode 100644 index 0000000..3ca8948 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventDelete.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 78e83efee5e48498697a633d44da9654 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.h new file mode 100644 index 0000000..1ed434d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.h @@ -0,0 +1,16 @@ +// +// TAUserEventSet.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDUserEventSet : TDUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.h.meta new file mode 100644 index 0000000..68c809f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fe02e78f8ce8844c6a44e3f72f0a4fa5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.m new file mode 100644 index 0000000..eae172f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.m @@ -0,0 +1,19 @@ +// +// TAUserEventSet.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEventSet.h" + +@implementation TDUserEventSet + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TDEventTypeUserSet; + } + return self; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.m.meta new file mode 100644 index 0000000..a9c0002 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSet.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fa8ff0a24d8a44baf8c83b4ac53779d6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.h new file mode 100644 index 0000000..709ec48 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.h @@ -0,0 +1,16 @@ +// +// TAUserEventSetOnce.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDUserEventSetOnce : TDUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.h.meta new file mode 100644 index 0000000..720f429 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 63e0e92cc8e83408cbf96e4c6f089dc0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.m new file mode 100644 index 0000000..f658c8f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.m @@ -0,0 +1,19 @@ +// +// TAUserEventSetOnce.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEventSetOnce.h" + +@implementation TDUserEventSetOnce + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TDEventTypeUserSetOnce; + } + return self; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.m.meta new file mode 100644 index 0000000..4cf6c21 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventSetOnce.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6130b943421394d6080baafd1dc59153 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.h new file mode 100644 index 0000000..98c0e72 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.h @@ -0,0 +1,16 @@ +// +// TAUserEventUniqueAppend.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEventAppend.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDUserEventUniqueAppend : TDUserEventAppend + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.h.meta new file mode 100644 index 0000000..93f6308 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e57d6baf2b811472c8a5de647a77931a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.m new file mode 100644 index 0000000..caf2279 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.m @@ -0,0 +1,19 @@ +// +// TAUserEventUniqueAppend.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEventUniqueAppend.h" + +@implementation TDUserEventUniqueAppend + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TDEventTypeUserUniqueAppend; + } + return self; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.m.meta new file mode 100644 index 0000000..b3b91b1 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUniqueAppend.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8c28f21e1f18848bb8088d92bafb44a4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.h new file mode 100644 index 0000000..75275c5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.h @@ -0,0 +1,16 @@ +// +// TAUserEventUnset.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDUserEventUnset : TDUserEvent + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.h.meta new file mode 100644 index 0000000..9de4162 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 174fff6c702c842a5ab7f44d3c179df1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.m new file mode 100644 index 0000000..8412240 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.m @@ -0,0 +1,19 @@ +// +// TAUserEventUnset.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDUserEventUnset.h" + +@implementation TDUserEventUnset + +- (instancetype)init { + if (self = [super init]) { + self.eventType = TDEventTypeUserUnset; + } + return self; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.m.meta new file mode 100644 index 0000000..e0871e1 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserEventUnset.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0fed9f759e1254d489963423aafc9425 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserPropertyHeader.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserPropertyHeader.h new file mode 100644 index 0000000..e3d213f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserPropertyHeader.h @@ -0,0 +1,19 @@ +// +// TDUserPropertyHeader.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#ifndef TDUserPropertyHeader_h +#define TDUserPropertyHeader_h + +#import "TDUserEventSet.h" +#import "TDUserEventSetOnce.h" +#import "TDUserEventUnset.h" +#import "TDUserEventAdd.h" +#import "TDUserEventDelete.h" +#import "TDUserEventAppend.h" +#import "TDUserEventUniqueAppend.h" + +#endif /* TDUserPropertyHeader_h */ diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserPropertyHeader.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserPropertyHeader.h.meta new file mode 100644 index 0000000..36ff600 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/EventModel/UserProperty/TDUserPropertyHeader.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: db85fc9b1ef384039b473d91fc3db066 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property.meta new file mode 100644 index 0000000..28ccda0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cf30d3548fdac432aaf17971e80d083b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins.meta new file mode 100644 index 0000000..0ea69e6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4410ea0e2817247de9eadf648fa01ac7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.h new file mode 100644 index 0000000..88cb3f1 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.h @@ -0,0 +1,21 @@ +// +// TAPresetPropertyPlugin.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#if TARGET_OS_IOS +#import +#endif +#import "TDPropertyPluginManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDPresetPropertyPlugin : NSObject + +@property(nonatomic, copy)NSString *instanceToken; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.h.meta new file mode 100644 index 0000000..e927f81 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 051f3bf3a1e8f4130b2c68e397b6df06 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.m new file mode 100644 index 0000000..d366a3e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.m @@ -0,0 +1,60 @@ +// +// TAPresetPropertyPlugin.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDPresetPropertyPlugin.h" +#import "TDAnalyticsPresetProperty.h" + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +#if __has_include() +#import +#else +#import "TDCorePresetProperty.h" +#endif + +@interface TDPresetPropertyPlugin () +@property (nonatomic, strong) NSMutableDictionary *properties; + +@end + +@implementation TDPresetPropertyPlugin + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.properties = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)start { + NSDictionary *staticProperties = [TDCorePresetProperty staticProperties]; + [self.properties addEntriesFromDictionary:staticProperties]; +} + +/// The properties here are dynamically updated +/// +- (void)asyncGetPropertyCompletion:(TDPropertyPluginCompletion)completion { + NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary]; + + NSDictionary *dynamicProperties = [TDCorePresetProperty dynamicProperties]; + [mutableDict addEntriesFromDictionary:dynamicProperties]; + + NSDictionary *analyticsProperties = [TDAnalyticsPresetProperty propertiesWithAppId:self.instanceToken]; + [mutableDict addEntriesFromDictionary:analyticsProperties]; + + if (completion) { + completion(mutableDict); + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.m.meta new file mode 100644 index 0000000..e7355fb --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPresetPropertyPlugin.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 41a6f60eeb74149a4b1c17f9b8ff6412 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.h new file mode 100644 index 0000000..62110b5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.h @@ -0,0 +1,42 @@ +// +// TDPropertyPluginManager.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import +#import "TDBaseEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^TDPropertyPluginCompletion)(NSDictionary *properties); + +@protocol TDPropertyPluginProtocol + +@property(nonatomic, copy)NSString *instanceToken; + +- (NSDictionary *)properties; + +@optional + +- (void)start; + +- (TDEventType)eventTypeFilter; + +- (void)asyncGetPropertyCompletion:(TDPropertyPluginCompletion)completion; + +@end + + +@interface TDPropertyPluginManager : NSObject + +- (void)registerPropertyPlugin:(id)plugin; + +- (NSMutableDictionary *)currentPropertiesForPluginClasses:(NSArray *)classes; + +- (NSMutableDictionary *)propertiesWithEventType:(TDEventType)type; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.h.meta new file mode 100644 index 0000000..f0374e3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1d497d7d900b340bc8afb11550ae0602 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.m new file mode 100644 index 0000000..8aa4da6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.m @@ -0,0 +1,125 @@ +// +// TDPropertyPluginManager.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/12. +// + +#import "TDPropertyPluginManager.h" + +@interface TDPropertyPluginManager () +@property (nonatomic, strong) NSMutableArray> *plugins; + +@end + + +@implementation TDPropertyPluginManager + +//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:(TDEventType)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]; + for (id plugin in plugins) { + if ([plugin respondsToSelector:@selector(start)]) { + [plugin start]; + } + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + if ([plugin respondsToSelector:@selector(asyncGetPropertyCompletion:)]) { + [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))); + } + + } + return properties; +} + +- (BOOL)isMatchedWithPlugin:(id)plugin eventType:(TDEventType)type { + TDEventType 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 = TDEventTypeTrack | TDEventTypeTrackFirst | TDEventTypeTrackUpdate | TDEventTypeTrackOverwrite; + } else { + eventTypeFilter = plugin.eventTypeFilter; + } + + if ((eventTypeFilter & type) == type) { + return YES; + } + return NO; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.m.meta new file mode 100644 index 0000000..c1fbbc6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDPropertyPluginManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ca009646dd0fd4c21ba1c71d04cbd54d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.h new file mode 100644 index 0000000..b34b95c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.h @@ -0,0 +1,19 @@ +// +// TASessionIdManager.h +// ThinkingSDK +// +// Created by Charles on 6.12.22. +// + +#import + + +NS_ASSUME_NONNULL_BEGIN + +@interface TDSessionIdManager : NSObject + ++ (instancetype)shareInstance; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.h.meta new file mode 100644 index 0000000..a5f02a3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ee185d71a647c463486848a4e1365ffc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.m new file mode 100644 index 0000000..bbb6509 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.m @@ -0,0 +1,75 @@ +// +// TASessionIdManager.m +// ThinkingSDK +// +// Created by Charles on 6.12.22. +// + +#if TARGET_OS_IOS +#import +#endif +#import "TDSessionIdManager.h" +#import "TDAppLifeCycle.h" +#import "TDAppState.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +@implementation TDSessionIdManager + ++ (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:kTDAppLifeCycleStateWillChangeNotification object:nil]; + +#endif +} + + +- (void)appStateWillChangeNotification:(NSNotification *)notification { + TDAppLifeCycleState newState = [[notification.userInfo objectForKey:kTDAppLifeCycleNewStateKey] integerValue]; + TDAppLifeCycleState oldState = [[notification.userInfo objectForKey:kTDAppLifeCycleOldStateKey] integerValue]; + + if (oldState == TDAppLifeCycleStateInit) { + return; + } + + if (newState == TDAppLifeCycleStateStart) { + @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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.m.meta new file mode 100644 index 0000000..f91aace --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdManager.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 056160bd921ba4a6da9c1472922e0368 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.h new file mode 100644 index 0000000..7cbeb8d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.h @@ -0,0 +1,23 @@ +// +// TASessionIdPropertyPlugin.h +// ThinkingSDK +// +// Created by Charles on 28.11.22. +// + +#if TARGET_OS_IOS +#import +#endif +#import "TDPropertyPluginManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDSessionIdPropertyPlugin : NSObject + +@property(nonatomic, copy)NSString *instanceToken; + +- (void)updateSessionId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.h.meta new file mode 100644 index 0000000..5a5b3be --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 0d3906cb9621143178e639f99a0bb87a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.m new file mode 100644 index 0000000..c849420 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.m @@ -0,0 +1,75 @@ +// +// TASessionIdPropertyPlugin.m +// ThinkingSDK +// +// Created by Charles on 28.11.22. +// + +#import "TDSessionIdPropertyPlugin.h" +#import "TDPresetProperties.h" +#import "TDAppLifeCycle.h" +#import "TDFile.h" +#import "TDAppState.h" + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +@interface TDSessionIdPropertyPlugin () +@property (nonatomic, strong) NSMutableDictionary *properties; +@property (nonatomic, strong) TDFile *file; +@property (atomic, assign) long long sessionid; +@property (atomic, copy) NSString *sessionidString; +@end + + +@implementation TDSessionIdPropertyPlugin + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.properties = [NSMutableDictionary dictionary]; + + } + return self; +} + +- (void)updateSessionId { +#if TARGET_OS_IOS + if (![TDCorePresetDisableConfig 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 (![TDCorePresetDisableConfig disableSessionID]) { + @synchronized ([self class]) { + if (!self.file) { + self.file = [[TDFile alloc] initWithAppid:self.instanceToken]; + } + self.sessionid = [self.file unarchiveSessionID]; + [self updateSessionId]; + } + + } +} + +- (void)asyncGetPropertyCompletion:(TDPropertyPluginCompletion)completion { + NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary]; + + if (completion) { + completion(mutableDict); + } +} + + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.m.meta new file mode 100644 index 0000000..9adebab --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/PropertyPlugins/TDSessionIdPropertyPlugin.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 075a93a31c872428194005ca4deaa887 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.h new file mode 100644 index 0000000..6492058 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.h @@ -0,0 +1,34 @@ +// +// TDAutoTrackSuperProperty.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import +#import "TDAutoTrackConst.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAutoTrackSuperProperty : NSObject + +- (void)registerSuperProperties:(NSDictionary *)properties withType:(TDAutoTrackEventType)type; + +- (NSDictionary *)currentSuperPropertiesWithEventName:(NSString *)eventName; + +- (void)registerDynamicSuperProperties:(NSDictionary *(^)(TDAutoTrackEventType, NSDictionary *))dynamicSuperProperties; + +- (NSDictionary *)obtainDynamicSuperPropertiesWithType:(TDAutoTrackEventType)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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.h.meta new file mode 100644 index 0000000..8be67ad --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e4239d98391f44715b2bd436774a1fc5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.m new file mode 100644 index 0000000..5141574 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.m @@ -0,0 +1,121 @@ +// +// TDAutoTrackSuperProperty.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import "TDAutoTrackSuperProperty.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +@interface TDAutoTrackSuperProperty () +@property (atomic, strong) NSMutableDictionary *eventProperties; +@property (nonatomic, copy) NSDictionary *(^dynamicSuperProperties)(TDAutoTrackEventType type, NSDictionary *properties); +/// Only used for auto track in Unity3D environment +@property (nonatomic, copy) NSDictionary *(^autoTrackDynamicSuperProperties)(void); + +@end + +@implementation TDAutoTrackSuperProperty + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.eventProperties = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)registerSuperProperties:(NSDictionary *)properties withType:(TDAutoTrackEventType)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]; + TDAutoTrackEventType 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 = [TDPropertyValidator validateProperties:[autoEventProperty copy]]; + return validProperties; +} + +- (void)registerDynamicSuperProperties:(NSDictionary *(^)(TDAutoTrackEventType, NSDictionary *))dynamicSuperProperties { + @synchronized (self) { + self.dynamicSuperProperties = dynamicSuperProperties; + } +} + +- (NSDictionary *)obtainDynamicSuperPropertiesWithType:(TDAutoTrackEventType)type currentProperties:(NSDictionary *)properties { + @synchronized (self) { + if (self.dynamicSuperProperties) { + NSDictionary *result = self.dynamicSuperProperties(type, properties); + + NSDictionary *validProperties = [TDPropertyValidator 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 = [TDPropertyValidator validateProperties:[properties copy]]; + return validProperties; + } + return nil; + } +} + +- (void)clearSuperProperties { + self.eventProperties = [@{} mutableCopy]; +} + +//MARK: - Private Methods + + + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.m.meta new file mode 100644 index 0000000..95ff29a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDAutoTrackSuperProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7e7e1b3a7158c454c996df4567bbdb5f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.h new file mode 100644 index 0000000..24c2737 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.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 TDSuperProperty : 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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.h.meta new file mode 100644 index 0000000..9c75dfd --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ffe5d63cbb6e04df89f9fd115255ab29 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.m new file mode 100644 index 0000000..d0246be --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.m @@ -0,0 +1,107 @@ +// +// TASuperProperty.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/10. +// + +#import "TDSuperProperty.h" +#import "TDPropertyValidator.h" +#import "TDLogging.h" +#import "TDFile.h" + +@interface TDSuperProperty () +///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 TDSuperProperty + +- (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 = [TDPropertyValidator 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]; + TDLogInfo(@"set super properties success"); +} + +- (void)unregisterSuperProperty:(NSString *)property { + NSError *error = nil; + [TDPropertyValidator 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]; + TDLogInfo(@"unset super properties success"); +} + +- (void)clearSuperProperties { + self.superProperties = @{}; + [self.file archiveSuperProperties:self.superProperties]; + TDLogInfo(@"clear super properties success"); +} + +- (NSDictionary *)currentSuperProperties { + if (self.superProperties) { + return [TDPropertyValidator 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 = [TDPropertyValidator validateProperties:[properties copy]]; + return validProperties; + } + return nil; + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.m.meta new file mode 100644 index 0000000..d17a90a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/TDSuperProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5ac704519bb1544229fbdd110009769f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate.meta new file mode 100644 index 0000000..5441881 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 860147d4f77334254a37e4e9d5bbec1a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.h new file mode 100644 index 0000000..970281f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.h @@ -0,0 +1,17 @@ +// +// NSArray+TDProperty.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TDValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSArray (TDProperty) + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.h.meta new file mode 100644 index 0000000..c0652d4 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4ccca081373f94af7ab1fc37b3da9a3a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.m new file mode 100644 index 0000000..8fe0347 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.m @@ -0,0 +1,16 @@ +// +// NSArray+TDProperty.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "NSArray+TDProperty.h" + +@implementation NSArray (TDProperty) + +- (void)ta_validatePropertyValueWithError:(NSError *__autoreleasing _Nullable *)error { + +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.m.meta new file mode 100644 index 0000000..f4730f9 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSArray+TDProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f0ccf793fb9d34fb8a044b00769459d0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.h new file mode 100644 index 0000000..65a5923 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.h @@ -0,0 +1,17 @@ +// +// NSDate+TDProperty.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TDValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSDate (TDProperty) + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.h.meta new file mode 100644 index 0000000..9ba94b6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 5bd640f0705e244d3b800f158b37a005 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.m new file mode 100644 index 0000000..6e5ab03 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.m @@ -0,0 +1,16 @@ +// +// NSDate+TDProperty.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "NSDate+TDProperty.h" + +@implementation NSDate (TDProperty) + +- (void)ta_validatePropertyValueWithError:(NSError *__autoreleasing _Nullable *)error { + +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.m.meta new file mode 100644 index 0000000..7f1d4db --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDate+TDProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b252ae6780e87490893021d8be60499e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.h new file mode 100644 index 0000000..eca6181 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.h @@ -0,0 +1,17 @@ +// +// NSDictionary+TDProperty.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TDValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSDictionary (TAProperty) + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.h.meta new file mode 100644 index 0000000..33c832b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d48c6bdcb25844d83b15e74bf7e84cad +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.m new file mode 100644 index 0000000..f76d4ba --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.m @@ -0,0 +1,16 @@ +// +// NSDictionary+TDProperty.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "NSDictionary+TDProperty.h" + +@implementation NSDictionary (TAProperty) + +- (void)ta_validatePropertyValueWithError:(NSError *__autoreleasing _Nullable *)error { + +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.m.meta new file mode 100644 index 0000000..038db38 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSDictionary+TDProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fe25956be5b1e4417b6d095ddf2925bb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.h new file mode 100644 index 0000000..30dc510 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.h @@ -0,0 +1,17 @@ +// +// NSNumber+TDProperty.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TDValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSNumber (TAProperty) + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.h.meta new file mode 100644 index 0000000..c0b9099 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f59c5ace498ac4c0cb5bdc9f50e136f9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.m new file mode 100644 index 0000000..79e3726 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.m @@ -0,0 +1,20 @@ +// +// NSNumber+TDProperty.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "NSNumber+TDProperty.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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.m.meta new file mode 100644 index 0000000..06e1125 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSNumber+TDProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b0e872d03f68c486bbb916834c990882 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.h new file mode 100644 index 0000000..543eecf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.h @@ -0,0 +1,17 @@ +// +// NSString+TDProperty.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TDValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface NSString (TAProperty) + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.h.meta new file mode 100644 index 0000000..117fadd --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bdd7b1a2929964449b750124cc364774 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.m new file mode 100644 index 0000000..9ecb9bd --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.m @@ -0,0 +1,36 @@ +// +// NSString+TDProperty.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "NSString+TDProperty.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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.m.meta new file mode 100644 index 0000000..91f8d41 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/NSString+TDProperty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a4c9df72edb2643dc90762dd6f76c593 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.h new file mode 100644 index 0000000..2a10f70 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.h @@ -0,0 +1,17 @@ +// +// TAPropertyDefaultValidator.h +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import +#import "TDValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDPropertyDefaultValidator : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.h.meta new file mode 100644 index 0000000..95bae47 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 55f53cfe19fee4cdcb3fc6c4c5490642 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.m new file mode 100644 index 0000000..f2f45ed --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.m @@ -0,0 +1,17 @@ +// +// TAPropertyDefaultValidator.m +// Adjust +// +// Created by Yangxiongon 2022/7/1. +// + +#import "TDPropertyDefaultValidator.h" +#import "TDPropertyValidator.h" + +@implementation TDPropertyDefaultValidator + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError *__autoreleasing _Nullable *)error { + [TDPropertyValidator validateBaseEventPropertyKey:key value:value error:error]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.m.meta new file mode 100644 index 0000000..dcac914 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyDefaultValidator.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: eebd3afcb4ccc446eb78003734d6a083 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.h new file mode 100644 index 0000000..331adcb --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.h @@ -0,0 +1,30 @@ +// +// TDPropertyValidator.h +// Adjust +// +// Created by Yangxiongon 2022/6/10. +// + +#import +#import "TDValidatorProtocol.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface TDPropertyValidator : 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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.h.meta new file mode 100644 index 0000000..ce093f1 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f045b31c3d4434ff3b597c9a05328f0a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.m new file mode 100644 index 0000000..6246e5b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.m @@ -0,0 +1,133 @@ +// +// TDPropertyValidator.m +// Adjust +// +// Created by Yangxiongon 2022/6/10. +// + +#import "TDPropertyValidator.h" +#import "NSString+TDProperty.h" +#import "TDPropertyDefaultValidator.h" + +@implementation TDPropertyValidator + +/// Custom attribute name format validation +static NSString *const kTANormalTrackProperNameValidateRegularExpression = @"^([a-zA-Z][a-zA-Z\\d_]*|\\#(ops_push_status|ops_push_id|ops_task_id|client_user_id|ops_trigger_time|ops_exp_group_id|ops_actual_push_time|ops_receipt_properties|ops_risk_type|rcc_pull_result))$"; +/// 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(TDPropertyValueValidating)]) { + 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:[[TDPropertyDefaultValidator alloc] init]]; +} + ++ (NSMutableDictionary *)validateProperties:(NSDictionary *)properties validator:(id)validator { + if (![properties isKindOfClass:[NSDictionary class]] || ![validator conformsToProtocol:@protocol(TDEventPropertyValidating)]) { + 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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.m.meta new file mode 100644 index 0000000..f4c43f2 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDPropertyValidator.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3bf426d0830804292bfa44163cde39d5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDValidatorProtocol.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDValidatorProtocol.h new file mode 100644 index 0000000..b3eca49 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDValidatorProtocol.h @@ -0,0 +1,45 @@ +// +// TDValidatorProtocol.h +// ThinkingSDK +// +// Created by Yangxiongon 2022/7/1. +// + +#ifndef TDValidatorProtocol_h +#define TDValidatorProtocol_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 TDPropertyValueValidating + +- (void)ta_validatePropertyValueWithError:(NSError **)error; + +@end + +/// The validator protocol of event properties, used to verify the key-value of a certain property +@protocol TDEventPropertyValidating + +- (void)ta_validateKey:(NSString *)key value:(id)value error:(NSError **)error; + +@end + +#endif /* TDValidatorProtocol_h */ diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDValidatorProtocol.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDValidatorProtocol.h.meta new file mode 100644 index 0000000..e5b2513 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/Property/Validate/TDValidatorProtocol.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bc9e5606226e945cbbec051ce90d733e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.h new file mode 100644 index 0000000..e1e5dbc --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.h @@ -0,0 +1,47 @@ +// +// 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 TDEventTracker; + +@interface TDEventTracker : 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; + +- (void)trackDebugEvent:(NSDictionary *)event; + +- (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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.h.meta new file mode 100644 index 0000000..fa2dccc --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 96e49283fc793452c9d7d6c1c2882bf3 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.m new file mode 100644 index 0000000..a40fa42 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.m @@ -0,0 +1,352 @@ +// +// TAEventTracker.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/19. +// + +#import "TDEventTracker.h" +#import "TDAnalyticsNetwork.h" +#import "TDEventRecord.h" +#import "TDConfigPrivate.h" + +#if TARGET_OS_IOS + +#if __has_include() +#import +#else +#import "TDNetworkReachability.h" +#endif + +#endif + +static dispatch_queue_t td_networkQueue; +static NSUInteger const kBatchSize = 50; +static NSURLSessionTask *g_currentTask = nil; + +@interface TDEventTracker () +@property (atomic, strong) TDAnalyticsNetwork *network; +@property (atomic, strong) TDConfig *config; +@property (atomic, strong) dispatch_queue_t queue; +@property (nonatomic, strong) TDSqliteDataQueue *dataQueue; +@property (atomic, assign) BOOL networkProcessing; + +@end + +@implementation TDEventTracker + ++ (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 instanceWithAppid:instanceToken].config; + self.network = [self generateNetworkWithConfig:self.config]; + self.dataQueue = [TDSqliteDataQueue sharedInstanceWithAppid:[self.config innerGetMapInstanceToken]]; + } + return self; +} + +- (TDAnalyticsNetwork *)generateNetworkWithConfig:(TDConfig *)config { + TDAnalyticsNetwork *network = [[TDAnalyticsNetwork alloc] init]; + network.appid = config.appid; + network.sessionDidReceiveAuthenticationChallenge = config.securityPolicy.sessionDidReceiveAuthenticationChallenge; + network.serverURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/sync", config.serverUrl]]; + network.serverDebugURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/data_debug", config.serverUrl]]; + network.securityPolicy = config.securityPolicy; + return network; +} + +//MARK: - Public + +- (void)track:(NSDictionary *)event immediately:(BOOL)immediately saveOnly:(BOOL)isSaveOnly { + TDMode mode = self.config.mode; + NSInteger count = 0; + if (mode == TDModeDebugOnly || mode == TDModeDebug) { + + if (isSaveOnly) { + return; + } + TDLogInfo(@"Enqueue data: %@", event); + dispatch_async(self.queue, ^{ + dispatch_async(td_networkQueue, ^{ + [self flushDebugEvent:event]; + }); + }); + // TDModeDebug 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 innerGetMapInstanceToken]]; + } + } else { + if (immediately) { + + if (isSaveOnly) { + return; + } + TDLogInfo(@"Enqueue data: %@", event); + dispatch_async(self.queue, ^{ + dispatch_async(td_networkQueue, ^{ + [self flushImmediately:event]; + }); + }); + } else { + TDLogInfo(@"Enqueue data: %@", event); + count = [self saveEventsData:event]; + } + } + if (count >= [self.config.uploadSize integerValue]) { + + if (isSaveOnly) { + return; + } + TDLogInfo(@"SDK flush success. The cache is full. count: %ld, uploadSize: %d", count, [self.config.uploadSize integerValue]); + [self flush]; + } +} + +- (void)trackDebugEvent:(NSDictionary *)event { + dispatch_async(self.queue, ^{ + dispatch_async(td_networkQueue, ^{ + [self.network flushDebugEvents:event appid:self.config.appid isDebugOnly:YES]; + }); + }); +} + +- (void)flushImmediately:(NSDictionary *)event { + TDLogInfo(@"SDK flush success. Immediately."); + [self.network flushEvents:@[event]]; +} + +- (NSInteger)saveEventsData:(NSDictionary *)data { + NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary:data]; + NSInteger count = 0; + @synchronized (TDSqliteDataQueue.class) { + + if (self.config.innerEnableEncrypt) { +#if TARGET_OS_IOS + NSDictionary *encryptData = [[ThinkingAnalyticsSDK instanceWithAppid:[self.config innerGetMapInstanceToken]].encryptManager encryptJSONObject:event]; + if (encryptData == nil) { + encryptData = event; + } + count = [self.dataQueue addObject:encryptData withAppid:[self.config innerGetMapInstanceToken]]; +#elif TARGET_OS_OSX + count = [self.dataQueue addObject:event withAppid:[self.config innerGetMapInstanceToken]]; +#endif + } else { + count = [self.dataQueue addObject:event withAppid:[self.config innerGetMapInstanceToken]]; + } + } + return count; +} + +- (void)flushDebugEvent:(NSDictionary *)event { + if (self.config.mode == TDModeDebug || self.config.mode == TDModeDebugOnly) { + BOOL isDebugOnly = self.config.mode == TDModeDebugOnly; + int debugResult = [self.network flushDebugEvents:event appid:self.config.appid isDebugOnly:isDebugOnly]; + if (debugResult == -1) { + // Downgrade + if (self.config.mode == TDModeDebug) { + dispatch_async(self.queue, ^{ + [self saveEventsData:event]; + }); + } else if (self.config.mode == TDModeDebugOnly) { + TDLogDebug(@"The data will be discarded due to this device is not allowed to debug:%@", event); + } + self.config.mode = TDModeNormal; + } + else if (debugResult == -2) { + TDLogDebug(@"Exception occurred when sending message to Server:%@", event); + if (self.config.mode == TDModeDebug) { + + 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 { + if (self.networkProcessing) { + return; + } + + 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 { +#if TARGET_OS_IOS + NSString *networkType = [[TDNetworkReachability shareInstance] networkState]; + if (!([self convertNetworkType:networkType] & [self.config getNetworkType])) { + if (completion) { + completion(); + } + return; + } +#endif + + NSArray *recordArray; + NSArray *recodIds; + NSArray *uuids; + @synchronized (TDSqliteDataQueue.class) { + + NSArray *records = [self.dataQueue getFirstRecords:kBatchSize withAppid:[self.config innerGetMapInstanceToken]]; + 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; + } + + self.networkProcessing = YES; + + BOOL flushSucc = YES; + int _maxStackCount = 0; + while (recordArray.count > 0 && uuids.count > 0 && flushSucc && _maxStackCount <= 100) { + @autoreleasepool { + flushSucc = [self.network flushEvents:recordArray]; + if (flushSucc) { + @synchronized (TDSqliteDataQueue.class) { + _maxStackCount ++; + BOOL ret = [self.dataQueue removeDataWithuids:uuids]; + if (!ret) { + break; + } + + NSArray *records = [self.dataQueue getFirstRecords:kBatchSize withAppid:[self.config innerGetMapInstanceToken]]; + 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 { + _maxStackCount = 0; + break; + } + } + } + if (completion) { + completion(); + } + + self.networkProcessing = NO; +} + +- (NSArray *)encryptEventRecords:(NSArray *)records { +#if TARGET_OS_IOS + NSMutableArray *encryptRecords = [NSMutableArray arrayWithCapacity:records.count]; + + TDEncryptManager *encryptManager = [ThinkingAnalyticsSDK instanceWithAppid:[self.config innerGetMapInstanceToken]].encryptManager; + + if (self.config.innerEnableEncrypt && 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, ^{}); +} + +- (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: - Setter & Getter + + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.m.meta new file mode 100644 index 0000000..c737eca --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TDEventTracker.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: de132dccb9e414af19f5261fa4b9b5fa +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration.meta new file mode 100644 index 0000000..81b4af7 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 42a60c1e385b74b2da608fb4a875b5f2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.h new file mode 100644 index 0000000..5d42800 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.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 TDTrackTimer : 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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.h.meta new file mode 100644 index 0000000..54daacd --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4341e200d79f546c689421e723d7cc4a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.m new file mode 100644 index 0000000..935afc2 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.m @@ -0,0 +1,150 @@ +// +// TATrackTimer.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/1. +// Copyright © 2022 thinking. All rights reserved. +// + +#import "TDTrackTimer.h" +#import "TDTrackTimerItem.h" +#import "TDLogging.h" + +#if __has_include() +#import +#else +#import "TDCoreDeviceInfo.h" +#endif + +@interface TDTrackTimer () +@property (nonatomic, strong) NSMutableDictionary *events; + +@end + +@implementation TDTrackTimer + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.events = [NSMutableDictionary dictionary]; + } + return self; +} + +- (void)trackEvent:(NSString *)eventName withSystemUptime:(NSTimeInterval)systemUptime { + if (!eventName.length) { + return; + } + TDTrackTimerItem *item = [[TDTrackTimerItem alloc] init]; + item.beginTime = systemUptime ?: [TDCoreDeviceInfo bootTime]; + @synchronized (self) { + self.events[eventName] = item; + } + TDLogInfo(@"time event success"); +} + +- (void)enterForegroundWithSystemUptime:(NSTimeInterval)systemUptime { + @synchronized (self) { + NSArray *keys = [self.events allKeys]; + for (NSString *key in keys) { + TDTrackTimerItem *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 { + @synchronized (self) { + NSArray *keys = [self.events allKeys]; + for (NSString *key in keys) { + TDTrackTimerItem *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; + } + TDTrackTimerItem *item = nil; + @synchronized (self) { + 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; + } + TDTrackTimerItem *item = nil; + @synchronized (self) { + 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 { + @synchronized (self) { + [self.events removeObjectForKey:eventName]; + } +} + +- (BOOL)isExistEvent:(NSString *)eventName { + BOOL result = NO; + @synchronized (self) { + result = self.events[eventName] != nil; + } + return result; +} + +- (void)clear { + @synchronized (self) { + [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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.m.meta new file mode 100644 index 0000000..b0fb54c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimer.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 22581b89ad23541399cb273d79c4cd59 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.h b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.h new file mode 100644 index 0000000..2acbdec --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.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 TDTrackTimerItem : 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/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.h.meta new file mode 100644 index 0000000..d8588bf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4e9cbb821670a4b9bbed0f25a7b3822d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.m b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.m new file mode 100644 index 0000000..9f97ab0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.m @@ -0,0 +1,17 @@ +// +// TATrackTimerItem.m +// ThinkingSDK +// +// Created by Yangxiongon 2022/6/1. +// Copyright © 2022 thinking. All rights reserved. +// + +#import "TDTrackTimerItem.h" + +@implementation TDTrackTimerItem + +-(NSString *)description { + return [NSString stringWithFormat:@"beginTime: %lf, foregroundDuration: %lf, enterBackgroundTime: %lf, backgroundDuration: %lf", _beginTime, _foregroundDuration, _enterBackgroundTime, _backgroundDuration];; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.m.meta new file mode 100644 index 0000000..a53ceb3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/EventTracker/TrackDuration/TDTrackTimerItem.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 48de2073ef1674bc191b3fc5c5262d00 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Exception.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Exception.meta new file mode 100644 index 0000000..de99cb0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Exception.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 424ad4c901eb74799ac16ee8ebcf5c5a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h new file mode 100644 index 0000000..5c5933e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h @@ -0,0 +1,15 @@ +#import + +#import "ThinkingAnalyticsSDKPrivate.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ThinkingExceptionHandler : NSObject + ++ (void)start; + ++ (void)trackCrashWithMessage:(NSString *)message; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h.meta new file mode 100644 index 0000000..e3d8c77 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 324500141046444d2b21f6b4a110accc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m new file mode 100644 index 0000000..ae941d0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m @@ -0,0 +1,223 @@ +#import "ThinkingExceptionHandler.h" +#include +#include +#import "TDLogging.h" +#import "TDAutoTrackManager.h" + +#if __has_include() +#import +#else +#import "TDCorePresetDisableConfig.h" +#endif + +static NSString * const TD_CRASH_REASON = @"#app_crashed_reason"; +static NSUInteger const TD_PROPERTY_CRASH_LENGTH_LIMIT = 8191*2; + +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; + +@interface ThinkingExceptionHandler () +@property (nonatomic) NSUncaughtExceptionHandler *td_lastExceptionHandler; +@property (nonatomic, unsafe_unretained) struct sigaction *td_signalHandlers; + +@end + +@implementation ThinkingExceptionHandler + ++ (void)start { + [self sharedHandler]; +} + ++ (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) { + _td_signalHandlers = calloc(NSIG, sizeof(struct sigaction)); + [self setupHandlers]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + // start APMStuck + Class cls = NSClassFromString(@"TAAPMStuckMonitor"); + if (cls && [cls respondsToSelector:@selector(shareInstance)]) { + id ins = [cls performSelector:@selector(shareInstance)]; + if (ins && [ins respondsToSelector:@selector(beginMonitor)]) { + [ins performSelector:@selector(beginMonitor)]; + } + } +#pragma clang diagnostic push + } + 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 { + NSDictionary *crashInfo = [ThinkingExceptionHandler crashInfoWithException:exception]; + TDAutoTrackEvent *crashEvent = [[TDAutoTrackEvent alloc] initWithName:TD_APP_CRASH_EVENT]; + [[TDAutoTrackManager sharedManager] trackWithEvent:crashEvent withProperties:crashInfo]; + + TDAutoTrackEvent *appEndEvent = [[TDAutoTrackEvent alloc] initWithName:TD_APP_END_EVENT]; + [[TDAutoTrackManager sharedManager] trackWithEvent:appEndEvent withProperties:nil]; + + dispatch_sync([ThinkingAnalyticsSDK sharedTrackQueue], ^{}); + dispatch_sync([ThinkingAnalyticsSDK sharedNetworkQueue], ^{}); + + NSSetUncaughtExceptionHandler(NULL); + for (int i = 0; i < sizeof(TDSignals) / sizeof(int); i++) { + signal(TDSignals[i], SIG_DFL); + } +} + ++ (void)trackCrashWithMessage:(NSString *)message { + NSDictionary *crashInfo = [ThinkingExceptionHandler crashInfoWithMessage:message]; + TDAutoTrackEvent *crashEvent = [[TDAutoTrackEvent alloc] initWithName:TD_APP_CRASH_EVENT]; + [[TDAutoTrackManager sharedManager] trackWithEvent:crashEvent withProperties:crashInfo]; +} + ++ (NSMutableDictionary *)crashInfoWithMessage:(NSString *)message { + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + + if ([TDCorePresetDisableConfig disableAppCrashedReason]) { + return properties; + } + + NSString *crashStr = message; + @try { + crashStr = [crashStr stringByReplacingOccurrencesOfString:@"\n" withString:@"
"]; + + NSUInteger strLength = [((NSString *)crashStr) lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + NSUInteger strMaxLength = TD_PROPERTY_CRASH_LENGTH_LIMIT; + if (strLength > strMaxLength) { + crashStr = [NSMutableString stringWithString:[ThinkingExceptionHandler limitString:crashStr withLength:strMaxLength - 1]]; + } + + [properties setValue:crashStr forKey:TD_CRASH_REASON]; + } @catch(NSException *exception) { + TDLogError(@"%@ error: %@", self, exception); + } + return properties; +} + ++ (NSMutableDictionary *)crashInfoWithException:(NSException *)exception { + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + + + if ([TDCorePresetDisableConfig 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 = TD_PROPERTY_CRASH_LENGTH_LIMIT; + if (strLength > strMaxLength) { + crashStr = [NSMutableString stringWithString:[ThinkingExceptionHandler 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; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m.meta new file mode 100644 index 0000000..a8812a3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Exception/ThinkingExceptionHandler.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 117c7c030e0b54e6e8f897168f54a42a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Hook.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook.meta new file mode 100644 index 0000000..a6f6ae8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1fad8194c321b459a87a1d406935f376 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.h new file mode 100644 index 0000000..55a67f3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.h @@ -0,0 +1,18 @@ + +#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 + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.h.meta new file mode 100644 index 0000000..8590660 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bf6b9bccbe81e45df980f2d62857723f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.m new file mode 100644 index 0000000..3b90390 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.m @@ -0,0 +1,43 @@ + +#import "NSObject+TDDelegateProxy.h" +#import + +#if TARGET_OS_IOS +#import +#endif + +@implementation NSObject (TDDelegateProxy) + +- (NSSet *)thinkingdata_optionalSelectors { + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setThinkingdata_optionalSelectors:(NSSet *)thinkingdata_optionalSelectors { + objc_setAssociatedObject(self, @selector(thinkingdata_optionalSelectors), thinkingdata_optionalSelectors, OBJC_ASSOCIATION_COPY); +} + +- (TDDelegateProxyObject *)thinkingdata_delegateObject { + return objc_getAssociatedObject(self, _cmd); +} + +- (void)setThinkingdata_delegateObject:(TDDelegateProxyObject *)thinkingdata_delegateObject { + objc_setAssociatedObject(self, @selector(thinkingdata_delegateObject), thinkingdata_delegateObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)thinkingdata_respondsToSelector:(SEL)aSelector { + if ([self thinkingdata_respondsToSelector:aSelector]) { + return YES; + } + if (@available(iOS 18.0, *)) { + char startOfHeader = (char)sel_getName(aSelector); + if (startOfHeader == '\x01') { + return NO; + } + } + if ([self.thinkingdata_optionalSelectors containsObject:NSStringFromSelector(aSelector)]) { + return YES; + } + return NO; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.m.meta new file mode 100644 index 0000000..f4adb9d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/NSObject+TDDelegateProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1443a480bd360400fbb0620d42807fd8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.h new file mode 100644 index 0000000..5a14c76 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/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/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.h.meta new file mode 100644 index 0000000..8b17caf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: abc2d6f428f894d56a694a776f137fc6 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.m new file mode 100644 index 0000000..ee58d2d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.m @@ -0,0 +1,210 @@ + +#import "TDDelegateProxy.h" +#import "TDLogging.h" + +#if __has_include() +#import +#else +#import "TDClassHelper.h" +#endif +#if __has_include() +#import +#else +#import "TDMethodHelper.h" +#endif +#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) { + TDLogInfo(@"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) { + TDLogInfo(@"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) { + TDLogInfo(@"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/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.m.meta new file mode 100644 index 0000000..8f43adb --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: df179f624e9fc47ec8d5df299227eba7 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.h new file mode 100644 index 0000000..76d5b16 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/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/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.h.meta new file mode 100644 index 0000000..603fba1 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9f695fe5c0b134075a398b6de1a0b0e0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.m new file mode 100644 index 0000000..63164d3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/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/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.m.meta new file mode 100644 index 0000000..c680fac --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Hook/TDDelegateProxyObject.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 98c147b3fa40d45539700d4a545ad01e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Logger.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Logger.meta new file mode 100644 index 0000000..7eb10d2 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Logger.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0120f127190314e89affebd1c9a54166 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.h new file mode 100644 index 0000000..033ede5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.h @@ -0,0 +1,27 @@ +#import +#import "TDConstant.h" + +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 (assign, nonatomic) TDLoggingLevel loggingLevel; + ++ (instancetype)sharedInstance; + +- (void)logCallingFunction:(TDLoggingLevel)type format:(id)messageFormat, ...; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.h.meta new file mode 100644 index 0000000..c9c161a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: d0672cd3af5d9412f878f36eacab6134 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.m new file mode 100644 index 0000000..48c3c9e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.m @@ -0,0 +1,54 @@ +#import "TDLogging.h" + +#if __has_include() +#import +#else +#import "TDOSLog.h" +#endif + +@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); + + TDLogType logType = TDLogTypeOff; + switch (type) { + case TDLoggingLevelNone: + logType = TDLogTypeOff; + break; + case TDLoggingLevelError: + logType = TDLogTypeError; + break; + case TDLoggingLevelWarning: + logType = TDLogTypeWarning; + break; + case TDLoggingLevelInfo: + logType = TDLogTypeInfo; + break; + case TDLoggingLevelDebug: + logType = TDLogTypeDebug; + break; + default: + logType = TDLogTypeOff; + break; + } + NSString *prefix = @"ThinkingData"; + [TDOSLog logMessage:formattedMessage prefix:prefix type:logType asynchronous:YES]; + } +} + +@end + diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.m.meta new file mode 100644 index 0000000..88e9273 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Logger/TDLogging.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c05e62825e23a4c159c5d4b8608a9e89 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Network.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Network.meta new file mode 100644 index 0000000..c36ad05 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Network.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a0ac6f354c3474862987da42fe4db1aa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.h new file mode 100644 index 0000000..3a6623a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.h @@ -0,0 +1,29 @@ +#import + +#import "ThinkingAnalyticsSDKPrivate.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^TDFlushConfigBlock)(NSDictionary *result, NSError * _Nullable error); + +@interface TDAnalyticsNetwork : NSObject + +@property (nonatomic, copy) NSString *appid; +@property (nonatomic, strong) NSURL *serverURL; + +@property (nonatomic, strong) NSURL *serverDebugURL; +@property (nonatomic, strong) TDSecurityPolicy *securityPolicy; +@property (nonatomic, copy) TDURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge; + +- (BOOL)flushEvents:(NSArray *)events; + +- (void)fetchRemoteConfig:(NSString *)appid handler:(TDFlushConfigBlock)handler; +- (int)flushDebugEvents:(NSDictionary *)record appid:(NSString *)appid isDebugOnly:(BOOL)isDebugOnly; +- (void)fetchIPMap; + ++ (void)enableDNSServcie:(NSArray *)services; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.h.meta new file mode 100644 index 0000000..2c85b13 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a542eb235a6bc440f9aebb7d13d953b5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.m new file mode 100644 index 0000000..99a8893 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.m @@ -0,0 +1,496 @@ +#import "TDAnalyticsNetwork.h" + +#if __has_include() +#import +#else +#import "NSData+TDGzip.h" +#endif +#if __has_include() +#import +#else +#import "TDJSONUtil.h" +#endif +#import "TDLogging.h" +#import "TDSecurityPolicy.h" +#import "TDAppState.h" + +#if __has_include() +#import +#else +#import "TDCoreDeviceInfo.h" +#endif + +#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"; + +static NSTimeInterval g_lastQueryDNSTime = 0; +static dispatch_queue_t g_queryDNSQueue = nil; +static NSArray *g_dnsServices = nil; +static NSMutableDictionary *g_dnsIpMap = nil; + +@interface TDAnalyticsNetwork () + +@property (atomic, assign) BOOL dnsServiceDegrade; + +@end + +@implementation TDAnalyticsNetwork + +- (NSURLSession *)sharedURLSession { + static dispatch_once_t onceToken; + static NSURLSession *sharedSession = nil; + dispatch_once(&onceToken, ^{ + 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 appid:(NSString *)appid isDebugOnly:(BOOL)isDebugOnly { + __block int debugResult = -1; + NSString *jsonString = [TDJSONUtil JSONStringForObject:record]; + NSMutableURLRequest *request = [self buildDebugRequestWithJSONString:jsonString appid:appid deviceId:[TDCoreDeviceInfo deviceId] isDebugOnly:isDebugOnly]; + 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) { + dispatch_semaphore_signal(flushSem); + return; + } + + NSDictionary *retDic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err]; + TDLogDebug(@"Send event, Response = %@", retDic); + + 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:%@", isDebugOnly ? @"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 { + if ([TDAnalyticsNetwork isEnableDNS]) { + self.dnsServiceDegrade = YES; + } + 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]; + TDLogDebug(@"Send event. %@\nRequest = %@", request.URL.absoluteString, record); + [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; + if (!data) { + dispatch_semaphore_signal(flushSem); + return; + } + id result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; + TDLogDebug(@"Send event, Response = %@", 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; + if ([TDAnalyticsNetwork isEnableDNS]) { + self.dnsServiceDegrade = YES; + } + 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]; + TDLogDebug(@"Send event. %@\nRequest = %@", request.URL.absoluteString, flushDic); + [task resume]; + dispatch_semaphore_wait(flushSem, DISPATCH_TIME_FOREVER); + return flushSucc; +} + ++ (void)enableDNSServcie:(NSArray *)services { + @synchronized (TDAnalyticsNetwork.class) { + g_dnsServices = [services copy]; + } + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + g_queryDNSQueue = dispatch_queue_create("cn.thinkingdata.analytics.queryDNS", DISPATCH_QUEUE_SERIAL); + }); +} + ++ (BOOL)isEnableDNS { + BOOL result = NO; + @synchronized (TDAnalyticsNetwork.class) { + if (g_dnsServices.count > 0) { + result = YES; + } + } + return result; +} + +- (void)fetchIPMap { + [self getDNSIps]; +} + +- (void)callbackNetworkErrorWithRequest:(NSString *)request error:(NSString *)error { + if (request == nil && error == nil) return; + + ThinkingAnalyticsSDK *tdSDK = [ThinkingAnalyticsSDK instanceWithAppid: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 formatURLWithOriginalUrl:self.serverURL]]; + [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; +} + +- (NSURL *)formatURLWithOriginalUrl:(NSURL *)url { + if (!url) { + return nil; + } + @synchronized (TDAnalyticsNetwork.class) { + if (!g_dnsServices || g_dnsServices.count <= 0) { + return url; + } + } + if (self.dnsServiceDegrade) { + return url; + } + NSString *ipStr = nil; + @synchronized (TDAnalyticsNetwork.class) { + ipStr = [g_dnsIpMap objectForKey:url.host]; + } + if ([ipStr isKindOfClass:NSString.class] && ipStr.length > 0) { + NSString *ipUrlString = [NSString stringWithFormat:@"%@://%@%@", url.scheme, ipStr, url.path]; + NSURL *serverUrl = [NSURL URLWithString:ipUrlString] ?: url; + return serverUrl; + } else { + [self getDNSIps]; + return url; + } +} + +- (NSMutableURLRequest *)buildDebugRequestWithJSONString:(NSString *)jsonString appid:(NSString *)appid deviceId:(NSString *)deviceId isDebugOnly:(BOOL)isDebugOnly { + // dryRun=0, if the verification is passed, it will be put into storage. dryRun=1, no storage + int dryRun = isDebugOnly ? 1 : 0; + NSString *appendParams = [NSString stringWithFormat:@"appid=%@&source=client&dryRun=%d&deviceId=%@", appid, dryRun, deviceId]; + TDLogDebug(@"RequestAppendParams: %@", appendParams); + NSString *postData = [NSString stringWithFormat:@"%@&data=%@", appendParams, [self URLEncode:jsonString]]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[self formatURLWithOriginalUrl: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(@"Get remote config failed:%@", error); + return; + } + NSError *err; + if (!data) { + return; + } + NSDictionary *ret = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&err]; + if (err) { + TDLogError(@"Get remote config error:%@", err); + } else if ([ret isKindOfClass:[NSDictionary class]] && [ret[@"code"] isEqualToNumber:[NSNumber numberWithInt:0]]) { + TDLogInfo(@"Get remote config for %@ : %@", appid, [ret objectForKey:@"data"]); + handler([ret objectForKey:@"data"], error); + } else { + TDLogError(@"Get 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; + + NSString *domain = challenge.protectionSpace.host; + + if ([TDAnalyticsNetwork isEnableDNS]) { + // is IP or not + if (![self.serverURL.host isEqualToString:domain] && ![self isDomainInDNSService:domain]) { + domain = [self getOriginHostWithIp:domain]; + if (domain == nil) { + domain = self.serverURL.host; + } + } + } + + if (self.sessionDidReceiveAuthenticationChallenge) { + disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential); + } else { + if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { + if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:domain]) { + credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; + if (credential) { + disposition = NSURLSessionAuthChallengeUseCredential; + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + } else { + disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; + } + } else { + disposition = NSURLSessionAuthChallengePerformDefaultHandling; + } + } + + if (completionHandler) { + completionHandler(disposition, credential); + } +} + +- (void)getDNSIps { + NSArray *dnsServices = nil; + @synchronized (TDAnalyticsNetwork.class) { + if (!g_dnsServices || g_dnsServices.count <= 0) { + return; + } + dnsServices = [g_dnsServices copy]; + // Throttle DNS network query. Period is 30s + NSTimeInterval nowTimeInterval = [[NSDate date] timeIntervalSince1970]; + if (nowTimeInterval - g_lastQueryDNSTime <= 30) { + return; + } else { + g_lastQueryDNSTime = nowTimeInterval; + } + } + NSString *serverHost = [self.serverURL host]; + dispatch_async(g_queryDNSQueue, ^{ + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + TDLogDebug(@"Parse DNS request is begining ..."); + for (TDDNSService dnsServiceUrl in dnsServices) { + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", dnsServiceUrl, serverHost]]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request setHTTPMethod:@"GET"]; + [request addValue:@"application/dns-json" forHTTPHeaderField:@"accept"]; + [request setTimeoutInterval:6]; + __block BOOL result = NO; + NSURLSessionDataTask *task = [[self sharedURLSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { + if (error == nil && data != nil && data.length > 0) { + NSError *jsonError = nil; + NSDictionary *dnsResult = nil; + @try { + dnsResult = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError]; + } @catch (NSException *exception) { + + } + if (jsonError == nil && [dnsResult isKindOfClass:NSDictionary.class]) { + NSString *ipStr = nil; + NSArray *answer = [dnsResult objectForKey:@"Answer"]; + if (answer && [answer isKindOfClass:[NSArray class]]) { + NSDictionary *dnsObj = [answer lastObject]; + if (dnsObj && [dnsObj isKindOfClass:[NSDictionary class]]) { + ipStr = [dnsObj objectForKey:@"data"]; + } + } + if (ipStr && [ipStr isKindOfClass:[NSString class]]) { + result = YES; + @synchronized (TDAnalyticsNetwork.class) { + if (g_dnsIpMap == nil) { + g_dnsIpMap = [NSMutableDictionary dictionary]; + } + [g_dnsIpMap setObject:ipStr forKey:serverHost]; + } + } + } + } else { + TDLogError(@"Parse DNS error: %@", error.localizedDescription); + } + dispatch_semaphore_signal(semaphore); + }]; + TDLogDebug(@"Parse DNS request: %@", request.URL.absoluteString); + [task resume]; + dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC))); + TDLogDebug(@"Parse DNS response: %@. Service url: %@", result ? @"success" : @"failed", request.URL.absoluteString); + if (result) { + @synchronized (TDAnalyticsNetwork.class) { + TDLogDebug(@"Parse DNS is end. %@", g_dnsIpMap); + } + break; + } + } + }); +} + +- (BOOL)isDomainInDNSService:(NSString *)domain { + NSArray *dNSServices = @[TDDNSServiceCloudFlare, TDDNSServiceCloudALi, TDDNSServiceCloudGoogle]; + for (TDDNSService dnsServiceUrl in dNSServices) { + if ([dnsServiceUrl containsString:domain]) { + return YES; + } + } + return NO; +} + +- (NSString *)getOriginHostWithIp:(NSString *)ip { + if ([ip isKindOfClass:NSString.class] && ip.length <= 0) { + return nil; + } + __block NSString *originHost = nil; + @synchronized (TDAnalyticsNetwork.class) { + [g_dnsIpMap enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull obj, BOOL * _Nonnull stop) { + if ([obj isKindOfClass:[NSString class]] && [obj containsString:ip]) { + originHost = key; + *stop = YES; + } + }]; + } + return originHost; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.m.meta new file mode 100644 index 0000000..d41d92e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDAnalyticsNetwork.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 15979de9edb0f4a7da257667abe7a19f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h new file mode 100644 index 0000000..f15a3c6 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h.meta new file mode 100644 index 0000000..58808da --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 9781dc44aea76461dbad775986c949f1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m new file mode 100644 index 0000000..c0f61af --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m.meta new file mode 100644 index 0000000..1e726c6 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Network/TDSecurityPolicy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 11369661a3b6e47e9a9b7c0607c9db2c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty.meta new file mode 100644 index 0000000..0991d1d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ca77b616b13e34ddbbc5ae27a6a510ed +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.h b/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.h new file mode 100644 index 0000000..5a87b9f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.h @@ -0,0 +1,79 @@ +// +// TDPresetProperties.h +// ThinkingSDK +// +// Created by huangdiao on 2021/5/25. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface TDPresetProperties : NSObject + +/// app bundle id +@property (nonatomic, copy, readonly) NSString *bundle_id; + +/// Mobile phone SIM card operator information. The value is null after ios 16 +@property (nonatomic, copy, readonly) NSString *carrier; + +/// Device id +@property (nonatomic, copy, readonly) NSString *device_id; + +/// Device model +@property (nonatomic, copy, readonly) NSString *device_model; + +/// Device manufacture +@property (nonatomic, copy, readonly) NSString *manufacturer; + +/// Network type +@property (nonatomic, copy, readonly) NSString *network_type; + +/// Operating system name +@property (nonatomic, copy, readonly) NSString *os; + +/// Operating system version +@property (nonatomic, copy, readonly) NSString *os_version; + +/// screen height +@property (nonatomic, strong, readonly) NSNumber *screen_height; + +/// screen width +@property (nonatomic, strong, readonly) NSNumber *screen_width; + +/// Mobile phone system language +@property (nonatomic, copy, readonly) NSString *system_language; + +/// Time zone offset +@property (nonatomic, copy, readonly) NSNumber *zone_offset; + +/// App version +@property (nonatomic, copy, readonly) NSString *appVersion; + +/// App install time +@property (nonatomic, copy, readonly) NSString *install_time; + +/// Is it a simulator +@property (nonatomic, strong, readonly) NSNumber *isSimulator; + +/// Available memory and total memory +@property (nonatomic, copy, readonly) NSString *ram; + +/// Available disk and total disk +@property (nonatomic, copy, readonly) NSString *disk; + +/// Frame rate +@property (nonatomic, strong, readonly) NSNumber *fps; + +/// Device type +@property (nonatomic, copy, readonly) NSString *deviceType; + +/** + * 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/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.h.meta new file mode 100644 index 0000000..eea1668 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8765a67cf93f8484583bbd6257fffde5 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.m b/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.m new file mode 100644 index 0000000..1edf24e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.m @@ -0,0 +1,146 @@ +// +// TDPresetProperties.m +// ThinkingSDK +// +// Created by huangdiao on 2021/5/25. +// Copyright © 2021 thinkingdata. All rights reserved. +// + +#import "TDPresetProperties.h" + +static const NSString *kTDPresetBundleId = @"#bundle_id"; +static const NSString *kTDPresetCarrier = @"#carrier"; +static const NSString *kTDPresetDeviceId = @"#device_id"; +static const NSString *kTDPresetDeviceModel = @"#device_model"; +static const NSString *kTDPresetManufacturer = @"#manufacturer"; +static const NSString *kTDPresetNetworkType = @"#network_type"; +static const NSString *kTDPresetOSName = @"#os"; +static const NSString *kTDPresetOSVersion = @"#os_version"; +static const NSString *kTDPresetScreenHeight = @"#screen_height"; +static const NSString *kTDPresetScreenWidth = @"#screen_width"; +static const NSString *kTDPresetSystemLanguage = @"#system_language"; +static const NSString *kTDPresetZoneOffset = @"#zone_offset"; +static const NSString *kTDPresetAppVersion = @"#app_version"; +static const NSString *kTDPresetInstallTime = @"#install_time"; +static const NSString *kTDPresetIsSimulator = @"#simulator"; +static const NSString *kTDPresetRam = @"#ram"; +static const NSString *kTDPresetDisk = @"#disk"; +static const NSString *kTDPresetFps = @"#fps"; +static const NSString *kTDPresetDeviceType = @"#device_type"; + +@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, strong, readwrite) NSNumber *screen_height; +@property (nonatomic, strong, readwrite) NSNumber *screen_width; +@property (nonatomic, copy, readwrite) NSString *system_language; +@property (nonatomic, copy, readwrite) NSNumber *zone_offset; +@property (nonatomic, copy, readwrite) NSString *appVersion; +@property (nonatomic, copy, readwrite) NSString *install_time; +@property (nonatomic, strong, readwrite) NSNumber *isSimulator; +@property (nonatomic, copy, readwrite) NSString *ram; +@property (nonatomic, copy, readwrite) NSString *disk; +@property (nonatomic, strong, readwrite) NSNumber *fps; +@property (nonatomic, copy, readwrite) NSString *deviceType; + +@end + +@implementation TDPresetProperties + +- (instancetype)initWithDictionary:(NSDictionary *)dict { + self = [super init]; + if (self) { + self.bundle_id = dict[kTDPresetBundleId]; + self.carrier = dict[kTDPresetCarrier]; + self.device_id = dict[kTDPresetDeviceId]; + self.device_model = dict[kTDPresetDeviceModel]; + self.manufacturer = dict[kTDPresetManufacturer]; + self.network_type = dict[kTDPresetNetworkType]; + self.os = dict[kTDPresetOSName]; + self.os_version = dict[kTDPresetOSVersion]; + self.screen_height = dict[kTDPresetScreenHeight]; + self.screen_width = dict[kTDPresetScreenWidth]; + self.system_language = dict[kTDPresetSystemLanguage]; + self.zone_offset = dict[kTDPresetZoneOffset]; + self.appVersion = dict[kTDPresetAppVersion]; + self.install_time = dict[kTDPresetInstallTime]; + self.isSimulator = dict[kTDPresetIsSimulator]; + self.ram = dict[kTDPresetRam]; + self.disk = dict[kTDPresetDisk]; + self.fps = dict[kTDPresetFps]; + self.deviceType = dict[kTDPresetDeviceType]; + } + return self; +} + +- (NSDictionary *)toEventPresetProperties { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + if (self.bundle_id) { + dict[kTDPresetBundleId] = self.bundle_id; + } + if (self.carrier) { + dict[kTDPresetCarrier] = self.carrier; + } + if (self.device_id) { + dict[kTDPresetDeviceId] = self.device_id; + } + if (self.device_model) { + dict[kTDPresetDeviceModel] = self.device_model; + } + if (self.manufacturer) { + dict[kTDPresetManufacturer] = self.manufacturer; + } + if (self.network_type) { + dict[kTDPresetNetworkType] = self.network_type; + } + if (self.os) { + dict[kTDPresetOSName] = self.os; + } + if (self.os_version) { + dict[kTDPresetOSVersion] = self.os_version; + } + if (self.screen_height) { + dict[kTDPresetScreenHeight] = self.screen_height; + } + if (self.screen_width) { + dict[kTDPresetScreenWidth] = self.screen_width; + } + if (self.system_language) { + dict[kTDPresetSystemLanguage] = self.system_language; + } + if (self.zone_offset) { + dict[kTDPresetZoneOffset] = self.zone_offset; + } + if (self.appVersion) { + dict[kTDPresetAppVersion] = self.appVersion; + } + if (self.install_time) { + dict[kTDPresetInstallTime] = self.install_time; + } + if (self.isSimulator) { + dict[kTDPresetIsSimulator] = self.isSimulator; + } + if (self.ram) { + dict[kTDPresetRam] = self.ram; + } + if (self.disk) { + dict[kTDPresetDisk] = self.disk; + } + if (self.fps) { + dict[kTDPresetFps] = self.fps; + } + if (self.deviceType) { + dict[kTDPresetDeviceType] = self.deviceType; + } + + return dict; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.m.meta new file mode 100644 index 0000000..1983aff --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/PresetProperty/TDPresetProperties.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c0b4133a7594d4fa1bcfc4e24177ea4e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Store.meta new file mode 100644 index 0000000..9d8ca71 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: de79325140491402e88158d3e68becea +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.h new file mode 100644 index 0000000..9cfb65a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/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/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.h.meta new file mode 100644 index 0000000..8403da8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f0fa3872576744e84bf96137f346eefd +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.m new file mode 100644 index 0000000..20137e2 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.m @@ -0,0 +1,101 @@ +// +// TDEventRecord.m +// ThinkingSDK +// +// Created by wwango on 2022/1/24. +// + +#import "TDEventRecord.h" + +#if __has_include() +#import +#else +#import "TDJSONUtil.h" +#endif + +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/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.m.meta new file mode 100644 index 0000000..2977112 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDEventRecord.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 656854da589404714ba6e708de4aacce +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h new file mode 100644 index 0000000..2f756da --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h.meta new file mode 100644 index 0000000..73e984b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: edc5e228d7fc2439a826a347e3f63e4f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m new file mode 100644 index 0000000..e7355da --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m @@ -0,0 +1,287 @@ +// +// TDFile.m +// ThinkingSDK +// +// Created by LiHuanan on 2020/9/8. +// Copyright © 2020 thinkingdata. All rights reserved. +// + +#import "TDFile.h" +#import "TDLogging.h" +#if __has_include() +#import +#else +#import "TDJSONUtil.h" +#endif + +@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/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m.meta new file mode 100644 index 0000000..e2d777d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDFile.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4b3d46619dada4e3fbbf81943c05f348 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h new file mode 100644 index 0000000..e87814d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h @@ -0,0 +1,8 @@ +#import + +@interface TDKeychainHelper : NSObject + ++ (void)saveInstallTimes:(NSString *)string; ++ (NSString *)readInstallTimes; + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h.meta new file mode 100644 index 0000000..3ab157f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 73497cc4e0a42458da46d1a0ad6013f1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m new file mode 100644 index 0000000..590f329 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m @@ -0,0 +1,36 @@ +#import "TDKeychainHelper.h" + +#if __has_include() +#import +#else +#import "TDKeychainManager.h" +#endif + +static NSString * const TDInstallTimesOld = @"com.thinkingddata.analytics.installtimes"; +static NSString * const TDInstallTimesNew = @"com.thinkingddata.analytics.installtimes_1"; + +@interface TDKeychainHelper () + +@end + +@implementation TDKeychainHelper + ++ (void)saveInstallTimes:(NSString *)string { + [TDKeychainManager saveItem:string forKey:TDInstallTimesNew]; + + // Compatibility handles the case of jumping back and forth between old and new SDK versions + [TDKeychainManager oldSaveItem:string forKey:TDInstallTimesOld]; +} + ++ (NSString *)readInstallTimes { + NSString *data = [TDKeychainManager itemForKey:TDInstallTimesNew]; + if (data == nil) { + data = [TDKeychainManager oldItemForKey:TDInstallTimesOld]; + if (data) { + [TDKeychainManager saveItem:data forKey:TDInstallTimesNew]; + } + } + return data; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m.meta new file mode 100644 index 0000000..82ad8f4 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDKeychainHelper.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b93c3eb20b626476baf8b47dbec8bf2e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h new file mode 100644 index 0000000..7b86803 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h.meta new file mode 100644 index 0000000..9a1788a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: e7000d0d7ca0c4b6185f8ea0d2719dc2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m new file mode 100644 index 0000000..3c0fbd5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m @@ -0,0 +1,363 @@ +#import "TDSqliteDataQueue.h" +#import + +#import "TDLogging.h" +#if __has_include() +#import +#else +#import "TDJSONUtil.h" +#endif +#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]; + }); + 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; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow: -oneDay * [TDConfig expirationDays]]; +#pragma clang diagnostic pop + + int expirationDate = [date timeIntervalSince1970]; + [self removeOldRecords:expirationDate]; +} + +- (NSInteger)addObject:(id)obj withAppid:(NSString *)appid { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + NSUInteger maxCacheSize = [TDConfig maxNumEvents]; +#pragma clang diagnostic pop + + 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; + } + + @try { + 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]]; + } + } + } @catch (NSException *exception) { + + } + } + } + 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/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m.meta new file mode 100644 index 0000000..849a461 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Store/TDSqliteDataQueue.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fcb464e18d497453b9d760cdc73d5a7a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Toast.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Toast.meta new file mode 100644 index 0000000..7ce02d3 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Toast.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9c37151661ecf42d3be5b0e33e5405c0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.h new file mode 100644 index 0000000..d42b0c5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/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/Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.h.meta new file mode 100644 index 0000000..ce4263a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c9cf0253f00c8488ea65bf03a71f3d28 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.m new file mode 100644 index 0000000..f92a475 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/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/Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.m.meta new file mode 100644 index 0000000..9bd55bb --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Toast/TDToastView.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 43699f8778c624c46bc5afbb602485d0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util.meta new file mode 100644 index 0000000..7d75670 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 25157fdc2a1044168862c6c03f435462 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category.meta new file mode 100644 index 0000000..9aa4aac --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b37051d2565fd43dfba2108cdecdd66f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h new file mode 100644 index 0000000..56f9f68 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h.meta new file mode 100644 index 0000000..3ce5724 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6dcaf30c4b0eb430b8244e993dc551a4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m new file mode 100644 index 0000000..759620d --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m.meta new file mode 100644 index 0000000..2796bb7 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSDictionary+TDJsonOutput.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 53a00c2672fe24a528f94803871d160f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h new file mode 100644 index 0000000..b60ab65 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h.meta new file mode 100644 index 0000000..43eb311 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1ab5993d5baaf4457b1b7f166e9bbbe1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m new file mode 100644 index 0000000..584c5e4 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m @@ -0,0 +1,187 @@ +// +// 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 +#elif TARGET_OS_OSX +#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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m.meta new file mode 100644 index 0000000..223a86a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Category/NSObject+TDUtil.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4bc8c4ead3507483e85343120a67c2cf +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h new file mode 100644 index 0000000..59eca16 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h.meta new file mode 100644 index 0000000..e1aa12b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3174e763d579e48f885f7bbf52454067 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m new file mode 100644 index 0000000..2bb5f8d --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m.meta new file mode 100644 index 0000000..456e875 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCheck.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 956605faf624745158ad0a460ca710b9 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h new file mode 100644 index 0000000..214daa8 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h.meta new file mode 100644 index 0000000..60bcc16 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: cdbf44bbd4cb144ff979af417b5774cf +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m new file mode 100644 index 0000000..5116c68 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m.meta new file mode 100644 index 0000000..d50e166 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDCommonUtil.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 646b3aad38f76494ba9bbbb79ce88007 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h new file mode 100644 index 0000000..5e9a897 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h.meta new file mode 100644 index 0000000..b949e41 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4b3d4c8f9a5e94ad2a1cbc415bf27aef +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m new file mode 100644 index 0000000..db9c0f5 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m.meta new file mode 100644 index 0000000..ff4bb97 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDSDKUtil.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a56c0e17f9b4643af98c0a4f64548a75 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h new file mode 100644 index 0000000..2abb6c5 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h.meta new file mode 100644 index 0000000..25581bf --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 26287937f0ac74abeaed5f1da5b092ed +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m new file mode 100644 index 0000000..81384d2 --- /dev/null +++ b/Assets/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/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m.meta new file mode 100644 index 0000000..6628f78 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/TDWeakProxy.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a6edacb4206a84710b119b660398b43d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.h b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.h new file mode 100644 index 0000000..8bf6d3a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.h @@ -0,0 +1,36 @@ +// +// Target_Analytics.h +// ThinkingSDK +// +// Created by 杨雄 on 2024/3/18. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface Target_Analytics : NSObject + +- (void)Action_nativeInitWithParams:(nullable NSDictionary *)params; + +- (nullable NSString *)Action_nativeGetAccountIdWithParams:(nullable NSDictionary *)params; + +- (nullable NSString *)Action_nativeGetDistinctIdWithParams:(nullable NSDictionary *)params; + +- (void)Action_nativeTrackEventWithParams:(nullable NSDictionary *)params; + +- (void)Action_nativeUserSetWithParams:(nullable NSDictionary *)params; + +- (nullable NSDictionary *)Action_nativeGetPresetPropertiesWithParams:(nullable NSDictionary *)params; + +- (void)Action_nativeTrackDebugEventWithParams:(nullable NSDictionary *)params; + +- (BOOL)Action_nativeGetEnableAutoPushWithParams:(nullable NSDictionary *)params; + +- (nullable NSArray *)Action_nativeGetAllAppIdsWithParams:(nullable NSDictionary *)params; + +- (nullable NSString *)Action_nativeGetSDKVersionWithParams:(nullable NSDictionary *)params; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.h.meta new file mode 100644 index 0000000..4dff872 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: ae6cef6c0d4a9421d97059c75673c5f2 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.m b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.m new file mode 100644 index 0000000..063ff96 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.m @@ -0,0 +1,121 @@ +// +// Target_Analytics.m +// ThinkingSDK +// +// Created by 杨雄 on 2024/3/18. +// + +#import "Target_Analytics.h" +#import "TDAnalytics+Public.h" +#import "TDAnalytics+Multiple.h" +#import "TDAnalytics+Private.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDAnalyticsPresetProperty.h" + +#if __has_include() +#import +#else +#import "ThinkingDataCore.h" +#endif + +@implementation Target_Analytics + +- (void)Action_nativeInitWithParams:(NSDictionary *)params { + TDSettings *settings = params[@"settings"]; + if (![settings isKindOfClass:TDSettings.class]) { + return; + } + [TDAnalytics enableLog:settings.enableLog]; + + TDConfig *config = [[TDConfig alloc] init]; + config.appid = settings.appId; + config.serverUrl = settings.serverUrl; + + TDMode mode = TDModeNormal; + switch (settings.mode) { + case TDSDKModeNomal:{ + mode = TDModeNormal; + } break; + case TDSDKModeDebug:{ + mode = TDModeDebug; + } break; + case TDSDKModeDebugOnly:{ + mode = TDModeDebugOnly; + } break; + default: + break; + } + config.mode = mode; + config.appid = settings.appId; + config.defaultTimeZone = settings.defaultTimeZone; + + if (![NSString td_isEmpty:settings.encryptKey]) { + [config enableEncryptWithVersion:settings.encryptVersion publicKey:settings.encryptKey]; + } + + config.enableAutoPush = settings.enableAutoPush; + config.enableAutoCalibrated = settings.enableAutoCalibrated; + [TDAnalytics startAnalyticsWithConfig:config]; +} + +- (nullable NSString *)Action_nativeGetAccountIdWithParams:(nullable NSDictionary *)params { + NSString *appId = params[@"appId"]; + NSString *accountId = [[ThinkingAnalyticsSDK instanceWithAppid:appId] innerAccountId]; + return accountId; +} + +- (nullable NSString *)Action_nativeGetDistinctIdWithParams:(nullable NSDictionary *)params { + NSString *appId = params[@"appId"]; + NSString *distinctId = [TDAnalytics getDistinctIdWithAppId:appId]; + return distinctId; +} + +- (void)Action_nativeTrackEventWithParams:(nullable NSDictionary *)params { + NSString *appId = params[@"appId"]; + NSString *eventName = params[@"eventName"]; + NSDictionary *properties = params[@"properties"]; + if ([eventName isKindOfClass:NSString.class] && eventName.length > 0) { + [TDAnalytics track:eventName properties:properties withAppId:appId]; + [TDAnalytics flushWithAppId:appId]; + } +} + +- (void)Action_nativeUserSetWithParams:(nullable NSDictionary *)params { + NSString *appId = params[@"appId"]; + NSDictionary *properties = params[@"properties"]; + if ([properties isKindOfClass:NSDictionary.class] && properties.count > 0) { + [TDAnalytics userSet:properties withAppId:appId]; + [TDAnalytics flushWithAppId:appId]; + } +} + +- (nullable NSDictionary *)Action_nativeGetPresetPropertiesWithParams:(nullable NSDictionary *)params { + NSString *appId = params[@"appId"]; + NSDictionary *dict = [TDAnalyticsPresetProperty propertiesWithAppId:appId]; + return dict; +} + +- (void)Action_nativeTrackDebugEventWithParams:(NSDictionary *)params { + NSString *appId = params[@"appId"]; + NSString *eventName = params[@"eventName"]; + NSDictionary *properties = params[@"properties"]; + if ([eventName isKindOfClass:NSString.class] && eventName.length > 0) { + [TDAnalytics trackDebug:eventName properties:properties appId:appId]; + } +} + +- (BOOL)Action_nativeGetEnableAutoPushWithParams:(NSDictionary *)params { + NSString *appId = params[@"appId"]; + return [[[ThinkingAnalyticsSDK instanceWithAppid:appId] config] enableAutoPush]; +} + +- (NSArray *)Action_nativeGetAllAppIdsWithParams:(NSDictionary *)params { + NSDictionary *instances = [ThinkingAnalyticsSDK _getAllInstances]; + return instances.allKeys; +} + +- (NSString *)Action_nativeGetSDKVersionWithParams:(NSDictionary *)params { + return [TDAnalytics getSDKVersion]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.m.meta new file mode 100644 index 0000000..ff1a9ab --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/Util/Target_Analytics.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: bd615af26aad4450a9939e7c52b8e60d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main.meta new file mode 100644 index 0000000..c644734 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e6e61b2e4e2f1466785d1454a550de40 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m b/Assets/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m new file mode 100644 index 0000000..bed17b9 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m @@ -0,0 +1,90 @@ +#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)innerLogin:(NSString *)accountId { + if ([self hasDisabled]) { + return; + } + if (![accountId isKindOfClass:[NSString class]] || accountId.length == 0) { + TDLogError(@"accountId invald", accountId); + return; + } + TDLogInfo(@"light SDK login, SDK Name = %@, AccountId = %@", self.config.name, accountId); + self.accountId = accountId; +} + +- (void)innerLogout { + if ([self hasDisabled]) { + return; + } + TDLogInfo(@"light SDK logout."); + self.accountId = nil; +} + +- (void)innerSetIdentify:(NSString *)distinctId { + if ([self hasDisabled]) { + return; + } + if (![distinctId isKindOfClass:[NSString class]] || distinctId.length == 0) { + TDLogError(@"identify cannot null"); + return; + } + + TDLogInfo(@"light SDK set distinct ID, Distinct Id = %@", distinctId); + + self.identifyId = distinctId; +} + ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType withAppId:(NSString *)appId { + return; +} + ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType callback:(NSDictionary * _Nonnull (^)(TDAutoTrackEventType, NSDictionary * _Nonnull))callback withAppId:(NSString *)appId { + return; +} ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType properties:(NSDictionary *)properties withAppId:(NSString *)appId { + return; +} + +- (void)innerFlush { + return; +} + +#pragma mark - EnableTracking + +- (void)innerSetTrackStatus: (TDTrackStatus)status { + switch (status) { + case TDTrackStatusPause: { + TDLogInfo(@"light instance [%@] change status to Pause", self.config.name) + self.isEnabled = NO; + break; + } + case TDTrackStatusStop: { + TDLogInfo(@"light instance [%@] change status to Stop", self.config.name) + self.isEnabled = NO; + break; + } + case TDTrackStatusSaveOnly: { + TDLogInfo(@"light instance [%@] change status to SaveOnly", self.config.name) + self.isEnabled = YES; + break; + } + case TDTrackStatusNormal: { + TDLogInfo(@"light instance [%@] change status to Normal", self.config.name) + self.isEnabled = YES; + break; + } + default: + break; + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m.meta new file mode 100644 index 0000000..a20c310 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/LightThinkingAnalyticsSDK.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6ea8076903d3c4e979c5765a2dfc7c59 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.h new file mode 100644 index 0000000..62de478 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.h @@ -0,0 +1,284 @@ +// +// TDAnalytics+Multiple.h +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/17. +// + +#if __has_include() +#import +#else +#import "TDAnalytics.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAnalytics (Multiple) + +//MARK: track + +/** + 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. + @param appId appId + */ ++ (void)flushWithAppId:(NSString * _Nullable)appId; + +/** + Switch reporting status + @param status TDTrackStatus reporting status + @param appId appId + */ ++ (void)setTrackStatus:(TDTrackStatus)status withAppId:(NSString * _Nullable)appId; + +/** + Track Events + @param eventName event name + @param appId appId + */ ++ (void)track:(NSString *)eventName withAppId:(NSString * _Nullable)appId; +/** + Track Events + @param eventName event name + @param properties event properties + @param appId appId + */ ++ (void)track:(NSString *)eventName properties:(nullable NSDictionary *)properties withAppId:(NSString * _Nullable)appId; +/** + Track Events + @param eventName event name + @param properties event properties + @param time event trigger time + @param timeZone event trigger time time zone + @param appId appId + */ ++ (void)track:(NSString *)eventName properties:(nullable NSDictionary *)properties time:(NSDate *)time timeZone:(NSTimeZone *)timeZone withAppId:(NSString * _Nullable)appId; +/** + Track Events + @param eventModel event Model + @param appId appId + */ ++ (void)trackWithEventModel:(TDEventModel *)eventModel withAppId:(NSString * _Nullable)appId; +/** + 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. + @param appId appId + */ ++ (void)timeEvent:(NSString *)eventName withAppId:(NSString * _Nullable)appId; + + +//MARK: user property + +/** + Sets the user property, replacing the original value with the new value if the property already exists. + @param properties user properties + @param appId appId + */ ++ (void)userSet:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId; +/** + Sets a single user attribute, ignoring the new attribute value if the attribute already exists. + @param properties user properties + @param appId appId + */ ++ (void)userSetOnce:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId; +/** + Reset single user attribute. + @param propertyName user properties + @param appId appId + */ ++ (void)userUnset:(NSString *)propertyName withAppId:(NSString * _Nullable)appId; +/** + Reset user properties. + @param propertyNames user properties +*/ ++ (void)userUnsets:(NSArray *)propertyNames withAppId:(NSString * _Nullable)appId; +/** + Adds the numeric type user attributes. + @param properties user properties + @param appId appId + */ ++ (void)userAdd:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId; +/** + Adds the numeric type user attribute. + @param propertyName propertyName + @param propertyValue propertyValue + @param appId appId + */ ++ (void)userAddWithName:(NSString *)propertyName andValue:(NSNumber *)propertyValue withAppId:(NSString * _Nullable)appId; +/** + Appends an element to a property of an array type. + @param properties user properties + @param appId appId + */ ++ (void)userAppend:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId; +/** + Appends an element to a property of an array type. It filters out elements that already exist. + @param properties user properties +*/ ++ (void)userUniqAppend:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId; +/** + Delete the user attributes. This operation is not reversible and should be performed with caution. + @param appId appId + */ ++ (void)userDeleteWithAppId:(NSString * _Nullable)appId; + + +//MARK: super property & preset property + +/** + 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. + @param properties super properties + @param appId appId + */ ++ (void)setSuperProperties:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId; +/** + Clears a public event attribute. + @param property property name + @param appId appId + */ ++ (void)unsetSuperProperty:(NSString *)property withAppId:(NSString * _Nullable)appId; +/** + Clear all public event attributes. + @param appId appId + */ ++ (void)clearSuperPropertiesWithAppId:(NSString * _Nullable)appId; +/** + Get the public event properties that have been set. + + @return super properties that have been set. + @param appId appId + */ ++ (NSDictionary *)getSuperPropertiesWithAppId:(NSString * _Nullable)appId; +/** + Set dynamic public properties. Each event uploaded after that will contain a public event attribute. + @param propertiesHandler propertiesHandler. + @param appId appId + */ ++ (void)setDynamicSuperProperties:(NSDictionary *(^)(void))propertiesHandler withAppId:(NSString * _Nullable)appId; +/** + Get the SDK's preset properties. + + @return preset property object + @param appId appId + */ ++ (TDPresetProperties *)getPresetPropertiesWithAppId:(NSString * _Nullable)appId; + +//MARK: error callback + +/** + Register TD error callback + + @param errorCallback code = 10001, ext = "string or json string", errorMsg = "error" + @param appId appId + */ ++ (void)registerErrorCallback:(void(^)(NSInteger code, NSString * _Nullable errorMsg, NSString * _Nullable ext))errorCallback withAppId:(NSString * _Nullable)appId; + + +//MARK: custom property + +/** + Set the distinct ID to replace the default UUID distinct ID. + @param distinctId distinctId + @param appId appId + */ ++ (void)setDistinctId:(NSString *)distinctId withAppId:(NSString * _Nullable)appId; + +/** + Get distinct ID: The #distinct_id value in the reported data. + + @param appId appId + @return distinctId + */ ++ (NSString *)getDistinctIdWithAppId:(NSString * _Nullable)appId; + +/** + Set the account ID. Each setting overrides the previous value. Login events will not be uploaded. + @param accountId accountId + @param appId appId + */ ++ (void)login:(NSString *)accountId withAppId:(NSString * _Nullable)appId; + +/** + Get account ID: The #account_id value in the reported data. + + @param appId appId + @return accountId + */ ++ (NSString *)getAccountIdWithAppId:(NSString * _Nullable)appId; + +/** + Clearing the account ID will not upload user logout events. + @param appId appId + */ ++ (void)logoutWithAppId:(NSString * _Nullable)appId; + +/** + Set the network conditions for uploading. By default, the SDK will set the network conditions as 3G, 4G and Wifi to upload data + @param type network type + @param appId appId + */ ++ (void)setUploadingNetworkType:(TDReportingNetworkType)type withAppId:(NSString * _Nullable)appId; + +/// Format the time output in the format of SDK +/// @param date date +/// @param appId appId +/// @return date string ++ (NSString *)timeStringWithDate:(NSDate *)date withAppId:(NSString * _Nullable)appId; + +#if TARGET_OS_IOS + +/** + Enable Auto-Tracking + @param eventType Auto-Tracking type + @param appId appId + */ ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos); +/** + Enable auto tracking with super properties. + @param eventType Auto-Tracking type + @param properties super properties + @param appId appId + */ ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType properties:(NSDictionary * _Nullable)properties withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos); + +/** + Enable the auto tracking function. + @param eventType Auto-Tracking type + @param 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 + @param appId appId + */ ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType callback:(NSDictionary *(^_Nullable)(TDAutoTrackEventType eventType, NSDictionary *properties))callback withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos); + +/** + Set and Update the value of a custom property for Auto-Tracking + @param eventType A list of TDAutoTrackEventType, indicating the types of automatic collection events that need to be enabled + @param properties properties + @param appId appId + */ ++ (void)setAutoTrackProperties:(TDAutoTrackEventType)eventType properties:(NSDictionary * _Nullable)properties withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos); + +/** + Ignore the Auto-Tracking of a UIViewController + @param controllers Ignore the name of the UIViewController + @param appId appId + */ ++ (void)ignoreAutoTrackViewControllers:(NSArray *)controllers withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos); + +/** + Ignore the Auto-Tracking of click UIView + @param aClass ignored UIView Class + @param appId appId + */ ++ (void)ignoreViewType:(Class)aClass withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos); + +/** + Dynamic super properties in auto track environment + Set dynamic public properties for auto track event + */ ++ (void)setAutoTrackDynamicProperties:(NSDictionary *(^)(void))dynamicSuperProperties withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos); + +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.h.meta new file mode 100644 index 0000000..4e14f88 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 039da0391bbe64e758f3be4538394c80 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.m b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.m new file mode 100644 index 0000000..897ac3b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.m @@ -0,0 +1,256 @@ +// +// TDAnalytics+Multiple.m +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/17. +// + +#import "TDAnalytics+Multiple.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDConfigPrivate.h" +#if TARGET_OS_IOS +#import "TDAutoTrackManager.h" +#endif + +@implementation TDAnalytics (Multiple) + ++ (void)flushWithAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerFlush]; +} + ++ (void)setTrackStatus:(TDTrackStatus)status withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerSetTrackStatus:status]; +} + ++ (void)track:(NSString *)eventName withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerTrack:eventName]; +} + ++ (void)track:(NSString *)eventName properties:(nullable NSDictionary *)properties withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerTrack:eventName properties:properties]; +} + ++ (void)track:(NSString *)eventName properties:(nullable NSDictionary *)properties time:(NSDate *)time timeZone:(NSTimeZone *)timeZone withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerTrack:eventName properties:properties time:time timeZone:timeZone]; +} + ++ (void)trackWithEventModel:(TDEventModel *)eventModel withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerTrackWithEventModel:eventModel]; +} + ++ (void)timeEvent:(NSString *)eventName withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerTimeEvent:eventName]; +} + +//MARK: user property + ++ (void)userSet:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerUserSet:properties]; +} + ++ (void)userSetOnce:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerUserSetOnce:properties]; +} + ++ (void)userUnset:(NSString *)propertyName withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerUserUnset:propertyName]; +} + ++ (void)userUnsets:(NSArray *)propertyNames withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerUserUnsets:propertyNames]; +} + ++ (void)userAdd:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerUserAdd:properties]; +} + ++ (void)userAddWithName:(NSString *)propertyName andValue:(NSNumber *)propertyValue withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerUserAdd:propertyName andPropertyValue:propertyValue]; +} + ++ (void)userAppend:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerUserAppend:properties]; +} + ++ (void)userUniqAppend:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerUserUniqAppend:properties]; +} + ++ (void)userDeleteWithAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerUserDelete]; +} + +//MARK: super property & preset property + ++ (void)setSuperProperties:(NSDictionary *)properties withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerSetSuperProperties:properties]; +} + ++ (void)unsetSuperProperty:(NSString *)property withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerUnsetSuperProperty:property]; +} + ++ (void)clearSuperPropertiesWithAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerClearSuperProperties]; +} + ++ (NSDictionary *)getSuperPropertiesWithAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + return [teSDK innerCurrentSuperProperties]; +} + ++ (void)setDynamicSuperProperties:(NSDictionary *(^)(void))propertiesHandler withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerRegisterDynamicSuperProperties:propertiesHandler]; +} + ++ (TDPresetProperties *)getPresetPropertiesWithAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + return [teSDK innerGetPresetProperties]; +} + +//MARK: error callback + ++ (void)registerErrorCallback:(void(^)(NSInteger code, NSString * _Nullable errorMsg, NSString * _Nullable ext))errorCallback withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerRegisterErrorCallback:errorCallback]; +} + +//MARK: custom property + ++ (void)setDistinctId:(NSString *)distinctId withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerSetIdentify:distinctId]; +} + ++ (NSString *)getDistinctIdWithAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + return [teSDK innerDistinctId]; +} + ++ (void)login:(NSString *)accountId withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerLogin:accountId]; +} + ++ (void)logoutWithAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerLogout]; +} + ++ (NSString *)getAccountIdWithAppId:(NSString *)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + return [teSDK innerAccountId]; +} + ++ (void)setUploadingNetworkType:(TDReportingNetworkType)type withAppId:(NSString * _Nullable)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerSetNetworkType:type]; +} + ++ (NSString *)timeStringWithDate:(NSDate *)date withAppId:(NSString *)appId { + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + return [teSDK innetGetTimeString:date]; +} + +//MARK: - auto track + ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType callback:(NSDictionary *(^ _Nullable)(TDAutoTrackEventType, NSDictionary *))callback withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos){ + [self innerEnableAutoTrack:eventType properties:nil callback:callback withAppId:appId]; +} + ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType properties:(NSDictionary * _Nullable)properties withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos){ + [self innerEnableAutoTrack:eventType properties:properties callback:nil withAppId:appId]; +} + ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos){ + [self innerEnableAutoTrack:eventType properties:nil callback:nil withAppId:appId]; +} + ++ (void)ignoreAutoTrackViewControllers:(nonnull NSArray *)controllers withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos){ + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + if ([teSDK hasDisabled]) { + return; + } + if (controllers == nil || controllers.count == 0) { + return; + } + @synchronized (teSDK.ignoredViewControllers) { + [teSDK.ignoredViewControllers addObjectsFromArray:controllers]; + } +} + ++ (void)ignoreViewType:(nonnull Class)aClass withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos){ + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + if ([teSDK hasDisabled]) { + return; + } + @synchronized (teSDK.ignoredViewTypeList) { + [teSDK.ignoredViewTypeList addObject:aClass]; + } +} + ++ (void)setAutoTrackProperties:(TDAutoTrackEventType)eventType properties:(NSDictionary * _Nullable)properties withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos){ +#if TARGET_OS_IOS + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + if ([teSDK hasDisabled]) { + return; + } + if (properties == nil) { + return; + } + @synchronized (teSDK.autoTrackSuperProperty) { + [teSDK.autoTrackSuperProperty registerSuperProperties:[properties copy] withType:eventType]; + } +#endif +} + ++ (void)innerEnableAutoTrack:(TDAutoTrackEventType)eventType properties:(NSDictionary * _Nullable)properties callback:(NSDictionary *(^ _Nullable)(TDAutoTrackEventType eventType, NSDictionary *properties))callback withAppId:(NSString * _Nullable)appId API_UNAVAILABLE(macos){ +#if TARGET_OS_IOS + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + + if (teSDK.autoTrackSuperProperty == nil) { + teSDK.autoTrackSuperProperty = [[TDAutoTrackSuperProperty alloc] init]; + } + [teSDK.autoTrackSuperProperty registerSuperProperties:properties withType:eventType]; + [teSDK.autoTrackSuperProperty registerDynamicSuperProperties:callback]; + + NSString *instanceToken = [teSDK.config innerGetMapInstanceToken]; + [[TDAutoTrackManager sharedManager] trackWithAppid:instanceToken withOption:eventType]; +#endif +} + ++ (void)setAutoTrackDynamicProperties:(NSDictionary * _Nonnull (^)(void))dynamicSuperProperties withAppId:(NSString *)appId { +#if TARGET_OS_IOS + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + if ([teSDK hasDisabled]) { + return; + } + @synchronized (teSDK.autoTrackSuperProperty) { + [teSDK.autoTrackSuperProperty registerAutoTrackDynamicProperties:dynamicSuperProperties]; + } +#endif +} + +//MARK: - + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.m.meta new file mode 100644 index 0000000..6722b50 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Multiple.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 54e52b932bbbe4791bfe599a3278d6b1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.h new file mode 100644 index 0000000..af3f1fe --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.h @@ -0,0 +1,22 @@ +// +// TDAnalytics+Private.h +// ThinkingSDK +// +// Created by 杨雄 on 2024/5/31. +// + +#if __has_include() +#import +#else +#import "TDAnalytics.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAnalytics (Private) + ++ (void)trackDebug:(NSString *)eventName properties:(nullable NSDictionary *)properties appId:(NSString * _Nullable)appId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.h.meta new file mode 100644 index 0000000..523d92e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 442a67bdcbbfd43e88aac2710506d772 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.m b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.m new file mode 100644 index 0000000..6118fcc --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.m @@ -0,0 +1,21 @@ +// +// TDAnalytics+Private.m +// ThinkingSDK +// +// Created by 杨雄 on 2024/5/31. +// + +#import "TDAnalytics+Private.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +@implementation TDAnalytics (Private) + ++ (void)trackDebug:(NSString *)eventName properties:(NSDictionary *)properties appId:(NSString * _Nullable)appId { + if (appId == nil) { + appId = [ThinkingAnalyticsSDK defaultAppId]; + } + ThinkingAnalyticsSDK *teSDK = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + [teSDK innerTrackDebug:eventName properties:properties]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.m.meta new file mode 100644 index 0000000..cc38bb5 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Private.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 14e555d29c8374add9223007e4a0e950 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.h new file mode 100644 index 0000000..10a83dd --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.h @@ -0,0 +1,294 @@ +// +// TDAnalytics+Public.h +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/17. +// + +#if __has_include() +#import +#else +#import "TDAnalytics.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAnalytics (Public) + +//MARK: SDK info ++ (void)enableLog:(BOOL)enable; ++ (void)calibrateTimeWithNtp:(NSString *)ntpServer; ++ (void)calibrateTime:(NSTimeInterval)timestamp; ++ (nullable NSString *)getLocalRegion; ++ (void)setCustomerLibInfoWithLibName:(NSString *)libName libVersion:(NSString *)libVersion; +/** + Get sdk version + + @return version string + */ ++ (NSString *)getSDKVersion; +/** + Get DeviceId + + @return deviceId + */ ++ (NSString *)getDeviceId; +/** + Format the time output in the format of SDK + @param date date + @return date string + */ ++ (NSString *)timeStringWithDate:(NSDate *)date; + +//MARK: - init + +/** + Initialization method + After the SDK initialization is complete, the saved instance can be obtained through this api + + @param appId appId + @param url server url + */ ++ (void)startAnalyticsWithAppId:(NSString *)appId serverUrl:(NSString *)url; + +/** + Initialization method + After the SDK initialization is complete, the saved instance can be obtained through this api + + @param config initialization configuration + */ ++ (void)startAnalyticsWithConfig:(nullable TDConfig *)config; + +/// Create light instance based on original instance +/// @param appId appId +/// @return light instance appId ++ (NSString * _Nullable)lightInstanceIdWithAppId:(NSString * _Nonnull)appId; + +//MARK: track + +/** + 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 TDTrackStatus reporting status + */ ++ (void)setTrackStatus:(TDTrackStatus)status; + +/** + Track Events + @param eventName event name + */ ++ (void)track:(NSString *)eventName; +/** + Track Events + @param eventName event name + @param properties event properties + */ ++ (void)track:(NSString *)eventName properties:(nullable NSDictionary *)properties; +/** + Track Events + @param eventName event name + @param properties event properties + @param time event trigger time + @param timeZone event trigger time time zone + */ ++ (void)track:(NSString *)eventName properties:(nullable NSDictionary *)properties time:(NSDate *)time timeZone:(NSTimeZone *)timeZone; +/** + Track Events + @param eventModel event Model + */ ++ (void)trackWithEventModel:(TDEventModel *)eventModel; +/** + 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 *)eventName; + + +//MARK: user property + +/** + Sets the user property, replacing the original value with the new value if the property already exists. + @param properties user properties + */ ++ (void)userSet:(NSDictionary *)properties; +/** + Sets a single user attribute, ignoring the new attribute value if the attribute already exists. + @param properties user properties + */ ++ (void)userSetOnce:(NSDictionary *)properties; +/** + Reset single user attribute. + @param propertyName user properties + */ ++ (void)userUnset:(NSString *)propertyName; +/** + Reset user properties. + @param propertyNames user properties +*/ ++ (void)userUnsets:(NSArray *)propertyNames; +/** + Adds the numeric type user attributes. + @param properties user properties + */ ++ (void)userAdd:(NSDictionary *)properties; +/** + Adds the numeric type user attribute. + @param propertyName propertyName + @param propertyValue propertyValue + */ ++ (void)userAddWithName:(NSString *)propertyName andValue:(NSNumber *)propertyValue; +/** + Appends an element to a property of an array type. + @param properties user properties + */ ++ (void)userAppend:(NSDictionary *)properties; +/** + Appends an element to a property of an array type. It filters out elements that already exist. + @param properties user properties +*/ ++ (void)userUniqAppend:(NSDictionary *)properties; +/** + Delete the user attributes. This operation is not reversible and should be performed with caution. + */ ++ (void)userDelete; + + +//MARK: super property & preset property + +/** + 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. + @param properties super properties + */ ++ (void)setSuperProperties:(NSDictionary *)properties; +/** + Clears a public event attribute. + @param property property name + */ ++ (void)unsetSuperProperty:(NSString *)property; +/** + Clear all public event attributes. + */ ++ (void)clearSuperProperties; +/** + Get the public event properties that have been set. + + @return super properties that have been set. + */ ++ (NSDictionary *)getSuperProperties; +/** + Set dynamic public properties. Each event uploaded after that will contain a public event attribute. + @param propertiesHandler propertiesHandler. + */ ++ (void)setDynamicSuperProperties:(NSDictionary *(^)(void))propertiesHandler; +/** + Get the SDK's preset properties. + + @return preset property object + */ ++ (TDPresetProperties *)getPresetProperties; + +//MARK: error callback + +/** + 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; + + +//MARK: custom property + +/** + Set the distinct ID to replace the default UUID distinct ID. + @param distinctId distinctId + */ ++ (void)setDistinctId:(NSString *)distinctId; + +/** + Get distinct ID: The #distinct_id value in the reported data. + + @return distinctId + */ ++ (NSString *)getDistinctId; + +/** + Set the account ID. Each setting overrides the previous value. Login events will not be uploaded. + @param accountId accountId + */ ++ (void)login:(NSString *)accountId; + +/** + Get account ID: The #account_id value in the reported data. + + @return accountId + */ ++ (NSString *)getAccountId; + +/** + Clearing the account ID will not upload user logout events. + */ ++ (void)logout; + +/** + Set the network conditions for uploading. By default, the SDK will set the network conditions as 3G, 4G and Wifi to upload data + @param type network type + */ ++ (void)setUploadingNetworkType:(TDReportingNetworkType)type; + +#if TARGET_OS_IOS + +/** + Enable Auto-Tracking + @param eventType Auto-Tracking type + */ ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType API_UNAVAILABLE(macos); +/** + Enable auto tracking with super properties. + @param eventType Auto-Tracking type + @param properties super properties + */ ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType properties:(NSDictionary * _Nullable)properties API_UNAVAILABLE(macos); + +/** + Enable the auto tracking function. + @param eventType Auto-Tracking type + @param 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:(TDAutoTrackEventType)eventType callback:(NSDictionary *(^_Nullable)(TDAutoTrackEventType eventType, NSDictionary *properties))callback API_UNAVAILABLE(macos); + +/** + Set and Update the value of a custom property for Auto-Tracking + @param eventType A list of TDAutoTrackEventType, indicating the types of automatic collection events that need to be enabled + @param properties properties + */ ++ (void)setAutoTrackProperties:(TDAutoTrackEventType)eventType properties:(NSDictionary * _Nullable)properties API_UNAVAILABLE(macos); + +/** + Ignore the Auto-Tracking of a UIViewController + @param controllers Ignore the name of the UIViewController + */ ++ (void)ignoreAutoTrackViewControllers:(NSArray *)controllers API_UNAVAILABLE(macos); + +/** + Ignore the Auto-Tracking of click UIView + @param aClass ignored UIView Class + */ ++ (void)ignoreViewType:(Class)aClass API_UNAVAILABLE(macos); + +/** + Dynamic super properties in auto track environment + Set dynamic public properties for auto track event + */ ++ (void)setAutoTrackDynamicProperties:(NSDictionary *(^)(void))dynamicSuperProperties API_UNAVAILABLE(macos); + +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.h.meta new file mode 100644 index 0000000..809f3ad --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 519475da6f5f54abd854ba2fff09b3d1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.m b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.m new file mode 100644 index 0000000..835fb54 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.m @@ -0,0 +1,328 @@ +// +// TDAnalytics+Public.m +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/17. +// + +#import "TDAnalytics+Public.h" +#import "TDAnalytics+Multiple.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDLogging.h" +#import "TDPublicConfig.h" +#import "TDConfigPrivate.h" + +#if __has_include() +#import +#else +#import "TDCalibratedTime.h" +#endif + +#if __has_include() +#import +#else +#import "TDCoreDeviceInfo.h" +#endif + +#if __has_include() +#import +#else +#import "NSString+TDCore.h" +#endif + +#if __has_include() +#import +#else +#import "NSURL+TDCore.h" +#endif + +@implementation TDAnalytics (Public) + +#pragma mark - Logging + ++ (void)enableLog:(BOOL)enable { + [TDLogging sharedInstance].loggingLevel = enable ? TDLoggingLevelDebug : TDLoggingLevelNone; +} + +#pragma mark - Calibrate time + ++ (void)calibrateTime:(NSTimeInterval)timestamp { + [[TDCalibratedTime sharedInstance] recalibrationWithTimeInterval:timestamp/1000.0]; +} + ++ (void)calibrateTimeWithNtp:(NSString *)ntpServer { + if ([ntpServer isKindOfClass:[NSString class]] && ntpServer.length > 0) { + [[TDCalibratedTime sharedInstance] recalibrationWithNtps:@[ntpServer]]; + } +} + +// MARK: info + ++ (nullable NSString *)getLocalRegion { + NSString *countryCode = [[NSLocale currentLocale] objectForKey: NSLocaleCountryCode]; + return countryCode; +} + ++ (void)setCustomerLibInfoWithLibName:(NSString *)libName libVersion:(NSString *)libVersion { + if (libName.length > 0) { + [TDDeviceInfo sharedManager].libName = libName; + } + if (libVersion.length > 0) { + [TDDeviceInfo sharedManager].libVersion = libVersion; + } +} + ++ (NSString *)getSDKVersion { + return TDPublicConfig.version; +} + ++ (NSString *)getDeviceId { + return [TDCoreDeviceInfo deviceId]; +} + ++ (NSString *)timeStringWithDate:(NSDate *)date { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + return [TDAnalytics timeStringWithDate:date withAppId:appId]; +} + +//MARK: - init + ++ (void)startAnalyticsWithAppId:(NSString *)appId serverUrl:(NSString *)url { + TDConfig *config = [[TDConfig alloc] init]; + config.appid = appId; + config.serverUrl = url; + [self startAnalyticsWithConfig:config]; +} + ++ (void)startAnalyticsWithConfig:(TDConfig *)config { + if (!config) { + return; + } + config.appid = [config.appid td_trim]; + + NSString *appId = config.appid; + if (appId.length == 0) { + return; + } + + NSMutableDictionary *instances = [ThinkingAnalyticsSDK _getAllInstances]; + if ([instances objectForKey:[config innerGetMapInstanceToken]]) { + return; + } + + config.serverUrl = [NSURL td_baseUrlStringWithString:config.serverUrl]; + NSString *url = config.serverUrl; + if (url.length == 0) { + return; + } + + ThinkingAnalyticsSDK *sdk = [[ThinkingAnalyticsSDK alloc] initWithConfig:config]; + TDLogInfo(@"instance token: %@", [sdk.config innerGetMapInstanceToken]); +} + ++ (NSString *)lightInstanceIdWithAppId:(NSString *)appId { + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + if (instance) { + ThinkingAnalyticsSDK *lightInstance = [instance innerCreateLightInstance]; + return [lightInstance.config innerGetMapInstanceToken]; + } + return nil; +} + +//MARK: track + ++ (void)flush { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics flushWithAppId:appId]; +} + ++ (void)setTrackStatus:(TDTrackStatus)status { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics setTrackStatus:status withAppId:appId]; +} + ++ (void)track:(NSString *)eventName { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics track:eventName withAppId:appId]; +} + ++ (void)track:(NSString *)eventName properties:(nullable NSDictionary *)properties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics track:eventName properties:properties withAppId:appId]; +} + ++ (void)track:(NSString *)eventName properties:(nullable NSDictionary *)properties time:(NSDate *)time timeZone:(NSTimeZone *)timeZone { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics track:eventName properties:properties time:time timeZone:timeZone withAppId:appId]; +} + ++ (void)trackWithEventModel:(TDEventModel *)eventModel { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics trackWithEventModel:eventModel withAppId:appId]; +} + ++ (void)timeEvent:(NSString *)eventName { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics timeEvent:eventName withAppId:appId]; +} + +//MARK: user property + ++ (void)userSet:(NSDictionary *)properties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics userSet:properties withAppId:appId]; +} + ++ (void)userSetOnce:(NSDictionary *)properties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics userSetOnce:properties withAppId:appId]; +} + ++ (void)userUnset:(NSString *)propertyName { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics userUnset:propertyName withAppId:appId]; +} + ++ (void)userUnsets:(NSArray *)propertyNames { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics userUnsets:propertyNames withAppId:appId]; +} + ++ (void)userAdd:(NSDictionary *)properties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics userAdd:properties withAppId:appId]; +} + ++ (void)userAddWithName:(NSString *)propertyName andValue:(NSNumber *)propertyValue { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics userAddWithName:propertyName andValue:propertyValue withAppId:appId]; +} + ++ (void)userAppend:(NSDictionary *)properties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics userAppend:properties withAppId:appId]; +} + ++ (void)userUniqAppend:(NSDictionary *)properties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics userUniqAppend:properties withAppId:appId]; +} + ++ (void)userDelete { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics userDeleteWithAppId:appId]; +} + +//MARK: super property & preset property + ++ (void)setSuperProperties:(NSDictionary *)properties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics setSuperProperties:properties withAppId:appId]; +} + ++ (void)unsetSuperProperty:(NSString *)property { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics unsetSuperProperty:property withAppId:appId]; +} + ++ (void)clearSuperProperties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics clearSuperPropertiesWithAppId:appId]; +} + ++ (NSDictionary *)getSuperProperties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + return [TDAnalytics getSuperPropertiesWithAppId:appId]; +} + ++ (void)setDynamicSuperProperties:(NSDictionary *(^)(void))propertiesHandler { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics setDynamicSuperProperties:propertiesHandler withAppId:appId]; +} + ++ (TDPresetProperties *)getPresetProperties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + return [TDAnalytics getPresetPropertiesWithAppId:appId]; +} + +//MARK: error callback + ++ (void)registerErrorCallback:(void(^)(NSInteger code, NSString * _Nullable errorMsg, NSString * _Nullable ext))errorCallback { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics registerErrorCallback:errorCallback withAppId:appId]; +} + +//MARK: custom property + ++ (void)setDistinctId:(NSString *)distinctId { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics setDistinctId:distinctId withAppId:appId]; +} + ++ (NSString *)getDistinctId { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + return [TDAnalytics getDistinctIdWithAppId:appId]; +} + ++ (void)login:(NSString *)accountId { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics login:accountId withAppId:appId]; +} + ++ (NSString *)getAccountId { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + return [TDAnalytics getAccountIdWithAppId:appId]; +} + ++ (void)logout { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics logoutWithAppId:appId]; +} + ++ (void)setUploadingNetworkType:(TDReportingNetworkType)type { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics setUploadingNetworkType:type withAppId:appId]; +} + +// MARK: auto track + +#if TARGET_OS_IOS + ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType API_UNAVAILABLE(macos){ + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics enableAutoTrack:eventType withAppId:appId]; +} + ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType properties:(NSDictionary * _Nullable)properties API_UNAVAILABLE(macos){ + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics enableAutoTrack:eventType properties:properties withAppId:appId]; +} + ++ (void)enableAutoTrack:(TDAutoTrackEventType)eventType callback:(NSDictionary *(^ _Nullable)(TDAutoTrackEventType, NSDictionary *))callback API_UNAVAILABLE(macos){ + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics enableAutoTrack:eventType callback:callback withAppId:appId]; +} + ++ (void)ignoreAutoTrackViewControllers:(nonnull NSArray *)controllers API_UNAVAILABLE(macos){ + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics ignoreAutoTrackViewControllers:controllers withAppId:appId]; +} + ++ (void)ignoreViewType:(nonnull Class)aClass API_UNAVAILABLE(macos){ + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics ignoreViewType:aClass withAppId:appId]; +} + ++ (void)setAutoTrackProperties:(TDAutoTrackEventType)eventType properties:(NSDictionary * _Nullable)properties API_UNAVAILABLE(macos){ + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics setAutoTrackProperties:eventType properties:properties withAppId:appId]; +} + ++ (void)setAutoTrackDynamicProperties:(NSDictionary * _Nonnull (^)(void))dynamicSuperProperties API_UNAVAILABLE(macos){ + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [TDAnalytics setAutoTrackDynamicProperties:dynamicSuperProperties withAppId:appId]; +} + +#endif + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.m.meta new file mode 100644 index 0000000..0d5b696 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+Public.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1acfcd5ae045b409fad76724fe46c1fd +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.h new file mode 100644 index 0000000..30a8fbe --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.h @@ -0,0 +1,30 @@ +// +// TDAnalytics+ThirdParty.h +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/17. +// + +#if __has_include() +#import +#else +#import "TDAnalytics.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAnalytics (ThirdParty) + +#if TARGET_OS_IOS + ++ (void)enableThirdPartySharing:(TDThirdPartyType)type API_UNAVAILABLE(macos); ++ (void)enableThirdPartySharing:(TDThirdPartyType)type properties:(NSDictionary *)properties API_UNAVAILABLE(macos); + ++ (void)enableThirdPartySharing:(TDThirdPartyType)type withAppId:(NSString *)appId API_UNAVAILABLE(macos); ++ (void)enableThirdPartySharing:(TDThirdPartyType)type properties:(NSDictionary *)properties withAppId:(NSString *)appId API_UNAVAILABLE(macos); + +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.h.meta new file mode 100644 index 0000000..36cb599 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6dc4712154e264aeb8dff858b6f9cadb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.m b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.m new file mode 100644 index 0000000..cc7aaea --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.m @@ -0,0 +1,73 @@ +// +// TDAnalytics+ThirdParty.m +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/17. +// + +#import "TDAnalytics+ThirdParty.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +@implementation TDAnalytics (ThirdParty) + +#if TARGET_OS_IOS + ++ (void)enableThirdPartySharing:(TDThirdPartyType)type { + [self enableThirdPartySharing:type properties:@{}]; +} + ++ (void)enableThirdPartySharing:(TDThirdPartyType)type properties:(NSDictionary *)properties { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + [self enableThirdPartySharing:type properties:properties withAppId:appId]; +} + ++ (void)enableThirdPartySharing:(TDThirdPartyType)type withAppId:(NSString *)appId { + [self enableThirdPartySharing:type properties:@{} withAppId:appId]; +} + ++ (void)enableThirdPartySharing:(TDThirdPartyType)type properties:(NSDictionary *)properties withAppId:(NSString *)appId { + ThinkingAnalyticsSDK *instance = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + + if (instance != nil) { + Class TAThirdPartyManager = NSClassFromString(@"TAThirdPartyManager"); + if (TAThirdPartyManager == nil) { + return; + } + NSObject *manager = [[TAThirdPartyManager alloc] init]; + if (manager == nil) { + return; + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + SEL action = @selector(enableThirdPartySharing:instance:property:); +#pragma clang diagnostic pop + + NSMethodSignature *methodSig = [manager methodSignatureForSelector:action]; + if (methodSig == nil) { + return; + } + + @try { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; + + NSNumber *thirdPartyTypeNumber = [NSNumber numberWithInteger:type]; + [invocation setArgument:&thirdPartyTypeNumber atIndex:2]; + + [invocation setArgument:&instance atIndex:3]; + + NSDictionary *thirdPartyProperties = properties; + [invocation setArgument:&thirdPartyProperties atIndex:4]; + + [invocation setSelector:action]; + [invocation setTarget:manager]; + [invocation invoke]; + } @catch (NSException *exception) { + TDLogError(@"ThirdParty invocate failed!") + } + } +} + +#endif + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.m.meta new file mode 100644 index 0000000..cafab61 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+ThirdParty.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1f2f67a3159fe48038d44fb0fc53df6c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.h new file mode 100644 index 0000000..eef38df --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.h @@ -0,0 +1,36 @@ +// +// TDAnalytics+WebView.h +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/17. +// + +#if __has_include() +#import +#else +#import "TDAnalytics.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface TDAnalytics (WebView) + +/// 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 ++ (BOOL)showUpWebView:(id)webView withRequest:(NSURLRequest *)request; + +/// When connecting data with H5, you need to call this interface to configure UserAgent ++ (void)addWebViewUserAgent; + +/// H5 is connected with the native APP SDK and used in conjunction with the addWebViewUserAgent interface +/// @param webView webView +/// @param request NSURLRequest request +/// @param appId appId +/// @return YES:Process this request NO: This request has not been processed ++ (BOOL)showUpWebView:(id)webView withRequest:(NSURLRequest *)request withAppId:(NSString * _Nullable)appId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.h.meta new file mode 100644 index 0000000..773efd0 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 85ee0bdc2089f4f74ba52d6aac59f187 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.m b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.m new file mode 100644 index 0000000..6370ef8 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.m @@ -0,0 +1,169 @@ +// +// TDAnalytics+WebView.m +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/17. +// + +#import "TDAnalytics+WebView.h" +#import "ThinkingAnalyticsSDKPrivate.h" + +static NSString * const TA_JS_TRACK_SCHEME = @"thinkinganalytics://trackEvent"; + +@implementation TDAnalytics (WebView) + ++ (BOOL)showUpWebView:(nonnull id)webView withRequest:(nonnull NSURLRequest *)request { + NSString *appId = [ThinkingAnalyticsSDK defaultAppId]; + return [self showUpWebView:webView withRequest:request withAppId:appId]; +} + ++ (BOOL)showUpWebView:(id)webView withRequest:(NSURLRequest *)request withAppId:(NSString *)appId { + if (webView == nil || request == nil || ![request isKindOfClass:NSURLRequest.class]) { + 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) { + NSString *eventData = [queryValue stringByRemovingPercentEncoding]; + if (eventData.length > 0) { + [TDAnalytics clickFromH5:eventData withAppId:appId]; + } + } + return YES; +} + ++ (void)addWebViewUserAgent { + 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 = ^() { + [TDAnalytics wkWebViewGetUserAgent:^(NSString *userAgent) { + setUserAgent(userAgent); + }]; + }; + td_dispatch_main_sync_safe(getUABlock); +} + +// MARK: private + +static WKWebView *_blankWebView = nil; ++ (void)wkWebViewGetUserAgent:(void(^)(NSString *))completion { + if (!_blankWebView) { + _blankWebView = [[WKWebView alloc] initWithFrame:CGRectZero]; + } + [_blankWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id __nullable userAgent, NSError * __nullable error) { + completion(userAgent); + }]; +} + ++ (void)clickFromH5:(NSString *)data withAppId:(NSString *)appId { + NSData *jsonData = [data dataUsingEncoding:NSUTF8StringEncoding]; + if (!jsonData) { + return; + } + + NSError *err; + NSDictionary *eventDict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err]; + if (err) { + return; + } + + ThinkingAnalyticsSDK *instance = nil; + NSString *jsAppid = eventDict[@"#app_id"]; + if ([jsAppid isKindOfClass:[NSString class]]) { + instance = [ThinkingAnalyticsSDK instanceWithAppid:jsAppid]; + } + if (!instance) { + instance = [ThinkingAnalyticsSDK instanceWithAppid:appId]; + } + if (!instance) { + return; + } + + NSArray *dataArr = eventDict[@"data"]; + if (![dataArr isKindOfClass:[NSArray class]]) { + return; + } + + NSDictionary *dataInfo = dataArr.firstObject; + 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"]; + + [self h5trackWithInstance:instance eventName:event_name extraID:extraID properties:dic type:type time:time]; + } +} + ++ (void)h5trackWithInstance:(ThinkingAnalyticsSDK *)instance eventName:(NSString *)eventName extraID:(NSString *)extraID properties:(NSDictionary *)propertieDict type:(NSString *)type time:(NSString *)time { + if ([ThinkingAnalyticsSDK isTrackEvent:type]) { + TDTrackEvent *event = nil; + if ([type isEqualToString:TD_EVENT_TYPE_TRACK]) { + TDTrackEvent *trackEvent = [[TDTrackEvent alloc] initWithName:eventName]; + event = trackEvent; + } else if ([type isEqualToString:TD_EVENT_TYPE_TRACK_FIRST]) { + TDTrackFirstEvent *firstEvent = [[TDTrackFirstEvent alloc] initWithName:eventName]; + firstEvent.firstCheckId = extraID; + event = firstEvent; + } else if ([type isEqualToString:TD_EVENT_TYPE_TRACK_UPDATE]) { + TDTrackUpdateEvent *updateEvent = [[TDTrackUpdateEvent alloc] initWithName:eventName]; + updateEvent.eventId = extraID; + event = updateEvent; + } else if ([type isEqualToString:TD_EVENT_TYPE_TRACK_OVERWRITE]) { + TDTrackOverwriteEvent *overwriteEvent = [[TDTrackOverwriteEvent alloc] initWithName:eventName]; + overwriteEvent.eventId = extraID; + event = overwriteEvent; + } + event.h5TimeString = time; + if ([propertieDict objectForKey:@"#zone_offset"]) { + event.h5ZoneOffSet = [propertieDict objectForKey:@"#zone_offset"]; + } + [instance asyncTrackEventObject:event properties:propertieDict isH5:YES]; + } else { + TDUserEvent *event = [[TDUserEvent alloc] initWithType:[TDBaseEvent typeWithTypeString:type]]; + [instance asyncUserEventObject:event properties:propertieDict isH5:YES]; + } +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.m.meta new file mode 100644 index 0000000..fedb85c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics+WebView.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 3295eb25caa35446e933c34935a737f4 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.h new file mode 100644 index 0000000..8da24a9 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.h @@ -0,0 +1,55 @@ +// +// TDAnalytics.h +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/17. +// + +#import + +#if TARGET_OS_IOS +#import + +#if __has_include() +#import +#else +#import "TDAutoTrackPublicHeader.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 + +@interface TDAnalytics : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.h.meta new file mode 100644 index 0000000..07b5983 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: fe6a3a72a21bf4e71bf22410f1d779e0 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.m b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.m new file mode 100644 index 0000000..fcc6709 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.m @@ -0,0 +1,12 @@ +// +// TDAnalytics.m +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/17. +// + +#import "TDAnalytics.h" + +@implementation TDAnalytics + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.m.meta new file mode 100644 index 0000000..a616e8f --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDAnalytics.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 6b511d6b0320645ec89166b365912c4a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDConstant.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDConstant.h new file mode 100644 index 0000000..77eeb5b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDConstant.h @@ -0,0 +1,282 @@ +// +// TDConstant.h +// ThinkingSDK +// +// Created by LiHuanan on 2020/9/8. +// Copyright © 2020 thinkingdata. All rights reserved. +// + +#import + +/** + Log Level + + - TDLoggingLevelNone : Not enabled by default + */ +typedef NS_OPTIONS(NSInteger, TDLoggingLevel) { + /** + Not enabled by default + */ + TDLoggingLevelNone = 0, + + /** + Error Log + */ + TDLoggingLevelError = 1 << 0, + + + /** + Warning Log + */ + TDLoggingLevelWarning = 1 << 1, + + /** + Info Log + */ + TDLoggingLevelInfo = 1 << 2, + + /** + Debug Log + */ + TDLoggingLevelDebug = 1 << 3, +}; + +/** +Debug Mode + +- ThinkingAnalyticsDebugOff : Not enabled by default +*/ +__attribute__((deprecated("This class is deprecated. Use the newClass instead: TDMode"))) +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, +}; + +/** +Debug Mode + +- ThinkingAnalyticsDebugOff : Not enabled by default +*/ +typedef NS_OPTIONS(NSInteger, TDMode) { + /** + Not enabled by default + */ + TDModeNormal = 0, + + /** + Enable DebugOnly Mode, Data is not persisted + */ + TDModeDebugOnly = 1 << 0, + + /** + Enable Debug Mode,Data will persist + */ + TDModeDebug = 1 << 1, +}; + + +/** + 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 + */ +__attribute__((deprecated("This class is deprecated. Use the newClass instead: TDReportingNetworkType"))) +typedef NS_OPTIONS(NSInteger, ThinkingAnalyticsNetworkType) { + + /** + only WIFI + */ + TDNetworkTypeOnlyWIFI = 1 << 0, + + /** + 2G、3G、4G、WIFI + */ + TDNetworkTypeALL = 1 << 1, + + /** + 3G、4G、WIFI + */ + TDNetworkTypeDefault = TDNetworkTypeALL, +}; + +typedef NS_OPTIONS(NSInteger, TDReportingNetworkType) { + TDReportingNetworkTypeWIFI = 1 << 0, + TDReportingNetworkTypeALL = 1 << 1, +}; + +/** + Auto-Tracking Enum + + - ThinkingAnalyticsEventTypeNone : auto-tracking is not enabled by default + */ +__attribute__((deprecated("This class is deprecated. Use the newClass instead: TDAutoTrackEventType"))) +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, TDThirdPartyType) { + TDThirdPartyTypeNone = 0, + TDThirdPartyTypeAppsFlyer = 1 << 0, + TDThirdPartyTypeIronSource = 1 << 1, + TDThirdPartyTypeAdjust = 1 << 2, + TDThirdPartyTypeBranch = 1 << 3, + TDThirdPartyTypeTopOn = 1 << 4, + TDThirdPartyTypeTracking = 1 << 5, + TDThirdPartyTypeTradPlus = 1 << 6, + TDThirdPartyTypeAppLovin = 1 << 7, + TDThirdPartyTypeKochava = 1 << 8, + TDThirdPartyTypeTalkingData = 1 << 9, + TDThirdPartyTypeFirebase = 1 << 10, +}; + +__attribute__((deprecated("This class is deprecated. Use the newClass instead: TDThirdPartyType"))) +typedef NS_OPTIONS(NSUInteger, TAThirdPartyShareType) { + TAThirdPartyShareTypeNONE = TDThirdPartyTypeNone, + TAThirdPartyShareTypeAPPSFLYER = TDThirdPartyTypeAppsFlyer, + TAThirdPartyShareTypeIRONSOURCE = TDThirdPartyTypeIronSource, + TAThirdPartyShareTypeADJUST = TDThirdPartyTypeAdjust, + TAThirdPartyShareTypeBRANCH = TDThirdPartyTypeBranch, + TAThirdPartyShareTypeTOPON = TDThirdPartyTypeTopOn, + TAThirdPartyShareTypeTRACKING = TDThirdPartyTypeTracking, + TAThirdPartyShareTypeTRADPLUS = TDThirdPartyTypeTradPlus, + TAThirdPartyShareTypeAPPLOVIN = TDThirdPartyTypeAppLovin, + TAThirdPartyShareTypeKOCHAVA = TDThirdPartyTypeKochava, + TAThirdPartyShareTypeTALKINGDATA = TDThirdPartyTypeTalkingData, + TAThirdPartyShareTypeFIREBASE = TDThirdPartyTypeFirebase, + + TDThirdPartyShareTypeNONE = TAThirdPartyShareTypeNONE, + TDThirdPartyShareTypeAPPSFLYER = TAThirdPartyShareTypeAPPSFLYER, + TDThirdPartyShareTypeIRONSOURCE = TAThirdPartyShareTypeIRONSOURCE, + TDThirdPartyShareTypeADJUST = TAThirdPartyShareTypeADJUST, + TDThirdPartyShareTypeBRANCH = TAThirdPartyShareTypeBRANCH, + TDThirdPartyShareTypeTOPON = TAThirdPartyShareTypeTOPON, + TDThirdPartyShareTypeTRACKING = TAThirdPartyShareTypeTRACKING, + TDThirdPartyShareTypeTRADPLUS = TAThirdPartyShareTypeTRADPLUS, +}; + +//MARK: - Data reporting status +typedef NS_ENUM(NSInteger, TDTrackStatus) { + /// Suspend reporting + TDTrackStatusPause, + /// Stop reporting and clear cache + TDTrackStatusStop, + /// Suspend reporting and continue to persist data + TDTrackStatusSaveOnly, + /// reset normal + TDTrackStatusNormal +}; + + +//MARK: - Data reporting status +__attribute__((deprecated("This class is deprecated. Use the newClass instead: TDTrackStatus"))) +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 +}; + +//MARK: - DNS service +typedef NSString *TDDNSService NS_TYPED_EXTENSIBLE_ENUM; +static TDDNSService const _Nonnull TDDNSServiceCloudFlare = @"https://cloudflare-dns.com/dns-query?name="; +static TDDNSService const _Nonnull TDDNSServiceCloudALi = @"https://223.5.5.5/resolve?name="; +static TDDNSService const _Nonnull TDDNSServiceCloudGoogle = @"https://8.8.8.8/resolve?name="; + diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDConstant.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDConstant.h.meta new file mode 100644 index 0000000..0611b38 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/TDConstant.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 4ca0c85929cda4d0e804eb407175415e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.h new file mode 100644 index 0000000..012192a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.h @@ -0,0 +1,463 @@ +// +// ThinkingAnalyticsSDK+OldPublic.h +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/10. +// + +#if __has_include() +#import +#else +#import "ThinkingAnalyticsSDK.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +@interface ThinkingAnalyticsSDK (OldPublic) + +//MARK: SDK info + ++ (void)calibrateTimeWithNtp:(NSString *)ntpServer DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics calibrateTimeWithNtp:]"); ++ (void)calibrateTime:(NSTimeInterval)timestamp DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics calibrateTime:]"); ++ (nullable NSString *)getLocalRegion DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics getLocalRegion]"); +/** + Set Log level + @param level log level + */ ++ (void)setLogLevel:(TDLoggingLevel)level DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics enableLog:]"); ++ (void)setCustomerLibInfoWithLibName:(NSString *)libName libVersion:(NSString *)libVersion DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setCustomerLibInfoWithLibName:libVersion:]"); +/** + Get sdk version + + @return version string + */ ++ (NSString *)getSDKVersion DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics getSDKVersion]"); +/** + Get DeviceId + + @return deviceId + */ ++ (NSString *)getDeviceId DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics getDeviceId]"); +/** + Format the time output in the format of SDK + @param date date + @return date string + */ ++ (NSString *)timeStringWithDate:(NSDate *)date DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics timeStringWithDate:]"); + +#pragma mark - Tracking + +/** + 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 sdk instance + */ ++ (ThinkingAnalyticsSDK *)startWithAppId:(NSString *)appId withUrl:(NSString *)url DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics startAnalyticsWithAppId:(NSString *)appId serverUrl:(NSString *)url]"); + +/** + Initialization method + After the SDK initialization is complete, the saved instance can be obtained through this api + + @param config initialization configuration + @return sdk instance + */ ++ (ThinkingAnalyticsSDK *)startWithConfig:(nullable TDConfig *)config DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics startAnalyticsWithConfig:(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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics startAnalyticsWithConfig:(nullable TDConfig *)config]"); + +/** + Get default instance + + @return SDK instance + */ ++ (nullable ThinkingAnalyticsSDK *)sharedInstance DEPRECATED_MSG_ATTRIBUTE("Deprecated. please use class method [TDAnalytics ...]"); + +/** + Get one instance according to appid or instanceName + + @param appid APP ID or instanceName + @return SDK instance + */ ++ (nullable ThinkingAnalyticsSDK *)sharedInstanceWithAppid:(NSString *)appid DEPRECATED_MSG_ATTRIBUTE("Deprecated. please use class method [TDAnalytics ...withAppId:(NSString *)appId]"); + +#pragma mark - Action Track + +/** + Track Events + + @param event event name + */ +- (void)track:(NSString *)event DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics track:]"); + +/** + Track Events + + @param event event name + @param propertieDict event properties + */ +- (void)track:(NSString *)event properties:(nullable NSDictionary *)propertieDict DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics track:properties:]"); + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics track:properties:time:timeZone:]"); + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics track:properties:time:timeZone:]"); + +/** + Track Events + + @param eventModel event Model + */ +- (void)trackWithEventModel:(TDEventModel *)eventModel DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics trackWithEventModel]"); + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated"); + +#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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics timeEvent:]"); + +/** + Identify + Set the distinct ID to replace the default UUID distinct ID. + */ +- (void)identify:(NSString *)distinctId DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setDistinctId:]"); + +/** + Get Distinctid + Get a visitor ID: The #distinct_id value in the reported data. + */ +- (NSString *)getDistinctId DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics getDistinctId]"); + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics login:]"); + +/** + Logout + Clearing the account ID will not upload user logout events. + */ +- (void)logout DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userSet:]"); + +/** + User_Set + + @param properties user properties + @param time event trigger time +*/ +- (void)user_set:(NSDictionary *)properties withTime:(NSDate * _Nullable)time DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userSet:]"); + +/** + User_Unset + + @param propertyName user properties + */ +- (void)user_unset:(NSString *)propertyName DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userUnset:]"); + +/** + User_Unset + Reset user properties. + + @param propertyName user properties + @param time event trigger time +*/ +- (void)user_unset:(NSString *)propertyName withTime:(NSDate * _Nullable)time DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userUnset:]"); + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userSetOnce:]"); + +/** + User_SetOnce + + @param properties user properties + @param time event trigger time +*/ +- (void)user_setOnce:(NSDictionary *)properties withTime:(NSDate * _Nullable)time DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userSetOnce:]"); + +/** + User_Add + Adds the numeric type user attributes. + + @param properties user properties + */ +- (void)user_add:(NSDictionary *)properties DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userAdd:]"); + +/** + User_Add + + @param properties user properties + @param time event trigger time +*/ +- (void)user_add:(NSDictionary *)properties withTime:(NSDate * _Nullable)time DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userAdd:]"); + +/** + User_Add + + @param propertyName propertyName + @param propertyValue propertyValue + */ +- (void)user_add:(NSString *)propertyName andPropertyValue:(NSNumber *)propertyValue DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userAddWithName:andValue:]"); + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userAddWithName:andValue:]"); + +/** + User_Delete + Delete the user attributes,This operation is not reversible and should be performed with caution. + */ +- (void)user_delete DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userDelete]"); + +/** + User_Delete + + @param time event trigger time + */ +- (void)user_delete:(NSDate * _Nullable)time DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userDelete]"); + +/** + User_Append + Append a user attribute of the List type. + + @param properties user properties +*/ +- (void)user_append:(NSDictionary *)properties DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userAppend:]"); + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userAppend:]"); + +/** + User_UniqAppend + + @param properties user properties +*/ +- (void)user_uniqAppend:(NSDictionary *)properties DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userUniqAppend:]"); + +/** + User_UniqAppend + + @param properties user properties + @param time event trigger time +*/ +- (void)user_uniqAppend:(NSDictionary *)properties withTime:(NSDate * _Nullable)time DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics userUniqAppend:]"); + + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setSuperProperties:]"); + +/** + Unset Super Property + Clears a public event attribute. + */ +- (void)unsetSuperProperty:(NSString *)property DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics unsetSuperProperty:]"); + +/** + Clear Super Properties + Clear all public event attributes. + */ +- (void)clearSuperProperties DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics clearSuperProperties]"); + +/** + Get Static Super Properties + Gets the public event properties that have been set. + */ +- (NSDictionary *)currentSuperProperties DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics getSuperProperties]"); + +/** + Dynamic super properties + Set dynamic public properties. Each event uploaded after that will contain a public event attribute. + */ +- (void)registerDynamicSuperProperties:(NSDictionary *(^)(void))dynamicSuperProperties DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setDynamicSuperProperties:]"); + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics registerErrorCallback:]"); + +/** + Gets prefabricated properties for all events. + */ +- (TDPresetProperties *)getPresetProperties DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics getSuperProperties]"); + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setUploadingNetworkType:]"); + +#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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics enableAutoTrack:]"); + +/** + Enable the auto tracking function. + + @param eventType Auto-Tracking type + @param properties properties + */ +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType properties:(NSDictionary *)properties DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics enableAutoTrack: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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics enableAutoTrack: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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setAutoTrackProperties:]"); + +/** + Ignore the Auto-Tracking of a page + + @param controllers Ignore the name of the UIViewController + */ +- (void)ignoreAutoTrackViewControllers:(NSArray *)controllers DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics ignoreAutoTrackViewControllers:]"); + +/** + Ignore the Auto-Tracking of click event + + @param aClass ignored controls Class + */ +- (void)ignoreViewType:(Class)aClass DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics ignoreViewType:]"); + +/** + Dynamic super properties in auto track environment. + Set dynamic public properties for auto track event + */ +- (void)setAutoTrackDynamicProperties:(NSDictionary *(^)(void))dynamicSuperProperties DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setAutoTrackDynamicProperties:]"); + +#endif + +//MARK: - + +/** + Get DeviceId + */ +- (NSString *)getDeviceId DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics getDeviceId]"); + +/** + 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 DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics flush]"); + +/** + Switch reporting status + + @param status TDTrackStatus reporting status + */ +- (void)setTrackStatus:(TATrackStatus)status DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setTrackStatus:]"); + +- (void)enableTracking:(BOOL)enabled DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setTrackStatus:]"); +- (void)optOutTracking DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setTrackStatus:]"); +- (void)optOutTrackingAndDeleteUser DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setTrackStatus:]"); +- (void)optInTracking DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics setTrackStatus:]"); + +/** + Create a light instance + */ +- (ThinkingAnalyticsSDK *)createLightInstance DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics lightInstanceIdWithAppId] and [TDAnalytics ...withAppId:(NSString *)appId]"); + +- (NSString *)getTimeString:(NSDate *)date DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics timeStringWithDate:]"); + +#if TARGET_OS_IOS +- (void)enableThirdPartySharing:(TAThirdPartyShareType)type DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics enableThirdPartySharing:]"); +- (void)enableThirdPartySharing:(TAThirdPartyShareType)type customMap:(NSDictionary *)customMap DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics enableThirdPartySharing:properties:]"); +#endif + +/// Deprecated. replace with: +showUpWebView:withRequest: +/// @param webView webView +/// @param request NSURLRequest +/// @return YES:Process this request NO: This request has not been processed +- (BOOL)showUpWebView:(id)webView WithRequest:(NSURLRequest *)request DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics showUpWebView:withRequest:]"); + +/// Deprecated. replace with: +addWebViewUserAgent +- (void)addWebViewUserAgent DEPRECATED_MSG_ATTRIBUTE("Deprecated. replace with: [TDAnalytics addWebViewUserAgent]"); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.h.meta new file mode 100644 index 0000000..d973a7a --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 407b7f91e5ee74e38998c9e3176bf949 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.m b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.m new file mode 100644 index 0000000..e7a4c0b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.m @@ -0,0 +1,315 @@ +// +// ThinkingAnalyticsSDK+OldPublic.m +// ThinkingSDK +// +// Created by 杨雄 on 2023/8/10. +// + +#import "ThinkingAnalyticsSDK+OldPublic.h" +#import "ThinkingAnalyticsSDKPrivate.h" +#import "TDAnalytics+Public.h" +#import "TDAnalytics+Multiple.h" +#import "TDConfigPrivate.h" +#import "TDAnalytics+ThirdParty.h" +#import "TDAnalytics+WebView.h" + +@implementation ThinkingAnalyticsSDK (OldPublic) + +#pragma mark - Logging + ++ (void)setLogLevel:(TDLoggingLevel)level { + [TDLogging sharedInstance].loggingLevel = level; +} + +#pragma mark - Calibrate time + ++ (void)calibrateTime:(NSTimeInterval)timestamp { + [TDAnalytics calibrateTime:timestamp]; +} + ++ (void)calibrateTimeWithNtp:(NSString *)ntpServer { + [TDAnalytics calibrateTimeWithNtp:ntpServer]; +} + +// MARK: info + ++ (nullable NSString *)getLocalRegion { + return [TDAnalytics getLocalRegion]; +} + ++ (void)setCustomerLibInfoWithLibName:(NSString *)libName libVersion:(NSString *)libVersion { + [TDAnalytics setCustomerLibInfoWithLibName:libName libVersion:libVersion]; +} + ++ (NSString *)getSDKVersion { + return [TDAnalytics getSDKVersion]; +} + ++ (NSString *)getDeviceId { + return [TDAnalytics getDeviceId]; +} + ++ (NSString *)timeStringWithDate:(NSDate *)date { + return [TDAnalytics timeStringWithDate:date]; +} + +// MARK: init + ++ (ThinkingAnalyticsSDK *)startWithAppId:(NSString *)appId withUrl:(NSString *)url { + return [self startWithAppId:appId withUrl:url withConfig:nil]; +} + ++ (ThinkingAnalyticsSDK *)startWithAppId:(NSString *)appId withUrl:(NSString *)url withConfig:(nullable TDConfig *)config { + if (!config) { + config = [[TDConfig alloc] init]; + } + config.appid = appId; + config.serverUrl = url; + return [ThinkingAnalyticsSDK startWithConfig:config]; +} + ++ (ThinkingAnalyticsSDK *)startWithConfig:(TDConfig *)config { + [TDAnalytics startAnalyticsWithConfig:config]; + return [ThinkingAnalyticsSDK instanceWithAppid:[config innerGetMapInstanceToken]]; +} + ++ (nullable ThinkingAnalyticsSDK *)sharedInstance { + return [ThinkingAnalyticsSDK defaultInstance]; +} ++ (nullable ThinkingAnalyticsSDK *)sharedInstanceWithAppid:(NSString *)appid { + return [ThinkingAnalyticsSDK instanceWithAppid:appid]; +} +- (ThinkingAnalyticsSDK *)createLightInstance { + return [self innerCreateLightInstance]; +} +- (void)track:(NSString *)event { + [TDAnalytics track:event withAppId:[self instanceAliasNameOrAppId]]; +} +// TAThirdParty model used. +- (void)track:(NSString *)event properties:(nullable NSDictionary *)propertieDict { + [TDAnalytics track:event properties:propertieDict withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)track:(NSString *)event properties:(nullable NSDictionary *)propertieDict time:(NSDate *)time { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + [self track:event properties:propertieDict time:time timeZone:nil]; +#pragma clang diagnostic pop +} +- (void)track:(NSString *)event properties:(nullable NSDictionary *)propertieDict time:(NSDate *)time timeZone:(NSTimeZone *)timeZone { + [TDAnalytics track:event properties:propertieDict time:time timeZone:timeZone withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)trackWithEventModel:(TDEventModel *)eventModel { + [TDAnalytics trackWithEventModel:eventModel withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)trackFromAppExtensionWithAppGroupId:(NSString *)appGroupId { + // deprecated +} +- (void)timeEvent:(NSString *)event { + [TDAnalytics timeEvent:event withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)identify:(NSString *)distinctId { + [TDAnalytics setDistinctId:distinctId withAppId:[self instanceAliasNameOrAppId]]; +} + +// TAThirdParty model used. +- (NSString *)getDistinctId { + return [TDAnalytics getDistinctIdWithAppId:[self instanceAliasNameOrAppId]]; +} + +- (void)login:(NSString *)accountId { + [TDAnalytics login:accountId withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)logout { + [TDAnalytics logoutWithAppId:[self instanceAliasNameOrAppId]]; +} +- (void)user_set:(NSDictionary *)properties { + [TDAnalytics userSet:properties withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)user_set:(NSDictionary *)properties withTime:(NSDate * _Nullable)time { + TDUserEventSet *event = [[TDUserEventSet alloc] init]; + if (time) { + event.time = time; + event.timeValueType = TDEventTimeValueTypeTimeOnly; + } + [self asyncUserEventObject:event properties:properties isH5:NO]; +} +- (void)user_unset:(NSString *)propertyName { + [TDAnalytics userUnset:propertyName withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)user_unset:(NSString *)propertyName withTime:(NSDate * _Nullable)time { + if ([propertyName isKindOfClass:[NSString class]] && propertyName.length > 0) { + NSDictionary *properties = @{propertyName: @0}; + TDUserEventUnset *event = [[TDUserEventUnset alloc] init]; + if (time) { + event.time = time; + event.timeValueType = TDEventTimeValueTypeTimeOnly; + } + [self asyncUserEventObject:event properties:properties isH5:NO]; + } +} +- (void)user_setOnce:(NSDictionary *)properties { + [TDAnalytics userSetOnce:properties withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)user_setOnce:(NSDictionary *)properties withTime:(NSDate * _Nullable)time { + TDUserEventSetOnce *event = [[TDUserEventSetOnce alloc] init]; + if (time) { + event.time = time; + event.timeValueType = TDEventTimeValueTypeTimeOnly; + } + [self asyncUserEventObject:event properties:properties isH5:NO]; +} +- (void)user_add:(NSDictionary *)properties { + [TDAnalytics userAdd:properties withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)user_add:(NSDictionary *)properties withTime:(NSDate * _Nullable)time { + TDUserEventAdd *event = [[TDUserEventAdd alloc] init]; + if (time) { + event.time = time; + event.timeValueType = TDEventTimeValueTypeTimeOnly; + } + [self asyncUserEventObject:event properties:properties isH5:NO]; +} +- (void)user_add:(NSString *)propertyName andPropertyValue:(NSNumber *)propertyValue { + [TDAnalytics userAddWithName:propertyName andValue:propertyValue withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)user_add:(NSString *)propertyName andPropertyValue:(NSNumber *)propertyValue withTime:(NSDate * _Nullable)time { + if (propertyName && propertyValue) { + [self user_add:@{propertyName: propertyValue} withTime:time]; + } +} +- (void)user_delete { + [TDAnalytics userDeleteWithAppId:[self instanceAliasNameOrAppId]]; +} +- (void)user_delete:(NSDate * _Nullable)time { + TDUserEventDelete *event = [[TDUserEventDelete alloc] init]; + if (time) { + event.time = time; + event.timeValueType = TDEventTimeValueTypeTimeOnly; + } + [self asyncUserEventObject:event properties:nil isH5:NO]; +} +- (void)user_append:(NSDictionary *)properties { + [TDAnalytics userAppend:properties withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)user_append:(NSDictionary *)properties withTime:(NSDate * _Nullable)time { + TDUserEventAppend *event = [[TDUserEventAppend alloc] init]; + if (time) { + event.time = time; + event.timeValueType = TDEventTimeValueTypeTimeOnly; + } + [self asyncUserEventObject:event properties:properties isH5:NO]; +} +- (void)user_uniqAppend:(NSDictionary *)properties { + [TDAnalytics userUniqAppend:properties withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)user_uniqAppend:(NSDictionary *)properties withTime:(NSDate * _Nullable)time { + TDUserEventUniqueAppend *event = [[TDUserEventUniqueAppend alloc] init]; + if (time) { + event.time = time; + event.timeValueType = TDEventTimeValueTypeTimeOnly; + } + [self asyncUserEventObject:event properties:properties isH5:NO]; +} +- (void)setSuperProperties:(NSDictionary *)properties { + [TDAnalytics setSuperProperties:properties withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)unsetSuperProperty:(NSString *)property { + [TDAnalytics unsetSuperProperty:property withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)clearSuperProperties { + [TDAnalytics clearSuperPropertiesWithAppId:[self instanceAliasNameOrAppId]]; +} +- (NSDictionary *)currentSuperProperties { + return [TDAnalytics getSuperPropertiesWithAppId:[self instanceAliasNameOrAppId]]; +} +- (void)registerDynamicSuperProperties:(NSDictionary *(^)(void))dynamicSuperProperties { + [TDAnalytics setDynamicSuperProperties:dynamicSuperProperties withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)registerErrorCallback:(void(^)(NSInteger code, NSString * _Nullable errorMsg, NSString * _Nullable ext))errorCallback { + [TDAnalytics registerErrorCallback:errorCallback withAppId:[self instanceAliasNameOrAppId]]; +} +- (TDPresetProperties *)getPresetProperties { + return [TDAnalytics getPresetPropertiesWithAppId:[self instanceAliasNameOrAppId]]; +} +- (void)setNetworkType:(ThinkingAnalyticsNetworkType)type { + [TDAnalytics setUploadingNetworkType:(TDReportingNetworkType)type withAppId:[self instanceAliasNameOrAppId]]; +} +#if TARGET_OS_IOS +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType { + [TDAnalytics enableAutoTrack:(TDAutoTrackEventType)eventType withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType properties:(NSDictionary *)properties { + [TDAnalytics enableAutoTrack:(TDAutoTrackEventType)eventType properties:properties withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)enableAutoTrack:(ThinkingAnalyticsAutoTrackEventType)eventType callback:(NSDictionary *(^)(ThinkingAnalyticsAutoTrackEventType eventType, NSDictionary *properties))callback { + [TDAnalytics enableAutoTrack:(TDAutoTrackEventType)eventType callback:^NSDictionary *(TDAutoTrackEventType type, NSDictionary *dict){ + return callback((ThinkingAnalyticsAutoTrackEventType)type, dict); + } withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)setAutoTrackProperties:(ThinkingAnalyticsAutoTrackEventType)eventType properties:(NSDictionary *)properties { + [TDAnalytics setAutoTrackProperties:(TDAutoTrackEventType)eventType properties:properties withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)ignoreAutoTrackViewControllers:(NSArray *)controllers { + [TDAnalytics ignoreAutoTrackViewControllers:controllers withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)ignoreViewType:(Class)aClass { + [TDAnalytics ignoreViewType:aClass withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)setAutoTrackDynamicProperties:(NSDictionary * _Nonnull (^)(void))dynamicSuperProperties { + [TDAnalytics setAutoTrackDynamicProperties:dynamicSuperProperties withAppId:[self instanceAliasNameOrAppId]]; +} +#endif + +- (NSString *)getDeviceId { + return [TDAnalytics getDeviceId]; +} +- (void)flush { + [TDAnalytics flushWithAppId:[self instanceAliasNameOrAppId]]; +} +- (void)setTrackStatus: (TATrackStatus)status { + [TDAnalytics setTrackStatus:(TDTrackStatus)status withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)enableTracking:(BOOL)enabled { + [TDAnalytics setTrackStatus:enabled ? TDTrackStatusNormal : TDTrackStatusPause withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)optOutTracking { + [TDAnalytics setTrackStatus:TDTrackStatusStop withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)optOutTrackingAndDeleteUser { + TDUserEventDelete *deleteEvent = [[TDUserEventDelete alloc] init]; + deleteEvent.immediately = YES; + [self asyncUserEventObject:deleteEvent properties:nil isH5:NO]; + + [TDAnalytics setTrackStatus:TDTrackStatusStop withAppId:[self instanceAliasNameOrAppId]]; +} +- (void)optInTracking { + [TDAnalytics setTrackStatus:TDTrackStatusNormal withAppId:[self instanceAliasNameOrAppId]]; +} +- (NSString *)getTimeString:(NSDate *)date { + return [TDAnalytics timeStringWithDate:date withAppId:[self instanceAliasNameOrAppId]]; +} + +//MARK: - ThirdParty + +#if TARGET_OS_IOS +- (void)enableThirdPartySharing:(TAThirdPartyShareType)type { + [TDAnalytics enableThirdPartySharing:(TDThirdPartyType)type properties:@{} withAppId:[self instanceAliasNameOrAppId]]; +} + +- (void)enableThirdPartySharing:(TAThirdPartyShareType)type customMap:(NSDictionary *)customMap { + [TDAnalytics enableThirdPartySharing:(TDThirdPartyType)type properties:customMap withAppId:[self instanceAliasNameOrAppId]]; +} +#endif + +//MARK: - WebView + +- (void)addWebViewUserAgent { + [TDAnalytics addWebViewUserAgent]; +} + +- (BOOL)showUpWebView:(id)webView WithRequest:(NSURLRequest *)request { + return [TDAnalytics showUpWebView:webView withRequest:request withAppId:[self instanceAliasNameOrAppId]]; +} + + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.m.meta new file mode 100644 index 0000000..ea15a3c --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK+OldPublic.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 2c4b48d28bbd743b69c0db48d6a4ab66 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h new file mode 100644 index 0000000..25a5e7d --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h @@ -0,0 +1,50 @@ +#import + +#if TARGET_OS_IOS +#import + +#if __has_include() +#import +#else +#import "TDAutoTrackPublicHeader.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 + +@interface ThinkingAnalyticsSDK : NSObject + + +@end + +NS_ASSUME_NONNULL_END diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h.meta new file mode 100644 index 0000000..9033870 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 148eb28cb0e494f208a263fa8b46ec91 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m new file mode 100644 index 0000000..30cdd2e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m @@ -0,0 +1,1032 @@ +#import "ThinkingAnalyticsSDKPrivate.h" + +#if TARGET_OS_IOS + +#import "TDAutoTrackManager.h" +#import "TDAppLaunchReason.h" +#import "TDPushClickEvent.h" + +#endif + +#if __has_include() +#import +#else +#import "TDJSONUtil.h" +#endif +#if __has_include() +#import +#else +#import "TDNotificationManager+Analytics.h" +#endif +#if __has_include() +#import +#else +#import "TDCalibratedTime.h" +#endif +#if __has_include() +#import +#else +#import "TDCoreDeviceInfo.h" +#endif +#if __has_include() +#import +#else +#import "NSString+TDCore.h" +#endif + +#if __has_include() +#import +#else +#import "TDNotificationManager+Networking.h" +#endif + +#if __has_include() +#import +#else +#import "TDMediator+Analytics.h" +#endif + +#import "TDConfig.h" +#import "TDPublicConfig.h" +#import "TDFile.h" +#import "TDCheck.h" +#import "TDAppState.h" +#import "TDEventRecord.h" +#import "TDAppLifeCycle.h" +#import "TDAnalytics+Public.h" +#import "TDConfigPrivate.h" + +#if !__has_feature(objc_arc) +#error The ThinkingSDK library must be compiled with ARC enabled +#endif + +@interface TDPresetProperties (ThinkingAnalytics) + +- (instancetype)initWithDictionary:(NSDictionary *)dict; +- (void)updateValuesWithDictionary:(NSDictionary *)dict; + +@end + +@interface ThinkingAnalyticsSDK () +@property (nonatomic, strong) TDEventTracker *eventTracker; +@property (nonatomic, strong) TDFile *file; +@property (nonatomic, strong) TDSuperProperty *superProperty; +@property (nonatomic, strong) TDPropertyPluginManager *propertyPluginManager; +@property (nonatomic, strong) TDAppLifeCycle *appLifeCycle; +@property (atomic, assign) BOOL isOptOut; +@property (nonatomic, strong, nullable) NSTimer *timer; +@property (nonatomic, strong) TDTrackTimer *trackTimer; +@property (atomic, strong) TDSqliteDataQueue *dataQueue; + +@end + +@implementation ThinkingAnalyticsSDK + +static NSLock *g_lock; +static NSMutableDictionary *g_instances; +static NSString *defaultProjectAppid; +static dispatch_queue_t td_trackQueue; + ++ (NSString *)defaultAppId { + return defaultProjectAppid; +} + ++ (void)initialize { + static dispatch_once_t ThinkingOnceToken; + dispatch_once(&ThinkingOnceToken, ^{ + td_trackQueue = dispatch_queue_create("cn.thinkingdata.analytics.track", DISPATCH_QUEUE_SERIAL); + g_lock = [[NSLock alloc] init]; + }); +} + ++ (dispatch_queue_t)sharedTrackQueue { + return td_trackQueue; +} + ++ (dispatch_queue_t)sharedNetworkQueue { + return [TDEventTracker td_networkQueue]; +} + +- (ThinkingAnalyticsSDK *)innerCreateLightInstance { + ThinkingAnalyticsSDK *lightInstance = [[LightThinkingAnalyticsSDK alloc] initWithAPPID:self.config.appid withServerURL:self.config.serverUrl withConfig:self.config]; + lightInstance.identifyId = [TDDeviceInfo sharedManager].uniqueId; + lightInstance.propertyPluginManager = self.propertyPluginManager; + return lightInstance; +} + +- (instancetype)initLight:(NSString *)appid withServerURL:(NSString *)serverURL withConfig:(TDConfig *)config { + if (self = [self init]) { + self.isEnabled = YES; + self.config = [config copy]; + + // random instance name + NSString *instanceName = [NSUUID UUID].UUIDString; + self.config.name = instanceName; + + self.config.appid = appid; + self.config.serverUrl = serverURL; + + NSString *instanceIdentify = [self instanceAliasNameOrAppId]; + if (!instanceIdentify) { + return nil; + } + + [g_lock lock]; + g_instances[instanceIdentify] = self; + [g_lock unlock]; + + self.superProperty = [[TDSuperProperty alloc] initWithToken:instanceIdentify isLight:YES]; + + self.trackTimer = [[TDTrackTimer alloc] init]; + + self.dataQueue = [TDSqliteDataQueue sharedInstanceWithAppid:appid]; + if (self.dataQueue == nil) { + TDLogError(@"SqliteException: init SqliteDataQueue failed"); + } + + self.eventTracker = [[TDEventTracker alloc] initWithQueue:td_trackQueue instanceToken:instanceIdentify]; + } + return self; +} + +- (instancetype)initWithConfig:(TDConfig *)config { + if (self = [super init]) { + if (!config) { + return nil; + } + self.config = config; + + NSString *instanceAliasName = [self instanceAliasNameOrAppId]; + if (!instanceAliasName) { + return nil; + } + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + g_instances = [NSMutableDictionary dictionary]; + defaultProjectAppid = instanceAliasName; + }); + + [g_lock lock]; + g_instances[instanceAliasName] = self; + [g_lock unlock]; + + self.file = [[TDFile alloc] initWithAppid:instanceAliasName]; + [self retrievePersistedData]; + + self.superProperty = [[TDSuperProperty alloc] initWithToken:instanceAliasName isLight:NO]; + + dispatch_async(td_trackQueue, ^{ + self.dataQueue = [TDSqliteDataQueue sharedInstanceWithAppid:instanceAliasName]; + if (self.dataQueue == nil) { + TDLogError(@"SqliteException: init SqliteDataQueue failed"); + } + + self.propertyPluginManager = [[TDPropertyPluginManager alloc] init]; + TDPresetPropertyPlugin *presetPlugin = [[TDPresetPropertyPlugin alloc] init]; + presetPlugin.instanceToken = [self instanceAliasNameOrAppId]; + [self.propertyPluginManager registerPropertyPlugin:presetPlugin]; + }); + + self.config.getInstanceName = ^NSString * _Nonnull{ + return instanceAliasName; + }; + +#if TARGET_OS_IOS + dispatch_async(td_trackQueue, ^{ + if (self.config.innerEnableEncrypt) { + self.encryptManager = [[TDEncryptManager alloc] initWithSecretKey:self.config.innerSecretKey]; + } + __weak __typeof(self)weakSelf = self; + [self.config innerUpdateConfig:^(NSDictionary * _Nonnull secretKey) { + if (weakSelf.config.innerEnableEncrypt && secretKey) { + [weakSelf.encryptManager handleEncryptWithConfig:secretKey]; + } + }]; + [self.config innerUpdateIPMap]; + }); +#elif TARGET_OS_OSX + [self.config innerUpdateConfig:^(NSDictionary * _Nonnull secretKey) {}]; +#endif + + self.trackTimer = [[TDTrackTimer alloc] init]; + + self.ignoredViewControllers = [[NSMutableSet alloc] init]; + self.ignoredViewTypeList = [[NSMutableSet alloc] init]; + + self.eventTracker = [[TDEventTracker alloc] initWithQueue:td_trackQueue instanceToken:instanceAliasName]; + + [self startFlushTimer]; + + [TDAppLifeCycle startMonitor]; + + [self registerAppLifeCycleListener]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStatusChangedNotification:) name:kNetworkNotificationNameStatusChange object:nil]; + +#if TARGET_OS_IOS + NSDictionary *ops = [TDAppLaunchReason getAppPushDic]; + if(ops != nil){ + TDPushClickEvent *pushEvent = [[TDPushClickEvent alloc]initWithName: @"te_ops_push_click"]; + pushEvent.ops = ops; + [self autoTrackWithEvent:pushEvent properties:@{}]; + [self innerFlush]; + } + [TDAppLaunchReason clearAppPushParams]; +#endif + + TDLogInfo(@"initialize success. Mode: %@\n AppID: %@\n ServerUrl: %@\n TimeZone: %@\n DeviceID: %@\n Lib: %@\n LibVersion: %@", [self modeEnumToString:self.config.mode], self.config.appid, self.config.serverUrl, self.config.defaultTimeZone ?: [NSTimeZone localTimeZone], [TDAnalytics getDeviceId], [[TDDeviceInfo sharedManager] libName] ,[[TDDeviceInfo sharedManager] libVersion]); + + [TDNotificationManager postAnalyticsInitEventWithAppId:instanceAliasName serverUrl:self.config.serverUrl]; + + [[TDMediator sharedInstance] registerSuccessWithTargetName:kTDMediatorTargetAnalytics]; + } + return self; +} + +- (void)registerAppLifeCycleListener { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter addObserver:self selector:@selector(appStateWillChangeNotification:) name:kTDAppLifeCycleStateWillChangeNotification object:nil]; + [notificationCenter addObserver:self selector:@selector(appStateDidChangeNotification:) name:kTDAppLifeCycleStateDidChangeNotification object:nil]; +} + +- (void)networkStatusChangedNotification:(NSNotification *)notification { + NSString *status = [notification.userInfo objectForKey:kNetworkNotificationParamsNetworkType]; + if (![status isEqualToString:@"NULL"]) { + [self innerFlush]; + } +} + +- (NSString*)modeEnumToString:(TDMode)enumVal { + NSArray *modeEnumArray = [[NSArray alloc] initWithObjects:@"Normal", @"DebugOnly", @"Debug", nil]; + return [modeEnumArray objectAtIndex:enumVal]; +} + +- (NSString *)instanceAliasNameOrAppId { + return [self.config innerGetMapInstanceToken]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"[ThinkingAnalyticsSDK] AppID: %@, ServerUrl: %@, Mode: %@, TimeZone: %@, DeviceID: %@, Lib: %@, LibVersion: %@", self.config.appid, self.config.serverUrl, [self modeEnumToString:self.config.mode], self.config.defaultTimeZone, [TDAnalytics getDeviceId], [[TDDeviceInfo sharedManager] libName] ,[[TDDeviceInfo sharedManager] libVersion]]; +} + +- (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) = ^{ + @synchronized (TDSqliteDataQueue.class) { + [self.dataQueue deleteAll:[self instanceAliasNameOrAppId]]; + } + [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 - 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.file deleteOldLoginId]; + } +} + +- (void)deleteAll { + dispatch_async(td_trackQueue, ^{ + @synchronized (TDSqliteDataQueue.class) { + [self.dataQueue deleteAll:[self instanceAliasNameOrAppId]]; + } + }); +} + +//MARK: - AppLifeCycle + +- (void)appStateWillChangeNotification:(NSNotification *)notification { + TDAppLifeCycleState newState = [[notification.userInfo objectForKey:kTDAppLifeCycleNewStateKey] integerValue]; + + if (newState == TDAppLifeCycleStateEnd) { + [self stopFlushTimer]; + } +} + +- (void)appStateDidChangeNotification:(NSNotification *)notification { + TDAppLifeCycleState newState = [[notification.userInfo objectForKey:kTDAppLifeCycleNewStateKey] integerValue]; + + if (newState == TDAppLifeCycleStateStart) { + [self startFlushTimer]; + NSTimeInterval systemUpTime = [TDCoreDeviceInfo bootTime]; + [self.trackTimer enterForegroundWithSystemUptime:systemUpTime]; + } else if (newState == TDAppLifeCycleStateEnd) { + NSTimeInterval systemUpTime = [TDCoreDeviceInfo bootTime]; + [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 == TDAppLifeCycleStateTerminate) { + dispatch_sync(td_trackQueue, ^{}); + [self.eventTracker flush]; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + dispatch_queue_t networkQueue = [TDEventTracker td_networkQueue]; + dispatch_async(networkQueue, ^{ + dispatch_semaphore_signal(semaphore); + }); + dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC))); + } +} + +// MARK: - + ++ (NSString *)getNetWorkStates { +#if TARGET_OS_IOS + return [TDCoreDeviceInfo networkType]; +#else + return @"--"; +#endif +} + +#pragma mark - Private + +- (void)asyncTrackEventObject:(TDTrackEvent *)event properties:(NSDictionary * _Nullable)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 handleTimeEvent:event]; + [self calibratedTimeWithEvent:event]; + + dispatch_async(td_trackQueue, ^{ + @autoreleasepool { + event.dynamicSuperProperties = [self.superProperty obtainDynamicSuperProperties]; + [self trackEvent:event properties:[properties copy] isH5:isH5]; + } + }); +} + +- (void)asyncUserEventObject:(TDUserEvent *)event properties:(NSDictionary * _Nullable)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 calibratedTimeWithEvent:event]; + + dispatch_async(td_trackQueue, ^{ + @autoreleasepool { + [self trackUserEvent:event properties:[properties copy] isH5:NO]; + } + }); +} + +- (void)calibratedTimeWithEvent:(TDBaseEvent *)event { + if (event.timeValueType == TDEventTimeValueTypeNone) { + event.time = [TDCalibratedTime now]; + } +} + ++ (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] + ; +} + +//MARK: - + +- (void)trackUserEvent:(TDUserEvent *)event properties:(NSDictionary *)properties isH5:(BOOL)isH5 { + + if (!event.isEnabled || event.isOptOut) { + return; + } + + if ([TDAppState shareInstance].relaunchInBackground && !self.config.trackRelaunchedInBackgroundEvents) { + return; + } + + [event.properties addEntriesFromDictionary:[TDPropertyValidator validateProperties:properties validator:event]]; + + if (event.timeZone == nil) { + event.timeZone = self.config.defaultTimeZone ?: [NSTimeZone localTimeZone]; + } + + NSDictionary *jsonObj = [event formatDateWithDict:event.jsonObject]; + + [self.eventTracker track:jsonObj immediately:event.immediately saveOnly:event.isTrackPause]; + TDLogInfo(@"user event success"); +} + +- (void)trackEvent:(TDTrackEvent *)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; + } + + 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]; + + NSDictionary *superProperties = [TDPropertyValidator validateProperties:self.superProperty.currentSuperProperties validator:event]; + + NSDictionary *dynamicSuperProperties = [TDPropertyValidator validateProperties:event.dynamicSuperProperties validator:event]; + + if (event.timeZone == nil) { + event.timeZone = self.config.defaultTimeZone ?: [NSTimeZone localTimeZone]; + } + + 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]; + [event.properties addEntriesFromDictionary:superProperties]; +#if TARGET_OS_IOS + if ([event isKindOfClass:[TDAutoTrackEvent class]]) { + TDAutoTrackEvent *autoEvent = (TDAutoTrackEvent *)event; + NSDictionary *autoSuperProperties = [self.autoTrackSuperProperty currentSuperPropertiesWithEventName:event.eventName]; + autoSuperProperties = [TDPropertyValidator validateProperties:autoSuperProperties validator:autoEvent]; + [event.properties addEntriesFromDictionary:autoSuperProperties]; + } +#endif + [event.properties addEntriesFromDictionary:dynamicSuperProperties]; + + properties = [TDPropertyValidator validateProperties:properties validator:event]; + [event.properties addEntriesFromDictionary:properties]; + + jsonObj = event.jsonObject; + } + + jsonObj = [event formatDateWithDict:jsonObj]; + + [TDNotificationManager postAnalyticsTrackWithAppId:self.config.appid event:jsonObj]; + + if (event.isDebug) { + [self.eventTracker trackDebugEvent:jsonObj]; + } else { + [self.eventTracker track:jsonObj immediately:event.immediately saveOnly:event.isTrackPause]; + } + TDLogInfo(@"track success"); +} + +#pragma mark - innerFlush 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(autoFlushWithTimer:) + 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: - Auto Track + +- (void)autoTrackWithEvent:(TDAutoTrackEvent *)event properties:(NSDictionary *)properties { + [self handleTimeEvent:event]; + [self asyncAutoTrackEventObject:event properties:properties]; +} + +/// Add event to event queue +- (void)asyncAutoTrackEventObject:(TDAutoTrackEvent *)event properties:(NSDictionary *)properties { + event.isEnabled = self.isEnabled; + event.trackPause = self.isTrackPause; + event.isOptOut = self.isOptOut; + event.accountId = self.accountId; + event.distinctId = self.identifyId; + + [self calibratedTimeWithEvent:event]; + + NSDictionary *dynamicProperties = [self.superProperty obtainDynamicSuperProperties]; + + NSMutableDictionary *autoTrackDynamicProperties = [NSMutableDictionary dictionary]; + [autoTrackDynamicProperties addEntriesFromDictionary:[self.autoTrackSuperProperty obtainAutoTrackDynamicSuperProperties]]; + [autoTrackDynamicProperties addEntriesFromDictionary:[self.autoTrackSuperProperty obtainDynamicSuperPropertiesWithType:event.autoTrackEventType currentProperties:event.properties]]; + + 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 copy] 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; +} + +- (TDAutoTrackSuperProperty *)autoTrackSuperProperty { + if (!_autoTrackSuperProperty) { + _autoTrackSuperProperty = [[TDAutoTrackSuperProperty alloc] init]; + } + return _autoTrackSuperProperty; +} + +#endif + +//MARK: - Private + +- (void)handleTimeEvent:(TDTrackEvent *)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 = [TDAppLifeCycle shareInstance].state == TDAppLifeCycleStateInit; + + 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]; + [self.trackTimer removeEvent:trackEvent.eventName]; + } else { + if (trackEvent.eventName == TD_APP_END_EVENT) { + return; + } + } +} + ++ (NSMutableDictionary *)_getAllInstances { + NSMutableDictionary *dict = nil; + [g_lock lock]; + dict = [g_instances mutableCopy]; + [g_lock unlock]; + return dict; +} + ++ (void)track_crashEventWithMessage:(NSString *)msg { +#if TARGET_OS_IOS + [ThinkingExceptionHandler trackCrashWithMessage:msg]; +#endif +} + +//MARK: - SDK instance + ++ (nullable ThinkingAnalyticsSDK *)defaultInstance { + NSString *appId = [self defaultAppId]; + return [self instanceWithAppid:appId]; +} + ++ (nullable ThinkingAnalyticsSDK *)instanceWithAppid:(NSString *)appid { + appid = appid.td_trim; + if (appid == nil || appid.length == 0) { + appid = [ThinkingAnalyticsSDK defaultAppId]; + } + ThinkingAnalyticsSDK *sdk = nil; + [g_lock lock]; + sdk = g_instances[appid]; + [g_lock unlock]; + return sdk; +} + +//MARK: - track event + +- (void)innerTrack:(NSString *)event { + [self innerTrack:event properties:nil time:nil timeZone:nil]; +} +- (void)innerTrack:(NSString *)event properties:(NSDictionary *)propertieDict { + [self innerTrack:event properties:propertieDict time:nil timeZone:nil]; +} +- (void)innerTrackDebug:(NSString *)event properties:(NSDictionary *)propertieDict { + TDTrackEvent *trackEvent = [[TDTrackEvent alloc] initWithName:event]; + trackEvent.isDebug = YES; + [self handleTimeEvent:trackEvent]; + [self asyncTrackEventObject:trackEvent properties:propertieDict isH5:NO]; +} +- (void)innerTrack:(NSString *)event properties:(NSDictionary * _Nullable)propertieDict time:(NSDate * _Nullable)time timeZone:(NSTimeZone * _Nullable)timeZone { + TDTrackEvent *trackEvent = [[TDTrackEvent alloc] initWithName:event]; + if (time) { + trackEvent.time = time; + trackEvent.timeValueType = TDEventTimeValueTypeTimeOnly; + if (timeZone) { + trackEvent.timeZone = timeZone; + trackEvent.timeValueType = TDEventTimeValueTypeTimeAndZone; + } + } + + [self asyncTrackEventObject:trackEvent properties:propertieDict isH5:NO]; +} +- (void)innerTrackWithEventModel:(TDEventModel *)eventModel { + TDTrackEvent *baseEvent = nil; + if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK_FIRST]) { + TDTrackFirstEvent *trackEvent = [[TDTrackFirstEvent alloc] initWithName:eventModel.eventName]; + trackEvent.firstCheckId = eventModel.extraID; + baseEvent = trackEvent; + } else if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK_UPDATE]) { + TDTrackUpdateEvent *trackEvent = [[TDTrackUpdateEvent alloc] initWithName:eventModel.eventName]; + trackEvent.eventId = eventModel.extraID; + baseEvent = trackEvent; + } else if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK_OVERWRITE]) { + TDTrackOverwriteEvent *trackEvent = [[TDTrackOverwriteEvent alloc] initWithName:eventModel.eventName]; + trackEvent.eventId = eventModel.extraID; + baseEvent = trackEvent; + } else if ([eventModel.eventType isEqualToString:TD_EVENT_TYPE_TRACK]) { + TDTrackEvent *trackEvent = [[TDTrackEvent alloc] initWithName:eventModel.eventName]; + baseEvent = trackEvent; + } + + if (eventModel.time) { + baseEvent.time = eventModel.time; + baseEvent.timeValueType = TDEventTimeValueTypeTimeOnly; + if (eventModel.timeZone) { + baseEvent.timeZone = eventModel.timeZone; + baseEvent.timeValueType = TDEventTimeValueTypeTimeAndZone; + } + } + + [self asyncTrackEventObject:baseEvent properties:eventModel.properties isH5:NO]; +} +- (void)innerTimeEvent:(NSString *)event { + if ([self hasDisabled]) { + return; + } + NSError *error = nil; + [TDPropertyValidator validateEventOrPropertyName:event withError:&error]; + if (error) { + return; + } + [self.trackTimer trackEvent:event withSystemUptime:[TDCoreDeviceInfo bootTime]]; +} + +//MARK: - user id + +- (void)innerSetIdentify:(NSString *)distinctId { + if ([self hasDisabled]) { + return; + } + if (![distinctId isKindOfClass:[NSString class]]) { + TDLogError(@"identify cannot null", distinctId); + return; + } + NSString *trimmedId = [distinctId stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if (trimmedId.length == 0) { + TDLogError(@"accountId invald", distinctId); + return; + } + + TDLogInfo(@"Set distinct ID, Distinct Id = %@", distinctId); + + @synchronized (self.file) { + self.identifyId = distinctId; + [self.file archiveIdentifyId:distinctId]; + [TDNotificationManager postAnalyticsSetDistinctIdEventWithAppId:self.config.appid accountId:self.accountId distinctId:self.identifyId]; + } +} + +- (NSString *)innerDistinctId { + return self.identifyId; +} + +- (NSString *)innerAccountId { + return self.accountId; +} + +// TAThirdParty model used. +- (NSString *)getAccountId { + return [self innerAccountId]; +} + +- (void)innerLogin:(NSString *)accountId { + if ([self hasDisabled]) { + return; + } + if (![accountId isKindOfClass:[NSString class]]) { + TDLogError(@"accountId invald", accountId); + return; + } + NSString *trimmedId = [accountId stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if (trimmedId.length == 0) { + TDLogError(@"accountId invald", accountId); + return; + } + + TDLogInfo(@"Login SDK, AccountId = %@", accountId); + + @synchronized (self.file) { + self.accountId = accountId; + [self.file archiveAccountID:accountId]; + [TDNotificationManager postAnalyticsLoginEventWithAppId:self.config.appid accountId:self.accountId distinctId:self.identifyId]; + } +} +- (void)innerLogout { + if ([self hasDisabled]) { + return; + } + + TDLogInfo(@"Logout SDK."); + + @synchronized (self.file) { + self.accountId = nil; + [self.file archiveAccountID:nil]; + [TDNotificationManager postAnalyticsLogoutEventWithAppId:self.config.appid distinctId:self.identifyId]; + } +} + +//MARK: - user profile + +- (void)innerUserSet:(NSDictionary *)properties { + TDUserEventSet *event = [[TDUserEventSet alloc] init]; + [self asyncUserEventObject:event properties:properties isH5:NO]; +} +- (void)innerUserUnset:(NSString *)propertyName { + if ([propertyName isKindOfClass:[NSString class]] && propertyName.length > 0) { + NSDictionary *properties = @{propertyName: @0}; + TDUserEventUnset *event = [[TDUserEventUnset alloc] init]; + [self asyncUserEventObject:event properties:properties isH5:NO]; + } +} +- (void)innerUserUnsets:(NSArray *)propertyNames { + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + for (NSString *name in propertyNames) { + if ([name isKindOfClass:[NSString class]] && name.length > 0) { + dict[name] = @0; + } + } + if (dict.count > 0) { + TDUserEventUnset *event = [[TDUserEventUnset alloc] init]; + [self asyncUserEventObject:event properties:dict isH5:NO]; + } +} +- (void)innerUserSetOnce:(NSDictionary *)properties { + TDUserEventSetOnce *event = [[TDUserEventSetOnce alloc] init]; + [self asyncUserEventObject:event properties:properties isH5:NO]; +} + +- (void)innerUserAdd:(NSDictionary *)properties { + TDUserEventAdd *event = [[TDUserEventAdd alloc] init]; + [self asyncUserEventObject:event properties:properties isH5:NO]; +} +- (void)innerUserAdd:(NSString *)propertyName andPropertyValue:(NSNumber *)propertyValue { + if (propertyName && propertyValue) { + [self innerUserAdd:@{propertyName: propertyValue}]; + } +} +- (void)innerUserDelete { + TDUserEventDelete *event = [[TDUserEventDelete alloc] init]; + [self asyncUserEventObject:event properties:nil isH5:NO]; +} +- (void)innerUserAppend:(NSDictionary *)properties { + TDUserEventAppend *event = [[TDUserEventAppend alloc] init]; + [self asyncUserEventObject:event properties:properties isH5:NO]; +} +- (void)innerUserUniqAppend:(NSDictionary *)properties { + TDUserEventUniqueAppend *event = [[TDUserEventUniqueAppend alloc] init]; + [self asyncUserEventObject:event properties:properties isH5:NO]; +} + +//MARK: - super properties + +- (void)innerSetSuperProperties:(NSDictionary *)properties { + if ([self hasDisabled]) { + return; + } + + dispatch_async(td_trackQueue, ^{ + [self.superProperty registerSuperProperties:properties]; + }); +} +- (void)innerUnsetSuperProperty:(NSString *)property { + if ([self hasDisabled]) { + return; + } + dispatch_async(td_trackQueue, ^{ + [self.superProperty unregisterSuperProperty:property]; + }); +} +- (void)innerClearSuperProperties { + if ([self hasDisabled]) { + return; + } + dispatch_async(td_trackQueue, ^{ + [self.superProperty clearSuperProperties]; + }); +} +- (NSDictionary *)innerCurrentSuperProperties { + return [self.superProperty currentSuperProperties]; +} + +- (void)innerRegisterDynamicSuperProperties:(NSDictionary *(^)(void))dynamicSuperProperties { + if ([self hasDisabled]) { + return; + } + if (!dynamicSuperProperties) { + TDLogError(@"Ignoring empty"); + return; + } + @synchronized (self.superProperty) { + [self.superProperty registerDynamicSuperProperties:dynamicSuperProperties]; + } +} + +- (TDPresetProperties *)innerGetPresetProperties { + NSMutableDictionary *presetDic = [NSMutableDictionary dictionary]; + + NSDictionary *pluginProperties = [self.propertyPluginManager currentPropertiesForPluginClasses:@[TDPresetPropertyPlugin.class]]; + [presetDic addEntriesFromDictionary:pluginProperties]; + + NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init]; + timeFormatter.dateFormat = kDefaultTimeFormat; + timeFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + timeFormatter.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; + timeFormatter.timeZone = self.config.defaultTimeZone; + presetDic = [TDJSONUtil formatDateWithFormatter:timeFormatter dict:presetDic]; + + return [[TDPresetProperties alloc] initWithDictionary:presetDic]; +} + +//MARK: - SDK error callback + +- (void)innerRegisterErrorCallback:(void(^)(NSInteger code, NSString * _Nullable errorMsg, NSString * _Nullable ext))errorCallback { + self.errorCallback = errorCallback; +} + +//MARK: - + +- (BOOL)innerIsViewTypeIgnored:(Class)aClass { + return [self.ignoredViewTypeList containsObject:aClass]; +} + +- (void)autoFlushWithTimer:(NSTimer *)timer { + if ([self hasDisabled] || self.isTrackPause) { + return; + } + [self.eventTracker flush]; +} + +- (void)innerFlush { + if ([self hasDisabled] || self.isTrackPause) { + return; + } + TDLogInfo(@"flush success. By manual."); + [self.eventTracker flush]; +} + +- (void)innerSetNetworkType:(TDReportingNetworkType)type { + if ([self hasDisabled]) { + return; + } + self.config.reportingNetworkType = type; +} + +- (void)innerSetTrackStatus: (TDTrackStatus)status { + switch (status) { + case TDTrackStatusPause: { + TDLogInfo(@"Change status to Pause") + self.isEnabled = NO; + dispatch_async(td_trackQueue, ^{ + [self.file archiveIsEnabled:NO]; + }); + break; + } + case TDTrackStatusStop: { + TDLogInfo(@"Change status to Stop") + [self doOptOutTracking]; + break; + } + case TDTrackStatusSaveOnly: { + TDLogInfo(@"Change status to SaveOnly") + 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 TDTrackStatusNormal: { + TDLogInfo(@"Change status to Normal") + 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 innerFlush]; + break; + } + default: + break; + } +} + +- (NSString *)innetGetTimeString:(NSDate *)date { + return [date td_formatWithTimeZone:self.config.defaultTimeZone formatString:kDefaultTimeFormat]; +} + +@end diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m.meta new file mode 100644 index 0000000..bae4200 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDK.m.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: b5b9d9a9bb04b48a7958705ab2237622 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h new file mode 100644 index 0000000..c312e4e --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h @@ -0,0 +1,152 @@ +#if __has_include() +#import +#else +#import "ThinkingAnalyticsSDK.h" +#endif + +#import +#import +#import +#import +#import + +#if TARGET_OS_IOS +#import "ThinkingExceptionHandler.h" +#import "TDAutoTrackEvent.h" +#import "TDAutoTrackSuperProperty.h" +#import "TDEncrypt.h" +#else +#import "TDAutoTrackConst.h" +#endif + +#import "TDLogging.h" +#import "TDDeviceInfo.h" +#import "TDCommonUtil.h" +#import "TDConfig.h" +#import "TDSqliteDataQueue.h" +#import "TDEventModel.h" + +#import "TDTrackTimer.h" +#import "TDSuperProperty.h" +#import "TDTrackEvent.h" +#import "TDTrackFirstEvent.h" +#import "TDTrackOverwriteEvent.h" +#import "TDTrackUpdateEvent.h" +#import "TDUserPropertyHeader.h" +#import "TDPropertyPluginManager.h" +#import "TDPresetPropertyPlugin.h" +#import "TDBaseEvent+H5.h" +#import "TDEventTracker.h" +#import "TDAppLifeCycle.h" + +#if __has_include() +#import +#else +#import "NSDate+TDCore.h" +#endif + +NS_ASSUME_NONNULL_BEGIN + +#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" + +@interface ThinkingAnalyticsSDK () + +@property (atomic, copy, nullable) NSString *accountId; +@property (atomic, copy) NSString *identifyId; +/// TD error callback +@property (atomic, copy) void(^errorCallback)(NSInteger code, NSString * _Nullable errorMsg, NSString * _Nullable ext); +@property (atomic, assign, getter=isTrackPause) BOOL trackPause; +@property (atomic, assign) BOOL isEnabled; +@property (nonatomic, strong) TDConfig *config; +@property (atomic, strong) NSMutableSet *ignoredViewControllers; +@property (atomic, strong) NSMutableSet *ignoredViewTypeList; + +#if TARGET_OS_IOS +@property (nonatomic, strong) TDAutoTrackSuperProperty *autoTrackSuperProperty; +@property (nonatomic, strong) TDEncryptManager *encryptManager; +- (void)autoTrackWithEvent:(TDAutoTrackEvent *)event properties:(nullable NSDictionary *)properties; +- (BOOL)isViewControllerIgnored:(UIViewController *)viewController; +#endif + + ++ (dispatch_queue_t)sharedTrackQueue; ++ (dispatch_queue_t)sharedNetworkQueue; + +// TAThirdParty model used. +- (NSString *)getAccountId; +- (BOOL)hasDisabled; ++ (BOOL)isTrackEvent:(NSString *)eventType; +- (void)startFlushTimer; ++ (NSMutableDictionary *)_getAllInstances; ++ (NSString *)defaultAppId; +- (void)asyncTrackEventObject:(TDTrackEvent *)event properties:(NSDictionary * _Nullable)properties isH5:(BOOL)isH5; +- (void)asyncUserEventObject:(TDUserEvent *)event properties:(NSDictionary * _Nullable)properties isH5:(BOOL)isH5; + +- (instancetype)initWithConfig:(TDConfig *)config; +- (instancetype)initLight:(NSString *)appid withServerURL:(NSString *)serverURL withConfig:(TDConfig *)config; + ++ (nullable ThinkingAnalyticsSDK *)defaultInstance; ++ (nullable ThinkingAnalyticsSDK *)instanceWithAppid:(NSString *)appid; + +- (void)innerTrack:(NSString *)event; +- (void)innerTrack:(NSString *)event properties:(NSDictionary * _Nullable)propertieDict; +- (void)innerTrack:(NSString *)event properties:(NSDictionary * _Nullable)propertieDict time:(NSDate * _Nullable)time timeZone:(NSTimeZone * _Nullable)timeZone; +- (void)innerTrackWithEventModel:(TDEventModel *)eventModel; +- (void)innerTrackDebug:(NSString *)event properties:(NSDictionary * _Nullable)propertieDict; +- (void)innerTimeEvent:(NSString *)event; +- (NSString *)innerAccountId; +- (NSString *)innerDistinctId; +- (void)innerSetIdentify:(NSString *)distinctId; +- (void)innerLogin:(NSString *)accountId; +- (void)innerLogout; +- (void)innerUserSet:(NSDictionary *)properties; +- (void)innerUserUnset:(NSString *)propertyName; +- (void)innerUserUnsets:(NSArray *)propertyNames; +- (void)innerUserSetOnce:(NSDictionary *)properties; +- (void)innerUserAdd:(NSDictionary *)properties; +- (void)innerUserAdd:(NSString *)propertyName andPropertyValue:(NSNumber *)propertyValue; +- (void)innerUserDelete; +- (void)innerUserAppend:(NSDictionary *)properties; +- (void)innerUserUniqAppend:(NSDictionary *)properties; + +- (void)innerSetSuperProperties:(NSDictionary *)properties; +- (void)innerUnsetSuperProperty:(NSString *)property; +- (void)innerClearSuperProperties; +- (NSDictionary *)innerCurrentSuperProperties; +- (void)innerRegisterDynamicSuperProperties:(NSDictionary *(^)(void))dynamicSuperProperties; +- (void)innerRegisterErrorCallback:(void(^)(NSInteger code, NSString * _Nullable errorMsg, NSString * _Nullable ext))errorCallback; +- (TDPresetProperties *)innerGetPresetProperties; +- (void)innerSetNetworkType:(TDReportingNetworkType)type; + +- (BOOL)innerIsViewTypeIgnored:(Class)aClass; + +- (void)innerFlush; +- (void)innerSetTrackStatus:(TDTrackStatus)status; +- (ThinkingAnalyticsSDK *)innerCreateLightInstance; +- (NSString *)innetGetTimeString:(NSDate *)date; +- (NSString *)instanceAliasNameOrAppId; +@end + +@interface TDEventModel () +@property (nonatomic, copy) NSString *extraID; +@property (nonatomic, strong) NSDate *time; +@property (nonatomic, strong) NSTimeZone *timeZone; +- (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/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h.meta new file mode 100644 index 0000000..41a7507 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingAnalyticsSDKPrivate.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1b247f06bcd1a43b5b044a21027ec051 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h new file mode 100644 index 0000000..ca7b353 --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h @@ -0,0 +1,68 @@ + +#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 + +#if __has_include() +#import +#else +#import "TDAnalytics+Public.h" +#endif + +#if __has_include() +#import +#else +#import "TDAnalytics+Multiple.h" +#endif + +#if __has_include() +#import +#else +#import "TDAnalytics+WebView.h" +#endif + +#if __has_include() +#import +#else +#import "TDAnalytics+ThirdParty.h" +#endif + +#if __has_include() +#import +#else +#import "ThinkingAnalyticsSDK+OldPublic.h" +#endif diff --git a/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h.meta b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h.meta new file mode 100644 index 0000000..44a1c4b --- /dev/null +++ b/Assets/Plugins/iOS/ThinkingSDK/Source/main/ThinkingSDK.h.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 8e46d5322f9ed4eb6902a67f0c199583 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/tvOS/Firebase/libFirebaseCppCrashlytics.a b/Assets/Plugins/tvOS/Firebase/libFirebaseCppCrashlytics.a new file mode 100644 index 0000000000000000000000000000000000000000..e4aaaa76f8e25cc6e226877488d1f2590ed26d82 GIT binary patch literal 885592 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≤+4f*^U-2Da+^j(#UsZp zV>FzuCZq}x3-jMh6I4ganlG$)E(bg6sQUA2t@gHo)~q2j$Ko%I*ayWY^A^PDkx_TA zBbNN>9^G>H7a)RXv@l4qj4wYyYGiGoaFATH4WgK)Mv+no=NHHF1v)^@R0 z*ZGiW&Wc2h--DA+!8w^wt0`EDz$-#{i_FPHCH;?EcG9y?H!oUpYP0kiB_Hc4w^(MP zSf;T-IJr{eg4v>Ij?r@E+hCKNWhU%dN23@5TJdS#3E*_J;`(X0BCU7@0y-Te4Am)B z+2T|XpUAMQ8A20;T4AV*Ri>jiO-0i&Pvk1dBXs62NM|lZ?XB}>-ZXc2sTI8wqJs3* zjI40F169k#Wwne~p1FEH?P=HX8XYs3oW;Q#*QuL9$Ks6V44|OWCm{H2Kp4jk@M`(r>%!xhF=iF zs0M!=MK5jT}pwCrgZmJp3JWZ_$ZbYc9UPiejID4>#&nN$-oCQ}S*<&Pg&i-F2G7jvraT z7Zco7G1!=t#a92_5uEn-o-IT*Df9U}-|x5Ux}w)9X-BPK;y@d^_O%Gycg90HS?zci zZI+c@0eCp)L`k16#d9V2V6oVBD_llf5Q|RXU0$lQ7+Pp&nP_ZO?Ppzrnw;uWKyHhk zrmmpY`xmD^JTeuhvGxZnPtbr)obS^s35*6!DNM%cj*RhpL@quNrRSTeW5>3lD?JQ% zya|?Wd@ah1ogPfwdFkky&FHUsD5}8b5WsGj4%7(@9u8m))(70vui6rR_hAtN8S* zM+~v@6fJiAwH7T33T#Fn7OgS^o&5{2xtvXiOLcl{8*QgeG`%G&Z28J;SpC=Hv4Mcm z8J3DrZ3?##ctlXo7^Z`6?*&(%UZo(#1{- zO4q#CQiJZ%V9bRZVQTbT`DYKQC+_X->!+8$R3%N(7GuWpIGMY&U{S~v_Hf_39sj;T~0arIXhi8ce|HFVLAJFQdkL-Q4s z28rx!2RLiOXeh;0hd+W^#@z;eyVtmfQGuk8NK=tpZr{!5YR+3$T9Uli@40bNeCfY3 zUAPtCQ*gqfaada4Qz_?@ap)(-;-bGQ7Jx}l58#_kNI}0i4z*)wGA}KUzWcQ&G`1H4 zQ}xpN8JZbY($UC`9ciNU-dR+1U|q?=>o*t~VOol#xAJy}s_EsIfN{Q2epH_C#n}Uq zU_CZsW;%%1r@Lr$Din%(kjy0UJW~y{ipiP2w&2%DR9arNhGNI(h)N>{r>S%H<;vHo z7<=fW*ESmX1svS0FgMgL=`*V0N%=Om6{aj2Xy~zU6Ha8-QDp^{O3zW0hyFZP_te^e z0kUXD)c`*lq$z> z-hpZj57Mc|)suvKz*PKDmO4L!EXdZN-9DIAc)-p6APg6S_uzl=NrPCi@JUD1B+Or& z%H%tynIBZ<)S24RQ<2#1Uxn8vSs?+rLb9%Aa6YOd{uDh5Q9@Sq)}gIa2F?Ta;5t$I|e9{*SVo&40G1Zd?x;LGCcIX$XF zMLzB1NK^GH_7suvKK&c6C`4`XT5B74-=#0pR=i?H3nLYe<5Uur!P>m5`B*~YEgN6s zY#;2=*8Ur*y!%k(Y9lGO3>Ou<@$@S`)8UwO+p!o;4)uV%&8MXkYDJ&7%8G?tP5_W7E3;_h8Pm%@P!kwwvpT z2eVAvViixrYUV+e)GcYioh6!`QJ`|(OoRUt-$p6xO*M>Ft9nw6k5p?H_{vm;tCOYL zEPL%*Klmp`i;1)34ALn(98^J)PJ2|Y@Y?Ze`jlzq5xHzmMHI%Hj$-PIopJvBmY$$} zP^QqI+Q?Q-f!jO-DuC&9V&yCHGs5a>xAsn<7>vr&#^SkpiJ~wsx`j9gi^PUydD)ty zmAm%bQ5D&0EIC_3-0~Y_)Ou~JQfSOQ)ScN&4GD(XUCAX09Ol8(PANR`lvs);rr<A-CZLRDg0q2Qgr3eIQv7K2u0aU7?NVleiZ?qq_> z!9IcOsvssgoHEc1Dj*40#QHKB7BzZte$1YOE@`7fh2@FwT26A!d`j(Q1EjCOP?3X* zI@#iqU>sdlAIta6v_6(BNW7#c_uA4qTt(#2mVLR{{3dCU zrYb<+H7{d&jD@BQ-AR`=w5q?pB{CfxTHkWGtk1Plw%#St`W^QhYgJfkL49{M{qEYf z9^Ww(EvGzJfsH=>6+?McgwOn7BF^u>cwdx34e<;NiZ|70zg(9}v80dWwQX~mViQK_ zrh`!85}oHc7Kl-LuS;`I`N!wAKO8GStKzKtU|i|{^iBCWrx1u+i%u*q(@u}LGEm(n zxo_0v3n?VArPK!Wt`4D$N85tz0n0bFd9#hq_~Yk3tp+o8o;QmvOLpU&vCo_$SV&ah z!YAAGJ~ABqWLr=w#uwZ4bPC>CD;}$;;iio3Cp$wA%l z9EZX!M_fTu6J?rF(g}60uj}iDwSeLjXSw90-pPQv{)_WmxS5Sdme78wl?k-n`nBVA zwsE$;yXnGny1XA;`7zysPFh!%K7*=!AV4b=^ucbY*3~9nny}F4?n~ZYsXPn(9Tc&^ z>RuC&2e#=Zlhc7#o{pTw^KFAzUvH8}_>St@oHOF|7>|}}L1P^Bz8RWc`^C*ZCk0%~ zLj7?@18`Kk%~s?i=QT}-#sj{Sqb706fDZcb!Ec%m$`%DnIqs(fy?%=~POgc&NXFpwM&&Z zvXjsXWc7ua!Wn~X)wLl-<*7~X^!+SU)g3o&GbiP>w1Q5;-Vo;ktXz6p*S4=cWS3=9 zy=Q4Xh1S4So`ft8W7q0h`*WYd4crmM1^@#&MKRt0(evl@N z7~9!t((x<%%9!UxG^}dWBxTB}SPg>ECH4mHDiF|onypn|>+05D00=A{V+GKoEN_9V zr^m4ueQpqZkh@hJ-p(#hXxC5M#A$fI>{(=HetXz9=d7 zxA^(h>FyWOX$`HU6{iYPhf?NtG?DqO?Qe7D=@0wBzAy75H7tl4`6{ zE&4uO8_EjH>2htLpg*Wha@uBF@#M53a`aJ{*`Nkm?Qi(rc2VcTXz90_k*0qSUU`gF ze67&w$|_GD?=)fLtZzb8f4IGAN)}%z=)|X|dudUpKauK9W<>YktnuL3g`-J&G@qU~ z13(zPq-u5ENIUvRZ${B@pup%WS7OogRdg;>Y)Z)Shc=h2Rp#`1X3E^UJU1nZ&-0>E z70UEk3KUJdV8UW}%1ezik)<+EyQ6j1cID+-tp8#6YFB{P!DbK?BKFM^*=%ZQZ^7e@ zo(8e~c2DD{ogDUCsSli! zQ+`T3`0u&k>izh4KvJ@_usS2hYXxb7Ui3rXRVWk#ltJfKjlN9a^Y!q6Z1{gE90pkU zjoN^iFnz|=cwhbd>jGX&TS3C zZL?#Y*=31LT08TJ@!OoHuz{y;Es&?iDY_>thNpU$iX$uZNrO4DiJ-`TCx#iZWqitl zwvPUAW8VPAVM}7-aF-$$T#evRO^nTqlNcv6qMJBn!HM2jnc2b!>*A#K7f$VvULu6_ ze@~F33pc3$pu?1@7l?1!zxvM3uvHHQ;TWd1qGfVya&vtm4M%vPP_n;1+jUkwKEap8fOcxQWnv=&4W_cG#i{O8|H}GQZ#M?N+4^OvuC8QnJ+43X zr#kED$ZU80U;+Xx>FulU>FXN67rhc`k<9Y??7-r#6u$os30Kxafxm|NZ3cd5L&<10@Z-$C zz`!46{&E99zL8{f8Tf0MzlVW8#{86lAKzFq_B8Nw%r~V!#{33D_z)NTA_G6i{9jAH zU4ARL{NkHR#tRWj|ZUfX{j2x2}?e zaa&5<#1no;=7+YD{98QXk751@^EdK@KZ*Hkww3{Z?g>A{{5)sOWM83eWx%r}$W-M2 z#lJ($Ki8A~eH{O|?WE_wdHC-$-`YX)z4C{ymh^EuO8lLKX%Z~!B<7DX-y6Q0`PR-H z!jt}eioc8GKktcuHS^an{}m7aedZ7EDjAbJ@mtqO`q*v~|LWn-Wd0~OOy2VER^ho~ zayx71KhOL;S3FbuGRAyU`_izNzx+So_|xMh|2rP}tuIS@!vu+5{U6Bu$lj7a#}huz z{2^{Q+!{T{ck^n9}+{4n!Z82AnQNycpk{u<`5H1ON^FCKo3`KIvc z1B!|5O9tI#@DH>9;dKVF*9Md{h1+hsc1Y z@WagKetw$tZCU5CeTC{J<#%`QG@4IR0^m$%s>i_;bu(Y~YVE-;{oc zJH}Q+_&D=J27ZqD=NR}_STfiQ3-aHgdYlse}ws?a-3wB-+ip#e6y6# zG`<{V{^5rFg(gXcNxpXGb2C>Uf13GROa=Tr^I6RW{58zKQHo*bZ#CyHJy|k#k|AyW z&zL_nMe{MnWB%@j@OkEMW8e=5@JE<$svl#_4;kVgccf%+GghF#2=iAN!mnZe z?gsuC^CJd+!&J#Q-M}AWzRCVam~Tpdl=)l@Z2Nhi?I(1UOxP5EJM&HY%`xAkzu^G> zDDzG7TSrUzO#Be@jqMBbjq)?!WPd}<=W1SHA8VL@grWS#MI^(-Z)g6!hVT}bn5qA4 zXFiKjAm0%4P31ene3O30m~Ybmxam@G6TgA^y@vednSZQ-KgN8M{Gk~#pvnH)1NgZB z{%`<)l=0vG2}1Je3Sl$m~XPrd_eqT%-_wB{vi}_Oxtl7m zzY*q}?BALt(>L)W%)i$l{}}U4^&`R^x2b;Qm~XPLVdn2+NPm?1Ci`yZf;ZWBp82Nq z*D&8?-&R}k@@oj-#{>97%skRA?eym}>Nxm`W^Dw?ZzYPmy{!HV8H1kdA4>5lWgZ}c&KhwY; zw@@-n^)JHw?G54M%s2HPWKseOttpO+Es_`5m& z9PeYZ+85jaQu-$ z$uPB#Ip&+@k9p>s=5Hg+H_11~d{g_?uw2S#O23`?ruv;`K9^5{{;U@(JH-6QWikcf+gD1)!3KVg`9~S}Bg{ATe`CylN`~ibSk`9OO2Wt&B|hrm@4@`^ zIg(+rkKq9R81v&Yn!@~_D;dW8GvAnh=I?5Vf0X&H27c%~$uNbFFyEAZj`=Ny_(zz} zYA(osWZ!Y-o9h1v^E(Xbhc1u|6F8&9C zaoq4Wdg9M<{O!!&#}Gcpe3Si-GT)@1G3N96-~#y~Jn%E6pJx662Kn;LXEheY-*ANt z*kA}BXMV`Q&oO^b13%CFrzC%x1j`y?{hofMWsTHX^$QMhkqya;#>=u3egg1!j& z1aKDccfdv<$+;hp^0hO&HwTh_-iBd34f-#Dt#E%I_*1yA1-=JN04d#*fP=uPjQazp zz`Z??;@KS75BwqO`*`4EzzM)rKnnjwU=#3cU;}UoyN?HwJ|_c7|9b=X1?~hSJ*-9h z^cm#16?i7xmjEe0Ng&z90^nbPbAWl^hQOPF|GY^0dmDHv!o31~3-q4=$zGmh`h$#D z0j~o89NHsLeMt{ zJ_+|57&pBD_pQMD;64*b<$NlT?4+G>KOouVPQWLCn*m9G?_$6~_VNgj!hHou_HiSS z?Bfb{pAGDR`wSr2$FV@NkEuYij|L#=eJwhW>)^fz2o=@e$e04+Y4DUxRx*<0io8;r;;gq>q5F0a119 zp97LUe*pXq@FC!@fGOb5fm4BR0PBHy#QQGhX%znmkjmo`AcemkNa4Q(90hhWb^@v1 zEC9X*`b@?ariU0eWBf*S&PaC$ zkmNiTNOp7>ljy?jC9X$)Aa1R5iJZ=S2elB860&xeWejf0*pdSq+ zxpn~F1gry6xqg_I@qQWj7q~A4b|Ks{;1_{YfYg3$10*~79P@9olOF=99nS;*hHy6k zNzMy^WbZS9WIta3Qa`l?@NVD-y^_BccnjRG0xyNXyMYwnmx0tTECg-~+#L8A@RJ_d zZvPRu2H{=?qN}O@A&}bdJAtD82JQy`6MzQ-*J3?|`mu+AB;VaYY9G!8{uy`zaC`8> zz$f9}9JmqO@17<5xqE?>{&hfVm(F9H2Yedtqkyg89}FZp>VUrot~}GS8bLn+*bLkb zNaeK|kjnXkl*HG7li~g*ko5KtkjnRL;LV8VbRg;f5a5=;@j&voGw?P_4|osoxn-8M z6!aT_Vc>iq5T|f)?3Fwz6E$j61;^++C{w@D$711h^XbDDZyZ$Kc<=cs7vY?*iTq{zBjn zftv!!|6fnGtY^W0jleTqf#(Bf0?GfM7g^RHfJ=d2fd5~gXjumV zBfvQDl7+H;JqY-B_#0bbSu;Uj+hJM1hWkR`gK(b!d=>5qz!2Q;&bO?+flmQ{1H2!I zI|%g`0!M-80@uJ^2Y{~v&jkJw^fQ2O0V6;QI1%_G;GV!S#Q)6`ENdUQ?*>x+8UX$q z^o79Bfx7{}3i^lbmi1q_p9H=I>;+PNUkoIBi30xv{Ovr;x&ing@HNPBCy?q-3itr% zbAVKTX8_*@P6U$t2LWGz|Gj}P19t-65C0ngN&ctiTGq92*8?g3je&=P{_*h^U2Us> z33wmyA>dTdR|1a!E&%QToCn+(Dw_x#fqMtwk-!are?+)X=SY2g2+YI%6mU=QZvcJ^ z?h}9)0;d9h0Nf5p@qBn3)C#;7cpUHq;19vy6L>7#Zyt;B1n@cF)1a>gz6iV>cp30w zAk~XF@EzcGz+K?~sW!{npW*|a4E*pI+@S9|6vTdjPl%un9N}+#C24@ONT-BZBrB^q&Er2K{@$Ujpv| zUIx4dNcrpmz6%TkcZL5CkG8A>fR6#s0H%N>M*?^O@JQgFfZGALBmYN9`?wp}LH>ao z0H*@y1GfVTJxsN%&Eb9txCQV#!0Es&kmO$s{4wG^o^kDwmbD!89|1}JM}aMH-v%VP z&j!8&3igQ;Ngg`6?iw?4Zz!gy8-V3emupp{t0{v zcsl%D2&DA;fIkDh12_oxF^pRSKLY*p$uiwBAj$tKkj6g=Aca2(Na3b3?hl*<_Xa@n z|LG*@|0lqE;QkJ9N8l>pEx;u3A4sPexESsYfEU62akI3q=YS8xeFu=zy8uZ3&H|2t zejMW=z;i+09{4)wV@+~A{X^hQa6bs7a=sBra$O9Z23!QB@$`PcUf};WO1W+Y&VoA% zBssBJd}m&t&=$Oy2@XdZ+{50Q_X4#5Wm#&iEtXv*3RPcm?nb zAjRJfr1&Gin?T=@>62C+5^fzPt%b_d><1c|f z2LDkYrQZvr^y5HEza2>F@5}U^nf|9kWcoJ)p9BALAceaCNZ}3xJ_Gt5Oy7a&Up*Lh z4EilV^8aNZ`L72)4f@)HB;Ew1apu*)p9A}W=SgM7`M@_zLjfdt*EXfByth{O9+zYz6;sCg548<6yNs1+kkaI(!*Ft;@d#d$II+~irwD@QaccB za^ORV=Sx6JHv^=6oB#ko39@@H}80ko5WXo>-TJ|6c(qp67w&{|+F@bv=;U zgADL$;Hf~W-}8W!@56zV?@2()_e3D&djgR1y$6uu+Y(6merFGv?>B)I|F?jYk38eG zK$4>mNcM9S5T;c>p6NRSDZV#%m*JlQUI>4~z+w2i3rKcz4dZ!0%4a|GJDEP8>8(J@ z$G$+y$9N#w&#pkSpDlr8Kbrt4-H*Q@^RX6q2k1{TJ_@9GZUs_2HvmZ=R|82OIUwod z93aJWCXn>85J=%7KniyRkis01C{YW4rvRfhWjNa5}RQaaZFN#2z}lD8j7^7a5p-Z+rt zJrzjdjs=puhXZrKdLWh49zZImErC=%W4lOw{uDR@_m6;--q(SYuUi?f15$p{K+4B) zK$t}RRHiorDcyhXEW`f~kmP+0NbyN&Z)XB>&TlPXI~&TY)5hKM*RfU(EEAfh5N? zcDJzmFFVTk-T+d5o&i#RegvfatOink9tKi=9sp82Hv%a?mjJ&+=>bXpP9VuYA4u{y z14(`hNc#U|2btcxK$8Du#^-?~|3g5M|6(ArUY}ul3P|am!0uz%{n_?1zK?*^p8NuM z4e)Uw$?+iL%|Mdp3U;q#_c9>0J0}Av|1*J<|D%CaUt54wUk?FNecc~Oa_j`8{MP{~ zfA4H3@fSdf=W!s#^KBsM@ev^DF%Kj?-VUUAt^iUz13(HF2U56GffQ~5kiyLdQn=|r z3Ks@axXpm1R|`mX@y0l**Jl_XW4s4Q`n?@U;d4OJ^J3#Lp0xA6zkn%qh z2odWWnSKzE;@^(lo3Z=bTgmty29lgN0ZGnlfh6Y@K$7!3Aj!D`NbxKMlAMcxBxeLj z_Od;YlV&tRJI5(&LMaKLnC|Hvvh$X21cLYd!d>BabJ^&RP4ZI9^ zDv;(&^MF);Cjd$A-GCJTN1IA{-)39`BzYeNk{ss&H-WnYNdAuoj>3H)zCdb!{};@w zfGAT@E?8R?@nB9W3 zpaaQYj@<_`jv^fqKjQ+%mIjgEO&Wk}>#S|)9VZ0Y#9|yhOu9!H^NcyhIAeq{#Aq>& zPL$za2SU_M)&MD;5vJ!EbBrs1i9HIV!d1Id4$={ZJP_aJ>n7(|#~5df zFoqZ{#?ga0JY$|Q$G8G0^a&LDWco==r+3eY9$|Wj(PA7uh~sC>Gv*lMj1k5Vqs2IS zActqnGv*lMj3)s}&UT=Xhv^|ki}8~Kq`xsB`5Oh2en%K-y+xD<^Ycuz7%j%py*WO{ zJY$YA&KO|~F7#pae2lc7A>w0&Vp1yNW6U$=7~_l)#t`GK zKvBLxQNBzc#XMZZ$Czi#F|GhoxHOQ$#hD&q3^7`aqnKxl_!#qyImS3+gfYZuF^*!= zC&Dx48FP$r#t37G(PA9Mq)&ur%roW~W$_psUi|IMWIAeq{ z#Aq>&j^p?k^NckArF7zq5ylXs#W=bxhiA+)<{0CQ5ylXs#W=bRhiA+)<{0CQCjlv+ z?Lf+Bgy|thi*a;oj*l_Vm}87HMi@hk7USqv9G)@Hm}87HMi@hky8?y%0)_oDeRNBX zk1@}fV_X3g_6rpD%k-0&-p=$0(?g6F#PnU6K91=Y(?8i*#zXJ3Q#_+U zp%2D9<1Ii+ZwN^Ka!g;r^fc4sOh1X~?M#m_y@lxwOb;=ASEi3+y2bQQHe&gJLOvkL z_d3(*U3;RBFnu-Chnb#d`YlW!VtS70E0~^UdYtJeF}RGd<7r9Alg@!Wd$-7)KGguqVbmV~#P-7-0-CT8y7qEH6;V z3j}ME5ym`Yjxo*{VGJ<>M7V!pT(%8#k-wHGmPO+wa<^Y6-RB+(H_Eb|7Fh6WIbWO) zvM7J_+vY3yi~o0BDczgjBi-sd1e-NT|LS`KcjhHueTQIEcB}6WTsl$ur}mD1;k%@N z^<9DAb9(AK0;>*_{;6H3UoZ33cLYAWQ-)XH4cPHq>0f;x;5O!~?*<%xuk^3J6L3@y z$B+JxeqSMdK;i0r|G%?*_gy9V7qDBs^MCC*>>uM1`t8O1(ACnthUHc7`qTMjir>O` zihiduU%l)9E0*^@4!=3)U%l6#WxjgH{{YU9dbfXky^LSI&;QQda8p@cBa*WQ^U^(& z-G^{_jA8suzqKsCdZ)je-RfQb%WvoS_muwc<@D0yrTZApk9ueSDVCQGAJXq}&OgoH z=oe?cdRKo(&QF~Ew=!S7dp<8OE0s?-yI*8^ z&Sm!l?7oWK&v1NquzM!+zs~M|DE+hhLUyZr^N+B+>K^eQzNT`y>DPgQWjicS|>|kI`>s6Q_rIO}{G|q+5MY;N`uf zTfP7P3FHv*p`Ot%#Nl%s9_kYO5sv>q%%}A~`n|;NxKx+**G7(S8_Caddg^;VmoZ;` z|6~mFVoIOpOY{pdUwxnCXRHsJZ_@8^=Bw|A+`{}(=#hR)m3}~{-`1=j^}UXT9KK6o{T#+e^dnJ7o-y=U^!w&`=~myHSqz%sV|+@#k6FJoE~MXu?0;k{>HaD6X&gkq zJnW0Yk8=4(QI6!M^-B8f!ulH8N4l4={~`9@$oXp^LGgP%FT-OGLHxeJ?l^3iewT3k z!_sLz#cp<5AG3Q*@e$9qbvZh#Dt=qdV)rT1eb;t$k1=OOj*#x3Z!g_L9nw7ryC8mg zj&#$0ExF_D|04DuIbZT8vVZkn(f7tl|LXmS|KafJJ&PUMCBJ=|48P&Y?7vsK>3#sE zr`{*K40;#tgyhdbxsY4EPqmTC&qB$c#PX^4<=#fQ3jdc%{_kL?J64JGMnW^ze0TVUTXY2=~nMY-HLK2zWN@)!DuH${4DQ$)|Yy3 z@hax4_brayoZ~xQrgtmG5fom1Ut-y|93R`~Zm73Hp0M}^oQok_^z5n{zouglyNQFP@bAj%%en>iw^Ku9I%{Uemvruiifyyz_AK z{TQ(tSYL-SKi?(OyPf$KyPFS{@u}|{(0(@QZc<{+oX?z6dkM$VP| z54is1Ie)Z{K=FlONA%l1CEdBBrTdk%bgTEt$1jv_i}|~pF5T+=qH8aZZuQ>sE;;GW z1L=21j@_pUr*+kZ(yiWCPhG_Bsgl1l%9-R--zRtm`6jn|Z>evAbgTC%&p(a%T;J=U z2l79{@jZ67bgTEyzs34e@9VrYKz!H}^h&=UV_rbv^YBN%W4L_Pdnfy|{0+xR{(sr6 z-n;w_=VyfFnfN6cpL%cn$(7O_Pss2$-67pW?EZA7Y){quK#TVwKJ16f|Id3%_ZYjs zy{mMuVfV&6OLrQ2qTlk6j8A=UJ6^{o*G!lzjEQk{33R?sQzn|NLwjpZZ?L;i%UXzj~kRX|@OT zp6UK4O1{PVrFSmLzj`lz_QuTT_TtK=(%sJXyQ)vRhq`6@dx`Q#dKu}adwUe0df$E_ z>sP(!cCOHW4wvAh|LIw&&ta4^{a%?Y-4^1Z->Zn1`pXe+UxzQ2ZuP!z{Uy?^-s}I* zAf-QIsrPO7V7Gejb1Nm zOK$bOkd2`)a<^Y7^qG>c-bamdd!^oE9pvyK)Fb+>LHSU8>U$P+kDJ_i z<_~iDr`f&X1j)Bp{`{BO|8yDO_CwOG-v7V#BeJ*X4ZuQhFiqOZ1HH>`G?;kB}53qCk(Rn1H zZ??~U{y+A<2R^Q<%KuF!P12GkftHfeB%M$wMGDMJ{xqRQCrzd_5K2f27DXnLWYUaG z{&X@;n%?tAy0d+s^so^$TG|HS)oLEpvV{V~y=9pe3zcs~cv>3jLS!y>?; zyyf#8{%ayXRnPN3ckuTGNa;5Z&#An8p4}V=&&%g4KP5@)0sSZRQxJNL zy}y*he_yFK41GgfJ5(Fgg^g7QC_}(>s?WPMD&N^ zO`?9$AHFKyPr(nN-$wa8^gjLm^=4jPzEAAzuZjE@`1|!pNk1J6^8Cv}A6kTdIp>Q) z|ADUbdsvj0@0YmeOXB;h_mZM1LPG<$3x3v1LME91-saguamP z2YO(I!2fy{=XFMZxQq=hn=MN@_mZGM?a>#e16&Ym_34hMZ4(NehGiy zDD>Sz(1FU!=i%?Yp6BKB7f;^6^YVR?=ec-3*uml5jebtwJNkM4Nw3I@{@ZsG&&&7y zog(yk%NKe1Nx`rE0>58_4h){Kw|5Hpw2J(t%lLcw{*bpoXObW5dJeA%{gBC%ljmuF z3-RN~MxOtQpifZNhmb$2uQ$lwcSFxnUcTSxjx{_l--odJLY^O~;pNvi@Vo_Z=r<(v zWf0})*Hg#e%lDx@FUrg3-ygo1znAZm`o8doRWlfK*-AklUKYK{@i)c6I6aG zhu3!n@}g_82Se~5mHB@KJZ1joE$aOIZ>aOzGt~KK(9f0M*OaUCn>6`VX7&4j)x7W2 zyuV$OU#!W~T&A-ALCyQUc&U8{%qy<7izwLM?>E=n(rUf)c?|A zH9WDtk24GZzg+{rLsS2NCjXG8JkEqr;ahaOI=@td?=}s-FdwS#3pMykt~Fl2r`6r( zan<_WeVx96fVZvBRomR@_V>D)1MaqK*7)6Bo<5hSt2ZzZfAKbVue;5g$QK(u0dK(5 z)z;ICYWf2HHcDe*C%;o5Uf&(?_`BUcSFC;j^S37zIZHNNTccB2;dpxxKV(sfl{(d_C_W1RfW#k>iX{hl_eD|S`D zuiEYN^|Ud(n#)|Mx~j{))z#g=Ygr``bOD8X2lS1#C72XUnYheTr;tkI#oT zd3(B@-R@SOr(K1gt=#46>-Bj9uGY;R;DkZLi8B!`ud|0h+A6)>jK40of8*vZPj{fs zCSJyYd8ll2z-IS(x;F+oT^_%`$FF!{x3;?50p0H&aCLjOxOg!|0h_&NbHLTp;qtq? zH+mE=NJ7)nT)9Qf0exg@}d-ZgCH-H;H! zx6S4Ac6kGR^*(1A`cMQSytEzc^Lbm_dV5{k&l*~7&RCy96S>-Yx_bSdzCKU8tJm!{ z)FrxdUIFdX7l^=5=Els{B50O)C3-zJ?JY^562VS;1W#NZpQno~heE-`R>MwbgV*L{ z?1x=}RgA_P`|+vGplKSRimz=}r!z|13~Dp5mJMDjC>I^Nkm{(EOAq+uwTs>A;{J!Q z0NhYS2mmKi^3_m(^LgC6lEjlcl%eBSt z?qwdIp|=+=n2QbiS#&sQaE+>6E>Cy63zjf~ezMt^*;-PSf8z!uFQGFDyRxm-v(eif zLG@JWQK1i3Ff(~tM1jlEwI$Rsy1LTdYFnkt)$YMavuD7?2S{zch)M(v;GK34QA$~G z$hmcQclQKnMBNS-DtaY}D*bW_c0>?S)?0}y{@CymlJP`985w}suo5(s?dc>~3}6XS zD{eSOq|yN-G*sf_5{);^gD91Kp0=Lu_6VU%x)$|tB6cORWGCL1L~7wpNlL}AI8zxM ziU|6otVVR93aY2ULm?$a@v;cSQ>8{!z$sHCD&+K0#cp%e_H@InZf*;#N4Tob&Ej-* z_Oz2J(VC98jhuldM+F(bgHoG*MnnZko2Kq6ud=a)^}xb(v4VMbz7M4mJ7h@Xk z<+8>1OA#XR09FtxP7sK32!+DOvy!FcoSy!`mn$+E0-RpkGJG`%S(#k>aOylN-Br^`Ax|fTn4x)62&3x^t)tej4m zA!dU(QbH&-UmC=)dX*E4vEUfZRp~ssWd_IMg;ktjs6J&*k&=cb(hT)$a3lQ?bjtw|KEcstM!$E`th*=sN9VDlImu(OnDvvB1iM zAa8rSr~TZnp7zZ?!j4C)RZGg*Q;Jk;p30c%aYad39xrEVFE-N3kD7(Dj{gf;y~qEB zLLbNfQB8Y$98HmFqbHSUn&NT9c7@oe!~z_98$@ilO4_4|u?xpS0itX#d`bc`w(rYNWiT}LgX z+?x@tP{iDjI%XqCLmg1LMwE9_y4PIB^{#g*zr&bU`<-@4SN%ne{rEQGQw(O?d$0gZ9wNFr-QDdz%oeE@!qs0y z-y-s^sX6tq;tH`oP|-+46MUTip7<1H~hXHce@-K zBv04n>*?ub5A?PnviSydHda!sxIo9y8Ri<%uer{(u6YHGe<=t`n<^NXa<<6`(b)BN z8Wyj0t+%s5XIe@N@7VbPj(A#2uJRkL7UWKMDNV%CNnLJiw=xFV<@=3cW6w4u=>uKq3^BUKB#C9+IAJQ1!4iG{}n%oK}YsDH>E0UEoVq1Lu zh?P|Lqr{g7JDY1=_%E8Up=Ibp>1kP5#z>TOWKm!4V6qJc=m>x{HdiOohUzZ&H6FGn zQPV@fHN|EagL(;H8%Z}?g-)G@%LO$L;Y6WEvDX?NLYsSlCQ!=*1HB%UT`X8c>zeJO0uC({}Eb1!(oOOdG}jrJCLFCNc~0qN!a?OyG^+lY=Bi#43uZM@AGaSqFu<1$Ltn5y}(2H3vu_lR{4Hcz81I8~1?`oCsCVj+AtzE~HQaDm+tjU&GO~=Q7FfbNX zd}&U&-^vb84Rd%@Oh_H!aHm%~JnX4#3ydtm@k2O@iIXvpZqCx#gfiIK&E2%H3maSz zmc%hW@)=4xu3Y@fVpxno(^c6(2;Gl+{cwGo*un(T-zquK)kIB3ttPK9BxZ4FLF=rU-C^7Qs>1Z%*bcq6>$(T9rYSgW7^Y!^@ zA%(M?`Z9*gavl-xHXjyq)kBmhgrwmho!2Ey389smB~i;1Yo-@F$m=mYV#nQxNF&D< zq7`!OBC+Mjs-?}q?4|4^km;j18w^s7()1bBtM0?GwLSbERWFQIWx38EQC&}Bp%v7_ z%&gon*$vdhC{+%hK@HGVav(&6Z5r3R8c`A2$V`M>7x(Sp{E}p91Waexm}EzQjA=&U z@}Rex`M)ZT#_fq4>at3qt3!H(D9xhhI@Ri`Z6mFnsFteKYV8%!+N##mG!Kj4g|@o! z^CZ-~Nh*Fu$m$*^L5kL3zaw>dHKLwP3Y3gk;dHiQAriExs-Hvx?N-WQx_~A{EVePe z0yC|B9$jaEtxZ&ME)i{Qq=<-AAqC8B5?9uh$tvpvP)$Nt$HbQ=u#~0{y)e|asK!N& zFFFO*Rl3{Tu_&ldA$JiXWh^t8h1^Ut+bn3!tsU(X3WIlbdVH8-r+~JTbP+ttNGvf; z%V&Kwy&`#8tKT;0Ai~uc2{8@AmCP?W1ED)Ge6o^K^`BSEvwk@W~*rT(h03}R<4W9-2eeK z4%9VRFj)*hwGv?Dhme{C&OEvl_Cc6GAlDR+zY|+wf?-kJooH1lht(?AD!o-j9S6@u zo!px%@7tn09M16AJ7Q3^hn?h0XGCmpyZiiho41dLqeB}_y*TNYDa8tmST=k5{I)8& z3|C!r6mUJ(dEj_SR>IjYrT}Tjq0DTK;&B|ehdMJ;c*;?`sq8TOaT#FGu60%Bc$f}sEiH3w~bYVbvVj_ue zoQ6@0f{|g2-F_Bf*RhMxllV_Xa2l8~3mlqCjADw@R-@K_#0Jc$m_GAC5ts<`QK6wp z{2_waxWg-saEy6?U)WR z2@8rI9m530W?@td1ecHwaZh~Yslv-3EP~pTA`uNRbVD)9C9D*xQ;_JmqoQ2cu0{o$ zz1bc&jW8yverbTaLfORQGU%fr7in3|;0~iNH7+_4QJt}3Z0Urbz|1)t-@$~aYtRfI zjhrpM{(@qdqz=`vl9ECe<02o6X&67vRy(N@fL24FF?U_&1tXHuH&+o#KR!b}UoCM4 zgHdc8kOMkl!F3{%qpqx690MZ{l#{3j2lR&HNs78OHR*ZINz}&lTnJY5>M);>>b)%3 z_tVt{kyfZBa(z<MqXMJe5|S)@@isw}$gkFT~A32Hn=?H?rjU7^(f+VvI&mK2UX0%*)jhQ(4t()jerLlPGs=#0u!fEmq- ztG(QKMMBqrtH_}%gGQqeS>htWvtWwCZl}HjSm!#AdTX(|7M=s?QQZH+PysGLBmx^fR>n)&$d09MsahP2 z{9%=EqM=!Wkd(fY29j=jV@qmuLBjaFT&Vyel(G>ep@Kt$xE_UuYSO?>0qaO6sZmZa z9_R7p?DEsOyzu@>q9(zj5mD^YEBI=(@^<(5vv zK;T57{M7eO*5h!7ri)?N5M0p|Q{E^QwOH~)AUlRyG#~7uGb`G>{SPOB zwc~dnNarhHd4?L*_;u-uh=Uunu##^Ng?!lhIQ5Fy^i@C$@Yt2itP!|IBjP*=ZC&Yz zfY6;waaWiE^^Q|DP^=-FDd@*cwo3MsZw7^-U~og1~y$Tc( zJBqB2pEaIV9OMJnwaOv3z0jN#0tR84qdIiViP93Mje7>*#p0smWi6`0(X;^LSN&O$ zD9fl5vQkb&juF15QH_mRnTE0|c6KAbD;}n(5r?_q_{N9>Ov}m?7?cz`c z*Q?p?0L?0*#K)!>4-(FFg0#Xgu5h>aYZkQ`44H7c8=d(R2cSqCjl5%L6yP);$wxRW zW*nAWolR_pb$}3rnVz=^zA`@;{RG|tc9p4Ul?WEGdnOyDL`6bgRV-G7CW1ub&M8fa z#l(b`hU#MiZ6uh&CaY5B3ah0mZlW6vJK!}%ZJ4|uO25k34NH#3tR}2uh*P2rIw73@ zsBM#{+VyLU-nKaC$v{?W8xaF!>~4ToFo+czh+1zQO@-g=!(#LLekdQ|yt4p@nx17{ zYX3d%599Zyt3#fYLf&Co!>5upu6NPFWGY6d0X{e1{d&+-5AEo;s+_e7Okx@z{pmPY zPVy0}YU4eGy0(*zvl2A;>;?p!(5B%l4HW3=SK0`H?XK)lFoUkE>y;@(1ULJ7u)drEduV1P*=yR zxrty9K0Zp_6AaxaQl0JhU#N`Kq4AemHvkDinJ{ndQ zkjhjcPeE34YuUkt(by6XP$i;q0VQcn(pOTo01hhti`{@!>QXi=p1O?uB(^Y2)dC9O z88#t4uu5dp2v(`dW(L`GKm`o7a1&6t^08&xK%+6BO0vcU)Y!4rNC2BfF~Bqqph`qp zFO>IMrP8%k+@AqNNd@WQDMy3CzP)N2Ru{tb*xIum^Lc7-ny>c9A;F%ly*M6%A4soo z$79oxB1N1Ljq9mf(uPOUA6VOs8An`?gUEc(=I($~5hh8kMpcF6MaQZzp4ko?&VEOz zlWj-D5o^t^vYOsr_J_?{kKWbmEjHz7`L@l;A|y)bW~<8eJRc6mh|AMcIS*3 zIW(tL&#le6aq561i zGLlESG*Q&2=^aM$pj6X}vkd6;4=WBo!B9K}*h*i7oL=5Ieb~v*_@pppu4r5G_489l z!rn#HJyeR?y1e1?^>I1&&bswo-Zo!*prhASP7~fvp0SD?)+tJ#xG(SAwJMVdq4D zjmJl{D=+aDD`K>(qoD-ELeF);Lg5F(EhbySL^__zs+E!W&55sR@fsR*()NSl}A3Zm+3r< zd{k!Fd1kXGgS{>GN6O|l@x8|`aldBl4`bbLwJm~%Wh)||*>s+jRYsO8 zk9=g)d1ePWT&|5Dk^Lhl~vX1P9!wde}X0=w9m*W}w zY9%Y)GRNgQxB1FT>YQr`p!glhm!x0MmZZH;NK#OwOGTQW&7VJ+!_$o-?H127M1EA{ zKYb=Iw_K!mF6H@Rk^cM)o*x!zrAS{ooj)HDX{|{Ayo5jhhDcY7^tIFY^Xo)9Po(=w z`SX<`{Z9+ew~6#mi+O&RNKY5(LnZwA)gpbZnCEX6sY9f16!GU@7wIJ;oiEZqEaLCm zMEdfnJpXBtKE06VFBIuRg*<AM9yf0clLgGm2Hqz}vWiZp8$FMmjUcbdq5M&yq{ z)`?DE66u*D{c#?D-XPNdn9K8*iFAobCv*ApYXsa)BLAxs`16e-Ef(o}bNKV0i*%<* zpAp|L7x|T0yxf>b`_O-KJ}^mN6X{-&?icAHk-jO?*~@tOQjyw3dZ9>v|1n8wtjmzj zJd3B)7S;@Ees|^A3?-F7Uip-qb~MQXd8=f5G+Qw6*?Mc#1*e}At?9iQa+$3(h%1JD0Nq~~49^LL2!yif7`Ln1By zG|vY``ie-`UB#b&Tcm#z>7}3H&ucT9JZ%;E10rn~&yR`xr&@Tq zNs%sc^ZczMJw=p%U*tPl`THlUcseZ7cf|8mBH!93%89h0o#%fh(vv+r-zL(xSMdDz zM1Ece&)+ScZx#8gMgFfMzj330TglVcME;6Sp8ttROT9e*&m#SfDF1=TFZ(Qif2&Am zU&Hg;M0!-DSNr($r$lP);`v=7eO;u^ZvOm5k*@FI`A0;0UN6t@5$VrFTE2-t9~J47 zBK<(56@LDHULQ|i7wLBcJm0&Srx*3}G3>E6Uw)Eq~tpIi4=Oj;Du3`fLTyU-wTuUGsUK{zasBi1$AZ^5@+m|E|cF zi@bG^zwZ%grg%Or@`3IA{ln!v{ZQnGMgB^WesPGuUncVNcJTadBDG%6^M5Jh=>d_i zxk2Q`^TQ&4#V~({-=0;+Ks&27Li^h-ajt#U0>zz3vS}+m`KmQndfg8>6zlYQ@+NZ z-zn0CU+4L4B7N7!^UsQWg_pZpD}VohNEd&D z=f5h_w?(??HvaqrD^K4P`3>LX`9FyClmEi=zZK~bQT~!|@#kL_`JagVg)+SJ`1>

7(cJ{NF|X%75kg2Sl2;m*?Fg9T4TeEAoF6X~#ZZ?jeyb7v=Kr=FhJZ z>D?mz{W<*k{D0%=Mv>kvp8r_nPrQe}Zx`u=NauWqKW`Q3{O|Jo9U?vBdpy5Sq|5j7 z{JkPAy_e@75$Wak@%$l?dITPezR#aqME-^4Jik#qzgMIu-p`-+i1a>@=Kp{{zfYv= ze#rBG5NZF9c>eDqy-mQs{sI0x|3RK!DV~2_Rrq{R>M{B0sF z{&$}Di}X>EE)ej~{ty2CQzHH2**yOrpWvxWq<{N3&s!Wkym+Y<@%Izq6jNmYH^4Fo#{|(qq&5}H58jxu5Ssgd~r6e>8DvNen-1v z!a@neo-B|yTszR|8whyY`qZ0_6Iq6%Kq@M{<2q#%b;oo(n3mUJsZ_vI-;HC;5f5|r zYq=M-jcJ}KQc6s;I?Jr}eYIg8*7o@5U{`HFfDdJS717H>`1&YSQzb+-74SZ9x2wrZnGK{Q;|*>+8=iO90D zYjlF2S0GXg4maa;UTniuH{D*Lt|5G38HVF3EmU#IW~?y;5jFB%&7L;^GrXe>9lW6C zYegv>DrH~nAewSkn(bh& z=o$WhnI1vYf1NVN~}HI=!x2tY#YH3M+1wnZqu2D=s`a~kSnKAv4tJ>VX?l#Pi%>d{w_f{9tBVEMB znoDfc$CX0L(?eAPqR>5291Z!LG@Y}t1e(nOn_Vd+s&gb|2Oy%5Vo83;-t)^k(sz5%2y#Pdrz0D+vpugVxTJ$IJ7SX=thU@(cT4q&$EY4 zm-2bsO81a(IbqCry!`>j{BD`l@aO?N=xZC9ueAuNrzMh_5 z_CV*9CN3B-tI7-T2!+_58BX!Rf80G#T2+3rOf9=urcTuYoH(aB(t+&@B)k%km%4Vn`#bQS(}-MZ-ikGPMQE*g}D zpGz$6deoeEOo~oN(CIOHQMg0kI52(TH=rt1A^u~Z3TM*pT3?O_bg~tWzEs^$PY8<+ zuk|dMm5Je9+R-Vsx;mZDwg%QR2qe%%C&AI3-gzc{Wi)SlTD=h3{>x1n~7&jd^3 zdG+xe?}T*R;DA*v9014O|=61K1^jpqCrSvqzOh|<#G zXihs>I=!0%lMi0QEceTnDvY#KIp9dj;P9)zKn)RUaB&osa)gnM>JEvt)}!FqOxB~E zT~KM|)EYx`Tdpl$MB&xZa<<`_c|iQw(=@ROrYv*@pg2u=W@^G%sX=P(ZYl~{Plb4Vv?TTucQ>ZE7v&xgNE9wjm_byTb+=d8_U!mTV zZcFlj5w3_f+mb5IGzl-?gr(i$tUp~I+V!j)(j(dfzdx-PK+S+WM-|-o{j(^+d^*EH z*}LNhxFRA%u054Meppoo2DX}w|H%~#i>T2R@#K_ITrqqIVK^M>_jJJQwz&uBIE?bZ zK(7ZS#fbtuix>+0h)XeQR*YzL(P%RAQxe5P9IX(>qY8NNaeH%s#&>)Yy4Bt8a{K-6 z0h&@5Q_^ATQ2%wc`Ea2q<~n^o7Z!@(oLo%6ORn06%VV#N5=H1xDKGXtZu7LWCs@41 z(k85u@VE1eH$ummvo{6{rQ|Yo*gop(>7`S*8h!3SM~@#82(0kAym;ke&%hOK-)4?2 zo@?`bRTKbtJG>sh_8b34SOwzo_a`9uFcgWQMd8EP`0#1XP&*a+ zmiZmYek?1uxV~2qGIn_y3(6cx3{(12QH?l{$3VLEKQfSP{f}^H5#5v3cfV?rRPifD>MAZE@fp7SzQT{-`4%)2d6?-nKL++}GYRh{OFke7g3wu~% ztZPw5#o7Ev)8S6kX^QXMUJ}3+>KRNY{JH>3Rjn~+e=h}gsNEY6)X8v z%}Re&eZ4qVOk4$99~->x6+(tPFVb;-WQ-ub%%H@zS8Op&99|4bP0woPC@ZFA3Z$o! zXiKJ(uSHjEVz(vgazS+0_(S^PYXhPzh7@gzTAP8QGg_P|gGbnwb!ggCzQyftm)Gdd zV9!>pQBl>RbGL=SG89+V5Aw#CxiY>RH7!;65%ceY7pB}C$)}A{2M?7S+PeqWP+{ZK zRRc|&%KHzsixkzaO$v-qR?^prEy&lTza@qXx>Y;bl5b3zn$u~5Izt;9D_SvR=vELr zP3Tsz5pnwAR<_L$!X$zJx5LxeU5Vd_>1@mjtzk;v4o{oCXLG>S)8X<{kWaPLC%i#J zQ%Jc&M^UKK+l|F)UYZ#eqh`elLdB+1TVz3fh&1a-E1>^p-{XTZxnjT?+tJ2y5VjpD z;+_sH6^OjLgb!HY8T~K0ZOFhYONexd-j|c;Jt#`i$G#Xg&g1j=DL_j5jVj81ml~D| zNa@D1w?SbCdFUl6O7=!>yVr$d&_z``5@Y=6tTh6ppqVojKwVu50&xc>eaqiVlyd&DZBI>kG6yv81id2m4u%m4&p43O_j%C72mMrUo2JAEJE0 z5k-TQy#RJw9Bx1XUj?I+peguHUxk;?5l9zp7OAIOW?OBHQjia9F;m@u9{sS1NGy!# z1=Sc83EUJLMxr_)BodD_Ib)H7FtN(yh#wQ9>!M;c9(SRJ8($T7;|&ZsbYWC-CwbgQ zB+m_%PE-_RvE#d85;y`xwf~XiQ*2x|>?KC>85LDoI1@%ywe&PLs!2$3Onga%X9`~< zS&xdCEa(X%7NHsSd2Y}nqT(fYhlKG`?UuChsTny;snFO4c%i2{ozT0?K5HDv1gCpq zxmAgRis4u#2r9ZWgUF#@TtySPbuq>^>x9OFNnAC`@M4v)5xs(!Bt+`LsG$yoxdDG^ zSGUHv#;zp^l_1xI{c~zF9=#@`&J1~E$X6x+GPb7W;;w)AQmWHw5E;H351X&VF0U}} zw7VP9F|}q~AqmX!yD3g@n;T)E?Xhgv4A*zqW+yK-5I!=bB~?a5*}tXIPmS8GE9qcJ zw2GD5?08ljbuZBjHa6;JqqNkiBVyrwoNOtKMnCc$T29CrpAJf+lo{iqEBH8;BK^oz z#M*(E4$4QJ9Y`NRV}#z*SV`~ZzD^hUNebPJ0ehKh{F??PgpC(nU9gGDVRbsd+Ghl8 zXmdvTC>=U~I1EyF&gpV%U|YgR9@1Ji7!&mf4Fr$7&e)CvVmaiDsH&tO52{p;v|?A8 ztY4K%y>WS2stPD5godd{KG7Q%&k#ceKjf3DI*0ZUp_s>0mWu=2upq-thgkxCqOP&i zW(^1S6xsk$DHUFZH5o}x)QU9PK~s?t-vBoBMUMi1I+a7&0IZ-|&+Z{ENe~!lIA}=qOv8|L>gAM%6KJsUHZd zGaVlFoYIuJS{*uYBP=*{w9G}Y^$2+&(BIEEyxvvUd#Pr?I&+m9R~?E-AV^L}C>rfk zSDhTBor6l;+Nk;&uvO}j#Gyrxy}+H+oS10TP_@F~Zhe%Mh{EAWPaV+~Ri?EP<Ceu)(5kYjWvD5C1S`;t%NYsrb!eERT9rJQJw$Kps*zznxk3?Jz8WAX2 zf&71VEI9?Zc*MOC1N%z0Uy76v#)i1>hBipTiP+p7a4IPca{NObgl;g|#e70EipPY| zPC9%YaU0c6En#z6)Bf>}T-RT0l!UeDcZ$_X3PXXBk2_PYlNMcOsH(eIAjz{$BurM0I?tG0st!<;-HNn8V&hWOTD zDGd(wYDAn-URDtjatJs-v~(>ymA+o7D2Xau;bHsAaXD8%xzs)c?6r48)-4}CE-fK-NNiL z{yQGLNxEeQuF>(~4Jl|Svj&T66@|1+=HU~-H>M?`E# z;dn*e)C!^}5ZMmhC!!4MiHIwP?D14+z((p;{~%X<=n97>rVv{Pl5w@A(wt;#Q?sX1 z{|1)-yTU|PE@TK`eH5$kjD4up@S!TmhvMt<0YY=V$r^UO$%U#;0rfWH_pv0`7~YP^ zR>0N3B`s=K<9M{f&_k+*heWZvNR;7Q0A=-cO2-A|*vf!!%+o_S%0)P-NlMsvP~P0b zo+`yY8UKfQc6tq&5z5EW5CfZX94Q~g=aE?u-M>k96U5+Ey8wcPt^Q$eQDS$#$P3QH z)BD)h6OK6;oJtcdV#DIE5Co-*0-x#s?ro~-_SmYdHmjA6I{FByqjXt5VpO88aXsF5 zSw&s=I%D@X={OIFl_O+C=9ca2u)CUYtQ&$vHqFYohY`PfJ2XNR_ z%Vo92AeV|mj1UcAU$H4D^-7x=zsL(wgJ@TlF;B(YfwgJetSF6ELc?t8g*uKI=!~8S zv1b=QqXTlmqj;1`?#qt{j&dK+-~!)J)d{p0pXn%=*lsiVnKl|{QRA_OlD$`O5yQ)Ri$nj%Jmtm!yCNC#>Q_Gm1)EJSMYcWHNg=#54b{CRI!b~5vQTxxz2GYO3G_+FlbGvKEBU8vOX-{jy!%v zSs%NpFGYb@EsjCg3e5^cu8;}eE0Z8)l^sm!hS6sFnW&F-wTOmQiI9348^$YAF8HAq z#Z!@v_Kl|>sT;*S1TIFg8kKBas&>UuS6o_4)dE`13iS}X35kPEA57CIb`)CLq+Wx6)56wnz{tP{)6%!&Fjq zMlK^|K-1BBC8h?g@7v;5oO5IlLSd1;YEQSj6<1(;JL)&M-F<$$&D+PP=0dCBy?D<~ zt*O8XHJd$sep{7X#@*NF-N*ots5_m!1eSI1JH%L5RSgTuH5eM$<}{;7n};G$gho|4 zfwcN1u4`hGu=vcUv6Gie)KZ34;g_*1WOXLYXBZ)l3mPNlHDpY1m^+64;xKo;FGCx{)ByhyU^Q6QzOi`lHB`fwZVK?cbm6HBvrP)5F< zHe4(iW>LECM#i|Bjq%xTQ-(G9ldAD<3brJVGW{1z0a9?n0kQ4M*C1rY#TlT6v|@(QsZ1yBq+i_176zrpS~Kg?}y1sJg1GM z&@jxqa0elL&O*4a!Un3Eraap*wK_wNi#SQe*`?j2pI-QS8;H56tWG!MKv8Y$bZVa= zIn}FN!^AZ82-M{5#{EnB7p+HjOj@Fjh$)eK9eN8BEtY4UCQXthulp7v&GdIzoYQzG zG_B-jGo-O)G5D|YI22+C~@`qdbYTF-GNRzQL*U8MFZ&G#vq1*Ne>R9+?tRw*jUFHCC`Qm?4|Eh4l$xxpR^F1*T#mFYS&X( zIQ6hNX%S(u?;+8#~ z+`?gPX_R>T@#5ADc+fN8D!ZH2iX}~5%)3*&<7p zxz=^5dShaBno!g%K_Chm;8vr>Z8dOH1r(ya`1T5Cn&xs0mU9f~dx&`UHusfUyWGF>+LD(O3aARIxl=G@8=Sm})I1 zHNZr*g*FN%X{Jb$jA=43=`$c8o=plpXv7ewv$g4Bl1r7>O+ySdyVXEr(>4Z7Rm9e( zTz79kr+8s@c@tPolpL0^Q1xMY^P44Ky}ULqAASq*?*TLWQ}=&+UgnP*=^Om2L_QmT z)4vnwA%18*cJ}bUd49cf!}@Su{;OdT|BK*d;Ln{$CFzIsC{qejBL7^j=71Y5sqgkO zs4kZpo($(E@xg72IlQVjC21Z#i-5PWtxat6b_Lw6T6j|euXhAItA)c8*@y)5+kJhC zr!H4r&H9?~1DQ^i@FPh{X2uZ3I|u~^NZ#&^;+Yn|Ulj2dcJco7QpLyklW3{@VFi~v z;0{00;+Ip*-xY`=8vu{+I|BM?VNnrUa?;h+(=Cc4uWs)KAr}t00Mgj)?doagi|aZz zceh2rD-`(U2zZ?WzbW8n!n4_mdgxE^!y9PpX_xzv%hk|u#UAOT85 z{ywago|?**-j}3c6aL;kZgxE*KMUo}rl&r**1Ye7Non`!(Bk*T%!}W9F<*NBr4yw0 zm*KmeQqG*CpOC6Yhg|PXnWgu~%r3ll;JxGhqn~h8XPTswQ1eYwX2<&&C23?s+ax- z3+EpNtfTWJ{Byi7nedO*FRh?=dxx%mFUKVP>F6onI(4s!Kih|A)Rtu?=|RZo{G(>+ zZo=m%+IiG$y8GxU(t`v8VmF^);{E$F45&N?m)ghc;VISiQ_u_gT3x3oPk1aweGFf~ z+>5gGH_`EfYwPxXSj*`&1zHl^4=0IU8T&o}J%|s}pg*HI+CB|hv$}!v2iLCNhq5YO z5N*%`tFe z<(N!`@ndw-;K=YJTGdKV;hT|CN!m^2j)G=Nn(P&PAvxGPS@OV%z(X!`6f}7ZJLt%6Kb+gP2DS<(-SXF-PBq^AhhPa*50Xq%w#P1Gl;jZ%%Y`}U1of3UVI z`asSslp&oazMBSKFQP3kk=+vZf>Na&iLmP$-Z6fmPQpXQCms5cK9KD_>vO0-OnZg> znIt-7!Dhi`3p!BWrhYv(JDD#)G27wB>#J_S=i{|Z=KqiA?z#KH-_g_59_Ne z?}Dzl8t}r;F^-u}I)T~k#pvGxCVb5K^f&i+ASVLn53XGUdns(K&>J$XUPGB>p!Hdx zeU54N?`LPi-%(1_YOh3}T351J(IJF5DK zk-P%d?U1#Q%nIHRUL=Pv3HvdHw#ohgl|2f3LpZ2q4S17pNivEu^q$GJo(z(2S}IkC z_&B7t>BRpj@RQLB{EXIr$G~I4jc7pqfchZ&2H&gr4EdSHdxEL--xgaGIA~<-DC#91 zgmBd8cj7tKMgR8VTjC4(@swx$1P;?}pp}pZB@IbGD}5;XM(fw$*$DnIIx6iqwNvgR zvcE&H318+zfELqCS1R^OSz&2iH#Q zWn(!-9n@yxscb7ov$<^DAY zN6!>vj3MwPyF z*;nHBL59fw%tD+Y(gz{`X$*Rs(M{MA=BJS?K@U?OI7)sA=rAgL4ALPq{$akBkT2>F z^q%wxr>7Hfi`a1vRwwIQ;XX`sRrW3N;aLA7zZZ4o%)&p}_r>4T=g7v9AB=Csm|tmc zOWvDCyGBJDkCJ{Ro&w(~VP{zX0i5YtyhFPQFCJf6%=}X7gK8V8&kN9l*%hW=&@N5~ zwcgOidw3Z|`*rxN)QKdItX~6PWq%+z%04dZd>NN1w2jJ;-HXumI(5b`TT%E$`ha*w z{Gq?Wvk-qYc2OSpP&?(=8N(HAB^^sRD{UX*XOL$ekJHME(ie~9p<+yw4j8pGe$PQH zBLluY{CW8Fw+|J+N3tgS>7zqM_`4WmpR3*@x!Y_04D&HZhd#sloNS9&U;3@!(PFfF z376UF#axb#-ogEXDfoKy?er4yT#R?7hwvV9PIQZG_gci#__#kB?et_f5{`m?Lu&s^ z)}^w4q0*i&3?a7ACCeo1ju(lZo)l*bo27++97q`ptOiNm%iA1P;+^rsxyp^QP!AL@VfUfKVY zb^Js3Jq_RDwp){tI!}%vFk23O5>1E~uMwX>+p}QP=U}{e*4#t4O5m4@UbSJ{n3IVZ z1bmrgvlPCwqCV!Ug6GtKUNh&gaavBPqxzscMnYXwFY!f>H}p=)ACdvt#xdPQ`bosY zkBYH2wTXOg`Zm_z#Ov88M}FUHW-0Uyvys#{6}YL|ufqqE@aNQhhto;+8MW<@eNb6m zg7* zQk#@KWA!WKQmIR1`coO@H_`CrbM;?sI5WW;6Gb$&`po*up;r&2J@o5gm^75QmXq={KKVY%%NnQvp*+~9O z9`{7HSvmIOxQOxJho-sG>5_DMrcYY-EHJ|F!w*fl(sD^!p4lK-9ms!Ut|_xH(`w?c z`TG`0YRUYZ)LtdZEAv#oD6?Eue!V2EFWM^gt`+4m8(Np+lNx0Bt8%VVmA6QeCDS3D zF266zsS3R(_{(z|l=)fhnLAD50mJw7oK?#AnT6rJV%>zaMjR1S8(vFcsXhB_i>>Tj z>$z5m?|PM@|6UkBg)NDgk0Ki|*Zd)Vt82$!sK5hSFVUK7J^kjd-SzFi{POeP`T6~q z{`U{x{QW1+sWaJU;aw>v{+w96a6(2Yo6lW$1FPm5Hm|S&uXwjb9^AFZm@2sslTw8r~uw+NZs`1HXJIpKh4^{1$ zth&d1t|U1eBe{cr%Q<{vaP^CsPki;RohL2*a7&4F`_2z<`N~e|hHs56eethno?e(& zcKJ)=dDGVXx2LjR9m#vEG4I8KytfEeM^3X!&Y<*a&h}O`gL;NES^&Z|X5n9Ea4c)rX^oO6C`s4jU$Du6GNZW#(zj9M zjsj#F=j31o%=`&ynK`p%&tvl+9V|ZJE_rog?yG|(2W#@*nL6d4O7q@vsG)jB+Q6(>rl>qSbEhR?>i+J%D9jw~1qGRr&L#7fyePN>bpQejm zFD-g`u;`!E&tEm=e>*rjzd#yyoMo0;)|zS;Hc7R!bFUvobL~>@4Xe?}+OzOKsqu#S zQsYht-ZCc^2}pAw|GA(_?hS*%`7I5m!#lE~vEU_3K3H>fJnwx+{%h8}w@0(LS?9b3 z1_krp7|DNUB=03_{xYf1G`^=kKSaR8rNu9g6+H~%j8VfL1x?%~M+S@E8Z3F*UGyqQ zXDNPu5Hu`#zO?9BqU3xHC7ULSkB$|+JW+gLuo&bzYf*N8&hWsV>=oAGz{s$_V7PbB zu>Xkpg3jx=jSlp^tew{u4SjT;#rh~1zvU5a| z-sw0Ltr%MQ>~!&?rA0@li(fX)8ocEogrcV8b$8Lx2yui|g^P0mfl#_jULPzzYML!g zHndqm-~4y2dGADYC>?IL5I6tOn70#h5xixCJ8Hcl@lCiJxaUKPL-O7UX+XA=I@QzD z#m~daNG*3=6SZEn_gVBJcS&Qxo_)7wZyOojR+zo5m-tK)0Iu#dUvR|CIGGK5Ro3;k zBmb?&{Ffa0-Jpbn6vrJ&-dDsi+JNZ&{nY1yVMWJx<;6xnS|3(xSsA%k$Ity99zeWh!}k zqWFoixzB-trecXuT$UY#0Odd`0U>)}YxcHfkjddKBg2&i!`rOceao^3I)?_S=-AvxO(n0JaPT(XuF3NO;o#)T zBem7L%ojW~wCeuBs$UFM@2Lg+MTcw8KZ=rDrGj?~^4{vrd&QC8Wz9R*nD3|>H?P`{ z+H0zwH#q?3;AHhp<_qp0tiGwX>PYPcusDa!`?IDS^WGlMx~3)X*paMhOa32P^0y*8 zmG=tp#2-*s1$lldkiWGc&$mD8uhzUjAUm4>(rEs8R%?#b^z6ujhk4u0HcY$*s;9Vrt)BR-jW`eT=_ulBH*)Qs9M^w@}b&AXyXp^s&PnUGz1 z6Y#|IiB+SZ7(KaQs-|lH#HmLotL~@rAKx|facR-6)12@lD({)~HvMI8nG^iqBMFCY=gn{2Ud=S#;Q#-+ z&QT@xW-U7SqG-7Eh5rRjc1V`2MbDh}S0?Jc((Gks#|Z~4mc!gb9h*BfT@15DZkDNc zcuVhaWeXXXJ}GC**5OJxQDlRL1HHq2okRUFI*x1@yRni(CJRjY0e8_efKXHN0^B%w zDR5iH<~}@Da(JQ$UfpvoB~RBZK4cm<2So_Ibq-~d>9a_#s~Z_7}{ zk>O39RQ|fc?CYe;;PvmYPVLU)u1%Me|3{cA=wwTNaOFcoEAKb0ghAP3c0j}5Z-QAN zyL0Ez%G+ue-48=!K3^gSsxeQp<-sVRC!mY;6S#%$xRR_rd*u1G8s1aAepo9g{3aF)o=dIy4B5 z7fp?o1bc(oTOmcAL%k0uS#R!JHrzWp48I6$ZJB!j?i{?4aeOitZqIWQCC7ru79Rqh zc-~TS1Yqe&$uUb2cUPX{-b>Bm;N09Zq&ubCY6>hhrv?vX-F1@GBt2ot8L`aWKlcWD zwR0lt)j9m(cFWxDb91wJ`OKPAGZ*b?fU&C@vN*PvJ1VWuNh2A(Sv3Wj-U44srO7gD zDtEgj=V{BcqrIk)!f!6jG~GA$+TTIo&r9n}$IOn5zgqTR?>4_U%aO5MD)u=(Zdx0h zwYRdM#UXue|JXSPridBY!WNe`kbTa^@$?% z8uYtYON-w@$yG{6u`qk<-fRoGrrAjJG=B6jcs+O4qRg70Z`pH>oQ!t!bQYEVgmj`L)x($nG<^Db z+gr9m|q1+`7vyDJ<;VV_kd1>Tb!p z=744HGmxsKIR`4-1^e9@HS=$&bnpD4??A!Hzs?+-URvH1~$t8RZQHPq*x`ET7!rn7cH` zT;awi+pX*8?VP9@L9t5;7x?xRTs7~8(w{HQyvABnc;D;;`!6YYb`*uXtqld|wjJ2N zbHeK0kkxj;y8dUGFWU3kx9`7dUY4zJ$d_T7yKrGc&hstXty#ayY+Y!#?{O^u`lbfU z*Cr6+ljhySWX60xghzCL^S1rNTlNfB1GMPH*enex6Pk_{FmqTed_83!@qmwir;+muHSvS@2$Uj z);<3Ci^m>%{_dar{GHETv+nAjKKjB>e&j`S>uPx z4zVSqQyDl`dq(PTF^x zIYTnZ<*3-^CO?Xepja5%p|dYSvCxl-{nw`+&CCpj9tN|PFe@}jKWvpD{&Xb|XKG=J#Kj1a<{CMSQ%w6I08 z*5ENG0x{xs+q32qa;R+<_AvoG`Kqj2XG!yx5EtkdB!Cs>6;s%-7pjMNF}?9vc2NN- zP4OcPSI+(Os9AE*o*VXafHG%%&vbUcAvK6c5^M6`Do4$EQm{7k5LMs&x2$iEn@j~E zE)jo!{8CHtD#RW6LqVL@;$uVEnGT9{vtJNM9{SnP<>OMzeVGz8=<`C8-)#WVeQ@5^{=M_o~Z5GMIx+Ek17ytZ)PZomsnVe9T6Ik=xHtdydMS>SmxyKi>R5|KQ-P6QreOszy0~GOG%0ZrBI}SXqw1SH5IN zcUx^1^2s^%;8pibf>RF*?n_Mfp7w|BbDseMJY{|J?y~cyAfqi! zW|aW^=;}KU0PN|ic4hqcY59?0Z~kv5S)APh?8q!8{PW89=W>Z+EufBkbN;Dq5dND0 zlY~kLzjNr;ATPrH&rI0uo33<7^O5JxC(ZEeQyb2=D9TZ%`%C98Q&u(>OD(TM>gdO-e(e6zJm?U(+DfMX z$K0z?(yr#t{_g!li@@|OmDs=c!qMRsQgENTMXyW`5?GZ&zI*?wXD7gPKfneF11?MF zYd>bx~3EaFrCS&j_-TL9S2UJh<`!v5nojyaHwbA z)zA{>s|Du#+eRUT@G%q$?V>xs`c;UQP<_GW%rbs^t37X?)UaLEQ~EFMc54-H#a|z* zLa(2#(q@MnuNywldMZ+vpk*S`M9^uWNf{3oA!W7P#0{L9b{|90}S%D#!KtQYN) z8qL{Hu50qQj9!$Vn|ZMEYZe%Ydok925cQSi1dAS?F6x+Xx&X@oc1-SC&E~uM_H4?1 zRm%SZ#%IC2vfR})>4yof4(lvdY-rJ8GXifHV0P&6)?J8HU5hy1+TmV=ta{D;TWP?z z@bHP4W2Th>n8$nR7$%7F$_gGXegRX)r8HCDVf_LH^DxD9zj+m=n6Ucb@Ui*fQ6t8V zL5e>1wqPbLyZ^me0J|kP?82m6A)5i-Gq{|lwx;sl9nX8iYHr13Jt873rc(4gCg4kp z9!99ro%PHJO!y;9!po}m z*Pegd(8|M&r;CQP3?p2$bsI0X_)gV$yrh;8iab9wrUc04?a0CO<~rzeU!PH(g3O%s3f0pt~HZRybF!tB1a z!>-Zc&js=Ay*HUw?V4P5(GDpO`3a8k{c;SR`pBWZhq~ z)>6AOcj0WmeXV85;I7@fpQ;-zSk}JEW?%i4k+@LHkua8i{-woM zNvn-4%h=O%Y?W5z&b3#bTY1)!oWqyyvOHV!>0K6Ui*5V(jz@AbmMp;24Tr5oCi~<_ z&Y|2%dr?_#?(V}@tJ7ZFa&T?Sc6)2v_pj;mUc2Pq+RpB?wVkl?8;ic(a_a8}o80ug ze#zN=cPu(%C(xJnp?{x)^n^KngxwLS+0eTOydpAJADP_#YSE)SjyypuyLL5DPlzF(+(ez;D` z-7s>17m_n|<3Z;#d~yA1X)-Hwcft66lg*L|=sDS@J5Su{Hm}afKnSAL>^LYD*rgr2 z90!9LGP-BE=RaKV1{y(iky`*9cGg^$ZOO@6aOs_o?3{3;QCF>;nD^P;3m85*N_;q; z^QD732i^2Z%Se9B{TU3_MXcNCLot{CbR19 zrPkCe%e1yOK5xp|UQ@YX_Cm=%w>8_cGc$9|g4Jkcq1iNIvJ|h#%&j#AcNNaxDs6As zf3ws2creFwj%0tnAmFw>Zk=zcknA9Mn^pRL<|7YHjAWavw-$h4JLVOPcWy60OV(Mz zL}v-w+tBsY;ghVHrh~n;)~pumKFWz4;%Q884 zaN&-L<+ZnGwwn&_H=XD+TQ43R!Sk#g3kt@UPpm#Q=yRMBJn~54_vikLbht8ewRK|w zv7ON?7ioz9g0=fir3J)Dh{=d#IXy(?!gVM#=J-6`7L`Q!sZ$W@DjG zt}hB7c{87B$UIxXKa=5A7e5~b>q3I)$Rw_80ar%8Fl+U!V8hU}rkv9TgYWJr2t4mt zb_kRz$lR}FN!>{@OX|Zc>DYAmq})5DoU?Y>etj%w+*fPK?8~a*w4>0*yv#{u-+@e) zUx*HQQqET!G8Y|QG=BiY?J{tDN!BBb#bqa28Qsrf->wbyWd;4_-J8vO+hp(&>R`1s z7-~b*CrA=DH01U?wP~oO5H&$euAf*wRFDZ)56&H5Uh~wZ+TEwK7cGMm%l{vD-vbxb zaqWMXU0_v&MTssE@S-FkiL1MSihpR9e+rQ#s3b9ecNcaQB7as`5c99QfI?cEVro;H zm%d_RTg^*b^XK(v`fLkQ`;yn19Cc0bv0**xpG_N*)3XXR1c+pnRd{?7nZS8{f}G^{McJ#&h&6ckgR|Nm>k+Jb^&N4U=1RLaO5fJEa#~@J zBv0WwL~F{LsC4aQrvWpHHoe|H(6d)-(6zsJ z*~52$71sXPw(#=_QBYq|^Qg8DXWxWJO?e!>*bwuHIYVy}j@(x`lnX1ig#I&Z)xc0Dk}gqCH^QU6Tmi;M>OMKHBU?ux}-UmG=~^bXWxXZaw^L^pkTh}guWO?ljgC( za}WPu+D?WyVAi$XK|!Qpu+Ks@bchMz?B~feQoCngo2J8=~XGT@VIWHZEw;B@lS;t~CdSb1^8pGYdy^~-yBw>8Tt23eonSHYl2uq@2va-uNeb()FUb^ih zOm3F>RF{vfcdC2iuj^rDN&RRPzW7*uEH^MtW`mdMI>}bC_LlVD&mUL!9xnBM*0bz1 zr739l`Mdr*V%%cA&v-Zs3xvw@9vTXq@w%@YlXgSl?K`yjok(lkhd@uvyCUYCx@iV$ zZ2Y08nJQ&mmkqs&(s+jU=f+3FwrI6m`(Uf|uvLZAi<5+s*!aPsm?UFK!sJ5G-%FO@ zl7u5H35K305!NIR^mkxl>=3c_%jz`7`46w)W9KpdsW8Hn^pS4W_fFmlBPmt$d;QkQ z-tH*zj@*nJjHuXanLiZTO2mWt^RQm^oaila!Zx6qTx#}T7Cf$;8r>jIjg~8>Mg=*T z8tKF&3#LYwE2c(@HV^dGq)biNzM2F3iISr=7?epqk^1Itwp>j1&z-}Mjjn*C3KmR# z-t5$HRXyf4#$)%$dUs7`Tzi!8%ieDEM{+Rqj` zu~m3Y{4r^R{pa?iB;%o&kFabPG}fd)n_wy~2LU*t~+nF9=DN|T=fwyJ|O!pesKxiK$N^VokXvY}^0 zIkSFHWu1a*uyowd#T!&Jua?V2>K_ATQjxMnXOmgw^g~0_kGV0i^HBc&{P{@*=2r}Q zO`?#s$_y{iGnkM^TXk*wHLNU2Z`F2u#Cc7UQJ*Lj_&jK&lu7v;D^M+WJM zmF+srftJQ`=09|Zvd&3)sJ_ljDF^-{g!hH;>ktKDE}(L4>C7~|{58l)ReS$TC@zMq zT@1WnE2c!F#*!B->L#dMrF#HoJAd(%{@Vtlelsg>ivcS`4Ky)DquGAB9`=8eP77mi zgw9W&Z+u3lO&1FERfiXuqs;$p(D&g?ELdSYNqVN1nOa{0eyC$t2gW|-dYeb7S(SD;quTOk4HR@k5gnp{ zu~=>E;J1X&XsZV9tDn($JdE@{^vbTj#3_*G9arl&I3`1AlvmP~zA|Pg!t^!bshH~M z{DDE%d%1tg#>IyN2e2pko5+m){(Ar1PEM!r55vL4*BQUf^rstX4j75XZnA|p1!#~T zCBocJ*7fgT&Wc_p)mxh9s&z&Z0?c)|xg}XlKIoe#2i6bdbBs{#bY={@sriqAZ_NG@4c3|QFCEztMGJ|qt<-o8RbpNyb$W+Rx1aRc zAxbuf+4n1@H5c1~2qRSx+(g~dV$@lg=azY%KFybgwQ>!0E9T1FO_IS_jBZQDxL1>D zd||(4w@zoh_A9(ENj5!5FPH-R9o+eBry``v1$>-Y2`8AbBY$=r6FAjlM0wwYbNYf* zU&&X=eZv0Yg8b<@UCc$i>r#CmpF|iGlZe-@!z6;r^p7KTq`A_} ztkw0n;6ektcEgRZ5AvY;12ZiY@y7pZSib(9``~Vpt=Wnxg^(aFzCf{0vtpmMUWddp z4Z@}?pO$UBq!Z}Np?yGQm634=vmf#nBx>i&7i5(%kO5)6dVq^KQ={n)nIEfdMwt1> zQ#525XQSVuO&p>iNc&WQwt=q&^MlB3!^dd0p9A2{_Wm`4VS2RYAx-r9ybGt4|T7O>4<^LNWdq#>*m)sG`Q#2H8*+OjSU+bUGxE>t94JjVJHi+LHv-4 ze;$!TvreBwo^@iM8+##`5n^>^!0IXrTNX*oNBQmce7*NK$~P9DGn$*FmHJoYbthkj$n$nRjULaFQ@2QR>S7XW7iWrD*BznrDp9H3 zeFkx9L-{Eje*G=Ba3L<*zBhkZ)GL+ex6M+W)IPG%IU+45*^;@t?+X9kx4Z_`DQKw2 zPre2%yRApS;}9dysdvyQH~o4)ZAs=M+AQi<*k58ksBcDNGCmT0w(flRl3K9A(w*(P5scgvp@2ccg!-r^PF$Zk7wn+ ze$ZF(yIJcl`GxPM&t|p0e#p1(TPeGmU-sSlRLX(oSA4f0PC54auY7mDpK`AFH$KO= zQ`fb;;oI=@)UJo$^wkWf9=hrO`szMQ9e#M&*U+7&|G|5{P2W$;?EIsz>7BHazkT3q z{`&0dm;UbC+&8=H|9s-x^49FVfBUEJ9-nyV2b0?FdsZC&L0sFHheW;cOmdq~^IUqS z(Vf!Pt~rxlVl-UYwoUWcoK~at>b5S;ujcGBx=n3g(Y!V1fYH35ZM&vp?lGgVwCzF7 z%X7~eyYt$*HA8ca$>KF_4{5e$ezvu#)ijrKgKJEq^FIJy9kp?LrVFzZiM{bRzC$10LChYk?M^Jn0fAU*EmW&2&k57jplV<^dOmM$Q- zMBuLho@|c=cpA%P{#OHUK)3+-1O?s!{ACEQ1|Hd2dTN(#z|;8M4E!=VJ@|Ycc=BWH z2cE_tqEG4H1RkskF9Q!zF+35B!@(OX!smb|y_WeY1D@zFMq|nJDSa34*8$%Me3>Hs z>%cEW_~*cb4L|+K5bg?u{{nomB7HgVwFoapBW_frKM4GN2tNk=y^8cp&{$Mn8XA8U z$0wp*TY+DLa1HRQ75Lu)--Pf_fp6q^;{RGO*Maany&$x6Jn7-DfiFe)$H0>wksK)f zOjPPtg#QNoEsFG;fWH>ua!d*+pPc@`fWHmlZv#*HWO>d2uSE!d1iY+Q)GoK;cOLMI zFo4PZn)ulUya>Dlcv|~0I^YrSFfo1b0*_=2Pjp_vZz{rjfKQRrBf&86qz}EoEA>IU zSW5p!PAHb;E9fjhC%Oq?GAcK6Jki+-ybIwQfp;qK?*m_l@N>Y|D)2Wi<>kh!%k2l= zj&$9?+jx57XD*bR`1mvM4GR1>fZvPoo#>1|;CSL^89K!Vgrg0DuwKT4&SSvSVWy41 z-!9{k{xjff5Pk{xYK|wlRl_0l5W))*r4N9}a{D{*UBLGO-^tUHp0|BTvLh>}1;z#a zJOQt=Vthfm+kt;Tk$w(3GvVI}!aoT-@n1h(&Hr>ObQ$=gz~2BU^HT*prJtLmNDuyB z2L4gt9{~Q}0Oj=S^CdmVxI_?cR^WdQJoSfrfG2&C(;Ew*;|L!G-mbuJ0seA?b7n~T zE~o!4@YLS70Kbu^CwWdmr!pZt06fIvr=JTvR8BYpJXF<>cLQ%h_==f=U{>IF1Ai^T zHv?a)!2c0=sF2VDJXFt5=dvQnp8g(qDqrSvE%3<*#~7vYmhjZCYk>z4(tw{Ur(XoU z1Rf#~@_~m)868UhJ>Y>7Zp0rHA>m;Meg=FT!nMGo>-p3F5_n`2HUqDu|0m#ypGSbl zkDpEqj55{pX&_5Dp7i_%;Ax!rJMhYS-3RLreE2*SS$ z{9>ek5O`Fd@k8aNt&sZp0Ps}`d>io8?pItIXm?8g0q~UWA>b<&=~u6m`r}#PmHqKY zz}F#NIgBFFW9;_&*rvZ4fFH=)t zI}~(&4g3QLZv?(efxj8b_AP`z0{&qI{sZ7QA^bpEz)n(qZL2Ug0RJ)YTNUZw0e&sQ z_e1z=IG*eqUT|di!65usfd@;%98ulwM}hYMe;4pHe#-h>avkOr2)_yZj}`cH!1p1% zJsmbnfq(9L&_K9sjwC;s&RiIm)xiHJ@MLdfya)JdgrlLfWN&2rap2tu*8<<5z?UI& zC&K>?Jjp{&4_7zqCl1JJ2FH_~d>{AS|_np5$=T)oQurLuk|wp97xSM^67;;HjVBRae!#@k8Lz zwS*18qf4^%IuUbn2n$tZ4@AW96vzcW29R(%{1SaKuwUZoL59T<-irhqfY$>@I!F2B z^bNq19H>sR91KYRL*N$x_bvRA{8)P6j{`py;TP~5$MKVZe~;6D6*wjR4}qug<#+f^ z;_3BBAAJL0j94nX7=K?wZHs~7hi$$sQ&cafpQ+50lu_5Pt>A(+pX8kv2PfW0;%U`HPrq*@xAOBWAYb zs>`iy0CV_&dQ*d~+EMSUbrGZUvu(Fll&YLPZ9}D_)>h?dXl!DL<|Y?D2wv6NYU5EYcyW|!uVlH*lB=k2 zVWZ38aXE|2uU~7+#w(<%+LdLQ6-kR+V83BYq{J2rvKKmPYpG2NEyd-ls>)Y@s>NIi z4%WDunrl6QVv5UGw5(ldE3OR7@BAz!#|y7(_PAO>vLzH-L|9bWVM8@q)iqVMti@6o zxQgFmS?ThW);D<^^;NC_QzQfxyS7{FH`gf(jZBhi?3bZ!1KPlimv9^Ep?97wwidJP zR%dNFRbZ80r&Yvq*-N}gZdsyWw3OcLC8Q;w#7?ywR8&;8WU^88p~xuuS3g+R0rJ5yDxxZZWS2E;pl%aU;YMa*)a(}PkmO$AWpjQozrE3@2it&tFULj@Y z*hVUQp{ne~a@j%LhBqw!3aDsg6AjY*Rj3N8F;P-QmV(@RmnGM1!EFJmjuu(pElZSQ z$ZuNV@HlEe=az`b!nhhFd<&@bF56t%q%iP>4YjqdDyD+c#(k9TKYqe$1}$V5 z0%K(;hn3@0`|R_jtej9_gc{?7LN)j(NMM5EbjlNy;&SwDWj*|p4C?eb*EKj@s@@&2 z=NKy^3>OlK#raj6nlWot_kuwFqM%`q=URgi&e0_0nrLSv7FGS)vdC|`Y@S9(ji(8= zO=%AT6Kz#@<+cN+B71QY7_9Qx8Zfjr)O#E?^{z(z=iWj=ObnZu5(kVxDQ0&qEycwE z;AJ7ndeubhwb*83^$3*0ge;M@AhxQZ&Q0eTFb#G)Y8p$+N3NGz1{nXCO$<;kEvjdR z{)P(M4HZmXCRzgvj4hjxZ?3`ApYC)~lvWN7*5-nVmI@n+qlxE)MV6y3gUO2%y+8t6 z)Z~H&I0N-imXVbe(kM3QQQdi}Us;W%xUAArTns(h;PM2Obz$2-D?mL``$w$RDEd}b zV@5U17&UuYTen2YYVt{q&PzDN>vz0X$ zW(H)j*laD_%qn_yF?Hh!n+=G{Z)UO5wYln@rD4nk*PoFEd%+vi0@FyAmzHRO&Z98R zqqOt|XGlF6Wv-DrS6!8R%Y3()erguw_#5T(=1rw~u=ZaZBMKW-tYqYZb~I%nyrWHc zOv|lvw6d{NIns=u>ny0Rr@FC0_N_@Cxv+|CS;A)^>8Pg^gxd%+0~)3=^Ig`*l%KmNdlH$jSzE-4j@tFeCnR}yCD)5`l0w6(+KaBTt^Ta* z8(kx33;2*FGZ2Qb(u_%dDwJ)hZPVORt5#6_>XZxfPSz zi@Mt2jQy`$Y%Ij~mT~}5_Nhpf8S5Hjgr(B>jXK)CP+sm=G&<^=9BkRI>UcEFE9@`~ zPIJ9t_d;62Gp!-_K|qnubM!2SM;0Zq1$sr6Z4Fy9S6FOo(F^1raltF9o>^YJa+FIp z4D?EY(&*P*zq!%jwxIzwC}!NFxjP^*Hs#1+z7dQBlI+Om;IBzG)MljSvejUoKkhc8 zCSyNkt92+LwBxu~M5 zwy6>2`qNT4yUF7$UhH&L)nX;bR-jmeDx^bPHM*GW&yOpVJ}P6y#h9Ng<|8^EM%cQX znrrL=Kv9ZTR+rl7T92i8m17IlEyuIP?ZQHQk*ySR;6bL7?Y~%?OXyQlF4pdkcc{I?#uu%$~qREr9rpe9rE15$T<7r((i?U-!V1m#Xj9Dgn zr>k_WMJkhQ1jFLQLJN*QjLL4V`ocV!9S97RKvOjMr&^f6DgtFGnsk&l2&IXQdIi^Grxc+riCSQB zN`oDDWrS_{@|Lg8^`M9zPYiQ3FQmnEje$MCxDe% zRi{~0$DGD|1Xwx)JT{`LL$Eli5PAxj9|H!t3vv#4Y-~v~Qs_Zsv5+4dqo#BeJRv8{Qzu|&2rmGwAg_5u(Wsjt;?dmf5zk78J*OS9$dUCY@(JbfHHv>A z9-+gJevEgVl^Mgy~{mu<>9f(jdZ5{07bmho=xSCh~lNlfuY1;chM;753Ab_%eFpXNAGcgjYZ0HK0QS zf#oUO5rJBgx8mZS7WS7l{zd)y9T&b3QVTsV9H4rE){Ef|33CiCy4_Y#=bEVrmD(``FWou_E&b)ZpcmqOP=D; z)5MI-Xvc|J=AiS$VKORw($WduFlm(YD|U2PN)zTxF^j++nB_cVESs2sI^jJe@_vr} zRI$HUnkA2TteB+?e{dLHByRk|$IG5jX&HNdCyP}KUVyb+gmnpLEz)t^aZay4hh<5a zeL26#CcGLMiU0B2`>`J{_SYo)i#T8GFa3g7HE8~M@d{GeAL)d#zc3mbFZ>Z>KlyAZ z&lh>d*w4tn)*)jRTc5`%V}F55zQ|+7elEs$+zv-h@S{FwJPwkIg+|ze#sQLa1~V|M zj_J(3@}#k!5GGcYEkz+r1<1>O`4+xKgrqT+IOmiZKdZnpFz=pC@$9`@n zdh9qr$bZ1uMlb6~uK|tbZKq2{kT24?V?Xf=Kk-w@US2cNqVMx|$p)Q`y8smqy?#UK zQc%`){6eMOypXfU;9YiSjQbb_Eq?uOqTmCYO5P{#1B!5BHE{a)LewYx@#727%Cw>! z%lTt}Q4<$f=wadD1IYfu#_l=^B`Tdj9tWui4JsW$_R|~F8DxLzaUDVqLajq@&2R+2cPzG}3*@Ww!Y^|Eo$q(v`?ka$Jh6q^~-l zG^SgTBj%29EwbWXWX=Jd^AB@1a@f0(rIg{Fiqa%Yc{{R%q3etc;Zm4%TP0ag> zBjAn}M@=oILmaYInd+K0G~rSdlXRC}L!+t5(_D$19!DdtHKXI=CcHJ-go|MEEV=Wu zkgcVrsiv}q?hrQB)YszDy(Uw2jnnC>M^-xgZK4}VYfZT58Hwu~Omz*;X1rz~8%&+iEyO)*?If{ zDVd+YBF>+YJ{o|B(GOF;mB(jD$wJC^^7tf*50k%@$Mb?i@*m{!jGZv~kMVe81o~%q zyp`g^*dH@}905-x{d4m8C#7T|{28W55!)retAX!GaQQvT<0J8ZfX7GD&r>`;5`V@x zso)F(VdS%%$BPl-Z|CuLidWcAC+GhlkLSUV`XA%*R+c@~ei`}4AR_6fb(HvO9v?}6 z?&b0GB9y-@* zQ$iKVF&^F^!^-l*@Sl>P${5mqb{@Y@N){4-fXB1?g`t0%$46>ke4d^?84>8`juL-6 zkB=lj_bB;yj*@>bk6#{vzf(Nk8zJ7AC`CllziuAS8ZL}}9Od!$2=sI4qb_*(_z}`R zwLCtOe7ku(uODNB=yh=YIXnt|-E>MFd46c+@saf3!{e=tN~nE!mdA6(Lh5&Jl=$UL zF$Ce+2=veJ_>u_mx%818JdxVZ$>Y5&RVe?bc)T-0dW!hIxFq6cNH-D;v1^ z{348f>EZFL8-^)=KaXef36uX6kGDoBKO2hopByFs(NXfBvxF9^JWOC zUkTzL#;2!p0DXX&fL{h2M!ETb1AzGcmg!Nz9|5)k)&Z6S)&tH1r2Iy}O2EUA_YS}( z0SVs)coX2gfY$?713m!w2^hK-@F*a3)6@;v1?U8<0IUFn%`oKxQu=v-sGLaz{0iVH z=rz$l1W0_B0}_25hkIw>o0i|#1vv0JSjsZS{a4+DqfCmA8fP4oec*nDV|AFv+ z3Er^>ung&T0aEz_U;*G!%m-^JKj12g2iy#}4sacy8SoasvvKS@vZkYeRR|9PLbPRv zC75>rklJGxU<+Ue;95WrAl1taxDt@Q)wT(65bJ;@z+Hgf0^AO`4X~EO3P2CSMS!HQ z%KCnKzs+&^dumq+XGk(*acV$SPtj~6an3Udnd8)KAIi{tUDhN6O~Zf`J`C6n_$=W4fI9*20_*~${#^!m8(w*#&OtNF>03QW301}@%zy|?O z!Cye|7~sDF4ghWkbOL@IFc4)|?^4*^oXTEOoDE(d%BPzU($ zfP-50eQwhpz$FNK0iOW87w~C7C*V@R62Kn;S^;+ho{M601=Cr;9O%m#3FZv~QoRoV zUIq9lAo14?NcnFE>;WtX{4rn(U^d`k4f~$1X)hq9Uk6C(%K@_h>AQdw9v0Xf&ol^V zMtA^_=s>2z9>8kA%K^IpA#zg(Ac`>60zSp#{}cQ=5eCe&1AY%(1$~y_9Tk9!k**Ao z>Qy4aJd{P>%{FBKqKV2x3Ff5$Mk8#LU|tfS4q<}?^Wp#@GEyUgc?44h>ZL5qU^2pX z{0BZ`)&U}2OgZ3BfVTpo>MqPp z=dg!EFNbyxtsIIR3LKt8|0h1l&q90-0Wx|V_HgLs(9WTiLy<#)!yz;z%gd&PVVVAW$)6$0%Pf$MN6A3q`SePJ74&}1!(=b%*~#h4`z$w77!{ZIN9g0V%5d}oDJ<__yvXBq zyu1fZQhX8*`*?f?55LCab9lI(#~1PNZ9H7T!$0NeojhDd@u=TtJp2q#@8RKlc=`?= z{s9l~~9pyp(@;YS&#Vh1(q_9HX(%a$)B@DJ<{Hd^A@I%lkZ6ARVQb_oX!8hr;qc z%VPXbVR>Jrn3X38WdG^E4%>B)X<5$!!2@{|3}|3LdvSoSmDkuK$z{Z0v&NnzO!`6Akh=*xaf zACH&)aR21;l>MS#=lsfk`i+-2d@@G-|$bOp-!4HMyeSmYEp6oXs zpU#)GwkH@llLXA=kk;NxQ}!B z$bQd%`tVk_LBGQ^Ep4VpYRy( zzp`KSX3mf7M}2|IPu`cR;`%7>Q~WpAN7=9b1}o1i$oo{K7=K7#<#Q8mgf)y)>Cav0 zf6DL_en7z8n2k89p0S-bVCGW&A@Jzm#Dg#usIn=9kLw(?MZ( z5Wg8g`JWCduR17xI{L4Y{u@E@)UB2AtAJ336N2iuFeo1CtxCTmDBckio)i=|1<}7J zD4uvx^7E~r^l#5s$5TI5rWe>1oDS(rJP4>iC{76atUJNL**w+RIn~Wsp&v45IVXI? z(*I6%aS$uPS4OEB4ZesO7H!;7=R;IV*p}cv>Vhvw~}xJG>8W4)=Ab(9BY`=yh&Ztw51lFQw4N-|C~hucfH>1v0r|ZVge^5lBXoqT)+N{;A#<@)~L+ zMkXM|5R&I{9TF5u@r?l~)N14J7~$<3d~>13Bh<1tuSoeSZVH~wGi+%XY=j%BJ;fBSq1g6H+HVv^A)pECJA~&GrZRT@jrDlh&}m!aYQp!@oIp^e0WsH!16qi$3!v?y_Zv+A=$CCQ%6eHvsx!5pd!hW2b6~J8qxHL_= znXuMXzX3gfeZySQ(sC_=hbxtljF?Di+vG&yeOM}ajN$VCaSLf2v=v_k82gt6f*Dj2 zv5<0%KC)|L6r^G$zcR?HC2_d92b>=PfnLa$&5y4OKb+Jrqn zN{zpV<}*qek@ga{(IGx1^(~6^GaC*EExXvzi6yr_zS%fYBWAZ@iF4^JH=atdM-y!1~&DJSSs)v*kpiu+j=lqNor z4YQ-8y4C-J7B=CpZZU4cFVjX|fQJ3fqq1&+8OepZi17kZ4|fyg2x3sJR>RafTiHK? zzrCRRbcBNav3q1fmqqMmIt8uq+bf##jp6n_MOieEeh!yZD@(*=kC)hpuf4hg?uxNp z!feB83q4&yF}R5^6@=Z6^mz{5SpGGZ##_l*_Rm!|#~`zmHNTanMjjr9xNYI-Bxbf`{QHrfZygOXcv6(46WY;-w1 zE@$9l+3LiRYelX^*ctYL?J?(p=Wq;pMjoYcG;b_K!rs=XW^|;xC)FucVi?OUlIraK zLx+K{t*R5K=#O0e3-V#*@s}R)W5(zIA>k322%4bKene}P%k9C1lH_~FzERiRw!;C2=Bbe+L;~8^d5i2;3GHX3`?j^#?!a|#iv2XLxC7O#vQI;hFrOgdV zyCf8qlgrXB4oRC8in5p&v&4%+5-(!-h3s!mNGwjJhT_aQEOj`+uz7McXxY$1maa9t?TwS>@J6pFG~WM(WxZ3C#eaqnnDJ#H}dY_YYN87b+n#T-Hr zsJWH}*^9)$V_Cd#p=IHs#Y+~7;zF|p5m{!6wV2I|a&iE3ExEZYxqWhBqob+1b_;GV zZK}Cp?ahr2HwvF$H@^XA_2uU`fBzG`+QW|G(;vL1B)!uoN-6MKhmeVP5h6W@7D+L^ zJdGU2>nZfAwt&}G*n@pASF48y0Bp93;&l~)u>7ByGT|Y5M6zoMyMKZh^AcWuY&N>+ z6_=6YPFT2Kn{2kC{EB=z7I!6ZI(V&wJ@nobUa=5%<+1w<2!}Ub0uQF;HJ+*lr;DR( zwz9IDt_#G=@6q694fYuEe$Ge_REO`hN~uEVV4zIuohA#;Mk+P$0y6|oiUVEbZYuJNVgarB}dU)MUz$<6x{d`uadG*tK zbo)NlYIgLr&px-e-F8l|5k4HA`Q1w=3EnkVYJ_R@yRRuN7kW&u$tIncV*MC$>J~KC zj~_#N6uR?MZR!&Qhqb~Jd)w2_Wg;)=Orzhu$cy`MK1ScbwJ6>r!}y=*3?*1UraV1b zl-qP??p{GyMEIWeI~h&-e{Z4iDrkP(qm{}z0s2E)>&GWziH7$G)8OD26)(Ddf@XurK&h$i^R@*eFiDp~G5sd;ty3e>&b zc&?{CC{7#o>hN5W3*xl5s`3rbjoL9hTljcrCFNDeQ5}Y+Ll&sldnC*0){lp^lb+Du zXZ;4=)noYKnUF8i4HpW5cGj*tcXFlm<5Pv9G8sOp*)co|qBcn+^CCQ+J*d7-cVywF$5`x)|4eTVurJ4lzI+YEmK`rR{E^D5(!{?{u0KPfQ$ zr}wPh_en$fKIr<6Uajr7pFzhzy+;Fm7j}f>0k{u`W50Xp-u~H4XZQBo4q()p_US$O zpdm;!P|m0KX!m{EFYFlBYMx;B0DUtYJ?hVLpOpK@5aiPnWDlq=Z$nlmN!QTdPeKk< zFY2=layiPj;M02w_k9Li#Ol;fb&A^eDflB^PK2R91bT8?$b2vx0s68$$zCYS{PdoJ zeV?I@;mQbG*D&R>dQqLg|JxDj^o5jDsJ7?3(S9Lqf_5OeQC){ow_rQ>Hp!0CaD5Dx zUvRq*Lk5(-2X;T07U3U*J(q3H5bO=Lg>2(xyB*vX;o9K@_#z#n{4~yx>}7rKA$^8D zk0f(89trx%dq0|;I}+`^AK7wcTj19#g}$JSF+Fef&ZMDQz-z0*X}oLu8J>c|-nZ!9 z2%1_|#+fs|`SAC@_qX4D`G>DPd&56|^1&akF%@aD==I}tOemv~Xi8K%n+M?4b0O9@ zcSrsqU1^LVYso{Jj=0OB^~q_vuU2}KilYi1no_XGkd>6HH?EqTwWh1YJ&-=PXjGPAL8R6*TEQ6jiPh-pB19n#=-?D^oa} z=lCE7XtlOvCr@w5fZjbQDTQjpDgT)gPn$$G7EuNZDjX@3A2vOIY%4zvtnG-^C_p& zQ{LE}@`jN1R(k4jU)sz0X~O2#*v-}2mdsAKIo9j$#Q%49ce=f?ttGLWb2}GRce;CG zJ;HE)+WWgxgvzwx^pqDJsi1J`MC$PFlox$zZ{?@GVNH3zFXiy=lvBH>{vkc}EK)8P z^mprHx8!y<_H-^X#vmaAmEyjjBCcsL|%@DfktQxOHkrzOZM1U+!dYl3+PDC{l`RaP8kYji|ZLaP0a0 zn$jpyGs7FdNc@VB(Hm7Ut81%p&aUkfyvADLtAC*MvH$-Iu>AxCC{*s>-P$Hv7bSHY zbcbT*TqUeIW-rlNCwrk;xEm0EaTS3uy~Dc85I23hRh)csmsNb==`usq`sWS$fsWmY zU6Tri%8a_lb{X{3J7OOZfh-Uw&wa$IFLD51@QB$AlK~n}vNqN(MPozf?46C!zx|z? zcXj4^wTrv7i@ohr9p&+LgYkEtiLdL9Z?VSSZIAT~#=K{UZS=<8&HmO6#W#r@{|v)i z{l?JD`+Z3Tz8M9+srR3l`Br|)QAf)Cj+A5RDSvQ~svUNu9kZsrd?Mw2U+VMuX~%j~ z-#Rh#(C(DyeMpuDbw8p>yD8^%Z~XrZ#@7$Vzhg}}VU0h5zddKYhV(?|p^_cx1#>%o zdF9fBPki+Bz5jgZcW-|1^e4?-tw-dHc-4 z<4^q8YiIA>c>SHfc=0c1QhxR8*IqmM@y7YHfAOOG+|{4{`!BZ1$Sr?g^0Sxw-L1EL zaM$#nIU2$KQpF~(Ax>HQ=8=fwnG`(50>3awYmz;p;4Ub*tB{ z#Q)jL(yzV0)!VJW_pZ`Rie7GTmwfYbtQ4$hBk3{3_IBTO!+@8WSB~q_+Dl%)dV^We zh#Z4ke>CPjbCRotD61bu$%?agXmnmbil{wN`-E1H2^D|DcyIbQ-}3eeIxojyDTid& zZZ<~?UUjlh?xjma<;R*L6h2{m!icMWWRy2o*jBN=BGD=%05=J7`~IhEs$LA>Sh9CL zA;~_e2~Ae>pCQRguAb`+5EI@OjBEZnV@04!k~E%JusuX*m3LW#ll?T?A_h}?=hCBr zwW~z!+Qsj}`1?yG*)}&+g-|=P5^ZErr^rMK{>-QvTu2EBJaSR4m zHtQ98XhS@e+~vanh>=cum}Y*deNm9~x{rzFyM{n~3Ti_Wi>$O=#^l7Z?(LHy6KjD@c33FZI{EQ`VcKS+e$7hqZZ!3iGyW^A5Le_qb#4 z!BAMz>2}B7?bf!mcDlvRKyZm7L@(%5?!)KCO?6LP$$1bjhnT%cR?S$=g+hbcYV_VKegY0H+r%m6v z$=KPrt8>dP-@Fgb#D6dp|JUC5(`IcYY#W(9_V>7h#@qp4>dTIp-*y)+IaIi07yEml zKX+##7JW)7N((ZC`e{JcC!n-ihf) z>#o&hXS)+f0%>=pz3fOCPEUQSFZF&6v(xFRXB;sVTibIF_b=ODmDVe(fX(?CPjCbbZ&NcCcpA-{GZJUCx+tRvnIUPgHrl6+NQm+sEchE zRD~q$8AMb1I`Wqc^+R-dXsN^2fu7Wtcc%{Jr@hsevOc5A9Djn^HvvT6F&Cz{HydM{ zN;+*lo%ec?_oJCX7=zsKuEuz4TKx0~oSlAVpJgG6OM4ao}zGKI$MLmX0 z=WC;y?RNjT}Rn@e(Ooic4rQ~W9 zOW|uAC3Y=QX{eVxlJkNcmGc^18=7k!jq~aoa8k?RwBeI!HEw0uD;Xuq7f8jm_&gZCu(i3W8ZQhs)!gkW zFD(?yTrIBJ=FM!2V?FJ}nA}ow>;TugM0k?xH;DYJV4`KweDi#>n8~mv%OW#E^RpJs zUvwp8KUaTf)^^d_YHsuP_UWUh#m?35NZl@qjX)d*q5z1J)Whc7zU+Qs(y!yRIYrrV zhSi77)@?ccLh5UA+K!U!xblKunJd1M-EYSo)a@@=vvt3U^KRE&o;i2XqW@a-t)DD@ zdg)KHW@i6n|1;06p7F=$v+7PR{j;{c;@Gj%Z`^+5(96e%S4?^yyzGN*B$xD*$&Hz? zIk9)IbMJzllZq}G>~H%+%8R?FVgdz|2pyC9@{F95_V{|7z-AIqSm;pe;NKBY|q`>pZiJMa?tN6Tza4|^Qblt7Ih~| zEX+NOlm$x$3-bJE2G-%=0Ev*baP{LLYv)s<=mL9facTr=31+$(I7|Xbm19Y z|GYkPxkm51e7i9zO5fJk8{4;E^OR0!EuH*KTv6g+)S<5YbgyVM-fuWOE$OI`RaveV z^)cE^5Z(rY?oax%zh+D-K-Qtyz8H-~8ia3)IUP3uInm<~G2lXw({u7KOk(eT&53Gl_buBU+UF-lc{8jl z;wDGy`s~$P?E_xHYKY6+IM8DNjJZj_ZK^&~#AWBnmdqrBu3a;uEm0dK)pS_EWmP{SkccipAv~%L3qK2D|+Hym5fM}{r z^lh1F6A{hQ>wA88BCg~_e5T+W=qV?mwCGpF=>=V%_lb5cgWskbEoLL=e>J6T*&+Kc zxpJR2L+CXIgYbK;I1uAC%n}M}_e1lKK=bYIST3mY$r-y`ncKZ*h3>@mlW3%4@26Ksuj|Q!c!pXbo}b*Uzde1%a0Z%- zM56ssTHDP=izwjV_b-8>X`pDC4|xAc`R5}4p%3!Ap?6H*wm)RpHnr;-u~W0_vfS#+ zWD@gdx81@?+%JsUL)J-adbTEN3!$#LP2OQ?Ax>EUTlvDCV|p z_p<-H29mqThSTE^r7>!{E}z*U>fpNd!i)dVUUjU!r8V}RlEZLD332%Q(5b0!32|@0 zALU4eL#($q1s<*5q11u=)E7_8#GDzfsv~`=1-sLRPoyFU7Z&E{FXzvI^XmQlwBvm< zF#Usb%u7Cz9BM^l9QxNx*vCq%03}GWNKIInqU)ckNsN)Kvwgs~Ki^7riWV}rZF^f~ zLstQ(;{Bp>&di_M{z2L72AHJ8B%QTIIMn6qgEqRI1_K{slKB`jm~YiV8Mo39G?Z_a zYFxobj{a0#2aO_`kV1c&+^sA;>oAgga}o7sa!OpHG$#3W#PUvO++y{kI%8;jAwTbJ z+lJ!??5~L5@fmBAzn^Wuu*n{W$i9iugg6?9<4D{t8ni+M-tDMQpptDBF0vYrR zgsVV*imu2nkW^V9>0BVkklb#DKvo(JvJ{#PTnejWJ0JzB?`_-uKr$G5)JL_HbbUFX zN?$InuCsD^{cEi{od$OSKM0)&YvXRGYj%+t6+*Y>B}bLytqm9o>MRcfZQ; zODr~~k6YBE@-!d?eI!QLW*;ie=Q55-(a+M6eGE5_WPbttOS9$fB1~ip^ikgTF{jYB zV@@&jqL|6&Xjk$1!mf7jSBzOD=1TW+n7!K;X2@pGW3CR2jWOu=s0TfpMCt5KR=iXoL=X_;K$wLRdmv_c>tvT$y`nyw){pC2z4?#Lt)T@)?FGo6BKP(5V2E>wwJ>@hQ@rTmWvj@28fH?aj zOar7XBOwFd`T~9~!pnf4!|{6H-vB-n;X8o8LdMTSJAyHqOLPEFYfz%22Yx#6M0Y3f z6ffg11D<&N9`Kha@XLTtNBF0}iwgYhz*CQY19%!ihz{}D3_PX#3-EMzFQX6qH#z-8 zD43G|e{lTeN<7ljz0u?!SOz@U_S5+Qcq(rL@O1V{rXLe4rMn;as}=Yx;A#E14|rS! z?@xa{@YIe2z*9YBI@Q2a_)XyDdXb!c9RJrK{3F0qc~hYfS1IVc06ewVV&LZ}@Wa59 z+;0J1ZWof<$H3z(3$`2aJqDhh@Omsq0ff!K&*gaHXCClW|3`qQb|d~NVIlCO5Bq_? zLXmzW@F+s~9qVSE=>y=gC+tuEZQw~RQ98B!p8}rbnGC!v zPvYkl;K}!t1$-u_L;Rlwp4#|KL;g`<; z$?0zep3>cgUy=ta7wMaMdZMXJzmwx1Q{eST{|v|XD)5x|2=K(;TliJ-^8ru)aZviH zQzUyb6a7)PC-e+>D#7Zmd|5D~WL_qe98%H@ZBI zn)>`^PeY@t*5v@bH7-wcWBpBz+Gba9GG&-04j`I{0H`9{?b7v--cq_!YN;x%!|A+%?Jua&(t8HjEnp2xWUhxNekoq^!J&9{(k7ROsZT|t zqbi6vqHd@!a@9HNogsNjn^rhHj#@Qd@j{V`%SI+VnU-ICj5#$0DP>R!h`+R{6xW;8 z;5`4`E+@ikU7l60En#Hm@0QmdWfzzbirA}A9r)GVPt5KB*(qpkjJZTYaj5N6Gkhr|o;Q?!P!Rf;! zVeK}CbbPak$|`MA^v%F-PjNlHb>dXfsu?kjUU+%snbG#6qYC?PBtG)LtfN%hB{(IYZNYN zkU65ZP}w6wC=8OC1oa9wYN#v{&k}5FXfPj(O`_~DO6roxDsj4Mv!qT2hPYkgiNjc( zkQ`ysFv~QSgtBR(;zqVjYHA}JC(gX0pNE(_H92aCz?)hjiI)aX$E1me0vkHHQSZfL z6I*&oD*Rr&f>OwIPZ1Yd%6y9=I(WjThzgT4OA#Gz%BP49lRH}x9Wv=t#D&S4BiS8# z`N==+W3#L}%-)=sIq*5OsjA9?uRi0bLaj>(KQss?S6z+A1nY~>2G=yvAZ8*`CO5?G zY7$KAF)P?m?{b>h6`vR`94(HTT1tmFGTJ5>_a@9NO--0ER$l{JFX(TG>Z+*M6XR#q-C3E+qvJy?0EAC4F3`^i|Zhr}Pk`k0;~353MQaZGoF z_(yrXGeSJuus~dOg!pRiLy8nX$m0)2$iJPO0eH$K5W@d)9$yh5UW8KtYekb05dH>} z4iNrA(7NC}y4|!Fkka=6Qu;>$Dg7=9=G_ZO={+3M_qeY@ z_;x_bR{==*$^j{#mE&^(vCcP{0V#hbha%vW2-A1GDZd_&^6LO8|0no^pS&~pL-E6a zM5h;!=p5j1KOokqCi?yzrP~Qe>F8ZS3VQ*G?!AEcDWmUQ;wNt%{tzAV{Shny%mB;* zq7yd3e==@$Kc%!y$ba zjq0-w5dIw#eRq`Tu zb4u~Mc(|K~OL*7{2)~pm7Z631$k0ha@UL|Lu`^F%=S2jj^{2tQ!F0T6yL`eZHntH`xU{;PQ613xkJhu{$K z1baE`;n2&WokJ^!B8LDh-yz=|hHi}0$7xsysB|L29;pseF!oz<; zqf`2F9=?Ug%k;7-j0Tn8uRaUrmEoxr4;t$rOnQDv@rv-fL?3D7_n%Kt7&b_Lf4LrI z(|2lR`#gn*B;Y|O#kDcF!@xK@zfT| za8(e!{Gj|tg3?z9g|`QVzm4`%(x>l`E5nO};+F;GKM)kYKPbH;C`|TTNqNtIE|aCk?qvKX`dAS6$JxA*;XO+KuQJV+2QD$tqhVba@n1TBz41 zC_#81LQtZB3nnOHWFJgWViXldW#uJ&P-4lGqSoG}VbjRYn~~F^aM_m=M1_1CL8!3X z1R-hUjghdf^x)KC>(2Zfp(z!vs?bQex*>2C*IUZgX(Ib*W|Bt;d zfsd-X_dl~FGl66hAWDQJ42uv@WY_})on%naqQ>nNEy{$=fPi5U!=jyJfUyN@w-lMx0c%Sf1Wqq z*7-Dk2FAGjI=ldaW1`rE_F^iOmrhabf$KsTLkkNgc$M>M#v_RCgye~swB8K>ESt}K zxES*Bk7>-)p6jGf>3j{PEfL&_2kIrxJVzn|_cZe2OpWIJQE*J}bXZR8t4HwGIa0Ttj{bIMoQpEb zNLSA;coy=@wF7x)aCSj=%E7;jCTw0U-cdw@+;ImIfmrahM5jGkY*%pk)J|6Cz!~bLJgY&Q}5!>|r<;$>xEvlse%51QCy?Ka|~7h*Y58b z=NO2DEStAut=qi=+l#8P4=yR(kb$+?IEY}s1!or&m5;y2MMPb{%qcchc7C%pEo9SO@-rKXX@Z zkL{L_El|acmJ6~ROA3SY0_96YttB&zy_3~RwGA#&%B_yr``K8Xgw2(;a!vU5g3Q2h zC-&d*fPw{q@)6knS?E|Avf=R#?3pS;dT_B9V41Qs6fDwCFeuaxG;rK0Mx^D53afvP zJDz73%*j1YffAM0u;`p0A2_=#*IS7_p+0OT>K>}YZmBWY7rD(Ac6}ap&5>L74vG|P z|D5AZ{xZeYB$Bax=A(LUvB5qm!L6xlY+me9#YV_y0U?<4vJLyKvg@&T$=7{_Z){}{ zJEwA9sKF+w3)k2J4w0MOpl#Z!kKZSfzZPXSZ2n;FWpx?CJ{G&59>wM_Uw3RHEwhb% z2OFL0a>T7E+$=eJQ#|(g%)#!Sde`3q4m^IAPdxGCtAMM)%@m|JgyL_lO5Rt`^lv<_ zZv@V55InbGFwSi#$#w=(N2EB#ikkS|oy0X_tKU|w(PX?lj{p`{bh+D`*4J6$DR8eW zu;!))2UojS)@1ihv|lqJZAGt#-KllUp}9mGw8R6KN~joua7*?@^paVKP&&JaM9zBXZ~^Wf@4RA-(5F(!IGPf zcYC95!LTt`UG>wC{yORp7c5%+ns0zVmHySD`_#bI;_mC#d3Z=ji4a~i3ywTX_y#<% z+lI7UBO36hVrzBbC&&|U>+VQ+I)o}#Ui8lZT$%JwESnbRAOB*%XW@suPCoNmW%aBd z{qQwzyoEYy*Cm#;Yo2pm1l1%u+O-ZF>+@&!!4e>%BQJ^GFCOd&Sa<758hS(C#`3U$ z&q(v9ihGi*7Y~Kcd)|vg){TAIsmea>8?jG&d49?P@9gt)xeQ9~&3*~@@AqcgzTcbu z|M%W(x#xNmWc^fot_$6b;@0tMsoRwJTB6O@XK3Pt`*x(H4e4B2wMy_7c!`zHo_fuEKq2r%-K0?F9QK&w6aabjEX2Xl^d1mUjI)CG{0; z3zp6OhoriBcv0dNKR}rRx7J7KVz1E(cCnnF*dOn$x!6rQ&XXb%0u|i0Rf3&0_J>kF z(aW%F)x|ze?ulz12YZ|)Uu@fC8Em)L1BbXc?mUi9+cCtR=lOx>2psVc?D}z9c63f$ zD4wUyFK}yp#|S=nZzYfwx!l|1yk?VC=AnI$g5g6YXA$drXY>d~yDSEUi7GUdG}h; zb$wj+rXL0DTjM5%Ck5jF99NuwbHMp^TxEV?puoB+#E)>tdfhPTXT#rJ_>XTV z;E#As#2+u-$q&Eo+{=&;^H6gA`gGiV_~T`~=WpicbrN~UX+LE?8OCB>#fN}`=`71X zBfT8yoR_*Ae`A#NIz7E6D*YtVy+}Kbzl-oE;q<}pI3cdUGxuDuJx2N}q=Vd|9O)M; z>AR4AC!Tj8eVLMe6$ny>=kXv?P)UCq={Mr}4W!?oqz}XZ3hBx@Oujej#ZSRC(gy$^ zG|%Gt^Hx7y+V*MZ>M8T>1)-c5-%b$YhFrX;y+sGNyQuLU*LKz%6Ueu6a(#R2ZuMCAGe5V!ohS_Tv^{gsemNO@ZQG;E7f<<@t-$5*?VuRrx$PA^ zlm54p@ahL6+KY#@0PU=Q98Eww%SLQKJIlv50__AAWd+&^<=Zg>?XCRVv;*zL;WP|E zdm*3N63{n)zxMKU*ItrZ+J!KSBT5V(O_xkZioSftZhDCaBd#3J7w(qjtC)`KeC^hv z{0=Q$?rn@!e^`HyF{+sGkLvGJd5?jAB4RXNn5KzYKI;_k$La57{W0KAI>r0Vs21aL za-A0X8Uww^kALxQ{mutIhmnu>kbbYy?*aYp*Y95a?$+m{)s`@0I7@^F6p_%#(2)9@i}y^bF%SpTj7}YXtK1*`OzW%4dUqWqN*t zUX1aX-{AjP{k_4@1$upk{9T|wvo2l_$9qq6ETMRqonhRqUhxVnU$SMrE4kPyWj7G%%p3#s|9(~z{X&r{|+VQ#9 z-!!@a-)M>~#}KjBV{C$55pK<}oP!(J^tN`r#{nT+^?DxT*;tPOGbyjrU5}w>kH9mY zaStfJ!MV1-KV;| zVctq!Oy?gjYgKr$YY@K_0$C(5tWW?h?$Ud&ZZr_21#V=f`J0?|JcWw|w;1 z-;KHAk6UWL{LSjWHBWf{>e$#j?^=DJVbmj!J^l1+Up1tjvnFrL9gEXG>$dNU&fV=R zXJP1V8XH8h#9U#bGFSMF^YW^c4r|l5J!ik{`+36e&v5>~a)$H2={%vuQ|eCb?acL* zrZ{^$Yj8{Xex6Xyv9xynFz0j7Z4sQ$F*?4St6>a*c>{J0(9e?-XkC*XYduslla}-&4L|ua!j(jJhq&(^X-vGph(m`^Yt2#P5Xu%ybZ&4GX z!megiBfq{MyE(nF8|?iu_4l!7PWJB&LSoFp^xTin2tJLtjq;#ApJvyRk+`vTT8YZr zh|5Q|5|?KW9_f)^9&IfypFCq`adT`QD=zmkn0Dh27jHC2z4#`j)9vmCxfwf49_tGl9yDEA1f-URAM8 zo$1?i%K-+zx#w*y5;Xv^=l4fXst8) zH5d0(TN@Kzo9oO|dT8an9v#nKHc7v<#WwLbjRq#EZLOcQZ3PxPBmcw=Hqp4w+)tVU z(EM9~_wVA*7&B{$ZR;r~gO;6#@%_d_Be5PF-*^n;d!Y^IVNG^Facp02acn=nr+EP1 zxIBn&WPXJ2XCB6y?L%BYjy2njgS^Mk{j0!PiT7p2;rTp#YcdJ27dvDw`aL4yA zv(Cl6JMIH<$9FcfhU1QJ;$@A+9pB2!x)^t?Nzb|hcL(m*bB(wDJ;bOr-pp^Rm*tZ5 za;P_YEji&aKgK8{Yv)fa-GXnwN>{q(#L}7#A$&`cfA~IP|Ho(Kc1`gdTr01vDQN{!$Ok!jGsH;BBcjo3H9Z_ko${6_5te3y{8L$~n`-xK6} zwh7<9M820GE!<9z@6obt+V^Ein=snUcPfpl@Lf$~4L{&j0N?wZaexOL_^nN66802~ ze4^WU9e<{A&p{+_r-XZr{06S9hu=IDmW$pq(N^J&`dv)Yxk1N`{4te*?5O;}S{5l^ z#E*0VzH-k4?yb@n<~@}(V%v$MX>UUmPNW&}VO~jF;3K~^iD%+w;Az4!3I^$U5-^A> zze_B24edJ~KJr@PW@@AItx)CrsHD5`ZB4e*X$M}qY#HsOZX^iOHsNFP zK1-)9>!^loOWHsdohA6*D$2qI%KD9W+!sPRA0q9LkG941eN+`6>J8{6bw<}qqs>OW zF?9xV3!9K?$TZtRJF%fIbMuC}oAK9UGjZVeHBEK_pP* zj~IJN@GaNKH)Z*q=l0@=P(FfV7Ifccil%d0I#})Nv6=c~=*|Y;}O^NAy6?vqIl@#XNjx+@&31 zIofXM5A6Zp(?;%v{a(lK%;MX@^h-uL!_H8K`Hty`cT>ag=hDuP6fYU+v~MStj>mVz zt#V%(zgNxtj15jKjl;K<@r`uS6zw7{H~2C)SNU#cKNdDk(^cvYzoU&caj9YYF8UID zbDH(@ThVC$Jnk)X+MDSh%L8ZXAbIReK)tZ}Y?D{|InDPH27Mg!OMOt_GmY;tjGW52 zLiKM<*L>K;_|~%)OBgg|Ig^e;-?uYxFZgyPJ+y`)=CY7}Vk<(lgvKG&bBN zFgBcwd)L5N$#2?!9e3sdA0qtTVDKAuoVZe6sNbi{Z|t8;r9Uv>8}v*@x}eP^%r2p^ zLI9_(xZ4BxrabjTi*bmLCIgp226Xw>abek$zUCN=JdpkkvQ9Y%t)v_@S~Z!Kb_Dz( zJeFlU4?$LqZ{2UuY0CH2K20v@zp$xitgr-t8}4y|v1&{u44KjN$SgCUQ>!wAca(L~ zh5AT*w6@+XaXVSZcE#fx;?P}1CZrBSuG%XX=SzBOa=|+P?esq;?=Qi3&rhNMka_wI zmG9$KzIRdh-o?y!$cgkND&JMT{qOKN+AfgCh8-~3XC42wkclmz0s9j4e;Y^-+Uz>& z|IID>6$b9HVkGoQQ>jPDbIKSQeb3Y? z^k*?_gXYf^o8OAQ8@f)KseCu-dv}HJ-OYSA^j*>weoW;*V_;!{zQZnwINV{EOg^F= z`Y!u~x?g4cwRS4~Go9|H{u$)r->yHQ*j%GOA!9Z8O~bY}z~_V^!&OtoPPUc4mUsFB z<9Eb&g7(#r?Y-dhKGL1}Kws6)p0v?NIyRnw9Wd-6_%8h|Vwwt6)W=tk!9(W-hI)?oasW@9f^Jjzf_-K&#CFuD}&IBz+%F^vl7~>^Io2m28 z(~Ob&QOA;-eQ=}UgAI6~YZ$Vk!oxWO&p=kM(PEQke{aNH*!N_@n|QHZ>}RU&G16r` zhB#8%AJ_$sH!wf>dt&LO8^I4P*3@xgITMeNpZ0jIO|i+peWKeooi-7hinai**>9q4 zT3U=)3-6-+=3L-~xLWh~vFd=#wWtI9XtfT!i>~8()&V=r_|A}VBYx-~CpsB1#5UcR zFz-f*Q$k1ReRCPr_q-N^9__Fz(YOu(uEL0U6`iEMRn>`|(2GcXQx(jRV_@W;)W>z` z)1w~{iGNTgT!%Ese6>xFiI~Qi7y3{gyy@s(y}to^%5fG|J`5d?#*ulepz~60)P9N~ zH{bg~d49)n3=ezNhE#tuNV<_m{nZ{76FKtbB^zn|_pp}XxTAr z0LL|q-_mB#-%6W`FXi}1KRur8 zXO1VGMm&k}F==0+W2ZCb#=JcNUyUUVpAa@+!FEK)rme-2gqIYBFVol##*1n@jPw?? z*6ZbajOR3udl_~J^p40|tXK}TLVf0V5iqs181W+BMaLeqfNPua;0MeaX!NzoYq)a@bt$EQF$Jt$m195TFZDP&u5Zscf)?}NbhJSq zKTyXFBDO!4zI{J7(Dv9sES=H&E8nU-BW9QLS9KYiDHn`oH^2vIafCJ&zvMSB$=d>|<*FN&2F7=CLl0gI3uzdE9ICH(^Um za;cATM32$S^&$G$3Fi*bzEjS*a4wvANiUhd>l57?jWq@p3k&);@(!P>zh}RI zX~x;KrnVtIJh9YAmb z*ohqPRmZn|7~j^{E+XbVvDAurSl|M9ZO+Af7qpAhX%`oRc6ir9F1`!ep>8Ro|J!Jn zpwliP2JP_fUqU<7{lAiSNjmM4V$crn{w1_S-R+`Xu-P_p4W`i7nTY-3DmkBvxm?;x z+AqSVEof^!T6=7}oIi`Q?Hi(OdkY#y*_!s$YuGzUH~OG6@hPh9fUV=YIK%cv*0RDD z8*A9qK2n69`goGs?~C@8I6ik*wEr>uTm%m4A$*fxh@#K8VT(_>Rxk!l_M(k#(S>V< z5MRhO=F&fM-CP@fO0J(pTebCos=xnk{m?1fUdQpd4)A?gm!``D*IKMYyv6ltTQC;1 z1>-|upSC_x8yC;OniZ~F!5wiI$B(!MrLiCA(XTl!YHWiZ)Qc?4WjBryFP(?q;od0A zbrTnfmxkcE0dR~unO9vGS9qFmIi}2V!AWxND-2)HxS&CA7h@X98}Mi! zcx%LC%*Wp+fhX}Wa5}N{a;#U<=f@S?jr?j}`Mb9UGg?n5-^RGyA=GWG`(2^8kve0n z6XkElMHY*8x58&f@Wi`Q=@G~BIPRgsGne^Z7D&&3nJlPwN4LY$?m{L`JN~Wfv6=pW zc9ghNr_Ol&d6dnKUYorU;}=fA#U7%0hhgXXlCh`#GLYeMi4^ zFXlA%>2s8{%WV6JrOk8W==Z^HTduW-Zv~A>|kK6HZ(44(-k<=^3jIeND6+ zKaH*H@ep}~_Czf_WKhS>P#&wpRhtPlD061Kq((_z#tXEHQtapYu<~~Op z>vI+uw(MiblQKU25eF$Sru=USy-m7}5WoK4`o(_6)e_Q{v#JvfKYn>F2pm=5pBD{+13OcR-xkKWY0T(oX*gd!{;Q~GkDxtE{t;iF#)Ak?k=7qH zu~;14gA-)O%T?ce7^JyVta7O;;Ie9i+Noq)r6%{Z`rz9)VSC}-#q@sl_} z*0UfxgvobuU28v~#VnU%pN5{!adCrYglPdhgBGGc+SH$VdF{sIw_Strhd6D0T|X_4 zX|IDGi^b*tll_aO!>6nB4$uRz@lgy( zqt@(?!~u4T3K430KEgZS17 zJ4GGfnm*!1xn?_9KjYtx$S3W}NUY&7)^?*U8Vw-hu%G-*-J%~^i!yv>c>@Ob1+q@8 zkJR=E0zQ9pe)yx4C%c;3uj3)(6#X~X2NVB|z~2IUu@-G)9i}}_&>yC$`#;n^isTdM zB>6;MqFs~=Ll$Iw4?d9Qrgg@ujH6tuGV5#JcOus#Lw1NaWsZ2Ujr8ZVMUtN7pl4M7 zpY)S?p~Lhg%zHWd#{Cm?AJXrMZpN4a_Yzb7an4riA>7w=o0E4! zAI)oZo7efovLWot?gO0+f6qOf=5^SlkJ=Aa;v3+@wo^7GodHYI8GN+pdQO?(zH!<~ zNe7HaMC1zXknKnPk+ssGUsZ3dEwax=In(9OkTJrOdrzT=K#vVNK&jLSV)XV%9QP`C^9uZqo|HS3W!%3Pcs0{b+0TvG&e$6k?YpGigC3CI+;2iT zH_j5^{8odEp)nohoDawqM`Z3fof+os@ki-JY`zIBVlRwux)f zBK;{{wxzBBZo^dM(REz2;rgCXDSOT9u49eM5O;p7(C{gHK{HLh6h5J?k$xC)(fMhT z!5DHt88G^N^e41?EUU^H>r(MzKFXYCGrbre)8iJjnLJa=M)Qk0MLA$UQENlnbBCub zr>*hY0-loJsMpEg;CC}`n`tKb)LY|Y3%pK;wo!SE`dV$Pl0Ulc7WMlydt|Z`Hp-dKGF`v{^(1?1b8LOz~@HbL;f0kcn7>=!;c+1sl17016ty# z@TX4kCI1bbHT(;0HE}oD6h)3HYccK4>E!WIp6xg%0ekLXXSt7@xq|g;uJMH0)I);L33a#Ma~WF>%9o%6D<%3+JJw zZ(GqHVO_MNd~dRaN*!lpLzQwWZiLU6N7Yxh-&7}PugXxw2htX{W*7er>N99dT9Q_$ zB*k=UIvPAkaW^!}yGR`b;bMG?FEQt75rM_qxRB>$6hJ70_eBb*R zj^&uf9$KO6zon1y#qbUEXYdWu-$KXR@)3t=2jC|XINpIVDf$J*;?cf?{>|`TEoD;i z&oOkJaq9QJLR+);oyP1@vS7pqvQLJ1fN=t2M2;&N{iRd-{_g^2kTyp@XGiZhGOi__ z8*I(%6sl~KjyCku;wn>|3z;_73Pj>u$Y-Rl92MspeNx7{k+^f67H`IkccBw)_eq=g z#mSOm0#_Wt$ByZr0+KQD7zy6)dm0}O6`wg%Ua$Ey4#XhYu{$j);)b-{$ zg65c;?NP@**|)2rF9)0rI$YW!$|&;^4)(D!I-(3F3s>wydZA0JF=`-j!zON z(pD=U6@S8iw=1?$X`}Os@cFj^ZDU(VGd|0{Eb3Qfi*0B8Mmz=LPPHG(GSOoMNUOk~ zjHA`|8gih z&L}5D)BT~Sb5BX<$oU$;n{fttYT%DM_6x??D~YzraY2ru>*t8dGL#X@L|t>=Rpll1 zkFg{35H}NjPO}X9J#t-k%ykzEj0oRQAMI}Y`KF8^LQksU57uHFai1}EiZ=0_(~Y1d z#~^qf6W3jc0!;^K+hyO=q0G?$AFgMY`=9We^k6@jcglmro&9Fuo`rn0IoyjWX^i)r zyODEw94p3nC&$HP8tYaR{yQ$7VBL)M~wjX?`(TQRr6=N5f!pQdX@dvM-eM0cP=@+?r} zok3Saw_1~T@=x^@PJJ&+p+3JS@j@Pfu?LGqJLArz6ABH4{+>D{@dwRg>1VV(HgIMc z6WtWpgh?KrDv#uWPCpYYoj&rtrP=00;}Wxux|X&gnt!d&Z?s-k?W3A#+8VBUI&q{f zao&()ED;_D&GN{6)D_7K$h(Oi$gA4=7UMSHxf67X%vl)o7^b-kGYra7oBj1D{1cp- z?TFy7zi)nS?pJeOL|ZqIadbW6+ylLF4tPJv1jb>jY#-)?JMyf>zvP4+SPzdg7{fN+ zq1&)?dx00%y6y)J4(Kw}vj3%wzt*+nnY25!CDaA#Cg8Nnvp7Y*V!)#8#Oeo|;02p$ zOWR6bP){R18EILxyTF6%Y56>+mq84 z9UJR(I|X`I*`CPZ+^aSYpvLy}P0HDi@ImxV>)8*9kpD0PZvUJU?#Dam-2?3(1G&9Omopz^?o#|+3pxHFQ?7;)wofU<-MR4 z$vEA;N0*&w4JaFzBcQ<8o#MJsl$fM>{INu)N^UCTgse_@gS$1sB5AWW zRDFqRj~Y+4lubonz6}_@=GcVuiCTQ}Z$7WwO1oplB5l&oN8SGSm`kPpG0u@=ACR>$ zbV};0Zm&#w{T=8J?67nJ#@9?ER_6z-Wt%eH5mCK^rtu^~*$8*cR1Z5;koa@iEqn8RJKs zTi4cND|R$Sovn_+MbT6pM`ha?3zMe(6TlblDd3(_V~?3(|8b|>Bp{C)wpb)+xJp_B zcY}_f!grsFj+8yN*BDzc(bCs!6C=JLMq8ylCf?);=lJ!0G~1)nnRvzWafS~df0#!t z6WzZx@YV7uJcz-=Q{|C9Bi4Aw>A=!yL)x`Po73%g8SRq(M(qfSu>vj_Hh zHm{X2(M!p1jMWea(tc$B}@7@}FGmDnl<9Amz|B@AKiODbih|D}xsovMI4>n1*@ zgR``aI=-Z{sf@2#HuvoMFy&=X{rdcl=Em;xlQ+yJ_zV=*qOVVp5imd<#m4q0wKV1L`r( zRiVBTceP%V-UVCtl^Smpo9xr@XMM43l7%Ue{`hX_b8BS+iY@4U2gs&n@k%y9J&x{MGmC($PnZTCek+ ztV>sDuZ|_1@%TZV*xc#`K=f_VnN@Xc}=>c(33tf5_=K% zIOq#v9=4gjj{Dw>{_M7>{;bq7#zY)bltTWZ4ZbM$4|HzusCIQBQ1;}_;XX|utm zb;S7u*8Aj|8^9qx9NXf!J>`jG<#G%HzLRC7oj|?S;3v<>k!kRwpp`tEfIgJ_dgXUH z5u?fXh`H47aW?8_^V8?@ObPC*m+i#3Y(xg|&WH_rfgaok#&304<-XyYj@mE}O4!Dp zFRpVVtc}>uAz?j)bqzYrw0b5r*Mq&mMm^mBVeI!M-C3S++)?BEd?(NH0Ui-L5P!_0 zsn3=;4KLP7e#rJgmWUJkjNH4t4(s#6z0LdgjkdBLwv~H^dEPW(lh;hAO(ktvmgzDM z?hRYZ^)5JD$Vg`#Y|xAOC})}u{Q&#&ZQ8#4-da0aoEZ=P)2~bay-wWMOOJaDn=k#p z9z#ocgM6)pe6b#hJIbj0TbXY91_*3`$QvmK8RBomC^9ZE#lJqvX(IM<*dpgR z$(RuH3Qo)?H-ZK%+lV$r z_lEQbKjofgoQuIUfxMOkz`^|)yyyDgLy&WpwL4-JL z9qy$b=952o&%AP9G3IAVOu ze%3uKoACN0+!xO}Wd8{=1)Kd$)l_XBlI^wtuW(S4B`IG_L)#2FVp_vc*u8!jhtSJO z9wQ&mAmKQi(FVrj?w;76i$7Bu`ofVtyC^5|A@0b}zGCE=_8GV`op&kQgLGN>A^LcF z{XDbVz=8SL4(f*h-Vs>P6GJ9R-z@SA_96@XII(mBzB8)pz#y%Ry7x-$(;EHzNS_pX zq4v?$KCL+b@AYx(N{d}eo&n`{^eNmneJ1Et(G!w&{W3by6%!@RlT)-Gj*CY zjWw>!bXA{?GECpkvk=&yqWw9OJ?l2M&AiW0(irk2`*^@drLF3>4EsP^NPiyb)A*Y0 z2K`lwbutPM{-u3r9k15#g$Tl|8OI>rs8=hewZRnU zDD*bTJj$tfH=-Rzow6O|D{wKzRyqwys|bA*{gCv59LiW~D%KRB?bH?dO%~Wg%D0SR zboxk|(LdMGSLyiB9>s`bh>Kfa8$e%9e5+7LHE`lPxh4R%%Z>AwXitr^K7eav@42*B z$iw)BWtwTLjOPUv+L9)uFZrR<7_xDVkaN~bpQ|pmU11&6@0MfEnES@uhz3XLug8F6 z%rDSpv^|#CJO-l8A8N6qp~psi9~mQEhcQz4GdV{3v_?b1H(~+CEyfz7$Qo7n>+$NC zA$%&^*i6@(5wA9(0TT5Qz3j)e2v%&)E|A=w&NR}6dHLII(bwqUxj}!=yvF;lKo>rn@DBcq^r~MBz4Ep2Nne<3 zd|gZZB|n@N$db+z`MWK?w5-FtJ~T!hly}mzy*7gOlQietP9ymzZG^@{@=LSh%5yB6 z4!jsU$o+QE5rO_z*cOoAstscuj64Gk>F%h07{_kISdStsSdW4=Q-XU#>&)v>TKL+a zGIj?YHrC2Z8^xFdI!76l=WNh!(igxcMb`I1@8q~K@R4hgKtHulqsEWyJGZufj4?84 zMZ3)La;~c)A6l-vP<*bhIS%;NVb{@i8|ULTuV*1WjQwOAg5BXqCxK=^(tVe5ZUWk3 z>?f1Hk!$NBF}_AW_Bp~m^nPqV(D6K--sF8h@RGa-4QTVp1CHNHSl~;;d3dkeF3Oyw zTM+rckJxl;KDSo2b(C|+WrXIO2bS_rJ^;SSUZ{2svZAgvU|z$g5~j4z;A68)M2)Y+ z(7n_4t3l`oX#kqYwE~nK;On&D4{;)8T>e(i9#L>7y{deGA>~?Yhm=3yE$NN&w8dzZTKb^Vy#xssRC`;XQmyHFi8i%HO{G$Fnen7zN@pR z)HzAB{ZPgPK@Wqz8>S+z1-HNWRK=>K2Vg#GkT3A0mH)e$*|aoU}dQ zyVOmV1st~D)MusapnP+@-O^W`wk+4thUx3UAv;DKrOg>*tO>ZqjQvNmqzf-qa!T=A9`A_;jRo+2A zNk`fr#`DeeJOxdFpPV~f*DTkp%V=xld!nG7v_qf+X=wCk%@2@)3CqzN>m1Yb-><)qPy_x-1!|z+MuTsqYN?6uq{tMV>mdY#MzM z!?($Lea*6?$|PZ_{*gAAv?I-pzPOsdwY3t49MHy)E{1=h%oywUW66SPJO^#1k7@v~ zBWsX-l!w^ohf2Ai3_{n9^Fs}pm*Z62%Z9Q4#&~_~OU7*Ix9c&3ocq`Jy8&lye2jVY zHjp+_HlWA+P1%;;V8VO)6{|&;XITbtfxCC8Lj` z!PRujfGOn~`Pj#6fb3BY<@YNwC&6;$Da#r$6Ys{Dg2Wlm%qP=2&q)k`*!-kEBy2~~pjj|!fZ%`lGCuK^v z39`Ly|D8uIyAH(No#nI;K zWp#Ry{@|e|+cw|_dPV0U4unBE$~&G_on^fzPg;_MUkJaw%p#^;ha~IOWVoH#mMr91mRM;KD=hdH-+GIEkHxmZk}ZTqSY>G;o;}G6a3={d zDG_iBF;hagKOb!AEh}>dES5sN-D*h;TSPq}ePXe1w%VF36Ph7S)8M3tRlsGHCGiD| zsI+)BIK9L)A*MN(S!M&DkR@@CMeHD$MvJ}5YWu{5P`U=kB^C;?F!50=0U{K_8E)w- z@QgqE28*SNm49MMEVGKEmNaYxx7s&ZZELLVW|TzzKV{p@IHB0dpI>abKvv*fWw9-r zD8xHJ$|}N^aiEelWvf-ZUWQV>Xkv^%i@R#rBTHtA*!1inKPl@}p68AHgXQ9DQi_m77ByS_t z2}&+)!L->cpm|n1WcX1l)Q$EbR+=comwd|N+@UzJJ`U_czC?li*EyGoN#K>VE1JC7 zq4qCWie*|tu^l?N3>hW8k}URg*csH_>T@6Gc+>kF=cLFp>vRj@wtGb%FW~kxV({iBkRkXtL*(E@? z;xurlg4!X|l4M#m9i!=#mDo%-qYdfBq6m0MUICAB&e>vuS3akpz1Uu6ac^Vo%)@r3 zDYRib$2mg=Es0C?rL!@TZ1@NxAz}da1iSGVBxgUqZdt|y70F7Iqn%V=TDzfuweS=(NpIaEGV44c;U>c3r3H=WPZV%>Fx{N zQ*WEQV4=IOd*%Z7+}mf*7QH4HO-A|2leO~KO*YC;o~)FgJXw~Xys($Muwa3E%Jk`T z-P2|+D7tOI^l8E<5wphV`ecPhiCA^Ss)0zhQpVTZ$XF$F7cQJWr)Z&j;cf0|)8|j0 zfxo#^r^}|h&wy-VYC%!K)R_wxiEgps zSDZtS$O(UwN~{p#lPB|hp$~^6fr2q5(bMO_pFQ>D-~aUEU%vgqRsZq5tGH^K?X^xWK_vLQ0UlyM-WYqnZvcz-aoabcP@0}7zzu200f5*K2 zDMM1TooPSlFl7FU@kLvE_8!-{d{s*B{nlyQoX@4YeV43ozit^azbtJ))_{t^%Qj8A z&;8mqz`e(f6>*UKUNh_|&*zoC-su|rci+6){P7(U`d<2KT;BY?&f_HqouTmPnJcpj zzPw?g{l`BU8)*FEf|BCCTW9_=A;IFk!1-(=Qd;OMzPRB?6O{{(G_x8OHr#!v0 zS8&RT8Q=8lV;7^mmA$?9zgFS9d-mXIfu1uHH%=Yj$JaOQdEqa;v|HMjp$gaCodUI5 zFb1@Qw`Z0RRnAmzLe{xrN2fquC-F!xA(mGmvcLz<0^RJ}y%w=DMWp81DtkLudaP?o ztWNyZr&?07Y$7Gg9w(eThQ)v7P5v@Jd7n3VuHSXYoqWLUYVafrjGMK zU4QZckLyU2>&uYiV1?_;eAixN%V&mtps^>pG2}YtO+MsteZ|Zjz=}`vU3cMkQ^ze{ z*8xCtCx2d%yd?rBU=2-}Ba^=d;4t~RU(252(5gKUNZyz4Y68?I*Jn)~Kgv&Ta3}9? z>S#*$Bo}Y))@M1m704?T-LB{R-&YHTM9llW&b%Qh_9?u)K z$v5f+TL&?8c}?~vU-pi=oYOB|Rh9F=@KaGY5HCVG*LG_^a^SxO_^m#3{5F@QyQ^0{ ze(2|pB^BjM(i}^Qf{Uvh^XqM6k1n6Q!Zx-(IQi)E#R12n@s1@&1=QhXzI%kLK2CcV^T@%cy5}s>Ewn7DnN>MT-r>UvqAulvLd2Wg$OYC?-(fKH; zgy@A``M^aoL1*~-wo^4&${+928J^9?7yzKN% zN>~c1r^DxASHvQ{Akq0Mh0rLoV5acAJ)EK+Q^uQ8Xu;+dImS=4&A9v?P9bXxO|?|v84FSKwTtkLz|>9q%FUrs62x6ji?=u zPT*g@u-I{j7tvPb#z$I{C0ntxd|`#7j}Sv|iqJ3G=7{75n3{0>!hl`)pPG}|n45XH zAic3B>0n9bw%p_+;jVY}bZG|iLund!H)Pb7r04r951p#wksEBR`}S7rhAcL@AQ)eU zT%V`7_J`vaQHWYMvoId84KN5*Ay1b!jZ0qkCfk^o>$1yiW7pS>*<3eh8_M~{ZnBMa zTdE%3*rxj3v56`N&K0>GBe&U7(`IxgXJd=ac*h-8<@2*0i=B=|N0-k7Qy^DPBsx&8 zTkGYZ?(XvWp7Np%jzy3-fRP%%#96+;TT<}U+*bTkRR)>R&~;Y{y;N;U=7+hN`JytR z)duM@Fmi=0+gtm{*lS0$Vir0MP&|b)V`G`O{@FoQ+93h~V@j)PCd(ZS87WbY8{LX~;3|lgH)nvR?o%8`bsU<_u z?qxXwq*D33iGbi(Tg}vf99^-9@{M;TcE1EsG=MeY)N5oUZ8x5 zsI_E(cxS`%M1*6md~$rjF@O!5CCl*iwhk~LpD6#kp(=F9$f4N zSf(ru1&jQ_;q~S73Yq`TG{>D{#G2(;=yURgj~2A%`7SXy8J=a|CPMhKi5^FDA#>cW zul=rrFdX&qUsbriLC#EJA6pj8dDJ(m-q(GVE$1B@T%1V$1huEQ?hq+}^JS6ifH!%L z-*o_PD<1)_>nrhWZZ_hNO*P#rZ8G*;4GF$g& zODSG{l4He1*Q zH#R54+7F79&zf9w5M)av4~1PH)yIDdtO72J-6^|ycnkDuiePy9fX-{58n(i=kYw^k+Zt7rN*9@jTwwr8zy z$AvzqIC~eHo*b~Pu~fwsq@)~#dq!uVKEA2Rg?2GW5a9A=Uw2sUvfwCDHs)ntcf`>| z(l>f@-KZCG#{jD;o9uEN5Gn8$2s1tg1O&5?8IO!H>f`t3Cm#gw+{p-N4^<@}z)-BW z-j}_tZqypv*cWQDL%y-1K_oYU!u9bDMJBR9RAAV?b^}^+s9PC)0a-aJ1QLOg!LeZr z9>=T!o=C6BsRCH0j1A{zYrK9zV}0;K(Y^9AG2W6LbbCvOg+^4weJxpEB|85fkoJxC zKx$y9)7ode&9mD(QMlu~|Kww~-{I{r+p>Gp*}M4wVHg*%7v`qf%SIt8eF4$DCFf=E z8e$yG5s>`P>f@o94SttsaD84M4`nCgU?PfKbLz>dM!}wzZZnKP zOS}x0m1MsY90hrXM(ps7ZbZ&vRmVN9ru<}YcF2|kse@kcQAMR>6pT;34~hmc*c9IY zI{@+ayP6<2?&MGX$;EgKyAA+P{6McO#WkN9l8aMx`TrD;P=~7IQ2e=C$D92XUV3!Srbcl#@H z#rYy*cFMg&w1A|-xd8ESVfp+*TX8WwCZY((Vu8S8Pj%96__dmJQJe8$brK?4y1$+o zdm7R|s6z0XS#Ocy+@UnO3>Ixx9j7ayn(&g(5NQ2uj z`1}c7N^dOR zw7!W`z43psS0}DbC`)tN23HrBm1OlzS$&T4?XtW?{!Yvvc17COQ0KmhQ{0c%l;y6x zJ0X}huFl@8sjQ$@TwLfWD6~(lO<8a0HX+SAJf*MmO{DDdS9*r!4bClGk>J}DUt5;9 zGQKpaZ{lh%Ft1KRqC2t6-R`8j6UJ4=*Lq4S*4~5sbMq&-s|tJdOSEbFquoAzG`d7cR=d%cz3VY4dY3q85cJ^s8-6Rq7|uCe#Z z4-WGN6UtJnOVTH#bsLEW<_~jpDb23*dUtw(;Z>Dxz{xFa^w-wc*LX^tja9Bs{MMZl z@5py_x%<`M2NQe&N3Gqu@^Hw-vJ>3ak!dRuN>i^$c_ar&){e||)}k!i>$aYkRPVR; zycpzl)>b`&CU-{_1&??dYcic(>=&l=O|11!NGY3HS`V1HKxP>lZm)jC(^S&U*(LtM zO=StCJ@{jyt-#td(cT@IM^vUbyI3!*OgUat7E11$SmjCSQkI{xJfSvgMXkLwIj5k~ zQ(WsQNV)sA+K|KA4MlFNbtn2khhRaTr$d+cp(#%#*m^kMw&$g-m{JOt-$&sL^Qy2S4u$x4VA3okTtkT<5f~w;$Y${7A={~`| z$M0U5VDGV^1jNUQWqnhYxvOe`@KJYCU*|F;9t|P!XMy)}iS}WYZs!6| zPxtZ!YyZS9@z-pyjj8lVYTG|Yp2t18A+){5k(aT%2CYLaeeF+ra>GGSr5kmXgl!}; zT3V2A5nY}|k805-+i2K(-SFF%Y%0H_vOGIrL-%@QAedBu-bTG^2}1h{*CMZDiQloX zKK@XOV}8J~ME*Y~>{{T~(|;{fE>CuLxRJpSY52j+DDbokH8KPp`G_~V&>ocz_&Z;p3+qi(^lF;`vn(~s)<56N17*XLh) zj^BFazMH1z-1VA$b}&5J#({JpQf7-yQ$N^Nl``!Xw@eVFNn+82AB*^a+mz>pJ z`e7Q5!~HVPKEK=cX&->WKkp_>=qLR%R!!UR7>0fb$vHxVyT`%6xZ?c*i`YH*)3WNs z-#lO!0{=u!ycqM;CeP0r7Tu2_P9(T@3;$~i&yF7&zOpm0LW;e+_@wfK+QPf{T!!HY z{0sPPfsD5&rM*=(<}%cXEE4(c9XoU*2saYM2vqdq-JOztZ>s2h&-&^+nkx#tZ%OqF zwDQy3Tugpw7=_|RVrp2}K#s%r_3l<|YU;H46M~mXRX5 zd)Du&=4lM>NYV)Z_;)XVCEgCo>K8x_xM5|SOFZQhR+-UCP*aCHuv&#Y!{v@=jRXF? zcDYO3O*Zh7*=GHwL#CLpQrNvrvWMamHy&R7O@I&jg&H56|7x5aP0}wS|In>L%-k8E z1Z!OO!;W3WgrJ@%K~3>Yw0Xe52(lo>Sv}(aAmW8*xK$%s3Y)q8^*~ZBz?%R*dCohf zX(BYt>6I0G1!#NurNI$SjDki89!N^N=CN7+j?;vAptsN}vJk_^lfrJ$S1U^ZB;D_F zFIZuTLUKoTagdW`Dw5X^bo|l}BnN9q2GV46wvMh04h1AV=T-^x`ahTS!19O02&rZ5 z=OnN92y55`^I=CPN2GyL1_q2t&NK=VMUwH!%!wdlib}>sV~ag*zSk}`dE-)#1A+*# zl@}UC!TTs!G2SN9dTEK?M9Ja{v#z^U*aMN8Nt{us9}0Y3!mni%g9TyUv#vfFFzfW3 z7z+c;!y}iE^rIFiCogZJMDl_GE1;$!uim~Xl8L1^SX?3GiDyP$Y);jAE9_t*bVObn z6W7FL7q)=;<@v{bfLWns6dKHxx78%=26J?rQfz|S^wW+#VMsjrT2`Gqp_?mhBLu=H zEp@Bp&hUSfUbYF`;p{Ljcf91zr1UVN*{J3}xj&pd9TWv(y!7UeD<4(V0@Wp>SCq9t zb?={RlIl@VZ@z}=gWtGb3INqf5~k2my=3v`7V7g&PQ}(ZVKcOhcn&z3`B?~T8m|@B z+4Sx)f9JVE`1G6_p0hr8WVd9~#}}<4oBSp=&A+rZ>4GQ?`l3&#j_G1TxlruD2M>ri z4@j>UhPey$y#D8%?@kkz<}^sitSj%CVzY*H9A&n%9`Ag&!|sg;0^4%Yx!w{GQib;5 zmd@}un9hlmg(*1vqrJ)5(dZ58)WMHTI%_ohd12qY7632mvaSWd4s)iYMgzPryTpN> zmM&F7(C!_5wBkb;Owe1qD82uAg!e@=hjFf zbDn#vAD!UHMMf`qFU^)Lo*JAIkp59N=d8wV@5I?7*`&MtVE6?tX8`ixmg$7{@vY~d z9R=-^KedE*y{mgAOg%7EhM|kQPZbLO_#$Oyc8*>%@Zg!;Na#JMEDs>EO;p1j~QJFAW1P&$Y zP1Fp2^+!EB-;Q3OMQFT{Hs|)S|EzHdf1XVe2O$J&fAaIIJ5CYeZMT~9q6_Dj*aH2N z)Y@-!WzD>ZOL^I51(l1(613Ygu!QAXm$}sDANXv=fFj{Z zQe&IirG22_W^OL`_j1AB&Q$67%)AN3E$I36LkZoFfSxwB;qMLY)N!Z?%yfa{yd>Y> zeg2;Q+mLgi3FiA(?5;s`oaYLt#-J6TCuIc3j4sg=)@cMoymsTYZ^HHsYS&aRtRvrR47l zF6kOAmahyAx>_K)XGWMJ)LBLH!NZA{x2$^iEzb@Iog+4#=IHi+P8iq(5nAk44A+1Q zXU{4D%mg$bGqLchB5roOPSPBz}Ae#ZV20G)n2{%`)fO|g=y`k=In4XV`7u= z-)}O))q8Ep(HtM}^0E(GaQybCei}pAeskxHmV7K}8rqVNvwqg&hL(Jseqx(cABWYD zVVLXReeO8-aoWpk5#Wi z>Rvaw%X9DFIU0)jQi*D3J{Y4dkgGU2YVqW~f zHQm{8-Sz7|xcW7(hM>ZU$O+MbWq_%<`0-t@LWWJlp{TXY-( z@XJjNfA{w>U=BF`hf4;&pu-wRDLdF-mEA(Lx5s4_X< zw;esW%3UVIeq{XXAo$xomB?hkSL;8c;mV6sE%w&~4s?XYzSGT$dC_A3;mQ4I&LK4* zyuSOj`-&{);ie8x^?z<6czvCT*YmG@t`t?Lf!B7xli}0$zxMwjcx?*BU-@QDjG*X8 z&(>NI44VV&zuXe%nt_lkU6r51|8ZWCAGPCKNfL1xy!~a)i=BUoP<((%8Q1*c?Ly@I z4-=mKZ*I8cJP~@qWC}W*+@9%&fsor|N2wkV+srl&93vNv>w1~bxG{M~dqoNv1 z=!l8wyXtOb*nEx2yqvpuBfNQ+^G#G5{;QvbqKw8XzxZL!HiSl|`0d8OU*otC&i8CE z%_`f;_~f;RMl(Kn&aEoNrvGXzI6DgF_4~S>S1dfQn`F55L|z2Q6wDnBDa1ggjVFvmh-WIijWWUHR5?F`RF| zpT5G-NY|hha=5R<-=CYjIZBLgFMogZJ0Rw-Ra-saxgD?OM)y6hU;F-;K(yl6oN?D^<(HVh12*Fj?i_yN~H>!S&0g@oukO0^#E-gL{a@*$iFnoZEV3=?v0 z4UUbX{JW3da;KlewyN(d{rmaJ7oh*8D$$d7UedDvb@n?napwN)SwFpT5KP>YCbv83 z+A(o(yEmAu$Ld*6htaR;2qfuNWN&5f^}js-Fx;+5FM2+EESP~Vf4qw1H@95D*mZ|j zz?bo2l>5>zmosM{hiVLWEl4^GIj2D{WKL}u;aAnLNDojiAl%S6M9!Cg{o>FTzVDSS zznO=($fNl_(0oeGo2g$$)BM5n=QjF5^PQ!H#)}5w$ow%aMoz9Tp8KR5wtc6GL^~Gx z96y5hG3nZY=ev)muB~?~x;CKehk0w^(M;XRuXp`<{2t_-jhwu+@$JiB9a#}ZUtoN$ zN~IxNj81Lh4-XdCK-$d?`lCG;wrtL<&t9sG*0qaQ{^c^rTZSq{H@z9lA_XOEZIS>Ekt@IY-Z|nBdlx2$0G(B+DB=eC^FUa&pTkQ28R*!=@Dl_Gr za^ix4Bce$3?z-1n^p0!a9@7z{F;Gd#F|BK}eaT_ae7#A4@1Oj6IsvL3mIKe1cVHZ@ zh8)jq`rE+**tg4djnHDeeINd46>L$08tPo~SKI37Ue&wf?>gj#5x!m3ohg6(yiaq7 zPB%nfeY3hQE~-ylpU`_uP zCT>Z$#2qcPa`<6?lT?ihbeo{rnIkp+76V)_`qS38ut?j_t_x())Qr>pue4oj&x!x7 z8-z6tnr+|d$#QsF46psFz|%5J8nCRcDt#e>3KQ6;(&K;ag#|lcii+otPMlE!&%Pb| z-Xv{hO!eFyX`5;<5TW%Z>s|Ym$G`k0;LsJpwiUSt%gpyJOFL>EBtqj7Bp39Fs?H}p zo0i`Ui)WJm%#}4cMPFY{W}XO;W*L ze{5xLOP0MeG1039ma1lLd@*eey!^G@Od-=BVuIapZ_Ne7HcrL1_-5?YhfsZBqSzM= zX_W*!Km~hwpVtEE%fJYezdJZ8e$W?Sgh>>i`P1Nut0za3E9l10KV)Vzo_a*LWIU@1Wx#w)QahT3D&an6DA;m2_?=L<-EkzmZ)zo~zhv(XUEMWhO zP0IVb3Gu%O!@j9L<(X$+51fm5BM(kWyQq)zu8jXU1bw`(RQ0FDkK_1^bPnrjInnFK zVEc3abDkx(;}{x6-1P5W1Gb1du1!%!J|A5CGt20JO-y)90{}hZT-y%^2JdpBJJ4hV zx>C`8@uT=dz}9S(*6tfpPn%%suRZvKhibB0*o3{mPn@E7)veMXpL=+Ez~zVBo~xrO zClOv86ufB@Ot;=H!S4C>r8E4UnRAdTGqcWp)0-KBgkPz(KuGg-VrKkjevr!)$ldtE zp+$~SQC-^WKYPS75aRoIHU(Cj#lkekMZF8AK=WAhJ=OS5dY+`q2YTeh;avE#|N4(-Erg(ETuQkU{KlJ)L_18Lg z9Xc@LQP zE-wDbjE@c-`eM@4L$}v`I%M0WKe^Z*w%DiDW<4LbD{(f5P( zfBBnd%oXvk*L}9&;jL2>?ib>@G$CFBpNBdF>02ApXQW!juJC1-)vd~tyO$KH zY}4DUUqnKL~1aQ_U}!M|Y3!8-Ud+n60qsf+xMrG<`>TnX(M z8Q{(z?mW!GhO{_DUE?|MwK6 zWAD~hY!fPof3JdT>$Mg3qc+!NW5xUmY$>T5gi4Q>j(Nv6W>a0_YctD>CYH}H3N9>k zaOY%NIW|w8^VYS=pJBUBzH5I4x9c>xU0CZs)SZ>Fr-u8ucJ)l(Q`QloC;NW3YOFBKVv;%23uwtilDlof;M>SMR zYkKNwr+#{MC8~o*))y9FL%wSqb+T||4W2fwc_j)fU3EL=g956oh^4jeS?{p|xA&=d zHN$t%ns)L;Zxk{f#{`O#M%Gsr?5aeyOx_w+UAWsf0>uW0b)#B~ccFR{vqJqRYSohd zx(5lHv7fe6X$mSyP>b@VJ?3UqD4D62=8Ja7)0mU?n6u`LcTYJ{Se1I@MDJ7SsUM`L zHm9fJ1Uu6EE|4~(0H$EK4mCU@T4%h7)5l^_!**veimX;8?fVc_c(EwBMR6`wxtN=H z-L2aqu$JpcUHI;f4g9DzO(?uZ6+}MBsTDhBEKa}3=1jv>_xn<(A+|&T8d6zH z(~70S8e>xA(xJsthI`HW^#_W)V%EG-IYVu0J?W1xm;S-s_dv@1eP%$E7fq6WF@Nc; zi*C93_PM3brPtrO=+61}358P0{M+U)UUqxAbI#)V3#gmMrDeA)y19J*Qfpb+;`!3t z#c2N^-6nri%DG~Qd5BrcVy|ABbA=hth77%8$Q1(@FTU-jA&YOhrEJJe%Wf?%TYAgF zrSosSmE$rD{P8tXP37O$NW$&ID#Vko8*KH%GYszIt!BwOw8?AOXVeXoh8d%`w3>6P zhZZ_o#gw7(wca&Hj-2|-tiK#@`S{3dvs#Zd?*Cxiz~m3zTc@+v54OB^v}0UkCigV> z{!;8kuc)Ocg+nsujIvssA#MMNwx)^?QufGI20l9K4o0UJ=f3BNQL5>Q30Qqry~`be z;>18*Flci|*`}sE6vWk4qR1=kMymTnox)L6EIR4GD49pmU)z?H7MY-&KAuXQPuOEo zEY_<=KG^U#@^Fktz_LvstC65$l*zQ{Mq}CNd_R z635NO-vq&$jgPY2kwg75XCR&pcZ37bvp~5C4hVv`)1vvh&Kx)r zc=(sV!=r(R#{&=B0}tN~JUkhA_(9-dN8sVdm+bBXc^hXb8(wEl;Dy@@qkh7Z#g^=w zEL@Yms!=bP7h5uKq$k2w_9SHyd&kY5Bras{IN1~9eD;ooJrU+wxLsUJJ-;Meu=H}F zqT-sL_XRJ`z$tMkXbhYQQY$(CneaFVgJfuCn-pf)_1S@zz{A(N-LJ(KV>FNHh81GX zMfD;Qn}#m7r0{$jCpGDt;{q>avEPqizwav%#G}yz@jbVNOQd!4qj^3H?>QyF_VQ=Y z7|A7>98Wxsr|fm2s|3Lv=9KW4J(g*UUauQy3O-NFM#R?6v7ZeyRQML;H`Q1_>m?cV zn?4dSFG-nKBG21-3A8|ClB<>=be)^>y_A8%V7yPC8^u01p3yZqZ20Yd>%a^>CLwFt z@W(+YrYPDuAz5sw=X1O~@8Pj!McA^7xS>bIZB|q3P=nPxr_7}@R9N52U*;KoBfgad z^4cl9Qe!8BoZpIAWI2*`eZF!TJH0rXj)pu-l`;0JvDN48|Ut?{nKmKc{O!L&2_VXpNG!DdF)7}n^NIIchb zCJ2RW-Qjplv@i#~!f7h2SZ$#112^G>e$T5J?*ko)NKJ_j>n$ zm3_jhjSG^((xpuE(6*w>jDK$uHk*ZYs6(fZ#fuHuZcCKQu9FUPEj8|(7%bjlHlUpk zn;Y^h183J-#QBbS8teIVk$y|i@g7dfq_4%%*RC6AK<<(Jy^GlQnq_(ne9y?f=d?g> zVFS|z!4l>&4j1N%`kh9bxW<&y&Q&8Z7-RGAZwC(O0Rl=w1+ko0#Y;G_yIT!#LY%Ruh4&PZ+E^bEaz1fJCT@{Cv@jWvLc zwI^tvK~L&=R7PU4#&E`tNkmj}9-bFzmz%N6am1KLnK7e%Nmah=N>RwrM}Trn<O6zamB(#E+nj zkorG?K*H`-QIoI(xjc^}IZUHZ-W36kl0;XSQ&RV^>l}mV zHR#DNNjogIh!JeUNf+M2_bc_F@&}9!**;5n;E1=<5syPcW@)jWH(eoZKoMNz8mDe+ zq~7tc!#luyg}jF%_D$87Bv%-Jpv?t-Bu5j3p09IU+3+ntZJi(DY%F?j!aPTfQ}?bR%oS7E;t-OAIY(TM3SJsuUKw3; ziODsy!u?po5zJ0rFY%zd-Yzu5m{wkElZAJ6eqm0$8Gnf+OcYko@I=qo`E#A;Jv}_n zc+L9GbDkOcYGU@XyTtAeRavjByZy3C@Eu#zsLB9ExF|B<6%G^^rlD9d&XiP&(~!c# zykQo|+FV2BOntgxoH3@1vzj}8u`s6flIR7I`h2b`CORp{yoVQj4aV=r8Som(dZ#2K zN(n--pT~>RqE*^V*b=U>p|-@3s8grL=2_1>Gf6xp8c5B}D^=y?oc7_3WtC!$FT2no zYzVyE(C-!b-JUG`!nXH^mkMo|8TX zEqWtPzH79Zqo0eBYQ%8U;jk|hdE0RBzG0IbT2)x2s=~N%QiU`Yzi`N*Y~Pk#-Z0sn z^H*>33)25_@D}{`WY81E($q*9$Y!P$t#y#haVBWG zI3t(j&UxL3sSAxsrzd06U{1BhOJC^2M{s=EXjz3z>b%`h;gif_yeUIJb|ki`itpI| z2KLV7%hqiXnn^PqV0+d9!-w`PhqLjna+f#@f>+U6vn(YK!pi7Zec;ozStqUdNyi(E=woI?ZYb)+KVb>=f1IL_3IAa}tezzX!4l!J z$Ac&4H8+?dL_s&qusOZ9f7UqT>f68%M?Qbz5q(mGSfp><_iNYP$E)>!8#|-# z-6!s-bf<(la??2owE>drfRGNEzR|%c-`#!oiQ4|=4&D>p!Hai>&3)b-?|OTs#qfc# z0_$-~vfl)|Ks}egg21cj^h;k*&QgK(^M8+Z3{`^a-7QvOW4S+3O(N0)=dspG&q82iLzbsK$nMEZ^!U zTi^a{pO$RhD%lDb%Vb-8(6Mvaqz08np1v{((&KX?7yYvdWeY6O<;}fCZUs^E8Y`I^ zz3%5>9;dj(-Y}0BO5Dz9x7eB__+GSHV=P>KW2ue9Xn(^HWuH!IU}u@n&QhjlvZH04 zei546aW$io13p8Lm{4hY2lEWFbe2K~jL+A|7%H@kb+qx4VlCGAK7y8Y>~6Jwe=_=N`1n{H+>PzSuQ89!pJU%e#=d^&!Q2*DezwH47U82| zbIM*M`|175D!%)!*Bcr~p^J26-@Sb3E6h>wBw`nSt#J~Z633OY^~$tMI?#LVB=3UwjcP`PZX)!s^f={owI^!+e24N34ThLhyWU1d0Y^m>Eo3<-91!NtQ z0!PAUi*kA5O^2jT2rrA*dYoLL9poi>WzppNRu*+Wyv9VGkN~63YAtc#d_Tqciu$KA z#xzCSIEWd2f+*lXnD8xonrs8Rg%fy0Mh^0}wyB(YSY$32b5Z!o6Yem6 zjY;I7az4o4DeQaEXNlp>8$~Rw=wh*KRKBu5n=kA`p565pu9FA1 z=I9v{LI^*{FXL>p9j#Yva{Et2<~(l{ao!yJZaVjeM)EpFmoPaYW4g#$w|GY`UJrR9 zDow6uG$|=G*%E>#`Sa7H^(CWEuWZ-i@8c&|u7TnyI&o%_V|0f8Rg!}37UJ+3H)1Sly6g9L8-h~oZUjVl@Me`}a{?^W&5uYYR>=cZblE2jw>~CE&as@Z$ zR`a9=a&`s%<=oK8(;#0H;q1RI!M$vi}CD=qNqN%)QOajo$wox&bPvpBu^ zXNDb>1c!N_E0>=PH;FMn|0?Og8~{xTE?AtYl7Px90a1!k`keZj>WxT=j>QR&u* zN-p0zb@ERCK0exFKP0`!RT!o&@0YOsjT+x)qzNx>F?1#j9i=gh(p3#pm1u-r!>q1r z_9pHWRvi?i-0(e%3kRqwgWMg)hyQxN{)3+T^>MT=$LT9He!Z-}0ytX;zy1^1(x@+F zmc|a*_HWLYLDf?V&bP`2rm0#lqU6t}IhIrL?kf% zH~GsmmibBH+ym|b-`-iDw>FI1Yk4q3Zy@o~>Ta185m)}D$IYASxHhNGhFityK%5fC zJ&eBz!c~6Ti|OmQV7aIz!Gs{i-NSd3k1|yKCH=b zo*XtwT%XJ^RA}0^KJ>^MK|0q`6ak&PgLjF2gguLOX$I9vG~Z(P=tS-%QseOKiQs0P zN-%@07=8Vo(bv>q`f?{4wYI%NQW|4jMOA%@V?9#CT&+b4=gDu@^IM9@(avlm-Ow_O z?4q*5(_T2(7TD%YbuRwy^Ae_Z8{0pdWcRDxZ~Ew1U^QFl6WsG*@u7gq~F^Pqn+msjeU%x-C1 z{zP26DzpFGY8Ws*Su3-hI9C}8cj|p)y_0oUcA_L_5_iAiJRXSc(*_z)T;(|pL#+#A z7m5)}JYkkq(S~%1TUseA5{B8Hbl9F(J_KMCHLW&iw zPJ>v9C1q8^3@*fg)5+kh%tp*yAeSOM{KD)IH!a zSH~%5hHj|J6Nne(DBkXXVe`$=LkSD4zGRs19|+(49bE44t+Cj0nzTy5;a36EF9hLJG~#Y1jpu-X;| ztASg9Gc-l}ZUs)@6Jf=j)F_gCtDbLYkF_{Wr}U{}8|EYGirI?tH(i#!O(73(*i`ow8G;)>4VJjUF&-zrRc(MK_-q|&9$oRcOn zqCV+w5}33N9h3cvD&R9+d!)^$AWYXC5eDTd2-BHijYfC07Gc_DRo&NJ)_^@Q5B5OH z#G5(kq@E!72tRa;V`O^h8g`zOnH?1hX9_aQyL|$Wy%))|l|x~C7p(`H6hZI`Vs+(& zCOHKiy)ax@9E>Ktv_Ey*m3>;{vo>&s{_Y$mT8u?W+wK?fnu5A0Fn#xUr>|qTig)hN z;GMHreu4(?oEHl3{Cdj3(?|`la1e^MAG2Q#n6fj}9d-D)lDV8zlWF`j;HCSf>7@bl4>Cj$&l?w`20f>j;i+K#rq7>;UJ&?gx}}y9Z(pp%;%4g zI%&6){kx6rWVGvM`#O!$u76*<_?X@M0NoO^oO1#HE{x&&qX;LJ_LCQ|iZy73CyAgD z$s@f0PjVo6l9CTSNa0DkyWmrQI23Vu81SiB1dB=>kXsBDYO(D0{S|SwNvB);q@3go zmhZUJ!x&siKBep_Ofdr}4Ej>uO&WDEY-+tcsxJvbOQ98FLzwps{(Ftp*OD5)-7$dDM- zSwxluLmk!->d+h?O}N5Qv8WWtnWzS)9(}opxllFc<^PD^yNET zmObHK?`uEvD3H? zISuJ9Q7iA-?jK>uziXRrs#vXxytckU-sC+Qu@G`!wkJ+WSG~y${mKHM63cIVFJcAI zv#f0ugP3N2?%9Z`rPaUia+aZOCu@8NKpXTgoW9s$5jGdp*s_~#DoVHEm%L@$MTl%V zFBTlnr;8OMDnZGzZ2O;!|CCd-vRxk>tvq$o_sxp{k`BBmM=MFIuZLDw;y+)@@t?o^ zzZd@*$>dH~-5wH$3Rd4+So|kbiT^|Co%uFjsv-TzoZ}8@6=#}ka5;5h zQ3gk@)vywcsZFl9N?Qutz#)INE{Kd-%~fq}sJ#i8Vt_cE5{I-(@{9fXGir|DkID68 z%<=2}fm}ZfkFi7JGk? zBoH^2GTN@ZFxK23zGYr8)_jy<&Grjn&6V$j#F}$&^<&L-aeiOn;Z{Tcq8?z)$AC4r zdo%#EmjLDkr}ek^0p=Tm0A~KiZ2*{cecJc{!2E#*V15Kr!+~pX{BDgW7+}6ZM*#Cl zZ|pacDp~4+0p?@%0GK02iQ!XKFbx3n-Il026=41b_Jj;DBU2fuLtC%8R$*ctCly7J zyB-yA*PYV8LoEKs;A5Nqqwui`CMzZe`=p`$5ZNBk!omrmg1)M&5n5Ih{!3&6jZDC=i$zlcK#l7_EcW<1(dS*pcUSRxj0}zAmQ<66$RVe zDJ{Gb=5|!(Z_}~h_ibOclY7B%yp)_g4$HaTa&IME1($AgyZ;BWP9S`lhVW%&-*Yeu zq^7~1c}30P{c*OESVT6t9d#P}EJ#F?DA!Cx^Ahi%|>{F|gn3ruOD& zs=xL|&RZJi$NgArBQlV)qz$dBfQaB`Hfsv0D#HRw9D^4~ZM75tCZ8F1a;6aXKxfp4$mlIR*P^_NHDA+y>V*AiFr#*<}&V8b(QGopr zw?R|&fpV;);wH*33X~E(mA{l5T}tRsDJ!73i00zW0?tSYbb{q$9V_#&G9z~r<)D>l zD~Zf)I;xnG=lkz*9tYB}2%m*JiPDj1-(hfufs#lZNIk1?_h$JnZ2}!!Z5BRgO+$i& zb<6+od9;GB<&-kdH=H3|r8xc)BvZ&~+<12sxwXblu0{WEbsp=3&ZD{e^O*6!=R8Qx z{>PpNRa5!-qv|~8(f5B1f9(EI=W#KUud<)6!{d2W!^j^frTjxR@0IBgNM>`OespDi za}B?hLeRH3hPtPUK-rOq7D)slGHFW!(J67934ecz-aH+Hdcv-HGo7HgYd$jU6wv%y z{+*wypHGGQ9abPaYi}t1jHO{+ihfq=B!i5DpeG2ONvzt3mCjYNV#M@XmeuA)RT7E} z*}i#@8mkvr!K;nx08x%@R>y)QHmBxofvML(A*dYqv} zRrI-ty~>57kF^N%q5tHg10sVdq#~UQV>=~|>x;i#aiT8Ev-YgkQp5s7)mpwxIhnd7 z&Zbva&FM4AE-^oP1+QUOSodaUzvbdr%x6ZKH*{MKD{2ejM4@F21kG!>WCw(1kD{Y$rUbKTP^U zE~a;16crWM8gKZm%xgWY9cSBgyicHZ{QuM4&rEmb|2evoy%``+l zbe*O){2$PD7X4T8Kdfe2TW3%93;4r3HF^ym1>{{&hJaJJJpnRHQ0sOeO-LvBo!%nK z!kA#1=e_qMegC+j=4SE%dAW${=3rm=l__bpZy@c(SjQO#uJrg&J-p^u$fT&z{VJ(q zW`$MNpFQNK9I|R2dm+G2a(qU%&BEoiNezyuTyC;B+Im)4PU{Vl+>iwhCJPc3S)lsZ zpY)@QbG#Qr`{I;1E*5{gi_fsy&+WIEN*@?!^eo45%&2^M=3^6*b1d&7rtFCR$G9Z}7BzGC64Z71 z*+)-4QWyA=xbT5*?kvt)&2oiT6lA|OmW7w%a66gRysh%?bR>v9dDG(*X_|L^>_Z=- zm@TRFxo1}mHa5DHl8VS;2;4S)Z&5^C7UIO#qzzUzPMm+2R5>h9hNN!B-Z>>T2G=!C zrtF=9GLdMfntxHe34dW0o&>8nB6bc%AlmfmdL`}JwLi_1DF?ye{LL`q+%n zbQjP$%ITalCy)h@0}mkwsWq>Noo5^=^bEoA2nCM8nR{a4jO<#EIp!LqlLGGSr zy?^Su-e?S{)(NZpGTEBgX5iH7QsS2$(4|tm&-x>%om-TzvAzPTt*NEd#*(UwS%tCu z36;=8(6ql1z_uXPU>$&D(+X7o>kr>I`HOgXoz7f5R7rVgd#iAOevW6pQmI2g>_HdR zUT-%VT)ABOCcCL{M*~B4oJ=n*U}SpL&Wb!vk`fQYP*UUW2ASo8ZeS=IVTM)9U4CQF;UPzW#J*wQen0K%9~yl zkvtT2eG3SS^w;-##@ua>+U|+^epH=xa0h*VtLFPV==(wzzVD^>GJ9wvYWbrcn|Y3_ z8MN$6t`f66VV5>Hs`bL6?N<#^yTo2~QOUNg-t;UX9U>^JJ#2`QEV63ak?Pq>vIuXB z$A;bR8h)omi1=Lv?a3~rQfKqlUEXxmDP4x*j6$bS)Qht{$4T?>=MUcW+5HWe{6R84 z%T5p9Y7Z<$DtxxR*ZWe8x`}S%A8nUzsLZnu;joGIWicPhoA{@+dDhg!Y9MO-1gxpu z`jP0%L5qj@Y^3s0+laC!9*6#`x)cdKYRG;2fkE~Wr7e*tzGW__Ct}-sQT!u1$Q-U$ zrzIB09<#B;`rsvk+gD)cs&x=TN?(ckgqV9#6)AlNkyNiQO^oTRk3DNS?M+{SW{hJx zvF#{1dul>Sm=IU=w(c)Et6bhYI3zJV$XPR|o^{=J=ZO?V5VocvmLRA3x*kt0ukAnM zgJ}IygX=&Q2hn~oUF|AxFr88xSQT8}QRk|e6$>7ArLRB+udb5P9ZpHNx1=NMJ#$*W z{%VF0l8+{AZ=T2wX{qjk2rm5&!uS~SiL9v3*hP@L#D#Lp14SF_iXj$By?$OHm z*4L1-rIGXbcXgHX(Ou@O@msXLY?3iQ*5T-n6YFVdY+f>!6TqZcOv7(i1fK;H)hh}N zE7wQL)knyLnNw_V>8WYUxG-wk()a`nGuaZ*{V^x%WXC7I;&`^%P{K|LHY0vlgXPEq zT#@FxAfs6`Al9X+5M7!`9_OH`<8$Z*-lNkXxtJuu53dSp5|YNtL1T8 z?6A1twJ4^vtaeTW*NrNl)Og{YXFR!+$N4GF{@5k`6^eskoJJn`mSXCi*R~Z?ioPFIC z$iBvwuzd?zo507hTKv!87}MSju38-yWc!AE64Zr9*-=H$YIYLR`91q%~4~a+7(~Wu_*iF;}%sK?a&V>d?NoV<2cKt2&y@0|<6udEOa&^xs@ndK5 z{v<^8QdCspo2~l92+p>YlREXu2d}OWJt+VSWyI@l*WhX~>m}aRMx~xjfl^OV)NzE@ zU8^7MA^(y7IVFzE#$VOf(BZM45Ae7f9{9UlL#;KiU=)peV4hBO1y;?Sd61V~0c^Y! z1Nu{x(Cu@xT8FAy{OIKM=3W3i-fB9Lr;6Hp`zfL}r9WTcutzyiJ3cdkem#Q!RjXC?5)*_qf~WXHH9+S0mE-iuPbc{e*;~@e8AC+MV9-? z5}8Vb7k+v2(yM^IQKEW{dk2{-kFCjz3r_|vH=TlLiPINR5RLZXeDYCo2QXKhf|0@7 z6$G~y+0lJ~{6%p~I(%yc3uu;iy81Rt!nN2N8dvro<(x~R8=?!sDT8@DNb)ICxk0v^ z6@f{p1QK`M(jOIz88*Y(l`x47bJMweuI{(198c?Px$~Oa9g#4E$IOT=LL0A&n7;9^ z!e7e2dxp0FcH7E6Du%Y1OK&sQ6J#j@=_8k&5^*T*3Aa9^W`% zYNS}SvhkU9HVjr1(|3Roe&Guf4HKP5Tqb7Dw9+U$*DZcns@0@TG^lA4VJvNe+2%vW z?3!6IS25J5jJe{8XiI6YHE-*2S+3G5Ns{ymZ{-B>RvGq9|wBN&_yo(US_P_ zHrAZrL=2EzI?()YG zjxW!&j){#BSd$kyy#K)tA(1b>CYC>gD4IXsaAzJrc8u-7I(uhEhQ4MJl+*UcyK*Zj z(D4g$7Q(4cyzFnz^?Lf%)IoUNN3Y8k>=)Be()InQS&sTBz?l$^5JI8MFJvPnq=X)fu&z%o1keyR(n4b?LIU67m@s8Fgc&z%e?)w)IT3cdyr zki|-3B8$LxN(-~&DQgb-_3WK4{IquVIZ7*O51H7sGgaywbXFR)0cAsh5m`hQ2gmqv7LE(*38!T0GS6orn|9L-PldDF;@O z6tydC1Q3#oL~iOB#U7Gn&v|0r@Pk2bH`V%OgUpXj zD)Oe79bNhvvto;Q!|_^!PcPS)+4Y(lv$4bF8Z#ODhHMxmw+)e*gl)E#6MdT801rI zu8=Z=2@r5dNSQ(Qd3pk5#@&)eX2|&g=j8)M(04K3zz6R!K0x0#*n~!&?>uXfZnfyC z{fsQ&sQ)URVQ4#9p<2;ki}!akTKxzaPiN;H!8hgacE`^-w31nm`OjaARh-L9@(Ie} z1k{C@DE#4}lDGh^oYzK6XL9-+E-K;-Fr}#*bPBcEHb<=(N(EX|A)VeYTu)Ld zU`MF7deynKtyqE~Bwi4Eu4#M>84a=n@p*=-n}3FJD?*K15OQxmmt~{uqX(%?m1mVt zv@&=`#po4EmRJ+C2)0!=)>yVr4yBP2At1Sf?>B8&57SQ#g#6dwLhN9P zMw)K7O~!}OK6434Q$r&&($IfjZd-)Qvn9Q^d5JHhx5XbJ>9}{QuphS&L@R!Qs#inM zR<~!)KN@`IKSl4g4G7rEKSl4oAFTJvzwSow{gk=Z|6iW#53!#fBVIa>{nXV5y+Ay| zgEA<;a83D*FhY&tMTM6?vDGfba=Y%E0lsoM5|*BmOu#nM#4chii>CFIbrEQIG8iYY**+xqdt1qtJGQyl(P| zamLB_<8K#PqS}rIl5UL*_;XM-uWC^!e&bOCzf`{OMzte^f^%%|p4uli>Og@*4QT>UC2IJ zdLH{kT~m-2(L8}+6T&`ugdECX`(#@e`=nxBxAsX7ezT|c$-48|Cn0{PTV3T_6RfL# zkbSZ(V4rC137NONB%Z-r-P4r{7gbT;*k%tY?R3NkbS^s|NQy9qgA2!_SyQQ@Keu$P zNEromr;%VWx=^HaIAC6sLH+nW{e|&X9HHvz|L?OWe#jYF?+-qsA18ZdKiZM;^Vky~ zb+ISD`4`v|mjBChrMthn$3H@Z@48}J%vlHgI93zWiwKJ8r3A(FGPN;1x4Bp9`D1zx z7Sl^dzubciv~fy^8UHVz^Oz8Gp7o>W+$_h)y5v{}`^{b6B$6K0$AYczWUouSUyZr0csy^~;kTNGPFAqsEj#44({efspLLpH!B<(hg3%=# z$H7QCYhQvCE|;kHJFo5WKy1DTzF~>lG3_GqpJ$Q(tORv`Te!?|dfC@y)m9TstrZ0` z(8Eu`bIe7Q=8Zy~?gDUq5Jt$~7c6`Fo%igt7@_~pMFn&+r;Hz@fM$qd+R|NW zimK)2m0=@lIM1Kb3}X?yq{c*UD?w#XN*Vltx0>F~Q~RcLe6qzYsaY=antqBbZ0i@0 zh3C)D(d<;fqGR4~80oH}GO`a3zEfD1!s5d5Wai#z9&U4`d|zOUOocuJaK;Ld>lG|i z)|Ua_HW)|$nf5Wa1}T8?h9|}D_fctOeXF)R;8t`8bV_QCRKQ+lbL1%&8)AMGDq3#{ zn$@%=X6wEO}@MHR#yeooAkZMIAv#w z+?I)SfX8c0;o-^8DRYKy`nV+o9C1QF+?r?R8j@U zg$f9oS;if}DA-_a6hqvDpEoV-8^9vI;J_GmyLe#41?la#Q9%?sJVi>(ge`!N8aAH%Va`b*w@A>-Xj0&a*J7 z3K?0t49%??PN~Or06D}?X)8k40T3UPJYn@GQ-6B>Ne}5u)vrCGui`FyPxE~p2Ny)W zLHK8vcfKOG9<(@)LI*|cMjISo2XVqt9nMwMX|gbtFuC6 zP=#G8`SP$eI@@<-MU70N3?$^k-AMjn4)>Ezd zZdVMsZlIwnEh$3woY*PwFSyquKpE{XtrE*C%_-BN%-maO53jr1;3E@CBNMysp~i;N zO4~43=jW)yLJt#!WENOBNI0jY%GDl^QQqkvgXd2v^N_Jc*~U^Zi3+^sU$2_;f*LZ= zlU!d$g%h$;cGnnCb}H$q>=aidQ8#(lA0nW+CAQ*#GtyK3GFLJqLi=2 zAHxff^N8;skNbAnHEh*G!cMJ;knib|<-d~i6Szaz9}}WEvE;GECs_i$Qwqd`RAO`^ zHVgWbFN}WlBftEY@tnXuYR+5LDaUYOjB1lCr!Ug@J;NM%h{6U1HH~I5?WE(PYW}4qnqrxoDHbm6>;E67{49jo8J(!$SrH0)Sk?V8TlY2V= zG+8YDHz&_e#~03?%YGyF|l4gEJ#jt&_j%s*)GSg57;hRJF!d7g}RT&M#ym+ zaIt)*vQ6^S^P7C7f&8X;j7>5AkT!22%l)&kfC)RuLMlW?!VTiB*jv#2Jl5$5GY;eB z_(_m08$$kZ@cEq*#|>e>E7Oq{e@Y&wJj_%E67$DZQA7ArKhAV(0&X8{TGw_uBlkOGtv^Q!`*)r`Z%Bf?um7v&%Yi2iLLkTpslYrr>z$|KNM%bRz8RJ_u7@kTk(heb4na{F&?RLiwnyY zmP_bA*z%nu@O+i z{=HR6lN%nk1iPmcT^|*ioSxGenK)~OBHNSCjiJl3P3r^noDM;HJ)T3ZfkOddt8EcQ zCn@=t=_3%tD$hC;5;z3VB`;R@GaFR{}l(^$ZFEoECkh8WXRWOxb>8` zXLk`j4Nq1vHCb;5>^tbsZDdEN=1?G87+uc}ZZ5Pd?bqPPio7(tKUI{0mFnp`biP`< zj?TBsc+l_yF#~LpBFZJf0La|a*uykVf*|)a*XE?0hgL9VctAlf&Jda{js95JkMjv+ zvb7`!d(ba68b4OqIN0Im!~%X3Di$z8js>VBY&~XhKd@NF5xbsE-N5tF zDJVsjHiWe)(WIM)3E|N4ia)9ykM{W-A0pTXFaJ>EKbL=KHkAPf0{z>_3U^9BAm6vA zc{*Ej)O_D=@^nDgKR%x(4CX2X+0UL&(|CP|aerbyP5Zw%pXLU$Cg1nx&Zm*_vh$sl zKi{{9xh=}kb;-Te+7m?2a(>bU~coU z_yViJ;I9jgul`6GMi#=|p56f&))nVFhyMezE4aWFam=3PcF4MQEbGbFjf^RRBdFF| zC?Htb>toyAEVPJ#KB#Xrnk|cD9jzy%e%9UM?1Dst4W;3?qV;mWuyp!luGG{9C_ek07_5wOKv)>%eb4~5+Sp5aq};IRLyZK;*Nif%*? zQY}`*bR(Kd)9;s+#*u<7Fs?+h;~KP$1GwpF8RyZGEi%%KSpw^J8IT^r=@%f53^ zPi?0AgKgcfsqKe~bE`IPqnZMVc0Gf9H1oj8*&bS)>iaZ;OoKmkrHcx&JUFz>P_!0 z4P~7jg~qg&S41gazAE|#tqpibXd5s%R(viN%6j@x>%&^>wd`P>63407v4X|0F@o2p zn2xg+#i_Juz?%Gff>UxUIAl7pVDAo~0`h+JjE`81g2fp&j}6dkcfY~vV^e1O@#!Am zuR-;(J@ZB6Yq+kP8Nkl8_GLG+N}B;w{-wqqI;HG~79S0a5u6Kg04+|ckOHQ&p5)cG z4iSy!c(tv=99CcLPTf?!1eTh-&t1p8K<>F3ObF_Q^LmJ0I9eGLn6Jiu4c1DXXXG_2 z9ZfG896+z-d3Mc3mvxG=eob8yNJ@>3!I99^Y2Q1RvMz$^w13EX9>4NOpQpMu#Fzr{ zp&tG=!(ktr68LRx&bkg~hN8k_7N$D)2D)ufpDOz77aE3}hqk1_Vj`?ky3yi}0 z2qph!f107GSn%R}QL_#6C!4~$PXnkl%jg#S*H}-zb`08 ze)Ug}k;^q3A!7jvYCMi*p(wpyz3H2~twArre9!UI{5_RI?2+o|Zb22vWq^Y zHL&)A?D8+Yxsz>5)>{5uGakDn!{3icD2(HBsD@gafLOI90=*b2&0m9O;tnJ3`-Rzl z4;+wDw~+>Xl|ucet-lo34O1aK*OXybANB(q`D><6-CU6Su-uvvcV=g^evC+js)Y;XH~TFMPk@~nVB*toJS zfH=5<(<_Mxf{sum)s8gBZO6~E&v$2Iuw1fDll<}2knuw$Ceu~-$~sMZ$mRrDr!Um% z^qrw}dTZ5B*6F?r^|w2}?vZu-5-s_H23?}%<(LX*W|(Y{_(`hfQFq>&ds6j+nsw_V z190i-_{mHxSf}K}oQblo!e>o+@C-dsUr`i;5}96(H8g=(z8{sjb{LB= zc79twDi;388@hx~Nj4)7>5amS3E;q&W5xF)5-BKNi1ArsQSZwRvX2B>+PqpmP^L9m z*PO5KT`%d(7>iUp)63#f&kB>E6Yq2HDfQ;Hp2t+RMB1gXoF?#+Ek%B1|4lJ>8@0a;go)Gj#K9+A$*cC80$ybKr&8S z5`rde3$^`ocCPxt{d3-KQv2te3Up+l7NC6AQ6bPj=P9{=&gS#=&#Abw2mNz~o_7xv zjIfK9Clg4p@@ISr6Fkt923NCo)03wB%dS0XuIm@7CyizYoi{#j?zf$eSe_c8`iS&aS;(7R0NurO;S`vZEpTrToKr1hbI&pgTRDL#SrT z7(Z6flYS|(oe2aaM8A}`yY@>-xc376QbNw3^-HXwgc|oJ#z_SMMa}o! z?s?{lnJd!``L{`H=WeV_wTN*wlltPW2X_qjw&S>0ijcTAM=oy2G)CQyRzbV}HozhC zZ5H^qn|oZK*O}G+eLlCQA&H7w?+%!=S!=B`>R0$ z_jr7y5uK#*5jixl8z0GiqmVTtD2jtg_iSM#7T1BD_5>inniEw|WBm7Y*R{63k zEmQ}@x|qK_hdNUB7g||^8(5{RSMG-ce7S}xE@^0@!`r(E-$qxJ??+{O=zcx7g;Nmb zC0c~pn^0yka)7nbw|3%I{9*r`630D;N9uh7s%hd*G1V!+$CY3Pwr4q&|CU$mFZ7c!Cgw1tADN-V=^J$z#nkTgsY57xvXG?4p~G zrjmQ&up~?~PbzEE<5MN`xn5|oc?Sh4%Jc}vQ(L44zGc=cB6<#ko~9BJ&6+|kg$bD) zb2uqc589P*pV&T&iAN{m&M(?ta#NFh<@c-O+NdvQr28_Q$s%BG*>3A1PHM*2RK&)0 zrPSt2K|A7|LaRd3M_=>6C7~)!zti*CRr=mX@l1zA^818gwbU9iQ_1x`iO z-=6rmb~mn6i;_mMq9oR#>Z6IdX!9*diQ#Ay-9NB6;;r-H3@@>DPhxl!eU8~*{6m(w zHC$rY`th_CA>&LkD5?Teis$^XyfdeQSX92pXou1!Wrv-zS<&sXjrF)q0KND*=bt$; z<#OO0Ykc_`hhr=DtNe_sG?`?ySwM><$%u-aNw=dHEqcJdPSBFS#m!uH%>=dmO0M9O zTn*v#EC3mLQ?bs)$t_`!%x~(R)bC_NdUv=l#g9$bCojEGEMl&9nTu+8`fiVyBg`&v zxl>LxGN0RW18$2bsNqx3VuYnj&G;~7a3J@nHQqn-IdiB$`kL5MZ^WG#-ybPG%ZJbE zE1-hph?QN1m--zYs%bF5f%fn3O~tif)C2k?-rNw8Y`YUtI#(kOomCFL5}E2}qY;ls zREgM-h;0%sh-hKG7i}rm%|2~K$U1F%7%qyC4oSGduz1Px<-Q2(JF(vOsNZ8I^Spci z)Yp=J@f-wPaWl-J%u`%N&5~1m>87x_@-t{WY3Vh)z-znoaHAsI9F76Dwl{SHkAxMM zpYcZhml*GF-Mf*Bp`LX0N4d-o-<9>4}Sf zlzL|~7y0O4UTn{Vn^%5Pt^T@za`#SBIy zTIGxNC6ISQXJ;ih@VGRo$)6at{MPJfKknXs|CZd6 zcVbt)hm*uLM}J?HxmxK~>EIysm$kO11~`-Rq>6iT^(@?b;VyS+*v2)LPVV4wtum1>@;w=?k8DzP{j3j%WxI)}Ls% zY?b?h*EBu%AoT@beNJES+7HaV#G*BoeeUgPU+{T9rZ0GZwudq8nzV5ce1c2PZyqhC(94rn;#gf@BBUl zE|#aE6_wv6Gu%TpzlzlKHjU+8gV7k9a+E{>bs$4!H^{JxHMY8Mh^=ToK>{ct;t+%;` z*m2v>^EdfhV4QM(lCQMDa7vJ?nso*+2W?Cq+7$);eqk=@-;3`&V@VtvxX;YIR)#_; z)=Xo{I7^rKj+z5i$k2MEb)jNYy_}(QvTR3*6xES)k9-A2K90(Q<=*hMI%J>F*g;s& zd1`$uTqxf%`0TXD(SBB-OC*~pTl$;Wb= zw(Y|b#YM!~t5qwmjrVPaN=Q`Z=2a5OjM^}=NfzpPnh*f`%%?*UxGH9bxwGkeE$GC`Ur7b%&y5><({tfX$u*b% zEye`m#?vsT1VN6?>hPH4IO0Rh3hv?q$b6}Im9m$u1n-3*vSiml@c}OZ{oUcbeNMR# zblj1Y$~=jfQea`BGJ0rOM-H?#g|2ne)kPpzq90x7TXqz3k?*@u|Kw^!ZtsQ?8gbUp{qU>Hc(}(ht6CrLxR{VK(8!{>xSW#Sx zk?NfG*tjZ`?t!TlzpM2%a;Bu${r6{BZjXKKaT({>!f*~xnAGgYIUIhR98hcXB86)pA|XDG#!x5kmA^uRI52}ig3!Y3Y*eFR`1t$mCqMf8f`A#tXw2JO^v zoeGu=AM$p#FGoR6#t+#Tua~Oesuc!qWTG2=I0YLS>LCn^{y8O%%fR2B#!`av88#P) z0dccGZFNB0s3{tj;rgwhC&fHHmbmHx#2K=CvGy|~@3N$X% zJXFFML32`Uo}W3#;xaD@a{O~jYVR?bk5%0hoKG4&zYE9FH&MaLrJapVV1(n$lO#AG zEVJ;l#+*40`OUMocwGB478;if7~a%b@jz?5s5(P0dV%}x6jth+F-KyMYxTi-q(4EP zUi#zY>9bmSx`WA6rSD4*>t{9>2!iZsCBn}JLKBa5IVFz6|IS0NGK&X8b~2&Bs}6K- zMnR}tIVTP;EHi1^S4qPQT`8dK2|#5b_x7h5SE_je2go#esl8W`m(8OGwk*~_`0BZ~ ztZeJ~bqe!*LhGdv`>5!p(7Enf+CxmsY+R(?J$lNSwR)7&01gy(DV*zu0~HV&yNIH$ z>9wcE?IsA&QP#`m#U48)j??xBBf9FQckB)1%tBUbavJ;y;EVT*rf{^8r3NcX&CZep zLyJ}Od}%^r}s&GqxMfQNc>MM28G2V(tpl;_C_ z2HB3XER5azg?fjvKcVEvqjm1b)Ep(pEu#L;zy%#6F7Ei~lXT^pvMhG$n*IzLx12Ed)~=0&PkGpjdVbPjN)EmXt&ZGGn5u}NLM<(8QDm*C!I3CZOE zgmCY&Vn6IpOWwCt?sDhjVO@L|HX?;W!e|WGb|z4B`R~83#=KDLTJq!1E9B)eYG3v* zFHNr=fBrOxml*|jsd^*-?MFd zb^Z2do?Z3f3k)-Q`>n#dMZhEV8&h)39Bh3%*PD_6e(&P$ynFYkbz$5`pQY<6uLdZt z@`3KpJ9eA7%Kd5CIih@N_FAP^)_SNkS0_QE1CPFTTOvXAntadZDOczh*!8nVmoF?M z2=w~cYm@7Eio!>KczKzqyHkQ&V*`kSpRnSqIUgFxLG6VMHaT$Tu^0A zY4(4ABYi*o9z)Ikv{rA-$C~fUcP@k#Z{w38)s~4O_omNZZ|RraW3+jaOyj_s)@JcC z@Q~Fz`342oJ6*!)LCzJ^;c+mgqdho(KS}mt`Ii^$Cy6BJv9iYU_d)E%A9!2aPx90j zwV&kq`XF}kdt`L3d%s7?mrZ9iSq)JEzek(PeePO=^fh~e*u*(?rC*1rE8RS%`?}Ju zHP~!_dGaAc>@B5QukWshW5&IqdN_KsHz4$*>EZbHxjh_HHD|2aiCz4>Z-{+CVTD$7 zNJO~QIu%c>N#vVVe5OZ!wL?#L-IcuvzHn~h`H~0WruB0-MC8YrUb$HA>+jOui?mF+ z7fH?o4*1Vlf7dum$O3=$`oJnywKx@Rsr4!xUUnvMr|Dqpm{{9T?Ja<}q~N7+NxIJ_IiBV-Nr+t-4h;mB7{aCs=SDB~}$ocj~kQ3Bq zvdB%2xiy7~66j_Dl$FPMDQ8}imO>K>i)fw3dsDFFRmma;;7z&TcVRz-H%?G!xc6W< zlX6-D%Pdw0ITmX z3l;-T*K`6FRQoX@uJaRnZnUZ$o+Ih5d#pHYTJF5v>8@tS!|7RJb8_b;&%^!VafV?X zk(u5IO9wCBiPxU!ONd2TZS{N+DInV9ab&4Ga;*lWFkTW!9ES7)OP)vm)n#-o&zi<@#j3x?Q zI3N&(me;B6iFIDSRLDwOMGf)FT3#ha5seio?M(m)Gq**#Z)y(R=YW2-Tz?b`SMJD$ zL4und9)x%^T&kkNJnF}4_|J)1w@3-&b10i4LF7howrHYM!n)hvTrDofIndb@<8Oi> z=Pv2+nCYp|c>&7#1YuVST;-p;hmdF`BY-Yw7)K(NL7FxEM~2?n5N6cD&zk1yMb)bm z+L!cCU4>J^)vc6^)8XEoRY{pPvkS}zDO*5AM9bqLR~TGXY@YG?R0@T9=o(nc{$Lu+ z!X1)6;VNp(F5eK#Rbyi9S0IS4W+`oYVftV#gaxZ#j>#?wKv*9F!s4w-F8jrZr8>IA zQhVTQQ!bPL4*HL*^+W<}j}2^(yU07ttZPlC%%lDu#|aeU!gxT6#d7>w1b#yf0`U{ef#&8+awsfTcFZ>3=`UkG z$-lfDZIW21e1X*addmV%ny2S%^XU_^vexC-_#_mIuMDI84^2lvF+%sQCgbafH6?II zNtf7t*IH>in;AN$qre)7cQm_!kbYO|qYyJ`Y)F5eqntlz94^eGl6dME%7jYUy)nR* zD8%G@qEEA$%J_Q`;lWp}nwZ>*Tm4-QmIDG;c=G6O36$X_)3@-sHkiI~+sRDwnvozF zhdd5Vj$;=d(bST+7VTomvq1gyB-0CFViOs$VxJZ5gD@rf?xVG< zd)|{fhnW!cf##4>1(spo?^^FE*EX|od{^yXLE66v#bU7HbgnwJ3qmIAfGvSt<2|fb9Q6@VR4OseA7@its@lw3svv}H^!b88kQP_YiRFMDb z_po_x3wM~FFk2pf*EZ95lE{rEy(f1k9>TapeiETn6j?6v8{c~;k-EPFH`)+WxyO+~ zTpuA=lE`o^JBpL-=)V=6M-nZ*?oD*FF6vvla>ic7xBtg-#FV&L=rw}o9- zG5B=(YUzcvbC2OuDYip_pz+fxvMLNKQgfPs?8PoFAv&%>$4T3+6Ny= zc&%de-t4ni9jUNv9%4HDU4`v=b6#drWzn}eGdhP*K# zH~*@#)fy5WGeWkBDmo8Gsb9gtU5 z<|)h?UR+iFyQ1X_Cs*BdvgpWvU0HR{nz3i!nqB4MJI9(#QfXBM|H!yJ6L(`(75{Sa z43lGR)k^-m;^ijuBULs0)(N$y_D8E$^M@zwHc8J^t>GV@bi`zNv1%=UXwq4eqovBj zZz?e*we7EZfPZIlUXtf{)dv0_lV>D(JE|V$AD*&2iTk*!mft?5Hp%=+)keN)%I+k` zr&XKyjwwfyJl|A3#_zoHY?9^sDlcDSHzl`4R&V8>vgajxdsWx*`|LB4%`w$a@@MSJ zliN+z+xX2>Ym>{8tDoWjG6Gg2{GX>DNf!H7|DInp?QC+}!0LMb=rmIw&t=um z@ef{=*T<4yy^DYDsu_J6Mpi%1@40GupVOnOck`a<+mM1#{onk~=`DR4N~&Mtk5BLD zQ+8$b%lzuA4JoH*R{Qv;ug*&0uB~q3U%9$C#WB136~5!@(iC%P^{f0Nzovv+u1w!Ec(CmFC@7{T|;ut2oW_diDGK$yudo?fa`gv#7({m<&}_>GPueLYbtU185VOj(?9WkuM2M_!gU zdSzAENym&VN9@X#VKvU>S(c=gHDQlCYqQ#tSFR4*=iHqormb8P_L=iYmaFf|wP8=p zIh$o3ywVfaFvm2|e(C?A?tS31I;#Btn}CYDsIj6(MU5+J+7g;8it}UM|-#>Wisx$A|a-jUjgOyjGc>k8qly84U>(9NKcI{N6ivT|N4REng@iZ~PhEZHOIyBN ze(ugoSGRm+%aQWerz+>xerwCu${$EI&b{#MEl10LkXku+^1EC9vHVY|iMgYvwtTDn z9Yg!(IzQd=?egyo9h=+ouUo!T{^y}{bGy!O`Cj>thiBBBd1%Y`%YQghU31{CTYgaf z=aKfBiWysfSiWs^pyteDxBj^N;OMrR3p2O=r2J>2`)aD5w)LmwXI^=%=FGFU{;d4c zE6>%OsNVW-mtftq+#}*I4zu6W4D2_wuiex6j+ty!995zaAf$SNr0vzbd~c zy=~rsj;+5g|A+Lxc}F_8{0|RMZr}R5@-MDGH*d6O>qF(Q+c4uAXK?H9%a3lT zzNY0hTmM-8o{9Ers_xnPpXCp39Jr?CfvtZo|KQ!*uBrXh*8eJh_pA0@v-%5L|5E<< zuR3r~JWJoxA45m$&|J`Cnf(qjB=vTVFTrJDaK-TfV#X4b%Q(Q+uQH)2(lu z_O(|JG}iuX>(*(%c=fi%9lzcBrfK)Sc3-3Om#z0s`|)d!HMY#CoP6`N`zOoJHIDvG z<>XtZeQvUB#^N21tDJn>v~!bX)r)sLp>p!RX>WgBS^MHMPp_PO$FwiJu54iOVaUQ;>wz_f$g%JyAbyS;Mqq7W{KT~1Z!0@@?dYz`$xltY?@eVh zUReA7%E?bpd&50t)i11gpmOrF)4p&|S^Eo}PgG9+{j@XplnuPF_EVK~+3H`C!B-yx z?klG4tok^VY5f1ydmKl;P1MPM8CR|3zx)&Y9E8a$)uZz}!pZw<({?+~M(`P$?D6GM z!ppx+gpv1;mr5TA%7--M;@wX{e<9E7p*=-<5~}0#)fXX--Z4dp5Fvr z^m6Gxgf97iEA$#e7rRY+zvJA>^I_vI4?m+)_d{&MI|(0i=# z{|;T`z6-jB*n{ z0f!e(9OpaGUuNhMegkv~_k4^Xgt^Z-@ zw*I$47k~Z@bl0E7o}Y%^2z}|}9cK~f=J(go-^lab&^NpED#scAm=6EfjPM_UF8N)6 z6WaN$`MA#S7opqse>rrK`&;Oe4>!L*hc13IToKe?ZtswGZ#VRpn()$I^+8|4^Y5Uy z7`n74d!avu=b9%5+pCX5e>C*@&>v;OOS}4M6TUGc{2?R%MbKMJc#;2K(B=6x(4`)` zc3b=jP5)8`eIxYi2zME}=|`*GIU5z6TgU}=+d5iA9@qd%b-jD=7xXVr*wYa1l?}dE&J_oA_(8c~=f^OS+JM;z6{|cS#X!{WUtI$P$J&mU=|Iwe( z{l^CAx0vwKuC9Wf;`!Uq@3iC`gD&9~(inHU;qlvNe%9Clx^0I+=;MU@A@ngz&S~hk z@q7b=hg%I@{BZt3$LZktz0ljO@SC8o{8RxA8Rpx?rC*=(yEk^dLyJE7kK{XP?3(tYb8-L9R3 z{(4K!6`$Ar&MThj_dBBh+o4N(zlJX9h(1C;2mN-QU-T@$-4^XQ)4_fpaze?TZ`OVLBoa-(6+n^8e`~&FHekeW3*I%KFKfU<* z_=ZbI=G(sJID$*MpFm$&)k^KG(bg}zWs~K~+a-jDg zb)0wdya)RI7X2^KU&-^rD;;OlqNl#0`~8nV-(u1K2>s(c&%MfVK4#J1_m8^%{0O>R zAEn;?!O*X~I!M3%n~VjBzX`hJ&n?G;&>!IW*U>{hQD?@cd`!>n(coKjF(fr|6{UIy^lef{v-3XVQsL zL>~P!-`0NnZ_sVOz2dmm`$e^k#jWtKhQ5vGebBdB^hbS1*NbcFta?E?-UfXa;U=L= zdAfFc2>Km7KegU(_rxD={I0goE1}!=`7CrP_n$zw%YDxGwBK~h*Lu0~KLP!zg#SMD zr^H+w@WB^87aF(teA43I7d4|3wD<>EGA>y?BAv z*VSh`bZM7923^`Ym;M{*(yl%RnJ=^Gt^ch3U^Vo1i~eTl5`Q0byB+uq^g-y;7g=^A z%!(f{c7~pUzR}2$_U${+SMdA`=*taV>|b?K%U{&!%a`!4hW;AD-3R^ER`|a_7k{c) z%vi;uf9Z#`B|Lv0I;wg3-SH#sXCJy&+rzcPL(s^K6x;~FXf1@Sm9Q2p+ zd!E?Q=8+gwdnhxKcD9hLVun`|E{5bGlPB+x}4*1zld<`o#B)}r4GUE1;Mpm$jGcR^3`d@uBVi~d*W zLp(nOUG#PJx$~@E2fPDgQ^h>`hoRH-J3FC^zHay*K%c|&SD`=GrBm*IhF;0@S?EtS z^cf9|V}7B-|0N^*eCU$jXQ8~E-#ek-3jG%7w-`AxKL09o(f?!6UH!%1=KYeo0sSc$ zL;Tm(^Pixv;rY|hdoB9hUunBsL1)-!(RV->`Rk$E^8W|=YUmF_cl$L-x96Pp+m=q> zZ-xF{=u#dBpp#`!pQrv>+w&51+n#OEuOi&@Ug9`cTH!~aSMj_I`m+sP?DFJ9O0W()}g$B+p-i-fz)w`i;)-^OyPgm3I3ObP4x%=(c@k|JHFPp#LZIyIeUW zv=jO!o>Nq=R~fqaf5my7|6f7JLYHSQjR}k!-w$2d?KJc;H$3vM_?=Gom(cBW z?}0Am(L>|?auZ(C{WWxv^F`>koK^p!^HoEmYUk?+^bZk!2lNjbIbw&thxEEiy7pY&gj^A?`J z2mQ?!{R7a&KUVbw$5&7KZ{3c62l^{ac&XQ~G4w?^n@#^U^!0@M0Q9sa|K)$t{_|v< z)b^i`L+>D5FLb+oxCmY1KLCB)k~45w=YRHUT@G%!SN@M4Cw&|G5i9&ppo@O@(&!CZ z^re5*<#7@EI*a~s=v#TdgT``;MX&!~J#KjrIxemJP3*A6&>N^kHvM0qQ-z$Z&|m7x zA^co+v{v(c3VOn&Q(oJlr+8k}@AsQhj+dZAaz4l}qIiDJ0YNqHdK8V>h($jRUCK8N z-7eqT%ga=IUbfb6-^8E430?eT2)gYjS4=BY+jy z^`}39jthDEd=R?$^R;(c{!IAa8v5%q=nXT2U=$Jz3BYIXpR?GhabX$HGbcz2U^fpV*{m@0u^M`_R{ulb| zpuYwBqzNzbC;z5Qtxr9E*xxr1zb*gUGBvJ!EA%(G;h}GVE^@BGX>B==nW^pncIck{ zk-r7{b%d`N_1AS}{BsPt_`^$~d;S3Z`Hw48<@!nJd*Qo&^=;@Pclj%W^Q?}?m#OuI z??L~l75?|o*YUi8Mv*F@-jV2wx0h8UD8{+fxg_8 zgFd%CsZ6!=KY&h_J^jB0UF5dj73`0e&ni>vJIA3vV1>U79anJHPx$SLl>6Y5wLKq% zZrk&TPbpLLfo&W8{zAfUf-e602k2sNx4h0m@8J1mcU$#{@HI~@Q~kmRpkq=m{0E>P z;CU9t|CB|4Dev`smFKTRKVs3Zcv_k27v4aj4O;X~(4}0?L$}Li`qOngJpSrnJ3Ih= zCEH2#w zbhrLWd3_AJ#DDzjgXQu^=@wvC?}h#mi~e)ylRPg)I9c%Qv!<#{ zjSs#C{U(e4N$5B7{J70Q{VzcmKfDz>O_?Vr`5av?UxRL!%a@=_J$c$2^*q3}=X3u~ zm+xz!_gQkbLYMFlW`zG8^ls?aZ}Izm$?uZqYWlk}=TtdR*~(==~P`{m`Wy{3Z0a zTJ&qKDsztWy#CGX?OF7{K>r!fEexhkTl7;`m#KNtY=nqjZn}5R)%E2|(Czy2SLh=m zhs5a$Re0$)hiWwalNt2W&_@Y3o5ow>x$-;bl_`7O3;m@o9XaoVF7@X$beaw?{4=l7 z_2~AuTlI+WgV4#U^I_(R>>mpRYj`NjONvgpr+{%D>P{Gaa9D_O&VK7(f|6X7fQ(CZ9c%GRbYhW=z|V!K%; z{0syxgZ?z0rEU>j$r1WGBZq7S^0%PlM#Nuq5mL*e$dGIi7khGeap~)XVa1Yuh5?fjcJ}LH?gPK;V;l13!Uy< zor`hpJpEctr+Wy{9|v9RNpt4gQ!>K8o|()YmoY}4wsY4UayTu~7ujLP(_@3f>k@svsnnX@zB}jftp3-j z-}Q;xyE|5->*`0=rxPP<6Jx!@>yn1HaBz4-Z)&hV;mTGq=BwWg>h}Wmd!hQhh*-B~ zN>ZFg^?R|KQYtyTE)n_bPTrN!A(W)LS`Bq3c9x!! z{pHDZgX8JsSet9TrN#i`iKSz`;{&OU>A}A7j$wif_ohN)EHNg{IpzvwYBzXTI@b@5 zCD-(hCv9G0=?YcOZ4;v-W9jP$Cla?WYaBF+E%vBw8>WgZCR&lEe6Jtt9ZHTT63PW? z6SoiFHH5qs>G_F7-NFsYzVygg$Fjz?MvuC>KHnG%RNS^@D-w&b)kt5jgk3Q|(bm?s zwC=jb;bdK7ZC!0`A&O8`o%0)&PCBR0xcR{VTC9%mK;xxZIJRPwm23UQEfw0IYM|lL@(31V4?8F)BOqE z3ME#g5^gh;uv(#nYK0Oj)=?={Lr_1MN^KaLmmHXv-Z+{Z?4P%mek0L0(VJL98$V3X zmhKzS1X>jDF-1?Ndc6cq{LtWVZyGDCNH6xDa`vZn$@E}4In*~YN_moxyz*d0?kc(r z+O#Q#P-b-FywO_u8C=*fRe{(w)950Zs4pZFrN}_8x{hn3=dY`YSPEV9YW8S;m+Qe( z=YsipS=WtO0+m%~maBaHj;;|gD8sjIWJZmpYoHO1Az zisB`3Lo&@UA>BLNXSLwDBwANzMQx)gNz7j|I;#GttL+#bOfOH4uTQ1h+{D8S;PP@) ztj-Oduc*$Eb-5XGJ|Wv!7AiC$8rG%j$I~N2Hzj4#oZz@MiEVeCSq84b7LY40rxBPSINGipWF?$bf z!@X-#$sFBte(ls!3UBkI%J~b{g{oH1(J(i zIv-im6l``uch@GQNz^?~V?P^pGK%qy5GF0Aw3Is~>K2HX$;MS8J=Qyz9+z^eThKqa zc5QMjDI+CWLvCBKIKhvn!O){g9Z2(71?&GaL`!Hx7Nk>=d z-lZFb$9vVZEHL?4PFv(R0rTnB)(;P|oHNLYmb-Bv?@$lrlGz(rjoZ{7CWE=`F}-vR zjHpZ>QJ>J=YIj|N;h)zelsxyrwd98{ou&3nENEqE4c8B=`31ljA|D4A<)P#wLiFFrAlOmWUp)f|R;ax6HzTCsY|6iogx)c5QhE~o&jY-lqn`+1dPnAUuy zu3>aU9f5VH@dc#PR#)q1+e#^rPL%0mDzLPR347?DIl+o^aJ`Cv7bB)IFjW)NWOdIB z1(&9J$HxczmZa8ojid(qHnQ|LwKJfe?vJsZ*P+mwtDaiAW~*rHX*F4f8C6H=)Im2^ zP-IH!+f8|6h#ID3RHW3%$Y=*AbM+vBu2E4r6}^EmNW8)_fLNp>8-?o=-D+sDG||nR zw$=AV#zSrg!syM@Jwsg@3Gy(O8tL`s;=Z1ucu0Q2b*I4N%zYi&FE&k7w#nNPV)E<# z9858Ch)qgnTXqu4YIu@b({g7a8RqY^2L*9?p(i#gw8nLCcNqexzE&L8@|}WuK<)ot z9;7V1W|ICpqzg35W_}mAm@aT>(4MB_&m4*6WKUI}?QSKyPmPlo%#93=Ob1F2R@eYX z6|9*R`gZaq$7vMZ4>6kbhEAknSJ=#vWSrB;!oF9czQd@MDD9di2Pz$w%90bKv@*$| z%!Vd>2h^;~%LamWPTd5HTZ+NSh)iAO6_~z}^}}g@?iT8x%i~OzzlSc)9HcG5Lt7K> zp#;gLe`eRSdTyf^rA=T&Bx^1-@x=^@v`~9YR5GE~DIYb^Dz&0hWqyh&K%5INh@Jzu zbyv*+8Uu3xtLe(WO6oSt@3`9ZP}lR1aN$#T{n6E(PQk10QDdK}W$U4j-E4$5NK*@O z70k|jgG@g?9ZSDtV0pW>5LUO)UmP(TN3{od(L47OQnM zFn6h&uNLm*eVe;_$E>yLxJk5#HLLLpWoqP^EO({DRi2N^>Yc9*A=;8ol)lmihYY7; z7A&>1Tm6O%iPadDX*L5Ue9QIM;>M`;j0FR#Cjx_25gufFqt>ZCSq4KgSMoYOwAW+S zC2GuSO??X*vqtD0VQr;HbY7^tF}Y!9XG!MR&npr};S`BA3ZH5W8GOeigtJz-?Mu#gP3lA-8M=LS_U?fy6*w2>|#-HgRCkr2~zFUf4VucPI* z&a6$*au=YV-y_lndZQrS&=ep}WjE0J&`jV+Q`PH&ld)T*sgjAFsuhql29sERA^MXs zX%W@Ri|QX4nOPPMjZi@H(Q_K?OsnP%Yw~Qh?A^RJf6ts+N#!g0bil%K&dCH0$@oXT zB98}Po!qAxZ0FT+T7Zo`W)In?1?t78Ki8#}5kKV4H@)%`$$DZmh>Un92XzpB|L zeC|#fVm{lQ9@ffclzl{Mn=UIE+wc}$-{@!}v(QvUjV!p>%Hf_zudBCfkKOHfN7jQu zyt7_Ga^UEND;(`vHwo6gSQm1=v;^n=>NC&%<=+%X94NFZZ|g9f7F+!po82y zIcCajG4|oEJdbWt{fe$!?c`ui`Vf)iqBAZ>ItTt#*yxu zvp|957|~>;mq%mkITCy;q+Bw7T5?d;7;K<@) zINNKGq_vTWlu2fU(-Z1^ATOf&8g<-A7u43Zp>@AyFNKSR@dl;kz;G? zlzl^2=w$PGe?@5fAw#Z=4ff;?rAqPGrC5#P#A6q z);81&+6(kHkAHv=las@nN5`YiK)1>0Td6A*%QA&b!otQgalMP)l+ow+)f+FW*OIqh zGT(qzR5oHW=)efvB=36+pkXUjmad0kf73 z!rF#H%Szf4PoA%-3=#ZMhkB!{z!anc+5=S?NAVN4bC-Y7sZotRZJIKr&<3`^EUHEs zBW526R9W@xSU_d6^V6n>___tl90}&qc<2!yI;kBMEr(*xsPtqerz3d1K>YJTv9mG;?<(_hVSr zGKxMk6(zD13skN3);dZl1ZICFz-l4QLb!hi4rP`5dRH6o&Gjck7Sq-8CEhGkX{%Zk zyW(JCzfgeITC|r|?*5T2GkEbw?vf(>EMsM!4Lh^*66%#*8;1O~oq8$h5enON-^2iK z2d20hHSj5kjvE;v zNS5Z_q3m-%x!sZ&V?*Rhidh%#v4#|KNurwHd6l7X=c!|Yta6syy^+dLtXR{{X55fh zsxq(~GOsd->>qqXsT{DM$fz4kB9tyRqY}APTyhutlUoAH*WD`aGev(r#_= z79kr`c{|$CcX&oOP%*YHdHKpPRyrm5PKPz6^{f0Ntx&`vpGR7afa9@0@A=&$u87su z1gZ1^B(%ClvGP8B7y)1p@d|Tes?K8W`nJ@B@y1&`2 zm*(d)nvXiOq~2UB>aslYtbao8%Ht@2<#$u>ZwH6farvedu~uF)Hc1P*KF`|P4wWn^ z=BK0kE|e~8t7)=3fjsU#i?a!fGNrlpOpe_1FL0_12lg)#bMH^f-zE{ao>{$O4!54g z86xu1Gb@>_yUL~(s%mz`zL(S?zjULv$77a{sK{A$S-+I~FzkEf@KSD^x{tuVm#?Mx zS;$tRx9_Dwx;u8IyWhcGcA&wwQ+T-cy&ma)IqMXy*~u;V1KHVwU)xmHJgM-GUlt%T zzYXp6tc8n}d5QWYd5&8og{Ndb3Zj-Eg58NeNiA;^l_*30D1KyEy(Sg72fpJ*@9ef* z$KxlO)d0s`5=L|DT~s;ZH-t=fs5thhr`SEL=+APOG#TmsecM@IG0@i($za1A$d)Oe zUi)K}CU}%j9U}09PTjjqvDA+Vi#40HE1CJpxJ^GaXKL-q-&cxSe9Md*lT|$-&v*IF zZHn_uEb2g4o?;?Sb4{^s&j+Zl23tU7X1Q&xhE|5S{0ah zT8&ZR&G!Q8{XE>h9GwCoSZodhUacUh+PUPBlk7#keE z)9M=hR;6(3=%R@1NbTFCg2!XycJbsb`mACV|F%UYU+HIu4|>Hldbx-!+!VaUDHj}A zyMIN<;>GS#$qMU9Go>#wFFIM~%rbt=WJD&b(gEXg?r?+YYPj5xX+-|Xsh83>$aIh) zoSUSq>v%71Wqp1iZ2ppe!jey38c?5%%B}Sex_-fKYOPVdv zd{?pN6!Ax^7prBWj90(Z;q>4@JkCgZTQ2j|U4EH}7*PaUl{iHr8x_m8p_$I#@mR8Q zw<0}<&wPt6PF+$h-Ai<`?wR!x9cw6R_T-K{@+#Mev0~;zQMT=38~!?Mf0uon^MZKgiCbJH&O7o>I8O^%UviMi#{G}WFV70x764r0W zIS|JymXQmxB8%L1R@~R-u2+}8-QYLM|9`psO?}-&Zh&(ql$3PFn~m-2&BhKtZ=na6 z3-FMDdwS`LAS#o)QxSHy`MS)j;FfX~oOSbFJ{OzCmi-jBQp<}`vgRN1VwARFkw&q1 z(mLq-Ss2%|{>(n0G^ts$mMK%SibwTqvW*jRnkUY(?lwi}TPM~*uwsUke#>U{*o6x$ z;`@AF?^Sr}d~uiovbC2`{KaMS7magoKi|ycKmE6L%zL%idOK;_d!xFw3BL!!O2iN0j8J1P35P&z~2&u2cH$9IT<5Aboj7*0qPhMzHYWU8vT0ViUUAy%J!{UB$B3a#(@zyPzS39q^cWh{3Lrw1}C-`d88%D ztG(uy@#NU}HDl|C846u<*T~qN*YFMBr0mkxaGEK-v1YtKlq4eLyiZG-;dJg%S8#@5c%2(QG^Oi}4Qys&>575gvC%}^ z#OTOa`uf2MnIkr;PY0shVvkH!nJPM$1tnIb$>a57y+g_IgvwPyZkZUqYsjB=7E%7z zpt2xcWVN+(exnqS$@7Y2x{Dqgt9d2mntlF!D_je*|d*_0M!sLs{=EQ$uMJln@Z6&R?KcSkn#ENxPc-1o059-g-B?soo z7lsG>=dI;+amKt8y$QAP%7V7XJZp|0NiaZm$k^l=At0a~lrB~qQW(OjKlc~VZwEz#D_&!OF@rBFIg zeG4_UR=H%R&fTS#Cr6WvD53 zu8~lJ($AytZROQ$q$uU-B==o?3ZlTr7WP2r=PjQ8@U zYN1@7HP_dpNA5-$>mA#eU;x07bBynM;K9SaL-N3oX-ZFu70T$TFahHmht`awlu~XY zVe11fFDq9je!=Ol9ZV&cv1U-X2A+h1bx5&gwru^-8urbkaNNA+Wd}<}oE|dgaMQ@L zr~9>Q6p=yn;KBwkY?waA-6O@hjQrwyi&L@~wAM_B>guc+QC*#-xw>j&s+3%w6~{~B zhGd#CNV<2p&u-asNw%)eip!T-S0v^y868!B)YW#dinm;SiKT5y1Lmh%UBR+CN0>b3 zWX<^mRri{>s)d!E%L}t84erwQMC$|!;tG87#*349AMQvh%>KHiA)L`E)r*@Uw z+DefF`}7G{+{TQp-Qo`ODLX?;#;@;9vvQrIF&2l!Ez~BiiurnE$GDZLr6VahFu}@v zo4Qs%S2vwsJGGdY1vyuDOB1gVl-lR-4HT?kU5uQzHD*Q1y#T_$$wIqZUH!;QW=?_y%5bY;0tV(=sa6 z;;ZtY^_@YM3sq7yaCL=`2+hlQA%~{)OB{NSa~WqT)aEqDZMd{OAzwQ!LE`fRujAo& zp3ID+#4&TPz{_LSCc1s=7@~__K3|aiGD<1LMw_AVa+Q;DgL)AsY{Ol)Mwz@>H}UD> zrgY^{X|S|8CaYRW-4TUt$%lxG82OmKLQX15q^v!2UNQ-}f=Ycr!1s>4Zp4$b1w~!q zgE6o`X?n774=T2YXS+V^%#K%^GS4*>P+ZIE2MH~v2RcTWRRb;t@tFe17F~hVm{ZIBQwzxvLs6r&^7QI9# zkweZ(dVU*uOgH9ql(gsSo@owFv(RJN)f$D#sZ*LqR(!dRa)j|jI=ODFH)RF3#s#!2 z*=NQIXyd0kM;6-2kjB*K$UI|Z4tZiqHsh*MPfQUNG>1=8%FSI?Yw)lgSPm%~^?nvz zgy}Qv+q<~d_2J8Z#K?6o<@GeOj>m5w% z|;%#jz{1rlOr zdrgj{r`}`sKK<#H?<4XebF9WPq>w8WeU07MmA$Wg`WoXAraR%I9rAew-gN34uu4&C zEypG}G&7bO>FsyBXLWo~eWX+ZgxqqWmz?P4RY>cyycoA&QLM{*;nmNAYjp2}o?aHF z7)r#ND$tiuu*z*q6-3Il1)A4F0Xc!0Ee4N%g&SzjDWmmz9D#L|qF! zOG$C~9Z={uqRbkb{uHo$#&9w3QS)I?PY*Gv`OKtRF0tGpCzGmrO(rdgOn9wfOCAD) z`LQ}wIyChbcHUzidsW6Vl`lFjkI}snLrmX@q+|PjBIDZIxOyN9sa~o2UgLefEF&Sd z!;=%EV+qdE$~u>2;i*T@vfQRksGDPPPO&*z;T2(dpM+ejA5L3)i=l41JkDzQ`|0A$ z_x+dPr>%T2D79f|p2??mXxKAIZX+6{PhglNAKRk6FKV2mrP>3f(n+;mXHmnilB+sp z{->B8#JTbmcPY5FSj`U_)kkK%axAKHW?wI$2EkVS^jk@*>Q7x+qH8`armuI@h-hlz zyje`c8E8U^P%%6I4KhLXwJiC~++Vzr06xSMaM3@iST+QZ3`_&yIST$^C;M;Y!a(Y4zoX4HR~kz7&3x#?mH-471`g zqMY99NF-Bg^y*#n>}9RE(Q7@159pox3Ly(~>Nx}CZ?k-FgzASidbHq**VBVY!%ky#0ha${499OPyXS3*Y*aZV7(ixiFD zGKLOu&9$KZ5O)zn2YaYJ)etu}x5|tQf}NG#kK#zK+uHb_{K>eSFZ^(u*{fHL0~s`9 z7;mzm-klFF*QxPkp~JqwsBeLql89OJAN9o;D=@>#q2QvvU+Yx}W0;s^@t2ID9lak* zaB40VwFH)f%;3PTG%^FjI@L*+R886eej-HXyiOuTm*?sc-l{Kw^W53%d^pxrr!KJe zinX}TM^y7yBuab-oWB0e&xE_0!cqh`2XX(HpNT^HpP=qy=a6n*wdB%f*)vLWtS8@M zX{A_}7>Dq4<&Fz(P({;jb*U^gGr{Sh(#S2i-?;vqh zzf^ULQ-C~`;XpG)6GW|usEb9WV>eAxrV~ApDAN$s?CB1s){D#TGG2b}n(;YLua1}Kn8Ctv4&DS#-E8nZF7liK2FiT`!lwf2ICj8I zh4ktn#}15L3ZDly4)so`uRQC+q(#ePjj(KHed>{261axuaXDYMw4J8SfYon?}-QL&$LiEDC-Pl4P{ zd78R_S|47V_V6gGI3sI+%<_Z`=+yZ>yOQ8EG^MzV-yKED&ro^liKtxJEFC-Bih=YP z?qnvIyYn@=ZtVA9>YbMrX<69Nz4Dsg{zUKCSntM!yp5_G7C*Q(evv{mEnApa(o!#2Db;%~0-I8Uue0esk;cM}1 zATT;(sfcQmy@g;#1bNPKGj8?wrww_|K1O{PcI$)q`@>iSQRhM*#w6|LE34FVZ@a=v zmz@?$H+rw71l?nVGiPsvxufU8KXoWQ@$m3ST5kT*@9&2@NCSJ1Q6Jw``!9oQ`5t{z zz4Gi!$aA1V?WUD*<}WSubp);3BVs;T7oxMyG!rijQOaJ6k@>lkm@$Z2AuQeZPGqkr>w72Y%y`YW zec?+7(x`a;9O$s^H*FilV}o8A=65m9qmPH()&{toy3@HR}5j>D3P0ha*Xp?qn zY2-S1UIz^eHLKJz-=^wITWW7n?^L=i97BhA+XR+l3e`uw`fIBsn*sVYbk&agt<%(p z<_jndQQ6-P@YjTCDAiQQeE2kvcN^^O-pDj5jW( zUivT07C{4q6rA#C4olm6D*1dSj($=tE{2<;8>U=6z;~KR!G3WZvokcgc~l>L4{J|k zY_AlS>SgsaGBYZqqtP2X+C}BQvo_U%^(V7mzhPv=w3MT_`t1(4rsrP*k@JoGMP+;= z&Krot+p(UR=>~3}?w8T2GAQ*RIhN#lrV?0{$(g;j5Y|W(T3)h-;>q(>l@WvA)cN~z z`P)>DJ?znIhYb8;4sI5h8)=v98P(KDg*LVYrmPxh?3ld?r!woCvVclu=cvsL{mocC zqt?a@kX19HM}cUi_hhsn3OhrT*de6Bh)dxSJ8UNnmp>%1&pRlpJKBrW_u)QT7gRIC zsCxXCIZ!R(K{~f_%}63RV4C_+mV17NEa=vt%R88reZbB82#hI)%!B_^Pa4#UMMyfT zCNY2UD^uv0W_ethOK0kdmm=}nzXD&MGzN*u70Cvg!R1s(ex^JMkwaGO)}do+nKBnD zc0Z)GzSN6xA)id}I^N>sFy_0<}yRXvI|PxvkWett?&0_sZ$BldS&DMV}W+G-ojzRSp1$J7p5FE^9Y_r74 zv37Ga;Gl`VXooR&6@otp7?o?x}TGW+l=18^m0$-k{ z2z0Vin{}^U>I?tWXtDQBJ%jYij)N*B>9LCvDG0h}Oa+KKomlva{L(u2)o$yaLNyrGrOo2GdlN-=UThEX4;HBn z%lxv{X_dQm-BA(Qs?RxFO5OY$WNN*&RjDi%9O||XOGCmiyFa4*z zj!F&+>SU+ZgmH9HeXM!k%<5ysgVarWdaqs1@~vARdQ=pvG#)u4w>jqr6eYs7Ew6Bi*IYk4mh zN8d?0MN<`H?~<3XT*e|(hEy_O4Xf%u(i%k_9D1bn2zXy$rR)*c#Oil~cdQlRsaf?s z(DVmu+aq|7p=vqxxe7M=>{|@=Q4w?Ihl#kk|0VNA8EQzzz+md8n)WO3QYlONByVk- z>lEiRLXSEKrCy@TxQ+!ia_@Cz&Z&PwZu{d{0j-L^?!&k;{OX(hb1pHEZHZHh%d*p> zUKuEElRQ*({z@uMJT0vOeV{|gwfnl8>zQ@pOgySJQNR7dV~O^k84 zz?3o2E*0L$E}#_{w-;uzXAHVkw}upjr?#*!^s`V!cighgoYb$SWpxtXhPWxf!nK!m zZSS>*@qw|y;XAFK!fId&PeK<*WY-#4`wO1JjolID1^=wF&00@PzS7T8M{J)dmj=Yk zEZiFM?HTnJnBM~zA&i&JOC~I=bIlUJ$h>4_nKa8@7}&`PI_=XrwzrnKRLL#A%| zr>9<(-yjo4#&%wsa{S7BWh~=HG*&fAlCtDftp=fVj=e#!3dA&@(cWsWb*1bZ0Afog zvI5v5^IISr8uIS5 z$;rN<`7Xfo<*f_7ndv)z=6if`3X^QvL(_}V)4N{qg{ z&d!Vb;@%{uJLg17`T-a9 z5*9&JDR+k>xt76XU++fQZPizhE@Mqly`SXYNV=4}?Ci?}r+zmpuPQ1#Btex_yDmE( z{#HTQYgyWiMViIlhwGrMSWee#16ln+X_B)x+on#=Dk9$=*_n-NpxynA(6?ROb78Xd zTg*r^yaz8_Vi8{}^t-acljoi$M$YyoMDd5)=Pw%P3k7|AdU{wEb<(|q!^tt#J@{)p z9J}x>DVOHU^=1Id*dy`u4hr1 zdpOTcsp8AHQK<-HhAaiDuUUs1@nPFhcQ1obFYMae=k|oH@N7z_KDvHxjal|CdU1mZOT<-P?l_Yy<$2nj*4f&X*K4uihuw=^0eToVgD{BN zH|rfAA6z$_b$Me(gIIVOFVqPr@AX&V?fGNjvb~qtrpko$p}D*QQ-m7SR-=KpTyHh( z8I7NHayV?IK6Xwn`N{F%f5r>0nIHd-NlKR%t227MmX#*-DL?dGgh3ggZ1CTzv6l%# zz8)Tvjqoppg5vx$BG7RjqKfq3Z&rj|e6<&IQ zb^M$4xT>?he#LDaHzfMT2YSawtuvpD-#X{lMey9W79>#pB0CU^;e*5L)R7hWq(Nt5 z9u)n1UY$XH^(^%r^{=L{ug0VMnZY4P5zXRm~`ESaRH{S<7V!>xPs4HR@bz&HCZq z4ZVXY2?vj8eKkYl>&EF#YxEFzWUOX9y?zZb(-c@TT{GT)XANKE8)tmmT-P|So@g5e z#|PI8rUug+YX*l?jD^Q*1_t~4lfy*qO%L|f$O+n1&4%7F0uPVW42|@!=L288qe_Xp zYR1>E=^x~C@LZv|kz{+v`UVKNcC<#u)?-}L*E}+;V~+7Xuzq}_W{8o5r&8~lL8rfW z?5@G#(Y|qQD);wZ`D3+H?moMy=sYwEp(>ky+60^}Sdq=YWcaf*PB)Jp%q$BqveK_G z{FXp4FS^rZ_;Vxpn+*Su=C`^r58bE#YA`8IM8w~3_ze;KGlsu9g5NStGfcu+`QL2# zdm`e`F)?;T@DCdPrU-uRqco#hAf$X&8veG3_-A6`&v>+s7?u8J!*7X@f7I|tBlr!E z(TsB%j2Rv0xGBHcGc@B&2LFuVUpD;A@~_ybBOH9J#tIEt<#W{Vo3tN!>CZ9ooxjx) zPigS@D-HjY;m^s4pECT*Gj+rx8S(cR{^7@IMngvYgNA?M@tS`&BmQ~AKludB*rOpU ze`%BPbOisL;qT2z-+70Qu>A^+jk+Fq`ENJ;qq8(WQ~yndf9c7ZKO;l_LBn^R8Xo_c z;Wt!jesxBC=YCCJ^)!taHF)~XGW@QmYsTdae!Jm!K0`A`Gvu!_{KKl@bDZjo{O>XR zv(M7;Gt2*&;m@ej{9_tC{Vp5+3BzBl`B~*x`y9<^iQtbKey0AF@6-{_{GCSAa(n5o za`|&KKePNc8~&{4Ykq%*eGVJ`#0xZkQbU&hmkd9u{hm{;BOZu|f70-`NAOo)Id%Ms zt293<{%*sMO26~!spFqA{HXZbOva|D54R#a~scBWC*VDdS(q>ok8LLViQNW;8_b2Mj+`{&^$+ z)O;N=O8?bH@XiSN7Y#ou{ptleqN#XU<(o46IT9?S|K4HvnH+VFdq{_EUgjZGS|^qX_7W<>d4%J8G|x7+ZmBjg`7{HXMu z7wU+O5%C)hf4Am)`I}|(chd0ZX0(rU4Bu(i5xXMfcNu<^{u73OFe3ik7X=i{vhS$j zp9lnFyl?btd9h}6YW=g~Z*S3j(~M>D4;g+``J6NSN)Z#{-_8 z`)97O8*JNkMh6mhTjsA{&B-MYEIX|{NEMV>%XJ;-G(34KFqj5$6u`> zOa6f2w@2^~7=D!hoHP6=`^++e=S0YFjNz{`{K<&;rwzY9g5T1i8BzLA7=D!h9W?wX z|2r2Gf6k3M{iygW4L`Gftu*y(r{Q-+xfbMr(*c~3_mJ=rwu>ohK@5}>TmT5o&Kr_{Wlwa zlzvAHe@;aFvxYxQ_#yT4lHsq`@mn=G&V79eew2M0Z_^=9YRHN|bERfP`DeS~w?xF>X82L% zdmx5?%J8Gw->R4Cgq8n>lwXtKS4prC{wl*a$z^3cKOG}~m*Gd1-$}!d%HNDTwBk|eR~dd)gnliCAJu-O48K+4ht$slhF=@O zKNFL_vq~aL(W_rs^{>M4CxjnTzqT2Elzk2xew2Mq8h&Gh{FyzP(IgO(|8~QlCHye| zHT)?3Pa1x|#1F||?>GE|5%E_iH6yD0_8IRbDN`i&h zr`zzO^1s>e+jTNo`9E&>QT{pOP8~5yexu<>>7O$EDiIT+|Mr;pM+`qozsoWF#+26I zIA&JhU4tekX63>4Buoki(fgeBQ{3F?=t+T z{B1IP<^Lh==U&5C`3vD6GyK|!^e@GvKPRmtwnW75H~iKJ{=OLgdBZ;#5r5Bm&4}{f zbB5oggBdj(=d$sy#toVgRer||Kgzxr41aru{F(34giUv8oUTW9Uiz~Qe{y3WeUD#j z_!W0+{w#qI|DR*{t25-cnE0DF>G-QPOxNK5K5O_55&X(mYsP5}o_;Hh{ELR)6cNAn zHJVW=5Tbv(;a5fQ2Mj;TKX(~^RQaEYN&iwz`jxL0#T;j+hAjK-H~gsjdCKrRBjV4Q z)QqU|Z8ZE7I#^cvGhU||QRUZU_)+%RZ1_>-cf{~B%YVR>|L)i8^o@R=e{3@R)EhKE zO8&kW{#nD{6p{bco2SnIPQ#DN|7pXo5-}nEdD-w=bu!a6_`h4;sQ<1K2#LSi@T1b- zXZT%OOqTphhTkpx+U)sR=N26?s($qw{%Rd8Oa4*AUlqZx+Nv2*{7r^`IwJncZJH6) zzwI!5Q$LKE=}(RSoHqQX2>F$7(hTMQA@kc#!;jK`pW$bwzsGIg48NjGrITg9>G$gN zcSh*9%kV3NAL1YT3_mJ=XAS?5mhai;fLs5z2N+rUoiY3<|F~rMK{1YV#K_ zc=o+$?0fJ&$2nQ%)NBKHgA)e(4K{&~gI*233*3pk&w-mk&g|B-gA%U=ECXx7zjvIP zN>IXAfctr#0p7>+QS9*va3}aR=>6bkp1Z)iz*g{M&>O*xJXeFS1!sWc;04M{!k+{W z5bhv&8_&DJJHQ==o-)`Diarmd z=kW6%LO%^&XlRZw4O)ror#Q?+5vCl>7-;bc51z*DR z3Gk;p?*soEoCGD^Gi1F;%^3T05^fHV9Gpq zfnv`_Q0!j`Hi9!jvBL@4rze1$!PkJJpya0u6u)Q!%fUwQAoPp(IL^nxbD)Gf0gk{w z3SQv(5Gej~z|eOaoCNp4Pl3zfcY==rn+(o1SP3e>0{0XC0`0ey-zjh=xC7h;Jq>;z zoDTK~pMFNd9R!~WPJpYxM(_wY2mFDA2Ok8_(eJ$nJPDox_k(`|SAka%-T@bbd+1*| zds=gjetZ}_4RZ9f=D5Ml;Qc&L7_0@qLbyYlHGRLqP2fL4ZwJ3a_$KhvJXeF?24@RG zp83CTD3W}YuH-WI<)0)86Y35wippvc_>irg+x0u+0-o9CIJ=;?ssM`t!_JO+y1hd|ML zKPY~*7Zg9*1WLTspp-`!DEVnLSOxBYJ`?;sbO#i@PEP1{@hB+edJq)3t>9mIZUQHG zt^(fzo?*T%?Z**N{A52Uelh_{{q6>*f$gB^*#L^XGtA?~e-47uPwfSzU)Tl;e-kL} z$td`4o*O`sR|!hLaEx_^=Yo^qH^3D5Bf@usXTcV5H#iskDL51SZ}51UUxeQVHi7M+ z^kcI?(XRr$A3QhiIR6740-q0mCn)V#3asS01(bg7(wI*FEO-k3F@w86(Pta@62hfH z(W4XmGkD~cj&nWq?O;3D4N7^ngHq1b1}nfDdA=~J?Q#y3^4$+gyS@_?`>zI{4Xy;m zeqG=v;WvQ)0A3z(oL50V4z__4pyV@Uuoe6+^vlBz1%e-fUk3Mr_26!B0hk7rUxCs- ztpufgYB$ew!8h<7OLt^~gU&H=v;o_~eT-#+j@o_8BegVGLFf&T>k^q?MB z90K?9yaW6l{1mtZYz42A@SynLOmGf(WWaI00PX>Wz6}&V-voXSIsM>6;4JW$;E8pP z^L}t2_<6#m!C&ya5|sEYpwxqE@O{uLK#6~0t>gR$xDC7$emgh-&H%p&o=-Z?bHHt& z)Q1*O>cd=6{BIU0^}zw(3!d-Ses~;|`mi0mhVbp+i@+J+dU@`1oL+Fx8prtn;dX%s zchvWQ~c$aQ>oWndH0e{W&Hn4%`PH-VO2Yd*0z$?MCFQa`0&wyv~+tV6* zPJzGX`J~34}=jq@+o{!&3e~LcGH1-?;rCx0ZzW{##l={62{3F;3O1@ga zGsJ7s*wYC9lII4EJ=Ngni9Z{Ze4W0O%munPPO@hd=OFYqAGCs#O59sK>^H+fEh?*O~N6W|F;7ssp!XN5# zoNK`*a0onpv*V<|?cgBT4gLu{x6E;lfu}*KzXPD8(*VAL=Zh~P|KNU5>hms8(%WpX z)6g41iB|=l6+OU@f|ojV_*3A2^SleJMNT*PV({!uj`P3J4}f3dxfT4L#Jf@BVeq>= z?*PSKY490f1(*c)bU02wxEfpoR)dnS!|jgqkHQD5z)n!)RD-j5o(}$y=My(LP9M)Z zz`q0A!J}XU_%HCQ44%85_8j_2Q1W>g{C994DCMvd6uYFs{{kDpxrD#a<~T0|4}fFf z04RF&gZF_=;ALx16P561ZRTFkawh6 zkN*yVpM<^>{0DFV{9E`9;48t)O*;K^;A!|LKp8I`HMkjkG|y>J(j5Rr|0YnzO($Nc z;~xbj-d=;-!495NpoH%SC44pb4`3x&4W7LgJAenlzX3Oc8^Ct(e$s0K#lL2OUjff9 z*6AGqCEPynBJ>>w)8G#1tHATnTfjen72wCfOO3jm&w--XDexBXASmPT6!?0u2^77K zFLIpQ!89m-&;%X_=YVeqk1TYY2PA!i{op_HJQo!GE-!GLH-cxu&w$HGj)D6Kci7-=gWC;G zf={`;e)bZ*nwTn)-N=-gb!ztGQWY(5T3_@khNKccaD0Q@uI zyTJbd+rdjF{Sk z34cao^C&3#rZhHpgCe(6^E=x$Hea|>%RL2(+!LV4JpfAheW1wQtFgHO6uGq;n=3&H zSE2cxGc`6Jtk!aOgCch)D00)FginDYcR*uv1^58`voFx)aT*l=JOoPk1K?T}0xQlQNptJ+k;HP+=4SpJ|0A;*)>G^uRcM*J$dVfJ<&w21yJfG9pa~k|2 z@lJtHB;Au5n~#Gs{yP93fWO<|Hc;e`f)~LQDDnq1_Vj}yf3?P*ZcyZRfg-n38ZPL88GzKpxA4b#^xqNZvdx5uhrOmdX8zI zz)vFQ5GZ!sYj6iB_S|fqN6m9TDEaLKCBN<9&&gk_#-0}N9M4S}duqWiA$KnLEYh#m z*jxolejQNkdHlH=4}l_o4=D0?gJREJ8hdtvV$U5Kdp3h&&q?r^$ls)~ISq>ZZcyYk zfg-OF6nPCAdul+Dq{imepp;t+DDfIW2{+5oPd`VtXK7DCp&tRIJP&JZUJXilb%AdKTR|zO*&t0~ zO$8|NW`GiJy2j??Ra)O8ptO%W!4H6&L22hEK(W(mQ0%k{6g#cd*wYP)ow_vkw1Q%% z7EtWoq_MdH6gy1^Pl668b~^rSo!>(S_k*J6Hc;%*4$?H%%r*2m;A4?nrLp-8&LeV; zf$snhfkz3y4-~)MX>bx0drTPqfT4FAdJ8D|tOg~Yb3pOmDvdp}LGj;8jXg8LFCl*h z$WP~Vjm?+IwDJc~{CA(houJ6y1d99#Q0$o2*fR=>9a9>6R)b>4RUki|D>XKEfg-;a z6nT}P$eRU&$U6i|xf}$=p1VNt!%2fFgZ-eC z&uUQoca_HGCQ!<2HYo9CffDWlfrNetq${Y|3krQVD1Nw0V{DGcOU!cktsPd(; zXC|ofrLpJo(@nlWl`oCW=Rs8t;7;V80L3qkfZ`YXLGg#ppp;W5DEV&(MUT0l*fSISBJyT{ zlAq}sn=e=D{G11OA?E}r{n=6Q)8JuH^0#03;4V<|mjWe!wV>p0E+~3cfRewJx4&v z_hF4a2SCa9eo*qgPh;~QP|9%!_)c&VlzfhYyTDbT%1) z2$X#81SOxVL7JkPPD5`6C7&%Co2v}H5|nmlmd55Iv$Xt!ptPIYzz>3>pwi1=J1F`! zndfTrJR6kuwE`47U3!wX(?w9)oeLU!&V$nKoYUBI8vGLao&u%aIjOPvI4E}74~iWo z4EBQ}zY~;lXa}VnS~d2xfKm=k8hdI%DTlcrO-E<7#^x$eOy=(cm0|m7tW{Ebtk~snFPb0ijZk zCqRjJ6jb%m&{u&}-I`WIZvv%!8#OkctkCu85GeI&Klo{IFDQPt6MP$TSA$f=nl4b} zw}T?5Rb%rUL!S*weW=vfeBuc@Uxz@k+a6HLVK*pt+oiE*Cn$E?p|NK(D0Z6!QMGfE z#^y9AcIyT|4pxH)2zUPR+K%TyvEy-2>~YB80Z{bb4vM{2f>fEBRzq(Bm0dJ8R~mW+ zD1JXvWAmZMY5Ds>$3W&fTH()Q1srX zv1czRdhgNLvlA4(cYvbzc8$%OLD72^D0(-7qW9&$(RyD3mENG#^8=vhzuVvrQ1nlM zqJK3=QPxx#`V3I?pRTd_2!-$q!W{-B`~gtp?*t`3n?T9W1St7QYwQ^XB|j;RJ*z>< z&ngg8cdpdf+yzR0TEX{#wV=$?t3lEK63UzeFM=vxpehf8dq7nlpehd#lhjm#5?(TM zk>_)d(RdnsEA$iKAD|xs#eRGKzv{jPNYbl1@7EJ4pa&qt3L&!LFbkCcx(5v0v z)o917@yy8HU?qR|&h+lK)!j36_sq`f0S!nX8)4QUS&I;;1u7yJga``ECQxB`*v1a! z$SUlp2o<_)xuO6miCA{zDxBmy_kO46e|7);+9mGV+HcN%oO|xM@5g^GQ1*RJ0>23Q z4&WaE4+6gmOaMi`OM#N!1wgQ#J=fySht9|SAPEnY@HYS@TpB3h-fnT{V{rU3+)o0< z{{;RM|9dU&JQpbPJdegK{yzhX|1%bMo&eqpIcmV)0nPzs|LIcTr-0H4NW2%$vGJa_ zxbp`+0E)r#*2`tW+eWt%V%egoKM3nPoQ6<=XtHn)XgIFhK ziD_bj=n$LN+4xTZC7maLlFlaG8^k(s11RBEff6oD_cT%VtBkz#Pte^VHs8YZfF?bl zNssOgVx5>JrioKP!yjn$L3fAPd^7byJOz~Sj{_xqgYI==mY60chz_xNE%^}}#5yrc zOcN7Chba44Qcj!12C+`u0E&FF&t>#U_k(nwqkEd}38F)6zLETi4Pu>`C8mi9qC;%H zf$@nAVx5>J9t4V=|3ujbGvz`52Hj86{RG|Xbl;%+ zD&4bmPZJZwZ9w5G`(u)>L-*!2OrO{w)`?kSnwTIu#OBqEPizqDMA@H`{H2KrqC;%% zV0@zNOPTxwMa~A@>%=TEO-v9SV)OOnM{E%5#0{YEUj+*PEZx(@1koXi9aK|3#0IfW z%o5YY1koWjU(5K!2C+`e64S&4(IGYyj8AM3PXfC0v5; z4zY>-mb2i$No){L0u6tl;ZOH0F-=Sm9byxU7sHR(Al8XlVw#vBI>aUxCni3zL97$A z#56HMl>HKsSN2InUWe{Y>{l3m#0F9JKg3=3I|S3j1koWjuVj2;gIFhKiD_bj=n$J& zq?q)H4Pu>`C8mi9qC;$Aabn^V8^k&>OH30JM2FbKet?NjY!K_jEHOwgoU zcnTFiQz|-eE^|l{cqwE)5HXE8_<+5(3CITn^?CS ze#8c`PTT-Wyj7sY%hEkfOb{Jn6YF-vkJupAiCJQrm>@dDCKf9uKCwZp6SKrLF+p^Q zvfeiFi49_%m?fr(38F)6VliXl6Hft6c>zs%(Y;R064S&4(IGZ3B|l<=SSQN*S<*`r z6GVsDyoB+I4Pu>`C8mi9qC;$6%=p9xu};hqW!)?JoC8We({xV|9b)q$@*_5gbz+v7 zCMJjuvH23lCpL(6VwRXDCWzaBrv3s={iS>J#pFk95bMMZpsBw=Q-A4xknVGIPt!d? zbcoG=M*hSGu}<6o8va1TpYCa5g6I&N7m^>bL97$A#56HM+y*rH2b%oTy?Fuo5l;an z+~YtA*Pwfym?fr(38F)6zKHyYvhI}nStn+RX<~xt5S!;SKCwZp6SG8Fhl-qYK#?;| z_bIxM&^aWJ3;#`GgIFhKiD_bj=n!9UC@;{+3xwDCo5TjOPRtV1!~_vw;{CyA%zbGg&F_C< zo^~;GbHRg_mid#=r%2!aX3!|xxtrjK-#al+ycl)!8TwDY2Q&oI_gZ#80vdVOcU-Ri zw4Z)J(ntIqpS0<{O~QjdLi#rGM|^!p{83yc@?yPNqbGX6QF^<9vc z-(kZ)hw)B+pZJtbPu~Uk?q@CiGsfRpvf*nOujThC(Kqs|?{}0Mppn1(sGog8LqGb? z$A1xi@Xt~HkBI(6pQOLT{OG$MCvJy7`0M)~3-rI^e(=Qa@0lNc=VPR3!|Qt<75eKt zA3tLLm#K9g!BmMb67r83o4Wth<{6W&!l0HiM&lrD$^rpy%{Je|u?x4K)(Eq0mTRtaAf0y)Qr1z8G zDbl}9`M*ax!SGL!{s+pdc4V%3#PV0WAIIshb~;{7TJ3V2WO{0c;|r8G{Rt=!zn77p z+QE31{%RNDZqjN8VUqmxyY_cdUk&p6EBfnq-TN7Tlm4${c>S*WA1yX#YDcH0CmEET11G{q$EwKFqED<|;77Z?E`!=w7BKqn(PszQ1xeX?^eIwkxf_z7O-q^uGuFn*6>&e|>*N z?o$XqeedNghL?Ro`MnACMf~+Wl&?Kv=||BI$ZuoR()u3AZ(+QU@cRDApTEY^`d-QZ zeuJg;eUa^qukVFGohH8QOUv&c>96mhJV9FD2m7BX8~z7aFUhaM^qOZ|`h)b>_nMx2 zl?|`&FKws4tk31QNqyXMg{4o?U*9`AL4SR}=;x?klHVs1HvD_2pVik|TJEQce{+YW zp&mo)`(W@gw9HrK_oeNYev0GeA-G9=eQ)b|>Q~>%Sm+Yg;FVFD$zRow8ze`0>{AM1p@$VpgN5j&uMje*lUF83$C7n&uB%S9; z|A-FX0-sCHs>`=x=J(>gmY&U8`r((#cQAG5nY5+<^ktS-`>Jc#T3YQvFEYHd%Z7hH z!>hfIw=lfgSNqzfHon@s`6=V8{hgQ1S%0TCQ4WS?dh*?p z&}xsp1inJ6eP{PhORK%RlW3pfukWY45%of7wWm5yeW`ue57U1~-O798g*Lp}$Nt1+ zme%)hj-s6kKUsgt?+VP*OnMWRek=9Yq<;bX4&tx&fwVYoHt?%jK4!fb% ze)mO;ul6*qV)|;o`orTAe$!EV(c4I?edxzY>w85zN$dMWhe)eEzh}oRKefL%|GQQm zwO9Da$1J^L#pd@P=&$yge*AIkul6U_YUFpT4gdF;XI%n$_fTKg(qHWjK1hGHZ#as6 zU-WZ=?crhcD?+RN;r+)ff3?S0Shuv=Uwk*q=SO$h^nb?oqxJ)3A4d48J)f7N-U+Sm zdpx;nX?;)OH8)#Y-xs<3Us+o1t(|zUrPV(C<#kJ|y_-*x{(jEN^Wpbde|--^zOR?` z_5GDs9I&*$r}CWzORIgj@}i}m&su(W-ePHef5Ta_w7&Q9_`5Bw?=yUv`g-~f8~=$l z@!xdRKGt`zZzS@ly{>&M-={xd!(UDL)!zR9A-&4{{gC;2W}glJ7Q_>N`aZ%pj$2yo zmEQl5rFR^);YH8Vzurapa<3QvO-JopKl3_Et3BzjY_qi5pFZz$ORK%;^@Qc8?<4&W zGyc>awRiSn=2z{rK6{zOuRChb{J*olsQvN>AeZ>>xXsG@A0W5Lr|&Z@kyd-_Uq8qC z-n^}Ujl z2Q97d_k78>%W9sTt^gYEQJ`~M%HzrJU1;XLI-J&@m( z|H9I0uk9Jq`o7AOY3r}=Nxf{w(rSP6Li(#c`0vnP?Q_fbwxVBsZ)fd1OFJx&gVev; zWBxd4wcq@ztPQX4-F)*BORIg?*F&$;|Eay!lS`IX`>Y?k!qRH*^{+3twA%Na+%Gix zOX~Akv_qlQKI^9sTl%h#Kw0>G5ORyZzK^#3Uo$+~h5Y{9%qNgvjH~i{?+2{^(}*X( z7wE6=pPZt2?*%^3@b_k{|EoD(??5}1 z-^b{G3;KEa9j3q9WB=U!mj8EA@8$QV-?Q|in5W5a;kRskRQq~=&Hiv3+~p_sl%#*u z_i?_%@cJIk=h07!zuKRFCuy}eUq(ME{`$VwmC(D;`d-#6>96l={UK?6FYAvOUf+j% zH)(wjZuNI8Kef;P6UzH2^eVqAK5G5-{h})wUhTEt&+z*G(I3)Z-!r;5YxSk}7sbwk z)E9lP;cnD3p}))WdmqY4XnpVK#lLFl@55bw*X1m|%<_BBoTc@BhR@P}2m6<2H?04u zw2AIK_s=b@_LS4C&#m_-&$j+*Pwv&X3ccy5ede$Hrlr-M@u`b#c(s3f&DEl>x})~2 zUvZ74pGLXL?=82H|9dUH<4xB8GW2uuyN~{A5A@m{)?eSx{C(1D@A>mg?*){*{7#ae zz9;&}Wt*Pb_q&MmLABR+(d%q}PO-f|{8~%vdx>vgf1&Ry9%p}{@0opV(#m%(%3Xe9 zk4VZ-?fItZUx&N=<{z;B&$E4OXZh&+nR_m?{`wy4w=c4^zF#W$h=qUp9Lw)X@>Bb= zw=7uysk-%-b+VMlqo~L7d*G1uS9`+GzQg+K`>^w`u>Sg<=$9|FwAwHJ5Xw{1*Z0=H z{1Qv+`>D>Ime%*^f5`eXma^%WUnTyVV(Y^EE;i+}>3jk8PJY+lZvF3ipQRtWLFC(X z)V}*&)Z3DK5aUiKukY=Sq8^LCzW@4@_gK34F&qAiW<07pYQJ{rSFFFj2XY1UAn{*7xybL| z+->P!+-2#@4q95@zrBw0AGIg{`y6l7e)69cZ1@fK&z1Wuy}ZlvyPW;`qm=L4EU(8f z?#SPNOY3`*3$vEi_e0;${!-u5EAO%X`hMeoWq5tB z<3H`U{`&simpLDLJL}6|AYUS%+TVR2>HAnd>66xf$J?xY-y*H=EqwbE)?e-8?;?E? zdXV3X*j_JSdT+di>5>1g2Q97d%Y4h!*Se$cNnM9}De`>t+_Z{=oFaM;Uem~l!H~e3W`|0$UpI!~nZ%04njsLlTzubfO`hO-s%Q1l0U(O!9 z^!oz-kHORHf6Ht9^o8h$y#CoM{q(*OKfN~)|8E2O-5ZGi?*r-o)pkF>?E(6c0RMa- z{fz+q^+0&pC-&y2@fkmTbwFMW7ryYX49MfSdkdBPpa!a@Ft>jAis+%vZ){b`{d?~k@TPpMt<4C?%sO3vbE2~JRTB|Gxc9Ks0Jc7@Z zYxzn!S9BNhwcTsg+Dd7EK88zXV|Arc+gsRhT-RON$hoznm6c;4-gRK1HtM>`DR%}; zZ(6O+99VFZlkVtq8%)CUOsN~o46=++6FE zI4Hj)G`hZ=XEMq)S_8?Xr({P*W~v@?_e!ydJQY^TnR4z>F~97SFFEGA)zxC5<{nx* zjFO1SxRgvN=X+8umdS}i*_5xFs~lM?<;%4_NqTl6c`NKfEtx9j%SUQQ-F&68Qt^1C zMh@kc!M&0@?w0e%TpP?2AemZOtGO$O-Ab-}B=7N%8akBhYK|6a(ZcN300Y;lanqG- zwG66~e20;lDeZmQB{D#RXv^9*jpSGvHKbBla*Kshp;nzKW=7G5LW0=PDHLC^aA;|D z)eT0QJ(SFJii)1dU0NxvR`S(qe%W2k6=M66$a+4G-ltj%$)}}pdQ+j!dQT*(#ST3u zJtV^7OohtC%@^|}896*1Oy_PmlbJ0fGp6`4u3%JbS2n3LlUhvH0+p(}yp3csZSoeA zTFhuUTNr`n+H+^EZmYTBu=jiy~UUtV@G z5{C4r!^Y`NWzhN`pMm~s=;`D=v2-YZq)-lNeNcUjN3&&N_OFXJe66_2o*G7}H1u32 zCtPIw)D#-!#!`M&)9x}zlg*Bh61Csz zz2oo9uctcR3#+&Rpfj%?2g>DPDy##up0w39pEy-TFk%nY`?@4B8@8J$6V?3EN_ja{ z>H1E^zntith}P`h^U_NyHY6cdV&eg^JFBDqrQLztmRCnfu3A;AaLMq9=M zYosmb;b|q6ba$_mF|4jF)fTZ`Rn3`wx;?3(tEtzN&h!|08nWD$W%m=5-NP@4wk&1l zP{w?uRw=9(uq|IWJR=(${+=afDU->xXFr5S+^!NO&scSBm(m%puI&Wrzgyoke3rXI zEU{ya$*(!Kaig};w+a%6B6?^Pcic5ouI4K>Id_>IO)mWU%szLiYR<%3lk95iG(bWv z{u;LTL+1p>XC~UGlO*crIq@*ZL9$s~$yJWK)jVX=P*HhNzX%TH{DHO7A)He6&!l?1 zbZDhG0E5GYV*UWm6$fCUs=yBE;Hv1K302WQgO(~{S$L0Q=ku%iTujFO;bm`Zm#xvM zRCJB*tVg>J^BxL2MfW-k^sV~uJKk#5{eY|1%&1!Hm3X)8P=bj$l+^>ntu?Ls9IoW^ zUe`|S)z8x?S|`S{D@QWAem3XNID?Zcl#i7Jco>|-_6`qhwoWm(I+)DdddU|Y^DPF~ z9C!jtP!%ao8)&O?I^KN*)FvANS;+Zh8pfcB4i`%rJIQl%HM1XhtbmhE*`hE9<-sMU zU8Q8!7u{ciF#d(*{x-6pY_*gmPpE(1gRveDaWXQ?k?KO0vMqzNy0v1>sAq!CD-}$R za&l0(=w>EYSIkj9_FU!{rhJO(e++Ij!SPM1<`*VTb86mEJD6HBtc)IKh{=)2);PZ~ z?#?ZY`LZ^Q!;NlKIyZ+}HuS0L#~Ff(R9yCLyBqFU5=AX2`nkl-<*k;v{<4 zd}TeK@iw~tD@(z9XT!1_c=tv6p4PTD?)hxh017>L3bMhY8|T%dl|uQ}P$GlwPRYRy zu0xrNV&08muim@wbEevI8eAC=w1TbI60U4{cNZaOr)ih^#my3TRrls9{f(pU1&o75DpEbI@J9YPK8tIfQGNxRkkdIZ$V_xhw&$L+AJnl&ZTftJM1Q)T*8GUmY$~Aj9=8_3ocMV1j=wz=E83!F9 zy+e?GhoF8DBcO|Yo6aM!cTY)nC%O;K38mwf@+E22M(QHE7t=+#t=st61}hfVOV=%x zmWze51iQI>tbiL*^H}efViFXkN950RX`MxFKee!tDODbOz01q_8W*{BYQ{J`GQQ+@=>QTzRfUCK;PDN+d$q=Guj0T9S!;SY^WM(fZk6; z+6M9^>D^mX;kzl6WK1p195Us#HN)gq_R6aLc1b=}*_s;8+*rcC<=pz7)kW7E4{P3F zyx@(lnH%TU5jPYSi`nHB-2c)gqI)z~UM}L$$afEJ=0=H&o%cY>gKsNN@_L{rlh{p! zsOx5D(U;HuXPWjlx^AYgl=IwB_oRB`Jol`jqML@5EEWN)Yt^GTGhMpXtrk~~;UkjT z(HSNsiywbd1K*nWwvCxRW$3ZX1Om6J+VnWBFXrs`6Zi_imms#dyHByV{pZ#P8~y~x zBNBwt*UIKTII6O{T&P0lOGiCLEz45et;BSrQe4R`>(-#mblu|0%BpdQ`U=sUH=wbZ zki2k#h7qyMjnm)49{0e)q^y5s6I8ybFl@%mH%8cvT};Wc_y%_|=`PIp+WJ3{+)2jT zWow_w)(yUFVSLe&vg*aDjZ6pw#THR2)c#iKEhM2kaSfJKKjm~i%4uB6Np^3&Wt4Ik z<>v0fUUw0DyZio$WX9$LieOFCTx3ix_WE+sZ%vb8>#iM{kp6bm`|aS&!fqG;+Eq5> z3~gvQIoqmZQk5d=VsyR3kR7u?#|GHk!gMcrm@egR&6`gW11$ul=J^&TCS8#?DYe^N zLYGEk>xEdMSndu zCJYh>7E;ol#si~;j_42wy^&vs{r0n4suNKoa?rLn>O4C^@pl-!hna(|S{#=HU(H5; z`u{@?WI1f)lG*(+`5Gv8@+j@aA7yLw8+M)|JHYw4#$*p6>*ts231M6kOCzr=DIkPh**P-B>p;OvRS|fH6Dn=iVOk9$zbLm*iN)BGI2tZ(A!!#|*QY zF`Whm_9%T5zL*rySLsFww{7MY-8m$LJenaw)3S3rOn#+i1}a#@va!#GfI1dfh3les z+RT4_CEE2&Jhm^JU|-$pM?}+R_dFum$Zk8X*Y35Knh5sVljya5xtC)evwzq6(#@Ka z@Rv`9g(Q2)dAZv$#&Kg{dU$ph}uVA-BDgS!14}-Xoao3wH;ji z@}g1TUS)218JB{po_ZImQt@>Lr;uxMvTZi4?Pw?832m`=kLHUwVwVl=jPymAQHG_& zF|9rI3ABp->)M39po{+qQL}4VHU2_blR0l=vRLabL#yV zmF&OSxpk=&b9NFf;}Uj-{COQik+&1YtcVJS@ol^9B>R>~s!v31hmdxhG`9^f!M8Jg zY&ZmD9DE|c#e|swU~kn|+*3FQsO9`-Z2PG2W*(g!ml>a!mi2t_mKC0Zpl!`A+8!69v|Gy3;Nx@3PuAwHj_x zZP&r4BTYM-8>=4k+J=j@dM5=moi~Pj#YDuxT&`M4B@0!%9o_oSv;afDrc)fpie)Wd ztt6*3jP1I_67US}dBD1)36WXn5uki=sD*7sJcH}@R%13Do_E#mYivc2q0e}YwVloQ zc>1SEo$FPH8Iq5ue}*(1p6zEYal?vqN3F5kJ5L$!cb?+QY_j`tKTZ=v8}_9d;peZ< z9af<|Eg@tilm>)>dCEKTpWcBPO~n3-7?&~T`6OvKCx3^QOPV>J%=^zgz&8{ zViYfhDAl*ifiWmT+`!m5|7M3hVq8Q&iGMzY%fifzz^$XiHbe2yQVfnCof#V~4sZC7 z9hlJYF&@xK_ge&=Wyh>IG%>at1e0c8!dDPoZ<54Ryq*$GVn%LeDq8yfxo*_a{xFjt zU^!#&>9k!ti-iSGi|#N4buPlTBM_#9V#B@nm8Vaxm~9cH-j|AGff2bCn*vVp}NR$qQuB6dV0){32LC*}!e zI5+D%3?cp$0?Ws^$(Fr;A$yq89r}6oRSV4?m&~zD4^xrT>Wm};)_(4%ZMv>yVU*;Z zoBI?My+0!@-(TZm7NhuZKsR)@DsBclIsU}PXa+_X%6+5*`@CEGNuIObC`Dx6s+|y*#OcT9j&@P94bXevA2k;B$GMMTBeGcYQe%s%^aZmOOJh>ZcI^IET zXzBQ*ziC*~`#wKYuCwF4eG%{ZG;|o>Dx0^hdqTE6Uw#oR>KTc0=2Y;WYBDc-L zgHl{#?Z^J^QX61^us2gYy7c#Z?f34YKaOpEKPcn(xVQfg-nWPuNing<2AY{AW5n`v z3e4&I=frmpB39)C@N74V`)9e`R*A>~+^3GnIvBSK(Ha+KZ#Gjrk1GnCC z7{w@hgz~+JnoNoL27TmKtYKi|#MAltpPih6afbQ*7}&765(wsfP^xXP{+B>0EVbl3 z*cF=k!|u{F4Uhk#;V0oOD9qITbxPKFFO1bOwJAKCIT_v8FGVuzQ2 zT);CgGMh}`6^)QRh+ta7*a49zm%2`H0PUTjWYDp;#!TwZ4B5WA-+wk_8wx}2u()CO zsl{z}yf-Urc6~jB1!ch_LT{$Xs@B%{P-Req>%q>U^LP=k6oLZ6F~@Yv%!zj-9$M+e zu;=BX{_9%X4kwTUtY4!~McQ1W?rD_DVCNXy*RrZfno(wUbjsLnv`@ujD4K&YH<)h> z8896k^@I!g?p{HsBV(84%2J;;Uk3zk5%s<{#d=U=77NmzfpIdoydJpJ7PDl6bvLp3 z(*>ijjB&H$OdIBM9%)xNX3u!5=0<$P7MlhXA(BPSHc_r-9*lN^*#W*P^XaM=DLS`I zvr1_zgx)IVjnH0%=zVf3M`9f%k*lH6lAt$Yp_s#}flI}tGS#)xTK|j-hqSD%sqkD~8E@1w91*cKBY8 z;y(D+PIRuuOs-H!rpHHnI>U)e{pgf*{5OP=W2aeX^*G?07Z6~fEl{qqP>`2jS($f>0M_8l{NgmG=2>MAH8r$1PffQqHkdV;h72WZk%{%=omWs=@ z!>jI?9C&9eF+vLW6++cpicg$r1<_lSJqaK);6c)-@w4{ncOHZ=HS9L|z!X3Xd--Vm}^Aq;)dRY^JX?u)wY_HwS5mVkrym=!gcsm-M055tTfE1p2Ag0AK zmY6EtO=8pD#P}BE0BOz+^vK)D5YFB`jk*7>!>$_eRz;^<)Y7ac^^HP%Pa{{Z%^Wgc zet4_&nYq{!BhJ3vyZOMpTN4Wt$*E~4=r$2{8jWxpiwG9sG#YkGg`Gx|(|CF8I=q^A zUD$JMDkS4*gj0lDYD&4{k6LMUGQ=qCGCCf1OGUVig@cWTol;?^(d48`g+CzGL`bUX zuv2m}>^3?P-2J)XA`BxEZCP# zCqpV7oeaArBiu$O!r{ikPRR(jR8m!%-1-Ycl6z!h!-X}p>Pi^~c5BDo^%0};k>xKk|Pd({|`h5aq<8F literal 0 HcmV?d00001 diff --git a/Assets/Plugins/tvOS/Firebase/libFirebaseCppCrashlytics.a.meta b/Assets/Plugins/tvOS/Firebase/libFirebaseCppCrashlytics.a.meta new file mode 100644 index 0000000..bd6a80c --- /dev/null +++ b/Assets/Plugins/tvOS/Firebase/libFirebaseCppCrashlytics.a.meta @@ -0,0 +1,81 @@ +fileFormatVersion: 2 +guid: b78f32d3909244bc819df359420806fb +labels: +- gvh +- gvh_version-13.1.0 +- gvhp_exportpath-Plugins/tvOS/Firebase/libFirebaseCppCrashlytics.a +timeCreated: 1480838400 +PluginImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + isPreloaded: 0 + platformData: + Android: + enabled: 0 + settings: + CPU: AnyCPU + Any: + enabled: 0 + settings: {} + Editor: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + Linux: + enabled: 0 + settings: + CPU: None + Linux64: + enabled: 0 + settings: + CPU: None + LinuxUniversal: + enabled: 0 + settings: + CPU: None + OSXIntel: + enabled: 0 + settings: + CPU: None + OSXIntel64: + enabled: 0 + settings: + CPU: None + OSXUniversal: + enabled: 0 + settings: + CPU: None + Web: + enabled: 0 + settings: {} + WebStreamed: + enabled: 0 + settings: {} + Win: + enabled: 0 + settings: + CPU: None + Win64: + enabled: 0 + settings: + CPU: None + WindowsStoreApps: + enabled: 0 + settings: + CPU: AnyCPU + iOS: + enabled: 0 + settings: + CompileFlags: + FrameworkDependencies: + tvOS: + enabled: 1 + settings: + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample.meta b/Assets/Sample.meta new file mode 100644 index 0000000..f4b4128 --- /dev/null +++ b/Assets/Sample.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3d6e178afec714cd393bf6735910293d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/Assets.meta b/Assets/Sample/Assets.meta new file mode 100644 index 0000000..554efb8 --- /dev/null +++ b/Assets/Sample/Assets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b29c81236c1a545488a2ac4eb6d5f598 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/Assets/button.png b/Assets/Sample/Assets/button.png new file mode 100644 index 0000000000000000000000000000000000000000..0cdf516301bf12a2c1d1dea3f44751fa5edbc63a GIT binary patch literal 3926 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 literal 0 HcmV?d00001 diff --git a/Assets/Sample/Assets/button_down.png.meta b/Assets/Sample/Assets/button_down.png.meta new file mode 100644 index 0000000..5712fdb --- /dev/null +++ b/Assets/Sample/Assets/button_down.png.meta @@ -0,0 +1,123 @@ +fileFormatVersion: 2 +guid: 937c96556f7574eaaa1751e2092f0b63 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 10 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 1 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 1024 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Standalone + maxTextureSize: 1024 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: iPhone + maxTextureSize: 1024 + resizeAlgorithm: 0 + textureFormat: 4 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 1 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Android + maxTextureSize: 1024 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/Assets/button_hover.png b/Assets/Sample/Assets/button_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..751844fda0f1b5bd9ee9f7d8a5c7b9cce752b73d GIT binary patch literal 3950 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

}dx%XPUl!DD z=4`LYOk8k|RnIU8el~w|#ICT-sB}0px1a2F6nes7K`g(IQ=nXQDD7?#q_p8k$8D02 zUXt4Ur=Gzrg%tj{ZYsHFUb^+5pwFE%XN48(&~N=8#I#}tw8{n|$M0?UvL&%9+$T4M z8=FKN3>vv#a}?x}Uwr2Jl$i0Oa_O9_o*Q&0&slIvWvmm}n$0_>wH zwY#)YCJap9)IH(9lO)73E-AMkA)p61i2E!bCGb`c20N&>kL`lJ@vAz`75^+k?C^q? zC>6D@XC2m5QIx9rVX#-{QD(5(J4uam9Rcf%*&Zf>ju-_6DV*RkoD)OeEWk|LSRB`7 z2YNCk!#CBUUOI4NMN3lo5tWH2y&LYC@`l0nUQz>YK^s+%*`ezpsSX_Bp&#j+if4qHuQ^)#w_VIf0)+7C0vfo1K~ z$L;#B4bGmy>IGfDkl z^6&TFL|3*f^$0nj#!9wuOeG($J9MnG`eouQI8k>f_$8>l`f7P{((Q{?9TI^{Mro5p z246nKJev<}pYZ4N9P7)|%p}jmoi{tX^fVo^PpBy9>X{bQ(f}Kuaac!Q$^}q6U)%(0 zC@5RV)J31n>dJHDVu%GE-Znh^I6t5~VlhSXLewL8OVW~=!8a1A8O7gxWBUuUdP7_u zCrjXvSZo!#$+vi?TCF%ZQ!;lQ_B8T!+?cWi&$KJ50^8gdlf#xTJ5j`L*KEKy-d$tA zF+`VJ`FTllQAqr(aI`;xvp1ajfFl^XC*VoK&X28z?w#qlzzwZm5t&(Mtlxa$>C|d6 zED|^+Ci@JZ2J20e%MlK#7&zj?og_qst|_W;&!Ln51DM(K7ytkO literal 0 HcmV?d00001 diff --git a/Assets/Sample/Assets/td_logo.png.meta b/Assets/Sample/Assets/td_logo.png.meta new file mode 100644 index 0000000..7e27c84 --- /dev/null +++ b/Assets/Sample/Assets/td_logo.png.meta @@ -0,0 +1,123 @@ +fileFormatVersion: 2 +guid: 1c8be89e83f14479081250d0c4806b85 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 10 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: -3 + maxTextureSize: 1024 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 1024 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Standalone + maxTextureSize: 1024 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: iPhone + maxTextureSize: 1024 + resizeAlgorithm: 0 + textureFormat: 4 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 1 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Android + maxTextureSize: 1024 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: ae3eea2b77eaa4081a2c018523da8fc8 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/Assets/td_skin.guiskin b/Assets/Sample/Assets/td_skin.guiskin new file mode 100644 index 0000000..2b9def7 --- /dev/null +++ b/Assets/Sample/Assets/td_skin.guiskin @@ -0,0 +1,1428 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12001, guid: 0000000000000000e000000000000000, type: 0} + m_Name: td_skin + m_EditorClassIdentifier: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_box: + m_Name: box + m_Normal: + m_Background: {fileID: 11001, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 1 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_button: + m_Name: button + m_Normal: + m_Background: {fileID: 2800000, guid: a57a02d0faa91492bb9147668fed8c00, type: 3} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Hover: + m_Background: {fileID: 2800000, guid: aac4f7023d2ef4295ac6071b8e1d8853, type: 3} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Active: + m_Background: {fileID: 2800000, guid: 937c96556f7574eaaa1751e2092f0b63, type: 3} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnNormal: + m_Background: {fileID: 11005, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9019608, g: 0.9019608, b: 0.9019608, a: 1} + m_OnHover: + m_Background: {fileID: 11004, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnActive: + m_Background: {fileID: 11002, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 24 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 28 + m_FontStyle: 0 + m_Alignment: 4 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 80 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_toggle: + m_Name: toggle + m_Normal: + m_Background: {fileID: 11018, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.89112896, g: 0.89112896, b: 0.89112896, a: 1} + m_Hover: + m_Background: {fileID: 11014, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Active: + m_Background: {fileID: 11013, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11016, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.8901961, g: 0.8901961, b: 0.8901961, a: 1} + m_OnHover: + m_Background: {fileID: 11015, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnActive: + m_Background: {fileID: 11017, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 14 + m_Right: 0 + m_Top: 14 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 15 + m_Right: 0 + m_Top: 3 + m_Bottom: 0 + m_Overflow: + m_Left: -1 + m_Right: 0 + m_Top: -4 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_label: + m_Name: label + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 1 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_textField: + m_Name: textfield + m_Normal: + m_Background: {fileID: 11024, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Hover: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnNormal: + m_Background: {fileID: 11025, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 3 + m_Right: 3 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 0 + m_TextClipping: 1 + m_ImagePosition: 3 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_textArea: + m_Name: textarea + m_Normal: + m_Background: {fileID: 11024, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9019608, g: 0.9019608, b: 0.9019608, a: 1} + m_Hover: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11025, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 3 + m_Right: 3 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 1 + m_RichText: 0 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_window: + m_Name: window + m_Normal: + m_Background: {fileID: 11023, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11022, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 8 + m_Right: 8 + m_Top: 18 + m_Bottom: 8 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 10 + m_Right: 10 + m_Top: 20 + m_Bottom: 10 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 1 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: -18} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalSlider: + m_Name: horizontalslider + m_Normal: + m_Background: {fileID: 11009, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 3 + m_Right: 3 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -2 + m_Bottom: -3 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 12 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalSliderThumb: + m_Name: horizontalsliderthumb + m_Normal: + m_Background: {fileID: 11011, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 11012, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 11010, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 7 + m_Right: 7 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 12 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalSlider: + m_Name: verticalslider + m_Normal: + m_Background: {fileID: 11021, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 3 + m_Bottom: 3 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: -1 + m_Overflow: + m_Left: -2 + m_Right: -3 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 12 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_verticalSliderThumb: + m_Name: verticalsliderthumb + m_Normal: + m_Background: {fileID: 11011, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 11012, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 11010, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 7 + m_Bottom: 7 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: -1 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 12 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_horizontalScrollbar: + m_Name: horizontalscrollbar + m_Normal: + m_Background: {fileID: 11008, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 9 + m_Right: 9 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 1 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 15 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarThumb: + m_Name: horizontalscrollbarthumb + m_Normal: + m_Background: {fileID: 11007, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 6 + m_Right: 6 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: 1 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 13 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarLeftButton: + m_Name: horizontalscrollbarleftbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarRightButton: + m_Name: horizontalscrollbarrightbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbar: + m_Name: verticalscrollbar + m_Normal: + m_Background: {fileID: 11020, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 9 + m_Bottom: 9 + m_Margin: + m_Left: 1 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 1 + m_Bottom: 1 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 15 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbarThumb: + m_Name: verticalscrollbarthumb + m_Normal: + m_Background: {fileID: 11019, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 6 + m_Bottom: 6 + m_Overflow: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 15 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_verticalScrollbarUpButton: + m_Name: verticalscrollbarupbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbarDownButton: + m_Name: verticalscrollbardownbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_ScrollView: + m_Name: scrollview + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_CustomStyles: + - m_Name: + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_Settings: + m_DoubleClickSelectsWord: 1 + m_TripleClickSelectsLine: 1 + m_CursorColor: {r: 1, g: 1, b: 1, a: 1} + m_CursorFlashSpeed: -1 + m_SelectionColor: {r: 1, g: 0.38403907, b: 0, a: 0.7} diff --git a/Assets/Sample/Assets/td_skin.guiskin.meta b/Assets/Sample/Assets/td_skin.guiskin.meta new file mode 100644 index 0000000..c4fb420 --- /dev/null +++ b/Assets/Sample/Assets/td_skin.guiskin.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 48b9be70902884a76ba1b53926fd3e37 +NativeFormatImporter: + userData: diff --git a/Assets/Sample/OtherScene.cs b/Assets/Sample/OtherScene.cs new file mode 100644 index 0000000..8710fb7 --- /dev/null +++ b/Assets/Sample/OtherScene.cs @@ -0,0 +1,32 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; +using ThinkingData.Analytics; + +public class OtherScene : MonoBehaviour +{ + // Start is called before the first frame update + void Start() + { + + } + + // Update is called once per frame + void Update() + { + + } + + void OnGUI() + { + if (GUI.Button(new Rect(20, 40, 300, 50), "Back To TDAnalyticsDemo")) + { + SceneManager.LoadScene("TDAnalyticsDemo", LoadSceneMode.Single); + } + if (GUI.Button(new Rect(20*2+300, 40, 300, 50), "TrackEvent")) + { + TDAnalytics.Track("TA", new Dictionary() { { "other_scene", "OtherScene" } }); + } + } +} diff --git a/Assets/Sample/OtherScene.cs.meta b/Assets/Sample/OtherScene.cs.meta new file mode 100644 index 0000000..12f3f26 --- /dev/null +++ b/Assets/Sample/OtherScene.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 157108d09ae574dedb267eedc3438757 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/OtherScene.unity b/Assets/Sample/OtherScene.unity new file mode 100644 index 0000000..c8a7683 --- /dev/null +++ b/Assets/Sample/OtherScene.unity @@ -0,0 +1,316 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.18018606, g: 0.22559257, b: 0.30678663, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &1123045975 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1123045978} + - component: {fileID: 1123045977} + - component: {fileID: 1123045976} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1123045976 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1123045975} + m_Enabled: 0 +--- !u!20 &1123045977 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1123045975} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.0039215684, g: 0.3612774, b: 1, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1123045978 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1123045975} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + 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!1 &1248963544 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1248963546} + - component: {fileID: 1248963545} + - component: {fileID: 1248963547} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1248963545 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1248963544} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 0.003921569, g: 0.28235295, b: 1, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1248963546 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1248963544} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!114 &1248963547 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1248963544} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 157108d09ae574dedb267eedc3438757, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/Assets/Sample/OtherScene.unity.meta b/Assets/Sample/OtherScene.unity.meta new file mode 100644 index 0000000..a39b6ff --- /dev/null +++ b/Assets/Sample/OtherScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 667cb3338b0414099b222ac71a908e8e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/TAExample.cs b/Assets/Sample/TAExample.cs new file mode 100644 index 0000000..cd2827a --- /dev/null +++ b/Assets/Sample/TAExample.cs @@ -0,0 +1,384 @@ +using UnityEngine; +using UnityEngine.SceneManagement; +using ThinkingAnalytics; +using System.Collections.Generic; +using System; + +public class TAExample : MonoBehaviour, IDynamicSuperProperties, IAutoTrackEventCallback +{ + + + public GUISkin skin; + private Vector2 scrollPosition = Vector2.zero; + //private static Color MainColor = new Color(0, 0,0); + private static Color MainColor = new Color(84f / 255, 116f / 255, 241f / 255); + private static Color TextColor = new Color(153f / 255, 153f / 255, 153f / 255); + static int Margin = 20; + static int Height = 60; + static float ContainerWidth = Screen.width - 2 * Margin; + // dynamic super properties interface implementation + public Dictionary GetDynamicSuperProperties() + { + return new Dictionary() + { + {"dynamic_property", DateTime.Now} + }; + } + // auto-tracking events interface implementation + public Dictionary AutoTrackEventCallback(int type, Dictionaryproperties) + { + return new Dictionary() + { + {"auto_track_dynamic", DateTime.Today} + }; + } + + private void Awake() + { + } + private void Start() + { + // When automatically initializing ThinkingAnalytics, you can call EnableAutoTrack() in Start() to enable auto-tracking events + // enable auto-tracking events + // ThinkingAnalyticsAPI.EnableAutoTrack(AUTO_TRACK_EVENTS.ALL); + } + + void OnGUI() + { + GUILayout.BeginArea(new Rect(Margin, Screen.height * 0.15f, Screen.width-2*Margin, Screen.height)); + scrollPosition = GUILayout.BeginScrollView(new Vector2(0, 0), GUILayout.Width(Screen.width - 2 * Margin), GUILayout.Height(Screen.height - 100)); + GUIStyle style = GUI.skin.label; + style.fontSize = 25; + GUILayout.Label("Initialization / UserIDSetting",style); + + GUIStyle buttonStyle = GUI.skin.button; + buttonStyle.fontSize = 20; + GUILayout.BeginHorizontal(GUI.skin.box,GUILayout.Height(Height)); + if (GUILayout.Button("ManualInitialization", GUILayout.Height(Height))) + { + // 1. Manual initialization (ThinkingAnalytics prefab loaded) + // ThinkingAnalyticsAPI.StartThinkingAnalytics(); + + + // 2. Manual initialization (dynamically loading ThinkingAnalyticsAPI script) + new GameObject("ThinkingAnalytics", typeof(ThinkingAnalyticsAPI)); + + // 2.1 Set instance parameters + string appId = "22e445595b0f42bd8c5fe35bc44b88d6"; + string serverUrl = "https://receiver-ta-dev.thinkingdata.cn"; + ThinkingAnalyticsAPI.StartThinkingAnalytics(appId, serverUrl); + + // 2.1 Set personalized instance parameters + // string appId = "22e445595b0f42bd8c5fe35bc44b88d6"; + // string serverUrl = "https://receiver-ta-dev.thinkingdata.cn"; + // ThinkingAnalyticsAPI.TAMode mode = ThinkingAnalyticsAPI.TAMode.NORMAL; + // ThinkingAnalyticsAPI.TATimeZone timeZone = ThinkingAnalyticsAPI.TATimeZone.Local; + // ThinkingAnalyticsAPI.Token token = new ThinkingAnalyticsAPI.Token(appId, serverUrl, mode, timeZone); + // Enable encrypted transmission (only iOS/Android) + // token.enableEncrypt = true; + // token.encryptVersion = 0; + // token.encryptPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIPi6aHymT1jdETRci6f1ck535n13IX3p9XNLFu5xncfzNFl6kFVMiMSXMIwWSW2lF6ELtIlDJ0B00qE9C02n6YbIAV+VvVkchydbWrm8VdnEJk/6tIydoUxGyM9pDT6U/PaoEiItl/BawDj3/+KW6U7AejYPij9uTQ4H3bQqj1wIDAQAB"; + // ThinkingAnalyticsAPI.StartThinkingAnalytics(token); + + // 2.2 Multi-project support + // string appId_2 = "1b1c1fef65e3482bad5c9d0e6a823356"; + // string serverUrl_2 = "https://receiver-ta-dev.thinkingdata.cn"; + // ThinkingAnalyticsAPI.TAMode mode_2 = ThinkingAnalyticsAPI.TAMode.NORMAL; + // ThinkingAnalyticsAPI.TATimeZone timeZone_2 = ThinkingAnalyticsAPI.TATimeZone.Local; + // ThinkingAnalyticsAPI.Token token_2 = new ThinkingAnalyticsAPI.Token(appId_2, serverUrl_2, mode_2, timeZone_2); + + // ThinkingAnalyticsAPI.Token[] tokens = new ThinkingAnalyticsAPI.Token[2]; + // tokens[0] = token; + // tokens[1] = token_2; + // ThinkingAnalyticsAPI.StartThinkingAnalytics(tokens); + + // Multi-item track events + // ThinkingAnalyticsAPI.Track("test_event"); + // ThinkingAnalyticsAPI.Track("test_event_2", appId:appId_2); + + + // Enable auto-tracking events + // ThinkingAnalyticsAPI.EnableAutoTrack(AUTO_TRACK_EVENTS.ALL); + // Enable auto-tracking events, and set properties + // ThinkingAnalyticsAPI.SetAutoTrackProperties(AUTO_TRACK_EVENTS.ALL, new Dictionary() + // { + // {"auto_track_static_1", "value_1"} + // }); + // ThinkingAnalyticsAPI.EnableAutoTrack(AUTO_TRACK_EVENTS.ALL, new Dictionary() + // { + // {"auto_track_static_2", "value_2"} + // }); + // Enable auto-tracking events, and set callback + // ThinkingAnalyticsAPI.EnableAutoTrack(AUTO_TRACK_EVENTS.ALL, this); + } + GUILayout.Space(20); + if (GUILayout.Button("SetAccountID", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.Login("TA"); + } + GUILayout.Space(20); + if (GUILayout.Button("SetDistinctID", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.Identify("TA_DistinctID"); + Debug.Log("DistinctID: " + ThinkingAnalyticsAPI.GetDistinctId()); + } + GUILayout.Space(20); + if (GUILayout.Button("ClearAccountID", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.Logout(); + } + GUILayout.EndHorizontal(); + + GUILayout.Space(20); + GUILayout.Label("EventTracking", GUI.skin.label); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("TrackEvent", GUILayout.Height(Height))) + { + Dictionary properties = new Dictionary(); + properties["channel"] = "ta";//string + properties["age"] = 1;//number - int + properties["weight"] = 5.46;//number - float + properties["balance"] = -0.4;//number - negative + properties["isVip"] = true;//bool + properties["birthday"] = new DateTime(2022,01,01);//date + properties["object"] = new Dictionary() { { "key", "value" } };//object + properties["object_arr"] = new List() { new Dictionary() { { "key", "value" } } };//object array + properties["arr"] = new List() { "value" };//array + + ThinkingAnalyticsAPI.Track("TA", properties); + } + GUILayout.Space(20); + if (GUILayout.Button("TrackFirstEvent", GUILayout.Height(Height))) + { + Dictionary properties = new Dictionary(); + properties["status"] = 1; + TDFirstEvent firstEvent = new TDFirstEvent("DEVICE_FIRST", properties); + ThinkingAnalyticsAPI.Track(firstEvent); + } + GUILayout.Space(20); + if (GUILayout.Button("TrackUpdateEvent", GUILayout.Height(Height))) + { + Dictionary properties = new Dictionary(); + properties["status"] = 2; + TDUpdatableEvent updatableEvent = new TDUpdatableEvent("UPDATABLE_EVENT", properties, "test_event_id"); + updatableEvent.EventTime = DateTime.Now; + updatableEvent.EventTimeZone = TimeZoneInfo.Local; + ThinkingAnalyticsAPI.Track(updatableEvent); + } + + GUILayout.Space(20); + if (GUILayout.Button("TrackOverwriteEvent", GUILayout.Height(Height))) + { + Dictionary properties = new Dictionary(); + properties["status"] = 3; + TDOverWritableEvent overWritableEvent = new TDOverWritableEvent("OVERWRITABLE_EVENT", properties, "test_event_id"); + overWritableEvent.EventTime = DateTime.Now; + overWritableEvent.EventTimeZone = TimeZoneInfo.Utc; + ThinkingAnalyticsAPI.Track(overWritableEvent); + } + + GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("TimeEvent", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.TimeEvent("TATimeEvent"); + } + + GUILayout.Space(20); + if (GUILayout.Button("Track-TimeEvent", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.Track("TATimeEvent"); + } + + GUILayout.Space(20); + if (GUILayout.Button("TrackEventWithDate", GUILayout.Height(Height))) + { + Dictionary properties = new Dictionary(); + ThinkingAnalyticsAPI.Track("TA_Utc", properties, DateTime.Now.AddDays(1), TimeZoneInfo.Utc); + ThinkingAnalyticsAPI.Track("TA_Local", properties, DateTime.Now.AddDays(1), TimeZoneInfo.Local); + } + + GUILayout.Space(20); + if (GUILayout.Button("TrackEventWithLightInstance", GUILayout.Height(Height))) + { + string lightToken = ThinkingAnalyticsAPI.CreateLightInstance(); + ThinkingAnalyticsAPI.Login("light_account", lightToken); + ThinkingAnalyticsAPI.Track("light_event", lightToken); + } + GUILayout.EndHorizontal(); + + + + + GUILayout.Space(20); + GUILayout.Label("UserProperty", GUI.skin.label); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("UserSet", GUILayout.Height(Height))) + { + Dictionary userProperties = new Dictionary(); + userProperties["age"] = 1; + ThinkingAnalyticsAPI.UserSet(userProperties); + } + + GUILayout.Space(20); + if (GUILayout.Button("UserSetOnce", GUILayout.Height(Height))) + { + Dictionary userProperties = new Dictionary(); + userProperties["gender"] = 1; + ThinkingAnalyticsAPI.UserSetOnce(userProperties); + + } + GUILayout.Space(20); + if (GUILayout.Button("UserAdd", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.UserAdd("usercoin", 1); + } + GUILayout.Space(20); + if (GUILayout.Button("UserUnset", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.UserUnset("usercoin"); + } + GUILayout.Space(20); + if (GUILayout.Button("UserDelete", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.UserDelete(); + } + GUILayout.Space(20); + if (GUILayout.Button("UserAppend", GUILayout.Height(Height))) + { + List propList = new List(); + propList.Add("ball"); + Dictionary userProperties = new Dictionary(); + userProperties["prop"] = propList; + ThinkingAnalyticsAPI.UserAppend(userProperties); + } + GUILayout.Space(20); + if (GUILayout.Button("UserUniqAppend", GUILayout.Height(Height))) + { + List propList = new List(); + propList.Add("apple"); + Dictionary userProperties = new Dictionary(); + userProperties["prop"] = propList; + ThinkingAnalyticsAPI.UserUniqAppend(userProperties); + } + GUILayout.EndHorizontal(); + + + + GUILayout.Space(20); + GUILayout.Label("OtherSetting", GUI.skin.label); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("Flush", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.Flush(); + } + GUILayout.Space(20); + if (GUILayout.Button("GetDeviceID", GUILayout.Height(Height))) + { + Debug.Log("DeviceID: " + ThinkingAnalyticsAPI.GetDeviceId()); + } + GUILayout.Space(20); + if (GUILayout.Button("Pause", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.SetTrackStatus(TA_TRACK_STATUS.PAUSE); + } + GUILayout.Space(20); + if (GUILayout.Button("Stop", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.SetTrackStatus(TA_TRACK_STATUS.STOP); + } + GUILayout.Space(20); + if (GUILayout.Button("SaveOnly", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.SetTrackStatus(TA_TRACK_STATUS.SAVE_ONLY); + } + GUILayout.Space(20); + if (GUILayout.Button("Normal", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.SetTrackStatus(TA_TRACK_STATUS.NORMAL); + } + GUILayout.Space(20); + if (GUILayout.Button("CalibrateTime", GUILayout.Height(Height))) + { + //currnt Unix timestamp, units Ms, e.g: 1608782412000 -> 2020-12-24 12:00:12 + ThinkingAnalyticsAPI.CalibrateTime(1608782412000); + + //NTP server, e.g: time.apple.com + ThinkingAnalyticsAPI.CalibrateTimeWithNtp("time.apple.com"); + //ThinkingAnalyticsAPI.CalibrateTimeWithNtp("time1.google.com"); + } + GUILayout.EndHorizontal(); + + GUILayout.Space(20); + GUILayout.Label("PropertiesSetting", GUI.skin.label); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("SetSuperProperties", GUILayout.Height(Height))) + { + Dictionary superProperties = new Dictionary(); + superProperties["VipLevel"] = 1; + superProperties["VipTitle"] = "Supreme King"; + ThinkingAnalyticsAPI.SetSuperProperties(superProperties); + } + GUILayout.Space(20); + if (GUILayout.Button("UpdateSuperProperties", GUILayout.Height(Height))) + { + Dictionary superProperties = new Dictionary(); + superProperties["VipLevel"] = 2; + ThinkingAnalyticsAPI.SetSuperProperties(superProperties); + } + + GUILayout.Space(20); + if (GUILayout.Button("ClearSuperProperties", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.UnsetSuperProperty("VipLevel"); + } + + GUILayout.Space(20); + if (GUILayout.Button("ClearAllSuperProperties", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.ClearSuperProperties(); + } + + GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("SetDynamicSuperProperties", GUILayout.Height(Height))) + { + ThinkingAnalyticsAPI.SetDynamicSuperProperties(this); + } + + GUILayout.Space(20); + if (GUILayout.Button("GetSuperProperties", GUILayout.Height(Height))) + { + Dictionary eventSuperProperties = ThinkingAnalyticsAPI.GetSuperProperties(); + string propertiesStr = " "; + foreach (KeyValuePair kv in eventSuperProperties) + { + propertiesStr = propertiesStr + kv.Key + " = " + kv.Value + "\n "; + } + Debug.Log("SuperProperties: \n" + propertiesStr); + } + GUILayout.Space(20); + if (GUILayout.Button("GetPresetProperties", GUILayout.Height(Height))) + { + TDPresetProperties presetProperties = ThinkingAnalyticsAPI.GetPresetProperties(); + string deviceModel = presetProperties.DeviceModel; + Debug.Log("TDPresetProperties DeviceModel is " + deviceModel); + Dictionary eventPresetProperties = presetProperties.ToEventPresetProperties(); + string propertiesStr = " "; + foreach (KeyValuePair kv in eventPresetProperties) + { + propertiesStr = propertiesStr + kv.Key + " = " + kv.Value + "\n "; + } + Debug.Log("PresetProperties: \n" + propertiesStr); + } + + GUILayout.Space(20); + if (GUILayout.Button("LoadScene", GUILayout.Height(Height))) + { + SceneManager.LoadScene("OtherScene", LoadSceneMode.Single); + } + GUILayout.EndHorizontal(); + GUILayout.EndScrollView(); + GUILayout.EndArea(); + } +} diff --git a/Assets/Sample/TAExample.cs.meta b/Assets/Sample/TAExample.cs.meta new file mode 100644 index 0000000..87cf226 --- /dev/null +++ b/Assets/Sample/TAExample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 87d98871b303b4786b3dac3fd1b6e6eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/TDAnalyticsDemo.cs b/Assets/Sample/TDAnalyticsDemo.cs new file mode 100644 index 0000000..39e49f5 --- /dev/null +++ b/Assets/Sample/TDAnalyticsDemo.cs @@ -0,0 +1,518 @@ +using UnityEngine; +using UnityEngine.SceneManagement; +using ThinkingData.Analytics; +using System.Collections.Generic; +using System; +using System.Threading; +using System.Collections; +using ThinkingAnalytics; +public class TDAnalyticsDemo : MonoBehaviour, TDDynamicSuperPropertiesHandler, TDAutoTrackEventHandler +{ + + + public GUISkin skin; + private Vector2 scrollPosition = Vector2.zero; + //private static Color MainColor = new Color(0, 0,0); + private static Color MainColor = new Color(84f / 255, 116f / 255, 241f / 255); + private static Color TextColor = new Color(153f / 255, 153f / 255, 153f / 255); + static int Margin = 20; + static int Height = 60; + static float ContainerWidth = Screen.width - 2 * Margin; + // dynamic super properties interface implementation + public Dictionary GetDynamicSuperProperties() + { + Thread currentThread = Thread.CurrentThread; + // 输出当前线程的信息 + Debug.Log("当前线程ID: " + currentThread.ManagedThreadId); + return new Dictionary() + { + {"dynamic_property", DateTime.Now}, + {"dynamic_property1", DateTime.Now}, + {"dynamic_property2", DateTime.Now}, + {"dynamic_property3", DateTime.Now}, + {"dynamicTime4", DateTime.Now} + }; + } + // auto-tracking events interface implementation + public Dictionary GetAutoTrackEventProperties(int type, Dictionaryproperties) + { + return new Dictionary() + { + {"auto_track_dynamic", DateTime.Today} + }; + } + + private void Awake() + { + } + private void Start() + { + // When automatically initializing ThinkingAnalytics, you can call EnableAutoTrack() in Start() to enable auto-tracking events + // enable auto-tracking events + // TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.ALL); + } + private void Update() + { + + } + + private void SampleDemo() + { + //Init SDK + string appId = "your-app-id"; + string serverUrl = "https://your.server.url"; + TDAnalytics.Init(appId, serverUrl); + + //Login SDK + TDAnalytics.Login("Tiki"); + + //Set Super Properties + //Dictionary superProperties = new Dictionary() { + // { "channel", "Apple Store" }, + // { "vip_level", 10 }, + // { "is_svip", true } + //}; + //TDAnalytics.SetSuperProperties(superProperties); + + + //Track Event + Dictionary eventProperties = new Dictionary() + { + { "product_name", "Majin Taito" }, + { "product_price", 6 }, + { "is_on_sale", true }, + { "begin_time", DateTime.Now }, + { "skins_name", new List() { + "Master Alchemist", + "Knights of the Round Table", + "Taotie", + "Glam Rock" + } } + }; + TDAnalytics.Track("product_buy", eventProperties); + + + //Track User Properties + Dictionary userProperties = new Dictionary() + { + { "email", "tiki@thinkingdata.cn" }, + { "diamond", 888 }, + { "is_svip", true }, + { "last_payment_time", DateTime.Now }, + { "owned_skins", new List() { + "Gun of Travel", + "Bow of Demigods", + "Golden Sagittarius" + } } + }; + TDAnalytics.UserSet(userProperties); + + } + + void OnGUI() + { + GUILayout.BeginArea(new Rect(Margin, Screen.height * 0.15f, Screen.width-2*Margin, Screen.height)); + scrollPosition = GUILayout.BeginScrollView(new Vector2(0, 0), GUILayout.Width(Screen.width - 2 * Margin), GUILayout.Height(Screen.height - 100)); + GUIStyle style = GUI.skin.label; + style.fontSize = 25; + GUILayout.Label("Initialization / UserIDSetting",style); + + GUIStyle buttonStyle = GUI.skin.button; + buttonStyle.fontSize = 20; + GUILayout.BeginHorizontal(GUI.skin.box,GUILayout.Height(Height)); + if (GUILayout.Button("ManualInitialization", GUILayout.Height(Height))) + { + // 1. Manual initialization (ThinkingAnalytics prefab loaded) + //TDAnalytics.Init(); + + + // 2. Manual initialization (dynamically loading TDAnalytics script) + //this.gameObject.AddComponent(); + + // 2.1 Set instance parameters + //string appId = "22e445595b0f42bd8c5fe35bc44b88d6"; + //string serverUrl = "https://receiver-ta-dev.thinkingdata.cn"; + //TDAnalytics.Init(appId, serverUrl); + + + // 2.1 Set personalized instance parameters + string appId = "1b1c1fef65e3482bad5c9d0e6a823356"; + string serverUrl = "https://receiver.ta.thinkingdata.cn"; + TDConfig tDConfig = new TDConfig(appId, serverUrl); + //tDConfig.mode = TDMode.Normal; + tDConfig.timeZone = TDTimeZone.Asia_Shanghai; + //Enable encrypted transmission(only iOS / Android) + int encryptVersion = 0; + string encryptPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCIPi6aHymT1jdETRci6f1ck535n13IX3p9XNLFu5xncfzNFl6kFVMiMSXMIwWSW2lF6ELtIlDJ0B00qE9C02n6YbIAV+VvVkchydbWrm8VdnEJk/6tIydoUxGyM9pDT6U/PaoEiItl/BawDj3/+KW6U7AejYPij9uTQ4H3bQqj1wIDAQAB"; + tDConfig.EnableEncrypt(encryptPublicKey, encryptVersion); + TDAnalytics.Init(tDConfig); + //TDAnalytics.SetNetworkType(TDNetworkType.Wifi); + TDAnalytics.SetDynamicSuperProperties(this); + //TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.AppInstall | TDAutoTrackEventType.AppStart | TDAutoTrackEventType.AppEnd); + TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.All); + + + //new GameObject("ThinkingAnalytics", typeof(ThinkingAnalyticsAPI)); + //string appId = "40eddce753cd4bef9883a01e168c3df0"; + //string serverUrl = "https://receiver-ta-preview.thinkingdata.cn"; + //ThinkingAnalyticsAPI.StartThinkingAnalytics(appId, serverUrl); + //ThinkingAnalyticsAPI.SetNetworkType(ThinkingAnalyticsAPI.NetworkType.WIFI); + //ThinkingAnalyticsAPI.EnableAutoTrack(AUTO_TRACK_EVENTS.ALL); + + // 2.2 Multi-project support + // string appId_2 = "cf918051b394495ca85d1b7787ad7243"; + // string serverUrl_2 = "https://receiver-ta-dev.thinkingdata.cn"; + // TDConfig tDConfig_2 = new TDConfig(appId_2, serverUrl_2); + + // TDAnalytics.Init(tDConfig_2); + + // Multi-item track events + // TDAnalytics.Track("test_event"); + // TDAnalytics.Track("test_event_2", appId:appId_2); + + + // Enable auto-tracking events + //TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.All); + // Enable auto-tracking events, and set properties + // TDAnalytics.SetAutoTrackProperties(TDAutoTrackEventType.All, new Dictionary() + // { + // {"auto_track_static_1", "value_1"} + // }); + // TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.All, new Dictionary() + // { + // {"auto_track_static_2", "value_2"} + // }); + // Enable auto-tracking events, and set callback + // TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.All, this); + } + GUILayout.Space(20); + if (GUILayout.Button("SetAccountID", GUILayout.Height(Height))) + { + TDAnalytics.Login("TA"); + } + GUILayout.Space(20); + if (GUILayout.Button("SetDistinctID", GUILayout.Height(Height))) + { + TDAnalytics.SetDistinctId("TD_DistinctID"); + string distinctId = TDAnalytics.GetDistinctId(); + Debug.Log("Current Distinct ID is : " + distinctId); + } + GUILayout.Space(20); + if (GUILayout.Button("ClearAccountID", GUILayout.Height(Height))) + { + TDAnalytics.Logout(); + } + GUILayout.EndHorizontal(); + + GUILayout.Space(20); + GUILayout.Label("EventTracking", GUI.skin.label); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("TrackEvent", GUILayout.Height(Height))) + { + Debug.Log("======1" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); + //Dictionary properties = new Dictionary(); + //properties["channel"] = "ta";//string + //properties["age"] = 1;//number - int + //properties["weight"] = 5.46;//number - float + //properties["balance"] = -0.4;//number - negative + //properties["isVip"] = true;//bool + //properties["birthday"] = new DateTime(2022,01,01);//date + //properties["birthday1"] = new DateTime();//date + //properties["birthday2"] = new DateTime(2023, 05, 01);//date + //properties["birthday3"] = new DateTime(2023, 05, 03);//date + //properties["object"] = new Dictionary() { { "key", "value" },{ "key1", DateTime.Now }, { "key2", DateTime.Now } };//object + //properties["object_arr"] = new List() { new Dictionary() { { "key", "value" }, { "key3", DateTime.Now }, { "key4", DateTime.Now } } };//object array + //properties["arr"] = new List() { "value" };//array + //TDAnalytics.Track("TA", properties); + Decimal a = 1; + Dictionary properties = new Dictionary(); + properties["channel"] = "ta";//string + properties["age"] = 1;//number - int + properties["weight"] = 5.46;//number - float + properties["balance"] = -0.4;//number - negative + properties["isVip"] = true;//bool + properties["date1"] = new DateTime(); + properties["date2"] = new DateTime(); + properties["date3"] = new DateTime(); + properties["date4"] = new DateTime(); + properties["date5"] = new DateTime(); + properties["date6"] = DateTime.Now; + properties["num"] = a; + properties["birthday"] = new DateTime(2022, 01, 01);//date + properties["object"] = new Dictionary() { { "key", "value" }, { "data1", new DateTime() }, { "data2", new DateTime() }, { "data3", new DateTime() }, { "data4", new DateTime() }, { "data5", new DateTime() } };//object + properties["object_arr"] = new List() { new Dictionary() { { "key", "value" }, { "data1", new DateTime() }, { "data2", new DateTime() }, { "data3", new DateTime() }, { "data4", new DateTime() }, { "data5", new DateTime() } } };//object array + properties["arr"] = new List() { "value", new DateTime(), new DateTime(), new DateTime(), new DateTime(), new DateTime() };//array + TDAnalytics.Track("TA", properties); + Debug.Log("======2" + DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); + } + GUILayout.Space(20); + if (GUILayout.Button("TrackFirstEvent", GUILayout.Height(Height))) + { + Dictionary properties = new Dictionary() { { "status",1 } }; + TDFirstEventModel firstEvent = new TDFirstEventModel("first_event"); + firstEvent.Properties = properties; + TDAnalytics.Track(firstEvent); + Dictionary properties_2 = new Dictionary() { { "status", 11 } }; + TDFirstEventModel firstEvent_2 = new TDFirstEventModel("first_event", "first_check_id"); + firstEvent_2.Properties = properties_2; + TDAnalytics.Track(firstEvent_2); + } + GUILayout.Space(20); + if (GUILayout.Button("TrackUpdateEvent", GUILayout.Height(Height))) + { + Dictionary properties = new Dictionary() { { "status", 2 } }; + TDUpdatableEventModel updatableEvent = new TDUpdatableEventModel("updatable_event", "test_event_id"); + updatableEvent.Properties = properties; + updatableEvent.SetTime(DateTime.Now, TimeZoneInfo.Local); + TDAnalytics.Track(updatableEvent); + } + + GUILayout.Space(20); + if (GUILayout.Button("TrackOverwriteEvent", GUILayout.Height(Height))) + { + Dictionary properties = new Dictionary() { { "status", 3 } }; + TDOverwritableEventModel overWritableEvent = new TDOverwritableEventModel("overwritable_event", "test_event_id"); + overWritableEvent.Properties = properties; + overWritableEvent.SetTime(DateTime.Now, TimeZoneInfo.Utc); + TDAnalytics.Track(overWritableEvent); + } + + GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("TimeEvent", GUILayout.Height(Height))) + { + TDAnalytics.TimeEvent("TATimeEvent"); + } + + GUILayout.Space(20); + if (GUILayout.Button("Track-TimeEvent", GUILayout.Height(Height))) + { + TDAnalytics.Track("TATimeEvent"); + } + + GUILayout.Space(20); + if (GUILayout.Button("TrackEventWithDate", GUILayout.Height(Height))) + { + Dictionary properties = new Dictionary(); + TDAnalytics.Track("TA_Utc", properties, DateTime.Now.AddDays(1), TimeZoneInfo.Utc); + TDAnalytics.Track("TA_Local", properties, DateTime.Now.AddDays(1), TimeZoneInfo.Local); + } + + GUILayout.Space(20); + if (GUILayout.Button("TrackEvent-LightInstance", GUILayout.Height(Height))) + { + string lightToken = TDAnalytics.LightInstance(); + TDAnalytics.Login("light_account", lightToken); + TDAnalytics.Track("light_event", lightToken); + TDAnalytics.Flush(); + } + GUILayout.Space(20); + if (GUILayout.Button("TrackEvent-MultiInstance", GUILayout.Height(Height))) + { + string appId_2 = "cf918051b394495ca85d1b7787ad7243"; + string serverUrl_2 = "https://receiver-ta-dev.thinkingdata.cn"; + TDConfig token_2 = new TDConfig(appId_2, serverUrl_2); + token_2.mode = TDMode.Normal; + token_2.timeZone = TDTimeZone.UTC; + // initial multi-instance + TDAnalytics.Init(token_2); + // login account + TDAnalytics.Login("Tiki", appId_2); + // track normal event + TDAnalytics.Track("TA", appId_2); + // track user properties + TDAnalytics.UserSet(new Dictionary() { { "age", 18 } }, appId_2); + // flush data + TDAnalytics.Flush(appId_2); + + string instanceName = "ThinkingData"; + TDConfig token_3 = new TDConfig(appId_2, serverUrl_2); + token_3.name = instanceName; + // initial multi-instance + TDAnalytics.Init(token_3); + // login account + TDAnalytics.Login("Thinker", instanceName); + // track normal event + TDAnalytics.Track("TA", instanceName); + // track user properties + TDAnalytics.UserSet(new Dictionary() { { "age", 18 } }, instanceName); + // flush data + TDAnalytics.Flush(instanceName); + } + GUILayout.EndHorizontal(); + + + + + GUILayout.Space(20); + GUILayout.Label("UserProperty", GUI.skin.label); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("UserSet", GUILayout.Height(Height))) + { + Dictionary userProperties = new Dictionary() { { "age", 18 } }; + TDAnalytics.UserSet(userProperties); + } + + GUILayout.Space(20); + if (GUILayout.Button("UserSetOnce", GUILayout.Height(Height))) + { + Dictionary userProperties = new Dictionary() { { "gender", 1 } }; + TDAnalytics.UserSetOnce(userProperties); + + } + GUILayout.Space(20); + if (GUILayout.Button("UserAdd", GUILayout.Height(Height))) + { + TDAnalytics.UserAdd("user_coin", 1); + } + GUILayout.Space(20); + if (GUILayout.Button("UserUnset", GUILayout.Height(Height))) + { + TDAnalytics.UserUnset("user_coin"); + TDAnalytics.UserUnset(new List() { "user_coin", "user_vip" }); + } + GUILayout.Space(20); + if (GUILayout.Button("UserDelete", GUILayout.Height(Height))) + { + TDAnalytics.UserDelete(); + } + GUILayout.Space(20); + if (GUILayout.Button("UserAppend", GUILayout.Height(Height))) + { + List propList = new List() { "apple", "ball" }; + Dictionary userProperties = new Dictionary() { { "prop", propList } }; + TDAnalytics.UserAppend(userProperties); + } + GUILayout.Space(20); + if (GUILayout.Button("UserUniqAppend", GUILayout.Height(Height))) + { + List propList = new List() { "apple", "banana" }; + Dictionary userProperties = new Dictionary() { { "prop", propList } }; + TDAnalytics.UserUniqAppend(userProperties); + } + GUILayout.EndHorizontal(); + + + + GUILayout.Space(20); + GUILayout.Label("OtherSetting", GUI.skin.label); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("Flush", GUILayout.Height(Height))) + { + TDAnalytics.Flush(); + } + GUILayout.Space(20); + if (GUILayout.Button("GetDeviceID", GUILayout.Height(Height))) + { + string deviceId = TDAnalytics.GetDeviceId(); + Debug.Log("Current Device ID is : " + deviceId); + } + GUILayout.Space(20); + if (GUILayout.Button("Pause", GUILayout.Height(Height))) + { + TDAnalytics.SetTrackStatus(TDTrackStatus.Pause); + } + GUILayout.Space(20); + if (GUILayout.Button("Stop", GUILayout.Height(Height))) + { + TDAnalytics.SetTrackStatus(TDTrackStatus.Stop); + } + GUILayout.Space(20); + if (GUILayout.Button("SaveOnly", GUILayout.Height(Height))) + { + TDAnalytics.SetTrackStatus(TDTrackStatus.SaveOnly); + } + GUILayout.Space(20); + if (GUILayout.Button("Normal", GUILayout.Height(Height))) + { + TDAnalytics.SetTrackStatus(TDTrackStatus.Normal); + } + GUILayout.Space(20); + if (GUILayout.Button("CalibrateTime", GUILayout.Height(Height))) + { + //currnt Unix timestamp, units Ms, e.g: 1672531200000 -> 2023-01-01 08:00:00 + TDAnalytics.CalibrateTime(1672531200000); + } + GUILayout.Space(20); + if (GUILayout.Button("CalibrateTime-NTP", GUILayout.Height(Height))) + { + //NTP server, e.g: time.apple.com + TDAnalytics.CalibrateTimeWithNtp("time.apple.com"); + } + GUILayout.EndHorizontal(); + + GUILayout.Space(20); + GUILayout.Label("PropertiesSetting", GUI.skin.label); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("SetSuperProperties", GUILayout.Height(Height))) + { + Dictionary superProperties = new Dictionary() { + { "vip_level", 1 }, + { "vip_title", "Supreme King" } + }; + TDAnalytics.SetSuperProperties(superProperties); + } + GUILayout.Space(20); + if (GUILayout.Button("UpdateSuperProperties", GUILayout.Height(Height))) + { + Dictionary superProperties = new Dictionary() { + { "vip_level", 2 } + }; + TDAnalytics.SetSuperProperties(superProperties); + } + + GUILayout.Space(20); + if (GUILayout.Button("ClearSuperProperties", GUILayout.Height(Height))) + { + TDAnalytics.UnsetSuperProperty("vip_level"); + } + + GUILayout.Space(20); + if (GUILayout.Button("ClearAllSuperProperties", GUILayout.Height(Height))) + { + TDAnalytics.ClearSuperProperties(); + } + + GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(GUI.skin.textArea, GUILayout.Height(Height)); + if (GUILayout.Button("SetDynamicSuperProperties", GUILayout.Height(Height))) + { + TDAnalytics.SetDynamicSuperProperties(this); + } + + GUILayout.Space(20); + if (GUILayout.Button("GetSuperProperties", GUILayout.Height(Height))) + { + Dictionary eventSuperProperties = TDAnalytics.GetSuperProperties(); + string propertiesStr = " "; + foreach (KeyValuePair kv in eventSuperProperties) + { + propertiesStr = propertiesStr + kv.Key + " = " + kv.Value + "\n "; + } + Debug.Log("SuperProperties: \n" + propertiesStr); + } + GUILayout.Space(20); + if (GUILayout.Button("GetPresetProperties", GUILayout.Height(Height))) + { + //TDPresetProperties presetProperties = TDAnalytics.GetPresetProperties(); + //string deviceModel = presetProperties.DeviceModel; + //Debug.Log("TDPresetProperties: DeviceModel is " + deviceModel); + //Dictionary eventPresetProperties = presetProperties.ToDictionary(); + //string propertiesStr = " "; + //foreach (KeyValuePair kv in eventPresetProperties) + //{ + // propertiesStr = propertiesStr + kv.Key + " = " + kv.Value + "\n "; + //} + //Debug.Log("PresetProperties: \n" + propertiesStr); + } + + GUILayout.Space(20); + if (GUILayout.Button("LoadScene", GUILayout.Height(Height))) + { + SceneManager.LoadScene("OtherScene", LoadSceneMode.Single); + } + GUILayout.EndHorizontal(); + GUILayout.EndScrollView(); + GUILayout.EndArea(); + } +} diff --git a/Assets/Sample/TDAnalyticsDemo.cs.meta b/Assets/Sample/TDAnalyticsDemo.cs.meta new file mode 100644 index 0000000..bd3b0fa --- /dev/null +++ b/Assets/Sample/TDAnalyticsDemo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc3b54d5c2d4a455fbc56ab57ca985c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/TDAnalyticsDemo.unity b/Assets/Sample/TDAnalyticsDemo.unity new file mode 100644 index 0000000..8923cff --- /dev/null +++ b/Assets/Sample/TDAnalyticsDemo.unity @@ -0,0 +1,513 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_AmbientEquatorColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_AmbientGroundColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 3 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &4 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 1 + m_BakeResolution: 50 + m_AtlasSize: 1024 + m_AO: 1 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 0 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 1 + m_BakeBackend: 0 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 512 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 0 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 679135154} +--- !u!196 &5 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666666 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &269916751 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 269916752} + - component: {fileID: 269916753} + m_Layer: 0 + m_Name: td_logo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &269916752 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 269916751} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 4, z: 0} + m_LocalScale: {x: 1.3, y: 1.3, z: 1.3} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1608319071} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &269916753 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 269916751} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 21300000, guid: 1c8be89e83f14479081250d0c4806b85, type: 3} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 5, y: 1.89} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !u!1001 &278149140 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_RootOrder + value: 2 + objectReference: {fileID: 0} + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 57277738888238098, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1362306111410183020, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: enableLog + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1362306111410183020, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: networkType + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1362306111410183020, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: startManually + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1362306111410183020, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: tokens.Array.data[0].mode + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 1362306111410183020, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: tokens.Array.data[0].appid + value: cf918051b394495ca85d1b7787ad7243 + objectReference: {fileID: 0} + - target: {fileID: 1362306111410183020, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: tokens.Array.data[0].timeZone + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 1362306111410183020, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: tokens.Array.data[0].serverUrl + value: https://receiver-ta-dev.thinkingdata.cn + objectReference: {fileID: 0} + - target: {fileID: 3471622134282543401, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_Name + value: TDAnalytics + objectReference: {fileID: 0} + - target: {fileID: 3471622134282543401, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} + propertyPath: m_IsActive + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 986ad473b78d14e08b1f05d368cb1591, type: 3} +--- !u!1 &541060113 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 541060118} + - component: {fileID: 541060117} + - component: {fileID: 541060115} + - component: {fileID: 541060114} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &541060114 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 541060113} + m_Enabled: 1 +--- !u!124 &541060115 +Behaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 541060113} + m_Enabled: 1 +--- !u!20 &541060117 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 541060113} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 0.32941177, g: 0.45490196, b: 0.94509804, a: 0.019607844} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 0 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &541060118 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 541060113} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -10} + 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!850595691 &679135154 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Settings.lighting + serializedVersion: 4 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 0 + m_BakeBackend: 0 + m_LightmapMaxSize: 1024 + m_BakeResolution: 50 + m_Padding: 2 + m_LightmapCompression: 0 + m_AO: 1 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 1 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 1 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 512 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 1 + m_PVREnvironmentMIS: 0 + m_PVRFilteringMode: 0 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_PVRTiledBaking: 0 +--- !u!1 &1608319069 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1608319071} + - component: {fileID: 1608319070} + - component: {fileID: 1608319072} + m_Layer: 0 + m_Name: ThingkingAnalyticsExample + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1608319070 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1608319069} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fc3b54d5c2d4a455fbc56ab57ca985c6, type: 3} + m_Name: + m_EditorClassIdentifier: + skin: {fileID: 11400000, guid: 48b9be70902884a76ba1b53926fd3e37, type: 2} +--- !u!4 &1608319071 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1608319069} + 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: 269916752} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1608319072 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1608319069} + m_Enabled: 0 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 87d98871b303b4786b3dac3fd1b6e6eb, type: 3} + m_Name: + m_EditorClassIdentifier: + skin: {fileID: 0} diff --git a/Assets/Sample/TDAnalyticsDemo.unity.meta b/Assets/Sample/TDAnalyticsDemo.unity.meta new file mode 100644 index 0000000..2bcad43 --- /dev/null +++ b/Assets/Sample/TDAnalyticsDemo.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 741c7d08d0bfd46ae96dff68cdd17f99 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/GameScene.unity b/Assets/Scenes/GameScene.unity index 23ab8a1..cfdaf42 100644 --- a/Assets/Scenes/GameScene.unity +++ b/Assets/Scenes/GameScene.unity @@ -38,7 +38,6 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} - m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: @@ -104,7 +103,7 @@ NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: - serializedVersion: 2 + serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 @@ -117,7 +116,7 @@ NavMeshSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - accuratePlacement: 0 + buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: @@ -154,7 +153,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2018301549} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -236,7 +234,6 @@ RectTransform: m_Children: - {fileID: 1715967758} m_Father: {fileID: 1332931146} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -377,9 +374,17 @@ Camera: m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 x: 0 @@ -413,13 +418,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 519420028} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -10} 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!1 &1222376247 GameObject: @@ -481,13 +486,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1222376247} + serializedVersion: 2 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: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1332931142 GameObject: @@ -568,6 +573,7 @@ Canvas: m_SortingBucketNormalizedSize: 0 m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 0 m_TargetDisplay: 0 @@ -586,7 +592,6 @@ RectTransform: - {fileID: 231270963} - {fileID: 2018301549} m_Father: {fileID: 0} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -636,7 +641,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 231270963} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -718,7 +722,6 @@ RectTransform: m_Children: - {fileID: 138126178} m_Father: {fileID: 1332931146} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -829,6 +832,7 @@ GameObject: m_Component: - component: {fileID: 2137923634} - component: {fileID: 2137923635} + - component: {fileID: 2137923636} m_Layer: 0 m_Name: '[SDKManager]' m_TagString: Untagged @@ -843,13 +847,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2137923633} + serializedVersion: 2 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: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &2137923635 MonoBehaviour: @@ -864,3 +868,23 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: IsMoreAdsBidding: 0 +--- !u!114 &2137923636 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2137923633} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a550f9677d329f940876ee3d139f8289, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 519420032} + - {fileID: 2137923634} + - {fileID: 1332931146} + - {fileID: 1222376250} diff --git a/Assets/Script/FirebaseManager.meta b/Assets/Script/SDKManager/FirebaseManager.meta similarity index 100% rename from Assets/Script/FirebaseManager.meta rename to Assets/Script/SDKManager/FirebaseManager.meta diff --git a/Assets/Script/FirebaseManager/FireBaseAnalyticsManager.cs b/Assets/Script/SDKManager/FirebaseManager/FireBaseAnalyticsManager.cs similarity index 100% rename from Assets/Script/FirebaseManager/FireBaseAnalyticsManager.cs rename to Assets/Script/SDKManager/FirebaseManager/FireBaseAnalyticsManager.cs diff --git a/Assets/Script/FirebaseManager/FireBaseAnalyticsManager.cs.meta b/Assets/Script/SDKManager/FirebaseManager/FireBaseAnalyticsManager.cs.meta similarity index 100% rename from Assets/Script/FirebaseManager/FireBaseAnalyticsManager.cs.meta rename to Assets/Script/SDKManager/FirebaseManager/FireBaseAnalyticsManager.cs.meta diff --git a/Assets/Script/FirebaseManager/FireBaseRemoteConfigManager.cs b/Assets/Script/SDKManager/FirebaseManager/FireBaseRemoteConfigManager.cs similarity index 86% rename from Assets/Script/FirebaseManager/FireBaseRemoteConfigManager.cs rename to Assets/Script/SDKManager/FirebaseManager/FireBaseRemoteConfigManager.cs index 3dcc11d..424cb61 100644 --- a/Assets/Script/FirebaseManager/FireBaseRemoteConfigManager.cs +++ b/Assets/Script/SDKManager/FirebaseManager/FireBaseRemoteConfigManager.cs @@ -6,24 +6,6 @@ using UnityEngine; public static class FireBaseRemoteConfigManager { - private static bool isInit = false; - - public async static void Init() - { - // 只初始化Firebase核心功能,不初始化Remote Config - var dependencyStatus = await FirebaseApp.CheckAndFixDependenciesAsync(); - - if (dependencyStatus == DependencyStatus.Available) - { - isInit = true; - Debug.Log("Firebase initialized successfully (without Remote Config defaults)"); - } - else - { - Debug.LogError($"Could not resolve Firebase dependencies: {dependencyStatus}"); - } - } - /// /// 获取int参数 /// @@ -32,7 +14,7 @@ public static class FireBaseRemoteConfigManager /// public static async Task GetRemoteConfigInt(string key, int defaultValue = 0) { - if (!isInit) + if (!FireBaseSDKManager.Instance.IsInit) { return defaultValue; } @@ -70,7 +52,7 @@ public static class FireBaseRemoteConfigManager /// public static async Task GetRemoteConfigString(string key, string defaultValue = "") { - if (!isInit) + if (!FireBaseSDKManager.Instance.IsInit) { return defaultValue; } @@ -108,7 +90,7 @@ public static class FireBaseRemoteConfigManager /// public static async Task GetRemoteConfigBool(string key, bool defaultValue = false) { - if (!isInit) + if (!FireBaseSDKManager.Instance.IsInit) { return defaultValue; } @@ -146,7 +128,7 @@ public static class FireBaseRemoteConfigManager /// public static async Task GetRemoteConfigBool(string key, float defaultValue = 0) { - if (!isInit) + if (!FireBaseSDKManager.Instance.IsInit) { return defaultValue; } diff --git a/Assets/Script/FirebaseManager/FireBaseRemoteConfigManager.cs.meta b/Assets/Script/SDKManager/FirebaseManager/FireBaseRemoteConfigManager.cs.meta similarity index 100% rename from Assets/Script/FirebaseManager/FireBaseRemoteConfigManager.cs.meta rename to Assets/Script/SDKManager/FirebaseManager/FireBaseRemoteConfigManager.cs.meta diff --git a/Assets/Script/SDKManager/FirebaseManager/FireBaseSDKManager.cs b/Assets/Script/SDKManager/FirebaseManager/FireBaseSDKManager.cs new file mode 100644 index 0000000..0f12ef2 --- /dev/null +++ b/Assets/Script/SDKManager/FirebaseManager/FireBaseSDKManager.cs @@ -0,0 +1,32 @@ +using System.Collections; +using System.Collections.Generic; +using Firebase; +using UnityEngine; + +public class FireBaseSDKManager : MonoBehaviour +{ + public static FireBaseSDKManager Instance; + + public bool IsInit {private set; get; } + + private void Awake() + { + Instance = this; + InitSDK(); + } + + private async void InitSDK() + { + var dependencyStatus = await FirebaseApp.CheckAndFixDependenciesAsync(); + + if (dependencyStatus == DependencyStatus.Available) + { + IsInit = true; + Debug.Log("Firebase initialized successfully (without Remote Config defaults)"); + } + else + { + Debug.LogError($"Could not resolve Firebase dependencies: {dependencyStatus}"); + } + } +} diff --git a/Assets/Script/SDKManager/FirebaseManager/FireBaseSDKManager.cs.meta b/Assets/Script/SDKManager/FirebaseManager/FireBaseSDKManager.cs.meta new file mode 100644 index 0000000..da204b5 --- /dev/null +++ b/Assets/Script/SDKManager/FirebaseManager/FireBaseSDKManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a550f9677d329f940876ee3d139f8289 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/SDKManager/ShuShuMangage.meta b/Assets/Script/SDKManager/ShuShuMangage.meta new file mode 100644 index 0000000..092813a --- /dev/null +++ b/Assets/Script/SDKManager/ShuShuMangage.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fa0ed228357266f4988a9f871e872723 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Script/SDKManager/ShuShuMangage/ShuShuEvent.cs b/Assets/Script/SDKManager/ShuShuMangage/ShuShuEvent.cs new file mode 100644 index 0000000..d4f0dd1 --- /dev/null +++ b/Assets/Script/SDKManager/ShuShuMangage/ShuShuEvent.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using ThinkingData.Analytics; + +public static class ShuShuEvent +{ + /// + /// 设置用户属性 + /// + public static void UserSet(Dictionary dic) + { + TDAnalytics.UserSet(dic); + } + + /// + /// 设置公共事件属性 + /// + /// + public static void SetSuperProperties(Dictionary superProperties) + { + TDAnalytics.SetSuperProperties(superProperties);//设置公共事件属性 + } + + /// + /// 发送事件 + /// + public static void Track(string eventName) + { + TDAnalytics.Track(eventName); + } + + /// + /// 发送事件 + /// + public static void Track(string eventName, Dictionary properties) + { + TDAnalytics.Track(eventName, properties); + } +} \ No newline at end of file diff --git a/Assets/Script/SDKManager/ShuShuMangage/ShuShuEvent.cs.meta b/Assets/Script/SDKManager/ShuShuMangage/ShuShuEvent.cs.meta new file mode 100644 index 0000000..6fcca9a --- /dev/null +++ b/Assets/Script/SDKManager/ShuShuMangage/ShuShuEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c8805dfdca0f41a6925658078a82a69d +timeCreated: 1756546330 \ No newline at end of file diff --git a/Assets/Script/SDKManager/ShuShuMangage/ShuShuMangage.cs b/Assets/Script/SDKManager/ShuShuMangage/ShuShuMangage.cs new file mode 100644 index 0000000..83e9fdb --- /dev/null +++ b/Assets/Script/SDKManager/ShuShuMangage/ShuShuMangage.cs @@ -0,0 +1,21 @@ +using System.Collections; +using System.Collections.Generic; +using ThinkingData.Analytics; +using UnityEngine; + +public class ShuShuMangage : MonoBehaviour +{ + private const string appid = "80f6819a81c743cbad667ecf242f3133"; + private const string server = ""; + + private void Start() + { + // 初始化SDK + TDAnalytics.Init(appid, server); + //开启自动采集事件 + TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.AppInstall | TDAutoTrackEventType.AppStart | TDAutoTrackEventType.AppEnd); + //如果用户已登录,可以设置用户的账号ID作为身份唯一标识 + TDAnalytics.Login("TA"); + } + +} diff --git a/Assets/Script/SDKManager/ShuShuMangage/ShuShuMangage.cs.meta b/Assets/Script/SDKManager/ShuShuMangage/ShuShuMangage.cs.meta new file mode 100644 index 0000000..d53521a --- /dev/null +++ b/Assets/Script/SDKManager/ShuShuMangage/ShuShuMangage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5019ae7cf0c6d3d4c89b8921e4beab15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets.meta b/Assets/StreamingAssets.meta new file mode 100644 index 0000000..ddec8e7 --- /dev/null +++ b/Assets/StreamingAssets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1b79abdaa780b2c4bacc34065090f709 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/StreamingAssets/google-services-desktop.json b/Assets/StreamingAssets/google-services-desktop.json new file mode 100644 index 0000000..e6e64dc --- /dev/null +++ b/Assets/StreamingAssets/google-services-desktop.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "601745472008", + "project_id": "ag787cd54047-e0ac1-gp", + "storage_bucket": "ag787cd54047-e0ac1-gp.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:601745472008:android:768b7c2f79e2e93725afbd", + "android_client_info": { + "package_name": "com.rush.cash.earn.fast.real.money.game" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyCbVg7dxQbatGTDomrKq4AkQAP4aH1vr4Y" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/Assets/StreamingAssets/google-services-desktop.json.meta b/Assets/StreamingAssets/google-services-desktop.json.meta new file mode 100644 index 0000000..ef0889d --- /dev/null +++ b/Assets/StreamingAssets/google-services-desktop.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1d355797e637f624b9148b20da4b2a10 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThinkingAnalytics.meta b/Assets/ThinkingAnalytics.meta new file mode 100644 index 0000000..da98201 --- /dev/null +++ b/Assets/ThinkingAnalytics.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 60b198fb61dd042d5b21af809183759f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThinkingAnalytics/Exception.meta b/Assets/ThinkingAnalytics/Exception.meta new file mode 100644 index 0000000..6e3b94b --- /dev/null +++ b/Assets/ThinkingAnalytics/Exception.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f347e0b07c5d64926ac64031c3bba013 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThinkingAnalytics/Exception/TDException.cs b/Assets/ThinkingAnalytics/Exception/TDException.cs new file mode 100644 index 0000000..8c0a8f2 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Exception/TDException.cs.meta b/Assets/ThinkingAnalytics/Exception/TDException.cs.meta new file mode 100644 index 0000000..766222b --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Resources.meta b/Assets/ThinkingAnalytics/Resources.meta new file mode 100644 index 0000000..7f3bdf2 --- /dev/null +++ b/Assets/ThinkingAnalytics/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4e5d07cff2f9b45d6afa9ab9a879fbff +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThinkingAnalytics/Resources/ta_public_config.xml b/Assets/ThinkingAnalytics/Resources/ta_public_config.xml new file mode 100644 index 0000000..3f98eff --- /dev/null +++ b/Assets/ThinkingAnalytics/Resources/ta_public_config.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets/ThinkingAnalytics/Resources/ta_public_config.xml.meta b/Assets/ThinkingAnalytics/Resources/ta_public_config.xml.meta new file mode 100644 index 0000000..d130ea4 --- /dev/null +++ b/Assets/ThinkingAnalytics/Resources/ta_public_config.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b67db39cd532b4f08b95b2cf5938553f +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThinkingAnalytics/TDAnalytics.asmdef b/Assets/ThinkingAnalytics/TDAnalytics.asmdef new file mode 100644 index 0000000..5ca02cd --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/TDAnalytics.asmdef.meta b/Assets/ThinkingAnalytics/TDAnalytics.asmdef.meta new file mode 100644 index 0000000..5415231 --- /dev/null +++ b/Assets/ThinkingAnalytics/TDAnalytics.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ff4bf6c59d1ac413a8f34ced5be76ecd +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThinkingAnalytics/TDAnalytics.cs b/Assets/ThinkingAnalytics/TDAnalytics.cs new file mode 100644 index 0000000..cddfbfe --- /dev/null +++ b/Assets/ThinkingAnalytics/TDAnalytics.cs @@ -0,0 +1,1617 @@ +/* + * + 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.2.2 + */ + +/** +\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} + // }); + // } + //} + + public static void TrackStr(string eventName, string properties = "", string appId = "") + { + if (tracking_enabled) + { + TDWrapper.TrackStr(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 + /// 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} + }); + } + } + + public static void SetSuperProperties(string 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} + }); + } + } + + public static void UserSet(string 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} + }); + } + + } + + public static void UserSetOnce(string 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} + }); + } + } + + public static void UserAddStr(string properties, string appId = "") + { + if (tracking_enabled) + { + TDWrapper.UserAddStr(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} + }); + } + } + + public static void UserAppend(string 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} + }); + } + } + + public static void UserUniqAppend(string 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/Assets/ThinkingAnalytics/TDAnalytics.cs.meta b/Assets/ThinkingAnalytics/TDAnalytics.cs.meta new file mode 100644 index 0000000..97dbead --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/TDAnalytics.prefab b/Assets/ThinkingAnalytics/TDAnalytics.prefab new file mode 100644 index 0000000..6b69e88 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/TDAnalytics.prefab.meta b/Assets/ThinkingAnalytics/TDAnalytics.prefab.meta new file mode 100644 index 0000000..4413269 --- /dev/null +++ b/Assets/ThinkingAnalytics/TDAnalytics.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 986ad473b78d14e08b1f05d368cb1591 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThinkingAnalytics/TDEnum.cs b/Assets/ThinkingAnalytics/TDEnum.cs new file mode 100644 index 0000000..7ed5d6e --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/TDEnum.cs.meta b/Assets/ThinkingAnalytics/TDEnum.cs.meta new file mode 100644 index 0000000..5fcf5d1 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/TDEvent.cs b/Assets/ThinkingAnalytics/TDEvent.cs new file mode 100644 index 0000000..d143e1d --- /dev/null +++ b/Assets/ThinkingAnalytics/TDEvent.cs @@ -0,0 +1,131 @@ +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; } + public string StrProperties { 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/Assets/ThinkingAnalytics/TDEvent.cs.meta b/Assets/ThinkingAnalytics/TDEvent.cs.meta new file mode 100644 index 0000000..77f698f --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/TDInterface.cs b/Assets/ThinkingAnalytics/TDInterface.cs new file mode 100644 index 0000000..88f33f1 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/TDInterface.cs.meta b/Assets/ThinkingAnalytics/TDInterface.cs.meta new file mode 100644 index 0000000..96f6ca1 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/TDPresetProperties.cs b/Assets/ThinkingAnalytics/TDPresetProperties.cs new file mode 100644 index 0000000..c60dd58 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/TDPresetProperties.cs.meta b/Assets/ThinkingAnalytics/TDPresetProperties.cs.meta new file mode 100644 index 0000000..bdf2e3b --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/ThinkingAnalyticsAPI.cs b/Assets/ThinkingAnalytics/ThinkingAnalyticsAPI.cs new file mode 100644 index 0000000..aa63d54 --- /dev/null +++ b/Assets/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.2.2 + */ +#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/Assets/ThinkingAnalytics/ThinkingAnalyticsAPI.cs.meta b/Assets/ThinkingAnalytics/ThinkingAnalyticsAPI.cs.meta new file mode 100644 index 0000000..b1c95d0 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Utils.meta b/Assets/ThinkingAnalytics/Utils.meta new file mode 100644 index 0000000..31f6fa6 --- /dev/null +++ b/Assets/ThinkingAnalytics/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: efe5e95f8073844fcbaedf8453895210 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThinkingAnalytics/Utils/TDCommonUtils.cs b/Assets/ThinkingAnalytics/Utils/TDCommonUtils.cs new file mode 100644 index 0000000..41448dd --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Utils/TDCommonUtils.cs.meta b/Assets/ThinkingAnalytics/Utils/TDCommonUtils.cs.meta new file mode 100644 index 0000000..d0a11be --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Utils/TDLog.cs b/Assets/ThinkingAnalytics/Utils/TDLog.cs new file mode 100644 index 0000000..ae46aa7 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Utils/TDLog.cs.meta b/Assets/ThinkingAnalytics/Utils/TDLog.cs.meta new file mode 100644 index 0000000..68e2f99 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Utils/TDMiniJson.cs b/Assets/ThinkingAnalytics/Utils/TDMiniJson.cs new file mode 100644 index 0000000..d54c573 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Utils/TDMiniJson.cs.meta b/Assets/ThinkingAnalytics/Utils/TDMiniJson.cs.meta new file mode 100644 index 0000000..90e907a --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Utils/TDPropertiesChecker.cs b/Assets/ThinkingAnalytics/Utils/TDPropertiesChecker.cs new file mode 100644 index 0000000..44e7fb0 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Utils/TDPropertiesChecker.cs.meta b/Assets/ThinkingAnalytics/Utils/TDPropertiesChecker.cs.meta new file mode 100644 index 0000000..0d18ac9 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Utils/TDPublicConfig.cs b/Assets/ThinkingAnalytics/Utils/TDPublicConfig.cs new file mode 100644 index 0000000..d33a81c --- /dev/null +++ b/Assets/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.2.2"; + + 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 data) { + if (data == null) + { + return ""; + } + + return serilize(data); + } + + private static string getTimeString(DateTime dateTime) { + if (defaultTimeZone == null) + { + return TDCommonUtils.FormatDate(dateTime, defaultTDTimeZone); + } + else { + return TDCommonUtils.FormatDate(dateTime, defaultTimeZone); + } + } + + 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"); + 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; + } + } + sdkClass.CallStatic("init",context,token.appId,token.serverUrl, (int)token.mode,token.name, timeZoneId, token.encryptVersion,token.encryptPublicKey); + } + + private static void flush(string appId) + { + sdkClass.CallStatic("flush", appId); + } + + private static long getDateTimeStamp(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 currentMillis; + } + + private static void track(string eventName, Dictionary properties, DateTime dateTime, string appId) + { + try + { + sdkClass.CallStatic("track", eventName, getJsonStr(properties), getDateTimeStamp(dateTime), "Local", appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void track(string eventName, Dictionary properties, DateTime dateTime, TimeZoneInfo timeZone, string appId) + { + try + { + if (timeZone == null) + { + sdkClass.CallStatic("track", eventName, getJsonStr(properties), getDateTimeStamp(dateTime), "", appId); + } + else { + sdkClass.CallStatic("track", eventName, getJsonStr(properties), getDateTimeStamp(dateTime), timeZone.Id, appId); + } + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void trackForAll(string eventName, Dictionary properties) + { + string appId = ""; + track(eventName, properties, appId); + } + + private static void track(TDEventModel taEvent, string appId) + { + int eventType = -1; + if (taEvent.EventType == TDEventModel.TDEventType.First) + { + eventType = 0; + } + else if (taEvent.EventType == TDEventModel.TDEventType.Updatable) + { + eventType = 1; + } + else if (taEvent.EventType == TDEventModel.TDEventType.Overwritable) + { + eventType = 2; + } + if (eventType < 0) return; + string jsonStr; + if (taEvent.Properties == null) + { + jsonStr = taEvent.StrProperties; + } + else { + jsonStr = getJsonStr(getFinalEventProperties(taEvent.Properties)); + } + if (taEvent.GetEventTimeZone() == null) + { + sdkClass.CallStatic("trackEvent", eventType, taEvent.EventName, jsonStr, taEvent.GetEventId(), getDateTimeStamp(taEvent.GetEventTime()), "", appId); + } + else + { + sdkClass.CallStatic("trackEvent", eventType, taEvent.EventName, jsonStr, taEvent.GetEventId(), getDateTimeStamp(taEvent.GetEventTime()), taEvent.GetEventTimeZone().Id, appId); + } + } + + private static void track(string eventName, Dictionary properties, string appId) + { + try + { + sdkClass.CallStatic("track", eventName, getJsonStr(properties), 0L, "", appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void trackStr(string eventName, string properties, string appId) + { + try + { + sdkClass.CallStatic("track", eventName, properties, 0L, "", appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void setSuperProperties(Dictionary superProperties, string appId) + { + try + { + sdkClass.CallStatic("setSuperProperties", getJsonStr(superProperties), appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void setSuperProperties(string superProperties, string appId) + { + try + { + sdkClass.CallStatic("setSuperProperties", superProperties, appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void unsetSuperProperty(string superPropertyName, string appId) + { + sdkClass.CallStatic("unsetSuperProperty", superPropertyName, appId); + } + + private static void clearSuperProperty(string appId) + { + sdkClass.CallStatic("clearSuperProperties", appId); + } + + private static Dictionary getSuperProperties(string appId) + { + Dictionary result = null; + try + { + result = TDMiniJson.Deserialize(sdkClass.CallStatic("getSuperProperties", appId)); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + return result; + } + + private static Dictionary getPresetProperties(string appId) + { + Dictionary result = null; + try + { + result = TDMiniJson.Deserialize(sdkClass.CallStatic("getPresetProperties",appId)); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + return result; + } + + private static void timeEvent(string eventName, string appId) + { + sdkClass.CallStatic("timeEvent", eventName, appId); + } + + private static void timeEventForAll(string eventName) + { + sdkClass.CallStatic("timeEvent", eventName, ""); + } + + private static void identify(string uniqueId, string appId) + { + sdkClass.CallStatic("identify", uniqueId, appId); + } + + private static string getDistinctId(string appId) + { + return sdkClass.CallStatic("getDistinctId", appId); + } + + private static void login(string uniqueId, string appId) + { + sdkClass.CallStatic("login", uniqueId, appId); + } + + private static void userSetOnce(Dictionary properties, string appId) + { + try + { + sdkClass.CallStatic("userSetOnce", getJsonStr(properties), 0L,appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userSetOnce(string properties, string appId) + { + try + { + sdkClass.CallStatic("userSetOnce", properties, 0L, appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userSetOnce(Dictionary properties, DateTime dateTime, string appId) + { + try + { + sdkClass.CallStatic("userSetOnce", getJsonStr(properties), getDateTimeStamp(dateTime), appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userSet(Dictionary properties, string appId) + { + try + { + sdkClass.CallStatic("userSet", getJsonStr(properties), 0L, appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userSet(Dictionary properties, DateTime dateTime, string appId) + { + try + { + sdkClass.CallStatic("userSet", getJsonStr(properties), getDateTimeStamp(dateTime), appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userSet(string properties, string appId) + { + try + { + sdkClass.CallStatic("userSet", properties, 0L, appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + 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); + } + try + { + sdkClass.CallStatic("userUnset", getJsonStr(finalProperties), getDateTimeStamp(dateTime), appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userAdd(Dictionary properties, string appId) + { + try + { + sdkClass.CallStatic("userAdd", getJsonStr(properties), 0L, appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userAddStr(string properties, string appId) + { + try + { + sdkClass.CallStatic("userAdd", properties, 0L, appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userAdd(Dictionary properties, DateTime dateTime, string appId) + { + try + { + sdkClass.CallStatic("userAdd", getJsonStr(properties), getDateTimeStamp(dateTime), appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userAppend(Dictionary properties, string appId) + { + try + { + sdkClass.CallStatic("userAppend", getJsonStr(properties), 0L, appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userAppend(string properties, string appId) + { + try + { + sdkClass.CallStatic("userAppend", properties, 0L, appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userAppend(Dictionary properties, DateTime dateTime, string appId) + { + try + { + sdkClass.CallStatic("userAppend", getJsonStr(properties), getDateTimeStamp(dateTime), appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userUniqAppend(Dictionary properties, string appId) + { + try + { + sdkClass.CallStatic("userUniqAppend", getJsonStr(properties), 0L, appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userUniqAppend(string properties, string appId) + { + try + { + sdkClass.CallStatic("userUniqAppend", properties, 0L, appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userUniqAppend(Dictionary properties, DateTime dateTime, string appId) + { + try + { + sdkClass.CallStatic("userUniqAppend", getJsonStr(properties), getDateTimeStamp(dateTime), appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void userDelete(string appId) + { + sdkClass.CallStatic("userDel", 0L, appId); + } + + private static void userDelete(DateTime dateTime, string appId) + { + try + { + sdkClass.CallStatic("userDel", getDateTimeStamp(dateTime), appId); + } + catch (Exception e) + { + if (TDLog.GetEnable()) TDLog.w("ThinkingAnalytics: unexpected exception: " + e); + } + } + + private static void logout(string appId) + { + sdkClass.CallStatic("logout", appId); + } + + private static string getDeviceId() + { + return sdkClass.CallStatic("getDeviceId"); + } + + private static void setDynamicSuperProperties(TDDynamicSuperPropertiesHandler dynamicSuperProperties, string appId) + { + DynamicListenerAdapter listenerAdapter = new DynamicListenerAdapter(); + sdkClass.CallStatic("setDynamicSuperPropertiesTrackerListener", appId, listenerAdapter); + } + + private static void setNetworkType(TDNetworkType networkType) { + int type; + switch (networkType) + { + case TDNetworkType.Wifi: + type = 1; + break; + case TDNetworkType.All: + type = 0; + break; + default: + type = 0; + break; + + } + sdkClass.CallStatic("setNetworkType", type,""); + } + + private static void enableAutoTrack(TDAutoTrackEventType events, Dictionary properties, string appId) + { + sdkClass.CallStatic("enableAutoTrack", (int)events, TDMiniJson.Serialize(properties), appId); + } + + private static void enableAutoTrack(TDAutoTrackEventType events, TDAutoTrackEventHandler eventCallback, string appId) + { + AutoTrackListenerAdapter listenerAdapter = new AutoTrackListenerAdapter(); + sdkClass.CallStatic("enableAutoTrack", (int)events, listenerAdapter, appId); + } + + private static void setAutoTrackProperties(TDAutoTrackEventType events, Dictionary properties, string appId) + { + sdkClass.CallStatic("setAutoTrackProperties", (int)events, TDMiniJson.Serialize(properties), appId); + } + + private static void setTrackStatus(TDTrackStatus status, string appId) + { + int type; + switch (status) + { + case TDTrackStatus.Pause: + type = 1; + break; + case TDTrackStatus.Stop: + type = 2; + break; + case TDTrackStatus.SaveOnly: + type = 3; + break; + case TDTrackStatus.Normal: + default: + type = 0; + break; + } + sdkClass.CallStatic("setTrackStatus", type, appId); + } + + private static void optOutTracking(string appId) + { + } + + private static void optOutTrackingAndDeleteUser(string appId) + { + } + + private static void optInTracking(string appId) + { + } + + private static void enableTracking(bool enabled, string appId) + { + } + + private static string createLightInstance() + { + return sdkClass.CallStatic("createLightInstance",""); + } + + private static void calibrateTime(long timestamp) + { + sdkClass.CallStatic("calibrateTime", timestamp); + } + + private static void calibrateTimeWithNtp(string ntpServer) + { + sdkClass.CallStatic("calibrateTimeWithNtp", ntpServer); + } + + private static void enableThirdPartySharing(TDThirdPartyType shareType, Dictionary properties, string appId) + { + sdkClass.CallStatic("enableThirdPartySharing", (int)shareType, TDMiniJson.Serialize(properties), appId); + } + + //dynamic super properties + public interface IDynamicSuperPropertiesTrackerListener + { + string getDynamicSuperPropertiesString(); + } + + private class DynamicListenerAdapter : AndroidJavaProxy { + public DynamicListenerAdapter() : base("cn.thinkingdata.analytics.ThinkingAnalyticsProxy$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.analytics.ThinkingAnalyticsProxy$AutoTrackEventTrackerListener") {} + string eventCallback(int type, string appId, string properties) + { + Dictionary ret; + 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/Assets/ThinkingAnalytics/Wrapper/TDAndroidWrapper.cs.meta b/Assets/ThinkingAnalytics/Wrapper/TDAndroidWrapper.cs.meta new file mode 100644 index 0000000..2c9773a --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Wrapper/TDOpenHarmonyWrapper.cs b/Assets/ThinkingAnalytics/Wrapper/TDOpenHarmonyWrapper.cs new file mode 100644 index 0000000..b86e465 --- /dev/null +++ b/Assets/ThinkingAnalytics/Wrapper/TDOpenHarmonyWrapper.cs @@ -0,0 +1,407 @@ +#if UNITY_OPENHARMONY && !(UNITY_EDITOR) +using System; +using System.Collections.Generic; +using ThinkingAnalytics; +using ThinkingData.Analytics.Utils; +using UnityEngine; +namespace ThinkingData.Analytics.Wrapper +{ + public partial class TDWrapper + { + private static readonly OpenHarmonyJSClass openHarmonyJsClass = new OpenHarmonyJSClass("TDOpenHarmonyProxy"); + + private static TimeZoneInfo defaultTimeZone = null; + private static TDTimeZone defaultTDTimeZone = TDTimeZone.Local; + + private static void init(TDConfig token) + { + string timeZoneId = token.getTimeZoneId(); + defaultTDTimeZone = token.timeZone; + string timeZone = ""; + if (null != timeZoneId && timeZoneId.Length > 0) + { + timeZone = timeZoneId; + if (defaultTimeZone == null) + { + try + { + defaultTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); + } + catch (Exception) + { + } + } + } + else + { + if (defaultTimeZone == null) + { + defaultTimeZone = TimeZoneInfo.Local; + } + } + openHarmonyJsClass.CallStatic("init", token.appId, token.serverUrl, (int)token.mode, timeZone, token.encryptVersion, token.encryptPublicKey); + + } + + private static string getTimeString(DateTime dateTime) + { + if (defaultTimeZone == null) + { + return TDCommonUtils.FormatDate(dateTime, defaultTDTimeZone); + } + else + { + return TDCommonUtils.FormatDate(dateTime, defaultTimeZone); + } + } + + private static void enableLog(bool enable) + { + openHarmonyJsClass.CallStatic("enableLog", enable); + } + + private static void setVersionInfo(string libName, string version) + { + openHarmonyJsClass.CallStatic("setLibraryInfo", libName, version); + } + + private static void identify(string uniqueId, string appId) + { + openHarmonyJsClass.CallStatic("setDistinctId", uniqueId, appId); + } + + private static string getDistinctId(string appId) + { + return openHarmonyJsClass.CallStatic("getDistinctId", appId); + } + + private static void login(string uniqueId, string appId) + { + openHarmonyJsClass.CallStatic("login", uniqueId, appId); + } + + private static void logout(string appId) + { + openHarmonyJsClass.CallStatic("logout", appId); + } + + private static void enableAutoTrack(TDAutoTrackEventType events, Dictionary properties, string appId) + { + openHarmonyJsClass.CallStatic("enableAutoTrack", (int)events, appId); + } + + private static void enableAutoTrack(TDAutoTrackEventType events, TDAutoTrackEventHandler eventCallback, string appId) + { + + } + + private static void setAutoTrackProperties(TDAutoTrackEventType events, Dictionary properties, string appId) + { + + } + + private static void track(string eventName, Dictionary properties, DateTime dateTime, string appId) + { + track(eventName, properties, dateTime, null, appId); + } + + 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 timeZoneId = ""; + if (timeZone != null) + { + timeZoneId = timeZone.Id; + } + openHarmonyJsClass.CallStatic("track", eventName, serilize(properties), currentMillis, timeZoneId, appId); + } + + private static void track(string eventName, Dictionary properties, string appId) + { + openHarmonyJsClass.CallStatic("track", eventName, serilize(properties), 0, "", appId); + } + + private static void trackForAll(string eventName, Dictionary properties) + { + track(eventName, properties, ""); + } + + private static void track(TDEventModel taEvent, string appId) + { + int eventType = -1; ; + switch (taEvent.EventType) + { + case TDEventModel.TDEventType.First: + eventType = 1; + break; + case TDEventModel.TDEventType.Updatable: + eventType = 2; + break; + case TDEventModel.TDEventType.Overwritable: + eventType = 3; + break; + } + if (eventType < 0) return; + string jsonStr; + if (taEvent.Properties == null) + { + jsonStr = taEvent.StrProperties; + } + else + { + jsonStr = serilize(taEvent.Properties); + } + long eventTime = 0; + 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); + eventTime = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + } + string timeZoneId = ""; + if (taEvent.GetEventTimeZone() != null) + { + timeZoneId = taEvent.GetEventTimeZone().Id; + } + openHarmonyJsClass.CallStatic("trackEvent", eventType, taEvent.EventName, jsonStr, taEvent.GetEventId(), eventTime, timeZoneId, appId); + } + + private static void trackStr(string eventName, string properties, string appId) + { + openHarmonyJsClass.CallStatic("track", eventName, properties, 0, "", appId); + } + + private static void setSuperProperties(Dictionary superProperties, string appId) + { + openHarmonyJsClass.CallStatic("setSuperProperties", serilize(superProperties), appId); + } + + private static void setSuperProperties(string superProperties, string appId) + { + openHarmonyJsClass.CallStatic("setSuperProperties", superProperties, appId); + } + + private static void unsetSuperProperty(string superPropertyName, string appId) + { + openHarmonyJsClass.CallStatic("unsetSuperProperty", superPropertyName, appId); + } + + private static void clearSuperProperty(string appId) + { + openHarmonyJsClass.CallStatic("clearSuperProperties", appId); + } + + private static Dictionary getSuperProperties(string appId) + { + string superPropertiesString = openHarmonyJsClass.CallStatic("getSuperProperties", appId); + return TDMiniJson.Deserialize(superPropertiesString); + } + + private static Dictionary getPresetProperties(string appId) + { + string presetPropertiesString = openHarmonyJsClass.CallStatic("getPresetProperties", appId); + return TDMiniJson.Deserialize(presetPropertiesString); + } + + private static void timeEvent(string eventName, string appId) + { + openHarmonyJsClass.CallStatic("timeEvent", eventName, appId); + } + + private static void timeEventForAll(string eventName) + { + openHarmonyJsClass.CallStatic("timeEvent", eventName, ""); + } + + private static void userSet(Dictionary properties, string appId) + { + userSet(properties, new DateTime(), appId); + } + + 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; + openHarmonyJsClass.CallStatic("userSet", serilize(properties), currentMillis, appId); + } + + private static void userSet(string properties, string appId) + { + openHarmonyJsClass.CallStatic("userSet", properties, 0, appId); + } + + private static void userSetOnce(Dictionary properties, string appId) + { + userSetOnce(properties, new DateTime(), appId); + } + + 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; + openHarmonyJsClass.CallStatic("userSetOnce", serilize(properties), currentMillis, appId); + } + + private static void userSetOnce(string properties, string appId) + { + openHarmonyJsClass.CallStatic("userSetOnce", properties, 0, appId); + } + + private static void userUnset(List properties, string appId) + { + userUnset(properties, new DateTime(), appId); + } + + 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) + { + openHarmonyJsClass.CallStatic("userUnset", property, currentMillis, appId); + } + } + + private static void userAdd(Dictionary properties, string appId) + { + userAdd(properties, new DateTime(), appId); + } + + 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; + openHarmonyJsClass.CallStatic("userAdd", serilize(properties), currentMillis, appId); + } + + private static void userAddStr(string properties, string appId) + { + openHarmonyJsClass.CallStatic("userAdd", properties, 0, appId); + } + + private static void userAppend(Dictionary properties, string appId) + { + userAppend(properties, new DateTime(), appId); + } + + + 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; + openHarmonyJsClass.CallStatic("userAppend", serilize(properties), currentMillis, appId); + } + + private static void userAppend(string properties, string appId) + { + openHarmonyJsClass.CallStatic("userAppend", properties, 0, appId); + } + + private static void userUniqAppend(Dictionary properties, string appId) + { + userUniqAppend(properties, new DateTime(), appId); + } + + 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; + openHarmonyJsClass.CallStatic("userUniqAppend", serilize(properties), currentMillis, appId); + } + + private static void userUniqAppend(string properties, string appId) + { + openHarmonyJsClass.CallStatic("userUniqAppend", properties, 0, appId); + } + + private static void userDelete(string appId) + { + userDelete(new DateTime(), 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; + openHarmonyJsClass.CallStatic("userDelete", currentMillis, appId); + } + + private static void flush(string appId) + { + openHarmonyJsClass.CallStatic("flush", appId); + } + + private static string getDeviceId() + { + return openHarmonyJsClass.CallStatic("getDeviceId"); + } + + private static void setNetworkType(TDNetworkType networkType) + { + openHarmonyJsClass.CallStatic("setNetWorkType", (int)networkType); + } + + private static void setDynamicSuperProperties(TDDynamicSuperPropertiesHandler dynamicSuperProperties, string appId) + { + + } + + private static void setTrackStatus(TDTrackStatus status, string appId) + { + + } + + private static void optOutTracking(string appId) + { + + } + + private static void optOutTrackingAndDeleteUser(string appId) + { + + } + + private static void optInTracking(string appId) + { + + } + + private static void enableTracking(bool enabled, string appId) + { + + } + + private static string createLightInstance() + { + + return ""; + } + + private static void calibrateTime(long timestamp) + { + openHarmonyJsClass.CallStatic("calibrateTime", timestamp); + } + + private static void calibrateTimeWithNtp(string ntpServer) + { + + } + + private static void enableThirdPartySharing(TDThirdPartyType shareType, Dictionary properties, string appId) + { + + } + + } +} + +#endif \ No newline at end of file diff --git a/Assets/ThinkingAnalytics/Wrapper/TDOpenHarmonyWrapper.cs.meta b/Assets/ThinkingAnalytics/Wrapper/TDOpenHarmonyWrapper.cs.meta new file mode 100644 index 0000000..1c5b263 --- /dev/null +++ b/Assets/ThinkingAnalytics/Wrapper/TDOpenHarmonyWrapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd865474607df4800a2a0de0ef67be63 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ThinkingAnalytics/Wrapper/TDPCWrapper.cs b/Assets/ThinkingAnalytics/Wrapper/TDPCWrapper.cs new file mode 100644 index 0000000..52c143d --- /dev/null +++ b/Assets/ThinkingAnalytics/Wrapper/TDPCWrapper.cs @@ -0,0 +1,492 @@ +#if ((!(UNITY_IOS) || UNITY_EDITOR) && (!(UNITY_ANDROID) || UNITY_EDITOR) && (!(UNITY_OPENHARMONY) || 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 trackStr(string eventName, string properties, string appId) + { + try + { + ThinkingPCSDK.Track(eventName, TDMiniJson.Deserialize(properties), appId); + } + catch (Exception) + { + } + } + + 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 setSuperProperties(string superProperties, string appId) + { + try + { + ThinkingPCSDK.SetSuperProperties(TDMiniJson.Deserialize(superProperties), appId); + } + catch (Exception) + { + } + } + + 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(string properties, string appId) + { + try + { + ThinkingPCSDK.UserSet(TDMiniJson.Deserialize(properties), appId); + } + catch (Exception) + { + } + } + + 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(string properties, string appId) + { + try + { + ThinkingPCSDK.UserSetOnce(TDMiniJson.Deserialize(properties), appId); + } + catch (Exception) + { + } + } + + 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 userAddStr(string properties, string appId) + { + try + { + ThinkingPCSDK.UserAdd(TDMiniJson.Deserialize(properties), appId); + } + catch (Exception) + { + } + } + + 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(string properties, string appId) + { + try + { + ThinkingPCSDK.UserAppend(TDMiniJson.Deserialize(properties), appId); + } + catch (Exception) + { + } + } + + 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(string properties, string appId) + { + try + { + ThinkingPCSDK.UserUniqAppend(TDMiniJson.Deserialize(properties), appId); + } + catch (Exception) + { + } + } + + 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/Assets/ThinkingAnalytics/Wrapper/TDPCWrapper.cs.meta b/Assets/ThinkingAnalytics/Wrapper/TDPCWrapper.cs.meta new file mode 100644 index 0000000..9cad8e3 --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Wrapper/TDWrapper.cs b/Assets/ThinkingAnalytics/Wrapper/TDWrapper.cs new file mode 100644 index 0000000..cbad9b5 --- /dev/null +++ b/Assets/ThinkingAnalytics/Wrapper/TDWrapper.cs @@ -0,0 +1,507 @@ +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 TrackStr(string eventName, string properties, string appId) + { + trackStr(eventName, 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 SetSuperProperties(string superProperties, string appId) + { + 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(string properties, string appId) + { + 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(string properties, string appId) + { + 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 UserAddStr(string properties, string appId) + { + userAddStr(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(string properties, string appId) + { + 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(string properties, string appId) + { + 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/Assets/ThinkingAnalytics/Wrapper/TDWrapper.cs.meta b/Assets/ThinkingAnalytics/Wrapper/TDWrapper.cs.meta new file mode 100644 index 0000000..3fc816f --- /dev/null +++ b/Assets/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/Assets/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs b/Assets/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs new file mode 100644 index 0000000..93eb7b5 --- /dev/null +++ b/Assets/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs @@ -0,0 +1,551 @@ +#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, int type, string event_name, string properties, string event_id, long time_stamp_millis, string timezone); + [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) + { + int type = -1; + switch (taEvent.EventType) + { + case TDEventModel.TDEventType.First: + type = 0; + break; + case TDEventModel.TDEventType.Updatable: + type = 1; + break; + case TDEventModel.TDEventType.Overwritable: + type = 2; + break; + } + string jsonStr; + if (taEvent.Properties == null) + { + jsonStr = taEvent.StrProperties; + } + else + { + jsonStr = serilize(taEvent.Properties); + } + long currentMillis = 0; + 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); + currentMillis = (dateTimeTicksUTC - dtFrom.Ticks) / 10000; + } + string timeZoneId = ""; + if (taEvent.GetEventTimeZone() != null) + { + timeZoneId = taEvent.GetEventTimeZone().Id; + } + ta_track_event(appId, type, taEvent.EventName, jsonStr, taEvent.GetEventId(), currentMillis, timeZoneId); + } + + private static void track(string eventName, Dictionary properties, string appId) + { + ta_track(appId, eventName, serilize(properties), 0, ""); + } + + private static void trackStr(string eventName, string properties, string appId) + { + ta_track(appId, eventName, 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 setSuperProperties(string superProperties, string appId) + { + ta_set_super_properties(appId, 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(string properties, string appId) + { + ta_user_set(appId, 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(string properties, string appId) + { + ta_user_set_once(appId, 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 userAddStr(string properties, string appId) + { + ta_user_add(appId, 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(string properties, string appId) + { + ta_user_append(appId, 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(string properties, string appId) + { + ta_user_uniq_append(appId, 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/Assets/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs.meta b/Assets/ThinkingAnalytics/Wrapper/TDiOSWrapper.cs.meta new file mode 100644 index 0000000..680368f --- /dev/null +++ b/Assets/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/Assets/google-services.json b/Assets/google-services.json new file mode 100644 index 0000000..e6e64dc --- /dev/null +++ b/Assets/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "601745472008", + "project_id": "ag787cd54047-e0ac1-gp", + "storage_bucket": "ag787cd54047-e0ac1-gp.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:601745472008:android:768b7c2f79e2e93725afbd", + "android_client_info": { + "package_name": "com.rush.cash.earn.fast.real.money.game" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyCbVg7dxQbatGTDomrKq4AkQAP4aH1vr4Y" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/Assets/google-services.json.meta b/Assets/google-services.json.meta new file mode 100644 index 0000000..58066ac --- /dev/null +++ b/Assets/google-services.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7b27930e2fb6bc84e96b66bdceb0c41e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml b/Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml new file mode 100644 index 0000000..93226a4 --- /dev/null +++ b/Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + Assets/Firebase/m2repository + + + + diff --git a/Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml.meta b/Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml.meta new file mode 100644 index 0000000..a66b439 --- /dev/null +++ b/Assets/rd3/Firebase/Editor/CrashlyticsDependencies.xml.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be690db6bda046a89e38b20ef9bfe06c +labels: +- gvh +- gvh_version-13.1.0 +- gvhp_exportpath-Firebase/Editor/CrashlyticsDependencies.xml +timeCreated: 1480838400 +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.dll b/Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.dll new file mode 100644 index 0000000000000000000000000000000000000000..9081133641e99f89ff268c0dab5d48acc3bdf1dc GIT binary patch literal 25600 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 BINksN literal 0 HcmV?d00001 diff --git a/Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.dll.meta b/Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.dll.meta new file mode 100644 index 0000000..df448f6 --- /dev/null +++ b/Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.dll.meta @@ -0,0 +1,118 @@ +fileFormatVersion: 2 +guid: 3781f2218eef4d5a823dba406baa434b +labels: +- gvh +- gvh_targets-editor +- gvh_version-13.1.0 +- gvhp_exportpath-Firebase/Editor/Firebase.Crashlytics.Editor.dll +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 0 + platformData: + - first: + : Linux + second: + enabled: 0 + settings: + CPU: None + - first: + : LinuxUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + : OSXIntel + second: + enabled: 0 + settings: + CPU: None + - first: + : OSXIntel64 + second: + enabled: 0 + settings: + CPU: None + - first: + : Web + second: + enabled: 0 + settings: {} + - first: + : WebStreamed + second: + enabled: 0 + settings: {} + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + CompileFlags: + FrameworkDependencies: + - first: + tvOS: tvOS + second: + enabled: 0 + settings: + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.pdb b/Assets/rd3/Firebase/Editor/Firebase.Crashlytics.Editor.pdb new file mode 100644 index 0000000000000000000000000000000000000000..9a396b2a43ba5d8f3ba5a3e3faeb4a82de4c1200 GIT binary patch literal 9916 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 BjF`3NM$aU5{I9AbwgG}KXS#TK?DvyzjT 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 literal 0 HcmV?d00001 diff --git a/Assets/rd3/Firebase/Plugins/iOS/Firebase.Crashlytics.pdb.meta b/Assets/rd3/Firebase/Plugins/iOS/Firebase.Crashlytics.pdb.meta new file mode 100644 index 0000000..7772bc1 --- /dev/null +++ b/Assets/rd3/Firebase/Plugins/iOS/Firebase.Crashlytics.pdb.meta @@ -0,0 +1,81 @@ +fileFormatVersion: 2 +guid: fc067b050957416ea05be8e76ac98a69 +labels: +- gvh +- gvh_version-13.1.0 +- gvhp_exportpath-Firebase/Plugins/iOS/Firebase.Crashlytics.pdb +timeCreated: 1480838400 +PluginImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + isPreloaded: 0 + platformData: + Android: + enabled: 0 + settings: + CPU: AnyCPU + Any: + enabled: 0 + settings: {} + Editor: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + Linux: + enabled: 0 + settings: + CPU: None + Linux64: + enabled: 0 + settings: + CPU: None + LinuxUniversal: + enabled: 0 + settings: + CPU: None + OSXIntel: + enabled: 0 + settings: + CPU: None + OSXIntel64: + enabled: 0 + settings: + CPU: None + OSXUniversal: + enabled: 0 + settings: + CPU: None + Web: + enabled: 0 + settings: {} + WebStreamed: + enabled: 0 + settings: {} + Win: + enabled: 0 + settings: + CPU: None + Win64: + enabled: 0 + settings: + CPU: None + WindowsStoreApps: + enabled: 0 + settings: + CPU: AnyCPU + iOS: + enabled: 1 + settings: + CompileFlags: + FrameworkDependencies: + tvOS: + enabled: 1 + settings: + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/rd3/Firebase/m2repository/com.meta b/Assets/rd3/Firebase/m2repository/com.meta index 03f8f60..dbb5378 100644 --- a/Assets/rd3/Firebase/m2repository/com.meta +++ b/Assets/rd3/Firebase/m2repository/com.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: bd4bb8f7b21d83945a529785442ad476 +guid: a03680944a70bd645bd7d1dbdf2a0c32 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/rd3/Firebase/m2repository/com/google.meta b/Assets/rd3/Firebase/m2repository/com/google.meta index 2723a01..46cc707 100644 --- a/Assets/rd3/Firebase/m2repository/com/google.meta +++ b/Assets/rd3/Firebase/m2repository/com/google.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8cbdb7918f043994a9eed3435e3ab3e7 +guid: 17a890ddad2c71941be3cd4c54417aa8 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/rd3/Firebase/m2repository/com/google/firebase.meta b/Assets/rd3/Firebase/m2repository/com/google/firebase.meta index fce46eb..9c3d19d 100644 --- a/Assets/rd3/Firebase/m2repository/com/google/firebase.meta +++ b/Assets/rd3/Firebase/m2repository/com/google/firebase.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 187d24513d48523459a2744fb57c9a5e +guid: 60ff2816b32efb846a2676821700f989 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity.meta b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity.meta new file mode 100644 index 0000000..7c66951 --- /dev/null +++ b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0dc9525fe3d784645add9b1dbbf8c054 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0.meta b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0.meta new file mode 100644 index 0000000..fc120e3 --- /dev/null +++ b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 420f611864d0eaa49af58bc665ac82e7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.pom b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.pom new file mode 100644 index 0000000..7befe3c --- /dev/null +++ b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.pom @@ -0,0 +1,13 @@ + + 4.0.0 + com.google.firebase + firebase-crashlytics-unity + 13.1.0 + aar + + + + diff --git a/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.pom.meta b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.pom.meta new file mode 100644 index 0000000..27ba1b6 --- /dev/null +++ b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.pom.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 22d139e4dda4455aa678b0397461628f +labels: +- gvh +- gvh_version-13.1.0 +- gvhp_exportpath-Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.pom +timeCreated: 1480838400 +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.srcaar b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/13.1.0/firebase-crashlytics-unity-13.1.0.srcaar new file mode 100644 index 0000000000000000000000000000000000000000..e726ad65f6160f6a86ce3e2239081fb0b7b789fe GIT binary patch literal 186520 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 + com.google.firebase + firebase-crashlytics-unity + + 13.1.0 + 13.1.0 + + + diff --git a/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/maven-metadata.xml.meta b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/maven-metadata.xml.meta new file mode 100644 index 0000000..a6bfbbd --- /dev/null +++ b/Assets/rd3/Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/maven-metadata.xml.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eda65244fc1b407e8948b25b31c17991 +labels: +- gvh +- gvh_version-13.1.0 +- gvhp_exportpath-Firebase/m2repository/com/google/firebase/firebase-crashlytics-unity/maven-metadata.xml +timeCreated: 1480838400 +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/ProjectSettings/AndroidResolverDependencies.xml b/ProjectSettings/AndroidResolverDependencies.xml new file mode 100644 index 0000000..053901c --- /dev/null +++ b/ProjectSettings/AndroidResolverDependencies.xml @@ -0,0 +1,66 @@ + + + androidx.annotation:annotation:1.2.0 + androidx.appcompat:appcompat:1.6.1 + androidx.browser:browser:1.4.0 + androidx.constraintlayout:constraintlayout:2.1.4 + androidx.lifecycle:lifecycle-process:2.6.2 + androidx.media3:media3-exoplayer:1.0.0-alpha01 + androidx.recyclerview:recyclerview:1.1.0 + androidx.recyclerview:recyclerview:1.2.1 + com.applovin.mediation:bigoads-adapter:5.5.1.2 + com.applovin.mediation:bytedance-adapter:7.5.0.3.0 + com.applovin.mediation:fyber-adapter:8.3.8.0 + com.applovin.mediation:google-adapter:[24.5.0.0] + com.applovin.mediation:google-ad-manager-adapter:[24.5.0.0] + com.applovin.mediation:mintegral-adapter:16.9.91.0 + com.applovin.mediation:moloco-adapter:4.0.0.0 + com.applovin.mediation:unityads-adapter:4.16.1.0 + com.applovin.mediation:vungle-adapter:7.5.1.0 + com.applovin:applovin-sdk:13.3.1 + com.bigossp:bigo-ads:5.0.0 + com.bigossp:bigo-ads:5.3.0 + com.fyber:marketplace-sdk:8.3.7 + com.google.android.gms:play-services-ads:24.4.0 + com.google.android.gms:play-services-ads:24.5.0 + com.google.android.gms:play-services-ads-identifier:18.0.1 + com.google.android.gms:play-services-ads-identifier:18.2.0 + com.google.android.gms:play-services-base:18.7.2 + com.google.android.gms:play-services-basement:18.1.0 + com.google.android.material:material:1.2.1 + com.google.android.ump:user-messaging-platform:3.2.0 + com.google.firebase:firebase-analytics:23.0.0 + com.google.firebase:firebase-analytics-unity:13.1.0 + com.google.firebase:firebase-app-unity:13.1.0 + com.google.firebase:firebase-common:22.0.0 + com.google.firebase:firebase-config:23.0.0 + com.google.firebase:firebase-config-unity:13.1.0 + com.google.firebase:firebase-crashlytics-ndk:20.0.0 + com.google.firebase:firebase-crashlytics-unity:13.1.0 + com.mbridge.msdk.oversea:mbridge_android_sdk:16.9.71 + com.pangle.global:pag-sdk:7.2.0.6 + com.unity3d.ads:unity-ads:4.14.0 + com.vungle:vungle-ads:7.5.0 + io.github.kwainetwork:adApi:1.2.19 + io.github.kwainetwork:adImpl:1.2.19 + org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ProjectSettings/GvhProjectSettings.xml b/ProjectSettings/GvhProjectSettings.xml index c5e8f98..47b0a3f 100644 --- a/ProjectSettings/GvhProjectSettings.xml +++ b/ProjectSettings/GvhProjectSettings.xml @@ -1,5 +1,6 @@ + diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index e16162b..2ddb3b9 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -163,7 +163,7 @@ PlayerSettings: androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.1 applicationIdentifier: - Android: com.money.unitysdk + Android: com.rush.cash.earn.fast.real.money.game Standalone: com.DefaultCompany.2DProject buildNumber: Standalone: 0