From 1492e59dca1c48a7e7799e763cbf934b88596272 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Fri, 26 Jun 2020 15:26:53 +0200 Subject: [PATCH 01/46] update sdk to 4.5.0 --- Podfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile b/Podfile index beb3366b5..cebfba85f 100644 --- a/Podfile +++ b/Podfile @@ -5,7 +5,7 @@ source "https://github.com/CocoaPods/Specs.git" def all_pods if ENV['PODFILE_PATH'].nil? - pod 'linphone-sdk', '4.4.3' + pod 'linphone-sdk', '~> 4.5.0-beta' else pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk end From 98449ef104f1732ebd6836efb9792c821f77941b Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Wed, 29 Jul 2020 10:37:51 +0200 Subject: [PATCH 02/46] update api --- Classes/AssistantView.m | 2 +- Classes/CallManager.swift | 4 +-- Classes/CallView.m | 4 +-- Classes/LinphoneAppDelegate.m | 8 ++--- Classes/LinphoneCoreSettingsStore.m | 4 +-- Classes/LinphoneManager.m | 16 +++++----- Classes/LinphoneUI/StatusBarView.m | 4 +-- Classes/LinphoneUI/UIVideoButton.m | 2 +- msgNotification/Utils.swift | 24 +++++++-------- .../NotificationViewController.swift | 26 ++++++++-------- .../NotificationService.swift | 30 +++++++++---------- 11 files changed, 62 insertions(+), 62 deletions(-) diff --git a/Classes/AssistantView.m b/Classes/AssistantView.m index 1fd1e5928..f00e7be8f 100644 --- a/Classes/AssistantView.m +++ b/Classes/AssistantView.m @@ -602,7 +602,7 @@ static UICompositeViewDescription *compositeDescription = nil; [self changeView:_remoteProvisioningLoginView back:FALSE animation:TRUE]; - linphone_proxy_config_destroy(default_conf); + linphone_proxy_config_unref(default_conf); } - (void)resetTextFields { diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift index 7cb376bc8..5e144eabc 100644 --- a/Classes/CallManager.swift +++ b/Classes/CallManager.swift @@ -445,12 +445,12 @@ class CoreManagerDelegate: CoreDelegate { } } - override func onCallStateChanged(lc: Core, call: Call, cstate: Call.State, message: String) { + override func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) { let addr = call.remoteAddress; let displayName = FastAddressBook.displayName(for: addr?.getCobject) ?? "Unknow" let callLog = call.callLog let callId = callLog?.callId - let video = UIApplication.shared.applicationState == .active && (lc.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false) + let video = UIApplication.shared.applicationState == .active && (core.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false) // we keep the speaker auto-enabled state in this static so that we don't // force-enable it on ICE re-invite if the user disabled it. CoreManagerDelegate.speaker_already_enabled = false diff --git a/Classes/CallView.m b/Classes/CallView.m index 0cc1db6d7..e00759d9f 100644 --- a/Classes/CallView.m +++ b/Classes/CallView.m @@ -687,7 +687,7 @@ static void hideSpinner(LinphoneCall *call, void *user_data) { if (call == linphone_core_get_current_call(LC)) { LinphoneCallParams *params = linphone_core_create_call_params(LC, call); linphone_call_accept_update(call, params); - linphone_call_params_destroy(params); + linphone_call_params_unref(params); [videoDismissTimer invalidate]; videoDismissTimer = nil; } @@ -698,7 +698,7 @@ static void hideSpinner(LinphoneCall *call, void *user_data) { LinphoneCallParams *params = linphone_core_create_call_params(LC, call); linphone_call_params_enable_video(params, TRUE); linphone_call_accept_update(call, params); - linphone_call_params_destroy(params); + linphone_call_params_unref(params); [videoDismissTimer invalidate]; videoDismissTimer = nil; } diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index 627d7d8d7..3fa616136 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -652,7 +652,7 @@ LinphoneCallParams *params = linphone_core_create_call_params(LC, call); linphone_call_accept_update(call, params); - linphone_call_params_destroy(params); + linphone_call_params_unref(params); } else if ([response.actionIdentifier isEqual:@"Accept"]) { LOGI(@"User accept video proposal"); if (call != linphone_core_get_current_call(LC)) @@ -663,7 +663,7 @@ LinphoneCallParams *params = linphone_core_create_call_params(LC, call); linphone_call_params_enable_video(params, TRUE); linphone_call_accept_update(call, params); - linphone_call_params_destroy(params); + linphone_call_params_unref(params); } else if ([response.actionIdentifier isEqual:@"Confirm"]) { if (linphone_core_get_current_call(LC) == call) linphone_call_set_authentication_token_verified(call, YES); @@ -706,7 +706,7 @@ LinphoneCallParams *params = linphone_core_create_call_params(LC, call); linphone_call_accept_update(call, params); - linphone_call_params_destroy(params); + linphone_call_params_unref(params); [videoDismissTimer invalidate]; } onConfirmationClick:^() { @@ -717,7 +717,7 @@ LinphoneCallParams *params = linphone_core_create_call_params(LC, call); linphone_call_params_enable_video(params, TRUE); linphone_call_accept_update(call, params); - linphone_call_params_destroy(params); + linphone_call_params_unref(params); [videoDismissTimer invalidate]; } inController:PhoneMainView.instance]; diff --git a/Classes/LinphoneCoreSettingsStore.m b/Classes/LinphoneCoreSettingsStore.m index 37566f274..9d0aacf72 100644 --- a/Classes/LinphoneCoreSettingsStore.m +++ b/Classes/LinphoneCoreSettingsStore.m @@ -551,7 +551,7 @@ const char *password = [accountPassword UTF8String]; const char *ha1 = [accountHa1 UTF8String]; - if (linphone_proxy_config_set_identity(proxyCfg, identity) == -1) { + if (linphone_proxy_config_set_identity_address(proxyCfg, linphoneAddress) == -1) { error = NSLocalizedString(@"Invalid username or domain", nil); goto bad_proxy; } @@ -586,7 +586,7 @@ [LinphoneManager.instance configurePushTokenForProxyConfig:proxyCfg]; linphone_proxy_config_enable_register(proxyCfg, is_enabled); - linphone_proxy_config_enable_avpf(proxyCfg, use_avpf); + linphone_proxy_config_set_avpf_mode(proxyCfg, use_avpf); linphone_proxy_config_set_expires(proxyCfg, expire); if (is_default) { linphone_core_set_default_proxy_config(LC, proxyCfg); diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m index 3fd69d5f5..e2b50a393 100644 --- a/Classes/LinphoneManager.m +++ b/Classes/LinphoneManager.m @@ -388,7 +388,7 @@ static int check_should_migrate_images(void *data, int argc, char **argv, char * withDefault:@"sip.linphone.org"] .UTF8String) != 0) { LOGI(@"Migrating proxy config to use AVPF"); - linphone_proxy_config_enable_avpf(proxy, TRUE); + linphone_proxy_config_set_avpf_mode(proxy, LinphoneAVPFEnabled); } proxies = proxies->next; } @@ -511,7 +511,7 @@ static void migrateWizardToAssistant(const char *entry, void *user_data) { + (void)dumpLcConfig { if (theLinphoneCore) { LpConfig *conf = LinphoneManager.instance.configDb; - char *config = lp_config_dump(conf); + char *config = linphone_config_dump(conf); LOGI(@"\n%s", config); ms_free(config); } @@ -1682,7 +1682,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { factory = factoryIpad; } _configDb = linphone_config_new_for_shared_core(kLinphoneMsgNotificationAppGroupId.UTF8String, @"linphonerc".UTF8String, factory.UTF8String); - lp_config_clean_entry(_configDb, "misc", "max_calls"); + linphone_config_clean_entry(_configDb, "misc", "max_calls"); } #pragma mark - Audio route Functions @@ -2015,7 +2015,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { - (void)configureVbrCodecs { PayloadType *pt; - int bitrate = lp_config_get_int( + int bitrate = linphone_config_get_int( _configDb, "audio", "codec_bitrate_limit", kLinphoneAudioVbrCodecDefaultBitrate); /*default value is in linphonerc or linphonerc-factory*/ const MSList *audio_codecs = linphone_core_get_audio_codecs(theLinphoneCore); @@ -2070,7 +2070,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { - (void)lpConfigSetString:(NSString *)value forKey:(NSString *)key inSection:(NSString *)section { if (!key) return; - lp_config_set_string(_configDb, [section UTF8String], [key UTF8String], value ? [value UTF8String] : NULL); + linphone_config_set_string(_configDb, [section UTF8String], [key UTF8String], value ? [value UTF8String] : NULL); } - (NSString *)lpConfigStringForKey:(NSString *)key { return [self lpConfigStringForKey:key withDefault:nil]; @@ -2084,7 +2084,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { - (NSString *)lpConfigStringForKey:(NSString *)key inSection:(NSString *)section withDefault:(NSString *)defaultValue { if (!key) return defaultValue; - const char *value = lp_config_get_string(_configDb, [section UTF8String], [key UTF8String], NULL); + const char *value = linphone_config_get_string(_configDb, [section UTF8String], [key UTF8String], NULL); return value ? [NSString stringWithUTF8String:value] : defaultValue; } @@ -2094,7 +2094,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { - (void)lpConfigSetInt:(int)value forKey:(NSString *)key inSection:(NSString *)section { if (!key) return; - lp_config_set_int(_configDb, [section UTF8String], [key UTF8String], (int)value); + linphone_config_set_int(_configDb, [section UTF8String], [key UTF8String], (int)value); } - (int)lpConfigIntForKey:(NSString *)key { return [self lpConfigIntForKey:key withDefault:-1]; @@ -2108,7 +2108,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { - (int)lpConfigIntForKey:(NSString *)key inSection:(NSString *)section withDefault:(int)defaultValue { if (!key) return defaultValue; - return lp_config_get_int(_configDb, [section UTF8String], [key UTF8String], (int)defaultValue); + return linphone_config_get_int(_configDb, [section UTF8String], [key UTF8String], (int)defaultValue); } - (void)lpConfigSetBool:(BOOL)value forKey:(NSString *)key { diff --git a/Classes/LinphoneUI/StatusBarView.m b/Classes/LinphoneUI/StatusBarView.m index cad865c74..eb4bb89d6 100644 --- a/Classes/LinphoneUI/StatusBarView.m +++ b/Classes/LinphoneUI/StatusBarView.m @@ -72,7 +72,7 @@ // Update to default state LinphoneProxyConfig *config = linphone_core_get_default_proxy_config(LC); - messagesUnreadCount = lp_config_get_int(linphone_core_get_config(LC), "app", "voice_mail_messages_count", 0); + messagesUnreadCount = linphone_config_get_int(linphone_core_get_config(LC), "app", "voice_mail_messages_count", 0); [self proxyConfigUpdate:config]; [self updateUI:linphone_core_get_calls_nb(LC)]; @@ -149,7 +149,7 @@ LOGI(@"Received new NOTIFY from voice mail: there is/are now %d message(s) unread", messagesUnreadCount); // save in lpconfig for future - lp_config_set_int(linphone_core_get_config(LC), "app", "voice_mail_messages_count", messagesUnreadCount); + linphone_config_set_int(linphone_core_get_config(LC), "app", "voice_mail_messages_count", messagesUnreadCount); [self updateVoicemail]; } diff --git a/Classes/LinphoneUI/UIVideoButton.m b/Classes/LinphoneUI/UIVideoButton.m index 223f89452..76083379a 100644 --- a/Classes/LinphoneUI/UIVideoButton.m +++ b/Classes/LinphoneUI/UIVideoButton.m @@ -67,7 +67,7 @@ INIT_WITH_COMMON_CF { LinphoneCallParams *call_params = linphone_core_create_call_params(LC,call); linphone_call_params_enable_video(call_params, FALSE); linphone_core_update_call(LC, call, call_params); - linphone_call_params_destroy(call_params); + linphone_call_params_unref(call_params); } else { LOGW(@"Cannot toggle video button, because no current call"); } diff --git a/msgNotification/Utils.swift b/msgNotification/Utils.swift index 92c2bb056..761cd7ad6 100644 --- a/msgNotification/Utils.swift +++ b/msgNotification/Utils.swift @@ -46,29 +46,29 @@ class LinphoneLoggingServiceManager: LoggingServiceDelegate { } } - override func onLogMessageWritten(logService: LoggingService, domain: String, lev: LogLevel, message: String) { - let level: String + override func onLogMessageWritten(logService: LoggingService, domain: String, level: LogLevel, message: String) { + let levelStr: String - switch lev { + switch level { case .Debug: - level = "Debug" + levelStr = "Debug" case .Trace: - level = "Trace" + levelStr = "Trace" case .Message: - level = "Message" + levelStr = "Message" case .Warning: - level = "Warning" + levelStr = "Warning" case .Error: - level = "Error" + levelStr = "Error" case .Fatal: - level = "Fatal" + levelStr = "Fatal" default: - level = "unknown" + levelStr = "unknown" } #if USE_CRASHLYTICS - Crashlytics.crashlytics().log("\(level) [\(domain)] \(message)\n") + Crashlytics.crashlytics().log("\(levelStr) [\(domain)] \(message)\n") #endif - NSLog("\(level) [\(domain)] \(message)\n") + NSLog("\(levelStr) [\(domain)] \(message)\n") } } diff --git a/msgNotificationContent/NotificationViewController.swift b/msgNotificationContent/NotificationViewController.swift index 6003d398c..ca57528d8 100644 --- a/msgNotificationContent/NotificationViewController.swift +++ b/msgNotificationContent/NotificationViewController.swift @@ -82,7 +82,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi } if (needToStop) { - log.error(msg: "core stopped by app") + log.error(message: "core stopped by app") throw LinphoneError.timeout } else { completion(.dismiss) @@ -90,7 +90,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi } } catch { - log.error(msg: "error: \(error)") + log.error(message: "error: \(error)") completion(.dismissAndForwardAction) } } @@ -123,19 +123,19 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi msgDelegate = LinphoneChatMessageManager() let chatMsg = try room.createMessage(message: replyText) chatMsg.addDelegate(delegate: msgDelegate) - room.sendChatMessage(msg: chatMsg) + chatMsg.send() room.markAsRead() } for i in 0...50 where !isReplySent && !needToStop { - log.debug(msg: "reply \(i)") + log.debug(message: "reply \(i)") lc!.iterate() usleep(10000) } } func startCore() throws { - config = Config.newForSharedCore(appGroupId: APP_GROUP_ID, configFilename: "linphonerc", factoryPath: "") + config = Config.newForSharedCore(appGroupId: APP_GROUP_ID, configFilename: "linphonerc", factoryConfigFilename: "") log = LoggingService.Instance /*enable liblinphone logs.*/ logDelegate = try! LinphoneLoggingServiceManager(config: config, log: log, domain: "msgNotificationContent") lc = try! Factory.Instance.createSharedCoreWithConfig(config: config, systemContext: nil, appGroupId: APP_GROUP_ID, mainCore: false) @@ -144,27 +144,27 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi lc!.addDelegate(delegate: coreDelegate) try lc!.start() - log.message(msg: "core started") + log.message(message: "core started") if (needToStop) { - log.error(msg: "core stopped by app") + log.error(message: "core stopped by app") throw LinphoneError.timeout } } func stopCore() { lc!.stopAsync() - log.message(msg: "stop core") + log.message(message: "stop core") for i in 0...100 where !coreStopped { - log.debug(msg: "stop \(i)") + log.debug(message: "stop \(i)") lc!.iterate() usleep(50000) } } class LinphoneCoreManager: CoreDelegate { - override func onGlobalStateChanged(lc: Core, gstate: GlobalState, message: String) { - log.message(msg: "global state changed: \(gstate) : \(message) \n") + override func onGlobalStateChanged(core: Core, state gstate: GlobalState, message: String) { + log.message(message: "global state changed: \(gstate) : \(message) \n") if (gstate == .Shutdown) { needToStop = true } else if (gstate == .Off) { @@ -174,8 +174,8 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi } class LinphoneChatMessageManager: ChatMessageDelegate { - override func onMsgStateChanged(msg: ChatMessage, state: ChatMessage.State) { - log.message(msg: "msg state changed: \(state)\n") + override func onMsgStateChanged(message: ChatMessage, state: ChatMessage.State) { + log.message(message: "msg state changed: \(state)\n") if (state == .Delivered) { isReplySent = true } diff --git a/msgNotificationService/NotificationService.swift b/msgNotificationService/NotificationService.swift index b52a3761c..0bcf505a3 100644 --- a/msgNotificationService/NotificationService.swift +++ b/msgNotificationService/NotificationService.swift @@ -58,15 +58,15 @@ class NotificationService: UNNotificationServiceExtension { if let bestAttemptContent = bestAttemptContent { createCore() - NotificationService.log.message(msg: "received push payload : \(bestAttemptContent.userInfo.debugDescription)") + NotificationService.log.message(message: "received push payload : \(bestAttemptContent.userInfo.debugDescription)") if let chatRoomInviteAddr = bestAttemptContent.userInfo["chat-room-addr"] as? String, !chatRoomInviteAddr.isEmpty { - NotificationService.log.message(msg: "fetch chat room for invite, addr: \(chatRoomInviteAddr)") + NotificationService.log.message(message: "fetch chat room for invite, addr: \(chatRoomInviteAddr)") let chatRoom = lc!.getNewChatRoomFromConfAddr(chatRoomAddr: chatRoomInviteAddr) if let chatRoom = chatRoom { stopCore() - NotificationService.log.message(msg: "chat room invite received") + NotificationService.log.message(message: "chat room invite received") bestAttemptContent.title = NSLocalizedString("GC_MSG", comment: "") if (chatRoom.hasCapability(mask:ChatRoomCapabilities.OneToOne.rawValue)) { if (chatRoom.peerAddress?.displayName.isEmpty != true) { @@ -83,7 +83,7 @@ class NotificationService: UNNotificationServiceExtension { return } } else if let callId = bestAttemptContent.userInfo["call-id"] as? String { - NotificationService.log.message(msg: "fetch msg for callid ["+callId+"]") + NotificationService.log.message(message: "fetch msg for callid ["+callId+"]") let message = lc!.getNewMessageFromCallid(callId: callId) if let message = message { @@ -114,7 +114,7 @@ class NotificationService: UNNotificationServiceExtension { contentHandler(bestAttemptContent) return } else { - NotificationService.log.message(msg: "Message not found for callid ["+callId+"]") + NotificationService.log.message(message: "Message not found for callid ["+callId+"]") } } serviceExtensionTimeWillExpire() @@ -124,7 +124,7 @@ class NotificationService: UNNotificationServiceExtension { override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. - NotificationService.log.warning(msg: "serviceExtensionTimeWillExpire") + NotificationService.log.warning(message: "serviceExtensionTimeWillExpire") stopCore() if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { NSLog("[msgNotificationService] serviceExtensionTimeWillExpire") @@ -174,15 +174,15 @@ class NotificationService: UNNotificationServiceExtension { } } - NotificationService.log.message(msg: "msg: \(content) \n") + NotificationService.log.message(message: "msg: \(content) \n") return msgData; } func createCore() { NSLog("[msgNotificationService] create core") - let config = Config.newForSharedCore(appGroupId: APP_GROUP_ID, configFilename: "linphonerc", factoryPath: "") + let config = Config.newForSharedCore(appGroupId: APP_GROUP_ID, configFilename: "linphonerc", factoryConfigFilename: "") - if (NotificationService.log == nil || NotificationService.log.getDelegate() == nil) { + if (NotificationService.log == nil) { NotificationService.log = LoggingService.Instance /*enable liblinphone logs.*/ NotificationService.logDelegate = try! LinphoneLoggingServiceManager(config: config!, log: NotificationService.log, domain: "msgNotificationService") } @@ -190,7 +190,7 @@ class NotificationService: UNNotificationServiceExtension { } func stopCore() { - NotificationService.log.message(msg: "stop core") + NotificationService.log.message(message: "stop core") if let lc = lc { lc.stop() } @@ -201,14 +201,14 @@ class NotificationService: UNNotificationServiceExtension { count += lc!.unreadChatMessageCount count += lc!.missedCallsCount count += lc!.callsNb - NotificationService.log.message(msg: "badge: \(count)\n") + NotificationService.log.message(message: "badge: \(count)\n") return count } func getDisplayNameFromSipAddress(sipAddr: String?) -> String? { if let sipAddr = sipAddr { - NotificationService.log.message(msg: "looking for display name for \(sipAddr)") + NotificationService.log.message(message: "looking for display name for \(sipAddr)") if (sipAddr == "") { return nil } @@ -216,15 +216,15 @@ class NotificationService: UNNotificationServiceExtension { let addressBook = defaults?.dictionary(forKey: "addressBook") if (addressBook == nil) { - NotificationService.log.message(msg: "address book not found in userDefaults") + NotificationService.log.message(message: "address book not found in userDefaults") return nil } if let displayName = addressBook?[sipAddr] as? String { - NotificationService.log.message(msg: "display name for \(sipAddr): \(displayName)") + NotificationService.log.message(message: "display name for \(sipAddr): \(displayName)") return displayName } else { - NotificationService.log.message(msg: "display name for \(sipAddr) not found in userDefaults") + NotificationService.log.message(message: "display name for \(sipAddr) not found in userDefaults") return nil } } From 894367f33be9be38693ae57828c23a0a6d13829e Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Wed, 16 Sep 2020 17:34:49 +0200 Subject: [PATCH 03/46] use new delegates --- Classes/CallManager.swift | 24 +++++++---- msgNotification/Utils.swift | 3 +- .../NotificationViewController.swift | 42 ++++++++----------- 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift index 5e144eabc..7cb4a2a73 100644 --- a/Classes/CallManager.swift +++ b/Classes/CallManager.swift @@ -33,11 +33,10 @@ import AVFoundation * CallManager is a class that manages application calls and supports callkit. * There is only one CallManager by calling CallManager.instance(). */ -@objc class CallManager: NSObject { +@objc class CallManager: NSObject, CoreDelegate { static var theCallManager: CallManager? let providerDelegate: ProviderDelegate! // to support callkit let callController: CXCallController! // to support callkit - let manager: CoreManagerDelegate! // callbacks of the linphonecore var lc: Core? @objc var speakerBeforePause : Bool = false @objc var speakerEnabled : Bool = false @@ -47,12 +46,13 @@ import AVFoundation var referedFromCall: String? var referedToCall: String? var endCallkit: Bool = false + static var speaker_already_enabled : Bool = false + fileprivate override init() { providerDelegate = ProviderDelegate() callController = CXCallController() - manager = CoreManagerDelegate() } @objc static func instance() -> CallManager { @@ -407,6 +407,7 @@ import AVFoundation } } } +<<<<<<< HEAD @objc func performActionWhenCoreIsOn(action: @escaping ()->Void ) { if (manager.globalState == .On) { @@ -442,6 +443,13 @@ class CoreManagerDelegate: CoreDelegate { } } else { CallManager.instance().endCallkit = false +======= + + func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) { + if (cstate == .PushIncomingReceived) { + CallManager.instance().displayIncomingCall(call: call, handle: "Calling", hasVideo: false, callId: call.callLog?.callId ?? "") + return; +>>>>>>> 22b977a1a... use new delegates } } @@ -453,7 +461,7 @@ class CoreManagerDelegate: CoreDelegate { let video = UIApplication.shared.applicationState == .active && (core.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false) // we keep the speaker auto-enabled state in this static so that we don't // force-enable it on ICE re-invite if the user disabled it. - CoreManagerDelegate.speaker_already_enabled = false + CallManager.speaker_already_enabled = false if (call.userData == nil) { let appData = CallAppData() @@ -507,7 +515,7 @@ class CoreManagerDelegate: CoreDelegate { if (CallManager.instance().speakerBeforePause) { CallManager.instance().speakerBeforePause = false CallManager.instance().enableSpeaker(enable: true) - CoreManagerDelegate.speaker_already_enabled = true + CallManager.speaker_already_enabled = true } break case .OutgoingInit, @@ -533,7 +541,7 @@ class CoreManagerDelegate: CoreDelegate { case .End, .Error: UIDevice.current.isProximityMonitoringEnabled = false - CoreManagerDelegate.speaker_already_enabled = false + CallManager.speaker_already_enabled = false if (CallManager.instance().lc!.callsNb == 0) { CallManager.instance().enableSpeaker(enable: false) // disable this because I don't find anygood reason for it: _bluetoothAvailable = FALSE; @@ -600,9 +608,9 @@ class CoreManagerDelegate: CoreDelegate { } if (cstate == .IncomingReceived || cstate == .OutgoingInit || cstate == .Connected || cstate == .StreamsRunning) { - if ((call.currentParams?.videoEnabled ?? false) && !CoreManagerDelegate.speaker_already_enabled && !CallManager.instance().bluetoothEnabled) { + if ((call.currentParams?.videoEnabled ?? false) && !CallManager.speaker_already_enabled && !CallManager.instance().bluetoothEnabled) { CallManager.instance().enableSpeaker(enable: true) - CoreManagerDelegate.speaker_already_enabled = true + CallManager.speaker_already_enabled = true } } diff --git a/msgNotification/Utils.swift b/msgNotification/Utils.swift index 761cd7ad6..d032440b4 100644 --- a/msgNotification/Utils.swift +++ b/msgNotification/Utils.swift @@ -30,7 +30,6 @@ enum LinphoneError: Error { class LinphoneLoggingServiceManager: LoggingServiceDelegate { init(config: Config, log: LoggingService?, domain: String) throws { if let log = log { - super.init() let debugLevel = config.getInt(section: "app", key: "debugenable_preference", defaultValue: LogLevel.Debug.rawValue) let debugEnabled = (debugLevel >= LogLevel.Debug.rawValue && debugLevel < LogLevel.Error.rawValue) @@ -46,7 +45,7 @@ class LinphoneLoggingServiceManager: LoggingServiceDelegate { } } - override func onLogMessageWritten(logService: LoggingService, domain: String, level: LogLevel, message: String) { + func onLogMessageWritten(logService: LoggingService, domain: String, level: LogLevel, message: String) { let levelStr: String switch level { diff --git a/msgNotificationContent/NotificationViewController.swift b/msgNotificationContent/NotificationViewController.swift index ca57528d8..a923e83c6 100644 --- a/msgNotificationContent/NotificationViewController.swift +++ b/msgNotificationContent/NotificationViewController.swift @@ -31,13 +31,11 @@ var needToStop: Bool = false var coreStopped: Bool = false var log: LoggingService! -class NotificationViewController: UIViewController, UNNotificationContentExtension { +class NotificationViewController: UIViewController, UNNotificationContentExtension, ChatMessageDelegate, CoreDelegate { var lc: Core? var config: Config! var logDelegate: LinphoneLoggingServiceManager! - var msgDelegate: LinphoneChatMessageManager! - var coreDelegate: LinphoneCoreManager! override func viewDidLoad() { super.viewDidLoad() @@ -120,9 +118,8 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi let local = try lc!.createAddress(address: localAddress) let room = lc!.findChatRoom(peerAddr: peer, localAddr: local) if let room = room { - msgDelegate = LinphoneChatMessageManager() let chatMsg = try room.createMessage(message: replyText) - chatMsg.addDelegate(delegate: msgDelegate) + chatMsg.addDelegate(delegate: self) chatMsg.send() room.markAsRead() } @@ -140,8 +137,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi logDelegate = try! LinphoneLoggingServiceManager(config: config, log: log, domain: "msgNotificationContent") lc = try! Factory.Instance.createSharedCoreWithConfig(config: config, systemContext: nil, appGroupId: APP_GROUP_ID, mainCore: false) - coreDelegate = LinphoneCoreManager() - lc!.addDelegate(delegate: coreDelegate) + lc!.addDelegate(delegate: self) try lc!.start() log.message(message: "core started") @@ -162,23 +158,19 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi } } - class LinphoneCoreManager: CoreDelegate { - override func onGlobalStateChanged(core: Core, state gstate: GlobalState, message: String) { - log.message(message: "global state changed: \(gstate) : \(message) \n") - if (gstate == .Shutdown) { - needToStop = true - } else if (gstate == .Off) { - coreStopped = true - } - } - } + func onGlobalStateChanged(core: Core, state gstate: GlobalState, message: String) { + log.message(message: "global state changed: \(gstate) : \(message) \n") + if (gstate == .Shutdown) { + needToStop = true + } else if (gstate == .Off) { + coreStopped = true + } + } - class LinphoneChatMessageManager: ChatMessageDelegate { - override func onMsgStateChanged(message: ChatMessage, state: ChatMessage.State) { - log.message(message: "msg state changed: \(state)\n") - if (state == .Delivered) { - isReplySent = true - } - } - } + func onMsgStateChanged(message: ChatMessage, state: ChatMessage.State) { + log.message(message: "msg state changed: \(state)\n") + if (state == .Delivered) { + isReplySent = true + } + } } From 9f01c08aa4a76df202c44fc3f709be3f2c8ccf44 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Wed, 23 Sep 2020 10:44:43 +0200 Subject: [PATCH 04/46] fix build --- Classes/CallManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift index 7cb4a2a73..3e3e101b8 100644 --- a/Classes/CallManager.swift +++ b/Classes/CallManager.swift @@ -64,7 +64,7 @@ import AVFoundation @objc func setCore(core: OpaquePointer) { lc = Core.getSwiftObject(cObject: core) - lc?.addDelegate(delegate: manager) + lc?.addDelegate(delegate: self) } @objc static func getAppData(call: OpaquePointer) -> CallAppData? { From daf5fba2d0071ba266b08ca0f769be804bfa34fc Mon Sep 17 00:00:00 2001 From: QuentinArguillere Date: Fri, 9 Oct 2020 15:16:57 +0200 Subject: [PATCH 05/46] Fix crash caused by use of linphoneAddress variable after destroying it --- Classes/LinphoneCoreSettingsStore.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Classes/LinphoneCoreSettingsStore.m b/Classes/LinphoneCoreSettingsStore.m index 9d0aacf72..1b4118483 100644 --- a/Classes/LinphoneCoreSettingsStore.m +++ b/Classes/LinphoneCoreSettingsStore.m @@ -547,7 +547,6 @@ linphone_address_set_domain(linphoneAddress, [domain UTF8String]); linphone_address_set_display_name(linphoneAddress, (displayName.length ? displayName.UTF8String : NULL)); const char *identity = linphone_address_as_string(linphoneAddress); - linphone_address_destroy(linphoneAddress); const char *password = [accountPassword UTF8String]; const char *ha1 = [accountHa1 UTF8String]; @@ -636,7 +635,9 @@ bad_proxy: if (proxy) ms_free(proxy); - + if (linphoneAddress) + linphone_address_destroy(linphoneAddress); + // in case of error, show an alert to the user if (error != nil) { linphone_proxy_config_done(proxyCfg); From 244d580bf8a145febcda51ea2efb55a9597e81f3 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 12 Oct 2020 11:31:06 +0200 Subject: [PATCH 06/46] fix workaround for incoming call --- Classes/ProviderDelegate.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Classes/ProviderDelegate.swift b/Classes/ProviderDelegate.swift index 1b7fdd467..482df084b 100644 --- a/Classes/ProviderDelegate.swift +++ b/Classes/ProviderDelegate.swift @@ -174,15 +174,19 @@ extension ProviderDelegate: CXProviderDelegate { Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: answer call with call-id: \(String(describing: callId)) and UUID: \(uuid.description).") let call = CallManager.instance().callByCallId(callId: callId) + + // workaround + CallManager.configAudioSession(audioSession: AVAudioSession.sharedInstance()) if (call == nil || call?.state != Call.State.IncomingReceived) { // The application is not yet registered or the call is not yet received, mark the call as accepted. The audio session must be configured here. - CallManager.configAudioSession(audioSession: AVAudioSession.sharedInstance()) + callInfo?.accepted = true callInfos.updateValue(callInfo!, forKey: uuid) CallManager.instance().providerDelegate.endCallNotExist(uuid: uuid, timeout: .now() + 10) } else { CallManager.instance().acceptCall(call: call!, hasVideo: call!.params?.videoEnabled ?? false) } + Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: answer call finished.") action.fulfill() } From 7e6a0f9e75e6a70da2f3eb91744dc4feea9e4479 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 4 Jan 2021 17:41:17 +0100 Subject: [PATCH 07/46] fix build --- Classes/CallManager.swift | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift index 3e3e101b8..8e0967155 100644 --- a/Classes/CallManager.swift +++ b/Classes/CallManager.swift @@ -47,7 +47,8 @@ import AVFoundation var referedToCall: String? var endCallkit: Bool = false static var speaker_already_enabled : Bool = false - + var globalState : GlobalState = .Off + var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = [] fileprivate override init() { @@ -275,7 +276,7 @@ import AVFoundation if (CallManager.instance().nextCallIsTransfer) { let call = CallManager.instance().lc!.currentCall - try call?.transfer(referTo: addr.asString()) + try call?.transferTo(referTo: addr) CallManager.instance().nextCallIsTransfer = false } else { //We set the record file name here because we can't do it after the call is started. @@ -407,35 +408,27 @@ import AVFoundation } } } -<<<<<<< HEAD @objc func performActionWhenCoreIsOn(action: @escaping ()->Void ) { - if (manager.globalState == .On) { + if (globalState == .On) { action() } else { - manager.actionsToPerformOnceWhenCoreIsOn.append(action) + actionsToPerformOnceWhenCoreIsOn.append(action) } } - -} -class CoreManagerDelegate: CoreDelegate { - static var speaker_already_enabled : Bool = false - var globalState : GlobalState = .Off - var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = [] - - override func onGlobalStateChanged(lc: Core, gstate: GlobalState, message: String) { - if (gstate == .On) { + func onGlobalStateChanged(core: Core, state: GlobalState, message: String) { + if (state == .On) { actionsToPerformOnceWhenCoreIsOn.forEach { $0() } actionsToPerformOnceWhenCoreIsOn.removeAll() } - globalState = gstate + globalState = state } - - override func onRegistrationStateChanged(lc: Core, cfg: ProxyConfig, cstate: RegistrationState, message: String) { - if lc.proxyConfigList.count == 1 && (cstate == .Failed || cstate == .Cleared){ + + func onRegistrationStateChanged(core: Core, proxyConfig: ProxyConfig, state: RegistrationState, message: String) { + if core.proxyConfigList.count == 1 && (state == .Failed || state == .Cleared){ // terminate callkit immediately when registration failed or cleared, supporting single proxy configuration CallManager.instance().endCallkit = true for call in CallManager.instance().providerDelegate.uuids { @@ -443,17 +436,10 @@ class CoreManagerDelegate: CoreDelegate { } } else { CallManager.instance().endCallkit = false -======= - - func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) { - if (cstate == .PushIncomingReceived) { - CallManager.instance().displayIncomingCall(call: call, handle: "Calling", hasVideo: false, callId: call.callLog?.callId ?? "") - return; ->>>>>>> 22b977a1a... use new delegates } } - override func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) { + func onCallStateChanged(core: Core, call: Call, state cstate: Call.State, message: String) { let addr = call.remoteAddress; let displayName = FastAddressBook.displayName(for: addr?.getCobject) ?? "Unknow" let callLog = call.callLog From f8dda2f1a71ce7ede1ed8b3d85962987995caea4 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Tue, 5 Jan 2021 12:18:17 +0100 Subject: [PATCH 08/46] fix switch video --- Classes/CallManager.swift | 7 +++++++ Classes/CallView.m | 9 ++------- Classes/LinphoneAppDelegate.m | 23 +++++------------------ 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift index 8e0967155..dae7018aa 100644 --- a/Classes/CallManager.swift +++ b/Classes/CallManager.swift @@ -417,6 +417,13 @@ import AVFoundation } } + @objc func acceptVideo(call: OpaquePointer, confirm: Bool) { + let sCall = Call.getSwiftObject(cObject: call) + let params = try? lc?.createCallParams(call: sCall) + params?.videoEnabled = confirm + try? sCall.acceptUpdate(params: params) + } + func onGlobalStateChanged(core: Core, state: GlobalState, message: String) { if (state == .On) { actionsToPerformOnceWhenCoreIsOn.forEach { diff --git a/Classes/CallView.m b/Classes/CallView.m index e00759d9f..705f4ef47 100644 --- a/Classes/CallView.m +++ b/Classes/CallView.m @@ -685,9 +685,7 @@ static void hideSpinner(LinphoneCall *call, void *user_data) { onCancelClick:^() { LOGI(@"User declined video proposal"); if (call == linphone_core_get_current_call(LC)) { - LinphoneCallParams *params = linphone_core_create_call_params(LC, call); - linphone_call_accept_update(call, params); - linphone_call_params_unref(params); + [CallManager.instance acceptVideoWithCall:call confirm:FALSE]; [videoDismissTimer invalidate]; videoDismissTimer = nil; } @@ -695,10 +693,7 @@ static void hideSpinner(LinphoneCall *call, void *user_data) { onConfirmationClick:^() { LOGI(@"User accept video proposal"); if (call == linphone_core_get_current_call(LC)) { - LinphoneCallParams *params = linphone_core_create_call_params(LC, call); - linphone_call_params_enable_video(params, TRUE); - linphone_call_accept_update(call, params); - linphone_call_params_unref(params); + [CallManager.instance acceptVideoWithCall:call confirm:TRUE]; [videoDismissTimer invalidate]; videoDismissTimer = nil; } diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index 3fa616136..f4dc0991a 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -649,21 +649,15 @@ LOGI(@"User declined video proposal"); if (call != linphone_core_get_current_call(LC)) return; - - LinphoneCallParams *params = linphone_core_create_call_params(LC, call); - linphone_call_accept_update(call, params); - linphone_call_params_unref(params); + [CallManager.instance acceptVideoWithCall:call confirm:FALSE]; } else if ([response.actionIdentifier isEqual:@"Accept"]) { LOGI(@"User accept video proposal"); if (call != linphone_core_get_current_call(LC)) return; [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications]; - [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; - LinphoneCallParams *params = linphone_core_create_call_params(LC, call); - linphone_call_params_enable_video(params, TRUE); - linphone_call_accept_update(call, params); - linphone_call_params_unref(params); + [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; + [CallManager.instance acceptVideoWithCall:call confirm:TRUE]; } else if ([response.actionIdentifier isEqual:@"Confirm"]) { if (linphone_core_get_current_call(LC) == call) linphone_call_set_authentication_token_verified(call, YES); @@ -703,21 +697,14 @@ LOGI(@"User declined video proposal"); if (call != linphone_core_get_current_call(LC)) return; - - LinphoneCallParams *params = linphone_core_create_call_params(LC, call); - linphone_call_accept_update(call, params); - linphone_call_params_unref(params); + [CallManager.instance acceptVideoWithCall:call confirm:FALSE]; [videoDismissTimer invalidate]; } onConfirmationClick:^() { LOGI(@"User accept video proposal"); if (call != linphone_core_get_current_call(LC)) return; - - LinphoneCallParams *params = linphone_core_create_call_params(LC, call); - linphone_call_params_enable_video(params, TRUE); - linphone_call_accept_update(call, params); - linphone_call_params_unref(params); + [CallManager.instance acceptVideoWithCall:call confirm:TRUE]; [videoDismissTimer invalidate]; } inController:PhoneMainView.instance]; From 779c939e7811fa065816273e9efaf09c1213bfe9 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Thu, 7 Jan 2021 21:26:37 +0100 Subject: [PATCH 09/46] tag beta 4.4.0 (1) --- linphone.xcodeproj/project.pbxproj | 72 +++++++++++++++--------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 43a5eedcd..d650aca28 100644 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -4875,7 +4875,7 @@ CODE_SIGN_STYLE = Automatic; COMPRESS_PNG_FILES = NO; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -4904,14 +4904,14 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)"; LINK_WITH_STANDARD_LIBRARIES = YES; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; OTHER_CFLAGS = ( "-DBCTBX_LOG_DOMAIN=\\\"ios\\\"", "-DCHECK_VERSION_UPDATE=FALSE", "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"4.4.3\\\"", + "-DLINPHONE_SDK_VERSION=\\\"4.5.0-beta.31+1a68848\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5001,7 +5001,7 @@ CODE_SIGN_STYLE = Automatic; COMPRESS_PNG_FILES = NO; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5027,14 +5027,14 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)"; LINK_WITH_STANDARD_LIBRARIES = YES; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; OTHER_CFLAGS = ( "-DBCTBX_LOG_DOMAIN=\\\"ios\\\"", "-DCHECK_VERSION_UPDATE=FALSE", "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"4.4.3\\\"", + "-DLINPHONE_SDK_VERSION=\\\"4.5.0-beta.31+1a68848\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5123,7 +5123,7 @@ CODE_SIGN_STYLE = Automatic; COMPRESS_PNG_FILES = NO; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5149,14 +5149,14 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)"; LINK_WITH_STANDARD_LIBRARIES = YES; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; OTHER_CFLAGS = ( "-DBCTBX_LOG_DOMAIN=\\\"ios\\\"", "-DCHECK_VERSION_UPDATE=FALSE", "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"4.4.3\\\"", + "-DLINPHONE_SDK_VERSION=\\\"4.5.0-beta.31+1a68848\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5244,7 +5244,7 @@ CODE_SIGN_STYLE = Automatic; COMPRESS_PNG_FILES = NO; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5270,14 +5270,14 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)"; LINK_WITH_STANDARD_LIBRARIES = YES; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; OTHER_CFLAGS = ( "-DBCTBX_LOG_DOMAIN=\\\"ios\\\"", "-DCHECK_VERSION_UPDATE=FALSE", "-DENABLE_QRCODE=TRUE", "-DENABLE_SMS_INVITE=TRUE", "$(inherited)", - "-DLINPHONE_SDK_VERSION=\\\"4.4.3\\\"", + "-DLINPHONE_SDK_VERSION=\\\"4.5.0-beta.31+1a68848\\\"", ); OTHER_SWIFT_FLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone; @@ -5317,7 +5317,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5329,7 +5329,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; INFOPLIST_FILE = linphoneExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -5359,7 +5359,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5370,7 +5370,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; INFOPLIST_FILE = linphoneExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -5400,7 +5400,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5411,7 +5411,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; INFOPLIST_FILE = linphoneExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -5442,7 +5442,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5453,7 +5453,7 @@ GCC_WARN_UNUSED_FUNCTION = YES; INFOPLIST_FILE = linphoneExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.linphoneExtension; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -5536,7 +5536,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5553,7 +5553,7 @@ INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; @@ -5592,7 +5592,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5605,7 +5605,7 @@ INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; @@ -5644,7 +5644,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5657,7 +5657,7 @@ INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; @@ -5696,7 +5696,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5709,7 +5709,7 @@ INFOPLIST_FILE = "$(SRCROOT)/msgNotificationService/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; @@ -5747,7 +5747,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5764,7 +5764,7 @@ INFOPLIST_FILE = msgNotificationContent/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; @@ -5802,7 +5802,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5815,7 +5815,7 @@ INFOPLIST_FILE = msgNotificationContent/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; @@ -5853,7 +5853,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5866,7 +5866,7 @@ INFOPLIST_FILE = msgNotificationContent/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; @@ -5904,7 +5904,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = Z2V957B3D6; ENABLE_BITCODE = NO; @@ -5917,7 +5917,7 @@ INFOPLIST_FILE = msgNotificationContent/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 4.3.2; + MARKETING_VERSION = 4.4.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited)"; From bf33513300ce8fb188e1a8411b59469b935461f0 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Fri, 15 Jan 2021 18:27:54 +0100 Subject: [PATCH 10/46] fix not found display name of message --- msgNotificationService/NotificationService.swift | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/msgNotificationService/NotificationService.swift b/msgNotificationService/NotificationService.swift index 0bcf505a3..6650360b2 100644 --- a/msgNotificationService/NotificationService.swift +++ b/msgNotificationService/NotificationService.swift @@ -220,13 +220,17 @@ class NotificationService: UNNotificationServiceExtension { return nil } - if let displayName = addressBook?[sipAddr] as? String { - NotificationService.log.message(message: "display name for \(sipAddr): \(displayName)") - return displayName - } else { - NotificationService.log.message(message: "display name for \(sipAddr) not found in userDefaults") - return nil + if let simpleAddr = lc?.interpretUrl(url: sipAddr) { + simpleAddr.clean() + let nomalSipaddr = simpleAddr.asString() + if let displayName = addressBook?[nomalSipaddr] as? String { + NotificationService.log.message(message: "display name for \(sipAddr): \(displayName)") + return displayName + } } + + NotificationService.log.message(message: "display name for \(sipAddr) not found in userDefaults") + return nil } return nil } From b62fe18ff36dae1381622eec199e60e8c0765825 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Wed, 20 Jan 2021 14:21:37 +0100 Subject: [PATCH 11/46] Revert "fix workaround for incoming call" This reverts commit 244d580bf8a145febcda51ea2efb55a9597e81f3. --- Classes/ProviderDelegate.swift | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Classes/ProviderDelegate.swift b/Classes/ProviderDelegate.swift index 482df084b..1b7fdd467 100644 --- a/Classes/ProviderDelegate.swift +++ b/Classes/ProviderDelegate.swift @@ -174,19 +174,15 @@ extension ProviderDelegate: CXProviderDelegate { Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: answer call with call-id: \(String(describing: callId)) and UUID: \(uuid.description).") let call = CallManager.instance().callByCallId(callId: callId) - - // workaround - CallManager.configAudioSession(audioSession: AVAudioSession.sharedInstance()) if (call == nil || call?.state != Call.State.IncomingReceived) { // The application is not yet registered or the call is not yet received, mark the call as accepted. The audio session must be configured here. - + CallManager.configAudioSession(audioSession: AVAudioSession.sharedInstance()) callInfo?.accepted = true callInfos.updateValue(callInfo!, forKey: uuid) CallManager.instance().providerDelegate.endCallNotExist(uuid: uuid, timeout: .now() + 10) } else { CallManager.instance().acceptCall(call: call!, hasVideo: call!.params?.videoEnabled ?? false) } - Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: answer call finished.") action.fulfill() } From 5a303829914dbbaf4f7cae470daf22948ce318b1 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Fri, 29 Jan 2021 17:32:14 +0100 Subject: [PATCH 12/46] rewrite log service --- msgNotification/Utils.swift | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/msgNotification/Utils.swift b/msgNotification/Utils.swift index d032440b4..4de533c19 100644 --- a/msgNotification/Utils.swift +++ b/msgNotification/Utils.swift @@ -33,13 +33,11 @@ class LinphoneLoggingServiceManager: LoggingServiceDelegate { let debugLevel = config.getInt(section: "app", key: "debugenable_preference", defaultValue: LogLevel.Debug.rawValue) let debugEnabled = (debugLevel >= LogLevel.Debug.rawValue && debugLevel < LogLevel.Error.rawValue) - if (debugEnabled) { - Factory.Instance.logCollectionPath = Factory.Instance.getDownloadDir(context: UnsafeMutablePointer(mutating: (APP_GROUP_ID as NSString).utf8String)) - Factory.Instance.enableLogCollection(state: LogCollectionState.Enabled) - log.domain = domain - log.logLevel = LogLevel(rawValue: debugLevel) - log.addDelegate(delegate: self) - } + Factory.Instance.logCollectionPath = Factory.Instance.getDownloadDir(context: UnsafeMutablePointer(mutating: (APP_GROUP_ID as NSString).utf8String)) + Factory.Instance.enableLogCollection(state: debugEnabled ? LogCollectionState.Enabled : LogCollectionState.Disabled) + log.domain = domain + log.logLevel = debugLevel==0 ? LogLevel.Fatal : LogLevel(rawValue: debugLevel) + log.addDelegate(delegate: self) } else { throw LinphoneError.loggingServiceUninitialized } From 9dd1e92821d78213d5052afb148ed7d45bbe7dff Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 8 Feb 2021 14:41:10 +0100 Subject: [PATCH 13/46] fix chat cell not work --- Classes/LinphoneUI/UIChatBubblePhotoCell.m | 1 + Classes/LinphoneUI/UIChatBubbleTextCell.m | 1 + 2 files changed, 2 insertions(+) diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m index 33ed9c745..5e62338f4 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m @@ -52,6 +52,7 @@ chatTableView = VIEW(ChatConversationView).tableController; videoDefaultSize = CGSizeMake(320, 240); assetIsLoaded = FALSE; + self.contentView.userInteractionEnabled = NO; } return self; } diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.m b/Classes/LinphoneUI/UIChatBubbleTextCell.m index 3de3f1168..7e20bc112 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.m +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m @@ -57,6 +57,7 @@ //[_imdmLabel addGestureRecognizer:resendRecognizer2]; //_imdmLabel.userInteractionEnabled = YES; + self.contentView.userInteractionEnabled = NO; return self; } From faa4e15462cd51a268c62d5a0bbc781d802a85f0 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 15 Feb 2021 11:24:53 +0100 Subject: [PATCH 14/46] add checkbox for accept terms of use and privacy policy --- Classes/AssistantView.h | 7 +- Classes/AssistantView.m | 35 ++++- Classes/Base.lproj/AssistantViewScreens.xib | 151 ++++++++++++++------ Resources/en.lproj/Localizable.strings | Bin 52084 -> 52510 bytes Resources/fr.lproj/Localizable.strings | Bin 55322 -> 55870 bytes 5 files changed, 149 insertions(+), 44 deletions(-) diff --git a/Classes/AssistantView.h b/Classes/AssistantView.h index 80423e53c..975103249 100644 --- a/Classes/AssistantView.h +++ b/Classes/AssistantView.h @@ -22,7 +22,7 @@ #import "TPKeyboardAvoidingScrollView.h" #import "PhoneMainView.h" -@interface AssistantView : UIViewController { +@interface AssistantView : UIViewController { @private LinphoneAccountCreator *account_creator; @@ -33,6 +33,7 @@ size_t number_of_configs_before; BOOL mustRestoreView; long phone_number_length; + BOOL acceptTerms; } @property(nonatomic) UICompositeViewDescription *outgoingView; @@ -70,6 +71,10 @@ @property (weak, nonatomic) IBOutlet UIButton *downloadButton; @property (weak, nonatomic) IBOutlet UITextField *urlLabel; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *createAccountNextButtonPositionConstraint; +@property (weak, nonatomic) IBOutlet UIButton *acceptButton; +- (IBAction)onAcceptTermsClick:(id)sender; +@property (weak, nonatomic) IBOutlet UITextView *acceptText; + + (NSString *)StringForXMLRPCError:(const char *)err; + (NSString *)errorForLinphoneAccountCreatorPhoneNumberStatus:(LinphoneAccountCreatorPhoneNumberStatus)status; diff --git a/Classes/AssistantView.m b/Classes/AssistantView.m index f00e7be8f..30bde7f05 100644 --- a/Classes/AssistantView.m +++ b/Classes/AssistantView.m @@ -62,6 +62,7 @@ typedef enum _ViewElement { historyViews = [[NSMutableArray alloc] init]; currentView = nil; mustRestoreView = NO; + acceptTerms = NO; } return self; } @@ -119,12 +120,28 @@ static UICompositeViewDescription *compositeDescription = nil; _outgoingView = DialerView.compositeViewDescription; _qrCodeButton.hidden = !ENABLE_QRCODE; [self resetLiblinphone:FALSE]; + [self enableWelcomeViewButtons]; + NSString *message = NSLocalizedString(@"I accept Belledonne Communications’ terms of use and privacy policy", nil); + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:message]; + [attributedString addAttribute:NSLinkAttributeName + value:@"https://www.linphone.org/general-terms" + range:[[attributedString string] rangeOfString:NSLocalizedString(@"terms of use", nil)]]; + [attributedString addAttribute:NSLinkAttributeName + value:@"https://www.linphone.org/privacy-policy" + range:[[attributedString string] rangeOfString:NSLocalizedString(@"privacy policy", nil)]]; + + NSDictionary *linkAttributes = @{NSForegroundColorAttributeName : [UIColor redColor], + NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle)}; + + _acceptText.linkTextAttributes = linkAttributes; + _acceptText.attributedText = attributedString; + _acceptText.editable = NO; + _acceptText.delegate = self; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [NSNotificationCenter.defaultCenter removeObserver:self]; - } - (void)fitContent { @@ -143,6 +160,11 @@ static UICompositeViewDescription *compositeDescription = nil; [self fitContent]; } +#pragma mark - UITextViewDelegate +- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction { + return [[UIApplication sharedApplication] openURL:URL]; +} + #pragma mark - Utils - (void)resetLiblinphone:(BOOL)core { @@ -269,6 +291,12 @@ static UICompositeViewDescription *compositeDescription = nil; return NSLocalizedString(@"Unknown error, please try again later.", nil); } +- (void)enableWelcomeViewButtons { + UIImage *image = acceptTerms ? [UIImage imageNamed:@"checkbox_checked.png"] : [UIImage imageNamed:@"checkbox_unchecked.png"]; + [_acceptButton setImage:image forState:UIControlStateNormal]; + _gotoRemoteProvisioningButton.enabled = _gotoLinphoneLoginButton.enabled = _gotoCreateAccountButton.enabled = _gotoLoginButton.enabled = acceptTerms; +} + + (NSString *)errorForLinphoneAccountCreatorPhoneNumberStatus:(LinphoneAccountCreatorPhoneNumberStatus)status { switch (status) { case LinphoneAccountCreatorPhoneNumberStatusTooShort: /**< Phone number too short */ @@ -1654,6 +1682,11 @@ void assistant_is_account_linked(LinphoneAccountCreator *creator, LinphoneAccoun } } +- (IBAction)onAcceptTermsClick:(id)sender { + acceptTerms = !acceptTerms; + [self enableWelcomeViewButtons]; +} + #pragma mark - select country delegate - (void)didSelectCountry:(NSDictionary *)country { diff --git a/Classes/Base.lproj/AssistantViewScreens.xib b/Classes/Base.lproj/AssistantViewScreens.xib index 6dd202962..abf045536 100644 --- a/Classes/Base.lproj/AssistantViewScreens.xib +++ b/Classes/Base.lproj/AssistantViewScreens.xib @@ -1,14 +1,17 @@ - + - + + + + @@ -42,7 +45,7 @@ + + + + + + + + + + + + + + + + + + + + + + - + @@ -221,7 +272,7 @@ - + @@ -293,7 +344,7 @@ - + @@ -344,7 +395,7 @@ - + @@ -373,7 +424,7 @@ - + @@ -402,7 +453,7 @@ - + @@ -458,7 +509,7 @@ - + @@ -586,7 +637,7 @@ - + @@ -600,7 +651,7 @@ - + @@ -692,7 +743,7 @@ Once it is done, come back here and click on the button. - + @@ -756,7 +807,7 @@ Once it is done, come back here and click on the button. - + @@ -765,7 +816,7 @@ Once it is done, come back here and click on the button. - + @@ -776,7 +827,7 @@ Once it is done, come back here and click on the button. - + @@ -824,7 +875,7 @@ Once it is done, come back here and click on the button. - + @@ -857,7 +908,7 @@ Once it is done, come back here and click on the button. - + @@ -882,7 +933,7 @@ Once it is done, come back here and click on the button. - + @@ -928,7 +979,7 @@ Once it is done, come back here and click on the button. - + @@ -1033,7 +1084,7 @@ Once it is done, come back here and click on the button. - + @@ -1066,7 +1117,7 @@ Once it is done, come back here and click on the button. - + @@ -1099,7 +1150,7 @@ Once it is done, come back here and click on the button. - + @@ -1153,7 +1204,7 @@ Once it is done, come back here and click on the button. - + @@ -1184,7 +1235,7 @@ Once it is done, come back here and click on the button. - + @@ -1288,7 +1339,7 @@ Once it is done, come back here and click on the button. - + @@ -1319,7 +1370,7 @@ Once it is done, come back here and click on the button. - + @@ -1339,7 +1390,7 @@ Once it is done, come back here and click on the button. - + @@ -1412,7 +1463,7 @@ Once it is done, come back here and click on the button. - + @@ -1435,7 +1486,7 @@ Once it is done, come back here and click on the button. - + @@ -1458,7 +1509,7 @@ Once it is done, come back here and click on the button. - + @@ -1480,7 +1531,7 @@ Once it is done, come back here and click on the button. - + @@ -1539,6 +1590,22 @@ Once it is done, come back here and click on the button. + + + + + + + + + + + + + + + + diff --git a/Resources/en.lproj/Localizable.strings b/Resources/en.lproj/Localizable.strings index 13f93ebf210ce938c5c6eb030aef5978858edc7e..6b66641c8bcad2086dc1f5e9886d8a3c4624e8b5 100644 GIT binary patch delta 439 zcmew|jd|WI<_#sM>y;Qh859^28Ipl8m7##41jus&vT}ehl_7;8pCJzjQ-Pw+Kz1$= zmIC>iK-GyrwV7b~Vg^YC1t2e#p$I5n3{;!XkOm}6fozb@M4*loAYA~I&txbA+g1tY z=hp+R0U1`wpac}K1!DR-s2;;bNDi`Q-~u`am%k7W#;pQk0v3P6Y|%yXHy&j`RUo4P DYI9I{ delta 9 QcmbO?i}}km<_#sM0T%HDxBvhE diff --git a/Resources/fr.lproj/Localizable.strings b/Resources/fr.lproj/Localizable.strings index 111352b45eb949f63b153cdbbb25cf35acae2609..b44a1e8489b205a0bc8e89cdb22e2eb751373daa 100644 GIT binary patch delta 468 zcmb7AI|{;35F8LhY^@Z;XYc}67Gj~P^cErS2>~_H7$H>-kl2~VoxGg1!0IP3UjxN38>Hoi?hcTK|_ifFW{!ou%$^SZ#Xi)tP!n#0t SH?8T1i+ni9@0Ggsr?dxbgj%5h delta 9 QcmdnDg?ZKn<_%Ie02ZJGWdHyG From 71a9b8ef411b36cf2286f1cb3c5ffff87b0472cd Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 15 Feb 2021 11:26:32 +0100 Subject: [PATCH 15/46] fix crash when parse recording file failed, may changed phone language --- Classes/RecordingsListTableView.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Classes/RecordingsListTableView.m b/Classes/RecordingsListTableView.m index 435b3ab75..2bb4e3273 100644 --- a/Classes/RecordingsListTableView.m +++ b/Classes/RecordingsListTableView.m @@ -84,6 +84,10 @@ NSArray *parsedName = [LinphoneUtils parseRecordingName:file]; NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; [dateFormat setDateFormat:@"EEEE, MMM d, yyyy"]; + if ([parsedName count] < 2) { + LOGW(@"Can not parse this recoding file: %@", file); + continue; + } NSString *dayPretty = [dateFormat stringFromDate:[parsedName objectAtIndex:1]]; NSMutableArray *recOfDay = [recordings objectForKey:dayPretty]; if (recOfDay) { From dc60be7eda6bec5098c6dfec8228df33fbb96d6f Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 15 Feb 2021 20:20:05 +0100 Subject: [PATCH 16/46] core is stopped when call is released --- Classes/CallManager.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift index dae7018aa..420c7fe1e 100644 --- a/Classes/CallManager.swift +++ b/Classes/CallManager.swift @@ -356,9 +356,6 @@ import AVFoundation } catch { Log.directLog(BCTBX_LOG_ERROR, text: "Failed to terminate call failed because \(error)") } - if (UIApplication.shared.applicationState == .background) { - CoreManager.instance().stopLinphoneCore() - } } @objc func markCallAsDeclined(callId: String) { From c67a3fbd99a386786e05371ec23f279f6ed35c53 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Tue, 16 Feb 2021 11:32:41 +0100 Subject: [PATCH 17/46] fix assistantview for dark mode --- Classes/AssistantView.m | 2 +- Classes/Base.lproj/AssistantViewScreens.xib | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Classes/AssistantView.m b/Classes/AssistantView.m index 30bde7f05..9d98a1458 100644 --- a/Classes/AssistantView.m +++ b/Classes/AssistantView.m @@ -122,7 +122,7 @@ static UICompositeViewDescription *compositeDescription = nil; [self resetLiblinphone:FALSE]; [self enableWelcomeViewButtons]; NSString *message = NSLocalizedString(@"I accept Belledonne Communications’ terms of use and privacy policy", nil); - NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:message]; + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:message attributes:@{NSForegroundColorAttributeName : [UIColor systemGrayColor]}]; [attributedString addAttribute:NSLinkAttributeName value:@"https://www.linphone.org/general-terms" range:[[attributedString string] rangeOfString:NSLocalizedString(@"terms of use", nil)]]; diff --git a/Classes/Base.lproj/AssistantViewScreens.xib b/Classes/Base.lproj/AssistantViewScreens.xib index abf045536..847755544 100644 --- a/Classes/Base.lproj/AssistantViewScreens.xib +++ b/Classes/Base.lproj/AssistantViewScreens.xib @@ -71,14 +71,14 @@ - + - + @@ -1595,9 +1595,6 @@ Once it is done, come back here and click on the button. - - - @@ -1607,5 +1604,8 @@ Once it is done, come back here and click on the button. + + + From 5cfa7933a83ac60a4df44c6a4681c2a8da545ada Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Wed, 17 Feb 2021 09:31:06 +0100 Subject: [PATCH 18/46] avoid send image with empty text to avoid crash --- Classes/Utils/FileTransferDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m index 944c4cb4b..4b1eaad53 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -277,7 +277,7 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m linphone_content_set_size(content, _data.length); _message = linphone_chat_room_create_file_transfer_message(chatRoom, content); BOOL isOneToOneChat = linphone_chat_room_get_capabilities(chatRoom) & LinphoneChatRoomCapabilitiesOneToOne; - if (!isOneToOneChat && ![_text isEqualToString:@""]) + if (!isOneToOneChat && (_text!=nil && ![_text isEqualToString:@""])) linphone_chat_message_add_text_content(_message, [_text UTF8String]); linphone_content_unref(content); From 13acb88907f27c8b2988562a0710babc81cdb983 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Fri, 19 Feb 2021 19:18:01 +0100 Subject: [PATCH 19/46] ipad: fix crash for restart app when current view is chat and receive image in the same time --- Classes/ChatConversationView.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Classes/ChatConversationView.m b/Classes/ChatConversationView.m index f32ba2d11..5ab887314 100644 --- a/Classes/ChatConversationView.m +++ b/Classes/ChatConversationView.m @@ -378,6 +378,10 @@ static UICompositeViewDescription *compositeDescription = nil; } [self configureForRoom:self.editing]; if (_chatRoom && _markAsRead) { + if (IPAD) { + [VIEW(ChatsListView).tableController loadData]; + } + [ChatConversationView markAsRead:_chatRoom]; } _markAsRead = TRUE; From 629397d5f6ab3a96fb589873f06eaef7b8de550a Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 22 Feb 2021 11:45:49 +0100 Subject: [PATCH 20/46] fix crash when one auth info est nil --- Classes/LinphoneManager.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m index e2b50a393..9bfd5520a 100644 --- a/Classes/LinphoneManager.m +++ b/Classes/LinphoneManager.m @@ -720,8 +720,8 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut // let the wizard handle its own errors if ([PhoneMainView.instance currentView] != AssistantView.compositeViewDescription) { const char * realmC = linphone_auth_info_get_realm(auth_info); - const char * usernameC = linphone_auth_info_get_username(auth_info); - const char * domainC = linphone_auth_info_get_domain(auth_info); + const char * usernameC = linphone_auth_info_get_username(auth_info) ? : ""; + const char * domainC = linphone_auth_info_get_domain(auth_info) ? : ""; static UIAlertController *alertView = nil; // avoid having multiple popups From 007bea2eaf86c155c8a0d9fe34f1ce7c1a360400 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 22 Feb 2021 14:12:35 +0100 Subject: [PATCH 21/46] fix crash accept video when call is already ended --- Classes/LinphoneAppDelegate.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index f4dc0991a..8837b84c3 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -688,6 +688,7 @@ [PhoneMainView.instance changeCurrentView:ChatsListView.compositeViewDescription]; } } else if ([response.notification.request.content.categoryIdentifier isEqual:@"video_request"]) { + if (!call) return; [PhoneMainView.instance changeCurrentView:CallView.compositeViewDescription]; NSTimer *videoDismissTimer = nil; UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:response.notification.request.content.body From 15ae79d561ad7bfa11dbe042ac25bf5e0b09be96 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 22 Feb 2021 15:35:51 +0100 Subject: [PATCH 22/46] fix crash restart app when current view is ChatConversationInfoView --- Classes/ChatConversationInfoView.h | 2 ++ Classes/ChatConversationInfoView.m | 4 ++++ Classes/LinphoneAppDelegate.m | 3 +++ 3 files changed, 9 insertions(+) diff --git a/Classes/ChatConversationInfoView.h b/Classes/ChatConversationInfoView.h index a3ad69c2a..2f2632d47 100644 --- a/Classes/ChatConversationInfoView.h +++ b/Classes/ChatConversationInfoView.h @@ -49,4 +49,6 @@ - (IBAction)onBackClick:(id)sender; - (IBAction)onQuitClick:(id)sender; +- (void)removeCallbacks; + @end diff --git a/Classes/ChatConversationInfoView.m b/Classes/ChatConversationInfoView.m index d7e431e3f..bbd88686e 100644 --- a/Classes/ChatConversationInfoView.m +++ b/Classes/ChatConversationInfoView.m @@ -132,6 +132,10 @@ static UICompositeViewDescription *compositeDescription = nil; } - (void)viewWillDisappear:(BOOL)animated { + [self removeCallbacks]; +} + +- (void)removeCallbacks { if (!_room || !_chatRoomCbs) return; diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index 8837b84c3..b973520be 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -66,6 +66,9 @@ if (PhoneMainView.instance.currentView == ChatConversationView.compositeViewDescription) { ChatConversationView *view = VIEW(ChatConversationView); [view removeCallBacks]; + } else if (PhoneMainView.instance.currentView == ChatConversationInfoView.compositeViewDescription) { + ChatConversationInfoView *view = VIEW(ChatConversationInfoView); + [view removeCallbacks]; } [CoreManager.instance stopLinphoneCore]; } From bf3d9129e05cb732f094d6f9d4cf125424dbd3d9 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Tue, 23 Feb 2021 15:22:33 +0100 Subject: [PATCH 23/46] fix show app's badge --- msgNotificationService/NotificationService.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/msgNotificationService/NotificationService.swift b/msgNotificationService/NotificationService.swift index 6650360b2..cbd4ff5ce 100644 --- a/msgNotificationService/NotificationService.swift +++ b/msgNotificationService/NotificationService.swift @@ -89,7 +89,9 @@ class NotificationService: UNNotificationServiceExtension { if let message = message { let msgData = parseMessage(message: message) - if !message.isUsingUserDefaults, let badge = updateBadge() as NSNumber? { + // Extension only upates app's badge when main shared core is Off = extension's core is On. + // Otherwise, the app will update the badge. + if lc?.globalState == GlobalState.On, let badge = updateBadge() as NSNumber? { bestAttemptContent.badge = badge } From 8164a2d94465909144a44468b0dc5915787f458d Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Tue, 23 Feb 2021 15:42:16 +0100 Subject: [PATCH 24/46] fix send message --- Classes/ChatConversationView.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/ChatConversationView.m b/Classes/ChatConversationView.m index 5ab887314..bf1d95b12 100644 --- a/Classes/ChatConversationView.m +++ b/Classes/ChatConversationView.m @@ -253,7 +253,7 @@ static UICompositeViewDescription *compositeDescription = nil; linphone_chat_room_cbs_set_participant_removed(_chatRoomCbs, on_chat_room_participant_removed); linphone_chat_room_cbs_set_participant_admin_status_changed(_chatRoomCbs, on_chat_room_participant_admin_status_changed); linphone_chat_room_cbs_set_chat_message_received(_chatRoomCbs, on_chat_room_chat_message_received); - linphone_chat_room_cbs_set_chat_message_sent(_chatRoomCbs, on_chat_room_chat_message_sent); + linphone_chat_room_cbs_set_chat_message_sending(_chatRoomCbs, on_chat_room_chat_message_sending); linphone_chat_room_cbs_set_is_composing_received(_chatRoomCbs, on_chat_room_is_composing_received); linphone_chat_room_cbs_set_conference_joined(_chatRoomCbs, on_chat_room_conference_joined); linphone_chat_room_cbs_set_conference_left(_chatRoomCbs, on_chat_room_conference_left); @@ -1114,7 +1114,7 @@ void on_chat_room_chat_message_received(LinphoneChatRoom *cr, const LinphoneEven [view.tableController scrollToLastUnread:TRUE]; } -void on_chat_room_chat_message_sent(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { +void on_chat_room_chat_message_sending(LinphoneChatRoom *cr, const LinphoneEventLog *event_log) { ChatConversationView *view = (__bridge ChatConversationView *)linphone_chat_room_cbs_get_user_data(linphone_chat_room_get_current_callbacks(cr)); [view.tableController addEventEntry:(LinphoneEventLog *)event_log]; [view.tableController scrollToBottom:true]; From f5b683e9d70d73f282ab1b9b2309ed00ac51a157 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Thu, 25 Feb 2021 09:36:14 +0100 Subject: [PATCH 25/46] rework storage and display files --- Classes/ChatConversationTableView.h | 4 +- Classes/ChatConversationTableView.m | 6 +- Classes/ChatConversationView.h | 4 + Classes/ChatConversationView.m | 222 +++++----------- Classes/LinphoneUI/UIChatBubblePhotoCell.m | 289 +++++++++------------ Classes/LinphoneUI/UIChatBubbleTextCell.m | 194 +++++++------- Classes/Utils/FileTransferDelegate.h | 4 +- Classes/Utils/FileTransferDelegate.m | 270 +++++-------------- 8 files changed, 352 insertions(+), 641 deletions(-) diff --git a/Classes/ChatConversationTableView.h b/Classes/ChatConversationTableView.h index a87f35f14..017889f00 100644 --- a/Classes/ChatConversationTableView.h +++ b/Classes/ChatConversationTableView.h @@ -27,8 +27,8 @@ @protocol ChatConversationDelegate -- (BOOL)startImageUpload:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality; -- (BOOL)startFileUpload:(NSData *)data assetId:(NSString *)phAssetId; +- (BOOL)resendFile:(NSData *)data withName:(NSString *)name type:(NSString *)type key:(NSString *)key message:(NSString *)message; +- (BOOL)startImageUpload:(UIImage *)image withQuality:(float)quality andMessage:(NSString *)message; - (BOOL)startFileUpload:(NSData *)data withName:(NSString *)name; - (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url; - (void)tableViewIsScrolling; diff --git a/Classes/ChatConversationTableView.m b/Classes/ChatConversationTableView.m index f72d9acc5..781fe0360 100644 --- a/Classes/ChatConversationTableView.m +++ b/Classes/ChatConversationTableView.m @@ -363,9 +363,9 @@ static const CGFloat MESSAGE_SPACING_PERCENTAGE = 1.f; - (void)removeSelectionUsing:(void (^)(NSIndexPath *))remover { [super removeSelectionUsing:^(NSIndexPath *indexPath) { LinphoneEventLog *event = [[eventList objectAtIndex:indexPath.row] pointerValue]; - // TODO: fix workaround - //linphone_event_log_delete_from_database(event); - linphone_chat_room_delete_message(_chatRoom, linphone_event_log_get_chat_message(event)); + if (linphone_event_log_get_chat_message(event)) { + linphone_chat_room_delete_message(_chatRoom, linphone_event_log_get_chat_message(event)); + } NSInteger index = indexPath.row + _currentIndex + (totalEventList.count - eventList.count); if (index < totalEventList.count) [totalEventList removeObjectAtIndex:index]; diff --git a/Classes/ChatConversationView.h b/Classes/ChatConversationView.h index 7f97ac7d1..0fc0a6a67 100644 --- a/Classes/ChatConversationView.h +++ b/Classes/ChatConversationView.h @@ -92,6 +92,10 @@ + (void)markAsRead:(LinphoneChatRoom *)chatRoom; + (void)autoDownload:(LinphoneChatMessage *)message; ++(NSString *)getKeyFromFileType:(NSString *)fileType fileName:(NSString *)name; ++ (NSURL *)getCacheFileUrl:(NSString *)name; ++ (void)writeFileInCache:(NSData *)data name:(NSString *)name; ++ (NSData *)getCacheFileData:(NSString *)name; - (void)configureForRoom:(BOOL)editing; - (IBAction)onBackClick:(id)event; diff --git a/Classes/ChatConversationView.m b/Classes/ChatConversationView.m index bf1d95b12..7afa6fcb0 100644 --- a/Classes/ChatConversationView.m +++ b/Classes/ChatConversationView.m @@ -337,33 +337,17 @@ static UICompositeViewDescription *compositeDescription = nil; //file shared from photo lib NSString *fileName = dict[@"url"]; [_messageField setText:dict[@"message"]]; - NSString *key = [[fileName componentsSeparatedByString:@"."] firstObject]; - NSMutableDictionary * assetDict = [LinphoneUtils photoAssetsDictionary]; - PHAsset *phasset = [assetDict objectForKey:key]; - if (!phasset) { - // for the images or videos not really in the photo album - [self confirmShare:[self nsDataRead] url:nil fileName:fileName assetId:nil]; - } else if ([fileName hasSuffix:@"JPG"] || [fileName hasSuffix:@"PNG"] || [fileName hasSuffix:@"jpg"] || [fileName hasSuffix:@"png"]) { - UIImage *image = [[UIImage alloc] initWithData:[self nsDataRead]]; - [self chooseImageQuality:image assetId:[phasset localIdentifier]]; - } else if ([fileName hasSuffix:@"MOV"] || [fileName hasSuffix:@"mov"]) { - [self confirmShare:[self nsDataRead] url:nil fileName:nil assetId:[phasset localIdentifier]]; - } else { - LOGE(@"Unable to parse file %@",fileName); - } - + [self confirmShare:[self nsDataRead] url:nil fileName:fileName]; [defaults removeObjectForKey:@"photoData"]; } else if (dictFile) { NSString *fileName = dictFile[@"url"]; [_messageField setText:dictFile[@"message"]]; - [self confirmShare:[self nsDataRead] url:nil fileName:fileName assetId:nil]; - + [self confirmShare:[self nsDataRead] url:nil fileName:fileName]; [defaults removeObjectForKey:@"icloudData"]; } else if (dictUrl) { NSString *url = dictUrl[@"url"]; [_messageField setText:dictUrl[@"message"]]; - [self confirmShare:nil url:url fileName:nil assetId:nil]; - + [self confirmShare:nil url:url fileName:nil]; [defaults removeObjectForKey:@"url"]; } } @@ -417,7 +401,7 @@ static UICompositeViewDescription *compositeDescription = nil; } } -- (BOOL)sendMessage:(NSString *)message withExterlBodyUrl:(NSURL *)externalUrl withInternalURL:(NSURL *)internalUrl { +- (BOOL)sendMessage:(NSString *)message withExterlBodyUrl:(NSURL *)externalUrl { if (_chatRoom == NULL) { LOGW(@"Cannot send message: No chatroom"); return FALSE; @@ -428,11 +412,6 @@ static UICompositeViewDescription *compositeDescription = nil; linphone_chat_message_set_external_body_url(msg, [[externalUrl absoluteString] UTF8String]); } - if (internalUrl) { - // internal url is saved in the appdata for display and later save - [LinphoneManager setValueInMessageAppData:[internalUrl absoluteString] forKey:@"localimage" inMessage:msg]; - } - // we must ref & unref message because in case of error, it will be destroy otherwise linphone_chat_message_send(msg); @@ -474,7 +453,7 @@ static UICompositeViewDescription *compositeDescription = nil; }); } -- (void)confirmShare:(NSData *)data url:(NSString *)url fileName:(NSString *)fileName assetId:(NSString *)phAssetId { +- (void)confirmShare:(NSData *)data url:(NSString *)url fileName:(NSString *)fileName { DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:@""]; dispatch_async(dispatch_get_main_queue(), ^{ [sheet addButtonWithTitle:NSLocalizedString(@"Send to this friend", nil) @@ -483,11 +462,10 @@ static UICompositeViewDescription *compositeDescription = nil; [self sendMessageInMessageField]; } if (url) - [self sendMessage:url withExterlBodyUrl:nil withInternalURL:nil]; - else if (fileName) - [self startFileUpload:data withName:fileName]; + [self sendMessage:url withExterlBodyUrl:nil]; else - [self startFileUpload:data assetId:phAssetId];}]; + [self startFileUpload:data withName:fileName]; + }]; [sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil]; [sheet showInView:PhoneMainView.instance.view]; @@ -568,7 +546,7 @@ static UICompositeViewDescription *compositeDescription = nil; } - (void)sendMessageInMessageField { - if ([self sendMessage:[_messageField text] withExterlBodyUrl:nil withInternalURL:nil]) { + if ([self sendMessage:[_messageField text] withExterlBodyUrl:nil]) { scrollOnGrowingEnabled = FALSE; [_messageField setText:@""]; scrollOnGrowingEnabled = TRUE; @@ -651,15 +629,15 @@ static UICompositeViewDescription *compositeDescription = nil; if ([_imagesArray count] > 0) { int i = 0; for (i = 0; i < [_imagesArray count] - 1; ++i) { - [self startImageUpload:[_imagesArray objectAtIndex:i] assetId:[_assetIdsArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue]; + [self startImageUpload:[_imagesArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:NULL]; } if (isOneToOne) { - [self startImageUpload:[_imagesArray objectAtIndex:i] assetId:[_assetIdsArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue]; + [self startImageUpload:[_imagesArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:NULL]; if (![[self.messageField text] isEqualToString:@""]) { - [self sendMessage:[_messageField text] withExterlBodyUrl:nil withInternalURL:nil]; + [self sendMessage:[_messageField text] withExterlBodyUrl:nil]; } } else { - [self startImageUpload:[_imagesArray objectAtIndex:i] assetId:[_assetIdsArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:[self.messageField text]]; + [self startImageUpload:[_imagesArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:[self.messageField text]]; } [self clearMessageView]; @@ -761,28 +739,15 @@ static UICompositeViewDescription *compositeDescription = nil; #pragma mark ChatRoomDelegate -- (BOOL)startImageUpload:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality { +- (BOOL)startImageUpload:(UIImage *)image withQuality:(float)quality andMessage:(NSString *)message { FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init]; - [fileTransfer upload:image withassetId:phAssetId forChatRoom:_chatRoom withQuality:quality]; + if (message) + [fileTransfer setText:message]; + [fileTransfer uploadImage:image forChatRoom:_chatRoom withQuality:quality]; [_tableController scrollToBottom:true]; return TRUE; } -- (BOOL)startImageUpload:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality andMessage:(NSString *)message { - FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init]; - [fileTransfer setText:message]; - [fileTransfer upload:image withassetId:phAssetId forChatRoom:_chatRoom withQuality:quality]; - [_tableController scrollToBottom:true]; - return TRUE; -} - -- (BOOL)startFileUpload:(NSData *)data assetId:(NSString *)phAssetId { - FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init]; - [fileTransfer uploadVideo:data withassetId:phAssetId forChatRoom:_chatRoom]; - [_tableController scrollToBottom:true]; - return TRUE; -} - - (BOOL)startFileUpload:(NSData *)data withName:(NSString *)name { FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init]; [fileTransfer uploadFile:data forChatRoom:_chatRoom withName:name]; @@ -790,8 +755,17 @@ static UICompositeViewDescription *compositeDescription = nil; return TRUE; } +- (BOOL)resendFile: (NSData *)data withName:(NSString *)name type:(NSString *)type key:(NSString *)key message:(NSString *)message { + FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init]; + if (message) + [fileTransfer setText:message]; + [fileTransfer uploadData:data forChatRoom:_chatRoom type:type subtype:type name:name key:key]; + [_tableController scrollToBottom:true]; + return TRUE; +} + - (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url { - [self sendMessage:message withExterlBodyUrl:[NSURL URLWithString:url] withInternalURL:nil]; + [self sendMessage:message withExterlBodyUrl:[NSURL URLWithString:url]]; } #pragma mark ImagePickerDelegate @@ -1179,6 +1153,23 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog [PhoneMainView.instance fullScreen:NO]; } ++ (NSData *)getCacheFileData: (NSString *)name { + NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; + return [NSData dataWithContentsOfFile:filePath]; +} + ++ (NSURL *)getCacheFileUrl: (NSString *)name { + NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; + return [NSURL fileURLWithPath:filePath]; +} + ++ (void)writeFileInCache:(NSData *)data name:(NSString *)name { + NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; + [[NSFileManager defaultManager] createFileAtPath:filePath + contents:data + attributes:nil]; +} + - (NSURL *)getICloudFileUrl:(NSString *)name { if (@available(iOS 11.0, *)) { return [NSURL fileURLWithPath:[LinphoneManager documentFile:name]]; @@ -1324,118 +1315,25 @@ void on_chat_room_conference_alert(LinphoneChatRoom *cr, const LinphoneEventLog [PhoneMainView.instance presentViewController:errView animated:YES completion:nil]; } ++ (NSString *)getKeyFromFileType:(NSString *)fileType fileName:(NSString *)name{ + if ([fileType isEqualToString:@"video"]) { + return @"localvideo"; + } else if ([fileType isEqualToString:@"image"] || [name hasSuffix:@"JPG"] || [name hasSuffix:@"PNG"] || [name hasSuffix:@"jpg"] || [name hasSuffix:@"png"]) { + return @"localimage"; + } + return @"localfile"; +} + + (void)autoDownload:(LinphoneChatMessage *)message { ChatConversationView *view = VIEW(ChatConversationView); - //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)]; - // get download path - NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; - NSFileManager *fileManager = [NSFileManager defaultManager]; - if ([fileManager fileExistsAtPath:filePath]) { - NSData* data = [NSData dataWithContentsOfFile:filePath]; - NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)]; + 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 = [ChatConversationView getKeyFromFileType:fileType fileName:name]; - // 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) { - [view showFileDownloadError]; - return; - } - __block NSString *createdAssetId = nil; - [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{ - createdAssetId = [PHAssetCreationRequest creationRequestForAssetFromImage:image].placeholderForCreatedAsset.localIdentifier; - } error:nil]; - if (createdAssetId != nil) { - LOGI(@"Image saved to [%@]", createdAssetId); - [LinphoneManager setValueInMessageAppData:createdAssetId - forKey:@"localimage" - inMessage:message]; - dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view]; - }); - } else { - LOGE(@"Cannot save image data downloaded"); - [LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:message]; - dispatch_async(dispatch_get_main_queue(), ^{ - 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 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 NSString *createdAssetId = nil; - [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{ - createdAssetId = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:filePath]].placeholderForCreatedAsset.localIdentifier; - } error:nil]; - if (createdAssetId != nil) { - LOGI(@"video saved to [%@]", createdAssetId); - [LinphoneManager setValueInMessageAppData:createdAssetId - forKey:@"localvideo" - inMessage:message]; - dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view]; - }); - } else { - LOGE(@"Cannot save video data downloaded"); - [LinphoneManager setValueInMessageAppData:nil forKey:@"localvideo" inMessage:message]; - dispatch_async(dispatch_get_main_queue(), ^{ - 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]; - }); - } - } - }; - - // 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]]) { - [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message]; - dispatch_async(dispatch_get_main_queue(), ^{ - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view];}); - } else { - LOGE(@"[Auto download error] can not save the file %@", name); - } - } - } + [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message]; + dispatch_async(dispatch_get_main_queue(), ^{ + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneMessageReceived object:view];}); } -(void) documentMenu:(UIDocumentMenuViewController *)documentMenu didPickDocumentPicker:(UIDocumentPickerViewController *)documentPicker { diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m index 5e62338f4..11f23748f 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m @@ -154,172 +154,105 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; LOGW(@"Cannot update message room cell: NULL message"); return; } - [super update]; + [super update]; const char *url = linphone_chat_message_get_external_body_url(self.message); BOOL is_external = (url && (strstr(url, "http") == url)) || linphone_chat_message_get_file_transfer_information(self.message); NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:self.message]; - NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:self.message]; - NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message]; + NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:self.message]; + NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message]; assert(is_external || localImage || localVideo || localFile); - - + + LinphoneContent *fileContent = linphone_chat_message_get_file_transfer_information(self.message); + NSString *type = fileContent ? [NSString stringWithUTF8String:linphone_content_get_type(fileContent)] : @""; if (!(localImage || localVideo || localFile)) { - // If the file has been downloaded in background, save it in the folders and display it. - ChatConversationView *view = VIEW(ChatConversationView); - //TODO: migrate with "linphone_iphone_file_transfer_recv" - LinphoneContent *content = linphone_chat_message_get_file_transfer_information(self.message); - NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)]; - // get download path - NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; - NSFileManager *fileManager = [NSFileManager defaultManager]; - if ([fileManager fileExistsAtPath:filePath]) { - NSData* data = [NSData dataWithContentsOfFile:filePath]; - NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)]; - - // 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) { - [view 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:self.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:self.message]; - [self updateButtons:[LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:self.message] localVideo:localVideo localFile:localFile]; - } - }); - }]; - } 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:self.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:self.message]; - [self updateButtons:localImage localVideo:[LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:self.message] localFile:localFile]; - } - }); - }]; - } - }; - - // 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:self.message]; - [self updateButtons:localImage localVideo:localVideo localFile:[LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message]]; - }); - } else { - LOGE(@"[Auto download error] can not save the file %@", name); - } - } - } - } - - [self updateButtons:localImage localVideo:localVideo localFile:localFile]; -} - -- (void)updateButtons: (NSString *)localImage localVideo:(NSString *)localVideo localFile:(NSString *)localFile { - LinphoneContent *fileContent = linphone_chat_message_get_file_transfer_information(self.message); - NSString *type = fileContent ? [NSString stringWithUTF8String:linphone_content_get_type(fileContent)] : nil; - if (!(localImage || localVideo || localFile)) { - _playButton.hidden = YES; - _fileName.hidden = _fileView.hidden = _fileButton.hidden = YES; - _messageImageView.hidden = _cancelButton.hidden = (_ftd.message == nil); - _downloadButton.hidden = !_cancelButton.hidden; - _fileTransferProgress.hidden = NO; - } else { - // file is being saved on device - just wait for it - if ([localImage isEqualToString:@"saving..."] || [localVideo isEqualToString:@"saving..."] || [localFile isEqualToString:@"saving..."]) { - _cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = _playButton.hidden = _fileName.hidden = _fileView.hidden = _fileButton.hidden = YES; + _playButton.hidden = YES; + _fileName.hidden = _fileView.hidden = _fileButton.hidden = YES; + _messageImageView.hidden = _cancelButton.hidden = (_ftd.message == nil); + _downloadButton.hidden = !_cancelButton.hidden; + _fileTransferProgress.hidden = NO; + } else { + // file is being saved on device - just wait for it + if ([localImage isEqualToString:@"saving..."] || [localVideo isEqualToString:@"saving..."] || [localFile isEqualToString:@"saving..."]) { + _cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = _playButton.hidden = _fileName.hidden = _fileView.hidden = _fileButton.hidden = YES; } else { if(!assetIsLoaded) { assetIsLoaded = TRUE; if (localImage) { // we did not load the image yet, so start doing so if (_messageImageView.image == nil) { - [self loadFirstImage:localImage type:PHAssetMediaTypeImage]; - _imageGestureRecognizer.enabled = YES; + NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localImage]; + if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { + NSData* data = [NSData dataWithContentsOfFile:filePath]; + UIImage *image = [[UIImage alloc] initWithData:data]; + [self loadImageAsset:nil image:image]; + _imageGestureRecognizer.enabled = YES; + } else { + // support previous versions + [self loadFirstImage:localImage type:PHAssetMediaTypeImage]; + _imageGestureRecognizer.enabled = YES; + + dispatch_async(dispatch_get_main_queue(), ^ { + UIImage *image = [chatTableView.imagesInChatroom objectForKey:localImage]; + NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]]; + NSData *data = UIImageJPEGRepresentation(image, 1); + [ChatConversationView writeFileInCache:data name:name]; + [LinphoneManager setValueInMessageAppData:name forKey:@"localimage" inMessage:self.message]; + }); + } } - } - else if (localVideo) { + } else if (localVideo) { if (_messageImageView.image == nil) { - [self loadFirstImage:localVideo type:PHAssetMediaTypeVideo]; - _imageGestureRecognizer.enabled = NO; + NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localVideo]; + if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { + UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:localVideo]]; + [self loadImageAsset:nil image:image]; + _imageGestureRecognizer.enabled = NO; + } else { + // support previous versions + [self loadFirstImage:localVideo type:PHAssetMediaTypeVideo]; + _imageGestureRecognizer.enabled = NO; + + dispatch_async(dispatch_get_main_queue(), ^ { + PHFetchResult *assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] options:nil]; + if (![assets firstObject]) + return; + PHAsset *asset = [assets firstObject]; + if (asset.mediaType != PHAssetMediaTypeVideo) + return; + PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init]; + options.version = PHImageRequestOptionsVersionCurrent; + options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic; + + [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) { + AVURLAsset *urlAsset = (AVURLAsset *)asset; + NSData *data = [NSData dataWithContentsOfURL:urlAsset.URL]; + NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]]; + [ChatConversationView writeFileInCache:data name:name]; + [LinphoneManager setValueInMessageAppData:name forKey:@"localvideo" inMessage:self.message]; + + }]; + + }); + } + } + } else if (localFile) { + BOOL newVersion = FALSE; + if(![[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localFile]]) { + dispatch_async(dispatch_get_main_queue(), ^ { + NSURL *url = [VIEW(ChatConversationView) getICloudFileUrl:localFile]; + NSData *data = [NSData dataWithContentsOfURL:url]; + [ChatConversationView writeFileInCache:data name:localFile]; + }); + } else { + newVersion = TRUE; } - } - else if (localFile) { if ([type isEqualToString:@"video"]) { - UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[VIEW(ChatConversationView) getICloudFileUrl:localFile]]; + UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:newVersion? [ChatConversationView getCacheFileUrl:localFile] : [VIEW(ChatConversationView) getICloudFileUrl:localFile]]; [self loadImageAsset:nil image:image]; _imageGestureRecognizer.enabled = NO; } else if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) { - NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]]; + NSData *data = newVersion? [ChatConversationView getCacheFileData:localFile] : [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]]; UIImage *image = [[UIImage alloc] initWithData:data]; [self loadImageAsset:nil image:image]; _imageGestureRecognizer.enabled = YES; @@ -342,7 +275,7 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; _playButton.hidden = ![type isEqualToString:@"video"]; _fileName.hidden = _fileView.hidden = _fileButton.hidden = localFile ? NO : YES; } - } + } } - (void)loadFirstImage:(NSString *)key type:(PHAssetMediaType)type { @@ -391,11 +324,29 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; } - (IBAction)onPlayClick:(id)sender { + NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:self.message]; + NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message]; + if (localVideo && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localVideo]]) { + AVPlayer *player = [AVPlayer playerWithURL:[ChatConversationView getCacheFileUrl:localVideo]]; + [self playVideoByPlayer:player]; + return; + } else if (localFile && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localFile]]) { + AVPlayer *player = [AVPlayer playerWithURL:[ChatConversationView getCacheFileUrl:localFile]]; + [self playVideoByPlayer:player]; + return; + } + PHAsset *asset = [_messageImageView asset]; if (!asset) { NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message]; - AVPlayer *player = [AVPlayer playerWithURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]]; + NSURL *url = [VIEW(ChatConversationView) getICloudFileUrl:localFile]; + AVPlayer *player = [AVPlayer playerWithURL:url]; [self playVideoByPlayer:player]; + dispatch_async(dispatch_get_main_queue(), ^ { + NSData *data = [NSData dataWithContentsOfURL:url]; + [ChatConversationView writeFileInCache:data name:localFile]; + }); + return; } PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init]; @@ -413,7 +364,11 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; - (IBAction)onFileClick:(id)sender { ChatConversationView *view = VIEW(ChatConversationView); NSString *name = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message]; - [view openFileWithURL:[view getICloudFileUrl:name]]; + if([[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]]) { + [view openFileWithURL:[ChatConversationView getCacheFileUrl:name]]; + } else { + [view openFileWithURL:[view getICloudFileUrl:name]]; + } } @@ -441,22 +396,38 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; - (IBAction)onImageClick:(id)event { LinphoneChatMessageState state = linphone_chat_message_get_state(self.message); if (state == LinphoneChatMessageStateNotDelivered) { + FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:self.message]; + if (thiz) { + [thiz stopAndDestroy]; + } [self onResendClick:event]; } else { if (![_messageImageView isLoading]) { ImageView *view = VIEW(ImageView); [PhoneMainView.instance changeCurrentView:view.compositeViewDescription]; + NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:self.message]; + NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message]; + NSString *imageName = NULL; + if (localImage && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localImage]]) { + imageName = localImage; + } else if (localFile && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localFile]]) { + if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) { + imageName = localFile; + } + } + + if (imageName) { + NSData *data = [NSData dataWithContentsOfFile:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:imageName]]; + UIImage *image = [[UIImage alloc] initWithData:data]; + if (image) + [view setImage:image]; + else + LOGE(@"Can't read image"); + return; + } + PHAsset *asset = [_messageImageView asset]; if (!asset) { - NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message]; - if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) { - NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]]; - UIImage *image = [[UIImage alloc] initWithData:data]; - if (image) - [view setImage:image]; - else - LOGE(@"Can't read image"); - } return; } PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.m b/Classes/LinphoneUI/UIChatBubbleTextCell.m index 7e20bc112..bcb30af36 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.m +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m @@ -276,74 +276,33 @@ if (linphone_chat_message_get_file_transfer_information(_message) != NULL) { NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:_message]; - NSNumber *uploadQuality =[LinphoneManager getMessageAppDataForKey:@"uploadQuality" inMessage:_message]; - NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message]; - NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:_message]; + NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message]; + NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:_message]; + /*FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:_message]; + if (thiz) { + [thiz stop] + }*/ [self onDelete]; - if(localImage){ - ChatConversationTableView *tableView = VIEW(ChatConversationView).tableController; - UIImage *img = [tableView.imagesInChatroom objectForKey:localImage]; - if (img) { - dispatch_async(dispatch_get_main_queue(), ^ { - [_chatRoomDelegate startImageUpload:img assetId:localImage withQuality:(uploadQuality ? [uploadQuality floatValue] : 0.9)]; - }); - } else { - PHFetchResult *assets = [LinphoneManager getPHAssets:localImage]; - - if (![assets firstObject]) - return; - PHAsset *asset = [assets firstObject]; - if (asset.mediaType != PHAssetMediaTypeImage) - return; - - PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; - options.synchronous = TRUE; - [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options - resultHandler:^(UIImage *image, NSDictionary * info) { - if (image) { - dispatch_async(dispatch_get_main_queue(), - ^(void) { - [_chatRoomDelegate startImageUpload:img assetId:localImage withQuality:(uploadQuality ? [uploadQuality floatValue] : 0.9)]; - }); - } else { - LOGE(@"Can't read image"); - } - }]; - } - } else if (localVideo) { - PHFetchResult *assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] options:nil]; - if (![assets firstObject]) - return; - PHAsset *asset = [assets firstObject]; - if (asset.mediaType != PHAssetMediaTypeVideo) - return; - - PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init]; - options.version = PHImageRequestOptionsVersionCurrent; - options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic; - - [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) { - AVURLAsset *urlAsset = (AVURLAsset *)asset; - - NSURL *url = urlAsset.URL; - NSData *data = [NSData dataWithContentsOfURL:url]; - dispatch_async(dispatch_get_main_queue(), - ^(void) { - [_chatRoomDelegate startFileUpload:data assetId:localVideo]; - }); - }]; - - } else if (localFile) { - NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]]; - [_chatRoomDelegate startFileUpload:data withName:localFile]; - } + if(localImage){ + dispatch_async(dispatch_get_main_queue(), ^ { + [_chatRoomDelegate resendFile:[ChatConversationView getCacheFileData:localImage] withName:localImage type:@"image" key:@"localimage" message:self.textMessage]; + }); + } else if (localVideo) { + dispatch_async(dispatch_get_main_queue(), ^ { + [_chatRoomDelegate resendFile:[ChatConversationView getCacheFileData:localVideo] withName:localVideo type:@"video" key:@"localvideo" message:self.textMessage]; + }); + } else if (localFile) { + dispatch_async(dispatch_get_main_queue(), ^ { + [_chatRoomDelegate resendFile:[ChatConversationView getCacheFileData:localFile] withName:localFile type:@"image" key:@"localfile" message:self.textMessage]; + }); + } } else { - [self onDelete]; + [self onDelete]; double delayInSeconds = 0.4; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { - [_chatRoomDelegate resendChat:self.textMessage withExternalUrl:nil]; + [_chatRoomDelegate resendChat:self.textMessage withExternalUrl:nil]; }); } } @@ -443,53 +402,74 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; font:messageFont]; size.height += textSize.height; } - - if(localFile) { - UIImage *image = nil; - NSString *type = [NSString stringWithUTF8String:linphone_content_get_type(fileContent)]; - if ([type isEqualToString:@"video"]) { - image = [self getImageFromVideoUrl:[VIEW(ChatConversationView) getICloudFileUrl:localFile]]; - } else if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) { - NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]]; - image = [[UIImage alloc] initWithData:data]; - } - if (image) { - size = [self getMediaMessageSizefromOriginalSize:image.size withWidth:width]; - // add size for message text - size.height += textSize.height; - size.width = MAX(textSize.width, size.width); - } else { - CGSize fileSize = CGSizeMake(230, 50); - size = [self getMediaMessageSizefromOriginalSize:fileSize withWidth:width]; - } - } else { - if (!localImage && !localVideo) { - //We are loading the image - return CGSizeMake(CELL_MIN_WIDTH + CELL_MESSAGE_X_MARGIN, CELL_MIN_HEIGHT + CELL_MESSAGE_Y_MARGIN + textSize.height + 20); - } - PHFetchResult *assets; - if(localImage) - assets = [LinphoneManager getPHAssets:localImage]; - else - assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] options:nil]; - if (![assets firstObject]) { - return CGSizeMake(CELL_MIN_WIDTH, CELL_MIN_WIDTH + CELL_MESSAGE_Y_MARGIN + textSize.height); - } else { - PHAsset *asset = [assets firstObject]; - CGSize originalImageSize = CGSizeMake([asset pixelWidth], [asset pixelHeight]); - size = [self getMediaMessageSizefromOriginalSize:originalImageSize withWidth:width]; - - // add size for message text - size.height += textSize.height; - size.width = MAX(textSize.width, size.width); - } - } - } + CGSize originalImageSize = CGSizeMake(230, 50); + if (localFile) { + UIImage *image = nil; + NSString *type = [NSString stringWithUTF8String:linphone_content_get_type(fileContent)]; + NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localFile]; + if ([type isEqualToString:@"video"]) { + if ([[NSFileManager defaultManager] fileExistsAtPath: filePath]) { + image = [self getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:localFile]]; + } else { + image = [self getImageFromVideoUrl:[VIEW(ChatConversationView) getICloudFileUrl:localFile]]; + } + } else if ([localFile hasSuffix:@"JPG"] || [localFile hasSuffix:@"PNG"] || [localFile hasSuffix:@"jpg"] || [localFile hasSuffix:@"png"]) { + if ([[NSFileManager defaultManager] fileExistsAtPath: filePath]) { + NSData *data = [NSData dataWithContentsOfFile:filePath]; + image = [[UIImage alloc] initWithData:data]; + } else { + NSData *data = [NSData dataWithContentsOfURL:[VIEW(ChatConversationView) getICloudFileUrl:localFile]]; + image = [[UIImage alloc] initWithData:data]; + } + } else { + // other files // todo for text + CGSize fileSize = CGSizeMake(230, 50); + size = [self getMediaMessageSizefromOriginalSize:fileSize withWidth:width]; + size.width = MAX(size.width + CELL_MESSAGE_X_MARGIN, CELL_MIN_WIDTH); + size.height = MAX(size.height + CELL_MESSAGE_Y_MARGIN, CELL_MIN_HEIGHT); + return size; + } - size.width = MAX(size.width + CELL_MESSAGE_X_MARGIN, CELL_MIN_WIDTH); - size.height = MAX(size.height + CELL_MESSAGE_Y_MARGIN, CELL_MIN_HEIGHT); - return size; + originalImageSize = image.size; + } else { + if (!localImage && !localVideo) { + //We are loading the image + return CGSizeMake(CELL_MIN_WIDTH + CELL_MESSAGE_X_MARGIN, CELL_MIN_HEIGHT + CELL_MESSAGE_Y_MARGIN + textSize.height + 20); + } + + if (localImage && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localImage]]) { + NSData* data = [ChatConversationView getCacheFileData:localImage]; + UIImage *image = [[UIImage alloc] initWithData:data]; + originalImageSize = image.size; + } else if (localVideo && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localVideo]]) { + UIImage *image = [UIChatBubbleTextCell getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:localVideo]]; + originalImageSize = image.size; + } else { + // support previous versions + PHFetchResult *assets; + if(localImage) + assets = [LinphoneManager getPHAssets:localImage]; + else + assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] options:nil]; + + if (![assets firstObject]) { + return CGSizeMake(CELL_MIN_WIDTH, CELL_MIN_WIDTH + CELL_MESSAGE_Y_MARGIN + textSize.height); + } else { + PHAsset *asset = [assets firstObject]; + originalImageSize = CGSizeMake([asset pixelWidth], [asset pixelHeight]); + } + } + } + size = [self getMediaMessageSizefromOriginalSize:originalImageSize withWidth:width]; + // add size for message text + size.height += textSize.height; + size.width = MAX(textSize.width, size.width); + } + + size.width = MAX(size.width + CELL_MESSAGE_X_MARGIN, CELL_MIN_WIDTH); + size.height = MAX(size.height + CELL_MESSAGE_Y_MARGIN, CELL_MIN_HEIGHT); + return size; } + (CGSize)ViewSizeForMessage:(LinphoneChatMessage *)chat withWidth:(int)width { diff --git a/Classes/Utils/FileTransferDelegate.h b/Classes/Utils/FileTransferDelegate.h index 019474f9c..acf6429c3 100644 --- a/Classes/Utils/FileTransferDelegate.h +++ b/Classes/Utils/FileTransferDelegate.h @@ -23,12 +23,14 @@ @interface FileTransferDelegate : NSObject -- (void)upload:(UIImage *)image withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality; +- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key; +- (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality; - (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name; - (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom; - (void)cancel; - (BOOL)download:(LinphoneChatMessage *)message; - (void)stopAndDestroy; ++ (FileTransferDelegate *)messageDelegate:(LinphoneChatMessage *)message; @property() LinphoneChatMessage *message; @property() NSString *text; diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m index 4b1eaad53..0879271d6 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -53,160 +53,26 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con } if (size == 0) { - LOGI(@"Transfer of %s (%d bytes): download finished", linphone_content_get_name(content), size); + NSString *name = [NSString stringWithUTF8String: linphone_content_get_name(content) ? : ""]; + LOGI(@"Transfer of %@ (%d bytes): download finished", name, 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) { - [view showFileDownloadError]; - [thiz stopAndDestroy]; - return; - } - - CFBridgingRetain(thiz); - [[LinphoneManager.instance fileTransferDelegates] removeObject:thiz]; - - // 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: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]; - } - [NSNotificationCenter.defaultCenter - postNotificationName:kLinphoneFileTransferRecvUpdate - object:thiz - userInfo:@{ - @"state" : @(LinphoneChatMessageStateDelivered), // we dont want to - // trigger - // FileTransferDone here - @"image" : image, - @"progress" : @(1.f), - }]; - - [thiz stopAndDestroy]; - CFRelease((__bridge CFTypeRef)thiz); - }); - }]; - } else if([fileType isEqualToString:@"video"]) { - CFBridgingRetain(thiz); - [[LinphoneManager.instance fileTransferDelegates] removeObject:thiz]; - NSString *name =[NSString stringWithUTF8String:linphone_content_get_name(content)]; - NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; - [[NSFileManager defaultManager] createFileAtPath:filePath - contents:thiz.data - attributes:nil]; - // 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: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]; - } - [NSNotificationCenter.defaultCenter - postNotificationName:kLinphoneFileTransferRecvUpdate - object:thiz - userInfo:@{ - @"state" : @(LinphoneChatMessageStateDelivered), // we dont want to - // trigger - // FileTransferDone here - @"progress" : @(1.f), - }]; - - [thiz stopAndDestroy]; - CFRelease((__bridge CFTypeRef)thiz); - }); - }]; - } - }; - // 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]; - [thiz stopAndDestroy]; - } - }); - }]; - } - } else { - [[LinphoneManager.instance fileTransferDelegates] removeObject:thiz]; - NSString *key = @"localfile" ; - NSString *name =[NSString stringWithUTF8String:linphone_content_get_name(content)]; - [LinphoneManager setValueInMessageAppData:@"saving..." forKey:key inMessage:message]; - - //write file to path - dispatch_async(dispatch_get_main_queue(), ^{ - if([view writeFileInICloud:thiz.data fileURL:[view getICloudFileUrl:name]]) { - [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message]; - - [NSNotificationCenter.defaultCenter - postNotificationName:kLinphoneFileTransferRecvUpdate - object:thiz - userInfo:@{ - @"state" : @(LinphoneChatMessageStateDelivered), // we dont want to trigger - @"progress" : @(1.f), // FileTransferDone here - }]; - } - [thiz stopAndDestroy]; - }); - } - } else { + NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)]; + NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:name]; + [LinphoneManager setValueInMessageAppData:@"saving..." forKey:key inMessage:message]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [ChatConversationView writeFileInCache:thiz.data name:name]; + [LinphoneManager setValueInMessageAppData:name + forKey:key + inMessage:message]; + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferRecvUpdate + object:thiz + userInfo:@{ + @"state" : @(LinphoneChatMessageStateDelivered), // we dont want to trigger + @"progress" : @(1.f), // FileTransferDone here + }]; + }); + } else { LOGD(@"Transfer of %s (%d bytes): already %ld sent, adding %ld", linphone_content_get_name(content), linphone_content_get_file_size(content), [thiz.data length], size); [thiz.data appendBytes:linphone_buffer_get_string_content(buffer) length:size]; @@ -218,7 +84,6 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con @"progress" : @([thiz.data length] * 1.f / linphone_content_get_file_size(content)), }]; } - } static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *message, const LinphoneContent *content, @@ -248,14 +113,18 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m // this is the last time we will be notified, so destroy ourselve if (remaining <= size) { LOGI(@"Upload ended"); + + linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(thiz.message), NULL); thiz.message = NULL; [thiz stopAndDestroy]; - //workaround fix : avoid chatconversationtableview scrolling - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferSendUpdate - object:thiz - userInfo:@{@"state" : @(LinphoneChatMessageStateDelivered), - }]; + + //workaround fix : avoid chatconversationtableview scrolling + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferSendUpdate + object:thiz + userInfo:@{@"state" : @(LinphoneChatMessageStateDelivered), + }]; + } return buffer; } else { @@ -266,65 +135,52 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m return NULL; } -- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key keyData:(NSString *)keyData qualityData:(NSNumber *)qualityData { - [LinphoneManager.instance.fileTransferDelegates addObject:self]; - - LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom)); - _data = [NSMutableData dataWithData:data]; - linphone_content_set_type(content, [type UTF8String]); - linphone_content_set_subtype(content, [subtype UTF8String]); - linphone_content_set_name(content, [name UTF8String]); - linphone_content_set_size(content, _data.length); - _message = linphone_chat_room_create_file_transfer_message(chatRoom, content); - BOOL isOneToOneChat = linphone_chat_room_get_capabilities(chatRoom) & LinphoneChatRoomCapabilitiesOneToOne; - if (!isOneToOneChat && (_text!=nil && ![_text isEqualToString:@""])) - linphone_chat_message_add_text_content(_message, [_text UTF8String]); - linphone_content_unref(content); - - linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(_message), - linphone_iphone_file_transfer_send); +- (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key{ + [LinphoneManager.instance.fileTransferDelegates addObject:self]; - // internal url is saved in the appdata for display and later save - [LinphoneManager setValueInMessageAppData:keyData forKey:key inMessage:_message]; - [LinphoneManager setValueInMessageAppData:qualityData forKey:@"uploadQuality" inMessage:_message]; - - LOGI(@"%p Uploading content from message %p", self, _message); - linphone_chat_message_send(_message); + LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom)); + _data = [NSMutableData dataWithData:data]; + linphone_content_set_type(content, [type UTF8String]); + linphone_content_set_subtype(content, [subtype UTF8String]); + linphone_content_set_name(content, [name UTF8String]); + linphone_content_set_size(content, _data.length); + _message = linphone_chat_room_create_file_transfer_message(chatRoom, content); + BOOL isOneToOneChat = linphone_chat_room_get_capabilities(chatRoom) & LinphoneChatRoomCapabilitiesOneToOne; + if (!isOneToOneChat && (_text!=nil && ![_text isEqualToString:@""])) + linphone_chat_message_add_text_content(_message, [_text UTF8String]); + linphone_content_unref(content); + + linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(_message), linphone_iphone_file_transfer_send); + + [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:_message]; + + LOGI(@"%p Uploading content from message %p", self, _message); + linphone_chat_message_send(_message); } -- (void)upload:(UIImage *)image withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality { - NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]]; - if (phAssetId) - [self uploadData:UIImageJPEGRepresentation(image, quality) forChatRoom:chatRoom type:@"image" subtype:@"jpeg" name:name key:@"localimage" keyData:phAssetId qualityData:[NSNumber numberWithFloat:quality]]; - else - [self uploadData:UIImageJPEGRepresentation(image, quality) forChatRoom:chatRoom type:@"image" subtype:@"jpeg" name:name key:@"localimage" keyData:nil qualityData:nil]; +- (void)uploadImage:(UIImage *)image forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality { + NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]]; + NSData *data = UIImageJPEGRepresentation(image, quality); + [ChatConversationView writeFileInCache:data name:name]; + [self uploadData:data forChatRoom:chatRoom type:@"image" subtype:@"jpg" name:name key:@"localimage"]; } - (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom { - NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]]; - if (phAssetId) - [self uploadData:data forChatRoom:chatRoom type:@"video" subtype:nil name:name key:@"localvideo" keyData:phAssetId qualityData:nil]; - else - [self uploadData:data forChatRoom:chatRoom type:@"video" subtype:nil name:name key:@"localvideo" keyData:@"ending..." qualityData:nil]; + NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]]; + [ChatConversationView writeFileInCache:data name:name]; + [self uploadData:data forChatRoom:chatRoom type:@"video" subtype:@"mov" name:name key:@"localvideo"]; } - (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name { - // we will write local files into ours folder of icloud - ChatConversationView *view = VIEW(ChatConversationView); - NSURL *url = [view getICloudFileUrl:name]; - if ([view writeFileInICloud:data fileURL:url]) { - AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil]; - if ([[asset tracksWithMediaType:AVMediaTypeVideo] count] > 0) { - // if it's a video - [self uploadData:data forChatRoom:chatRoom type:@"video" subtype:nil name:name key:@"localfile" keyData:name qualityData:nil]; - } else { - [self uploadData:data forChatRoom:chatRoom type:@"file" subtype:nil name:name key:@"localfile" keyData:name qualityData:nil]; - } - } + [ChatConversationView writeFileInCache:data name:name]; + NSURL *url = [ChatConversationView getCacheFileUrl:name]; + AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil]; + NSString *fileType = [[asset tracksWithMediaType:AVMediaTypeVideo] count] > 0 ? @"video" : @"file"; + NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:name]; + + [self uploadData:data forChatRoom:chatRoom type:fileType subtype:name.lastPathComponent name:name key:key]; } - - - (BOOL)download:(LinphoneChatMessage *)message { [[LinphoneManager.instance fileTransferDelegates] addObject:self]; From 317665a90b1ad360936da12f9d2f11bc3b33a9c4 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Thu, 25 Feb 2021 09:40:23 +0100 Subject: [PATCH 26/46] enable save image/video to Photos --- .../Base.lproj/UIChatBubblePhotoCell.xib | 31 +++-- Classes/LinphoneUI/UIChatBubblePhotoCell.h | 2 + Classes/LinphoneUI/UIChatBubblePhotoCell.m | 113 ++++++++++++++++++ 3 files changed, 133 insertions(+), 13 deletions(-) diff --git a/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib b/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib index bfc9f5d1b..2d4295fee 100644 --- a/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib +++ b/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib @@ -1,11 +1,9 @@ - - - - + + - + @@ -30,6 +28,7 @@ + @@ -93,7 +92,7 @@ - - - - + @@ -238,7 +239,7 @@ - + @@ -287,7 +288,7 @@ - + @@ -338,5 +339,11 @@ + + + + + + From 470e7add1f96db15f558399e3d4429193b49570b Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Thu, 18 Mar 2021 18:02:31 +0100 Subject: [PATCH 41/46] improve send/recv file --- .../Base.lproj/UIChatBubblePhotoCell.xib | 46 +++++----- Classes/LinphoneUI/UIChatBubblePhotoCell.m | 37 +++++++- Classes/LinphoneUI/UIChatBubbleTextCell.h | 2 +- Classes/LinphoneUI/UIChatBubbleTextCell.m | 7 +- Classes/Utils/FileTransferDelegate.m | 86 +++++-------------- 5 files changed, 86 insertions(+), 92 deletions(-) diff --git a/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib b/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib index 2d4295fee..da225bc9d 100644 --- a/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib +++ b/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib @@ -84,6 +84,29 @@ + + + + + + + + @@ -144,29 +167,6 @@ - - - - - - - - diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m index 119da2ab1..3100c0d83 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.m +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.m @@ -78,9 +78,20 @@ _finalImage.hidden = TRUE; _fileTransferProgress.progress = 0; assetIsLoaded = FALSE; - [self disconnectFromFileDelegate]; - [super setChatMessage:amessage]; + /* As the cell UI will be reset, fileTransDelegate need to be reconnected. Otherwise, the UIProgressView will not work */ + [self disconnectFromFileDelegate]; + if (amessage) { + for (FileTransferDelegate *aftd in [LinphoneManager.instance fileTransferDelegates]) { + if (aftd.message == amessage && linphone_chat_message_get_state(amessage) == LinphoneChatMessageStateFileTransferInProgress) { + LOGI(@"Chat message [%p] with file transfer delegate [%p], connecting to it!", amessage, aftd); + [self connectToFileDelegate:aftd]; + break; + } + } + } + + [super setChatMessageForCbs:amessage]; } - (void) loadImageAsset:(PHAsset*) asset image:(UIImage *)image { @@ -157,6 +168,26 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; LOGW(@"file content is null"); return; } + + BOOL is_outgoing = linphone_chat_message_is_outgoing(self.message); + if (!is_outgoing) { + LinphoneChatMessageState state = linphone_chat_message_get_state(self.message); + if (state != LinphoneChatMessageStateFileTransferDone && state != LinphoneChatMessageStateDisplayed) { + if (state == LinphoneChatMessageStateFileTransferInProgress) { + _cancelButton.hidden = _fileTransferProgress.hidden = NO; + _downloadButton.hidden = YES; + _playButton.hidden = YES; + _fileName.hidden = _fileView.hidden = _fileButton.hidden =YES; + } else { + _downloadButton.hidden = NO; + _cancelButton.hidden = _fileTransferProgress.hidden = YES; + _playButton.hidden = YES; + _fileName.hidden = _fileView.hidden = _fileButton.hidden = YES; + } + return; + } + } + NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(fileContent)]; NSString *fileName = [NSString stringWithUTF8String:linphone_content_get_name(fileContent)]; NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:fileName]; @@ -539,6 +570,7 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; } else { ChatConversationView *view = VIEW(ChatConversationView); [view.tableController updateEventEntry:self.event]; + [view.tableController scrollToBottom:true]; } } - (void)onFileTransferRecvUpdate:(NSNotification *)notif { @@ -550,6 +582,7 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; } else { ChatConversationView *view = VIEW(ChatConversationView); [view.tableController updateEventEntry:self.event]; + [view.tableController scrollToBottom:true]; } } diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.h b/Classes/LinphoneUI/UIChatBubbleTextCell.h index caf95c8ef..2bf4c248b 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.h +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.h @@ -52,7 +52,7 @@ + (UIImage *)getImageFromVideoUrl:(NSURL *)url; - (void)setEvent:(LinphoneEventLog *)event; -- (void)setChatMessage:(LinphoneChatMessage *)message; +- (void)setChatMessageForCbs:(LinphoneChatMessage *)message; - (void)onDelete; - (void)onResend; diff --git a/Classes/LinphoneUI/UIChatBubbleTextCell.m b/Classes/LinphoneUI/UIChatBubbleTextCell.m index 1c1db4154..4522ff5b9 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.m +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m @@ -63,7 +63,7 @@ - (void)dealloc { [self setEvent:NULL]; - [self setChatMessage:NULL]; + [self setChatMessageForCbs:NULL]; } #pragma mark - @@ -77,10 +77,10 @@ LOGE(@"Impossible to create a ChatBubbleText whit a non message event"); return; } - [self setChatMessage:linphone_event_log_get_chat_message(event)]; + [self setChatMessageForCbs:linphone_event_log_get_chat_message(event)]; } -- (void)setChatMessage:(LinphoneChatMessage *)amessage { +- (void)setChatMessageForCbs:(LinphoneChatMessage *)amessage { if (!amessage || amessage == _message) { return; } @@ -312,6 +312,7 @@ static void message_status(LinphoneChatMessage *msg, LinphoneChatMessageState st LinphoneEventLog *event = (LinphoneEventLog *)linphone_chat_message_cbs_get_user_data(linphone_chat_message_get_callbacks(msg)); ChatConversationView *view = VIEW(ChatConversationView); [view.tableController updateEventEntry:event]; + [view.tableController scrollToBottom:true]; } static void participant_imdn_status(LinphoneChatMessage* msg, const LinphoneParticipantImdnState *state) { diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m index 618a23ac9..48ec16a3c 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -43,57 +43,41 @@ return nil; } -static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, const LinphoneContent *content, - const LinphoneBuffer *buffer) { +static void file_transfer_progress_indication_recv(LinphoneChatMessage *message, LinphoneContent* content, size_t offset, size_t total) { FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:message]; - size_t size = linphone_buffer_get_size(buffer); - if (!thiz.data) { - thiz.data = [[NSMutableData alloc] initWithCapacity:linphone_content_get_file_size(content)]; - } - - if (size == 0) { + if (offset == total) { NSString *name = [NSString stringWithUTF8String: linphone_content_get_name(content) ? : ""]; - LOGI(@"Transfer of %@ (%d bytes): download finished", name, size); - assert([thiz.data length] == linphone_content_get_file_size(content)); + LOGI(@"Transfer of %@ (%d bytes): download finished", name, total); NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)]; NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:name]; - [LinphoneManager setValueInMessageAppData:@"saving..." forKey:key inMessage:message]; dispatch_async(dispatch_get_main_queue(), ^{ - [ChatConversationView writeFileInCache:thiz.data name:name]; [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message]; - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferRecvUpdate - object:thiz - userInfo:@{ - @"state" : @(LinphoneChatMessageStateDelivered), // we dont want to trigger - @"progress" : @(1.f), // FileTransferDone here - }]; - if ([ConfigManager.instance lpConfigBoolForKeyWithKey:@"auto_write_to_gallery_preference"]) { - [ChatConversationView writeMediaToGallery:name fileType:fileType]; - } + dispatch_async(dispatch_get_main_queue(), ^{ + if ([ConfigManager.instance lpConfigBoolForKeyWithKey:@"auto_write_to_gallery_preference"]) { + [ChatConversationView writeMediaToGallery:name fileType:fileType]; + } + }); }); } else { - LOGD(@"Transfer of %s (%d bytes): already %ld sent, adding %ld", linphone_content_get_name(content), - linphone_content_get_file_size(content), [thiz.data length], size); - [thiz.data appendBytes:linphone_buffer_get_string_content(buffer) length:size]; + LOGD(@"Transfer of %s (%d bytes): already %ld recv", linphone_content_get_name(content), + total, offset); [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferRecvUpdate object:thiz userInfo:@{ @"state" : @(linphone_chat_message_get_state(message)), - @"progress" : @([thiz.data length] * 1.f / linphone_content_get_file_size(content)), + @"progress" : @(offset * 1.f / total), }]; } } -static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *message, const LinphoneContent *content, - size_t offset, size_t size) { +static void file_transfer_progress_indication_send(LinphoneChatMessage *message, LinphoneContent* content, size_t offset, size_t total) { FileTransferDelegate *thiz = [FileTransferDelegate messageDelegate:message]; - size_t total = thiz.data.length; - if (thiz.data) { + if (total) { size_t remaining = total - offset; NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:@{ @@ -105,37 +89,17 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferSendUpdate object:thiz userInfo:dict]; - - LinphoneBuffer *buffer = NULL; - @try { - buffer = linphone_buffer_new_from_data([thiz.data subdataWithRange:NSMakeRange(offset, size)].bytes, size); - } @catch (NSException *exception) { - LOGE(@"Exception: %@", exception); - } - // this is the last time we will be notified, so destroy ourselve - if (remaining <= size) { + if (offset == total) { LOGI(@"Upload ended"); - - linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(thiz.message), NULL); thiz.message = NULL; [thiz stopAndDestroy]; - - //workaround fix : avoid chatconversationtableview scrolling - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneFileTransferSendUpdate - object:thiz - userInfo:@{@"state" : @(LinphoneChatMessageStateDelivered), - }]; - } - return buffer; } else { LOGE(@"Transfer of %s (%d bytes): %d Error - no upload data in progress!", linphone_content_get_name(content), total, offset); } - - return NULL; } - (void)uploadData:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom type:(NSString *)type subtype:(NSString *)subtype name:(NSString *)name key:(NSString *)key{ @@ -146,18 +110,17 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m [LinphoneManager.instance.fileTransferDelegates addObject:self]; LinphoneContent *content = linphone_core_create_content(linphone_chat_room_get_core(chatRoom)); - _data = [NSMutableData dataWithData:data]; linphone_content_set_type(content, [type UTF8String]); linphone_content_set_subtype(content, [subtype UTF8String]); linphone_content_set_name(content, [name UTF8String]); - linphone_content_set_size(content, _data.length); + linphone_content_set_file_path(content, [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name].UTF8String); _message = linphone_chat_room_create_file_transfer_message(chatRoom, content); BOOL isOneToOneChat = linphone_chat_room_get_capabilities(chatRoom) & LinphoneChatRoomCapabilitiesOneToOne; if (!isOneToOneChat && (_text!=nil && ![_text isEqualToString:@""])) linphone_chat_message_add_text_content(_message, [_text UTF8String]); linphone_content_unref(content); - linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(_message), linphone_iphone_file_transfer_send); + linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(_message), file_transfer_progress_indication_send); [LinphoneManager setValueInMessageAppData:name forKey:key inMessage:_message]; @@ -197,16 +160,14 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m _message = message; - const char *url = linphone_chat_message_get_external_body_url(_message); - LOGI(@"%p Downloading content in %p from %s", self, message, url); + LinphoneContent *content = linphone_chat_message_get_file_transfer_information(_message); + if (content == nil) return FALSE; - if (url == nil) - return FALSE; + LOGI(@"%p Downloading content in %p ", self, message); - linphone_chat_message_cbs_set_file_transfer_recv(linphone_chat_message_get_callbacks(_message), - linphone_iphone_file_transfer_recv); - - linphone_chat_message_download_file(_message); + linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(_message), file_transfer_progress_indication_recv); + linphone_content_set_file_path(content, [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:[NSString stringWithUTF8String:linphone_content_get_name(content)]].UTF8String); + linphone_chat_message_download_content(_message, content); return TRUE; } @@ -217,8 +178,7 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m LinphoneChatMessage *msg = _message; _message = NULL; LOGI(@"%p Cancelling transfer from %p", self, msg); - linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(msg), NULL); - linphone_chat_message_cbs_set_file_transfer_recv(linphone_chat_message_get_callbacks(msg), NULL); + linphone_chat_message_cbs_set_file_transfer_progress_indication(linphone_chat_message_get_callbacks(msg), NULL); // when we cancel file transfer, this will automatically trigger NotDelivered callback... recalling ourself a // second time so we have to unset message BEFORE calling this linphone_chat_message_cancel_file_transfer(msg); From 565ad48532a2e03b66ca38446baa45c32968f846 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Mon, 22 Mar 2021 21:10:44 +0100 Subject: [PATCH 42/46] avoid overlapping chat cells --- Classes/ChatConversationTableView.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Classes/ChatConversationTableView.m b/Classes/ChatConversationTableView.m index 031aa723a..2cb6664fb 100644 --- a/Classes/ChatConversationTableView.m +++ b/Classes/ChatConversationTableView.m @@ -255,11 +255,11 @@ static const int BASIC_EVENT_LIST=15; kCellId = NSStringFromClass(UIChatBubblePhotoCell.class); else kCellId = NSStringFromClass(UIChatBubbleTextCell.class); - + + // To use less memory and to avoid overlapping. To be improved. UIChatBubbleTextCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellId]; - if (!cell) { - cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId]; - } + cell = [[NSClassFromString(kCellId) alloc] initWithIdentifier:kCellId]; + [cell setEvent:event]; if (chat) { cell.isFirst = [self isFirstIndexInTableView:indexPath chat:chat]; From 164b0f58c3054df42120aa3bd91e6cb639001f87 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Tue, 23 Mar 2021 11:43:36 +0100 Subject: [PATCH 43/46] fix crash enterBackground when current view is DevicesListView --- Classes/LinphoneAppDelegate.m | 4 ++-- Classes/PhoneMainView.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index 705e000d0..ed15cb9fc 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -69,8 +69,8 @@ } else if (PhoneMainView.instance.currentView == ChatConversationInfoView.compositeViewDescription) { ChatConversationInfoView *view = VIEW(ChatConversationInfoView); [view removeCallbacks]; - } else if (PhoneMainView.instance.currentView == RecordingsListView.compositeViewDescription) { - // To avoid crash if recording is still played. + } else if (PhoneMainView.instance.currentView == RecordingsListView.compositeViewDescription || PhoneMainView.instance.currentView == DevicesListView.compositeViewDescription) { + // To avoid crash [PhoneMainView.instance changeCurrentView:DialerView.compositeViewDescription]; } [CoreManager.instance stopLinphoneCore]; diff --git a/Classes/PhoneMainView.h b/Classes/PhoneMainView.h index 414bbd6de..60633724e 100644 --- a/Classes/PhoneMainView.h +++ b/Classes/PhoneMainView.h @@ -51,6 +51,7 @@ #import "UIConfirmationDialog.h" #import "Utils.h" #import "LaunchScreen.h" +#import "DevicesListView.h" #define DYNAMIC_CAST(x, cls) \ ({ \ From 7f436ecf40f4cda0db9d00b3b850db4af3645c2a Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Tue, 23 Mar 2021 12:36:09 +0100 Subject: [PATCH 44/46] set default conference_factory_uri --- Resources/assistant_linphone_create.rc | 1 + Resources/assistant_linphone_existing.rc | 1 + 2 files changed, 2 insertions(+) diff --git a/Resources/assistant_linphone_create.rc b/Resources/assistant_linphone_create.rc index bf449993e..eaf8392eb 100644 --- a/Resources/assistant_linphone_create.rc +++ b/Resources/assistant_linphone_create.rc @@ -15,6 +15,7 @@ sip.linphone.org nat_policy_default_values 1 + sip:conference-factory@sip.linphone.org
diff --git a/Resources/assistant_linphone_existing.rc b/Resources/assistant_linphone_existing.rc index beb34acf7..826dcfc36 100644 --- a/Resources/assistant_linphone_existing.rc +++ b/Resources/assistant_linphone_existing.rc @@ -15,6 +15,7 @@ sip.linphone.org nat_policy_default_values 1 + sip:conference-factory@sip.linphone.org
From b5df5110d37ce6b1e58fb2699a94df8f3baa288f Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Tue, 23 Mar 2021 12:36:59 +0100 Subject: [PATCH 45/46] use new api configure audio session --- Classes/CallManager.swift | 12 ------------ Classes/ProviderDelegate.swift | 3 ++- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/Classes/CallManager.swift b/Classes/CallManager.swift index 420c7fe1e..6f1162f6f 100644 --- a/Classes/CallManager.swift +++ b/Classes/CallManager.swift @@ -332,18 +332,6 @@ import AVFoundation providerDelegate.uuids.removeAll() } - // To be removed. - static func configAudioSession(audioSession: AVAudioSession) { - do { - try audioSession.setCategory(AVAudioSession.Category.playAndRecord, mode: AVAudioSession.Mode.voiceChat, options: AVAudioSession.CategoryOptions(rawValue: AVAudioSession.CategoryOptions.allowBluetooth.rawValue | AVAudioSession.CategoryOptions.allowBluetoothA2DP.rawValue)) - try audioSession.setMode(AVAudioSession.Mode.voiceChat) - try audioSession.setPreferredSampleRate(48000.0) - try AVAudioSession.sharedInstance().setActive(true, options: []) - } catch { - Log.directLog(BCTBX_LOG_WARNING, text: "CallKit: Unable to config audio session because : \(error)") - } - } - @objc func terminateCall(call: OpaquePointer?) { if (call == nil) { Log.directLog(BCTBX_LOG_ERROR, text: "Can not terminate null call!") diff --git a/Classes/ProviderDelegate.swift b/Classes/ProviderDelegate.swift index 1b7fdd467..c57a005af 100644 --- a/Classes/ProviderDelegate.swift +++ b/Classes/ProviderDelegate.swift @@ -174,9 +174,9 @@ extension ProviderDelegate: CXProviderDelegate { Log.directLog(BCTBX_LOG_MESSAGE, text: "CallKit: answer call with call-id: \(String(describing: callId)) and UUID: \(uuid.description).") let call = CallManager.instance().callByCallId(callId: callId) + CallManager.instance().lc?.configureAudioSession() if (call == nil || call?.state != Call.State.IncomingReceived) { // The application is not yet registered or the call is not yet received, mark the call as accepted. The audio session must be configured here. - CallManager.configAudioSession(audioSession: AVAudioSession.sharedInstance()) callInfo?.accepted = true callInfos.updateValue(callInfo!, forKey: uuid) CallManager.instance().providerDelegate.endCallNotExist(uuid: uuid, timeout: .now() + 10) @@ -242,6 +242,7 @@ extension ProviderDelegate: CXProviderDelegate { action.fail() } + CallManager.instance().lc?.configureAudioSession() try CallManager.instance().doCall(addr: addr!, isSas: callInfo?.sasEnabled ?? false) } catch { Log.directLog(BCTBX_LOG_ERROR, text: "CallKit: Call started failed because \(error)") From c273564d60778b852af0e9fe68f20d97af0e5183 Mon Sep 17 00:00:00 2001 From: Danmei Chen Date: Tue, 23 Mar 2021 16:05:16 +0100 Subject: [PATCH 46/46] push new translations --- .../{ => Base.lproj}/RecordingsListView.xib | 0 Classes/fr.lproj/RecordingsListView.strings | 18 ++++++++++++++++++ .../en.lproj/Call.strings | Bin 550 -> 754 bytes .../en.lproj/Network.strings | Bin 984 -> 902 bytes linphone.xcodeproj/project.pbxproj | 18 ++++++++++++++---- 5 files changed, 32 insertions(+), 4 deletions(-) rename Classes/{ => Base.lproj}/RecordingsListView.xib (100%) create mode 100644 Classes/fr.lproj/RecordingsListView.strings diff --git a/Classes/RecordingsListView.xib b/Classes/Base.lproj/RecordingsListView.xib similarity index 100% rename from Classes/RecordingsListView.xib rename to Classes/Base.lproj/RecordingsListView.xib diff --git a/Classes/fr.lproj/RecordingsListView.strings b/Classes/fr.lproj/RecordingsListView.strings new file mode 100644 index 000000000..8025d9b51 --- /dev/null +++ b/Classes/fr.lproj/RecordingsListView.strings @@ -0,0 +1,18 @@ + +/* Class = "UIButton"; accessibilityLabel = "Select all"; ObjectID = "16S-9G-2cb"; */ +"16S-9G-2cb.accessibilityLabel" = "Select all"; + +/* Class = "UIButton"; accessibilityLabel = "Edit"; ObjectID = "CWx-9g-0JG"; */ +"CWx-9g-0JG.accessibilityLabel" = "Edit"; + +/* Class = "UIButton"; accessibilityLabel = "Delete all"; ObjectID = "CqG-tB-maQ"; */ +"CqG-tB-maQ.accessibilityLabel" = "Delete all"; + +/* Class = "UIButton"; accessibilityLabel = "Back"; ObjectID = "rAc-tI-AVp"; */ +"rAc-tI-AVp.accessibilityLabel" = "Back"; + +/* Class = "UIButton"; accessibilityLabel = "Delete all"; ObjectID = "zDs-pW-vyA"; */ +"zDs-pW-vyA.accessibilityLabel" = "Delete all"; + +/* Class = "UILabel"; text = "No recording found"; ObjectID = "zXd-Ic-rwm"; */ +"zXd-Ic-rwm.text" = "No recording found"; diff --git a/Settings/InAppSettings.bundle/en.lproj/Call.strings b/Settings/InAppSettings.bundle/en.lproj/Call.strings index bd4eeece62d37c3bf3a1f4fe98cfd87e99aee71b..841da9c055264cafd1945800562158840e7fe633 100644 GIT binary patch literal 754 zcmbV~%?^Sv5QOLKQ;@!f@unsmPy^~60ZkL3#GivNug>GLzUJ5>!xC30)@EFB+9@VK$^t1i>`t+w+nT$ub6D@(Q)aAS(Avx=dREMR z=9A#Yvz9X%+`0GJ$MkqgtaC``-1bbQnX_TWmT9$3yF<>K@hQV9=akc<=PEhhklT?U y(;qEIs+uR=zA HGff8oCZ`gT diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index d650aca28..1da74ac8b 100644 --- a/linphone.xcodeproj/project.pbxproj +++ b/linphone.xcodeproj/project.pbxproj @@ -103,6 +103,7 @@ 615A28402180A2620060F920 /* invite_linphone@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 615A283F2180A2620060F920 /* invite_linphone@2x.png */; }; 615A28422180C0870060F920 /* recording.png in Resources */ = {isa = PBXBuildFile; fileRef = 615A28412180C0820060F920 /* recording.png */; }; 615A28442180C0900060F920 /* recording@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 615A28432180C08F0060F920 /* recording@2x.png */; }; + 617B4A60260A2B7800A87337 /* RecordingsListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 617B4A62260A2B7800A87337 /* RecordingsListView.xib */; }; 6180D6FE21EE41A800AD9CB6 /* QuickLook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6180D6FD21EE41A800AD9CB6 /* QuickLook.framework */; }; 61AE364F20C00B370089D9D3 /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AE364E20C00B370089D9D3 /* ShareViewController.m */; }; 61AE365220C00B370089D9D3 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 61AE365020C00B370089D9D3 /* MainInterface.storyboard */; }; @@ -678,7 +679,6 @@ CF1DE92D210A0F5D00A0A97E /* UILinphoneAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = CF1DE924210A0F5A00A0A97E /* UILinphoneAudioPlayer.m */; }; CF1DE92E210A0F5D00A0A97E /* UILinphoneAudioPlayer.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF1DE92B210A0F5B00A0A97E /* UILinphoneAudioPlayer.xib */; }; CF7602D7210867E800749F76 /* RecordingsListView.m in Sources */ = {isa = PBXBuildFile; fileRef = CF7602D5210867E800749F76 /* RecordingsListView.m */; }; - CF7602D8210867E800749F76 /* RecordingsListView.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF7602D6210867E800749F76 /* RecordingsListView.xib */; }; CF7602E221086EB200749F76 /* RecordingsListTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = CF7602E021086EB200749F76 /* RecordingsListTableView.m */; }; CF7602E72108759A00749F76 /* UIRecordingCell.m in Sources */ = {isa = PBXBuildFile; fileRef = CF7602E52108759A00749F76 /* UIRecordingCell.m */; }; CF7602E82108759A00749F76 /* UIRecordingCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF7602E62108759A00749F76 /* UIRecordingCell.xib */; }; @@ -959,6 +959,8 @@ 615A283F2180A2620060F920 /* invite_linphone@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "invite_linphone@2x.png"; sourceTree = ""; }; 615A28412180C0820060F920 /* recording.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = recording.png; sourceTree = ""; }; 615A28432180C08F0060F920 /* recording@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "recording@2x.png"; sourceTree = ""; }; + 617B4A61260A2B7800A87337 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/RecordingsListView.xib; sourceTree = ""; }; + 617B4A64260A2B8500A87337 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/RecordingsListView.strings; sourceTree = ""; }; 6180D6FD21EE41A800AD9CB6 /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = System/Library/Frameworks/QuickLook.framework; sourceTree = SDKROOT; }; 6187B1B524B3271500D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AboutView.strings; sourceTree = ""; }; 6187B1B624B3271500D580FB /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/AssistantLinkView.strings; sourceTree = ""; }; @@ -1715,7 +1717,6 @@ CF1DE92C210A0F5C00A0A97E /* UILinphoneAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UILinphoneAudioPlayer.h; sourceTree = ""; }; CF7602D4210867E800749F76 /* RecordingsListView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RecordingsListView.h; sourceTree = ""; }; CF7602D5210867E800749F76 /* RecordingsListView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RecordingsListView.m; sourceTree = ""; }; - CF7602D6210867E800749F76 /* RecordingsListView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RecordingsListView.xib; sourceTree = ""; }; CF7602DF21086EB100749F76 /* RecordingsListTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RecordingsListTableView.h; sourceTree = ""; }; CF7602E021086EB200749F76 /* RecordingsListTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RecordingsListTableView.m; sourceTree = ""; }; CF7602E42108759A00749F76 /* UIRecordingCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UIRecordingCell.h; sourceTree = ""; }; @@ -2100,7 +2101,7 @@ CF7602E021086EB200749F76 /* RecordingsListTableView.m */, CF7602D4210867E800749F76 /* RecordingsListView.h */, CF7602D5210867E800749F76 /* RecordingsListView.m */, - CF7602D6210867E800749F76 /* RecordingsListView.xib */, + 617B4A62260A2B7800A87337 /* RecordingsListView.xib */, D35E759C159460B50066B1C1 /* SettingsView.h */, D35E759D159460B50066B1C1 /* SettingsView.m */, 636316D61A1DEC650009B839 /* SettingsView.xib */, @@ -3498,7 +3499,7 @@ 8CE24F4C1F8234A30077AC0A /* next_default@2x.png in Resources */, 244523B11E8266CC0037A187 /* chat_read.png in Resources */, 61AEBEC62191E47500F35E7F /* chevron_list_close.png in Resources */, - CF7602D8210867E800749F76 /* RecordingsListView.xib in Resources */, + 617B4A60260A2B7800A87337 /* RecordingsListView.xib in Resources */, 639E9CAC1C0DB80300019A75 /* UIContactDetailsCell.xib in Resources */, 633FEE511D3CD5590014B822 /* deselect_all@2x.png in Resources */, 8CF25D951F9F336100BEA0C1 /* check_unselected@2x.png in Resources */, @@ -4359,6 +4360,15 @@ name = ShopView.xib; sourceTree = ""; }; + 617B4A62260A2B7800A87337 /* RecordingsListView.xib */ = { + isa = PBXVariantGroup; + children = ( + 617B4A61260A2B7800A87337 /* Base */, + 617B4A64260A2B8500A87337 /* fr */, + ); + name = RecordingsListView.xib; + sourceTree = ""; + }; 61AE365020C00B370089D9D3 /* MainInterface.storyboard */ = { isa = PBXVariantGroup; children = (