Add Upload media (Photos, Videos, Files) feature

This commit is contained in:
Benoit Martins 2023-01-16 11:58:29 +01:00 committed by QuentinArguillere
parent e813651818
commit caf9a510e0
6 changed files with 242 additions and 60 deletions

View file

@ -914,9 +914,9 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut
return;
if (hasFile) {
if (PhoneMainView.instance.currentView == ChatConversationView.compositeViewDescription && room == PhoneMainView.instance.currentRoom)
if (PhoneMainView.instance.currentView == ChatConversationViewSwift.compositeViewDescription && room == PhoneMainView.instance.currentRoom)
return;
[ChatConversationView autoDownload:msg];
[self autoDownload:msg];
}
// Post event
@ -930,6 +930,21 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:self userInfo:dict];
}
- (void)autoDownload:(LinphoneChatMessage *)message {
LinphoneContent *content = linphone_chat_message_get_file_transfer_information(message);
NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)];
NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)];
NSString *key = [ChatConversationViewSwift getKeyFromFileType:fileType fileName:name];
[LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message];
dispatch_async(dispatch_get_main_queue(), ^{
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:VIEW(ChatConversationViewSwift)];
if (![VFSUtil vfsEnabledWithGroupName:kLinphoneMsgNotificationAppGroupId] && [ConfigManager.instance lpConfigBoolForKeyWithKey:@"auto_write_to_gallery_preference"]) {
[ChatConversationViewSwift writeMediaToGalleryFromName:name fileType:fileType];
}
});
}
static void linphone_iphone_message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *message) {
[(__bridge LinphoneManager *)linphone_core_cbs_get_user_data(linphone_core_get_current_callbacks(lc)) onMessageReceived:lc room:room message:message];
}

View file

@ -57,7 +57,7 @@ import AVFoundation
var mediaSelectorVisible = false
var mediaCollectionView : [UIImage] = []
var mediaNameCollection : [String] = []
var mediaURLCollection : [URL] = []
var collectionView: UICollectionView = {
let top_bar_height = 66.0
@ -121,6 +121,9 @@ import AVFoundation
return menu
}()
var fileContext : [Data] = []
override func viewDidLoad() {
super.viewDidLoad(
backAction: {
@ -187,9 +190,10 @@ import AVFoundation
self.isComposingView.transform = CGAffineTransformIdentity;
self.mediaSelector.transform = CGAffineTransformIdentity;
self.contentView.transform = CGAffineTransformIdentity;
//self.collectionView = nil
self.mediaCollectionView = []
self.mediaNameCollection = []
self.mediaURLCollection = []
self.fileContext = []
self.messageView.fileContext = false
}
@ -584,6 +588,7 @@ import AVFoundation
func sendMessageInMessageField(rootMessage: ChatMessage?) {
if sendMessage(message: messageView.messageText.text, withExterlBodyUrl: nil, rootMessage: rootMessage) {
messageView.messageText.text = ""
messageView.isComposing = false
}
}
@ -601,33 +606,77 @@ import AVFoundation
stopVoiceRecordPlayer()
linphone_chat_message_add_content(rootMessage, voiceContent)
}
*//*
if fileContext.count() > 0 {
if linphone_chat_room_get_capabilities(chatRoom?.getCobject) & LinphoneChatRoomCapabilitiesConference != 0 {
startMultiFilesUpload(rootMessage)
} else {
var i = 0
for i in 0..<(fileContext.count() - 1) {
startUploadData(fileContext.datasArray[i], withType: fileContext.typesArray[i], withName: fileContext.namesArray[i], andMessage: nil, rootMessage: nil)
}
if isOneToOne {
startUploadData(fileContext.datasArray[i], withType: fileContext.typesArray[i], withName: fileContext.namesArray[i], andMessage: nil, rootMessage: nil)
if messageView.messageText.text != "" {
sendMessage(message: messageView.messageText.text, withExterlBodyUrl: nil, rootMessage: rootMessage)
}
*/
if fileContext.count > 0 {
let conference = chatRoom!.hasCapability(mask: Int(LinphoneChatRoomCapabilitiesConference.rawValue))
if (linphone_chat_room_get_capabilities(chatRoom?.getCobject) != 0) && conference {
let result = ChatMessage.getSwiftObject(cObject: rootMessage!)
startMultiFilesUpload(result)
} else {
startUploadData(fileContext.datasArray[i], withType: fileContext.typesArray[i], withName: fileContext.namesArray[i], andMessage: messageField.text(), rootMessage: rootMessage)
for i in 0..<(fileContext.count) {
startUploadData(fileContext[i], withType: FileType.init(mediaURLCollection[i].pathExtension)?.getGroupTypeFromFile(), withName: mediaURLCollection[i].lastPathComponent, andMessage: nil, rootMessage: nil)
}
if messageView.messageText.text != "" {
let result = ChatMessage.getSwiftObject(cObject: rootMessage!)
sendMessageInMessageField(rootMessage: result)
}
}
}
clearMessageView()
return
}
*/
fileContext = []
messageView.fileContext = false
mediaSelectorVisible = false
mediaSelectorHeight = 0.0
self.isComposingView.transform = CGAffineTransformIdentity;
self.mediaSelector.transform = CGAffineTransformIdentity;
self.contentView.transform = CGAffineTransformIdentity;
self.mediaCollectionView = []
self.mediaURLCollection = []
return
}
let result = ChatMessage.getSwiftObject(cObject: rootMessage!)
sendMessageInMessageField(rootMessage: result)
}
func startMultiFilesUpload(_ rootMessage: ChatMessage?) -> Bool {
let fileTransfer = FileTransferDelegate()
fileTransfer.text = messageView.messageText.text
fileTransfer.uploadFileContent(forSwift: fileContext, urlList: mediaURLCollection, for: chatRoom?.getCobject, rootMessage: rootMessage?.getCobject)
messageView.messageText.text = ""
tableController.scroll(toBottom: true)
return true
}
@objc class func writeFileInImagesDirectory(_ data: Data?, name: String?) {
let filePath = URL(fileURLWithPath: LinphoneManager.imagesDirectory()).appendingPathComponent(name ?? "").path
if name != nil || (name == "") {
print("try to write file in \(filePath)")
}
FileManager.default.createFile(
atPath: filePath,
contents: data,
attributes: nil)
}
func startUploadData(_ data: Data?, withType type: String?, withName name: String?, andMessage message: String?, rootMessage: ChatMessage?) -> Bool {
let fileTransfer = FileTransferDelegate.init()
if let message {
fileTransfer.text = message
}
var resultType = "file"
var key = "localfile"
if type == "file_video_default" {
resultType = "video"
key = "localvideo"
} else if type == "file_picture_default" {
resultType = "image"
key = "localimage"
}
fileTransfer.uploadData(data, for: chatRoom?.getCobject, type: resultType, subtype: resultType, name: name, key: key, rootMessage: rootMessage?.getCobject)
tableController.scroll(toBottom: true)
return true
}
func on_chat_room_chat_message_received(_ cr: ChatRoom?, _ event_log: EventLog?) {
let chat = event_log?.chatMessage
if chat == nil {
@ -780,21 +829,16 @@ import AVFoundation
})
}
class func autoDownload(_ message: ChatMessage?) {
let content = linphone_chat_message_get_file_transfer_information(message?.getCobject)
let name = String(utf8String: linphone_content_get_name(content))
let fileType = String(utf8String: linphone_content_get_type(content))
let key = ChatConversationView.getKeyFromFileType(fileType, fileName: name)
LinphoneManager.setValueInMessageAppData(name, forKey: key, in: message?.getCobject)
DispatchQueue.main.async(execute: {
if !VFSUtil.vfsEnabled(groupName: kLinphoneMsgNotificationAppGroupId) && ConfigManager.instance().lpConfigBoolForKey(key: "auto_write_to_gallery_preference") {
ChatConversationViewSwift.writeMediaToGallery(name: name, fileType: fileType)
}
})
@objc class func getKeyFromFileType(_ fileType: String?, fileName name: String?) -> String? {
if fileType == "video" {
return "localvideo"
} else if (fileType == "image") || name?.hasSuffix("JPG") ?? false || name?.hasSuffix("PNG") ?? false || name?.hasSuffix("jpg") ?? false || name?.hasSuffix("png") ?? false {
return "localimage"
}
return "localfile"
}
class func writeMediaToGallery(name: String?, fileType: String?) {
@objc class func writeMediaToGalleryFromName(_ name: String?, fileType: String?) {
let filePath = LinphoneManager.validFilePath(name)
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!) {
@ -990,6 +1034,7 @@ import AVFoundation
if(self.mediaCollectionView.count > 0 && !mediaSelectorVisible){
self.selectionMedia()
self.messageView.sendButton.isEnabled = true
self.messageView.fileContext = true
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
@ -999,10 +1044,12 @@ import AVFoundation
let deleteButton = CallControlButton(width: 22, height: 22, buttonTheme:VoipTheme.nav_black_button("reply_cancel"), onClickAction: {
self.collectionView.deleteItems(at: [indexPath])
self.mediaCollectionView.remove(at: indexPath.row)
self.mediaNameCollection.remove(at: indexPath.row)
self.mediaURLCollection.remove(at: indexPath.row)
self.fileContext.remove(at: indexPath.row)
if(self.mediaCollectionView.count == 0){
self.messageView.fileContext = false
self.selectionMedia()
if self.messageView.messageText.text.isEmpty {
if self.messageView.messageText.text.isEmpty{
self.messageView.sendButton.isEnabled = false
} else {
self.messageView.sendButton.isEnabled = true
@ -1013,10 +1060,10 @@ import AVFoundation
let imageCell = mediaCollectionView[indexPath.row]
var myImageView = UIImageView()
if(mediaNameCollection[indexPath.row] == FileType.file_picture_default.rawValue || mediaNameCollection[indexPath.row] == FileType.file_video_default.rawValue){
if(FileType.init(mediaURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_picture_default.rawValue || FileType.init(mediaURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_video_default.rawValue){
myImageView = UIImageView(image: imageCell)
}else{
let fileNameText = mediaNameCollection[indexPath.row]
let fileNameText = mediaURLCollection[indexPath.row].lastPathComponent
let fileName = SwiftUtil.textToImage(drawText:fileNameText, inImage:imageCell, forReplyBubble:false)
myImageView = UIImageView(image: fileName)
}
@ -1025,7 +1072,7 @@ import AVFoundation
viewCell.addSubview(myImageView)
myImageView.alignParentBottom(withMargin: 4).alignParentLeft(withMargin: 4).done()
if(mediaNameCollection[indexPath.row] == FileType.file_video_default.rawValue){
if(FileType.init(mediaURLCollection[indexPath.row].pathExtension)?.getGroupTypeFromFile() == FileType.file_video_default.rawValue){
var imagePlay = UIImage()
if #available(iOS 13.0, *) {
imagePlay = (UIImage(named: "vr_play")!.withTintColor(.white))
@ -1053,14 +1100,24 @@ import AVFoundation
for item in itemProviders {
if item.canLoadObject(ofClass: UIImage.self) {
item.loadObject(ofClass: UIImage.self) { (image, error) in
DispatchQueue.main.async {
if let image = image as? UIImage {
self.collectionView.performBatchUpdates({
let indexPath = IndexPath(row: self.mediaCollectionView.count, section: 0)
self.mediaCollectionView.append(image)
self.collectionView.insertItems(at: [indexPath])
self.mediaNameCollection.append(FileType.file_picture_default.rawValue)
}, completion: nil)
if item.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
item.loadFileRepresentation(forTypeIdentifier: UTType.image.identifier) { urlFile, err in
DispatchQueue.main.async {
if let image = image as? UIImage {
self.collectionView.performBatchUpdates({
self.mediaURLCollection.append(urlFile!)
let indexPath = IndexPath(row: self.mediaCollectionView.count, section: 0)
self.mediaCollectionView.append(image)
self.collectionView.insertItems(at: [indexPath])
}, completion: nil)
}
}
do {
let data = try Data(contentsOf: urlFile!)
self.fileContext.append(data)
} catch let error{
print(error.localizedDescription)
}
}
}
}
@ -1074,7 +1131,13 @@ import AVFoundation
let indexPath = IndexPath(row: self.mediaCollectionView.count, section: 0)
self.mediaCollectionView.append(image!)
self.collectionView.insertItems(at: [indexPath])
self.mediaNameCollection.append(FileType.file_video_default.rawValue)
self.mediaURLCollection.append(urlFile!)
do {
let data = try Data(contentsOf: url)
self.fileContext.append(data)
} catch let error{
print(error.localizedDescription)
}
}, completion: nil)
}
}
@ -1096,7 +1159,21 @@ import AVFoundation
let indexPath = IndexPath(row: self.mediaCollectionView.count, section: 0)
self.mediaCollectionView.append(image)
self.collectionView.insertItems(at: [indexPath])
self.mediaNameCollection.append(FileType.file_picture_default.rawValue)
let date = Date()
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd-HHmmss"
let dateString = df.string(from: date)
let fileUrl = URL(string: dateString + ".jpeg")
self.mediaURLCollection.append(fileUrl!)
let data = image.jpegData(compressionQuality: 1)
self.fileContext.append(data!)
}, completion: nil)
case "public.movie":
let videoUrl = info[UIImagePickerController.InfoKey.mediaURL] as! URL
@ -1106,7 +1183,13 @@ import AVFoundation
let indexPath = IndexPath(row: self.mediaCollectionView.count, section: 0)
self.mediaCollectionView.append(image!)
self.collectionView.insertItems(at: [indexPath])
self.mediaNameCollection.append(FileType.file_video_default.rawValue)
self.mediaURLCollection.append(videoUrl)
do {
let data = try Data(contentsOf: videoUrl)
self.fileContext.append(data)
} catch let error{
print(error.localizedDescription)
}
}, completion: nil)
default:
print("Mismatched type: \(mediaType)")
@ -1130,7 +1213,13 @@ import AVFoundation
let indexPath = IndexPath(row: self.mediaCollectionView.count, section: 0)
self.mediaCollectionView.append(image!)
self.collectionView.insertItems(at: [indexPath])
self.mediaNameCollection.append(FileType.file_picture_default.rawValue)
self.mediaURLCollection.append(url)
do {
let data = try Data(contentsOf: url)
self.fileContext.append(data)
} catch let error{
print(error.localizedDescription)
}
}, completion: nil)
}else if(videoExtension.contains(url.pathExtension.lowercased())){
let thumbnail = createThumbnailOfVideoFromFileURL(videoURL: url.relativeString)
@ -1138,7 +1227,13 @@ import AVFoundation
let indexPath = IndexPath(row: self.mediaCollectionView.count, section: 0)
self.mediaCollectionView.append(thumbnail!)
self.collectionView.insertItems(at: [indexPath])
self.mediaNameCollection.append(FileType.file_video_default.rawValue)
self.mediaURLCollection.append(url)
do {
let data = try Data(contentsOf: url)
self.fileContext.append(data)
} catch let error{
print(error.localizedDescription)
}
}, completion: nil)
}else{
//let otherFile = UIImage(named: "chat_error")
@ -1148,7 +1243,13 @@ import AVFoundation
let indexPath = IndexPath(row: self.mediaCollectionView.count, section: 0)
self.mediaCollectionView.append(otherFileImage!)
self.collectionView.insertItems(at: [indexPath])
self.mediaNameCollection.append(url.lastPathComponent)
self.mediaURLCollection.append(url)
do {
let data = try Data(contentsOf: url)
self.fileContext.append(data)
} catch let error{
print(error.localizedDescription)
}
}, completion: nil)
}
}

View file

@ -35,6 +35,8 @@ class MessageView: UIView, UITextViewDelegate {
let messageTextView = UIView()
let messageText = UITextView()
let ephemeralIndicator = UIImageView(image: UIImage(named: "ephemeral_messages_color_A.png"))
var fileContext = false
var isComposing = false
override init(frame: CGRect) {
super.init(frame: frame)
@ -82,10 +84,17 @@ class MessageView: UIView, UITextViewDelegate {
func textViewDidChangeSelection(_ textView: UITextView) {
let chatRoom = ChatRoom.getSwiftObject(cObject: PhoneMainView.instance().currentRoom)
if messageText.text.isEmpty {
if messageText.text.isEmpty && fileContext == false {
sendButton.isEnabled = false
} else {
chatRoom.compose()
if !isComposing {
chatRoom.compose()
let timer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false) { timer in
self.isComposing = false
}
}
isComposing = true
sendButton.isEnabled = true
}
}

View file

@ -25,6 +25,7 @@
@interface FileTransferDelegate : NSObject
- (void)uploadFileContent: (FileContext *)context forChatRoom:(LinphoneChatRoom *)chatRoom rootMessage:(LinphoneChatMessage *)rootMessage;
- (void)uploadFileContentForSwift: (NSArray<NSData *>*)context urlList:(NSArray<NSURL *>*)urls forChatRoom:(LinphoneChatRoom *)chatRoom rootMessage:(LinphoneChatMessage *)rootMessage;
- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key rootMessage:(LinphoneChatMessage *)rootMessage;
- (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality;
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name rootMessage:(LinphoneChatMessage *)rootMessage;

View file

@ -184,6 +184,61 @@ static void file_transfer_progress_indication_send(LinphoneChatMessage *message,
}
}
- (void)uploadFileContentForSwift: (NSArray<NSData *>*)context urlList:(NSArray<NSURL *>*)urls forChatRoom:(LinphoneChatRoom *)chatRoom rootMessage:(LinphoneChatMessage *)rootMessage{
[LinphoneManager.instance.fileTransferDelegates addObject:self];
_message = rootMessage;
NSMutableArray<NSString *> *names = [[NSMutableArray alloc] init];
NSMutableArray<NSString *> *types = [[NSMutableArray alloc] init];
int i = 0;
for (i = 0; i < [context count]; ++i) {
LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom));
//NSString *type = @"image";//[urls objectAtIndex:i].pathExtension;
NSString *type = [self getTypeForFileTransferDelegate:[urls objectAtIndex:i].pathExtension.localizedLowercaseString];
NSString *name = [urls objectAtIndex:i].lastPathComponent;
NSData *data = [context objectAtIndex:i];
[ChatConversationViewSwift writeFileInImagesDirectory:data name:name];
linphone_content_set_type(content, [type UTF8String]);
linphone_content_set_subtype(content, [name.pathExtension UTF8String]);
linphone_content_set_name(content, [name UTF8String]);
linphone_content_set_file_path(content, [[LinphoneManager imagesDirectory] stringByAppendingPathComponent:name].UTF8String);
[names addObject:name];
[types addObject:type];
linphone_chat_message_add_file_content(_message, content);
linphone_content_unref(content);
}
BOOL basic = linphone_chat_message_get_chat_room(rootMessage);
const LinphoneAccountParams *params = linphone_account_get_params(linphone_core_get_default_account(LC));
BOOL cpimEnabled = linphone_account_params_cpim_in_basic_chat_room_enabled(params);
if ((!basic || cpimEnabled) && _text!=nil && ![_text isEqualToString:@""])
linphone_chat_message_add_utf8_text_content(_message, [_text UTF8String]);
// todo indication progress
[LinphoneManager setValueInMessageAppData:names forKey:@"multiparts" inMessage:_message];
[LinphoneManager setValueInMessageAppData:types forKey:@"multipartstypes" inMessage:_message];
LOGI(@"%p Uploading content from message %p", self, _message);
linphone_chat_message_send(_message);
if (basic && !cpimEnabled && _text!=nil && ![_text isEqualToString:@""]) {
linphone_chat_message_send(linphone_chat_room_create_message_from_utf8(linphone_chat_message_get_chat_room(rootMessage), _text.UTF8String));
}
}
- (NSString*)getTypeForFileTransferDelegate:(NSString *)type {
if([type isEqual:@"png"] || [type isEqual:@"jpg"] || [type isEqual:@"jpeg"] || [type isEqual:@"bmp"] || [type isEqual:@"heic"] || [type isEqual:@"file_picture_default"]){
return @"image";
}else if([type isEqual:@"mkv"] || [type isEqual:@"avi"] || [type isEqual:@"mov"] || [type isEqual:@"mp4"] || [type isEqual:@"file_video_default"]){
return @"video";
}else{
return @"file";
}
}
- (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality {
NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]];

View file

@ -17,3 +17,4 @@
#import "UIChatBubbleTextCell.h"
#import "ChatConversationTableView.h"
#import "EphemeralSettingsView.h"
#import "FileTransferDelegate.h"