// // KMDropboxManager.m // PDF Reader Pro Edition // // Created by 万军 on 2020/2/18. // Copyright © 2020 WanJun. All rights reserved. // #import "KMDropboxManager.h" #import #import "KMCloudUploadOperationQueue.h" #import "KMCloudDownloadOperationQueue.h" #import "DropboxModel.h" @interface KMDropboxManager () @property (nonatomic, copy) NSString *appKey; //获取本地当前目录下文件列表(仅当前层) @property (nonatomic, retain) NSMutableArray *localFileList; @property (nonatomic, retain) KMServicesCloudFile *localRootFolder; @property (nonatomic, retain) NSMutableDictionary *cloudFilesCache; @property (nonatomic, copy) CloudDeleteFileCallBack deleteFileBlock; @property (nonatomic, copy) CreateFolderCallBack createFolderBlock; @property (nonatomic, copy) GetFileListCallBack getFileListBlock; @property (nonatomic, copy) CloudLoginCallBack cloudLoginBlock; @property (nonatomic, copy) CloudLogoutCallBack cloudLogoutBlock; @end @implementation KMDropboxManager @synthesize appKey = _appKey; static KMDropboxManager *_instance; - (void)dealloc {} + (instancetype)allocWithZone:(struct _NSZone *)zone { @synchronized(self) { if (nil == _instance) { _instance = [super allocWithZone:zone]; } return _instance; } } + (instancetype)shareInstance { @synchronized(self) { if (nil == _instance) { _instance = [[self alloc] init]; } return _instance; } } - (id)copyWithZone:(NSZone *)zone { return _instance; } - (id)mutableCopyWithZone:(NSZone *)zone { return _instance; } - (id)init { self = [super init]; if (self) { _cloudFilesCache = [[NSMutableDictionary alloc] init]; #if VERSION_FREE self.appKey = @"ksw72buyettl2d4"; #else #endif #if VERSION_PRO self.appKey = @"4tcmyravvap2dmu"; #else self.appKey = @"4tcmyravvap2dmu"; #endif NSString *registeredUrlToHandle = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"][0][@"CFBundleURLSchemes"][0]; if (!self.appKey || [registeredUrlToHandle rangeOfString:@"<"].location != NSNotFound) { NSString *message = @"You need to set `appKey` variable in `AppDelegate.m`, as well as add to `Info.plist`, before you can use DBRoulette."; NSLog(@"%@", message); exit(1); } //初始化DBUserClient实例 // [DBClientsManager setupWithAppKeyDesktop:@"5ep155clehnzu4u"];//jxjlqfm1cfp2mjj // if (@available(macOS 11.0, *)) { //big sur上必须要 //#if VERSION_PRO // [DBClientsManager setupWithAppKeyDesktop:@"amipzwtlg9lrwyl"]; //#else // [DBClientsManager setupWithAppKeyDesktop:@"jxjlqfm1cfp2mjj"]; //#endif // // } [self getList:@""]; [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleAppleEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL]; } return self; } - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { // [[DropboxModel shared] handleAppleEvent:event withReplyEvent:replyEvent]; NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]]; [DBClientsManager handleRedirectURL:url completion:^(DBOAuthResult * _Nullable authResult) { [[DropboxModel shared] handleAuthResult:authResult]; if (authResult != nil) { if ([authResult isSuccess]) { if ([self isSignedIn]) { DBUserClient *client = [DBClientsManager authorizedClient]; [[client.usersRoutes getCurrentAccount] setResponseBlock:^(DBUSERSFullAccount * _Nullable result, DBNilObject * _Nullable routeError, DBRequestError * _Nullable networkError) { KMDropboxUserMetadata *model = [[KMDropboxUserMetadata alloc] init]; model.accountId = result.accountId; model.givenName = result.name.givenName; model.surname = result.name.surname; model.email = result.email; model.emailVerified = result.emailVerified; model.disabled = result.disabled; model.country = result.country; model.locale = result.locale; model.referralLink = result.locale; [self setValue:result.accountId forKey:NSStringFromSelector(@selector(accountId))]; [self setValue:[NSString stringWithFormat:@"%@%@",result.name.surname,result.name.givenName] forKey:NSStringFromSelector(@selector(name))]; if (_cloudLoginBlock) { _cloudLoginBlock(model ,YES); } }]; } [self getList:@""];//获取根目录文件列表 } else if ([authResult isCancel]) { NSLog(@"Authorization flow was manually canceled by user!"); } else if ([authResult isError]) { if (_cloudLoginBlock) { _cloudLoginBlock(nil, NO); } } [[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)]; } }]; } - (KMServicesCloudFile *)cloudFolderCacheForDisPath:(NSString *)path { if (!path) { return self.localRootFolder; } else { return [self.cloudFilesCache objectForKey:path]; } } - (BOOL)isSignedIn { return ([DBClientsManager authorizedClient] ? YES : NO); } - (NSString *)email { return [DropboxModel shared].userEmail; } #pragma mark - 授权登陆 - (void)authorizedLoginCompletion:(CloudLoginCallBack)completion { self.cloudLoginBlock = completion; if (![self isSignedIn]) { [self dropboxLogin]; } else { NSLog(@"已登陆"); } } - (void)dropboxLogin { //开始授权流程 NSWindow *window = [NSApplication sharedApplication].mainWindow; NSViewController *viewC = window.contentViewController; [DBClientsManager authorizeFromControllerDesktop:[NSWorkspace sharedWorkspace] controller:viewC openURL:^(NSURL * _Nonnull url) { [[NSWorkspace sharedWorkspace] openURL:url]; }]; } #pragma mark - 取消授权 - (void)authorizedLogoutCompletion:(CloudLogoutCallBack)completion { self.cloudLogoutBlock = completion; if ([DBClientsManager authorizedClient]) { [DBClientsManager unlinkAndResetClients]; if (_cloudLogoutBlock) { _cloudLogoutBlock(YES); } } } #pragma mark - 文件上传 - (KMCloudOperation *)uploadCloudPath:(KMServicesCloudFile *)cloudPath localPath:(NSURL *)localPath currentConvetProgress:(CurrentProgressCallBack)currentProgress completion:(CompletionCallBack)completion { KMCloudUploadOperationQueue *queue = [KMCloudUploadOperationQueue sharedInstance]; queue.maxConcurrentOperationCount = 4; queue.name = [localPath path]; KMCloudOperation *op = [[KMCloudOperation alloc] initWithLoadCloudPath:cloudPath serverType:KMDropbox localPath:localPath loadState:KMCloudLoadState_Upload currentConvetProgress:currentProgress completion:completion]; if (![self isSignedIn]) { [self dropboxLogin]; } else { if (![self isLoadOperationExisting:op]) { [queue addUploadOperation:op]; } } return op; } - (void)uploadCancel:(KMCloudOperation *)operation { if ([self isLoadOperationExisting:operation]) { [operation cancel]; KMCloudUploadOperationQueue *queue = [KMCloudUploadOperationQueue sharedInstance]; [queue cancel:operation.filePath]; [self removeUploadOperation:operation]; } else { return; } } #pragma mark - 文件下载 - (KMCloudOperation *)downloadCloudPath:(KMServicesCloudFile *)cloudPath localPath:(NSURL *)localPath currentConvetProgress:(CurrentProgressCallBack)currentProgress completion:(CompletionCallBack)completion { KMCloudDownloadOperationQueue *queue = [KMCloudDownloadOperationQueue sharedInstance]; queue.maxConcurrentOperationCount = 4; queue.name = cloudPath.displayName; KMCloudOperation *op = [[KMCloudOperation alloc] initWithLoadCloudPath:cloudPath serverType:KMDropbox localPath:localPath loadState:KMCloudLoadState_Download currentConvetProgress:currentProgress completion:completion]; if (![self isSignedIn]) { [self dropboxLogin]; } else { if (![self isLoadOperationExisting:op]) { [queue addDownloadOperation:op]; } } return op; } - (void)downloadCancel:(KMCloudOperation *)operation { if ([self isLoadOperationExisting:operation]) { [operation cancel]; KMCloudDownloadOperationQueue *queue = [KMCloudDownloadOperationQueue sharedInstance]; [queue cancel:operation.filePath]; [self removeDownloadOperation:operation]; } else { return; } } #pragma mark - 文件删除 - (void)deleteFileWithPath:(KMServicesCloudFile *)fileData completion:(CloudDeleteFileCallBack)completion { self.deleteFileBlock = completion; if (![self isSignedIn]) { [self dropboxLogin]; } else { DBUserClient *client = [DBClientsManager authorizedClient]; // [[client.filesRoutes delete_:fileData.path_display] setResponseBlock:^(DBFILESDeleteResult * _Nullable result, DBFILESDeleteError * _Nullable routeError, DBRequestError * _Nullable networkError) { // if (result) { // NSString *folderPath = [self getFolderPathWithFileData:fileData]; // KMDropboxFileMetadata *fileModel = [[KMDropboxFileMetadata alloc] init]; // fileModel.name = result.metadata.name; // fileModel.path_lower = result.metadata.pathLower; // fileModel.path_display = result.metadata.pathDisplay; // if (_deleteFileBlock) { // _deleteFileBlock(fileData, YES); // } // [self getList:folderPath]; // } else { // KMDropboxErrorMetadata *errorModel = [[KMDropboxErrorMetadata alloc] init]; // errorModel.routeError = [NSNumber numberWithInteger:routeError.tag]; // errorModel.networkError = [NSNumber numberWithInteger:networkError.statusCode.integerValue]; // if (_deleteFileBlock) { // _deleteFileBlock(fileData, NO); // } // } // }]; // // [[client.filesRoutes delete_V2:fileData.path_display] setResponseBlock:^(DBFILESDeleteResult * _Nullable result, DBFILESDeleteError * _Nullable routeError, DBRequestError * _Nullable networkError) { // if (result) { // NSString *folderPath = [self getFolderPathWithFileData:fileData]; // KMDropboxFileMetadata *fileModel = [[KMDropboxFileMetadata alloc] init]; // fileModel.name = result.metadata.name; // fileModel.path_lower = result.metadata.pathLower; // fileModel.path_display = result.metadata.pathDisplay; // if (_deleteFileBlock) { // _deleteFileBlock(fileData, YES); // } // [self getList:folderPath]; // } else { // KMDropboxErrorMetadata *errorModel = [[KMDropboxErrorMetadata alloc] init]; // errorModel.routeError = [NSNumber numberWithInteger:routeError.tag]; // errorModel.networkError = [NSNumber numberWithInteger:networkError.statusCode.integerValue]; // if (_deleteFileBlock) { // _deleteFileBlock(fileData, NO); // } // } // }]; } } #pragma mark - 文件夹创建 - (void)createFolderWithPath:(KMServicesCloudFile *)fileData folderName:(NSString *)folderName completion:(CreateFolderCallBack)completion { self.createFolderBlock = completion; NSString *pathUrl = [self getFolderPathWithFileData:fileData]; if (![self isSignedIn]) { [self dropboxLogin]; } else { DBUserClient *client = [DBClientsManager authorizedClient]; // [[client.filesRoutes createFolderV2:[NSString stringWithFormat:@"%@/%@",pathUrl,folderName]] setResponseBlock:^(DBFILESCreateFolderResult * _Nullable result, DBFILESCreateFolderError * _Nullable routeError, DBRequestError * _Nullable networkError) { // if (result) { // // KMDropboxFileMetadata *fileModel = [[KMDropboxFileMetadata alloc] init]; // fileModel.id_ = result.metadata.id_; // fileModel.name = result.metadata.name; // fileModel.path_lower = result.metadata.pathLower; // fileModel.path_display = result.metadata.pathDisplay; // if (_createFolderBlock) { // _createFolderBlock(fileModel, YES); // } // [self getList:pathUrl]; // } else { // KMDropboxErrorMetadata *errorModel = [[KMDropboxErrorMetadata alloc] init]; // errorModel.routeError = [NSNumber numberWithInteger:routeError.tag]; // errorModel.networkError = [NSNumber numberWithInteger:networkError.statusCode.integerValue]; // if (_createFolderBlock) { // _createFolderBlock(fileData, YES); // } // } // }]; } } #pragma mark - 文件列表信息 - (void) getListWithFilePath:(KMServicesCloudFile *)fileData setResponseBlock:(GetFileListCallBack)responseBlock { self.getFileListBlock = responseBlock; NSString *pathUrl = [self getFolderPathWithFileData:fileData]; if (fileData.displayName) { [self.cloudFilesCache setValue:fileData forKey:fileData.displayName]; } if (![self isSignedIn]) { [self dropboxLogin]; } else if ([pathUrl isEqualToString:@""] || pathUrl == nil) { [self getList:pathUrl]; } else { _localFileList = [[NSMutableArray alloc] init]; DBUserClient *client = [DBClientsManager authorizedClient]; [[client.filesRoutes listFolder:pathUrl] setResponseBlock:^(DBFILESListFolderResult *response, DBFILESListFolderError *routeError, DBRequestError *networkError) { if (response) { NSArray *entries = response.entries; [self printEntries:entries]; if (![response.hasMore boolValue]) { if (_getFileListBlock) { _getFileListBlock(_localFileList,KMDropbox,nil); [_localFileList removeAllObjects]; } } } else { KMDropboxErrorMetadata *errorModel = [[KMDropboxErrorMetadata alloc] init]; errorModel.routeError = [NSNumber numberWithInteger:routeError.tag]; errorModel.networkError = [NSNumber numberWithInteger:networkError.statusCode.integerValue]; if (_getFileListBlock) { _getFileListBlock(nil,KMDropbox,nil); } } }]; } } - (void)getList:(NSString *)filePath { _localFileList = [[NSMutableArray alloc] init]; DBUserClient *client = [DBClientsManager authorizedClient]; [[client.filesRoutes listFolder:filePath.length < 1 ? @"" :filePath] setResponseBlock:^(DBFILESListFolderResult *response, DBFILESListFolderError *routeError, DBRequestError *networkError) { if (response) { NSArray *entries = response.entries; BOOL hasMore = [response.hasMore boolValue]; [self printEntries:entries]; if (hasMore) { //is folder // [self listFolderContinueWithClient:client cursor:cursor]; } else { NSLog(@"List folder complete."); } if (_getFileListBlock) { _getFileListBlock(_localFileList,KMDropbox,nil); [_localFileList removeAllObjects]; } } else { KMDropboxErrorMetadata *errorModel = [[KMDropboxErrorMetadata alloc] init]; errorModel.routeError = [NSNumber numberWithInteger:routeError.tag]; errorModel.networkError = [NSNumber numberWithInteger:networkError.statusCode.integerValue]; if (_getFileListBlock) { _getFileListBlock(nil,KMDropbox,nil); } } }]; } - (void)printEntries:(NSArray *)entries { for (DBFILESMetadata *entry in entries) { KMServicesCloudFile *fileModel = [[KMServicesCloudFile alloc] init]; if ([entry isKindOfClass:[DBFILESFileMetadata class]]) { DBFILESFileMetadata *fileMetadata = (DBFILESFileMetadata *)entry; fileModel.client_modified = fileMetadata.clientModified; fileModel.content_hash = fileMetadata.contentHash; fileModel.fileId = fileMetadata.id_; fileModel.displayName = fileMetadata.pathDisplay; fileModel.path_lower = fileMetadata.pathLower; fileModel.rev = fileMetadata.rev; fileModel.fileModiDate = fileMetadata.serverModified; fileModel.fileSize = fileMetadata.size.integerValue; fileModel.filetype = KMCloudServiceFileType_File; fileModel.fileName = fileMetadata.name; [_localFileList addObject:fileModel]; } else if ([entry isKindOfClass:[DBFILESFolderMetadata class]]) { DBFILESFolderMetadata *folderMetadata = (DBFILESFolderMetadata *)entry; fileModel.fileId = folderMetadata.id_; fileModel.fileName = folderMetadata.name; fileModel.displayName = folderMetadata.pathDisplay; fileModel.path_lower = folderMetadata.pathLower; fileModel.filetype = KMCloudServiceFileType_Folder; [_localFileList addObject:fileModel]; } } } #pragma mark - private methods - (void)removeUploadOperation:(KMCloudOperation *)operation { KMCloudUploadOperationQueue *queue = [KMCloudUploadOperationQueue sharedInstance]; if ([queue isUploadOperationExisting:operation.filePath]) { NSLog(@"removeUploadOperation operation.filePath == %@",operation.filePath); [queue cancel:operation.filePath]; } } - (void)removeDownloadOperation:(KMCloudOperation *)operation { KMCloudDownloadOperationQueue *queue = [KMCloudDownloadOperationQueue sharedInstance]; if ([queue isDownloadOperationExisting:operation.filePath]) { NSLog(@"removeOperation operation.filePath == %@",operation.filePath); [queue cancel:operation.filePath]; } } - (BOOL)isLoadOperationExisting:(KMCloudOperation *)operation { KMCloudUploadOperationQueue *uploadQueue = [KMCloudUploadOperationQueue sharedInstance]; KMCloudDownloadOperationQueue *downloadQueue = [KMCloudDownloadOperationQueue sharedInstance]; NSMutableArray *operationArr = [NSMutableArray array]; for (NSArray *opArr in @[uploadQueue.operations, downloadQueue.operations]) { for (int j = 0; j < opArr.count; j++) { [operationArr addObject:opArr[j]]; } } for (KMCloudOperation *op in operationArr) { if ([op.filePath isEqualToString:operation.filePath]) { return YES; } } return NO; } //获取文件对象所处的文件夹路径 - (NSString *)getFolderPathWithFileData:(KMServicesCloudFile *)fileData { if (KMCloudServiceFileType_Folder == fileData.filetype) { return fileData.displayName; } else { NSString *folderPath = [fileData.displayName stringByDeletingLastPathComponent]; if ([folderPath isEqualToString:@"/"]) { return @""; } else { return folderPath; } } return nil; } - (KMServicesCloudFile *)localRootFolder { KMServicesCloudFile *fileData = [[KMServicesCloudFile alloc] init]; fileData.displayName = @""; fileData.path_lower = @""; fileData.filetype = KMCloudServiceFileType_Folder; return fileData; } @end