diff --git a/Classes/ChatRoomTableViewController.h b/Classes/ChatRoomTableViewController.h
index 192ac77d6..afc1c75fa 100644
--- a/Classes/ChatRoomTableViewController.h
+++ b/Classes/ChatRoomTableViewController.h
@@ -23,7 +23,7 @@
@protocol ChatRoomDelegate
-- (BOOL)chatRoomStartImageDownload:(NSURL*)url userInfo:(id)userInfo;
+- (BOOL)chatRoomStartImageDownload:(LinphoneChatMessage*)msg;
- (BOOL)chatRoomStartImageUpload:(UIImage*)image url:(NSURL*)url;
- (void)resendChat:(NSString*)message withExternalUrl:(NSString*)url;
diff --git a/Classes/ChatRoomViewController.h b/Classes/ChatRoomViewController.h
index f543f1868..ef59ea284 100644
--- a/Classes/ChatRoomViewController.h
+++ b/Classes/ChatRoomViewController.h
@@ -29,7 +29,7 @@
#include "linphone/linphonecore.h"
-@interface ChatRoomViewController : UIViewController {
+@interface ChatRoomViewController : UIViewController {
LinphoneChatRoom *chatRoom;
ImageSharing *imageSharing;
OrderedDictionary *imageQualities;
diff --git a/Classes/ChatRoomViewController.m b/Classes/ChatRoomViewController.m
index 53caf7475..4c46cd65b 100644
--- a/Classes/ChatRoomViewController.m
+++ b/Classes/ChatRoomViewController.m
@@ -21,12 +21,20 @@
#import "PhoneMainView.h"
#import "DTActionSheet.h"
#import "UILinphone.h"
+//#import "UIAlertView+Blocks.h"
+#import "DTAlertView.h"
#import
#import
#import "Utils.h"
-@implementation ChatRoomViewController
+@implementation ChatRoomViewController {
+ /* Message transfer transient storage */
+ NSData* upload_data;
+ size_t upload_bytes_sent;
+
+ NSMutableData* download_data;
+}
@synthesize tableController;
@synthesize sendButton;
@@ -64,6 +72,10 @@
[NSNumber numberWithFloat:0.5], NSLocalizedString(@"Average", nil),
[NSNumber numberWithFloat:0.0], NSLocalizedString(@"Minimum", nil), nil];
self->composingVisible = TRUE;
+
+ self->upload_data = nil;
+ self->upload_bytes_sent = 0;
+ self->download_data = nil;
}
return self;
}
@@ -173,8 +185,8 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
- if(imageSharing) {
- [imageSharing cancel];
+ if(upload_data || download_data ) {
+ // TODO: when the API permits it, we should cancel the transfer.
}
[messageField resignFirstResponder];
@@ -211,7 +223,6 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
-
}
-(void)didReceiveMemoryWarning {
@@ -317,34 +328,7 @@ static void message_status(LinphoneChatMessage* msg,LinphoneChatMessageState sta
}
- (void)saveAndSend:(UIImage*)image url:(NSURL*)url {
- if(url == nil) {
- [waitView setHidden:FALSE];
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- [[LinphoneManager instance].photoLibrary
- writeImageToSavedPhotosAlbum:image.CGImage
- orientation:(ALAssetOrientation)[image imageOrientation]
- completionBlock:^(NSURL *assetURL, NSError *error){
- dispatch_async(dispatch_get_main_queue(), ^{
- [waitView setHidden:TRUE];
- if (error) {
- LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
-
- UIAlertView* errorAlert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Transfer error", nil)
- message:NSLocalizedString(@"Cannot write image to photo library", nil)
- delegate:nil
- cancelButtonTitle:NSLocalizedString(@"Ok",nil)
- otherButtonTitles:nil ,nil];
- [errorAlert show];
- return;
- }
- LOGI(@"Image saved to [%@]", [assetURL absoluteString]);
- [self chatRoomStartImageUpload:image url:assetURL];
- });
- }];
- });
- } else {
- [self chatRoomStartImageUpload:image url:url];
- }
+ [self chatRoomStartImageUpload:image url:url];
}
- (void)chooseImageQuality:(UIImage*)image url:(NSURL*)url {
@@ -498,8 +482,20 @@ static void message_status(LinphoneChatMessage* msg,LinphoneChatMessageState sta
#pragma mark - Action Functions
- (IBAction)onBackClick:(id)event {
- [self.tableController setChatRoom:NULL];
- [[PhoneMainView instance] popCurrentView];
+ if( upload_data != nil || download_data != nil ){
+ DTAlertView *alertView = [[DTAlertView alloc] initWithTitle:NSLocalizedString(@"Cancel transfer?", nil)
+ message:NSLocalizedString(@"You have a transfer in progress, leaving this view will cancel it. Are you sure?", nil)];
+ [alertView addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil)
+ block:nil];
+ [alertView addButtonWithTitle:NSLocalizedString(@"Yes", nil)
+ block:^{
+ [self.tableController setChatRoom:NULL];
+ [[PhoneMainView instance] popCurrentView];
+ }];
+ } else {
+ [self.tableController setChatRoom:NULL];
+ [[PhoneMainView instance] popCurrentView];
+ }
}
- (IBAction)onEditClick:(id)event {
@@ -586,20 +582,48 @@ static void message_status(LinphoneChatMessage* msg,LinphoneChatMessageState sta
#pragma mark ChatRoomDelegate
-- (BOOL)chatRoomStartImageDownload:(NSURL*)url userInfo:(id)userInfo {
- if(imageSharing == nil) {
- imageSharing = [ImageSharing newImageSharingDownload:url delegate:self userInfo:userInfo];
+- (BOOL)chatRoomStartImageDownload:(LinphoneChatMessage*)msg {
+ if(self->download_data == nil) {
+ const char* url = linphone_chat_message_get_external_body_url(msg);
+ LOGI(@"Content to download: %s", url);
+
+ if( url == nil ) return FALSE;
+
+ download_data = [[NSMutableData alloc] init];
+
+ linphone_chat_message_set_user_data(msg, (void*)CFBridgingRetain(self));
+ linphone_chat_message_start_file_download(msg,NULL,NULL);
+
[messageView setHidden:TRUE];
- [transferView setHidden:FALSE];
- return TRUE;
- }
- return FALSE;
+ [transferView setHidden:FALSE];
+ return TRUE;
+ }
+ return FALSE;
}
- (BOOL)chatRoomStartImageUpload:(UIImage*)image url:(NSURL*)url{
- if(imageSharing == nil) {
- NSString *urlString = [[LinphoneManager instance] lpConfigStringForKey:@"sharing_server_preference"];
- imageSharing = [ImageSharing newImageSharingUpload:[NSURL URLWithString:urlString] image:image delegate:self userInfo:url];
+ if( self->upload_data == nil) {
+ LinphoneContent* content = linphone_core_create_content(linphone_chat_room_get_lc(chatRoom));
+ self->upload_data = UIImageJPEGRepresentation(image, 1.0);
+ linphone_content_set_type(content, "image");
+ linphone_content_set_subtype(content, "jpeg");
+ linphone_content_set_name(content, [[NSString stringWithFormat:@"%li-%f.jpg", (long)[image hash],[NSDate timeIntervalSinceReferenceDate]] UTF8String]);
+ linphone_content_set_size(content, [self->upload_data length]);
+
+ LinphoneChatMessage* message = linphone_chat_room_create_file_transfer_message(chatRoom, content);
+ linphone_chat_message_set_user_data(message, (void*)CFBridgingRetain(self));
+
+ if ( url ) {
+ // internal url is saved in the appdata for display and later save
+ [LinphoneManager setValueInMessageAppData:[url absoluteString] forKey:@"localimage" inMessage:message];
+ }
+
+ // TODO: in the user data, we should maybe put a standalone delegate alloced and retained, instead of self.
+ // This will make sure that when receiving a memory alert or go to another view, we still send the message
+ linphone_chat_room_send_message2(chatRoom, message, message_status, (void*)CFBridgingRetain(self));
+
+ [tableController addChatEntry:linphone_chat_message_ref(message)];
+ [tableController scrollToBottom:true];
[messageView setHidden:TRUE];
[transferView setHidden:FALSE];
return TRUE;
@@ -613,75 +637,12 @@ static void message_status(LinphoneChatMessage* msg,LinphoneChatMessageState sta
#pragma mark ImageSharingDelegate
-- (void)imageSharingProgress:(ImageSharing*)aimageSharing progress:(float)progress {
- [imageTransferProgressBar setProgress:progress];
-}
-
- (void)imageSharingAborted:(ImageSharing*)aimageSharing {
[messageView setHidden:FALSE];
[transferView setHidden:TRUE];
imageSharing = nil;
}
-- (void)imageSharingError:(ImageSharing*)aimageSharing error:(NSError *)error {
- [messageView setHidden:FALSE];
- [transferView setHidden:TRUE];
- NSString *url = [aimageSharing.connection.currentRequest.URL absoluteString];
- if (aimageSharing.upload) {
- LOGE(@"Cannot upload file to server [%@] because [%@]", url, [error localizedDescription]);
- UIAlertView* errorAlert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Transfer error", nil)
- message:NSLocalizedString(@"Cannot transfer file to remote contact", nil)
- delegate:nil
- cancelButtonTitle:NSLocalizedString(@"Ok",nil)
- otherButtonTitles:nil ,nil];
- [errorAlert show];
- } else {
- LOGE(@"Cannot download file from [%@] because [%@]", url, [error localizedDescription]);
- UIAlertView* errorAlert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Transfer error", nil)
- message:NSLocalizedString(@"Cannot transfer file from remote contact", nil)
- delegate:nil
- cancelButtonTitle:NSLocalizedString(@"Continue", nil)
- otherButtonTitles:nil, nil];
- [errorAlert show];
- }
- imageSharing = nil;
-}
-
-- (void)imageSharingUploadDone:(ImageSharing*)aimageSharing url:(NSURL*)url{
- [self sendMessage:nil withExterlBodyUrl:url withInternalURL:[aimageSharing userInfo] ];
-
- [messageView setHidden:FALSE];
- [transferView setHidden:TRUE];
- imageSharing = nil;
-}
-
-- (void)imageSharingDownloadDone:(ImageSharing*)aimageSharing image:(UIImage *)image {
- [messageView setHidden:FALSE];
- [transferView setHidden:TRUE];
-
- __block LinphoneChatMessage *chat = (LinphoneChatMessage *)[(NSValue*)[imageSharing userInfo] pointerValue];
- [[LinphoneManager instance].photoLibrary writeImageToSavedPhotosAlbum:image.CGImage
- orientation:(ALAssetOrientation)[image imageOrientation]
- completionBlock:^(NSURL *assetURL, NSError *error){
- if (error) {
- LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
-
- UIAlertView* errorAlert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Transfer error", nil)
- message:NSLocalizedString(@"Cannot write image to photo library", nil)
- delegate:nil
- cancelButtonTitle:NSLocalizedString(@"Ok",nil)
- otherButtonTitles:nil ,nil];
- [errorAlert show];
- return;
- }
- LOGI(@"Image saved to [%@]", [assetURL absoluteString]);
- [LinphoneManager setValueInMessageAppData:[assetURL absoluteString] forKey:@"localimage" inMessage:chat];
- [tableController updateChatEntry:chat];
- }];
- imageSharing = nil;
-}
-
-
#pragma mark ImagePickerDelegate
- (void)imagePickerDelegateImage:(UIImage*)image info:(NSDictionary *)info {
@@ -698,6 +659,93 @@ static void message_status(LinphoneChatMessage* msg,LinphoneChatMessageState sta
[self chooseImageQuality:image url:url];
}
+#pragma mark - LinphoneChatContentTransferDelegate
+
+- (void)onProgressReport:(LinphoneChatMessage *)msg forContent:(const LinphoneContent *)content percent:(int)percent {
+ [imageTransferProgressBar setProgress:percent/100.0f];
+}
+
+- (void)onDataRequested:(LinphoneChatMessage *)msg forContent:(const LinphoneContent *)content buffer:(char *)buffer withSize:(size_t *)size {
+ if( self->upload_data ){
+ size_t to_send = *size;
+ size_t remaining = [upload_data length] - self->upload_bytes_sent;
+
+ LOGI(@"Asking %ld bytes, sent %ld of %d, %ld remaining", to_send, self->upload_bytes_sent, [upload_data length], remaining);
+
+ if( remaining < to_send ) to_send = remaining;
+
+ @try {
+ [upload_data getBytes:(void*)buffer range:NSMakeRange(upload_bytes_sent, to_send)];
+ upload_bytes_sent += to_send;
+ *size = to_send;
+ }
+ @catch (NSException *exception) {
+ LOGE(@"Exception: %@", exception);
+ }
+
+
+ if( to_send == 0 || upload_bytes_sent == [upload_data length] ){
+ LOGI(@"Upload finished, cleanup..");
+ upload_data = nil;
+ upload_bytes_sent = 0;
+
+ // update UI
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [messageView setHidden:FALSE];
+ [transferView setHidden:TRUE];
+ });
+ }
+
+ } else {
+ LOGE(@"Error: no upload data in progress!");
+ }
+}
+
+- (void)onDataReceived:(LinphoneChatMessage *)msg forContent:(const LinphoneContent *)content buffer:(const char *)buffer withSize:(size_t)size {
+ if( download_data ){
+ LOGI(@"Receiving data for %s : %zu bytes, already got %d of %zu", linphone_content_get_name(content), size, [download_data length], linphone_content_get_size(content));
+
+ if( size != 0 ){
+ [download_data appendBytes:buffer length:size];
+ }
+
+ if( size == 0 && [download_data length] == linphone_content_get_size(content)){
+
+ LOGI(@"Transfer is finished, save image and update chat");
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ //we're finished, save the image and update the message
+ [messageView setHidden:FALSE];
+ [transferView setHidden:TRUE];
+
+ UIImage* image = [UIImage imageWithData:download_data];
+
+ download_data = nil;
+
+ [[LinphoneManager instance].photoLibrary
+ writeImageToSavedPhotosAlbum:image.CGImage
+ orientation:(ALAssetOrientation)[image imageOrientation]
+ completionBlock:^(NSURL *assetURL, NSError *error){
+ if (error) {
+ LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
+
+ UIAlertView* errorAlert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Transfer error", nil)
+ message:NSLocalizedString(@"Cannot write image to photo library", nil)
+ delegate:nil
+ cancelButtonTitle:NSLocalizedString(@"Ok",nil)
+ otherButtonTitles:nil ,nil];
+ [errorAlert show];
+ return;
+ }
+ LOGI(@"Image saved to [%@]", [assetURL absoluteString]);
+ [LinphoneManager setValueInMessageAppData:[assetURL absoluteString] forKey:@"localimage" inMessage:msg];
+ [tableController updateChatEntry:msg];
+ }];
+
+ });
+ }
+ }
+}
#pragma mark - Keyboard Event Functions
diff --git a/Classes/HistoryDetailsViewController.m b/Classes/HistoryDetailsViewController.m
index fd0d0c187..0872ae64f 100644
--- a/Classes/HistoryDetailsViewController.m
+++ b/Classes/HistoryDetailsViewController.m
@@ -369,7 +369,6 @@ static UICompositeViewDescription *compositeDescription = nil;
char* lAddress = linphone_address_as_string_uri_only(addr);
if(lAddress == NULL)
return;
-
// Go to ChatRoom view
[[PhoneMainView instance] changeCurrentView:[ChatViewController compositeViewDescription]];
ChatRoomViewController *controller = DYNAMIC_CAST([[PhoneMainView instance] changeCurrentView:[ChatRoomViewController compositeViewDescription] push:TRUE], ChatRoomViewController);
diff --git a/Classes/ImageSharing.m b/Classes/ImageSharing.m
index 6fb88bfd0..3ebee1b80 100644
--- a/Classes/ImageSharing.m
+++ b/Classes/ImageSharing.m
@@ -86,7 +86,6 @@
- (void)uploadImageTo:(NSURL*)url image:(UIImage*)image {
LOGI(@"downloading [%@]", [url absoluteString]);
-
// setting up the request object now
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
diff --git a/Classes/LinphoneManager.h b/Classes/LinphoneManager.h
index 7daa5e605..59dba725e 100644
--- a/Classes/LinphoneManager.h
+++ b/Classes/LinphoneManager.h
@@ -98,6 +98,14 @@ struct NetworkReachabilityContext {
};
@end
+@protocol LinphoneChatContentTransferDelegate
+
+-(void)onProgressReport:(LinphoneChatMessage*)msg forContent:(const LinphoneContent*)content percent:(int)percent;
+-(void)onDataRequested:(LinphoneChatMessage*)msg forContent:(const LinphoneContent*)content buffer:(char*)buffer withSize:(size_t*)size;
+-(void)onDataReceived:(LinphoneChatMessage*)msg forContent:(const LinphoneContent*)content buffer:(const char*)buffer withSize:(size_t)size;
+
+@end
+
typedef struct _LinphoneManagerSounds {
SystemSoundID vibrate;
} LinphoneManagerSounds;
diff --git a/Classes/LinphoneManager.m b/Classes/LinphoneManager.m
index fb440ffb9..e2e93603e 100644
--- a/Classes/LinphoneManager.m
+++ b/Classes/LinphoneManager.m
@@ -145,10 +145,10 @@ struct codec_name_pref_table codec_pref_table[]={
{ "mp4v-es", 90000, "mp4v-es_preference"},
{ "h264", 90000, "h264_preference"},
{ "vp8", 90000, "vp8_preference"},
- { "mpeg4-generic", 16000, "aaceld_16k_preference"},
- { "mpeg4-generic", 22050, "aaceld_22k_preference"},
- { "mpeg4-generic", 32000, "aaceld_32k_preference"},
- { "mpeg4-generic", 44100, "aaceld_44k_preference"},
+ { "mpeg4-generic", 16000, "aaceld_16k_preference"},
+ { "mpeg4-generic", 22050, "aaceld_22k_preference"},
+ { "mpeg4-generic", 32000, "aaceld_32k_preference"},
+ { "mpeg4-generic", 44100, "aaceld_44k_preference"},
{ "mpeg4-generic", 48000, "aaceld_48k_preference"},
{ "opus", 48000, "opus_preference"},
{ NULL,0,Nil }
@@ -194,9 +194,9 @@ struct codec_name_pref_table codec_pref_table[]={
}
+ (BOOL)isRunningTests {
- NSDictionary *environment = [[NSProcessInfo processInfo] environment];
- NSString *injectBundle = environment[@"XCInjectBundle"];
- return [[injectBundle pathExtension] isEqualToString:@"xctest"];
+ NSDictionary *environment = [[NSProcessInfo processInfo] environment];
+ NSString *injectBundle = environment[@"XCInjectBundle"];
+ return [[injectBundle pathExtension] isEqualToString:@"xctest"];
}
+ (BOOL)isNotIphone3G
@@ -244,12 +244,12 @@ struct codec_name_pref_table codec_pref_table[]={
#endif
+ (BOOL)langageDirectionIsRTL {
- static NSLocaleLanguageDirection dir = NSLocaleLanguageDirectionLeftToRight;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- dir = [NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]];
- });
- return dir == NSLocaleLanguageDirectionRightToLeft;
+ static NSLocaleLanguageDirection dir = NSLocaleLanguageDirectionLeftToRight;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ dir = [NSLocale characterDirectionForLanguage:[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]];
+ });
+ return dir == NSLocaleLanguageDirectionRightToLeft;
}
#pragma mark - Lifecycle Functions
@@ -262,12 +262,12 @@ struct codec_name_pref_table codec_pref_table[]={
LOGE(@"cannot register route change handler [%ld]",lStatus);
}
- NSString *path = [[NSBundle mainBundle] pathForResource:@"msg" ofType:@"wav"];
- self.messagePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:path] error:nil];
+ NSString *path = [[NSBundle mainBundle] pathForResource:@"msg" ofType:@"wav"];
+ self.messagePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:path] error:nil];
- sounds.vibrate = kSystemSoundID_Vibrate;
+ sounds.vibrate = kSystemSoundID_Vibrate;
- logs = [[NSMutableArray alloc] init];
+ logs = [[NSMutableArray alloc] init];
database = NULL;
speakerEnabled = FALSE;
bluetoothEnabled = FALSE;
@@ -543,9 +543,9 @@ static void linphone_iphone_log(struct _LinphoneCore * lc, const char * message)
- (void)displayStatus:(NSString*) message {
// Post event
- [[NSNotificationCenter defaultCenter] postNotificationName:kLinphoneDisplayStatusUpdate
- object:self
- userInfo:@{@"message":message}];
+ [[NSNotificationCenter defaultCenter] postNotificationName:kLinphoneDisplayStatusUpdate
+ object:self
+ userInfo:@{@"message":message}];
}
@@ -639,14 +639,14 @@ static void linphone_iphone_display_status(struct _LinphoneCore * lc, const char
data->notification = [[UILocalNotification alloc] init];
if (data->notification) {
- // iOS8 doesn't need the timer trick for the local notification.
- if( [[UIDevice currentDevice].systemVersion floatValue] >= 8){
- data->notification.soundName = @"ring.caf";
- data->notification.category = @"incoming_call";
- } else {
- data->notification.soundName = @"shortring.caf";
- data->timer = [NSTimer scheduledTimerWithTimeInterval:4.0 target:self selector:@selector(localNotifContinue:) userInfo:data->notification repeats:TRUE];
- }
+ // iOS8 doesn't need the timer trick for the local notification.
+ if( [[UIDevice currentDevice].systemVersion floatValue] >= 8){
+ data->notification.soundName = @"ring.caf";
+ data->notification.category = @"incoming_call";
+ } else {
+ data->notification.soundName = @"shortring.caf";
+ data->timer = [NSTimer scheduledTimerWithTimeInterval:4.0 target:self selector:@selector(localNotifContinue:) userInfo:data->notification repeats:TRUE];
+ }
data->notification.repeatInterval = 0;
@@ -664,9 +664,9 @@ static void linphone_iphone_display_status(struct _LinphoneCore * lc, const char
incallBgTask=0;
}];
- if( data->timer ){
- [[NSRunLoop currentRunLoop] addTimer:data->timer forMode:NSRunLoopCommonModes];
- }
+ if( data->timer ){
+ [[NSRunLoop currentRunLoop] addTimer:data->timer forMode:NSRunLoopCommonModes];
+ }
}
}
@@ -740,9 +740,9 @@ static void linphone_iphone_display_status(struct _LinphoneCore * lc, const char
[self setupGSMInteraction];
}
// Post event
- NSDictionary* dict = @{@"call": [NSValue valueWithPointer:call],
- @"state": [NSNumber numberWithInt:state],
- @"message":[NSString stringWithUTF8String:message]};
+ NSDictionary* dict = @{@"call": [NSValue valueWithPointer:call],
+ @"state": [NSNumber numberWithInt:state],
+ @"message":[NSString stringWithUTF8String:message]};
[[NSNotificationCenter defaultCenter] postNotificationName:kLinphoneCallUpdate object:self userInfo:dict];
}
@@ -845,12 +845,12 @@ static void linphone_iphone_registration_state(LinphoneCore *lc, LinphoneProxyCo
silentPushCompletion(UIBackgroundFetchResultNewData);
silentPushCompletion = nil;
}
- const LinphoneAddress* remoteAddress = linphone_chat_message_get_from_address(msg);
- char* c_address = linphone_address_as_string_uri_only(remoteAddress);
- NSString* address = [NSString stringWithUTF8String:c_address];
- NSString* remote_uri = [NSString stringWithUTF8String:c_address];
- const char* call_id = linphone_chat_message_get_custom_header(msg, "Call-ID");
- NSString* callID = [NSString stringWithUTF8String:call_id];
+ const LinphoneAddress* remoteAddress = linphone_chat_message_get_from_address(msg);
+ char* c_address = linphone_address_as_string_uri_only(remoteAddress);
+ NSString* address = [NSString stringWithUTF8String:c_address];
+ NSString* remote_uri = [NSString stringWithUTF8String:c_address];
+ const char* call_id = linphone_chat_message_get_custom_header(msg, "Call-ID");
+ NSString* callID = [NSString stringWithUTF8String:call_id];
ms_free(c_address);
@@ -874,9 +874,9 @@ static void linphone_iphone_registration_state(LinphoneCore *lc, LinphoneProxyCo
UILocalNotification* notif = [[UILocalNotification alloc] init];
if (notif) {
notif.repeatInterval = 0;
- if( [[UIDevice currentDevice].systemVersion floatValue] >= 8){
- notif.category = @"incoming_msg";
- }
+ if( [[UIDevice currentDevice].systemVersion floatValue] >= 8){
+ notif.category = @"incoming_msg";
+ }
notif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"IM_MSG",nil), address];
notif.alertAction = NSLocalizedString(@"Show", nil);
notif.soundName = @"msg.caf";
@@ -915,6 +915,29 @@ static void linphone_iphone_notify_received(LinphoneCore *lc, LinphoneEvent *lev
[(__bridge LinphoneManager*)linphone_core_get_user_data(lc) onNotifyReceived:lc event:lev notifyEvent:notified_event content:body];
}
+#pragma mark - FileTransfer functions
+
+static void linphone_iphone_file_transfer_recv(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, const char* buff, size_t size) {
+ id delegate = (__bridge id)linphone_chat_message_get_user_data(message);
+ LOGI(@"Transfer of %s, incoming data (%d bytes)", linphone_content_get_name(content), size);
+ [delegate onDataReceived:message forContent:content buffer:buff withSize:size];
+}
+
+static void linphone_iphone_file_transfer_send(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, char* buff, size_t* size){
+ id delegate = (__bridge id)linphone_chat_message_get_user_data(message);
+ LOGI(@"Transfer of %s, requesting data (%d bytes)", linphone_content_get_name(content), *size);
+ [delegate onDataRequested:message forContent:content buffer:buff withSize:size];
+}
+
+static void linphone_iphone_file_transfer_progress(LinphoneCore *lc, LinphoneChatMessage *message, const LinphoneContent* content, size_t offset, size_t total){
+ id delegate = (__bridge id)linphone_chat_message_get_user_data(message);
+ float progress = offset*100.f/total;
+ LOGI(@"Progress of transfer %s: %d%%", linphone_content_get_name(content), progress);
+ [delegate onProgressReport:message forContent:content percent:progress];
+}
+
+
+
#pragma mark - Message composition start
- (void)onMessageComposeReceived:(LinphoneCore*)core forRoom:(LinphoneChatRoom*)room {
@@ -936,58 +959,58 @@ static void linphone_iphone_is_composing_received(LinphoneCore *lc, LinphoneChat
}
+ (void)kickOffNetworkConnection {
- static BOOL in_progress = FALSE;
- if( in_progress ){
- LOGW(@"Connection kickoff already in progress");
- return;
- }
- in_progress = TRUE;
+ static BOOL in_progress = FALSE;
+ if( in_progress ){
+ LOGW(@"Connection kickoff already in progress");
+ return;
+ }
+ in_progress = TRUE;
/* start a new thread to avoid blocking the main ui in case of peer host failure */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- static int sleep_us = 10000;
- static int timeout_s = 5;
- BOOL timeout_reached = FALSE;
- int loop = 0;
+ static int sleep_us = 10000;
+ static int timeout_s = 5;
+ BOOL timeout_reached = FALSE;
+ int loop = 0;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"192.168.0.200"/*"linphone.org"*/, 15000, nil, &writeStream);
BOOL res = CFWriteStreamOpen (writeStream);
const char* buff="hello";
- time_t start = time(NULL);
- time_t loop_time;
+ time_t start = time(NULL);
+ time_t loop_time;
- if( res == FALSE ){
- LOGI(@"Could not open write stream, backing off");
- CFRelease(writeStream);
- in_progress = FALSE;
- return;
- }
+ if( res == FALSE ){
+ LOGI(@"Could not open write stream, backing off");
+ CFRelease(writeStream);
+ in_progress = FALSE;
+ return;
+ }
- // check stream status and handle timeout
- CFStreamStatus status = CFWriteStreamGetStatus(writeStream);
- while (status != kCFStreamStatusOpen && status != kCFStreamStatusError ) {
- usleep(sleep_us);
- status = CFWriteStreamGetStatus(writeStream);
- loop_time = time(NULL);
- if( loop_time - start >= timeout_s){
- timeout_reached = TRUE;
- break;
- }
- loop++;
- }
+ // check stream status and handle timeout
+ CFStreamStatus status = CFWriteStreamGetStatus(writeStream);
+ while (status != kCFStreamStatusOpen && status != kCFStreamStatusError ) {
+ usleep(sleep_us);
+ status = CFWriteStreamGetStatus(writeStream);
+ loop_time = time(NULL);
+ if( loop_time - start >= timeout_s){
+ timeout_reached = TRUE;
+ break;
+ }
+ loop++;
+ }
- if (status == kCFStreamStatusOpen ) {
- CFWriteStreamWrite (writeStream,(const UInt8*)buff,strlen(buff));
- } else if( !timeout_reached ){
- CFErrorRef error = CFWriteStreamCopyError(writeStream);
- LOGD(@"CFStreamError: %@", error);
- CFRelease(error);
- } else if( timeout_reached ){
- LOGI(@"CFStream timeout reached");
- }
+ if (status == kCFStreamStatusOpen ) {
+ CFWriteStreamWrite (writeStream,(const UInt8*)buff,strlen(buff));
+ } else if( !timeout_reached ){
+ CFErrorRef error = CFWriteStreamCopyError(writeStream);
+ LOGD(@"CFStreamError: %@", error);
+ CFRelease(error);
+ } else if( timeout_reached ){
+ LOGI(@"CFStream timeout reached");
+ }
CFWriteStreamClose (writeStream);
CFRelease(writeStream);
- in_progress = FALSE;
+ in_progress = FALSE;
});
}
@@ -1185,7 +1208,7 @@ void networkReachabilityCallBack(SCNetworkReachabilityRef target, SCNetworkReach
}
}
-#pragma mark -
+#pragma mark - VTable
static LinphoneCoreVTable linphonec_vtable = {
.show =NULL,
@@ -1205,9 +1228,15 @@ static LinphoneCoreVTable linphonec_vtable = {
.is_composing_received = linphone_iphone_is_composing_received,
.configuring_status = linphone_iphone_configuring_status_changed,
.global_state_changed = linphone_iphone_global_state_changed,
- .notify_received = linphone_iphone_notify_received
+ .notify_received = linphone_iphone_notify_received,
+ .file_transfer_recv = linphone_iphone_file_transfer_recv,
+ .file_transfer_send = linphone_iphone_file_transfer_send,
+ .file_transfer_progress_indication = linphone_iphone_file_transfer_progress
+
};
+#pragma mark -
+
//scheduling loop
- (void)iterate {
linphone_core_iterate(theLinphoneCore);
@@ -1300,6 +1329,11 @@ static LinphoneCoreVTable linphonec_vtable = {
linphone_core_set_static_picture(theLinphoneCore, imagePath);
}
+ NSString *urlString = [self lpConfigStringForKey:@"sharing_server_preference"];
+ if( urlString ){
+ linphone_core_set_file_transfer_server(theLinphoneCore, [urlString UTF8String]);
+ }
+
/*DETECT cameras*/
frontCamId= backCamId=nil;
char** camlist = (char**)linphone_core_get_video_devices(theLinphoneCore);
@@ -1394,24 +1428,24 @@ static BOOL libStarted = FALSE;
connectivity=none;
- ms_init(); // Need to initialize mediastreamer2 before loading the plugins
+ ms_init(); // Need to initialize mediastreamer2 before loading the plugins
- libmsilbc_init();
+ libmsilbc_init();
#if defined (HAVE_SILK)
- libmssilk_init();
+ libmssilk_init();
#endif
#ifdef HAVE_AMR
- libmsamr_init(); //load amr plugin if present from the liblinphone sdk
+ libmsamr_init(); //load amr plugin if present from the liblinphone sdk
#endif
#ifdef HAVE_X264
- libmsx264_init(); //load x264 plugin if present from the liblinphone sdk
+ libmsx264_init(); //load x264 plugin if present from the liblinphone sdk
#endif
#ifdef HAVE_OPENH264
- libmsopenh264_init(); //load openh264 plugin if present from the liblinphone sdk
+ libmsopenh264_init(); //load openh264 plugin if present from the liblinphone sdk
#endif
#if HAVE_G729
- libmsbcg729_init(); // load g729 plugin
+ libmsbcg729_init(); // load g729 plugin
#endif
linphone_core_set_log_collection_path([[LinphoneManager cacheDirectory] UTF8String]);
@@ -1490,39 +1524,39 @@ static int comp_call_id(const LinphoneCall* call , const char *callid) {
}
- (void)cancelLocalNotifTimerForCallId:(NSString*)callid {
- //first, make sure this callid is not already involved in a call
- MSList* calls = (MSList*)linphone_core_get_calls(theLinphoneCore);
- MSList* call = ms_list_find_custom(calls, (MSCompareFunc)comp_call_id, [callid UTF8String]);
- if (call != NULL) {
- LinphoneCallAppData* data = (__bridge LinphoneCallAppData *)(linphone_call_get_user_data((LinphoneCall*)call->data));
- if ( data->timer )
- [data->timer invalidate];
- data->timer = nil;
- return;
- }
+ //first, make sure this callid is not already involved in a call
+ MSList* calls = (MSList*)linphone_core_get_calls(theLinphoneCore);
+ MSList* call = ms_list_find_custom(calls, (MSCompareFunc)comp_call_id, [callid UTF8String]);
+ if (call != NULL) {
+ LinphoneCallAppData* data = (__bridge LinphoneCallAppData *)(linphone_call_get_user_data((LinphoneCall*)call->data));
+ if ( data->timer )
+ [data->timer invalidate];
+ data->timer = nil;
+ return;
+ }
}
- (void)acceptCallForCallId:(NSString*)callid {
- //first, make sure this callid is not already involved in a call
- MSList* calls = (MSList*)linphone_core_get_calls(theLinphoneCore);
- MSList* call = ms_list_find_custom(calls, (MSCompareFunc)comp_call_id, [callid UTF8String]);
- if (call != NULL) {
- [self acceptCall:(LinphoneCall*)call->data];
- return;
- };
+ //first, make sure this callid is not already involved in a call
+ MSList* calls = (MSList*)linphone_core_get_calls(theLinphoneCore);
+ MSList* call = ms_list_find_custom(calls, (MSCompareFunc)comp_call_id, [callid UTF8String]);
+ if (call != NULL) {
+ [self acceptCall:(LinphoneCall*)call->data];
+ return;
+ };
}
- (void)addPushCallId:(NSString*) callid {
//first, make sure this callid is not already involved in a call
- MSList* calls = (MSList*)linphone_core_get_calls(theLinphoneCore);
- if (ms_list_find_custom(calls, (MSCompareFunc)comp_call_id, [callid UTF8String])) {
- LOGW(@"Call id [%@] already handled",callid);
- return;
- };
- if ([pushCallIDs count] > 10 /*max number of pending notif*/)
- [pushCallIDs removeObjectAtIndex:0];
+ MSList* calls = (MSList*)linphone_core_get_calls(theLinphoneCore);
+ if (ms_list_find_custom(calls, (MSCompareFunc)comp_call_id, [callid UTF8String])) {
+ LOGW(@"Call id [%@] already handled",callid);
+ return;
+ };
+ if ([pushCallIDs count] > 10 /*max number of pending notif*/)
+ [pushCallIDs removeObjectAtIndex:0];
- [pushCallIDs addObject:callid];
+ [pushCallIDs addObject:callid];
}
- (BOOL)popPushCallID:(NSString*) callId {
@@ -1542,11 +1576,11 @@ static int comp_call_id(const LinphoneCall* call , const char *callid) {
}
- (void)playMessageSound {
- BOOL success = [self.messagePlayer play];
- if( !success ){
- LOGE(@"Could not play the message sound");
- }
- AudioServicesPlaySystemSound([LinphoneManager instance].sounds.vibrate);
+ BOOL success = [self.messagePlayer play];
+ if( !success ){
+ LOGE(@"Could not play the message sound");
+ }
+ AudioServicesPlaySystemSound([LinphoneManager instance].sounds.vibrate);
}
static int comp_call_state_paused (const LinphoneCall* call, const void* param) {
@@ -1559,7 +1593,7 @@ static int comp_call_state_paused (const LinphoneCall* call, const void* param)
[[UIApplication sharedApplication] endBackgroundTask:pausedCallBgTask];
}];
LOGI(@"Long running task started, remaining [%g s] because at least one call is paused"
- ,[[UIApplication sharedApplication] backgroundTimeRemaining]);
+ ,[[UIApplication sharedApplication] backgroundTimeRemaining]);
}
- (BOOL)enterBackgroundMode {
LinphoneProxyConfig* proxyCfg;
@@ -1864,18 +1898,18 @@ static void audioRouteChangeListenerCallback (
}
LinphoneCall* call=NULL;
- BOOL addressIsASCII = [address canBeConvertedToEncoding:[NSString defaultCStringEncoding]];
+ BOOL addressIsASCII = [address canBeConvertedToEncoding:[NSString defaultCStringEncoding]];
if ([address length] == 0) return; //just return
- if( !addressIsASCII ){
- UIAlertView* error = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Invalid SIP address",nil)
- message:NSLocalizedString(@"The address should only contain ASCII data",nil)
- delegate:nil
- cancelButtonTitle:NSLocalizedString(@"Continue",nil)
- otherButtonTitles:nil];
- [error show];
+ if( !addressIsASCII ){
+ UIAlertView* error = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Invalid SIP address",nil)
+ message:NSLocalizedString(@"The address should only contain ASCII data",nil)
+ delegate:nil
+ cancelButtonTitle:NSLocalizedString(@"Continue",nil)
+ otherButtonTitles:nil];
+ [error show];
- }
+ }
LinphoneAddress* linphoneAddress = linphone_core_interpret_url(theLinphoneCore, [address cStringUsingEncoding:[NSString defaultCStringEncoding]]);
if (linphoneAddress) {
@@ -1901,7 +1935,7 @@ static void audioRouteChangeListenerCallback (
otherButtonTitles:nil];
[error show];
- }
+ }
if (call) {
@@ -1929,16 +1963,16 @@ static void audioRouteChangeListenerCallback (
pushNotificationToken = nil;
}
- if(apushNotificationToken != nil) {
- pushNotificationToken = apushNotificationToken;
- }
- LinphoneProxyConfig *cfg=nil;
- linphone_core_get_default_proxy(theLinphoneCore, &cfg);
- if (cfg ) {
- linphone_proxy_config_edit(cfg);
- [self configurePushTokenForProxyConfig: cfg];
- linphone_proxy_config_done(cfg);
- }
+ if(apushNotificationToken != nil) {
+ pushNotificationToken = apushNotificationToken;
+ }
+ LinphoneProxyConfig *cfg=nil;
+ linphone_core_get_default_proxy(theLinphoneCore, &cfg);
+ if (cfg ) {
+ linphone_proxy_config_edit(cfg);
+ [self configurePushTokenForProxyConfig: cfg];
+ linphone_proxy_config_done(cfg);
+ }
}
- (void)configurePushTokenForProxyConfig:(LinphoneProxyConfig*)proxyCfg{
@@ -1956,10 +1990,10 @@ static void audioRouteChangeListenerCallback (
#else
#define APPMODE_SUFFIX @"prod"
#endif
- NSString *params = [NSString stringWithFormat:@"app-id=%@.%@;pn-type=apple;pn-tok=%@;pn-msg-str=IM_MSG;pn-call-str=IC_MSG;pn-call-snd=ring.caf;pn-msg-snd=msg.caf", [[NSBundle mainBundle] bundleIdentifier],APPMODE_SUFFIX,tokenString];
+ NSString *params = [NSString stringWithFormat:@"app-id=%@.%@;pn-type=apple;pn-tok=%@;pn-msg-str=IM_MSG;pn-call-str=IC_MSG;pn-call-snd=ring.caf;pn-msg-snd=msg.caf", [[NSBundle mainBundle] bundleIdentifier],APPMODE_SUFFIX,tokenString];
- linphone_proxy_config_set_contact_uri_parameters(proxyCfg, [params UTF8String]);
- linphone_proxy_config_set_contact_parameters(proxyCfg, NULL);
+ linphone_proxy_config_set_contact_uri_parameters(proxyCfg, [params UTF8String]);
+ linphone_proxy_config_set_contact_parameters(proxyCfg, NULL);
} else {
// no push token:
linphone_proxy_config_set_contact_uri_parameters(proxyCfg, NULL);
@@ -1990,22 +2024,22 @@ static void audioRouteChangeListenerCallback (
if (! [[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&isDir] && isDir == NO) {
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:&error];
}
- return cachePath;
+ return cachePath;
}
+ (int)unreadMessageCount {
- int count = 0;
- MSList* rooms = linphone_core_get_chat_rooms([LinphoneManager getLc]);
- MSList* item = rooms;
- while (item) {
- LinphoneChatRoom* room = (LinphoneChatRoom*)item->data;
- if( room ){
- count += linphone_chat_room_get_unread_messages_count(room);
- }
- item = item->next;
- }
+ int count = 0;
+ MSList* rooms = linphone_core_get_chat_rooms([LinphoneManager getLc]);
+ MSList* item = rooms;
+ while (item) {
+ LinphoneChatRoom* room = (LinphoneChatRoom*)item->data;
+ if( room ){
+ count += linphone_chat_room_get_unread_messages_count(room);
+ }
+ item = item->next;
+ }
- return count;
+ return count;
}
+ (BOOL)copyFile:(NSString*)src destination:(NSString*)dst override:(BOOL)override {
@@ -2038,16 +2072,16 @@ static void audioRouteChangeListenerCallback (
- (void)configureVbrCodecs{
PayloadType *pt;
int bitrate=lp_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);
- const MSList* codec = audio_codecs;
- while (codec) {
- pt = codec->data;
- if( linphone_core_payload_type_is_vbr(theLinphoneCore, pt) ) {
- linphone_core_set_payload_type_bitrate(theLinphoneCore, pt, bitrate);
- }
+ const MSList *audio_codecs = linphone_core_get_audio_codecs(theLinphoneCore);
+ const MSList* codec = audio_codecs;
+ while (codec) {
+ pt = codec->data;
+ if( linphone_core_payload_type_is_vbr(theLinphoneCore, pt) ) {
+ linphone_core_set_payload_type_bitrate(theLinphoneCore, pt, bitrate);
+ }
- codec = codec->next;
- }
+ codec = codec->next;
+ }
}
-(void)setLogsEnabled:(BOOL)enabled {
diff --git a/Classes/LinphoneUI/UIChatRoomCell.m b/Classes/LinphoneUI/UIChatRoomCell.m
index 45d02b40b..6eaccfcbc 100644
--- a/Classes/LinphoneUI/UIChatRoomCell.m
+++ b/Classes/LinphoneUI/UIChatRoomCell.m
@@ -322,9 +322,7 @@ static UIFont *CELL_FONT = nil;
}
- (IBAction)onDownloadClick:(id)event {
- NSURL* url = [NSURL URLWithString:[NSString stringWithUTF8String:linphone_chat_message_get_external_body_url(chat)]];
- [chatRoomDelegate chatRoomStartImageDownload:url userInfo:[NSValue valueWithPointer:chat]];
-
+ [chatRoomDelegate chatRoomStartImageDownload:chat];
}
- (IBAction)onImageClick:(id)event {
diff --git a/Classes/Utils/InAppSettingsKit/Controllers/IASKSpecifierValuesViewController.m b/Classes/Utils/InAppSettingsKit/Controllers/IASKSpecifierValuesViewController.m
index 6e1791aec..533eb508b 100755
--- a/Classes/Utils/InAppSettingsKit/Controllers/IASKSpecifierValuesViewController.m
+++ b/Classes/Utils/InAppSettingsKit/Controllers/IASKSpecifierValuesViewController.m
@@ -105,6 +105,7 @@
}
- (void)viewDidUnload {
+ [super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.tableView = nil;
diff --git a/Classes/Utils/UIAlertView+Blocks/UIAlertView+Blocks.h b/Classes/Utils/UIAlertView+Blocks/UIAlertView+Blocks.h
new file mode 100644
index 000000000..d5372d8d2
--- /dev/null
+++ b/Classes/Utils/UIAlertView+Blocks/UIAlertView+Blocks.h
@@ -0,0 +1,59 @@
+//
+// UIAlertView+Blocks.h
+// UIAlertViewBlocks
+//
+// Created by Ryan Maxwell on 29/08/13.
+//
+// The MIT License (MIT)
+//
+// Copyright (c) 2013 Ryan Maxwell
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import
+
+typedef void (^UIAlertViewBlock) (UIAlertView *alertView);
+typedef void (^UIAlertViewCompletionBlock) (UIAlertView *alertView, NSInteger buttonIndex);
+
+@interface UIAlertView (Blocks)
+
++ (instancetype)showWithTitle:(NSString *)title
+ message:(NSString *)message
+ style:(UIAlertViewStyle)style
+ cancelButtonTitle:(NSString *)cancelButtonTitle
+ otherButtonTitles:(NSArray *)otherButtonTitles
+ tapBlock:(UIAlertViewCompletionBlock)tapBlock;
+
++ (instancetype)showWithTitle:(NSString *)title
+ message:(NSString *)message
+ cancelButtonTitle:(NSString *)cancelButtonTitle
+ otherButtonTitles:(NSArray *)otherButtonTitles
+ tapBlock:(UIAlertViewCompletionBlock)tapBlock;
+
+@property (copy, nonatomic) UIAlertViewCompletionBlock tapBlock;
+@property (copy, nonatomic) UIAlertViewCompletionBlock willDismissBlock;
+@property (copy, nonatomic) UIAlertViewCompletionBlock didDismissBlock;
+
+@property (copy, nonatomic) UIAlertViewBlock willPresentBlock;
+@property (copy, nonatomic) UIAlertViewBlock didPresentBlock;
+@property (copy, nonatomic) UIAlertViewBlock cancelBlock;
+
+@property (copy, nonatomic) BOOL(^shouldEnableFirstOtherButtonBlock)(UIAlertView *alertView);
+
+@end
diff --git a/Classes/Utils/UIAlertView+Blocks/UIAlertView+Blocks.m b/Classes/Utils/UIAlertView+Blocks/UIAlertView+Blocks.m
new file mode 100644
index 000000000..2cdfd71ff
--- /dev/null
+++ b/Classes/Utils/UIAlertView+Blocks/UIAlertView+Blocks.m
@@ -0,0 +1,264 @@
+//
+// UIAlertView+Blocks.m
+// UIAlertViewBlocks
+//
+// Created by Ryan Maxwell on 29/08/13.
+//
+// The MIT License (MIT)
+//
+// Copyright (c) 2013 Ryan Maxwell
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "UIAlertView+Blocks.h"
+
+#import
+
+static const void *UIAlertViewOriginalDelegateKey = &UIAlertViewOriginalDelegateKey;
+
+static const void *UIAlertViewTapBlockKey = &UIAlertViewTapBlockKey;
+static const void *UIAlertViewWillPresentBlockKey = &UIAlertViewWillPresentBlockKey;
+static const void *UIAlertViewDidPresentBlockKey = &UIAlertViewDidPresentBlockKey;
+static const void *UIAlertViewWillDismissBlockKey = &UIAlertViewWillDismissBlockKey;
+static const void *UIAlertViewDidDismissBlockKey = &UIAlertViewDidDismissBlockKey;
+static const void *UIAlertViewCancelBlockKey = &UIAlertViewCancelBlockKey;
+static const void *UIAlertViewShouldEnableFirstOtherButtonBlockKey = &UIAlertViewShouldEnableFirstOtherButtonBlockKey;
+
+@implementation UIAlertView (Blocks)
+
++ (instancetype)showWithTitle:(NSString *)title
+ message:(NSString *)message
+ style:(UIAlertViewStyle)style
+ cancelButtonTitle:(NSString *)cancelButtonTitle
+ otherButtonTitles:(NSArray *)otherButtonTitles
+ tapBlock:(UIAlertViewCompletionBlock)tapBlock {
+
+ NSString *firstObject = otherButtonTitles.count ? otherButtonTitles[0] : nil;
+
+ UIAlertView *alertView = [[self alloc] initWithTitle:title
+ message:message
+ delegate:nil
+ cancelButtonTitle:cancelButtonTitle
+ otherButtonTitles:firstObject, nil];
+
+ alertView.alertViewStyle = style;
+
+ if (otherButtonTitles.count > 1) {
+ for (NSString *buttonTitle in [otherButtonTitles subarrayWithRange:NSMakeRange(1, otherButtonTitles.count - 1)]) {
+ [alertView addButtonWithTitle:buttonTitle];
+ }
+ }
+
+ if (tapBlock) {
+ alertView.tapBlock = tapBlock;
+ }
+
+ [alertView show];
+
+#if !__has_feature(objc_arc)
+ return [alertView autorelease];
+#else
+ return alertView;
+#endif
+}
+
+
++ (instancetype)showWithTitle:(NSString *)title
+ message:(NSString *)message
+ cancelButtonTitle:(NSString *)cancelButtonTitle
+ otherButtonTitles:(NSArray *)otherButtonTitles
+ tapBlock:(UIAlertViewCompletionBlock)tapBlock {
+
+ return [self showWithTitle:title
+ message:message
+ style:UIAlertViewStyleDefault
+ cancelButtonTitle:cancelButtonTitle
+ otherButtonTitles:otherButtonTitles
+ tapBlock:tapBlock];
+}
+
+#pragma mark -
+
+- (void)_checkAlertViewDelegate {
+ if (self.delegate != (id)self) {
+ objc_setAssociatedObject(self, UIAlertViewOriginalDelegateKey, self.delegate, OBJC_ASSOCIATION_ASSIGN);
+ self.delegate = (id)self;
+ }
+}
+
+- (UIAlertViewCompletionBlock)tapBlock {
+ return objc_getAssociatedObject(self, UIAlertViewTapBlockKey);
+}
+
+- (void)setTapBlock:(UIAlertViewCompletionBlock)tapBlock {
+ [self _checkAlertViewDelegate];
+ objc_setAssociatedObject(self, UIAlertViewTapBlockKey, tapBlock, OBJC_ASSOCIATION_COPY);
+}
+
+- (UIAlertViewCompletionBlock)willDismissBlock {
+ return objc_getAssociatedObject(self, UIAlertViewWillDismissBlockKey);
+}
+
+- (void)setWillDismissBlock:(UIAlertViewCompletionBlock)willDismissBlock {
+ [self _checkAlertViewDelegate];
+ objc_setAssociatedObject(self, UIAlertViewWillDismissBlockKey, willDismissBlock, OBJC_ASSOCIATION_COPY);
+}
+
+- (UIAlertViewCompletionBlock)didDismissBlock {
+ return objc_getAssociatedObject(self, UIAlertViewDidDismissBlockKey);
+}
+
+- (void)setDidDismissBlock:(UIAlertViewCompletionBlock)didDismissBlock {
+ [self _checkAlertViewDelegate];
+ objc_setAssociatedObject(self, UIAlertViewDidDismissBlockKey, didDismissBlock, OBJC_ASSOCIATION_COPY);
+}
+
+- (UIAlertViewBlock)willPresentBlock {
+ return objc_getAssociatedObject(self, UIAlertViewWillPresentBlockKey);
+}
+
+- (void)setWillPresentBlock:(UIAlertViewBlock)willPresentBlock {
+ [self _checkAlertViewDelegate];
+ objc_setAssociatedObject(self, UIAlertViewWillPresentBlockKey, willPresentBlock, OBJC_ASSOCIATION_COPY);
+}
+
+- (UIAlertViewBlock)didPresentBlock {
+ return objc_getAssociatedObject(self, UIAlertViewDidPresentBlockKey);
+}
+
+- (void)setDidPresentBlock:(UIAlertViewBlock)didPresentBlock {
+ [self _checkAlertViewDelegate];
+ objc_setAssociatedObject(self, UIAlertViewDidPresentBlockKey, didPresentBlock, OBJC_ASSOCIATION_COPY);
+}
+
+- (UIAlertViewBlock)cancelBlock {
+ return objc_getAssociatedObject(self, UIAlertViewCancelBlockKey);
+}
+
+- (void)setCancelBlock:(UIAlertViewBlock)cancelBlock {
+ [self _checkAlertViewDelegate];
+ objc_setAssociatedObject(self, UIAlertViewCancelBlockKey, cancelBlock, OBJC_ASSOCIATION_COPY);
+}
+
+- (void)setShouldEnableFirstOtherButtonBlock:(BOOL(^)(UIAlertView *alertView))shouldEnableFirstOtherButtonBlock {
+ [self _checkAlertViewDelegate];
+ objc_setAssociatedObject(self, UIAlertViewShouldEnableFirstOtherButtonBlockKey, shouldEnableFirstOtherButtonBlock, OBJC_ASSOCIATION_COPY);
+}
+
+- (BOOL(^)(UIAlertView *alertView))shouldEnableFirstOtherButtonBlock {
+ return objc_getAssociatedObject(self, UIAlertViewShouldEnableFirstOtherButtonBlockKey);
+}
+
+#pragma mark - UIAlertViewDelegate
+
+- (void)willPresentAlertView:(UIAlertView *)alertView {
+ UIAlertViewBlock block = alertView.willPresentBlock;
+
+ if (block) {
+ block(alertView);
+ }
+
+ id originalDelegate = objc_getAssociatedObject(self, UIAlertViewOriginalDelegateKey);
+ if (originalDelegate && [originalDelegate respondsToSelector:@selector(willPresentAlertView:)]) {
+ [originalDelegate willPresentAlertView:alertView];
+ }
+}
+
+- (void)didPresentAlertView:(UIAlertView *)alertView {
+ UIAlertViewBlock block = alertView.didPresentBlock;
+
+ if (block) {
+ block(alertView);
+ }
+
+ id originalDelegate = objc_getAssociatedObject(self, UIAlertViewOriginalDelegateKey);
+ if (originalDelegate && [originalDelegate respondsToSelector:@selector(didPresentAlertView:)]) {
+ [originalDelegate didPresentAlertView:alertView];
+ }
+}
+
+
+- (void)alertViewCancel:(UIAlertView *)alertView {
+ UIAlertViewBlock block = alertView.cancelBlock;
+
+ if (block) {
+ block(alertView);
+ }
+
+ id originalDelegate = objc_getAssociatedObject(self, UIAlertViewOriginalDelegateKey);
+ if (originalDelegate && [originalDelegate respondsToSelector:@selector(alertViewCancel:)]) {
+ [originalDelegate alertViewCancel:alertView];
+ }
+}
+
+- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
+ UIAlertViewCompletionBlock completion = alertView.tapBlock;
+
+ if (completion) {
+ completion(alertView, buttonIndex);
+ }
+
+ id originalDelegate = objc_getAssociatedObject(self, UIAlertViewOriginalDelegateKey);
+ if (originalDelegate && [originalDelegate respondsToSelector:@selector(alertView:clickedButtonAtIndex:)]) {
+ [originalDelegate alertView:alertView clickedButtonAtIndex:buttonIndex];
+ }
+}
+
+- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex {
+ UIAlertViewCompletionBlock completion = alertView.willDismissBlock;
+
+ if (completion) {
+ completion(alertView, buttonIndex);
+ }
+
+ id originalDelegate = objc_getAssociatedObject(self, UIAlertViewOriginalDelegateKey);
+ if (originalDelegate && [originalDelegate respondsToSelector:@selector(alertView:willDismissWithButtonIndex:)]) {
+ [originalDelegate alertView:alertView willDismissWithButtonIndex:buttonIndex];
+ }
+}
+
+- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
+ UIAlertViewCompletionBlock completion = alertView.didDismissBlock;
+
+ if (completion) {
+ completion(alertView, buttonIndex);
+ }
+
+ id originalDelegate = objc_getAssociatedObject(self, UIAlertViewOriginalDelegateKey);
+ if (originalDelegate && [originalDelegate respondsToSelector:@selector(alertView:didDismissWithButtonIndex:)]) {
+ [originalDelegate alertView:alertView didDismissWithButtonIndex:buttonIndex];
+ }
+}
+
+- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView {
+ BOOL(^shouldEnableFirstOtherButtonBlock)(UIAlertView *alertView) = alertView.shouldEnableFirstOtherButtonBlock;
+
+ if (shouldEnableFirstOtherButtonBlock) {
+ return shouldEnableFirstOtherButtonBlock(alertView);
+ }
+
+ id originalDelegate = objc_getAssociatedObject(self, UIAlertViewOriginalDelegateKey);
+ if (originalDelegate && [originalDelegate respondsToSelector:@selector(alertViewShouldEnableFirstOtherButton:)]) {
+ return [originalDelegate alertViewShouldEnableFirstOtherButton:alertView];
+ }
+
+ return YES;
+}
+
+@end
diff --git a/Resources/licenses.html b/Resources/licenses.html
index 31b1bdba7..183077683 100644
--- a/Resources/licenses.html
+++ b/Resources/licenses.html
@@ -37,6 +37,11 @@
http://www.tortuga22.com
Apache license
+Ryan Maxwell
+UIAlertview+Blocks
+https://github.com/ryanmaxwell/UIAlertView-Blocks
+MIT license
+
OrderedDictionary
Matt Gallagher
http://cocoawithlove.com
diff --git a/Resources/wizard_linphone_create.rc b/Resources/wizard_linphone_create.rc
index 292d662d4..e023f4454 100644
--- a/Resources/wizard_linphone_create.rc
+++ b/Resources/wizard_linphone_create.rc
@@ -22,7 +22,7 @@
1
- https://www.linphone.org:444/upload.php
+ https://www.linphone.org:444/lft.php
1
stun.linphone.org
1
diff --git a/Resources/wizard_linphone_existing.rc b/Resources/wizard_linphone_existing.rc
index 2244776e8..1e71c41d3 100644
--- a/Resources/wizard_linphone_existing.rc
+++ b/Resources/wizard_linphone_existing.rc
@@ -22,7 +22,7 @@
1
- https://www.linphone.org:444/upload.php
+ https://www.linphone.org:444/lft.php
1
stun.linphone.org
1
diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj
index c47341102..7cf06a6bf 100755
--- a/linphone.xcodeproj/project.pbxproj
+++ b/linphone.xcodeproj/project.pbxproj
@@ -892,6 +892,7 @@
/* Begin PBXFileReference section */
045B5CB218D72E9A0088350C /* libbzrtp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libbzrtp.a; path = "liblinphone-sdk/apple-darwin/lib/libbzrtp.a"; sourceTree = ""; };
+ 15017E6F1773578400784ACB /* libxml2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libxml2.a; path = "liblinphone-sdk/apple-darwin/lib/libxml2.a"; sourceTree = ""; };
152F22331B15E83B008C0621 /* libilbcrfc3951.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libilbcrfc3951.a; path = "liblinphone-sdk/apple-darwin/lib/libilbcrfc3951.a"; sourceTree = ""; };
152F22351B15E889008C0621 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = usr/lib/libxml2.dylib; sourceTree = SDKROOT; };
1560821E18EEF26100765332 /* libmsopenh264.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmsopenh264.a; path = "liblinphone-sdk/apple-darwin/lib/mediastreamer/plugins/libmsopenh264.a"; sourceTree = ""; };
@@ -951,6 +952,7 @@
22276E8213C73D3100210156 /* libswscale.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libswscale.a; path = "liblinphone-sdk/apple-darwin/lib/libswscale.a"; sourceTree = ""; };
22276E8613C73D8A00210156 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
22276E8813C73DC000210156 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
+ 223148E31178A08200637D6A /* libilbc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libilbc.a; path = "liblinphone-sdk/apple-darwin/lib/libilbc.a"; sourceTree = ""; };
223148E51178A09900637D6A /* libmsilbc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmsilbc.a; path = "liblinphone-sdk/apple-darwin/lib/mediastreamer/plugins/libmsilbc.a"; sourceTree = ""; };
2234C8E715EE2F7F00E18E83 /* chat_message_delivered.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = chat_message_delivered.png; path = Resources/chat_message_delivered.png; sourceTree = ""; };
2234C8E815EE2F7F00E18E83 /* chat_message_not_delivered.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = chat_message_not_delivered.png; path = Resources/chat_message_not_delivered.png; sourceTree = ""; };
@@ -2287,6 +2289,7 @@
F03A9B9718C0DB6F00C4D7FE /* libc++.dylib */,
F0BB8C111936240300974404 /* libcunit.a */,
220FAD2910765B400068D98F /* libgsm.a */,
+ 223148E31178A08200637D6A /* libilbc.a */,
2211DB911475562600DEE054 /* liblinphone.a */,
F0BB8C0F193623F200974404 /* liblinphonetester.a */,
22405EE916006F0700B92522 /* libmediastreamer_base.a */,
@@ -2316,6 +2319,7 @@
D30BF33216A427BC00AF0026 /* libtunnel.a */,
7066FC0B13E830E400EFC6DC /* libvpx.a */,
22AA8AFB13D7125500B30535 /* libx264.a */,
+ 15017E6F1773578400784ACB /* libxml2.a */,
F0B89C2118DC89E30050B60E /* MediaPlayer.framework */,
D37DC7171594AF3400B2A5EB /* MessageUI.framework */,
226EF06B15FA256B005865C7 /* MobileCoreServices.framework */,