diff --git a/swift/ChatRoomTutorial/ChatRoomTutorial/ChatRoomExample.swift b/swift/ChatRoomTutorial/ChatRoomTutorial/ChatRoomExample.swift index 3ba2ca2..d054054 100644 --- a/swift/ChatRoomTutorial/ChatRoomTutorial/ChatRoomExample.swift +++ b/swift/ChatRoomTutorial/ChatRoomTutorial/ChatRoomExample.swift @@ -23,39 +23,30 @@ class ChatRoomExampleContext : ObservableObject /*------------ Logs related variables ------------------------*/ var loggingUnit = LoggingUnit() - /*-------- Chatroom tutorial related variables --------------- - -------- "A" always initiates the chat, "B" answers --------*/ + /*-------- Chatroom tutorial related variables ---------------*/ + @Published var dest : String = "sip:chatdest@sip.linphone.org" + @Published var id : String = "sip:thisphone@sip.linphone.org" + @Published var passwd : String = "thispassword" + @Published var loggedIn: Bool = false let mFactoryUri = "sip:conference-factory@sip.linphone.org" var mProxyConfig : ProxyConfig! var mChatMessage : ChatMessage? + var mLastFileMessageReceived : ChatMessage? let mLinphoneCoreDelegate = LinphoneCoreDelegate() let mChatMessageDelegate = LinphoneChatMessageTracker() let mChatRoomDelegate = LinphoneChatRoomStateTracker() var mChatRoom : ChatRoom? + @Published var encryptionEnabled : Bool = false + @Published var groupChatEnabled : Bool = true @Published var chatroomState = ChatroomExampleState.Unstarted - @Published var isFlexiSip : Bool = true @Published var textToSend: String = "msg to send" @Published var sReceivedMessages : String = "" - @Published var dest : String = "sip:chatdest@sip.linphone.org" - @Published var id : String = "sip:thisphone@sip.linphone.org" - @Published var passwd : String = "mypassword" - @Published var loggedIn: Bool = false - //var fileFolderUrl : URL? - //var fileUrl : URL? - - func getStateAsString() -> String - { - switch (chatroomState) - { - case ChatroomExampleState.Unstarted : return "Unstarted" - case ChatroomExampleState.Starting: return "Starting" - case ChatroomExampleState.Started: return "Started" - } - } + var fileFolderUrl : URL? + var fileUrl : URL? init() { @@ -67,23 +58,24 @@ class ChatRoomExampleContext : ObservableObject // main loop for receiving notifications and doing background linphonecore work: mCore.autoIterateEnabled = true + mCore.limeX3DhEnabled = true; + mCore.limeX3DhServerUrl = "https://lime.linphone.org/lime-server/lime-server.php" + mCore.fileTransferServer = "https://www.linphone.org:444/lft.php" try? mCore.start() - // Important ! Will notify when config are registered so that we can proceed with the chatroom creations - // Also handles chat message reception + // Important ! Will notify when config logged in, or when a message is received mCore.addDelegate(delegate: mLinphoneCoreDelegate) - /* let documentsPath = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]) - let myFiles = documentsPath.appendingPathComponent("TutorialFiles") - fileUrl = myFiles?.appendingPathComponent("file_to_transfer.txt") + fileFolderUrl = documentsPath.appendingPathComponent("TutorialFiles") + fileUrl = fileFolderUrl?.appendingPathComponent("file_to_transfer.txt") do{ - try FileManager.default.createDirectory(atPath: myFiles!.path, withIntermediateDirectories: true, attributes: nil) + try FileManager.default.createDirectory(atPath: fileFolderUrl!.path, withIntermediateDirectories: true, attributes: nil) try String("My file content").write(to: fileUrl!, atomically: false, encoding: .utf8) }catch let error as NSError{ - print("Unable to create directory",error) + print("Unable to create d)irectory",error) } - */ + } func createProxyConfigAndRegister() @@ -109,13 +101,15 @@ class ChatRoomExampleContext : ObservableObject do { let chatDest = [try Factory.Instance.createAddress(addr: dest)] let chatParams = try mCore.createDefaultChatRoomParams() - if (isFlexiSip) { + if (groupChatEnabled) { chatParams.backend = ChatRoomBackend.FlexisipChat - chatParams.encryptionEnabled = false - chatParams.groupEnabled = false + chatParams.encryptionEnabled = encryptionEnabled + if (encryptionEnabled) { + chatParams.encryptionBackend = ChatRoomEncryptionBackend.Lime + } + chatParams.groupEnabled = groupChatEnabled chatParams.subject = "Tutorial Chatroom" mChatRoom = try mCore.createChatRoom(params: chatParams, localAddr: mProxyConfig.contact!, participants: chatDest) - mChatRoom!.addDelegate(delegate: mChatRoomDelegate) // Flexisip chatroom requires a setup time. The delegate will set the state to started when it is ready. chatroomState = ChatroomExampleState.Starting } @@ -127,17 +121,21 @@ class ChatRoomExampleContext : ObservableObject } } catch { print(error) + return; } - + + mChatRoom!.addDelegate(delegate: mChatRoomDelegate) + + DispatchQueue.global(qos: .userInitiated).async { - if (self.isFlexiSip) { + if (self.groupChatEnabled) { // Wait until we're sure that the chatroom is ready to send messages while(self.chatroomState != ChatroomExampleState.Started){ usleep(100000) } } if let chatRoom = self.mChatRoom { - self.send(room: chatRoom, msg: "Hello, \((self.isFlexiSip) ? "Flexisip" : "Basic") World !") + self.send(room: chatRoom, msg: "Hello, \((self.groupChatEnabled) ? "Group" : "") World !") } } } @@ -154,7 +152,7 @@ class ChatRoomExampleContext : ObservableObject { do { - self.mChatMessage = try room.createMessage(message: msg) + self.mChatMessage = try room.createMessageFromUtf8(message: msg) self.mChatMessage!.addDelegate(delegate: self.mChatMessageDelegate) self.mChatMessage!.send() } catch { @@ -168,19 +166,41 @@ class ChatRoomExampleContext : ObservableObject send(room: chatRoom, msg: textToSend) } } - /* - func sendFile() + + func sendExampleFile() { do { let content = try mCore.createContent() - content.filePath = fileUrl!.absoluteString + content.filePath = fileUrl!.path + content.name = "file_to_transfer.txt" + content.type = "text" + content.subtype = "plain" - //mChatRoomA?.createFileTransferMessage(initialContent: <#T##Content#>) - print(try String(contentsOf: fileUrl!, encoding: .utf8)) + mChatMessage = try mChatRoom!.createFileTransferMessage(initialContent: content) + mChatMessage!.addDelegate(delegate: self.mChatMessageDelegate) + mChatMessage!.send() }catch let error as NSError { print("Unable to create directory",error) } - }*/ + } + + func downloadLastFileMessage() { + if let message = mLastFileMessageReceived { + for content in message.contents { + if (content.isFileTransfer && content.filePath.isEmpty) { + let contentName = content.name + if (!contentName.isEmpty) { + content.filePath = fileFolderUrl!.appendingPathComponent(contentName).path + print("Start downloading \(content.name) into \(content.filePath)") + if (!message.downloadContent(content: content)) { + print ("Download of \(contentName) failed") + } + } + } + } + } + mLastFileMessageReceived = nil + } } @@ -200,29 +220,37 @@ class LinphoneCoreDelegate: CoreDelegate { tutorialContext.mChatRoom = room tutorialContext.chatroomState = ChatroomExampleState.Started } - if (message.contentType == "text/plain") { - tutorialContext.sReceivedMessages += "\n\(message.textContent)" + + if (message.hasTextContent()) { + tutorialContext.sReceivedMessages += "\n\(message.utf8Text)" + } + + for content in message.contents { + if (content.isFileTransfer) { + tutorialContext.mLastFileMessageReceived = message + tutorialContext.sReceivedMessages += "\n File(s) available(s) for download" + break; + } } - print(message.contents.count) } } class LinphoneChatRoomStateTracker: ChatRoomDelegate { - + var tutorialContext : ChatRoomExampleContext! - - func onStateChanged(chatRoom cr: ChatRoom, newState: ChatRoom.State) { - if (newState == ChatRoom.State.Created) - { - // This will only have sense when WE are creating a flexisip chatroom. - print("ChatRoomTrace - Chatroom ready to start") - tutorialContext.chatroomState = ChatroomExampleState.Started - } - } + + func onConferenceJoined(chatRoom: ChatRoom, eventLog: EventLog) { + print("ChatRoomTrace - Chatroom ready to start") + tutorialContext.chatroomState = ChatroomExampleState.Started + } } class LinphoneChatMessageTracker: ChatMessageDelegate { func onMsgStateChanged(message msg: ChatMessage, state: ChatMessage.State) { print("MessageTrace - msg state changed: \(state)\n") } + + func onFileTransferRecv(message: ChatMessage, content: Content, buffer: Buffer) { + + } } diff --git a/swift/ChatRoomTutorial/ChatRoomTutorial/ContentView.swift b/swift/ChatRoomTutorial/ChatRoomTutorial/ContentView.swift index 729b54c..62fbb62 100644 --- a/swift/ChatRoomTutorial/ChatRoomTutorial/ContentView.swift +++ b/swift/ChatRoomTutorial/ChatRoomTutorial/ContentView.swift @@ -8,6 +8,16 @@ import SwiftUI +func getStateAsString(chatroomState : ChatroomExampleState) -> String +{ + switch (chatroomState) + { + case ChatroomExampleState.Unstarted : return "Unstarted" + case ChatroomExampleState.Starting: return "Starting" + case ChatroomExampleState.Started: return "Started" + } +} + struct ContentView: View { @ObservedObject var tutorialContext : ChatRoomExampleContext @@ -36,7 +46,7 @@ struct ContentView: View { .foregroundColor(Color.white) .frame(width: 90.0, height: 42.0) .background(Color.gray) - } + }.disabled(tutorialContext.loggedIn) Text("Login State : ") .font(.footnote) Text(tutorialContext.loggedIn ? "Logged in" : "Unregistered") @@ -48,20 +58,28 @@ struct ContentView: View { Text("Chat destination :") TextField("", text : $tutorialContext.dest) .textFieldStyle(RoundedBorderTextFieldStyle()) - } + }.disabled(tutorialContext.chatroomState != ChatroomExampleState.Unstarted) .padding(.top, 5) HStack { - VStack() { - Toggle(isOn: $tutorialContext.isFlexiSip) { - Text("FlexiSip ChatRoom") - }.frame(width: 210).padding(.top) - + VStack { + Toggle(isOn: $tutorialContext.groupChatEnabled) { + Text("Group ChatRoom") + }.frame(width: 210) + .padding(.top) + .disabled(tutorialContext.chatroomState != ChatroomExampleState.Unstarted) + Toggle(isOn: $tutorialContext.encryptionEnabled) { + VStack { + Text("Lime Encryption") + Text("(group chat only)").italic().font(.footnote) + } + }.frame(width: 210) + .disabled(tutorialContext.chatroomState != ChatroomExampleState.Unstarted) HStack { Text("Chatroom state: ") .font(.footnote) - Text(tutorialContext.getStateAsString()) + Text(getStateAsString(chatroomState: tutorialContext.chatroomState)) .font(.footnote) - .foregroundColor((tutorialContext.chatroomState == ChatroomExampleState.Started) ? Color.green : Color.black) + .foregroundColor((tutorialContext.chatroomState == ChatroomExampleState.Started) ? Color.green : Color .black) } } Button(action: { @@ -74,9 +92,10 @@ struct ContentView: View { { Text((tutorialContext.chatroomState == ChatroomExampleState.Started) ? "Reset" : "Start\nChat") .font(.largeTitle) - .foregroundColor(Color.white) - .frame(width: 100.0, height: 82.0) - .background(Color.gray) + .foregroundColor(Color.white) + .frame(width: 140.0, height: 100.0) + .background(Color.gray) + .padding() } } HStack { @@ -99,6 +118,25 @@ struct ContentView: View { .background(Color.gray) }.disabled(tutorialContext.chatroomState != ChatroomExampleState.Started) } + HStack { + Button(action: tutorialContext.sendExampleFile) + { + Text("Send example \n file") + .foregroundColor(Color.white) + .multilineTextAlignment(.center) + .frame(width: 120.0, height: 50.0) + .background(Color.gray) + }.disabled(tutorialContext.chatroomState != ChatroomExampleState.Started) + + Button(action: tutorialContext.downloadLastFileMessage) + { + Text("Download last files \n received") + .foregroundColor(Color.white) + .multilineTextAlignment(.center) + .frame(width: 150.0, height: 50.0) + .background(Color.gray) + }.disabled(tutorialContext.mLastFileMessageReceived == nil) + } } Spacer() }.padding(.top)