From a53b1630671ba004e0e8db5f556338f79c1bceef Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Fri, 29 Mar 2019 17:28:42 +0100 Subject: [PATCH] fix crash of download photo or video --- Classes/ChatConversationView.h | 3 +- Classes/ChatConversationView.m | 237 ++++++++++++++------------- Classes/Utils/FileTransferDelegate.m | 19 +-- 3 files changed, 130 insertions(+), 129 deletions(-) diff --git a/Classes/ChatConversationView.h b/Classes/ChatConversationView.h index 9f54956a1..3ace2c648 100644 --- a/Classes/ChatConversationView.h +++ b/Classes/ChatConversationView.h @@ -103,7 +103,8 @@ - (void)openFileWithURL:(NSURL *)url; - (void)clearMessageView; -- (void)autoDownload:(LinphoneChatMessage *)message view:(ChatConversationView *)view inChat:(BOOL)inChat; +- (void)showFileDownloadError; +- (void)autoDownload:(LinphoneChatMessage *)message view:(ChatConversationView *)view; - (NSURL *)getICloudFileUrl:(NSString *)name; - (BOOL)writeFileInICloud:(NSData *)data fileURL:(NSURL *)fileURL; diff --git a/Classes/ChatConversationView.m b/Classes/ChatConversationView.m index e9871972e..ac5dff0d7 100644 --- a/Classes/ChatConversationView.m +++ b/Classes/ChatConversationView.m @@ -994,7 +994,7 @@ void on_chat_room_chat_message_received(LinphoneChatRoom *cr, const LinphoneEven return; if (hasFile) { - [view autoDownload:chat view:view inChat:TRUE]; + [view autoDownload:chat view:view]; [view.tableController addEventEntry:(LinphoneEventLog *)event_log]; return; } @@ -1202,7 +1202,25 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog } } -- (void)autoDownload:(LinphoneChatMessage *)message view:(ChatConversationView *)view inChat:(BOOL)inChat{ +- (void)showFileDownloadError { + UIAlertController *errView = [UIAlertController + alertControllerWithTitle:NSLocalizedString(@"File download error", nil) + message:NSLocalizedString(@"Error while downloading the file.\n" + @"The file is probably encrypted.\n" + @"Please retry to download this file after activating LIME.", + nil) + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action){ + }]; + + [errView addAction:defaultAction]; + [PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; +} + +- (void)autoDownload:(LinphoneChatMessage *)message view:(ChatConversationView *)view { //TODO: migrate with "linphone_iphone_file_transfer_recv" LinphoneContent *content = linphone_chat_message_get_file_transfer_information(message); NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)]; @@ -1212,115 +1230,112 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog if ([fileManager fileExistsAtPath:filePath]) { NSData* data = [NSData dataWithContentsOfFile:filePath]; NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)]; - if ([fileType isEqualToString:@"image"]) { - // we're finished, save the image and update the message - UIImage *image = [UIImage imageWithData:data]; - if (!image) { - UIAlertController *errView = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"File download error", nil) - message:NSLocalizedString(@"Error while downloading the file.\n" - @"The file is probably encrypted.\n" - @"Please retry to download this file after activating LIME.", - nil) - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK" - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action){ - }]; - - [errView addAction:defaultAction]; - [PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; - return; - } - - // until image is properly saved, keep a reminder on it so that the - // chat bubble is aware of the fact that image is being saved to device - //[LinphoneManager setValueInMessageAppData:@"saving..." forKey:@"localimage" inMessage:self.message]; - - __block PHObjectPlaceholder *placeHolder; - [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ - PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromImage:image]; - placeHolder = [request placeholderForCreatedAsset]; - } completionHandler:^(BOOL success, NSError *error) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (error) { - LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]); - [LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:message]; - UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil) - message:NSLocalizedString(@"Cannot write image to photo library", - nil) - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) {}]; - - [errView addAction:defaultAction]; - [PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; - } else { - LOGI(@"Image saved to [%@]", [placeHolder localIdentifier]); - [LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier] - forKey:@"localimage" - inMessage:message]; - } - if(inChat) { - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view]; - [view.tableController scrollToLastUnread:TRUE]; - } - }); - }]; - } else if([fileType isEqualToString:@"video"]) { - // until image is properly saved, keep a reminder on it so that the - // chat bubble is aware of the fact that image is being saved to device - //[LinphoneManager setValueInMessageAppData:@"saving..." forKey:@"localvideo" inMessage:self.message]; - - __block PHObjectPlaceholder *placeHolder; - [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ - PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:filePath]]; - placeHolder = [request placeholderForCreatedAsset]; - } completionHandler:^(BOOL success, NSError * _Nullable error) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (error) { - LOGE(@"Cannot save video data downloaded [%@]", [error localizedDescription]); - [LinphoneManager setValueInMessageAppData:nil forKey:@"localvideo" inMessage:message]; - UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil) - message:NSLocalizedString(@"Cannot write video to photo library", - nil) - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) {}]; - - [errView addAction:defaultAction]; - [PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; - } else { - LOGI(@"video saved to [%@]", [placeHolder localIdentifier]); - [LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier] - forKey:@"localvideo" - inMessage:message]; - } - if(inChat) { - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view]; - [view.tableController scrollToLastUnread:TRUE]; - } - }); - }]; - - } else { - NSString *key = @"localfile"; - //write file to path - dispatch_async(dispatch_get_main_queue(), ^{ - [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message]; - [LinphoneManager setValueInMessageAppData:filePath forKey:@"cachedfile" inMessage:message]; - if(inChat) { - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view]; - [view.tableController scrollToLastUnread:TRUE]; - } - }); - } - } + + // define a block , not called immediately. To avoid crash when saving photo before PHAuthorizationStatusNotDetermined. + void (^block)(void)= ^ { + if ([fileType isEqualToString:@"image"]) { + // we're finished, save the image and update the message + UIImage *image = [UIImage imageWithData:data]; + if (!image) { + [self showFileDownloadError]; + return; + } + __block PHObjectPlaceholder *placeHolder; + [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ + PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromImage:image]; + placeHolder = [request placeholderForCreatedAsset]; + } completionHandler:^(BOOL success, NSError *error) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (error) { + LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]); + [LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:message]; + UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil) + message:NSLocalizedString(@"Cannot write image to photo library", + nil) + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) {}]; + + [errView addAction:defaultAction]; + [PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; + } else { + LOGI(@"Image saved to [%@]", [placeHolder localIdentifier]); + [LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier] + forKey:@"localimage" + inMessage:message]; + } + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view]; + [view.tableController scrollToLastUnread:TRUE]; + }); + }]; + } else if([fileType isEqualToString:@"video"]) { + // until image is properly saved, keep a reminder on it so that the + // chat bubble is aware of the fact that image is being saved to device + + __block PHObjectPlaceholder *placeHolder; + [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ + PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:filePath]]; + placeHolder = [request placeholderForCreatedAsset]; + } completionHandler:^(BOOL success, NSError * _Nullable error) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (error) { + LOGE(@"Cannot save video data downloaded [%@]", [error localizedDescription]); + [LinphoneManager setValueInMessageAppData:nil forKey:@"localvideo" inMessage:message]; + UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil) + message:NSLocalizedString(@"Cannot write video to photo library", + nil) + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) {}]; + + [errView addAction:defaultAction]; + [PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; + } else { + LOGI(@"video saved to [%@]", [placeHolder localIdentifier]); + [LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier] + forKey:@"localvideo" + inMessage:message]; + } + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view]; + [view.tableController scrollToLastUnread:TRUE]; + }); + }]; + } + }; + + // When you save an image or video to a photo library, make sure that it is allowed. Otherwise, there will be a backup error. + if ([fileType isEqualToString:@"image"] || [fileType isEqualToString:@"video"]) { + if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) { + block(); + } else { + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { + dispatch_async(dispatch_get_main_queue(), ^{ + if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusAuthorized) { + block(); + } else { + [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show]; + } + }); + }]; + } + } else { + NSString *key = @"localfile"; + //write file to path + if([view writeFileInICloud:data fileURL:[view getICloudFileUrl:name]]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message]; + //[LinphoneManager setValueInMessageAppData:filePath forKey:@"cachedfile" inMessage:message]; + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view]; + [view.tableController scrollToLastUnread:TRUE]; + });} + } + } } + + @end diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m index 6c8e43872..def684007 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -45,27 +45,13 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con LOGI(@"Transfer of %s (%d bytes): download finished", linphone_content_get_name(content), size); assert([thiz.data length] == linphone_content_get_file_size(content)); NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)]; - + ChatConversationView *view = VIEW(ChatConversationView); void (^block)(void)= ^ { if ([fileType isEqualToString:@"image"]) { // we're finished, save the image and update the message UIImage *image = [UIImage imageWithData:thiz.data]; if (!image) { - UIAlertController *errView = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"File download error", nil) - message:NSLocalizedString(@"Error while downloading the file.\n" - @"The file is probably encrypted.\n" - @"Please retry to download this file after activating LIME.", - nil) - preferredStyle:UIAlertControllerStyleAlert]; - - UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"OK" - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action){ - }]; - - [errView addAction:defaultAction]; - [PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; + [view showFileDownloadError]; [thiz stopAndDestroy]; return; } @@ -191,7 +177,6 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con [[LinphoneManager.instance fileTransferDelegates] removeObject:thiz]; NSString *key = @"localfile" ; NSString *name =[NSString stringWithUTF8String:linphone_content_get_name(content)]; - ChatConversationView *view = VIEW(ChatConversationView); [LinphoneManager setValueInMessageAppData:@"saving..." forKey:key inMessage:message]; //write file to path