diff --git a/Classes/AppManager.swift b/Classes/AppManager.swift index 324222ac4..d1987e746 100644 --- a/Classes/AppManager.swift +++ b/Classes/AppManager.swift @@ -57,7 +57,8 @@ enum NetworkType: Int { let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true) var writablePath = paths[0] writablePath = writablePath.appending("/\(filePath)") - Log.directLog(BCTBX_LOG_MESSAGE, text: "file path is \(writablePath)") + let message:String = "file path is \(writablePath)" + Log.directLog(BCTBX_LOG_MESSAGE, text: message) return writablePath //file name is recording_contact-name_dayName-day-monthName-year-hour-minutes-seconds //The recording prefix is used to identify recordings in the cache directory. 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 1fd1e5928..894d20634 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 attributes:@{NSForegroundColorAttributeName : [UIColor systemGrayColor]}]; + [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 */ @@ -602,7 +630,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 { @@ -690,10 +718,6 @@ static UICompositeViewDescription *compositeDescription = nil; account_creator, inputEntry.length > 0 ? inputEntry.UTF8String : NULL, prefix.UTF8String); if (s != LinphoneAccountCreatorPhoneNumberStatusOk) { linphone_account_creator_set_phone_number(account_creator, NULL, NULL); - // if phone is empty and username is empty, this is wrong - if (linphone_account_creator_get_username(account_creator) == NULL) { - s = LinphoneAccountCreatorPhoneNumberStatusTooShort; - } } createPhone.errorLabel.text = [AssistantView errorForLinphoneAccountCreatorPhoneNumberStatus:s]; @@ -1654,6 +1678,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..847755544 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/Classes/Base.lproj/DialerView.xib b/Classes/Base.lproj/DialerView.xib index ddfac9acc..e71288e54 100644 --- a/Classes/Base.lproj/DialerView.xib +++ b/Classes/Base.lproj/DialerView.xib @@ -1,9 +1,10 @@ - + - + + @@ -45,7 +46,7 @@ - + @@ -70,7 +71,7 @@ - + @@ -238,7 +239,7 @@ - + @@ -287,7 +288,7 @@ - + @@ -338,5 +339,11 @@ + + + + + + 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/CallManager.swift b/Classes/CallManager.swift index 7cb376bc8..6f1162f6f 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,14 @@ import AVFoundation var referedFromCall: String? var referedToCall: String? var endCallkit: Bool = false + static var speaker_already_enabled : Bool = false + var globalState : GlobalState = .Off + var actionsToPerformOnceWhenCoreIsOn : [(()->Void)] = [] fileprivate override init() { providerDelegate = ProviderDelegate() callController = CXCallController() - manager = CoreManagerDelegate() } @objc static func instance() -> CallManager { @@ -64,7 +65,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? { @@ -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. @@ -331,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!") @@ -355,9 +344,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) { @@ -409,32 +395,32 @@ import AVFoundation } @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) { + @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 { $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 { @@ -445,15 +431,15 @@ class CoreManagerDelegate: CoreDelegate { } } - override func onCallStateChanged(lc: Core, call: Call, 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 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 + CallManager.speaker_already_enabled = false if (call.userData == nil) { let appData = CallAppData() @@ -507,7 +493,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 +519,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 +586,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/Classes/CallView.m b/Classes/CallView.m index 0cc1db6d7..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_destroy(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_destroy(params); + [CallManager.instance acceptVideoWithCall:call confirm:TRUE]; [videoDismissTimer invalidate]; videoDismissTimer = nil; } 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..67defd595 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; @@ -299,7 +303,7 @@ static UICompositeViewDescription *compositeDescription = nil; } cell.uri = _contacts[indexPath.row]; LinphoneAddress *addr = linphone_address_new(cell.uri.UTF8String); - cell.nameLabel.text = [FastAddressBook displayNameForAddress:addr]; + cell.nameLabel.text = (addr == nil? cell.uri : [FastAddressBook displayNameForAddress:addr]); [cell.avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:YES withRoundedRadius:YES]; cell.controllerView = self; if(![_admins containsObject:cell.uri]) { 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..2cb6664fb 100644 --- a/Classes/ChatConversationTableView.m +++ b/Classes/ChatConversationTableView.m @@ -193,12 +193,11 @@ static const int BASIC_EVENT_LIST=15; - (BOOL)isFirstIndexInTableView:(NSIndexPath *)indexPath chat:(LinphoneChatMessage *)chat { LinphoneEventLog *previousEvent = nil; NSInteger indexOfPreviousEvent = indexPath.row - 1; - while (!previousEvent && indexOfPreviousEvent > -1) { - LinphoneEventLog *tmp = [[eventList objectAtIndex:indexOfPreviousEvent] pointerValue]; - if (linphone_event_log_get_type(tmp) == LinphoneEventLogTypeConferenceChatMessage) { - previousEvent = tmp; + if (indexOfPreviousEvent > -1) { + previousEvent = [[eventList objectAtIndex:indexOfPreviousEvent] pointerValue]; + if (linphone_event_log_get_type(previousEvent) != LinphoneEventLogTypeConferenceChatMessage) { + return TRUE; } - --indexOfPreviousEvent; } if (!previousEvent) return TRUE; @@ -256,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]; @@ -363,9 +362,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..3e4532df0 100644 --- a/Classes/ChatConversationView.h +++ b/Classes/ChatConversationView.h @@ -92,6 +92,11 @@ + (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)writeMediaToGallery:(NSString *)name fileType:(NSString *)fileType; - (void)configureForRoom:(BOOL)editing; - (IBAction)onBackClick:(id)event; diff --git a/Classes/ChatConversationView.m b/Classes/ChatConversationView.m index f32ba2d11..51b0ab6af 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); @@ -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"]; } } @@ -378,6 +362,10 @@ static UICompositeViewDescription *compositeDescription = nil; } [self configureForRoom:self.editing]; if (_chatRoom && _markAsRead) { + if (IPAD) { + [VIEW(ChatsListView).tableController loadData]; + } + [ChatConversationView markAsRead:_chatRoom]; } _markAsRead = TRUE; @@ -413,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; @@ -424,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); @@ -470,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) @@ -479,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]; @@ -564,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; @@ -647,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]; @@ -757,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]; @@ -786,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 @@ -836,6 +814,88 @@ static UICompositeViewDescription *compositeDescription = nil; } } ++ (void)writeMediaToGallery:(NSString *)name fileType:(NSString *)fileType { + NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:filePath]) { + NSData* data = [NSData dataWithContentsOfFile:filePath]; + + // 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) { + ChatConversationView *view = VIEW(ChatConversationView); + [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]); + 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]); + } + }); + }]; + } else if([fileType isEqualToString:@"video"]) { + __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]); + 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]); + } + }); + }]; + } + }; + + // 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 ([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]; + } + }); + }]; + } + } +} -(void) writeVideoToGallery:(NSURL *)url { NSString *localIdentifier; @@ -1110,7 +1170,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]; @@ -1175,6 +1235,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]]; @@ -1320,118 +1397,30 @@ 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"; +} + +/* There are three cases: auto download in foreground, auto download in background, on click download*/ + (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]; + if ([ConfigManager.instance lpConfigBoolForKeyWithKey:@"auto_write_to_gallery_preference"]) { + [ChatConversationView writeMediaToGallery:name fileType:fileType]; } - } + }); } -(void) documentMenu:(UIDocumentMenuViewController *)documentMenu didPickDocumentPicker:(UIDocumentPickerViewController *)documentPicker { diff --git a/Classes/LinphoneAppDelegate.m b/Classes/LinphoneAppDelegate.m index 627d7d8d7..ed15cb9fc 100644 --- a/Classes/LinphoneAppDelegate.m +++ b/Classes/LinphoneAppDelegate.m @@ -66,6 +66,12 @@ 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]; + } else if (PhoneMainView.instance.currentView == RecordingsListView.compositeViewDescription || PhoneMainView.instance.currentView == DevicesListView.compositeViewDescription) { + // To avoid crash + [PhoneMainView.instance changeCurrentView:DialerView.compositeViewDescription]; } [CoreManager.instance stopLinphoneCore]; } @@ -649,21 +655,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_destroy(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_destroy(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); @@ -694,6 +694,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 @@ -703,21 +704,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_destroy(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_destroy(params); + [CallManager.instance acceptVideoWithCall:call confirm:TRUE]; [videoDismissTimer invalidate]; } inController:PhoneMainView.instance]; diff --git a/Classes/LinphoneCoreSettingsStore.m b/Classes/LinphoneCoreSettingsStore.m index 37566f274..2c3578c0d 100644 --- a/Classes/LinphoneCoreSettingsStore.m +++ b/Classes/LinphoneCoreSettingsStore.m @@ -338,7 +338,8 @@ [self setCString:linphone_core_get_file_transfer_server(LC) forKey:@"file_transfer_server_url_preference"]; int maxSize = linphone_core_get_max_size_for_auto_download_incoming_files(LC); [self setObject:maxSize==0 ? @"Always" : (maxSize==-1 ? @"Nerver" : @"Customize") forKey:@"auto_download_mode"]; - [self setInteger:maxSize forKey:@"auto_download_incoming_files_max_size"]; + [self setInteger:maxSize forKey:@"auto_download_incoming_files_max_size"]; + [self setBool:[lm lpConfigBoolForKey:@"auto_write_to_gallery_preference" withDefault:YES] forKey:@"auto_write_to_gallery_mode"]; } // network section @@ -547,11 +548,10 @@ 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]; - 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); @@ -636,7 +636,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); @@ -778,6 +780,7 @@ } linphone_core_set_max_size_for_auto_download_incoming_files(LC, maxSize); [lm lpConfigSetString:[self stringForKey:@"auto_download_mode"] forKey:@"auto_download_mode"]; + [lm lpConfigSetBool:[self boolForKey:@"auto_write_to_gallery_mode"] forKey:@"auto_write_to_gallery_preference"]; // network section BOOL edgeOpt = [self boolForKey:@"edge_opt_preference"]; diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m index 3fd69d5f5..17585fed7 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); } @@ -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 @@ -1365,6 +1365,8 @@ void popup_link_account_cb(LinphoneAccountCreator *creator, LinphoneAccountCreat if (theLinphoneCore != nil) { // just in case application terminate before linphone core initialization + // rare case, remove duplicated fileTransferDelegates to avoid crash + [_fileTransferDelegates setArray:[[NSSet setWithArray:_fileTransferDelegates] allObjects]]; for (FileTransferDelegate *ftd in _fileTransferDelegates) { [ftd stopAndDestroy]; } @@ -1682,7 +1684,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 +2017,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 +2072,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 +2086,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 +2096,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 +2110,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/Base.lproj/UIChatBubblePhotoCell.xib b/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib index bfc9f5d1b..da225bc9d 100644 --- a/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib +++ b/Classes/LinphoneUI/Base.lproj/UIChatBubblePhotoCell.xib @@ -1,11 +1,9 @@ - - - - + + - + @@ -30,6 +28,7 @@ + @@ -85,6 +84,29 @@ + + + + + + + + @@ -93,7 +115,7 @@ - - - - - + @@ -189,14 +189,19 @@ + + + + + - - + + - + 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/UIChatBubblePhotoCell.h b/Classes/LinphoneUI/UIChatBubblePhotoCell.h index 7bc03ab5c..69ab9281b 100644 --- a/Classes/LinphoneUI/UIChatBubblePhotoCell.h +++ b/Classes/LinphoneUI/UIChatBubblePhotoCell.h @@ -40,6 +40,7 @@ @property(strong, nonatomic) IBOutlet UITapGestureRecognizer *imageGestureRecognizer; @property (weak, nonatomic) IBOutlet UIButton *fileButton; @property (weak, nonatomic) IBOutlet UIView *fileView; +@property (strong, nonatomic) IBOutlet UILongPressGestureRecognizer *plusLongGestureRecognizer; - (void)setEvent:(LinphoneEventLog *)event; - (void)setChatMessage:(LinphoneChatMessage *)message; @@ -50,6 +51,7 @@ - (IBAction)onResendClick:(id)event; - (IBAction)onPlayClick:(id)sender; - (IBAction)onFileClick:(id)sender; +- (IBAction)onPlusClick:(id)sender; @end diff --git a/Classes/LinphoneUI/UIChatBubblePhotoCell.m b/Classes/LinphoneUI/UIChatBubblePhotoCell.m index 33ed9c745..3100c0d83 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; } @@ -71,31 +72,26 @@ - (void)setChatMessage:(LinphoneChatMessage *)amessage { _imageGestureRecognizer.enabled = NO; + _plusLongGestureRecognizer.enabled = NO; _messageImageView.image = nil; _finalImage.image = nil; _finalImage.hidden = TRUE; _fileTransferProgress.progress = 0; assetIsLoaded = FALSE; - [self disconnectFromFileDelegate]; + /* As the cell UI will be reset, fileTransDelegate need to be reconnected. Otherwise, the UIProgressView will not work */ + [self disconnectFromFileDelegate]; if (amessage) { - const LinphoneContent *c = linphone_chat_message_get_file_transfer_information(amessage); - if (c) { - const char *name = linphone_content_get_name(c); - for (FileTransferDelegate *aftd in [LinphoneManager.instance fileTransferDelegates]) { - if (linphone_chat_message_get_file_transfer_information(aftd.message) && - (linphone_chat_message_is_outgoing(aftd.message) == linphone_chat_message_is_outgoing(amessage)) && - strcmp(name, linphone_content_get_name( - linphone_chat_message_get_file_transfer_information(aftd.message))) == 0) { - LOGI(@"Chat message [%p] with file transfer delegate [%p], connecting to it!", amessage, aftd); - [self connectToFileDelegate:aftd]; - break; - } + 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 setChatMessage:amessage]; + [super setChatMessageForCbs:amessage]; } - (void) loadImageAsset:(PHAsset*) asset image:(UIImage *)image { @@ -106,6 +102,7 @@ _messageImageView.hidden = YES; _finalImage.hidden = NO; _fileView.hidden = YES; + _plusLongGestureRecognizer.enabled = YES; [self layoutSubviews]; }); } @@ -128,10 +125,13 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; }]; } -- (void) loadFileAsset { +- (void) loadFileAsset:(NSString *)name { + NSString *text = [NSString stringWithFormat:@"📎 %@",name]; + _fileName.text = text; dispatch_async(dispatch_get_main_queue(), ^{ _fileName.hidden = _fileView.hidden = _fileButton.hidden = NO; _imageGestureRecognizer.enabled = NO; + _plusLongGestureRecognizer.enabled = NO; }); } @@ -143,6 +143,7 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; [_messageImageView stopLoading]; _messageImageView.hidden = YES; _imageGestureRecognizer.enabled = YES; + _plusLongGestureRecognizer.enabled = YES; _finalImage.hidden = NO; [self layoutSubviews]; }); @@ -153,195 +154,194 @@ 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); - - 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]; - } - }); - }]; - } + LinphoneContent *fileContent = linphone_chat_message_get_file_transfer_information(self.message); + if (fileContent == nil) { + 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 { - 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); - } + _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]; - [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; - } 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; - } - } - else if (localVideo) { - if (_messageImageView.image == nil) { - [self loadFirstImage:localVideo type:PHAssetMediaTypeVideo]; - _imageGestureRecognizer.enabled = NO; - } - } - else if (localFile) { - if ([type isEqualToString:@"video"]) { - UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[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]]; - UIImage *image = [[UIImage alloc] initWithData:data]; + if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { + // already downloaded + if (!assetIsLoaded) { + assetIsLoaded = TRUE; + NSString *key = [ChatConversationView getKeyFromFileType:fileType fileName:fileName]; + if ([key isEqualToString:@"localimage"]) { + // we did not load the image yet, so start doing so + if (_messageImageView.image == nil) { + NSData* data = [NSData dataWithContentsOfFile:filePath]; + UIImage *image = [[UIImage alloc] initWithData:data]; + if (image) { [self loadImageAsset:nil image:image]; _imageGestureRecognizer.enabled = YES; } else { - NSString *text = [NSString stringWithFormat:@"📎 %@",localFile]; - _fileName.text = text; - [self loadFileAsset]; + // compability with other platforms + [self loadFileAsset:fileName]; + } + } + } else if ([key isEqualToString:@"localvideo"]) { + if (_messageImageView.image == nil) { + UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:fileName]]; + if (image) { + [self loadImageAsset:nil image:image]; + _imageGestureRecognizer.enabled = NO; + } else { + // compability with other platforms + [self loadFileAsset:fileName]; + } + } + } else if ([key isEqualToString:@"localfile"]) { + if ([fileType isEqualToString:@"video"]) { + UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:fileName]]; + [self loadImageAsset:nil image:image]; + _imageGestureRecognizer.enabled = NO; + } else if ([fileName hasSuffix:@"JPG"] || [fileName hasSuffix:@"PNG"] || [fileName hasSuffix:@"jpg"] || [fileName hasSuffix:@"png"]) { + NSData *data = [ChatConversationView getCacheFileData:fileName]; + UIImage *image = [[UIImage alloc] initWithData:data]; + [self loadImageAsset:nil image:image]; + _imageGestureRecognizer.enabled = YES; + } else { + [self loadFileAsset:fileName]; + } + } + + if (!(localImage || localVideo || localFile)) { + // If the file has been downloaded in background, save it in the folders and display it. + [LinphoneManager setValueInMessageAppData:fileName forKey:key inMessage:self.message]; + dispatch_async(dispatch_get_main_queue(), ^ { + if ([ConfigManager.instance lpConfigBoolForKeyWithKey:@"auto_write_to_gallery_preference"]) { + [ChatConversationView writeMediaToGallery:fileName fileType:fileType]; + } + }); + } + } + [self uploadingImage:fileType localFile:localFile]; + } else { + // support previous methode: + 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; + } 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; + + 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) { + if (_messageImageView.image == nil) { + [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) { + dispatch_async(dispatch_get_main_queue(), ^ { + NSURL *url = [VIEW(ChatConversationView) getICloudFileUrl:localFile]; + NSData *data = [NSData dataWithContentsOfURL:url]; + [ChatConversationView writeFileInCache:data name:localFile]; + }); + + if ([fileType isEqualToString:@"video"]) { + UIImage* image = [UIChatBubbleTextCell getImageFromVideoUrl:[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]]; + UIImage *image = [[UIImage alloc] initWithData:data]; + [self loadImageAsset:nil image:image]; + _imageGestureRecognizer.enabled = YES; + } else { + [self loadFileAsset:fileName]; + } } } } + [self uploadingImage:fileType localFile:localFile]; } - // we are uploading the image - if (_ftd.message != nil) { - _cancelButton.hidden = _fileTransferProgress.hidden = super.notDelivered ? YES : NO; - _downloadButton.hidden = YES; - _playButton.hidden = YES; - _fileName.hidden = _fileView.hidden = _fileButton.hidden =YES; - } else { - _cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = YES; - _playButton.hidden = ![type isEqualToString:@"video"]; - _fileName.hidden = _fileView.hidden = _fileButton.hidden = localFile ? NO : YES; - } - } + } +} + +- (void)uploadingImage:(NSString *)fileType localFile:(NSString *)localFile { + // we are uploading the image + if (_ftd.message != nil) { + _cancelButton.hidden = _fileTransferProgress.hidden = super.notDelivered ? YES : NO; + _downloadButton.hidden = YES; + _playButton.hidden = YES; + _fileName.hidden = _fileView.hidden = _fileButton.hidden =YES; + } else { + _cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = YES; + _playButton.hidden = ![fileType isEqualToString:@"video"]; + _fileName.hidden = _fileView.hidden = _fileButton.hidden = localFile ? NO : YES; + } } - (void)loadFirstImage:(NSString *)key type:(PHAssetMediaType)type { @@ -390,11 +390,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]; @@ -409,10 +427,34 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; }]; } +- (IBAction)onPlusClick:(id)sender { + UILongPressGestureRecognizer *gesture = (UILongPressGestureRecognizer *)sender; + if (gesture.state != UIGestureRecognizerStateBegan) { + // allow only one click once time + return; + } + DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:@""]; + dispatch_async(dispatch_get_main_queue(), ^{ + [sheet addButtonWithTitle:NSLocalizedString(@"Save to Gallery", nil) + block:^() { + LinphoneContent *content = linphone_chat_message_get_file_transfer_information(self.message); + NSString *name = [NSString stringWithUTF8String:linphone_content_get_name(content)]; + [ChatConversationView writeMediaToGallery:name fileType:[NSString stringWithUTF8String:linphone_content_get_type(content)?:""]]; + }]; + + [sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil]; + [sheet showInView:PhoneMainView.instance.view]; + }); +} + - (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]]; + } } @@ -445,17 +487,29 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; 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]; @@ -516,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 { @@ -527,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 3de3f1168..4522ff5b9 100644 --- a/Classes/LinphoneUI/UIChatBubbleTextCell.m +++ b/Classes/LinphoneUI/UIChatBubbleTextCell.m @@ -57,12 +57,13 @@ //[_imdmLabel addGestureRecognizer:resendRecognizer2]; //_imdmLabel.userInteractionEnabled = YES; + self.contentView.userInteractionEnabled = NO; return self; } - (void)dealloc { [self setEvent:NULL]; - [self setChatMessage:NULL]; + [self setChatMessageForCbs:NULL]; } #pragma mark - @@ -76,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; } @@ -275,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]; }); } } @@ -352,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) { @@ -403,6 +364,14 @@ static const CGFloat CELL_IMAGE_X_MARGIN = 100; return [self ViewHeightForMessageText:chat withWidth:width textForImdn:nil]; } ++ (CGSize)ViewHeightForFile:(int)width { + CGSize fileSize = CGSizeMake(230, 50); + CGSize 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; +} + + (CGSize)ViewHeightForMessageText:(LinphoneChatMessage *)chat withWidth:(int)width textForImdn:(NSString *)imdnText{ NSString *messageText = [UIChatBubbleTextCell TextMessageForChat:chat]; static UIFont *messageFont = nil; @@ -442,53 +411,75 @@ 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 { + return [self ViewHeightForFile: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; + 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]; + if (!image) { + return [self ViewHeightForFile:width]; + } + originalImageSize = image.size; + } else if (localVideo && [[NSFileManager defaultManager] fileExistsAtPath:[[LinphoneManager cacheDirectory] stringByAppendingPathComponent:localVideo]]) { + UIImage *image = [UIChatBubbleTextCell getImageFromVideoUrl:[ChatConversationView getCacheFileUrl:localVideo]]; + if (!image) { + return [self ViewHeightForFile:width]; + } + 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/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/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) \ ({ \ 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)") 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) { 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 944c4cb4b..48ec16a3c 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -43,189 +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 (offset == total) { + NSString *name = [NSString stringWithUTF8String: linphone_content_get_name(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]; - if (size == 0) { - LOGI(@"Transfer of %s (%d bytes): download finished", linphone_content_get_name(content), size); - assert([thiz.data length] == linphone_content_get_file_size(content)); - NSString *fileType = [NSString stringWithUTF8String:linphone_content_get_type(content)]; - ChatConversationView *view = VIEW(ChatConversationView); - void (^block)(void)= ^ { - if ([fileType isEqualToString:@"image"]) { - // we're finished, save the image and update the message - UIImage *image = [UIImage imageWithData:thiz.data]; - if (!image) { - [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 { - 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]; + dispatch_async(dispatch_get_main_queue(), ^{ + [LinphoneManager setValueInMessageAppData:name + forKey:key + inMessage:message]; + 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 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:@{ @@ -237,109 +89,85 @@ 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 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 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{ + if ([[LinphoneManager.instance fileTransferDelegates] containsObject:self]) { + LOGW(@"fileTransferDelegates has already added %p", self); + return; + } + [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)); + linphone_content_set_type(content, [type UTF8String]); + linphone_content_set_subtype(content, [subtype UTF8String]); + linphone_content_set_name(content, [name UTF8String]); + 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_progress_indication(linphone_chat_message_get_callbacks(_message), file_transfer_progress_indication_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 { + if ([[LinphoneManager.instance fileTransferDelegates] containsObject:self]) { + LOGW(@"fileTransferDelegates has already added %p", self); + return FALSE; + } [[LinphoneManager.instance fileTransferDelegates] addObject:self]; _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; } @@ -350,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); diff --git a/Classes/Utils/Log.m b/Classes/Utils/Log.m index 7e5aac6a3..b3cfba62f 100644 --- a/Classes/Utils/Log.m +++ b/Classes/Utils/Log.m @@ -63,7 +63,7 @@ } + (void)directLog:(OrtpLogLevel)level text:(NSString *)text { - bctbx_log(BCTBX_LOG_DOMAIN, level, "%s", text.cString); + bctbx_log(BCTBX_LOG_DOMAIN, level, "%s", [text cStringUsingEncoding:NSUTF8StringEncoding]); } #pragma mark - Logs Functions callbacks 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/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 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
diff --git a/Resources/en.lproj/Localizable.strings b/Resources/en.lproj/Localizable.strings index 13f93ebf2..6b66641c8 100644 Binary files a/Resources/en.lproj/Localizable.strings and b/Resources/en.lproj/Localizable.strings differ diff --git a/Resources/fr.lproj/Localizable.strings b/Resources/fr.lproj/Localizable.strings index 111352b45..b44a1e848 100644 Binary files a/Resources/fr.lproj/Localizable.strings and b/Resources/fr.lproj/Localizable.strings differ diff --git a/Settings/InAppSettings.bundle/Chat.plist b/Settings/InAppSettings.bundle/Chat.plist index dbc290ddb..ac258525b 100644 --- a/Settings/InAppSettings.bundle/Chat.plist +++ b/Settings/InAppSettings.bundle/Chat.plist @@ -26,6 +26,16 @@ Customize + + DefaultValue + + Key + auto_write_to_gallery_mode + Title + Auto write to gallery + Type + PSToggleSwitchSpecifier + Key auto_download_incoming_files_max_size diff --git a/Settings/InAppSettings.bundle/en.lproj/Call.strings b/Settings/InAppSettings.bundle/en.lproj/Call.strings index bd4eeece6..841da9c05 100644 Binary files a/Settings/InAppSettings.bundle/en.lproj/Call.strings and b/Settings/InAppSettings.bundle/en.lproj/Call.strings differ diff --git a/Settings/InAppSettings.bundle/en.lproj/Chat.strings b/Settings/InAppSettings.bundle/en.lproj/Chat.strings index 2a3697347..2aebbf99b 100644 Binary files a/Settings/InAppSettings.bundle/en.lproj/Chat.strings and b/Settings/InAppSettings.bundle/en.lproj/Chat.strings differ diff --git a/Settings/InAppSettings.bundle/en.lproj/Network.strings b/Settings/InAppSettings.bundle/en.lproj/Network.strings index f9eeebf57..3daa14822 100644 Binary files a/Settings/InAppSettings.bundle/en.lproj/Network.strings and b/Settings/InAppSettings.bundle/en.lproj/Network.strings differ diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj index 43a5eedcd..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 = ( @@ -4875,7 +4885,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 +4914,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 +5011,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 +5037,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 +5133,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 +5159,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 +5254,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 +5280,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 +5327,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 +5339,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 +5369,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 +5380,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 +5410,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 +5421,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 +5452,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 +5463,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 +5546,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 +5563,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 +5602,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 +5615,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 +5654,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 +5667,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 +5706,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 +5719,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 +5757,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 +5774,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 +5812,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 +5825,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 +5863,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 +5876,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 +5914,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 +5927,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)"; diff --git a/msgNotification/Utils.swift b/msgNotification/Utils.swift index 92c2bb056..4de533c19 100644 --- a/msgNotification/Utils.swift +++ b/msgNotification/Utils.swift @@ -30,45 +30,42 @@ 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) - 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 } } - override func onLogMessageWritten(logService: LoggingService, domain: String, lev: LogLevel, message: String) { - let level: String + 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..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() @@ -82,7 +80,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 +88,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi } } catch { - log.error(msg: "error: \(error)") + log.error(message: "error: \(error)") completion(.dismissAndForwardAction) } } @@ -120,65 +118,59 @@ 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) - room.sendChatMessage(msg: chatMsg) + chatMsg.addDelegate(delegate: self) + 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) - coreDelegate = LinphoneCoreManager() - lc!.addDelegate(delegate: coreDelegate) + lc!.addDelegate(delegate: self) 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") - 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(msg: ChatMessage, state: ChatMessage.State) { - log.message(msg: "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 + } + } } diff --git a/msgNotificationService/NotificationService.swift b/msgNotificationService/NotificationService.swift index b52a3761c..cbd4ff5ce 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,13 +83,15 @@ 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 { 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 } @@ -114,7 +116,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 +126,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 +176,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 +192,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 +203,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,17 +218,21 @@ 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)") - return displayName - } else { - NotificationService.log.message(msg: "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 }