XenonSDK.m 34 KB

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