XenonSDK.m 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. //
  2. // XenonSDK.m
  3. // XenonSDK
  4. //
  5. // Created by SAGESSE on 2019/3/11.
  6. // Copyright © 2019 SAGESSE. All rights reserved.
  7. //
  8. #import "XenonSDK.h"
  9. #import <Security/Security.h>
  10. #import <sys/utsname.h>
  11. #if SDK_HAS_IDFA
  12. #import <AdSupport/AdSupport.h>
  13. #endif
  14. #import "BubbleView.h"
  15. #import "RootViewController.h"
  16. #import "NoticeViewController.h"
  17. #import "UserBindPhoneViewController.h"
  18. #import "UserLoginFastViewController.h"
  19. #import "UserLoginPhoneViewController.h"
  20. #import "UserCenterViewController.h"
  21. #import "XSUtils.h"
  22. #import <objc/message.h>
  23. #import "KDIAPManager.h"
  24. @interface XenonSDK ()
  25. @end
  26. ///
  27. /// Game SDK
  28. ///
  29. @implementation XenonSDK
  30. @synthesize user = _user;
  31. @synthesize token = _token;
  32. ///
  33. /// A sington instance of SDK.
  34. ///
  35. + (instancetype)sharedSDK {
  36. static id sharedSDK = nil;
  37. static dispatch_once_t onceToken;
  38. dispatch_once(&onceToken, ^{
  39. sharedSDK = XenonSDK.new;
  40. if (XSNetwork.baseURL == nil) {
  41. //测试地址 @"http://47.56.223.128:9099";
  42. //国内正式地址 @"https://app.funcheergame.com";
  43. //国外正式地址 @"https://fapp.funcheergame.com"
  44. XSNetwork.baseURL = @"https://fapp.funcheergame.com";
  45. }
  46. });
  47. return sharedSDK;
  48. }
  49. ///
  50. /// Init the sdk with application code.
  51. /// \param parameter The parameter provided by the platform.
  52. ///
  53. /// \param complete This closure called when init complete.
  54. ///
  55. - (void)initWithParameter:(NSString*)parameter complete:(void (^)(NSError* error))complete {
  56. // If init is complete, ignore it this call.
  57. if (self.configuration != nil) {
  58. if (complete != nil) {
  59. complete(nil); // sdk is ready.
  60. }
  61. return;
  62. }
  63. // Parse pre-configuration for parameter.
  64. NSArray* configs = [parameter componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"|&"]];
  65. if (configs.count != 4) {
  66. if (complete != nil) {
  67. complete([XSNetwork errorWithCode:-1 message:@"参数错误"]); // error -1
  68. }
  69. return ;
  70. }
  71. // Update pre-configuration for parameter.
  72. self.appId = configs[0];
  73. self.channelId = configs[1];
  74. self.adId = configs[2];
  75. self.adFlag = configs[3];
  76. // Update system configration for server.
  77. [XSNetwork configureWithIdentifier:self.adId flags:self.adFlag complete:^(XSConfiguration* object, NSError *error) {
  78. // If an error occurs, directly callback cp.
  79. if (error != nil) {
  80. if (complete != nil) {
  81. complete(error);
  82. }
  83. return;
  84. }
  85. // An unknown error.
  86. if (object.closeState != 0) {
  87. // Display an system notice.
  88. id qq = self.configuration.fixLinkQQ ?: @"";
  89. id tel = self.configuration.fixLinkTel ?: @"";
  90. // Set the update title & contents.
  91. id contents = [NSString stringWithFormat:@"\n亲爱的玩家您好, 由于服务器紧急维护, 目前游戏暂时无法进入, 给您带来的不便我们深表歉意!\n如果您还有其他问题请与我们联系!\n\n客服QQ: %@\n客服电话: %@", qq, tel];
  92. UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"系统公告" message:contents preferredStyle:UIAlertControllerStyleAlert];
  93. [alert addAction:[UIAlertAction actionWithTitle:@"我知道了" style:UIAlertActionStyleDefault handler:nil]];
  94. self.rootViewController = alert;
  95. complete([XSNetwork errorWithCode:-8 message:nil]);
  96. return;
  97. }
  98. // Setup sdk with server configuration.
  99. self.configuration = object;
  100. // Get third-party platform parameters.
  101. if (self.configuration.thirdPartyPrams.length != 0) {
  102. NSDictionary* thirdPartyPrams = [NSJSONSerialization JSONObjectWithData:[self.configuration.thirdPartyPrams dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
  103. // If the tracker is turned on, create the object.
  104. int mask = 0;
  105. NSString* dps = thirdPartyPrams[@"dp"];
  106. if (dps.length != 0) {
  107. sscanf(dps.UTF8String, "%x", &mask);
  108. if (mask != 0) {
  109. XSTracker.shared = [XSTracker new];
  110. XSTracker.shared.mask = mask;
  111. [XSNetwork trigger:20];
  112. }
  113. }
  114. // In the initialization.
  115. [XSTracker.shared trace:0x010000 parameters:[NSString stringWithFormat:@"%@", NSProcessInfo.processInfo.environment]];
  116. // If an advertisement parameter is specified, try to parse advertising parameter.
  117. NSString* adPrams = self.configuration.adPrams;
  118. if (adPrams.length != 0) {
  119. // Parse advertising parameter.
  120. [[adPrams componentsSeparatedByString:@"&"] enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  121. // Value must be `key=value`
  122. NSArray* tmp = [obj componentsSeparatedByString:@"="];
  123. if (tmp.count != 2) {
  124. return;
  125. }
  126. if ([tmp[0] isEqualToString:@"td_app_id"]) {
  127. self.configuration.tdAppId = tmp[1];
  128. }
  129. if ([tmp[0] isEqualToString:@"td_game_id"]) {
  130. self.configuration.tdGameId = tmp[1];
  131. }
  132. }];
  133. // If td game id is not provided, the TD sdk is not initialized.
  134. if (self.configuration.tdAppId.length != 0 && self.configuration.tdGameId.length != 0) {
  135. // Init for data analzer server.
  136. XSCollector.shared = [[XSCollector alloc] initWithAppId:self.configuration.tdAppId gameId:self.configuration.tdGameId];
  137. }
  138. }
  139. }
  140. [self sdk_checkVersion:^{
  141. [self sdk_checkNote:^{
  142. // Check workflow ended.
  143. [XSTracker.shared trace:0x010001];
  144. // Init sdk complete.
  145. if (complete != nil)
  146. complete(nil);
  147. // init workflow ended.
  148. [XSTracker.shared trace:0x010002];
  149. }];
  150. }];
  151. }];
  152. }
  153. ///
  154. /// Login the sdk.
  155. /// \param complete This closure called when login complete.
  156. ///
  157. - (void)loginWithComplete:(void (^)(id user, NSError* error))complete {
  158. // If init is complete, ignore it this call.
  159. if (self.configuration == nil) {
  160. if (complete != nil)
  161. complete(nil, [XSNetwork errorWithCode:-1 message:@"请先初始化"]); // error -1
  162. return;
  163. }
  164. [XSTracker.shared trace:0x010010];
  165. // Unified operation after successful login.
  166. void (^loginedCallback)(void) = ^{
  167. // Build return data.
  168. id user = nil;
  169. if (self.user != nil) {
  170. user = @{
  171. @"uid": self.user.uid,
  172. @"token": self.user.token,
  173. @"userName": self.user.userName ?: @"",
  174. @"phone": self.user.phone ?: @""
  175. };
  176. // According to air bubbles.
  177. if (!self.configuration.isFloatDisable) {
  178. UIWindow* window = UIApplication.sharedApplication.delegate.window ?: UIApplication.sharedApplication.keyWindow;
  179. BubbleView* view = [[BubbleView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
  180. [view.contentView addTarget:self action:@selector(center) forControlEvents:UIControlEventTouchUpInside];
  181. [window addSubview:view];
  182. // It's shown in the first one.
  183. UIView* xmp = [window viewWithTag:-1];
  184. if (xmp != nil) {
  185. [window bringSubviewToFront:xmp];
  186. }
  187. [self.bubbleView removeFromSuperview];
  188. self.bubbleView = view;
  189. }
  190. [XSTracker.shared trace:0x010013];
  191. }
  192. [XSTracker.shared trace:0x010011];
  193. if (complete) {
  194. complete(user, nil);
  195. }
  196. [XSTracker.shared trace:0x010012];
  197. };
  198. // Try automatically login if needed.
  199. if (self.user != nil) {
  200. [XSNetwork showHudLoading];
  201. [XSNetwork authWithUser:self.user.uid complete:^(XSUser* user, NSError *error) {
  202. // If the login fails, clear the user information and login again.
  203. if (user.token.length == 0 || error != nil) {
  204. [XSNetwork showHudFailure:@"登录时效期已过,请重新登录"];
  205. self.user = nil;
  206. [self loginWithComplete:complete];
  207. return;
  208. }
  209. // Update user info.
  210. XSUser* duser = self.user;
  211. duser.token = user.token;
  212. duser.isRegister = false;
  213. self.user = duser;
  214. // Login successful, display HUD.
  215. [XSNetwork showPrompt:duser.name];
  216. // Check if the user is already connected to the phone.
  217. [self sdk_checkPhone:^{
  218. loginedCallback();
  219. }];
  220. }];
  221. [XSTracker.shared trace:0x010014];
  222. return;
  223. }
  224. // Setup login callback.
  225. __weak __typeof(self) ws = self;
  226. self.sdk_loginCallback = ^(XSUser* user) {
  227. [ws setUser:user];
  228. [ws.rootViewController dismissViewControllerAnimated:YES completion:nil];
  229. // Login successful, display HUD.
  230. [XSNetwork showPrompt:user.name];
  231. // Check if the user is already connected to the phone.
  232. [ws sdk_checkPhone:^{
  233. loginedCallback();
  234. }];
  235. };
  236. // The last time was to log out, so the quick login list is displayed.
  237. if (XSUser.history.count != 0) {
  238. self.rootViewController = [[RootViewController alloc] initWithRootViewController:UserLoginFastViewController.new];
  239. return;
  240. }
  241. // To account login
  242. self.rootViewController = [[RootViewController alloc] initWithRootViewController:UserLoginPhoneViewController.new];
  243. }
  244. ///
  245. /// Logout the sdk.
  246. /// \param complete This closure called when logout complete.
  247. ///
  248. - (void)logoutWithComplete:(void (^)(void))complete {
  249. // If init is complete, ignore it this call.
  250. if (self.configuration == nil) {
  251. if (complete != nil)
  252. complete();
  253. return;
  254. }
  255. self.user = nil;
  256. [self.bubbleView removeFromSuperview];
  257. self.bubbleView = nil;
  258. if (complete != nil)
  259. complete();
  260. [XSTracker.shared trace:0x010020];
  261. }
  262. ///
  263. /// Logout the game and sdk.
  264. /// \param handler This closure called when user logout in web view.
  265. /// \note Warning this handler will retain in until next call this method.
  266. ///
  267. - (void)logoutWhenNotification:(void (^)(void))handler {
  268. self.sdk_logoutCallback = ^(id user) {
  269. if (handler != nil)
  270. handler();
  271. [XSTracker.shared trace:0x010020];
  272. };
  273. }
  274. ///
  275. /// Buy a product.
  276. /// \param parameters Payment request parameters.
  277. /// \param complete Payment result of the callback, if the apple payment is completed before the callback, other payments will be the official launch of the callback
  278. ///
  279. - (void)buy:(NSDictionary*)parameters complete:(void (^)(id order, NSError* error))complete {
  280. // Check paramrters.
  281. if (self.configuration == nil || self.user == nil) {
  282. if (complete != nil) {
  283. complete(nil, [XSNetwork errorWithCode:-1 message:@"请先登录"]); // error -1
  284. return ;
  285. }
  286. }
  287. [XSTracker.shared trace:0x010030];
  288. [XSNetwork showHudLoading];
  289. //
  290. // [XSNetwork routeWithComplete:^(id object, NSError *error) {
  291. //
  292. // if (error != nil) {
  293. // [XSNetwork showHudFailure:error];
  294. // if (complete != nil) {
  295. // complete(nil, error);
  296. // }
  297. // return;
  298. // }
  299. // Get parmeters.
  300. XSUser* user = self.user;
  301. //NSString* path = object[fqsd(@"PREAACs/NCYsJjQuLRkA")];
  302. //#if SDK_HAS_H5_PAYMENT
  303. //Generate h5 payment parameters.
  304. // if (path.length != 0) {
  305. //
  306. // [XSNetwork hideHud];
  307. // [XSTracker.shared trace:0x010031];
  308. //
  309. // id payments = @{
  310. // @"cpBillNo": parameters[@"orderId"] ?: @"",
  311. // @"orderAmount": parameters[@"amount"] ?: @"",
  312. // @"subject": parameters[@"productName"] ?: @"",
  313. //
  314. // @"roleName": parameters[@"roleName"] ?: @"",
  315. // @"serverId": parameters[@"serverId"] ?: @"",
  316. // @"extraInfo": parameters[@"extraInfo"] ?: @"",
  317. //
  318. // @"phone": @"",
  319. // @"isTest": @"1",
  320. // @"orderPlatform": @"0",
  321. // @"orderType": @"1",
  322. // @"remark": @"0",
  323. // @"isApp": @"0",
  324. //
  325. // @"uid": user.uid,
  326. // @"userName": user.userName ?: @""
  327. // };
  328. //
  329. // NSURLComponents* components = [NSURLComponents componentsWithString:path];
  330. // NSDictionary* json = [XSNetwork bodyWithParameters:payments];
  331. // NSString* str = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:json options:0 error:nil] encoding:NSUTF8StringEncoding];
  332. //
  333. // components.queryItems = @[[NSURLQueryItem queryItemWithName:@"jsonData" value:str],
  334. // [NSURLQueryItem queryItemWithName:@"v" value:@"1"]];
  335. //
  336. // if (components.URL == nil) {
  337. // if (complete != nil) {
  338. // complete(nil, [XSNetwork errorWithCode:-1 message:@"后台参数错误"]);
  339. // }
  340. // return;
  341. // }
  342. //
  343. // UserCenterViewController* rootViewController = UserCenterViewController.new;
  344. // rootViewController.custom = components.URL.absoluteString;
  345. // rootViewController.otherView = self.bubbleView;
  346. // rootViewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
  347. // self.rootViewController = rootViewController;
  348. //
  349. // // Cannot get the callback state.
  350. // rootViewController.callback = ^{
  351. // if (complete != nil) {
  352. // complete(nil, nil);
  353. // }
  354. // };
  355. // return;
  356. // }
  357. //#endif
  358. //#if SDK_HAS_IAP_PAYMENT
  359. // Generate iap payment parameters.
  360. id payments = @{
  361. @"productCode": parameters[@"productCode"] ?: @"",
  362. @"isCoin": @"0",
  363. @"orderPlatform": @"0",
  364. @"orderType": @"1",
  365. @"orderAmount": parameters[@"amount"] ?: @"",
  366. @"isTest": @"0",
  367. @"subject": parameters[@"productName"] ?: @"",
  368. @"roleName": parameters[@"roleName"] ?: @"",
  369. @"cpBillNo": parameters[@"orderId"] ?: @"",
  370. @"remark": @"0",
  371. @"serverId": parameters[@"serverId"] ?: @"",
  372. @"extraInfo": parameters[@"extraInfo"] ?: @"",
  373. @"uid": user.uid,
  374. @"userName": user.userName ?: @""
  375. };
  376. [XSTracker.shared trace:0x010032];
  377. [XSNetwork payWithParameters:payments complete:^(id object, NSError *error) {
  378. if (error != nil) {
  379. [XSNetwork showHudFailure:error];
  380. if (complete != nil) {
  381. complete(nil, error);
  382. }
  383. return;
  384. }
  385. [XSTracker.shared trace:0x010034];
  386. [XSNetwork showHudSuccess:@"支付成功"];
  387. if (complete != nil)
  388. complete(payments, nil);
  389. }];
  390. //#endif
  391. //}];
  392. }
  393. ///
  394. /// User Center.
  395. ///
  396. - (void)center {
  397. if (self.configuration == nil || self.user == nil) {
  398. return;
  399. }
  400. [XSTracker.shared trace:0x010040];
  401. UserCenterViewController* rootViewController = UserCenterViewController.new;
  402. rootViewController.otherView = self.bubbleView;
  403. rootViewController.modalPresentationStyle = UIModalPresentationOverFullScreen;
  404. self.rootViewController = rootViewController;
  405. }
  406. /// Report role info.
  407. /// \param roleName This role name.
  408. ///
  409. /// \param level This role current level.
  410. ///
  411. /// \param serverId This role current server.
  412. ///
  413. - (void)reportWithRoleName:(NSString*)roleName level:(NSInteger)level serverId:(NSString*)serverId {
  414. if (self.configuration == nil || self.user == nil) {
  415. return;
  416. }
  417. [XSNetwork reportWithUser:self.user.userName Name:roleName level:level server:serverId];
  418. [XSCollector.shared reportWithAccount:self.user.uid name:roleName level:level server:serverId];
  419. }
  420. // MARK: -
  421. - (void)sdk_checkVersion:(void(^)(void))complete {
  422. // If no configuration file is provided, initialization failed.
  423. if (self.configuration == nil) {
  424. complete();
  425. return;
  426. }
  427. // If update cheker is not enabled, ignore it.
  428. NSURL* url = [NSURL URLWithString:self.configuration.updateUrl];
  429. if (url == nil || self.configuration.updateState == 0) {
  430. complete();
  431. return;
  432. }
  433. // If the prompt is too frequent, wait.
  434. // 1 is normal update.
  435. if (self.configuration.updateState == 1 && !([XSDataCenter doubleForKey:@"verion-later"] < NSDate.new.timeIntervalSince1970)) {
  436. complete();
  437. return;
  438. }
  439. NoticeViewController* viewController = NoticeViewController.new;
  440. // Set the update title & contents.
  441. viewController.title = @"发现新版本";
  442. viewController.contents = [NSString stringWithFormat:@"亲爱的玩家您好,我们发布了新的版本, 更新到新版本可体验更多新奇有趣的玩法!\n\n最新版本: %@\n当前版本: %@\n\n\n", self.configuration.updateVersion, self.shortVersion];
  443. // Set the confirm closure.
  444. [viewController addActionWithName:@"立即升级" handler:^{
  445. // -[UIApplication openURL:]
  446. ((void(*)(id,SEL,id))objc_msgSend)(UIApplication.sharedApplication, NSSelectorFromString(fqsd(@"p2cAAMrAyrKwq5rIAA==")), url);
  447. }];
  448. // Set the cancel closure.
  449. if (self.configuration.updateState == 1) {
  450. __weak __typeof(self) ws = self;
  451. [viewController addActionWithName:@"稍后再说" handler:^{
  452. // Again 24 hours later.
  453. [XSDataCenter setDouble:[NSDate dateWithTimeIntervalSinceNow:24*60*60].timeIntervalSince1970 forKey:@"verion-later"];
  454. if (ws.rootViewController == nil) {
  455. complete();
  456. return;
  457. }
  458. // Automatic hidden controller.
  459. [ws.rootViewController dismissViewControllerAnimated:YES completion:complete];
  460. }];
  461. }
  462. // Show view contorller in key window.
  463. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  464. self.rootViewController = [[RootViewController alloc] initWithRootViewController:viewController];
  465. });
  466. }
  467. - (void)sdk_checkNote:(void(^)(void))complete {
  468. // If no configuration file is provided, initialization failed.
  469. NSURL* url = [NSURL URLWithString:self.configuration.closeUrl];
  470. if (url == nil || self.configuration.linkQq == 0) {
  471. complete();
  472. return ;
  473. }
  474. // If the announcement is displayed, it is no longer displayed.
  475. if (self.configuration.linkQq != 2 && [[XSDataCenter stringForKey:@"notice-later"] isEqualToString:url.absoluteString]) {
  476. complete();
  477. return;
  478. }
  479. NoticeViewController* viewController = NoticeViewController.new;
  480. // Set the update title & contents.
  481. viewController.title = @"系统公告";
  482. viewController.contents = url;
  483. // Set the confirm closure.
  484. __weak __typeof(self) ws = self;
  485. [viewController addActionWithName:@"我知道了" handler:^{
  486. // Never again.
  487. [XSDataCenter setString:url.absoluteString forKey:@"notice-later"];
  488. if (ws.rootViewController == nil) {
  489. complete();
  490. return;
  491. }
  492. // Automatic hidden controller.
  493. [ws.rootViewController dismissViewControllerAnimated:YES completion:complete];
  494. }];
  495. // Show view contorller in key window.
  496. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  497. self.rootViewController = [[RootViewController alloc] initWithRootViewController:viewController];
  498. });
  499. }
  500. - (void)sdk_checkPhone:(void(^)(void))complete {
  501. // Check if the phone character exists.
  502. if (self.user.isRegister || self.user.phone.length != 0 || self.configuration.serverStatus == 1) {
  503. complete();
  504. return;
  505. }
  506. // If the prompt is too frequent, wait.
  507. id key = [NSString stringWithFormat:@"%@-bind-later", self.user.uid];
  508. if ([XSDataCenter doubleForKey:key] > NSDate.new.timeIntervalSince1970) {
  509. complete();
  510. return;
  511. }
  512. UserBindPhoneViewController* bindViewController = UserBindPhoneViewController.new;
  513. RootViewController* rooViewController = [[RootViewController alloc] initWithRootViewController:bindViewController];
  514. UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"您的帐号存在风险, 为了您的帐号安全建议您绑定手机号。" preferredStyle:UIAlertControllerStyleAlert];
  515. // Add confirm action.
  516. [alert addAction:[UIAlertAction actionWithTitle:@"绑定手机" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  517. bindViewController.callback = complete;
  518. self.rootViewController = rooViewController;
  519. }]];
  520. // Add cancel action.
  521. [alert addAction:[UIAlertAction actionWithTitle:@"稍后提醒" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  522. // Again 24 hours later.
  523. [XSDataCenter setDouble:[NSDate dateWithTimeIntervalSinceNow:24*60*60].timeIntervalSince1970 forKey:key];
  524. // Continue.
  525. complete();
  526. }]];
  527. (void)rooViewController.view; // preload;
  528. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  529. self.rootViewController = alert;
  530. });
  531. // Hide all hud if needed.
  532. [XSNetwork hideHud];
  533. }
  534. // MARK: -
  535. - (void)setUser:(XSUser *)user {
  536. _user = user;
  537. _token = user.token;
  538. // Cache.
  539. [XSDataCenter setValue:user forKey:@"User.current"];
  540. if (user == nil) {
  541. return;
  542. }
  543. // Make a new login history.
  544. NSMutableSet* ids = [NSMutableSet set];
  545. NSMutableArray* history = [NSMutableArray array];
  546. user.lastTime = NSDate.new.timeIntervalSince1970;
  547. [ids addObject:user.uid];
  548. [history addObject:user];
  549. for (XSUser* tmp in XSUser.history) {
  550. if (![ids containsObject:tmp.uid]) {
  551. [ids addObject:tmp.uid];
  552. [history addObject:tmp];
  553. }
  554. }
  555. XSUser.history = history;
  556. }
  557. - (XSUser*)user {
  558. if (_user != nil) {
  559. return _user;
  560. }
  561. static dispatch_once_t onceToken;
  562. dispatch_once(&onceToken, ^{
  563. self->_user = [XSDataCenter valueForKey:@"User.current"];
  564. self->_token = self->_user.token;
  565. });
  566. return _user;
  567. }
  568. - (void)setRootViewController:(UIViewController *)rootViewController {
  569. _rootViewController = rootViewController;
  570. if (rootViewController == nil) {
  571. return;
  572. }
  573. [UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:rootViewController
  574. animated:YES
  575. completion:nil];
  576. }
  577. /// Permanent preservation of data.
  578. - (void)setPermanentValue:(NSString*)value forKey:(NSString*)key {
  579. // Set value to user defautls.
  580. [XSDataCenter setString:value forKey:key];
  581. // Configure the search setting.
  582. id setting = @{
  583. // Configure the keychain key type.
  584. CFBridgingRelease(kSecClass): CFBridgingRelease(kSecClassGenericPassword),
  585. CFBridgingRelease(kSecAttrAccount): key
  586. }.mutableCopy;
  587. // Delete old item before add new item
  588. SecItemDelete(CFBridgingRetain(setting));
  589. // Add new object to search dictionary.
  590. [setting setObject:[value dataUsingEncoding:NSUTF8StringEncoding] forKey:CFBridgingRelease(kSecValueData)];
  591. // Add item to keychain with the search dictionary
  592. SecItemAdd(CFBridgingRetain(setting), nil);
  593. }
  594. /// Read data for permanent preservationed.
  595. - (id)permanentValueForKey:(NSString*)key {
  596. // Read data from user defatuls.
  597. NSString* udid = [XSDataCenter stringForKey:key];
  598. if (udid.length != 0) {
  599. return udid;
  600. }
  601. // Configure the search setting.
  602. id setting = @{
  603. // Configure the keychain key type.
  604. CFBridgingRelease(kSecClass): CFBridgingRelease(kSecClassGenericPassword),
  605. // The class name of the SDK is dynamic.
  606. CFBridgingRelease(kSecAttrService): NSStringFromClass(self.class),
  607. CFBridgingRelease(kSecAttrAccount): key,
  608. // Gets result data of data.
  609. CFBridgingRelease(kSecReturnData): CFBridgingRelease(kCFBooleanTrue)
  610. };
  611. // Start query the keychain query.
  612. CFTypeRef result = nil;
  613. if (SecItemCopyMatching(CFBridgingRetain(setting), &result) == noErr) {
  614. return nil;
  615. }
  616. // Convert data to a string
  617. NSString* value = [[NSString alloc] initWithData:CFBridgingRelease(result) encoding:NSUTF8StringEncoding];
  618. if (value.length == 0) {
  619. return nil;
  620. }
  621. // Set value to user defautls, reduce Keychain access count.
  622. [XSDataCenter setString:value forKey:key];
  623. return value;
  624. }
  625. - (NSString*)advertisingIdentifier {
  626. // Read the data from keychain.
  627. NSString* advertisingIdentifier = [self permanentValueForKey:@"udid"];
  628. if (advertisingIdentifier.length != 0) {
  629. return advertisingIdentifier;
  630. }
  631. #if SDK_HAS_IDFA
  632. // Try to obtain idfa.
  633. ASIdentifierManager* advertisingManager = ASIdentifierManager.sharedManager;
  634. advertisingIdentifier = advertisingManager.advertisingIdentifier.UUIDString;
  635. if (!advertisingManager.isAdvertisingTrackingEnabled) {
  636. advertisingIdentifier = [NSString stringWithFormat:@"#%@", NSUUID.new.UUIDString];
  637. }
  638. #else
  639. // Generate an id to replace idfa.
  640. advertisingIdentifier = [NSString stringWithFormat:@"#%@", NSUUID.new.UUIDString];
  641. #endif
  642. // Save to keychain to prevent updates.
  643. [self setPermanentValue:advertisingIdentifier forKey:@"udid"];
  644. return advertisingIdentifier;
  645. }
  646. - (NSString*)advertisingVendor {
  647. // This parameter has been deprecated and must be empty string.
  648. return @"";
  649. }
  650. - (NSBundle*)bundle {
  651. return [NSBundle bundleForClass:self.class];
  652. }
  653. - (NSString*)model {
  654. struct utsname sinfo;
  655. uname(&sinfo);
  656. return [NSString stringWithUTF8String: sinfo.machine];
  657. }
  658. /// The sdk verions.
  659. - (NSString*)version {
  660. return self.bundle.infoDictionary[@"CFBundleShortVersionString"] ?: @"1.0";
  661. }
  662. /// The game verions.
  663. - (NSString*)shortVersion {
  664. return NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"] ?: @"1.0";
  665. }
  666. ///check In-app Purchase lost list.
  667. - (void)checkOrderStatus {
  668. //检查内购掉单.
  669. [KDIAPManager checkOrderStatus];
  670. }
  671. ///手机震动
  672. -(void)iphoneVibrate {
  673. //手机--设置--声音--响铃模式震动(打开),不然无效.
  674. AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
  675. }
  676. //跳转appstore评论
  677. -(void)skipToAppsotreWithID:(NSString *)appleId {
  678. NSString *urlStr = [NSString stringWithFormat:@"https://itunes.apple.com/us/app/itunes-u/id%@?action=write-review&mt=8",
  679. appleId];
  680. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlStr]];
  681. }
  682. ///自动登录
  683. - (void)xautoWithComplete:(void (^)(id user, NSError* error))complete {
  684. XSUser* user = [XSDataCenter valueForKey:@"User.current"];
  685. if (user.uid.length == 0) {
  686. complete(nil,[XSNetwork errorWithCode:-1 message:@"未登录记录!"]);
  687. return;
  688. }
  689. // Try authorization login.
  690. XenonSDK.sharedSDK.token = user.token;
  691. [XSNetwork authWithUser:user.uid complete:^(XSUser* object, NSError *error) {
  692. // Check for general errors.
  693. if (error != nil && ![error.domain isEqual:@"XSNetwork"]) {
  694. [XSNetwork showHudFailure:error];
  695. complete(nil,[XSNetwork errorWithCode:-1 message:error.domain]);
  696. return;
  697. }
  698. // Login successful.
  699. if (user.token.length != 0 && error == nil) {
  700. // Update user info.
  701. user.token = object.token;
  702. user.isRegister = NO;
  703. XenonSDK.sharedSDK.user = user;
  704. if (XenonSDK.sharedSDK.sdk_loginCallback) {
  705. complete(user,nil);
  706. XenonSDK.sharedSDK.sdk_loginCallback(user);
  707. }
  708. return;
  709. }
  710. [XSNetwork showHudFailure:@"登录时效期已过,请重新登录"];
  711. complete(nil,[XSNetwork errorWithCode:-1 message:@"登录时效期已过,请重新登录"]);
  712. // Login failed to login page.
  713. UserLoginPhoneViewController* viewController = UserLoginPhoneViewController.new;
  714. [viewController automatic:user.account];
  715. [self.rootViewController.navigationController pushViewController:viewController animated:YES];
  716. }];
  717. }
  718. ///手动登录
  719. -(void)manualLogin {
  720. //XSUser *user = [XSDataCenter valueForKey:@"User.current"];
  721. if (XSUser.history.count != 0) {
  722. self.rootViewController = [[RootViewController alloc] initWithRootViewController:UserLoginFastViewController.new];
  723. return;
  724. }
  725. // To account login
  726. self.rootViewController = [[RootViewController alloc] initWithRootViewController:UserLoginPhoneViewController.new];
  727. }
  728. ///Switch Account
  729. -(void)switchAccount {
  730. [XenonSDK.sharedSDK logoutWithComplete:^{
  731. //PS:封神传说cp平台,收到登出通知后会调用一次登录接口, 这里重复了.注释掉
  732. XSUser.history = nil;
  733. [XenonSDK.sharedSDK loginWithComplete:^(id user, NSError *error) {
  734. }];
  735. }];
  736. }
  737. /// 统计事件1
  738. /// @param eventName 事件名称
  739. -(void)tjEvent:(NSInteger )eventName {
  740. [XSTracker.shared trace:eventName];
  741. }
  742. /// 统计事件2
  743. /// @param eventName 事件名称
  744. /// @param parameters 事件对应值
  745. -(void)tjEvent:(NSInteger )eventName value:(id)parameters {
  746. [XSTracker.shared trace:eventName parameters:parameters];
  747. }
  748. -(void)showHudSuccess:(NSString*)str {
  749. [XSNetwork showHudSuccess:str];
  750. }
  751. @end