From 50950337a75b47f3b9cc31d98644c3493aa5afdf Mon Sep 17 00:00:00 2001 From: Benjamin Reis Date: Mon, 30 Jan 2017 15:04:05 +0100 Subject: [PATCH] Refactor of CallKit --- Classes/LinphoneManager.h | 3 +- Classes/LinphoneManager.m | 38 +++++----- Classes/LinphoneUI/UIPauseButton.m | 81 +++----------------- Classes/PhoneMainView.m | 58 +++++++-------- Classes/ProviderDelegate.h | 4 + Classes/ProviderDelegate.m | 107 +++++++++++++++------------ Classes/Utils/FileTransferDelegate.m | 2 +- 7 files changed, 122 insertions(+), 171 deletions(-) diff --git a/Classes/LinphoneManager.h b/Classes/LinphoneManager.h index 614dddc5d..d619c6939 100644 --- a/Classes/LinphoneManager.h +++ b/Classes/LinphoneManager.h @@ -158,7 +158,8 @@ typedef struct _LinphoneManagerSounds { + (NSString*)cacheDirectory; - (void)acceptCall:(LinphoneCall *)call evenWithVideo:(BOOL)video; -- (BOOL)call:(const LinphoneAddress *)address; +- (void)call:(const LinphoneAddress *)address; +- (BOOL)doCall:(const LinphoneAddress *)iaddr; +(id)getMessageAppDataForKey:(NSString*)key inMessage:(LinphoneChatMessage*)msg; +(void)setValueInMessageAppData:(id)value forKey:(NSString*)key inMessage:(LinphoneChatMessage*)msg; diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m index b49c18cca..87bfe5172 100644 --- a/Classes/LinphoneManager.m +++ b/Classes/LinphoneManager.m @@ -237,7 +237,6 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre - (id)init { if ((self = [super init])) { - AudioSessionInitialize(NULL, NULL, NULL, NULL); [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(audioRouteChangeListenerCallback:) name:AVAudioSessionRouteChangeNotification @@ -2262,22 +2261,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { LinphoneCall *c = linphone_core_get_current_call(theLinphoneCore); LOGI(@"Sound interruption detected!"); if (c && linphone_call_get_state(c) == LinphoneCallStreamsRunning) { - if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { - NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids - objectForKey:[NSString - stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(c))]]; - if (!uuid) { - linphone_core_pause_call(theLinphoneCore, c); - return; - } - CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:YES]; - CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; - [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr - completion:^(NSError *err){ - }]; - } else { - linphone_core_pause_call(theLinphoneCore, c); - } + linphone_core_pause_call(theLinphoneCore, c); } } @@ -2452,7 +2436,25 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) { linphone_core_accept_call_with_params(theLinphoneCore, call, lcallParams); } -- (BOOL)call:(const LinphoneAddress *)iaddr { +- (void)call:(const LinphoneAddress *)iaddr { + if (linphone_core_get_calls_nb(theLinphoneCore) < 1 && + floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { + NSUUID *uuid = [NSUUID UUID]; + [LinphoneManager.instance.providerDelegate.uuids setObject:uuid forKey:@""]; + LinphoneManager.instance.providerDelegate.pendingAddr = linphone_address_clone(iaddr); + NSString *address = [FastAddressBook displayNameForAddress:iaddr]; + CXHandle *handle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:address]; + CXStartCallAction *act = [[CXStartCallAction alloc] initWithCallUUID:uuid handle:handle]; + CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; + [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr + completion:^(NSError *err){ + }]; + } else { + [self doCall:iaddr]; + } +} + +- (BOOL)doCall:(const LinphoneAddress *)iaddr { // First verify that network is available, abort otherwise. if (!linphone_core_is_network_reachable(theLinphoneCore)) { UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Network Error", nil) diff --git a/Classes/LinphoneUI/UIPauseButton.m b/Classes/LinphoneUI/UIPauseButton.m index 9ca7b0689..9e0254c3c 100644 --- a/Classes/LinphoneUI/UIPauseButton.m +++ b/Classes/LinphoneUI/UIPauseButton.m @@ -82,22 +82,7 @@ switch (type) { case UIPauseButtonType_Call: { if (call != nil) { - if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { - NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids - objectForKey:[NSString stringWithUTF8String:linphone_call_log_get_call_id( - linphone_call_get_call_log(call))]]; - if (!uuid) { - linphone_core_pause_call(LC, call); - return; - } - CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:YES]; - CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; - [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr - completion:^(NSError *err){ - }]; - } else { - linphone_core_pause_call(LC, call); - } + linphone_core_pause_call(LC, call); } else { LOGW(@"Cannot toggle pause buttton, because no current call"); } @@ -113,22 +98,7 @@ case UIPauseButtonType_CurrentCall: { LinphoneCall *currentCall = [UIPauseButton getCall]; if (currentCall != nil) { - if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { - NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids - objectForKey:[NSString stringWithUTF8String:linphone_call_log_get_call_id( - linphone_call_get_call_log(currentCall))]]; - if (!uuid) { - linphone_core_pause_call(LC, currentCall); - return; - } - CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:YES]; - CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; - [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr - completion:^(NSError *err){ - }]; - } else { - linphone_core_pause_call(LC, currentCall); - } + linphone_core_pause_call(LC, currentCall); } else { LOGW(@"Cannot toggle pause buttton, because no current call"); } @@ -141,22 +111,7 @@ switch (type) { case UIPauseButtonType_Call: { if (call != nil) { - if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { - NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids - objectForKey:[NSString stringWithUTF8String:linphone_call_log_get_call_id( - linphone_call_get_call_log(call))]]; - if (!uuid) { - linphone_core_resume_call(LC, call); - return; - } - CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO]; - CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; - [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr - completion:^(NSError *err){ - }]; - } else { - linphone_core_resume_call(LC, call); - } + linphone_core_resume_call(LC, call); } else { LOGW(@"Cannot toggle pause buttton, because no current call"); } @@ -164,11 +119,9 @@ } case UIPauseButtonType_Conference: { if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { - NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids allValues].firstObject; + NSString *key = (NSString *)[LinphoneManager.instance.providerDelegate.uuids allKeys][0]; + NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids objectForKey:key]; if (!uuid) { - linphone_core_enter_conference(LC); - // Fake event - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; return; } CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO]; @@ -176,31 +129,15 @@ [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr completion:^(NSError *err){ }]; - } else { - linphone_core_enter_conference(LC); - // Fake event - [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; } + linphone_core_enter_conference(LC); + // Fake event + [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; break; } case UIPauseButtonType_CurrentCall: { LinphoneCall *currentCall = [UIPauseButton getCall]; - if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { - NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids - objectForKey:[NSString stringWithUTF8String:linphone_call_log_get_call_id( - linphone_call_get_call_log(currentCall))]]; - if (!uuid) { - linphone_core_resume_call(LC, currentCall); - return; - } - CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO]; - CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; - [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr - completion:^(NSError *err){ - }]; - } else { - linphone_core_resume_call(LC, currentCall); - } + linphone_core_resume_call(LC, currentCall); break; } } diff --git a/Classes/PhoneMainView.m b/Classes/PhoneMainView.m index 10f865e62..e32261420 100644 --- a/Classes/PhoneMainView.m +++ b/Classes/PhoneMainView.m @@ -19,7 +19,6 @@ #import #import - #import "LinphoneAppDelegate.h" #import "PhoneMainView.h" @@ -366,6 +365,7 @@ static RootViewManager *rootViewManagerInstance = nil; } case LinphoneCallError: { [self displayCallError:call message:message]; + break; } case LinphoneCallEnd: { const MSList *calls = linphone_core_get_calls(LC); @@ -376,23 +376,7 @@ static RootViewManager *rootViewManagerInstance = nil; [self popCurrentView]; } } else { - if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { - NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids - objectForKey:[NSString - stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log( - (LinphoneCall *)calls->data))]]; - if (!uuid) { - linphone_core_resume_call(LC, (LinphoneCall *)calls->data); - return; - } - CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO]; - CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; - [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr - completion:^(NSError *err){ - }]; - } else { - linphone_core_resume_call(LC, (LinphoneCall *)calls->data); - } + linphone_core_resume_call(LC, (LinphoneCall *)calls->data); [self changeCurrentView:CallView.compositeViewDescription]; } break; @@ -405,20 +389,17 @@ static RootViewManager *rootViewManagerInstance = nil; case LinphoneCallOutgoingProgress: { if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max && call && (linphone_core_get_calls_nb(LC) < 2)) { - // Create CallKit Call + // Link call ID to UUID NSString *callId = [NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))]; - NSUUID *uuid = [NSUUID UUID]; - [LinphoneManager.instance.providerDelegate.uuids setObject:uuid forKey:callId]; - [LinphoneManager.instance.providerDelegate.calls setObject:callId forKey:uuid]; - NSString *address = [FastAddressBook displayNameForAddress:linphone_call_get_remote_address(call)]; - CXHandle *handle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:address]; - CXStartCallAction *act = [[CXStartCallAction alloc] initWithCallUUID:uuid handle:handle]; - CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; - [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr - completion:^(NSError *err){ - }]; + NSUUID *uuid = [LinphoneManager.instance.providerDelegate.uuids objectForKey:@""]; + if (uuid) { + [LinphoneManager.instance.providerDelegate.uuids removeObjectForKey:@""]; + [LinphoneManager.instance.providerDelegate.uuids setObject:uuid forKey:callId]; + [LinphoneManager.instance.providerDelegate.calls setObject:callId forKey:uuid]; + } } + break; } case LinphoneCallOutgoingRinging: { if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max && call) { @@ -440,12 +421,29 @@ static RootViewManager *rootViewManagerInstance = nil; [LinphoneManager.instance.providerDelegate.provider reportCallWithUUID:uuid updated:update]; } } + break; } case LinphoneCallPaused: case LinphoneCallPausing: case LinphoneCallRefered: case LinphoneCallReleased: - case LinphoneCallResuming: + break; + case LinphoneCallResuming: { + if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max && call) { + NSUUID *uuid = (NSUUID *)[LinphoneManager.instance.providerDelegate.uuids + objectForKey:[NSString stringWithUTF8String:linphone_call_log_get_call_id( + linphone_call_get_call_log(call))]]; + if (!uuid) { + return; + } + CXSetHeldCallAction *act = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:NO]; + CXTransaction *tr = [[CXTransaction alloc] initWithAction:act]; + [LinphoneManager.instance.providerDelegate.controller requestTransaction:tr + completion:^(NSError *err){ + }]; + } + break; + } case LinphoneCallUpdating: break; } diff --git a/Classes/ProviderDelegate.h b/Classes/ProviderDelegate.h index 9652be9d2..d2a66344f 100644 --- a/Classes/ProviderDelegate.h +++ b/Classes/ProviderDelegate.h @@ -18,9 +18,13 @@ @property CXCallController *controller; @property NSMutableDictionary *calls; @property NSMutableDictionary *uuids; +@property LinphoneCall *pendingCall; +@property LinphoneAddress *pendingAddr; +@property BOOL pendingCallVideo; - (void)reportIncomingCallwithUUID:(NSUUID *)uuid handle:(NSString *)handle video:(BOOL)video; - (void)config; +- (void)configAudioSession:(AVAudioSession *)audioSession; @end #endif /* ProviderDelegate_h */ diff --git a/Classes/ProviderDelegate.m b/Classes/ProviderDelegate.m index edd8cf534..fd058527f 100644 --- a/Classes/ProviderDelegate.m +++ b/Classes/ProviderDelegate.m @@ -19,7 +19,9 @@ self = [super init]; self.calls = [[NSMutableDictionary alloc] init]; self.uuids = [[NSMutableDictionary alloc] init]; - + self.pendingCall = NULL; + self.pendingAddr = NULL; + self.pendingCallVideo = FALSE; CXCallController *callController = [[CXCallController alloc] initWithQueue:dispatch_get_main_queue()]; [callController.callObserver setDelegate:self queue:dispatch_get_main_queue()]; self.controller = callController; @@ -46,6 +48,13 @@ [self.provider setDelegate:self queue:dispatch_get_main_queue()]; } +- (void)configAudioSession:(AVAudioSession *)audioSession { + [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; + [audioSession setMode:AVAudioSessionModeVoiceChat error:nil]; + double sampleRate = 44100.0; + [audioSession setPreferredSampleRate:sampleRate error:nil]; +} + - (void)reportIncomingCallwithUUID:(NSUUID *)uuid handle:(NSString *)handle video:(BOOL)video { // Create update to describe the incoming call and caller CXCallUpdate *update = [[CXCallUpdate alloc] init]; @@ -56,36 +65,11 @@ update.supportsUngrouping = TRUE; update.hasVideo = video; - LOGD(@"configuring audio session"); - AVAudioSession *audioSession = [AVAudioSession sharedInstance]; - [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; - [audioSession setMode:AVAudioSessionModeVoiceChat error:nil]; - - double sampleRate = 44100.0; - [audioSession setPreferredSampleRate:sampleRate error:nil]; - - NSTimeInterval bufferDuration = .005; - [audioSession setPreferredIOBufferDuration:bufferDuration error:nil]; - LOGD(@"Activating audio session"); - [audioSession setActive:TRUE error:nil]; - // Report incoming call to system LOGD(@"CallKit: report new incoming call"); [self.provider reportNewIncomingCallWithUUID:uuid update:update completion:^(NSError *error) { - LOGD(@"configuring audio session"); - AVAudioSession *audioSession = [AVAudioSession sharedInstance]; - [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; - [audioSession setMode:AVAudioSessionModeVoiceChat error:nil]; - - double sampleRate = 44100.0; - [audioSession setPreferredSampleRate:sampleRate error:nil]; - - NSTimeInterval bufferDuration = .005; - [audioSession setPreferredIOBufferDuration:bufferDuration error:nil]; - LOGD(@"Activating audio session"); - [audioSession setActive:TRUE error:nil]; }]; } @@ -93,8 +77,9 @@ - (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action { LOGD(@"CallKit : Answering Call"); - NSUUID *uuid = action.callUUID; + [self configAudioSession:[AVAudioSession sharedInstance]]; [action fulfill]; + NSUUID *uuid = action.callUUID; NSString *callID = [self.calls objectForKey:uuid]; // first, make sure this callid is not already involved in a call LinphoneCall *call = [LinphoneManager.instance callByCallId:callID]; @@ -102,7 +87,8 @@ BOOL video = (!([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) && linphone_core_get_video_policy(LC)->automatically_accept && linphone_call_params_video_enabled(linphone_call_get_remote_params((LinphoneCall *)call))); - [LinphoneManager.instance acceptCall:(LinphoneCall *)call evenWithVideo:video]; + self.pendingCall = call; + self.pendingCallVideo = video; return; }; } @@ -110,19 +96,25 @@ - (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action { LOGD(@"CallKit : Starting Call"); // To restart Audio Unit - AVAudioSession *audioSession = [AVAudioSession sharedInstance]; - [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; - [audioSession setMode:AVAudioSessionModeVoiceChat error:nil]; - double sampleRate = 44100.0; - [audioSession setPreferredSampleRate:sampleRate error:nil]; - NSTimeInterval bufferDuration = .005; - [audioSession setPreferredIOBufferDuration:bufferDuration error:nil]; - [audioSession setActive:TRUE error:nil]; + [self configAudioSession:[AVAudioSession sharedInstance]]; [action fulfill]; + NSUUID *uuid = action.callUUID; + + NSString *callID = [self.calls objectForKey:uuid]; // first, make sure this callid is not already involved in a call + LinphoneCall *call; + if (![callID isEqualToString:@""]) { + call = linphone_core_get_current_call(LC); + } else { + call = [LinphoneManager.instance callByCallId:callID]; + } + if (call != NULL) { + _pendingCall = call; + } } - (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action { LOGD(@"CallKit : Ending the Call"); + [action fulfill]; if (linphone_core_is_in_conference(LC)) { LinphoneManager.instance.conf = TRUE; linphone_core_terminate_conference(LC); @@ -137,7 +129,6 @@ linphone_core_terminate_call(LC, (LinphoneCall *)call); } } - [action fulfill]; } - (void)provider:(CXProvider *)provider performSetMutedCallAction:(nonnull CXSetMutedCallAction *)action { @@ -150,17 +141,7 @@ - (void)provider:(CXProvider *)provider performSetHeldCallAction:(nonnull CXSetHeldCallAction *)action { LOGD(@"CallKit : Call paused status changed"); - // To restart Audio Unit - AVAudioSession *audioSession = [AVAudioSession sharedInstance]; - [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; - [audioSession setMode:AVAudioSessionModeVoiceChat error:nil]; - double sampleRate = 44100.0; - [audioSession setPreferredSampleRate:sampleRate error:nil]; - NSTimeInterval bufferDuration = .005; - [audioSession setPreferredIOBufferDuration:bufferDuration error:nil]; - [audioSession setActive:TRUE error:nil]; [action fulfill]; - if (linphone_core_is_in_conference(LC) && action.isOnHold) { linphone_core_leave_conference(LC); [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; @@ -183,19 +164,20 @@ if (action.isOnHold) { linphone_core_pause_call(LC, (LinphoneCall *)call); } else { + [self configAudioSession:[AVAudioSession sharedInstance]]; if (linphone_core_get_conference(LC)) { linphone_core_enter_conference(LC); [NSNotificationCenter.defaultCenter postNotificationName:kLinphoneCallUpdate object:self]; } else { - linphone_core_resume_call(LC, (LinphoneCall *)call); + _pendingCall = call; } } } } - (void)provider:(CXProvider *)provider performPlayDTMFCallAction:(CXPlayDTMFCallAction *)action { - [action fulfill]; LOGD(@"CallKit : playing DTMF"); + [action fulfill]; NSUUID *call_uuid = action.callUUID; NSString *callID = [self.calls objectForKey:call_uuid]; LinphoneCall *call = [LinphoneManager.instance callByCallId:callID]; @@ -205,6 +187,33 @@ - (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession { LOGD(@"CallKit : Audio session activated"); + // Now we can (re)start the call + if (_pendingCall) { + LinphoneCallState state = linphone_call_get_state(_pendingCall); + switch (state) { + case LinphoneCallIncomingReceived: + [LinphoneManager.instance acceptCall:(LinphoneCall *)_pendingCall evenWithVideo:_pendingCallVideo]; + break; + case LinphoneCallPaused: + linphone_core_resume_call(LC, (LinphoneCall *)_pendingCall); + break; + case LinphoneCallStreamsRunning: + // May happen when multiple calls + break; + default: + break; + } + } else { + if (_pendingAddr) { + [LinphoneManager.instance doCall:_pendingAddr]; + } else { + LOGE(@"CallKit : No pending call"); + } + } + + _pendingCall = NULL; + _pendingAddr = NULL; + _pendingCallVideo = FALSE; } - (void)provider:(CXProvider *)provider didDeactivateAudioSession:(nonnull AVAudioSession *)audioSession { diff --git a/Classes/Utils/FileTransferDelegate.m b/Classes/Utils/FileTransferDelegate.m index 5194dd72c..2e335c885 100644 --- a/Classes/Utils/FileTransferDelegate.m +++ b/Classes/Utils/FileTransferDelegate.m @@ -51,7 +51,7 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"File download error", nil) message:NSLocalizedString(@"Error while downloading the file.\n" - @"The file is probably emcrypted.\n" + @"The file is probably encrypted.\n" @"Please retry to download this file after activating LIME.", nil) preferredStyle:UIAlertControllerStyleAlert];