From 4855e4744407c45edb6427b363895486dea40024 Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Fri, 30 Mar 2012 14:17:43 +0200 Subject: [PATCH] Implement call transfers, and fix video spinner --- Classes/CallDelegate.h | 3 +- Classes/IncallViewController.h | 2 + Classes/IncallViewController.m | 88 +++++++++++++++++++++++++++- Classes/IncallViewController.xib | 82 +++++++++++++++++++++----- Classes/LinphoneUI/LinphoneManager.h | 3 + Classes/LinphoneUI/LinphoneManager.m | 17 +++--- Classes/LinphoneUI/UICallButton.h | 6 +- Classes/LinphoneUI/UICallButton.m | 27 ++++++++- Classes/PhoneViewController.m | 17 +++++- submodules/linphone | 2 +- 10 files changed, 213 insertions(+), 34 deletions(-) diff --git a/Classes/CallDelegate.h b/Classes/CallDelegate.h index f09b1fdbf..516c2074d 100644 --- a/Classes/CallDelegate.h +++ b/Classes/CallDelegate.h @@ -25,7 +25,8 @@ enum CallDelegateType { CD_NEW_CALL, CD_ZRTP, CD_VIDEO_UPDATE, - CD_STOP_VIDEO_ON_LOW_BATTERY + CD_STOP_VIDEO_ON_LOW_BATTERY, + CD_TRANSFER_CALL }; @protocol UIActionSheetCustomDelegate diff --git a/Classes/IncallViewController.h b/Classes/IncallViewController.h index 81f6e588c..e1bd40cdb 100644 --- a/Classes/IncallViewController.h +++ b/Classes/IncallViewController.h @@ -40,6 +40,7 @@ UIToggleVideoButton* addVideo; UITableView* callTableView; UIButton* addCall, *mergeCalls; + UIButton* transfer; //key pad @@ -115,6 +116,7 @@ @property (nonatomic, retain) IBOutlet UITableView* callTableView; @property (nonatomic, retain) IBOutlet UIButton* addCall; @property (nonatomic, retain) IBOutlet UIButton* mergeCalls; +@property (nonatomic, retain) IBOutlet UIButton* transfer; @property (nonatomic, retain) IBOutlet UIButton* one; @property (nonatomic, retain) IBOutlet UIButton* two; diff --git a/Classes/IncallViewController.m b/Classes/IncallViewController.m index 1b0d2a049..384cc840e 100644 --- a/Classes/IncallViewController.m +++ b/Classes/IncallViewController.m @@ -52,6 +52,7 @@ const NSInteger SECURE_BUTTON_TAG=5; @synthesize callTableView; @synthesize addCall; @synthesize mergeCalls; +@synthesize transfer; @synthesize one; @synthesize two; @@ -432,6 +433,8 @@ void addAnimationFadeTransition(UIView* view, float duration) { [videoCameraSwitch setPreview:videoPreview]; addVideo.videoUpdateIndicator = videoUpdateIndicator; + [transfer addTarget:self action:@selector(transferPressed) forControlEvents:UIControlEventTouchUpInside]; + // prevent buttons resizing /* endCtrl.imageView.contentMode = UIViewContentModeCenter; @@ -444,9 +447,49 @@ void addAnimationFadeTransition(UIView* view, float duration) { } +-(void) transferPressed { + /* allow only if call is active */ + if (!linphone_core_get_current_call([LinphoneManager getLc])) + return; + + /* build UIActionSheet */ + if (visibleActionSheet != nil) { + [visibleActionSheet dismissWithClickedButtonIndex:visibleActionSheet.cancelButtonIndex animated:TRUE]; + } + + CallDelegate* cd = [[CallDelegate alloc] init]; + cd.eventType = CD_TRANSFER_CALL; + cd.delegate = self; + cd.call = linphone_core_get_current_call([LinphoneManager getLc]); + NSString* title = NSLocalizedString(@"Transfer call to:",nil); + visibleActionSheet = [[UIActionSheet alloc] initWithTitle:title + delegate:cd + cancelButtonTitle:NSLocalizedString(@"Cancel",nil) + destructiveButtonTitle:NSLocalizedString(@"A new call",nil) + otherButtonTitles:nil]; + + // add button for each trasnfer-to valid call + const MSList* calls = linphone_core_get_calls([LinphoneManager getLc]); + while (calls) { + LinphoneCall* call = (LinphoneCall*) calls->data; + LinphoneCallAppData* data = ((LinphoneCallAppData*)linphone_call_get_user_pointer(call)); + if (call != cd.call && !linphone_call_get_current_params(call)->in_conference) { + const LinphoneAddress* addr = linphone_call_get_remote_address(call); + NSString* btnTitle = [NSString stringWithFormat : NSLocalizedString(@"%s",nil), (linphone_address_get_display_name(addr) ?linphone_address_get_display_name(addr):linphone_address_get_username(addr))]; + data->transferButtonIndex = [visibleActionSheet addButtonWithTitle:btnTitle]; + } else { + data->transferButtonIndex = -1; + } + calls = calls->next; + } + + visibleActionSheet.actionSheetStyle = UIActionSheetStyleDefault; + [visibleActionSheet showInView:self.view]; +} + -(void) addCallPressed { [LinphoneManager logUIElementPressed:"CALL button"]; - [self dismissModalViewControllerAnimated:true]; + [[LinphoneManager instance] displayDialer]; } @@ -549,6 +592,7 @@ void addAnimationFadeTransition(UIView* view, float duration) { } if (!mVideoShown) [[UIApplication sharedApplication] setIdleTimerDisabled:false]; mIncallViewIsReady=FALSE; + dismissed = false; } - (void)viewDidUnload { @@ -600,6 +644,7 @@ void addAnimationFadeTransition(UIView* view, float duration) { UIViewController* modalVC = self.modalViewController; UIDevice *device = [UIDevice currentDevice]; device.proximityMonitoringEnabled = NO; + dismissed = true; if (modalVC != nil) { mVideoIsPending=FALSE; // clear previous native window ids @@ -613,15 +658,33 @@ void addAnimationFadeTransition(UIView* view, float duration) { } [self dismissModalViewControllerAnimated:FALSE]; //disable animation to avoid blanc bar just below status bar*/ - dismissed = true; [self updateUIFromLinphoneState: YES]; } + +static void hideSpinner(LinphoneCall* lc, void* user_data); + +-(void) hideSpinnerIndicator: (LinphoneCall*)call { + if (!videoWaitingForFirstImage.hidden) { + videoWaitingForFirstImage.hidden = TRUE; + } /*else { + linphone_call_set_next_video_frame_decoded_callback(call, hideSpinner, self); + }*/ +} + +static void hideSpinner(LinphoneCall* call, void* user_data) { + IncallViewController* thiz = (IncallViewController*) user_data; + [thiz hideSpinnerIndicator:call]; +} + -(void) displayVideoCall:(LinphoneCall*) call FromUI:(UIViewController*) viewCtrl forUser:(NSString*) username withDisplayName:(NSString*) displayName { [self enableVideoDisplay]; + [self updateUIFromLinphoneState: YES]; videoWaitingForFirstImage.hidden = NO; [videoWaitingForFirstImage startAnimating]; + + linphone_call_set_next_video_frame_decoded_callback(call, hideSpinner, self); return; if (mIncallViewIsReady) { @@ -1022,6 +1085,27 @@ void addAnimationFadeTransition(UIView* view, float duration) { } break; } + case CD_TRANSFER_CALL: { + LinphoneCall* call = (LinphoneCall*)datas; + if (buttonIndex == actionSheet.destructiveButtonIndex) { + // transfer to a new call: enable transfer mode and hide incallview + [UICallButton enableTransforMode:YES]; + [[LinphoneManager instance] displayDialer]; + } else { + // browse existing call and trasnfer to the one matching the btn id + const MSList* calls = linphone_core_get_calls([LinphoneManager getLc]); + while (calls) { + LinphoneCall* call2 = (LinphoneCall*) calls->data; + LinphoneCallAppData* data = ((LinphoneCallAppData*)linphone_call_get_user_pointer(call2)); + if (data->transferButtonIndex == buttonIndex) { + linphone_core_transfer_call_to_another([LinphoneManager getLc], call, call2); + } + data->transferButtonIndex = -1; + calls = calls->next; + } + } + break; + } default: ms_error("Unhandled CallDelegate event of type: %d received - ignoring", type); } diff --git a/Classes/IncallViewController.xib b/Classes/IncallViewController.xib index 0d5a16bd6..7d509ae97 100644 --- a/Classes/IncallViewController.xib +++ b/Classes/IncallViewController.xib @@ -55,7 +55,7 @@ {320, 480} - + _NS:196 3 @@ -90,6 +90,7 @@ {{141, 212}, {37, 37}} + _NS:1030 NO IBCocoaTouchFramework @@ -227,7 +228,7 @@ 292 - {{107, 70}, {106, 66}} + {{160, 4}, {80, 66}} @@ -260,7 +261,7 @@ 292 - {{0, 4}, {107, 66}} + {{0, 4}, {80, 66}} @@ -301,10 +302,10 @@ 292 - {{0, 70}, {107, 66}} + {{0, 70}, {80, 66}} - + NO NO @@ -324,10 +325,37 @@ + + + 292 + {{80, 70}, {160, 66}} + + + + + NO + NO + IBCocoaTouchFramework + 0 + 0 + Transfer + + + 1 + MC4xOTYwNzg0MzE0IDAuMzA5ODAzOTIxNiAwLjUyMTU2ODYyNzUAA + + + + + 2 + 15 + + + 292 - {{213, 70}, {107, 66}} + {{240, 70}, {80, 66}} @@ -356,7 +384,7 @@ -2147483356 - {{213, 70}, {107, 66}} + {{240, 70}, {80, 66}} @@ -389,7 +417,7 @@ -2147483356 - {{247.5, 84.5}, {37, 37}} + {{261.5, 84.5}, {37, 37}} @@ -402,10 +430,10 @@ 292 - {{107, 4}, {106, 66}} + {{80, 4}, {80, 66}} - + NO NO @@ -428,7 +456,7 @@ 292 - {{213, 4}, {107, 66}} + {{240, 4}, {80, 66}} @@ -458,10 +486,10 @@ 292 - {{107, 70}, {106, 66}} + {{160, 4}, {80, 66}} - + NO NO @@ -1105,6 +1133,14 @@ 145 + + + transfer + + + + 150 + doAction: @@ -1239,6 +1275,7 @@ + controls @@ -1429,6 +1466,12 @@ + + 147 + + + transfer + @@ -1461,6 +1504,8 @@ 140.IBPluginDependency 142.IBPluginDependency 144.IBPluginDependency + 147.IBPluginDependency + 147.IBUIButtonInspectorSelectedStateConfigurationMetadataKey 15.IBPluginDependency 16.CustomClassName 16.IBPluginDependency @@ -1528,6 +1573,8 @@ com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin UIMuteButton com.apple.InterfaceBuilder.IBCocoaTouchPlugin @@ -1578,7 +1625,7 @@ - 145 + 150 @@ -1626,6 +1673,7 @@ speaker star three + transfer two videoCallQuality videoCameraSwitch @@ -1665,6 +1713,7 @@ UIButton UIButton UIButton + UIButton UIImageView UICamSwitch UIView @@ -1705,6 +1754,7 @@ speaker star three + transfer two videoCallQuality videoCameraSwitch @@ -1818,6 +1868,10 @@ three UIButton + + transfer + UIButton + two UIButton diff --git a/Classes/LinphoneUI/LinphoneManager.h b/Classes/LinphoneUI/LinphoneManager.h index 701e4bfa4..b0cace918 100644 --- a/Classes/LinphoneUI/LinphoneManager.h +++ b/Classes/LinphoneUI/LinphoneManager.h @@ -44,6 +44,7 @@ struct NetworkReachabilityContext { typedef struct _LinphoneCallAppData { bool_t batteryWarningShown; + int transferButtonIndex; } LinphoneCallAppData; @@ -74,6 +75,8 @@ typedef struct _LinphoneCallAppData { +(void) set:(UIView*)view hidden: (BOOL) hidden withName:(const char*)name andReason:(const char*) reason; +(void) logUIElementPressed:(const char*) name; +-(void) displayDialer; + -(void) registerLogView:(id) view; -(void) startLibLinphone; diff --git a/Classes/LinphoneUI/LinphoneManager.m b/Classes/LinphoneUI/LinphoneManager.m index af4ca92da..ef5498cca 100644 --- a/Classes/LinphoneUI/LinphoneManager.m +++ b/Classes/LinphoneUI/LinphoneManager.m @@ -277,6 +277,12 @@ extern void libmsbcg729_init(); } +-(void) displayDialer { + [callDelegate displayDialerFromUI:mCurrentViewController + forUser:@"" + withDisplayName:@""]; +} + +(LinphoneCore*) getLc { if (theLinphoneCore==nil) { @throw([NSException exceptionWithName:@"LinphoneCoreException" reason:@"Linphone core not initialized yet" userInfo:nil]); @@ -399,14 +405,6 @@ static void linphone_iphone_registration_state(LinphoneCore *lc, LinphoneProxyCo [(LinphoneManager*)linphone_core_get_user_data(lc) onRegister:lc cfg:cfg state:state message:message]; } --(void) call_video_first_image_decoded:(LinphoneCall*) call { - [callDelegate firstVideoFrameDecoded: call]; -} - -static void linphone_call_first_video_frame(LinphoneCore* lc, LinphoneCall* call) { - [[LinphoneManager instance] call_video_first_image_decoded: call]; -} - static LinphoneCoreVTable linphonec_vtable = { .show =NULL, .call_state_changed =(LinphoneCallStateCb)linphone_iphone_call_state, @@ -419,8 +417,7 @@ static LinphoneCoreVTable linphonec_vtable = { .display_warning=linphone_iphone_log, .display_url=NULL, .text_received=NULL, - .dtmf_received=NULL, - .call_first_video_frame=linphone_call_first_video_frame + .dtmf_received=NULL }; diff --git a/Classes/LinphoneUI/UICallButton.h b/Classes/LinphoneUI/UICallButton.h index f5dc0db54..5fc1547fb 100644 --- a/Classes/LinphoneUI/UICallButton.h +++ b/Classes/LinphoneUI/UICallButton.h @@ -22,7 +22,11 @@ @interface UICallButton : UIButton { @private - UITextField* mAddress; + UITextField* mAddress; } -(void) initWithAddress:(UITextField*) address; + ++(void) enableTransforMode:(BOOL) enable; ++(BOOL) transforModeEnabled; + @end diff --git a/Classes/LinphoneUI/UICallButton.m b/Classes/LinphoneUI/UICallButton.m index 080912cd8..6957344e5 100644 --- a/Classes/LinphoneUI/UICallButton.m +++ b/Classes/LinphoneUI/UICallButton.m @@ -23,6 +23,17 @@ @implementation UICallButton + +static BOOL transferMode = NO; + ++(void) enableTransforMode:(BOOL) enable { + transferMode = enable; +} + ++(BOOL) transforModeEnabled { + return transferMode; +} + -(void) touchUp:(id) sender { if (!linphone_core_is_network_reachabled([LinphoneManager getLc])) { UIAlertView* error = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Network Error",nil) @@ -58,7 +69,12 @@ if ([mAddress.text length] == 0) return; //just return if ([mAddress.text hasPrefix:@"sip:"]) { - linphone_core_invite_with_params([LinphoneManager getLc],[mAddress.text cStringUsingEncoding:[NSString defaultCStringEncoding]],lcallParams); + if (transferMode) { + linphone_core_transfer_call([LinphoneManager getLc], linphone_core_get_current_call([LinphoneManager getLc]), [mAddress.text cStringUsingEncoding:[NSString defaultCStringEncoding]]); + } else { + linphone_core_invite_with_params([LinphoneManager getLc],[mAddress.text cStringUsingEncoding:[NSString defaultCStringEncoding]],lcallParams); + } + [UICallButton enableTransforMode:NO]; } else if ( proxyCfg==nil){ UIAlertView* error = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Invalid sip address",nil) message:NSLocalizedString(@"Either configure a SIP proxy server from settings prior to place a call or use a valid sip address (I.E sip:john@example.net)",nil) @@ -78,12 +94,16 @@ linphone_address_set_display_name(tmpAddress,(lDisplayName)?[lDisplayName cStringUsingEncoding:[NSString defaultCStringEncoding]]:nil); - - linphone_core_invite_address_with_params([LinphoneManager getLc],tmpAddress,lcallParams) ; + if (transferMode) { + linphone_core_transfer_call([LinphoneManager getLc], linphone_core_get_current_call([LinphoneManager getLc]), normalizedUserName); + } else { + linphone_core_invite_address_with_params([LinphoneManager getLc],tmpAddress,lcallParams) ; + } linphone_address_destroy(tmpAddress); } linphone_call_params_destroy(lcallParams); + [UICallButton enableTransforMode:NO]; } else if (linphone_core_inc_invite_pending([LinphoneManager getLc])) { linphone_core_accept_call([LinphoneManager getLc],linphone_core_get_current_call([LinphoneManager getLc])); } @@ -100,6 +120,7 @@ */ -(void) initWithAddress:(UITextField*) address{ mAddress=[address retain]; + transferMode = NO; [self addTarget:self action:@selector(touchUp:) forControlEvents:UIControlEventTouchUpInside]; } diff --git a/Classes/PhoneViewController.m b/Classes/PhoneViewController.m index 65de89c11..091e04901 100644 --- a/Classes/PhoneViewController.m +++ b/Classes/PhoneViewController.m @@ -106,6 +106,8 @@ [LinphoneManager set:callShort hidden:zeroCall withName:"CALL_SHORT button" andReason:__FUNCTION__]; [LinphoneManager set:backToCallView hidden:zeroCall withName:"BACK button" andReason:__FUNCTION__]; + [callShort setTitle:[UICallButton transforModeEnabled] ? @"transfer":@"call" forState:UIControlStateNormal]; + if (!callShort.hidden) [callShort setEnabled:!linphone_core_sound_resources_locked([LinphoneManager getLc])]; } @catch (NSException* exc) { @@ -230,7 +232,6 @@ [myTabBarController setSelectedIndex:DIALER_TAB_INDEX]; [mMainScreenWithVideoPreview showPreview:YES]; - } //status reporting @@ -284,10 +285,19 @@ } -(void) backToCallViewPressed { - [self displayInCall: nil + [UICallButton enableTransforMode:NO]; + [self presentModalViewController:(UIViewController*)mIncallViewController animated:true]; + + LinphoneCall* call = linphone_core_get_current_call([LinphoneManager getLc]); + + if (!call || !linphone_call_params_video_enabled(linphone_call_get_current_params(call)) || linphone_call_get_state(call) != LinphoneCallStreamsRunning) { + [self displayInCall: call FromUI:nil forUser:nil withDisplayName:nil]; + } else { + [self displayVideoCall:call FromUI:nil forUser:nil withDisplayName:nil]; + } } -(void) displayCall: (LinphoneCall*) call InProgressFromUI:(UIViewController*) viewCtrl forUser:(NSString*) username withDisplayName:(NSString*) displayName { @@ -316,6 +326,8 @@ [LinphoneManager set:callLarge hidden:YES withName:"CALL_LARGE button" andReason:__FUNCTION__]; [LinphoneManager set:callShort hidden:NO withName:"CALL_SHORT button" andReason:__FUNCTION__]; [LinphoneManager set:backToCallView hidden:NO withName:"CALL_BACK button" andReason:__FUNCTION__]; + + [self updateCallAndBackButtons]; } @@ -325,6 +337,7 @@ withDisplayName:displayName]; [mMainScreenWithVideoPreview showPreview:NO]; + [self updateCallAndBackButtons]; } -(void) displayAskToEnableVideoCall:(LinphoneCall*) call forUser:(NSString*) username withDisplayName:(NSString*) displayName { diff --git a/submodules/linphone b/submodules/linphone index 9a21860f4..d481382fb 160000 --- a/submodules/linphone +++ b/submodules/linphone @@ -1 +1 @@ -Subproject commit 9a21860f49a6c4c52f5aafbcba84cf960d94b400 +Subproject commit d481382fb4c55300737d1f6fc02cecb4d509cc60