linphone-iphone/Classes/InCallViewController.m
2012-09-05 10:37:39 +02:00

570 lines
20 KiB
Objective-C

/* InCallViewController.h
*
* Copyright (C) 2009 Belledonne Comunications, Grenoble, France
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import <AudioToolbox/AudioToolbox.h>
#import <AddressBook/AddressBook.h>
#import <QuartzCore/CAAnimation.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/EAGLDrawable.h>
#import "IncallViewController.h"
#import "UICallCell.h"
#import "LinphoneManager.h"
#import "PhoneMainView.h"
#import "UILinphone.h"
#include "linphonecore.h"
const NSInteger SECURE_BUTTON_TAG=5;
@implementation InCallViewController
@synthesize callTableController;
@synthesize callTableView;
@synthesize videoGroup;
@synthesize videoView;
@synthesize videoPreview;
@synthesize videoCameraSwitch;
@synthesize videoWaitingForFirstImage;
#ifdef TEST_VIDEO_VIEW_CHANGE
@synthesize testVideoView;
#endif
#pragma mark - Lifecycle Functions
- (id)init {
self = [super initWithNibName:@"InCallViewController" bundle:[NSBundle mainBundle]];
if(self != nil) {
self->singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showControls:)];
self->videoZoomHandler = [[VideoZoomHandler alloc] init];
}
return self;
}
- (void)dealloc {
[callTableController release];
[callTableView release];
[videoGroup release];
[videoView release];
[videoPreview release];
#ifdef TEST_VIDEO_VIEW_CHANGE
[testVideoView release];
#endif
[videoCameraSwitch release];
[videoWaitingForFirstImage release];
[videoZoomHandler release];
[[PhoneMainView instance].view removeGestureRecognizer:singleFingerTap];
[singleFingerTap release];
// Remove all observer
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
#pragma mark - UICompositeViewDelegate Functions
static UICompositeViewDescription *compositeDescription = nil;
+ (UICompositeViewDescription *)compositeViewDescription {
if(compositeDescription == nil) {
compositeDescription = [[UICompositeViewDescription alloc] init:@"InCall"
content:@"InCallViewController"
stateBar:@"UIStateBar"
stateBarEnabled:true
tabBar:@"UICallBar"
tabBarEnabled:true
fullscreen:false
landscapeMode:true
portraitMode:true];
}
return compositeDescription;
}
#pragma mark - ViewController Functions
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
UIDevice *device = [UIDevice currentDevice];
device.proximityMonitoringEnabled = YES;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (visibleActionSheet != nil) {
[visibleActionSheet dismissWithClickedButtonIndex:visibleActionSheet.cancelButtonIndex animated:NO];
}
if (hideControlsTimer != nil) {
[hideControlsTimer invalidate];
hideControlsTimer = nil;
}
if ([[UIDevice currentDevice].systemVersion doubleValue] < 5.0) {
[callTableController viewWillDisappear:animated];
}
// Remove observer
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kLinphoneCallUpdate
object:nil];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if ([[UIDevice currentDevice].systemVersion doubleValue] < 5.0) {
[callTableController viewWillAppear:animated];
}
// Set observer
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(callUpdateEvent:)
name:kLinphoneCallUpdate
object:nil];
// Update on show
LinphoneCall* call = linphone_core_get_current_call([LinphoneManager getLc]);
LinphoneCallState state = (call != NULL)?linphone_call_get_state(call): 0;
[self callUpdate:call state:state animated:FALSE];
[self orientationUpdate:[PhoneMainView instance].interfaceOrientation];
if ([[UIDevice currentDevice].systemVersion doubleValue] < 5.0) {
[callTableController viewDidAppear:animated];
}
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[[UIApplication sharedApplication] setIdleTimerDisabled:false];
UIDevice *device = [UIDevice currentDevice];
device.proximityMonitoringEnabled = NO;
if ([[UIDevice currentDevice].systemVersion doubleValue] < 5.0) {
[callTableController viewDidDisappear:animated];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Set windows (warn memory leaks)
linphone_core_set_native_video_window_id([LinphoneManager getLc],(unsigned long)videoView);
linphone_core_set_native_preview_window_id([LinphoneManager getLc],(unsigned long)videoPreview);
[singleFingerTap setNumberOfTapsRequired:1];
[singleFingerTap setCancelsTouchesInView: FALSE];
[[PhoneMainView instance].view addGestureRecognizer:singleFingerTap];
[videoZoomHandler setup:videoGroup];
videoGroup.alpha = 0;
[videoCameraSwitch setPreview:videoPreview];
removeTableBackground([callTableController view]);
}
- (void)viewDidUnload {
[super viewDidUnload];
[[PhoneMainView instance].view removeGestureRecognizer:singleFingerTap];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
[self orientationUpdate:toInterfaceOrientation];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
CGRect frame = [videoPreview frame];
switch (toInterfaceOrientation) {
case UIInterfaceOrientationPortrait:
[videoPreview setTransform: CGAffineTransformMakeRotation(0)];
break;
case UIInterfaceOrientationPortraitUpsideDown:
[videoPreview setTransform: CGAffineTransformMakeRotation(M_PI)];
break;
case UIInterfaceOrientationLandscapeLeft:
[videoPreview setTransform: CGAffineTransformMakeRotation(M_PI / 2)];
break;
case UIInterfaceOrientationLandscapeRight:
[videoPreview setTransform: CGAffineTransformMakeRotation(-M_PI / 2)];
break;
default:
break;
}
[videoPreview setFrame:frame];
}
#pragma mark -
- (void)orientationUpdate:(UIInterfaceOrientation)orientation {
int oldLinphoneOrientation = linphone_core_get_device_rotation([LinphoneManager getLc]);
int newRotation = 0;
switch (orientation) {
case UIInterfaceOrientationPortrait:
newRotation = 0;
break;
case UIInterfaceOrientationPortraitUpsideDown:
newRotation = 180;
break;
case UIInterfaceOrientationLandscapeRight:
newRotation = 270;
break;
case UIInterfaceOrientationLandscapeLeft:
newRotation = 90;
break;
default:
newRotation = oldLinphoneOrientation;
}
if (oldLinphoneOrientation != newRotation) {
linphone_core_set_device_rotation([LinphoneManager getLc], newRotation);
LinphoneCall* call = linphone_core_get_current_call([LinphoneManager getLc]);
if (call && linphone_call_params_video_enabled(linphone_call_get_current_params(call))) {
//Orientation has changed, must call update call
linphone_core_update_call([LinphoneManager getLc], call, NULL);
}
}
}
- (void)callUpdate:(LinphoneCall *)call state:(LinphoneCallState)state animated:(BOOL)animated {
// Update table
[callTableView reloadData];
// Fake call update
if(call == NULL) {
return;
}
// Handle data associated with the call
if(state == LinphoneCallReleased) {
[callTableController removeCallData: call];
} else {
[callTableController addCallData: call];
}
switch (state) {
case LinphoneCallIncomingReceived:
case LinphoneCallOutgoingInit:
{
if(linphone_core_get_calls_nb([LinphoneManager getLc]) > 1) {
[callTableController minimizeAll];
}
}
case LinphoneCallConnected:
case LinphoneCallStreamsRunning:
case LinphoneCallUpdated:
{
//check video
if (linphone_call_params_video_enabled(linphone_call_get_current_params(call))) {
[self displayVideoCall:animated];
} else {
[self displayTableCall:animated];
}
break;
}
case LinphoneCallUpdatedByRemote:
{
const LinphoneCallParams* current = linphone_call_get_current_params(call);
const LinphoneCallParams* remote = linphone_call_get_remote_params(call);
/* remote wants to add video */
if (!linphone_call_params_video_enabled(current) &&
linphone_call_params_video_enabled(remote) &&
!linphone_core_get_video_policy([LinphoneManager getLc])->automatically_accept) {
linphone_core_defer_call_update([LinphoneManager getLc], call);
[self displayAskToEnableVideoCall:call];
} else if (linphone_call_params_video_enabled(current) && !linphone_call_params_video_enabled(remote)) {
[self displayTableCall:animated];
}
break;
}
case LinphoneCallPausing:
case LinphoneCallPaused:
case LinphoneCallPausedByRemote:
{
[self displayTableCall:animated];
break;
}
case LinphoneCallEnd:
case LinphoneCallError:
{
if(linphone_core_get_calls_nb([LinphoneManager getLc]) <= 2) {
[callTableController maximizeAll];
}
break;
}
default:
break;
}
}
- (void)showControls:(id)sender {
if (hideControlsTimer) {
[hideControlsTimer invalidate];
hideControlsTimer = nil;
}
if([[[PhoneMainView instance] currentView] equal:[InCallViewController compositeViewDescription]] && videoShown) {
// show controls
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
[[PhoneMainView instance] showTabBar: true];
[[PhoneMainView instance] showStateBar: true];
[videoCameraSwitch setAlpha:1.0];
[UIView commitAnimations];
// hide controls in 5 sec
hideControlsTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:@selector(hideControls:)
userInfo:nil
repeats:NO];
}
}
- (void)hideControls:(id)sender {
if (hideControlsTimer) {
[hideControlsTimer invalidate];
hideControlsTimer = nil;
}
if([[[PhoneMainView instance] currentView] equal:[InCallViewController compositeViewDescription]] && videoShown) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3];
[videoCameraSwitch setAlpha:0.0];
[UIView commitAnimations];
[[PhoneMainView instance] showTabBar: false];
[[PhoneMainView instance] showStateBar: false];
}
}
#ifdef TEST_VIDEO_VIEW_CHANGE
// Define TEST_VIDEO_VIEW_CHANGE in IncallViewController.h to enable video view switching testing
- (void)_debugChangeVideoView {
static bool normalView = false;
if (normalView) {
linphone_core_set_native_video_window_id([LinphoneManager getLc], (unsigned long)videoView);
} else {
linphone_core_set_native_video_window_id([LinphoneManager getLc], (unsigned long)testVideoView);
}
normalView = !normalView;
}
#endif
- (void)enableVideoDisplay:(BOOL)animation {
if(videoShown && animation)
return;
videoShown = true;
[videoZoomHandler resetZoom];
if(animation) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
}
[videoGroup setAlpha:1.0];
[callTableView setAlpha:0.0];
if(animation) {
[UIView commitAnimations];
}
if([[LinphoneManager instance] lpConfigBoolForKey:@"self_video_preference"]) {
[videoPreview setHidden:FALSE];
} else {
[videoPreview setHidden:TRUE];
}
if ([LinphoneManager instance].frontCamId !=nil) {
// only show camera switch button if we have more than 1 camera
[videoCameraSwitch setHidden:FALSE];
}
[videoCameraSwitch setAlpha:0.0];
[[PhoneMainView instance] fullScreen: true];
[[PhoneMainView instance] showTabBar: false];
[[PhoneMainView instance] showStateBar: false];
#ifdef TEST_VIDEO_VIEW_CHANGE
[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(_debugChangeVideoView) userInfo:nil repeats:YES];
#endif
// [self batteryLevelChanged:nil];
[videoWaitingForFirstImage setHidden: NO];
[videoWaitingForFirstImage startAnimating];
LinphoneCall *call = linphone_core_get_current_call([LinphoneManager getLc]);
//linphone_call_params_get_used_video_codec return 0 if no video stream enabled
if (call != NULL && linphone_call_params_get_used_video_codec(linphone_call_get_current_params(call))) {
linphone_call_set_next_video_frame_decoded_callback(call, hideSpinner, self);
}
}
- (void)disableVideoDisplay:(BOOL)animation {
if(!videoShown && animation)
return;
videoShown = false;
if(animation) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
}
[videoGroup setAlpha:0.0];
[[PhoneMainView instance] showTabBar: true];
[callTableView setAlpha:1.0];
[videoCameraSwitch setHidden:TRUE];
if(animation) {
[UIView commitAnimations];
}
if (hideControlsTimer != nil) {
[hideControlsTimer invalidate];
hideControlsTimer = nil;
}
[[PhoneMainView instance] fullScreen:false];
}
- (void)displayVideoCall:(BOOL)animated {
[self enableVideoDisplay:animated];
}
- (void)displayTableCall:(BOOL)animated {
[self disableVideoDisplay:animated];
}
#pragma mark - Spinner Functions
- (void)hideSpinnerIndicator: (LinphoneCall*)call {
videoWaitingForFirstImage.hidden = TRUE;
}
static void hideSpinner(LinphoneCall* call, void* user_data) {
InCallViewController* thiz = (InCallViewController*) user_data;
[thiz hideSpinnerIndicator:call];
}
#pragma mark - Event Functions
- (void)callUpdateEvent: (NSNotification*) notif {
LinphoneCall *call = [[notif.userInfo objectForKey: @"call"] pointerValue];
LinphoneCallState state = [[notif.userInfo objectForKey: @"state"] intValue];
[self callUpdate:call state:state animated:TRUE];
}
#pragma mark - ActionSheet Functions
- (void)dismissActionSheet: (id)o {
if (visibleActionSheet != nil) {
[visibleActionSheet dismissWithClickedButtonIndex:visibleActionSheet.cancelButtonIndex animated:TRUE];
visibleActionSheet = nil;
}
}
- (void)displayAskToEnableVideoCall:(LinphoneCall*) call {
if (linphone_core_get_video_policy([LinphoneManager getLc])->automatically_accept)
return;
const char* lUserNameChars = linphone_address_get_username(linphone_call_get_remote_address(call));
NSString* lUserName = lUserNameChars?[[[NSString alloc] initWithUTF8String:lUserNameChars] autorelease]:NSLocalizedString(@"Unknown",nil);
const char* lDisplayNameChars = linphone_address_get_display_name(linphone_call_get_remote_address(call));
NSString* lDisplayName = [lDisplayNameChars?[[NSString alloc] initWithUTF8String:lDisplayNameChars]:@"" autorelease];
// ask the user if he agrees
CallDelegate* cd = [[CallDelegate alloc] init];
cd.eventType = CD_VIDEO_UPDATE;
cd.delegate = self;
cd.call = call;
if (visibleActionSheet != nil) {
[visibleActionSheet dismissWithClickedButtonIndex:visibleActionSheet.cancelButtonIndex animated:TRUE];
}
NSString* title = [NSString stringWithFormat : NSLocalizedString(@"'%@' would like to enable video",nil), ([lDisplayName length] > 0)?lDisplayName:lUserName];
visibleActionSheet = [[UIActionSheet alloc] initWithTitle:title
delegate:cd
cancelButtonTitle:NSLocalizedString(@"Decline",nil)
destructiveButtonTitle:NSLocalizedString(@"Accept",nil)
otherButtonTitles:nil];
visibleActionSheet.actionSheetStyle = UIActionSheetStyleDefault;
[visibleActionSheet showInView:[PhoneMainView instance].view];
/* start cancel timer */
cd.timeout = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(dismissActionSheet:) userInfo:nil repeats:NO];
[visibleActionSheet release];
}
- (void)actionSheet:(UIActionSheet *)actionSheet ofType:(enum CallDelegateType)type clickedButtonAtIndex:(NSInteger)buttonIndex withUserDatas:(void *)datas {
LinphoneCall* call = (LinphoneCall*)datas;
// maybe we could verify call validity
switch (type) {
case CD_ZRTP: {
if (buttonIndex == 0)
linphone_call_set_authentication_token_verified(call, YES);
else if (buttonIndex == 1)
linphone_call_set_authentication_token_verified(call, NO);
visibleActionSheet = nil;
break;
}
case CD_VIDEO_UPDATE: {
LinphoneCall* call = (LinphoneCall*)datas;
LinphoneCallParams* paramsCopy = linphone_call_params_copy(linphone_call_get_current_params(call));
if ([visibleActionSheet destructiveButtonIndex] == buttonIndex) {
// accept video
linphone_call_params_enable_video(paramsCopy, TRUE);
linphone_core_accept_call_update([LinphoneManager getLc], call, paramsCopy);
} else {
// decline video
[LinphoneLogger logc:LinphoneLoggerLog format:"User declined video proposal"];
linphone_core_accept_call_update([LinphoneManager getLc], call, NULL);
}
linphone_call_params_destroy(paramsCopy);
visibleActionSheet = nil;
break;
}
default:
[LinphoneLogger logc:LinphoneLoggerError format:"Unhandled CallDelegate event of type: %d received - ignoring", type];
}
}
@end