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 */,