// // AIInfoManager.m // PDF Reader Pro Edition // // Created by Niehaoyu on 2024/1/24. // #import "AIInfoManager.h" #import "NSNULL+Filtration.h" #import "NSObject+DeviceInfo.h" #import "IAPProductsManager.h" #import #import "ASIFormDataRequest.h" #import "JSONKit.h" static AIInfoManager *__Manager = nil; @interface AIInfoManager () @property (nonatomic, readwrite) BOOL aiInfoValid; @end @implementation AIInfoManager + (AIInfoManager *)defaultManager { if (!__Manager) { __Manager = [[AIInfoManager alloc] init]; } return __Manager; } - (instancetype)init { self = [super init]; self.aiInfoValid = NO; if (![[NSUserDefaults standardUserDefaults] objectForKey:@"kAIIconShowIdentifyKeyTip"]) { self.showAIIcon = YES; [[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"kAIIconShowIdentifyKeyTip"]; [[NSUserDefaults standardUserDefaults] synchronize]; } _showAIIcon = [[NSUserDefaults standardUserDefaults] boolForKey:kAIIconShowIdentifyKey]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self fetchAIInfoWithComplention:^(NSDictionary * _Nonnull info, NSError * _Nonnull error) { }]; }); return self; } #pragma mark - Setter - (NSString *)bundleIdentify { return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]?:@""; } - (NSString *)udid { // return @"8D397BE0-F478-5269-A8F1-BE1E3B757221"; return GetHardwareUUID()?:@""; } - (void)setShowAIIcon:(BOOL)showAIIcon { _showAIIcon = showAIIcon; [[NSUserDefaults standardUserDefaults] setBool:_showAIIcon forKey:kAIIconShowIdentifyKey]; [[NSUserDefaults standardUserDefaults] synchronize]; } #pragma mark - Method - (void)activateAIWithInfo:(NSDictionary *)info complention:(AIActivityComplention)complention { if (![info[@"cdkey"] length] && ![info[@"receipt_str"] length]) { //激活码为空 NSError *error = [NSError errorWithDomain:@"" code:ActivityErrorTypeCDKeyEmpty userInfo:nil]; if (complention) complention(nil, error); return; } #if VERSION_DMG NSString *cdKey = info[@"cdkey"]?:@""; if (![cdKey hasPrefix:@"AI-"]) { //激活码为空 NSError *error = [NSError errorWithDomain:@"" code:ActivityErrorTypeFormatError userInfo:nil]; if (complention) complention(nil, error); return; } #endif __block AIActivityComplention tComplention = complention; __block VerificationManager *weak_self = self; NSString *platform = @"DMG"; #if VERSION_DMG platform = @"DMG"; #else platform = @"AiStore"; #endif //Process Params NSString *app_version = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] description]; if (!app_version.length) app_version = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] description]; NSDictionary *params = @{ //激活信息 @"subscription":@{ @"app_code":@"com.brother.pdfreaderpro.ai", #if VERSION_DMG @"cdkey":info[@"cdkey"]?:@"", #else @"receipt_str":info[@"receipt_str"]?:@"" #endif }, //设备信息 @"device":@{ @"app_name":[self bundleIdentify]?:@"", @"unique_sn":[self udid]?:@"", @"model":GetProductName()?:@"", @"os":GetSystemVersion()?:@"", @"time_zone":[[NSTimeZone localTimeZone] localizedName:NSTimeZoneNameStyleShortDaylightSaving locale:[NSLocale currentLocale]]?:@"", @"language":[[NSLocale currentLocale] localeIdentifier]?:@"", @"app_version":app_version?:@"", @"platform":platform?:@"DMG" } }; NSMutableData *postData = [VerificationManager mutableDataWithDic:params]; //Send Request NSString *urlString = [kVerificationServer stringByAppendingString:@"api/auth_devices/activate"]; NSURL *url = [NSURL URLWithString:urlString]; ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; request.requestMethod = @"POST"; [request setPostBody:postData]; NSString *postLength = [NSString stringWithFormat:@"%ld", (unsigned long)[postData length]]; NSMutableDictionary *requestHeaders = [NSMutableDictionary dictionaryWithObjectsAndKeys: postLength, @"Content-Length", nil]; request.defaultResponseEncoding = NSUTF8StringEncoding; [request setRequestHeaders:requestHeaders]; [request addRequestHeader:@"Content-Type" value:@"application/json"]; [request addRequestHeader:@"Accept" value:@"application/vnd.api+json;version=1"]; __block void(^processRequest)(void) = ^{ dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary *info = [[request.responseString objectFromJSONString] filterNullObject]; NSError *error = request.error; if (info && [info isKindOfClass:[NSDictionary class]]) { if ([info valueForKey:@"errors"]) { error = [NSError errorWithDomain:@"激活失败" code:ActivityErrorTypeUnknow userInfo:info]; }else if ([info valueForKey:@"error"]) { error = [NSError errorWithDomain:@"激活失败" code:ActivityErrorTypeUnknow userInfo:info]; } }else { if(error) { error = [NSError errorWithDomain:error.domain code:ActivityErrorTypeNetworkDisable userInfo:error.userInfo]; } else { error = [NSError errorWithDomain:@"激活失败" code:ActivityErrorTypeUnknow userInfo:@{}]; } } if (!error) { NSMutableDictionary *dic = [[NSMutableDictionary alloc] init]; AIInfo *aiInfo = [[AIInfo alloc] initWithDict:info[@"data"]]; self.aiInfo = aiInfo; self.aiInfoValid = YES; NSString *notificationName = (NSString *)kDeviceAIStatusChangeNotification; [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self userInfo:info]; } if (tComplention) tComplention(info, error); }); }; [request setFailedBlock:^{ processRequest(); }]; [request setCompletionBlock:^{ processRequest(); }]; [request startAsynchronous]; } - (void)fetchAIInfoWithComplention:(AIActivityComplention)complention { __block AIActivityComplention tComplention = complention; __block AIInfoManager *weak_self = self; NSString *platform = @"DMG"; #if VERSION_DMG platform = @"DMG"; #else platform = @"AiStore"; #endif //Process Params NSString *app_version = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] description]; if (!app_version.length) app_version = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] description]; NSDictionary *params = @{@"unique_sn":[self udid]?:@"", @"app_name":[self bundleIdentify]?:@"", @"platform":platform?:@"DMG", #if VERSION_DMG #else @"receipt_str":[IAPProductsManager defaultManager].temptransactioReceipt?:@"" #endif }; NSMutableData *postData = [VerificationManager mutableDataWithDic:params]; //Send Request NSString *urlString = [kVerificationServer stringByAppendingString:@"api/getAiInfo"]; NSURL *url = [NSURL URLWithString:urlString]; ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; request.requestMethod = @"POST"; [request setPostBody:postData]; NSString *postLength = [NSString stringWithFormat:@"%ld", (unsigned long)[postData length]]; NSMutableDictionary *requestHeaders = [NSMutableDictionary dictionaryWithObjectsAndKeys: postLength, @"Content-Length", nil]; request.defaultResponseEncoding = NSUTF8StringEncoding; [request setRequestHeaders:requestHeaders]; [request addRequestHeader:@"Content-Type" value:@"application/json"]; [request addRequestHeader:@"Accept" value:@"application/vnd.api+json;version=1"]; __block void(^processRequest)(void) = ^{ dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary *info = [[request.responseString objectFromJSONString] filterNullObject]; BOOL isReceiptNeverUsed = NO;//当前票据未激活设备(针对AppStore版购买过但未激活的情况) NSError *error = request.error; if (info && [info isKindOfClass:[NSDictionary class]]) { if ([info valueForKey:@"errors"]) { error = [NSError errorWithDomain:@"激活失败" code:ActivityErrorTypeUnknow userInfo:info]; NSDictionary *errorsDict = ((NSArray *)[info valueForKey:@"errors"]).firstObject; if ([errorsDict valueForKey:@"attribute"]) { if ([[errorsDict valueForKey:@"attribute"] isEqualToString:@"inactivated"]) { isReceiptNeverUsed = YES; } else if ([[errorsDict valueForKey:@"attribute"] isEqualToString:@"device_expired"]) { self.aiInfoValid = NO; } } }else if ([info valueForKey:@"error"]) { error = [NSError errorWithDomain:@"激活失败" code:ActivityErrorTypeUnknow userInfo:info]; } }else { if(error) { error = [NSError errorWithDomain:error.domain code:ActivityErrorTypeNetworkDisable userInfo:error.userInfo]; } else { error = [NSError errorWithDomain:@"激活失败" code:ActivityErrorTypeUnknow userInfo:@{}]; } } if (!error) { AIInfo *aiInfo = [[AIInfo alloc] initWithDict:info[@"data"]]; self.aiInfo = aiInfo; self.aiInfoValid = YES; } NSString *notificationName = (NSString *)kDeviceAIStatusChangeNotification; [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self userInfo:info]; #if VERSION_DMG #else if (isReceiptNeverUsed && [IAPProductsManager defaultManager].temptransactioReceipt) { //当前票据未激活设备(针对AppStore版购买过但未激活的情况) BOOL didPurchase = NO; #if VERSION_FREE if ([IAPProductsManager defaultManager].aiAllAccessPack1month_lite.isSubscribed) { didPurchase = YES; } #else if ([IAPProductsManager defaultManager].aiAllAccessPack1month_pro.isSubscribed) { didPurchase = YES; } #endif if (didPurchase) { NSDictionary *infoDic = @{@"receipt_str":[IAPProductsManager defaultManager].temptransactioReceipt}; [self activateAIWithInfo:infoDic complention:^(NSDictionary * _Nonnull info, NSError * _Nonnull error) { }]; } } #endif if (tComplention) tComplention(info, error); }); }; [request setFailedBlock:^{ processRequest(); }]; [request setCompletionBlock:^{ processRequest(); }]; [request startAsynchronous]; } - (void)restoreAIWithInfo:(NSDictionary *)info complention:(AIActivityComplention)complention { __block AIActivityComplention tComplention = complention; __block AIInfoManager *weak_self = self; NSString *platform = @"DMG"; #if VERSION_DMG platform = @"DMG"; #else platform = @"AiStore"; #endif //Process Params NSString *app_version = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] description]; if (!app_version.length) app_version = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] description]; NSDictionary *params = @{@"unique_sn":[self udid]?:@"", @"app_name":[self bundleIdentify]?:@"", @"receipt_str":info[@"receipt_str"]?:@"", @"platform":platform?:@"DMG"}; NSMutableData *postData = [VerificationManager mutableDataWithDic:params]; //Send Request NSString *urlString = [kVerificationServer stringByAppendingString:@"api/changeStoreBind"]; NSURL *url = [NSURL URLWithString:urlString]; ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; request.requestMethod = @"POST"; [request setPostBody:postData]; NSString *postLength = [NSString stringWithFormat:@"%ld", (unsigned long)[postData length]]; NSMutableDictionary *requestHeaders = [NSMutableDictionary dictionaryWithObjectsAndKeys: postLength, @"Content-Length", nil]; request.defaultResponseEncoding = NSUTF8StringEncoding; [request setRequestHeaders:requestHeaders]; [request addRequestHeader:@"Content-Type" value:@"application/json"]; [request addRequestHeader:@"Accept" value:@"application/vnd.api+json;version=1"]; __block void(^processRequest)(void) = ^{ dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary *info = [[request.responseString objectFromJSONString] filterNullObject]; NSError *error = request.error; if (info && [info isKindOfClass:[NSDictionary class]]) { if ([info valueForKey:@"errors"]) { error = [NSError errorWithDomain:@"激活失败" code:ActivityErrorTypeUnknow userInfo:info]; }else if ([info valueForKey:@"error"]) { error = [NSError errorWithDomain:@"激活失败" code:ActivityErrorTypeUnknow userInfo:info]; } }else { if(error) { error = [NSError errorWithDomain:error.domain code:ActivityErrorTypeNetworkDisable userInfo:error.userInfo]; } else { error = [NSError errorWithDomain:@"激活失败" code:ActivityErrorTypeUnknow userInfo:@{}]; } } if (!error) { AIInfo *aiInfo = [[AIInfo alloc] initWithDict:info[@"data"]]; self.aiInfo = aiInfo; NSString *notificationName = (NSString *)kDeviceAIStatusChangeNotification; [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self userInfo:info]; } if (tComplention) tComplention(info, error); }); }; [request setFailedBlock:^{ processRequest(); }]; [request setCompletionBlock:^{ processRequest(); }]; [request startAsynchronous]; } @end @interface AIInfo () @property (nonatomic, readwrite) NSDictionary *infoDict; @property (nonatomic, readwrite) BOOL validAIFunction; @property (nonatomic, readwrite) NSString *cdKey; @property (nonatomic, readwrite) int totalToken; @property (nonatomic, readwrite) int usedTimes; @property (nonatomic, readwrite) NSDate *startDate; @property (nonatomic, readwrite) NSDate *endDate; @property (nonatomic, readwrite) int remainingDays; @property (nonatomic, readwrite) int pre_creditToken; @end @implementation AIInfo - (id)initWithDict:(NSDictionary *)dict { self = [super init]; if (self) { self.infoDict = dict; [self setUp]; } return self; } - (id)init { self = [super init]; if (self) { [self setUp]; } return self; } - (void)setUp { self.cdKey = self.infoDict[@"cdkey"]?:@""; self.totalToken = [self.infoDict[@"total"] intValue]; self.usedTimes = [self.infoDict[@"used_times"] intValue]; self.remainingDays = [self.infoDict[@"remaining_days"] intValue]; if (self.infoDict[@"start_date"]) { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setLocale:[NSLocale systemLocale]]; [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; self.startDate = [formatter dateFromString:self.infoDict[@"start_date"]] ?: [[NSDate alloc] initWithTimeIntervalSince1970:0]; } else { self.startDate = [[NSDate alloc] initWithTimeIntervalSince1970:0]; } if (self.infoDict[@"end_date"]) { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setLocale:[NSLocale systemLocale]]; [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; self.endDate = [formatter dateFromString:self.infoDict[@"end_date"]] ?: [[NSDate alloc] initWithTimeIntervalSince1970:0]; } else { self.startDate = [[NSDate alloc] initWithTimeIntervalSince1970:0]; } self.pre_creditToken = [self.infoDict[@"pre_credit"] intValue]; } - (NSDate *)startDate { return _startDate ?: [[NSDate alloc] initWithTimeIntervalSince1970:0]; } - (NSDate *)endDate { return _endDate ?: [[NSDate alloc] initWithTimeIntervalSince1970:0]; } - (BOOL)validAIFunction { CGFloat timeInterval = self.endDate.timeIntervalSinceNow; if (timeInterval > 0) { return YES; } else { return NO; } } @end #if DEBUG @implementation AIInfoManager (KMTest) //- (AIInfo *)aiInfo { // return [[AIInfo alloc] initWithDict:@{@"1" : @"1"}]; //} @end #endif