XenonSDK.m 33 KB

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