XenonSDK.m 32 KB

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