/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-iphone
*
* 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 3 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, see .
*/
#import
#import
#import
#import
#import
#import
#import
#import "CallView.h"
#import "CallSideMenuView.h"
#import "LinphoneManager.h"
#import "PhoneMainView.h"
#import "Utils.h"
#include "linphone/linphonecore.h"
#import "linphoneapp-Swift.h"
const NSInteger SECURE_BUTTON_TAG = 5;
@implementation CallView {
BOOL hiddenVolume;
}
#pragma mark - Lifecycle Functions
- (id)init {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:[NSBundle mainBundle]];
if (self != nil) {
singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleControls:)];
videoZoomHandler = [[VideoZoomHandler alloc] init];
videoHidden = TRUE;
[self updateCallView];
}
return self;
}
#pragma mark - UICompositeViewDelegate Functions
static UICompositeViewDescription *compositeDescription = nil;
+ (UICompositeViewDescription *)compositeViewDescription {
if (compositeDescription == nil) {
compositeDescription = [[UICompositeViewDescription alloc] init:self.class
statusBar:StatusBarView.class
tabBar:nil
sideMenu:CallSideMenuView.class
fullscreen:false
isLeftFragment:YES
fragmentWith:nil];
compositeDescription.darkBackground = true;
}
return compositeDescription;
}
- (UICompositeViewDescription *)compositeViewDescription {
return self.class.compositeViewDescription;
}
#pragma mark - ViewController Functions
- (void)viewDidLoad {
[super viewDidLoad];
_routesEarpieceButton.enabled = !IPAD;
// TODO: fixme! video preview frame is too big compared to openGL preview
// frame, so until this is fixed, temporary disabled it.
#if 0
_videoPreview.layer.borderColor = UIColor.whiteColor.CGColor;
_videoPreview.layer.borderWidth = 1;
#endif
[singleFingerTap setNumberOfTapsRequired:1];
[singleFingerTap setCancelsTouchesInView:FALSE];
[self.videoView addGestureRecognizer:singleFingerTap];
[videoZoomHandler setup:_videoGroup];
_videoGroup.alpha = 0;
[_videoCameraSwitch setPreview:_videoPreview];
UIPanGestureRecognizer *dragndrop =
[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveVideoPreview:)];
dragndrop.minimumNumberOfTouches = 1;
[_videoPreview addGestureRecognizer:dragndrop];
[_zeroButton setDigit:'0'];
[_zeroButton setDtmf:true];
[_oneButton setDigit:'1'];
[_oneButton setDtmf:true];
[_twoButton setDigit:'2'];
[_twoButton setDtmf:true];
[_threeButton setDigit:'3'];
[_threeButton setDtmf:true];
[_fourButton setDigit:'4'];
[_fourButton setDtmf:true];
[_fiveButton setDigit:'5'];
[_fiveButton setDtmf:true];
[_sixButton setDigit:'6'];
[_sixButton setDtmf:true];
[_sevenButton setDigit:'7'];
[_sevenButton setDtmf:true];
[_eightButton setDigit:'8'];
[_eightButton setDtmf:true];
[_nineButton setDigit:'9'];
[_nineButton setDtmf:true];
[_starButton setDigit:'*'];
[_starButton setDtmf:true];
[_hashButton setDigit:'#'];
[_hashButton setDtmf:true];
}
- (void)dealloc {
[PhoneMainView.instance.view removeGestureRecognizer:singleFingerTap];
// Remove all observer
[NSNotificationCenter.defaultCenter removeObserver:self];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_waitView.hidden = TRUE;
CallManager.instance.nextCallIsTransfer = FALSE;
callRecording = FALSE;
_recordButtonOnView.hidden = TRUE;
// Update on show
[self hideRoutes:TRUE animated:FALSE];
[self hideOptions:TRUE animated:FALSE];
[self hidePad:TRUE animated:FALSE];
[self hideSpeaker:LinphoneManager.instance.bluetoothAvailable];
[self callDurationUpdate];
[self onCurrentCallChange];
// Set windows (warn memory leaks)
linphone_core_set_native_video_window_id(LC, (__bridge void *)(_videoView));
linphone_core_set_native_preview_window_id(LC, (__bridge void *)(_videoPreview));
[self previewTouchLift];
// Enable tap
[singleFingerTap setEnabled:TRUE];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(messageReceived:)
name:kLinphoneMessageReceived
object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(bluetoothAvailabilityUpdateEvent:)
name:kLinphoneBluetoothAvailabilityUpdate
object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(callUpdateEvent:)
name:kLinphoneCallUpdate
object:nil];
[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:@selector(callDurationUpdate)
userInfo:nil
repeats:YES];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
[[UIDevice currentDevice] setProximityMonitoringEnabled:TRUE];
[PhoneMainView.instance setVolumeHidden:TRUE];
hiddenVolume = TRUE;
// we must wait didAppear to reset fullscreen mode because we cannot change it in viewwillappear
LinphoneCall *call = linphone_core_get_current_call(LC);
LinphoneCallState state = (call != NULL) ? linphone_call_get_state(call) : 0;
[self callUpdate:call state:state animated:FALSE];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[UIDevice currentDevice] setProximityMonitoringEnabled:FALSE];
[self disableVideoDisplay:TRUE animated:NO];
if (hideControlsTimer != nil) {
[hideControlsTimer invalidate];
hideControlsTimer = nil;
}
if (hiddenVolume) {
[PhoneMainView.instance setVolumeHidden:FALSE];
hiddenVolume = FALSE;
}
if (videoDismissTimer) {
[self dismissVideoActionSheet:videoDismissTimer];
[videoDismissTimer invalidate];
videoDismissTimer = nil;
}
// Remove observer
[NSNotificationCenter.defaultCenter removeObserver:self];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[[UIApplication sharedApplication] setIdleTimerDisabled:false];
[[UIDevice currentDevice] setProximityMonitoringEnabled:FALSE];
[PhoneMainView.instance fullScreen:false];
// Disable tap
[singleFingerTap setEnabled:FALSE];
if (linphone_core_get_calls_nb(LC) == 0) {
// reseting speaker button because no more call
_speakerButton.selected = FALSE;
}
NSString *address = [LinphoneManager.instance lpConfigStringForKey:@"sas_dialog_denied"];
if (address) {
UIConfirmationDialog *securityDialog = [UIConfirmationDialog ShowWithMessage:NSLocalizedString(@"Trust has been denied. Make a call to start the authentication process again.", nil)
cancelMessage:NSLocalizedString(@"CANCEL", nil)
confirmMessage:NSLocalizedString(@"CALL", nil)
onCancelClick:^() {
}
onConfirmationClick:^() {
LinphoneAddress *addr = linphone_address_new(address.UTF8String);
[CallManager.instance startCallWithAddr:addr isSas:TRUE];
linphone_address_unref(addr);
} ];
[securityDialog.securityImage setImage:[UIImage imageNamed:@"security_alert_indicator.png"]];
securityDialog.securityImage.hidden = FALSE;
[securityDialog setSpecialColor];
[LinphoneManager.instance lpConfigSetString:nil forKey:@"sas_dialog_denied"];
}
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
[self updateUnreadMessage:NO];
[self previewTouchLift];
[_recordButtonOnView setHidden:!callRecording];
[self updateCallView];
LinphoneCall *call = linphone_core_get_current_call(LC) ;
if (call && linphone_call_get_state(call) == LinphoneCallStatePausedByRemote) {
_pausedByRemoteView.hidden = NO;
[self updateInfoView:TRUE];
}
_conferenceView.hidden = !linphone_core_is_in_conference(LC);
}
#pragma mark - UI modification
- (void)updateInfoView:(BOOL)pausedByRemote {
CGRect infoFrame = _infoView.frame;
if (pausedByRemote || !videoHidden) {
infoFrame.origin.y = 0;
} else {
infoFrame.origin.y = (_avatarImage.frame.origin.y-66)/2;
}
_infoView.frame = infoFrame;
}
- (void)updateCallView {
CGRect pauseFrame = _callPauseButton.frame;
CGRect recordFrame = _recordButtonOnView.frame;
if (videoHidden) {
pauseFrame.origin.y = _bottomBar.frame.origin.y - pauseFrame.size.height - 60;
} else {
pauseFrame.origin.y = _videoCameraSwitch.frame.origin.y+_videoGroup.frame.origin.y;
}
recordFrame.origin.y = _bottomBar.frame.origin.y - pauseFrame.size.height - 60;
_callPauseButton.frame = pauseFrame;
_recordButtonOnView.frame = recordFrame;
[self updateInfoView:FALSE];
}
- (void)hideSpinnerIndicator:(LinphoneCall *)call {
_videoWaitingForFirstImage.hidden = TRUE;
}
static void hideSpinner(LinphoneCall *call, void *user_data) {
CallView *thiz = (__bridge CallView *)user_data;
[thiz hideSpinnerIndicator:call];
}
- (void)updateBottomBar:(LinphoneCall *)call state:(LinphoneCallState)state {
[_speakerButton update];
[_microButton update];
[_callPauseButton update];
[_conferencePauseButton update];
[_videoButton update];
[_hangupButton update];
_optionsButton.enabled = (!call || !linphone_core_sound_resources_locked(LC));
_optionsTransferButton.enabled = call && !linphone_core_sound_resources_locked(LC);
// enable conference button if 2 calls are presents and at least one is not in the conference
int confSize = linphone_core_get_conference_size(LC) - (linphone_core_is_in_conference(LC) ? 1 : 0);
_optionsConferenceButton.enabled =
((linphone_core_get_calls_nb(LC) > 1) && (linphone_core_get_calls_nb(LC) != confSize));
// Disable transfert in conference
if (linphone_core_get_current_call(LC) == NULL) {
[_optionsTransferButton setEnabled:FALSE];
} else {
[_optionsTransferButton setEnabled:TRUE];
}
switch (state) {
case LinphoneCallEnd:
case LinphoneCallError:
case LinphoneCallIncoming:
case LinphoneCallOutgoing:
[self hidePad:TRUE animated:TRUE];
[self hideOptions:TRUE animated:TRUE];
[self hideRoutes:TRUE animated:TRUE];
default:
break;
}
}
- (void)toggleControls:(id)sender {
bool controlsHidden = (_bottomBar.alpha == 0.0);
[self hideControls:!controlsHidden sender:sender];
}
- (void)timerHideControls:(id)sender {
[self hideControls:TRUE sender:sender];
}
- (void)hideControls:(BOOL)hidden sender:(id)sender {
if (videoHidden && hidden)
return;
if (hideControlsTimer) {
[hideControlsTimer invalidate];
hideControlsTimer = nil;
}
if ([[PhoneMainView.instance currentView] equal:CallView.compositeViewDescription]) {
// show controls
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.35];
_pausedCallsTable.tableView.alpha = _videoCameraSwitch.alpha = _callPauseButton.alpha = _routesView.alpha =
_optionsView.alpha = _numpadView.alpha = _bottomBar.alpha = (hidden ? 0 : 1);
_infoView.alpha = (hidden ? 0 : .8f);
[UIView commitAnimations];
[PhoneMainView.instance hideTabBar:hidden];
[PhoneMainView.instance hideStatusBar:hidden];
if (!hidden) {
// hide controls in 5 sec
hideControlsTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:@selector(timerHideControls:)
userInfo:nil
repeats:NO];
}
}
}
- (void)disableVideoDisplay:(BOOL)disabled animated:(BOOL)animation {
if (disabled == videoHidden && animation)
return;
videoHidden = disabled;
if (!disabled) {
[videoZoomHandler resetZoom];
}
if (animation) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
}
[_videoGroup setAlpha:disabled ? 0 : 1];
[self hideControls:!disabled sender:nil];
if (animation) {
[UIView commitAnimations];
}
// only show camera switch button if we have more than 1 camera
_videoCameraSwitch.hidden = (disabled || !LinphoneManager.instance.frontCamId);
_videoPreview.hidden = (disabled || !linphone_core_self_view_enabled(LC));
if (hideControlsTimer != nil) {
[hideControlsTimer invalidate];
hideControlsTimer = nil;
}
if(![PhoneMainView.instance isIphoneXDevice]){
[PhoneMainView.instance fullScreen:!disabled];
}
[PhoneMainView.instance hideTabBar:!disabled];
if (!disabled) {
#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(LC);
// 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, (__bridge void *)(self));
}
}
}
- (void)displayVideoCall:(BOOL)animated {
[self disableVideoDisplay:FALSE animated:animated];
}
- (void)displayAudioCall:(BOOL)animated {
[self disableVideoDisplay:TRUE animated:animated];
}
- (void)callDurationUpdate {
int duration =
linphone_core_get_current_call(LC) ? linphone_call_get_duration(linphone_core_get_current_call(LC)) : 0;
_durationLabel.text = [LinphoneUtils durationToString:duration];
[_pausedCallsTable update];
[_conferenceCallsTable update];
}
- (void)onCurrentCallChange {
LinphoneCall *call = linphone_core_get_current_call(LC);
_noActiveCallView.hidden = (call || linphone_core_is_in_conference(LC));
_callView.hidden = !call;
_conferenceView.hidden = !linphone_core_is_in_conference(LC);
_callPauseButton.hidden = !call && !linphone_core_is_in_conference(LC);
[_callPauseButton setType:UIPauseButtonType_CurrentCall call:call];
[_conferencePauseButton setType:UIPauseButtonType_Conference call:call];
if (!_callView.hidden) {
const LinphoneAddress *addr = linphone_call_get_remote_address(call);
[ContactDisplay setDisplayNameLabel:_nameLabel forAddress:addr];
char *uri = linphone_address_as_string_uri_only(addr);
ms_free(uri);
[_avatarImage setImage:[FastAddressBook imageForAddress:addr] bordered:YES withRoundedRadius:YES];
}
}
- (void)hidePad:(BOOL)hidden animated:(BOOL)animated {
if (hidden) {
[_numpadButton setOff];
} else {
[_numpadButton setOn];
}
if (hidden != _numpadView.hidden) {
if (animated) {
[self hideAnimation:hidden forView:_numpadView completion:nil];
} else {
[_numpadView setHidden:hidden];
}
}
}
- (void)hideRoutes:(BOOL)hidden animated:(BOOL)animated {
if (hidden) {
[_routesButton setOff];
} else {
[_routesButton setOn];
}
_routesBluetoothButton.selected = [CallManager.instance isBluetoothEnabled];
_routesSpeakerButton.selected = [CallManager.instance isSpeakerEnabled];
_routesEarpieceButton.selected = !_routesBluetoothButton.selected && !_routesSpeakerButton.selected;
if (hidden != _routesView.hidden) {
if (animated) {
[self hideAnimation:hidden forView:_routesView completion:nil];
} else {
[_routesView setHidden:hidden];
}
}
}
- (void)hideOptions:(BOOL)hidden animated:(BOOL)animated {
if (hidden) {
[_optionsButton setOff];
} else {
[_optionsButton setOn];
}
if (hidden != _optionsView.hidden) {
if (animated) {
[self hideAnimation:hidden forView:_optionsView completion:nil];
} else {
[_optionsView setHidden:hidden];
}
}
}
- (void)hideSpeaker:(BOOL)hidden {
_speakerButton.hidden = hidden;
_routesButton.hidden = !hidden;
}
#pragma mark - Event Functions
- (void)bluetoothAvailabilityUpdateEvent:(NSNotification *)notif {
bool available = [[notif.userInfo objectForKey:@"available"] intValue];
dispatch_async(dispatch_get_main_queue(), ^{
[self hideSpeaker:available];
});
}
- (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];
}
- (void)callUpdate:(LinphoneCall *)call state:(LinphoneCallState)state animated:(BOOL)animated {
[self updateBottomBar:call state:state];
if (hiddenVolume) {
[PhoneMainView.instance setVolumeHidden:FALSE];
hiddenVolume = FALSE;
}
// Update tables
[_pausedCallsTable update];
[_conferenceCallsTable update];
static LinphoneCall *currentCall = NULL;
if (!currentCall || linphone_core_get_current_call(LC) != currentCall) {
currentCall = linphone_core_get_current_call(LC);
[self onCurrentCallChange];
}
// Fake call update
if (call == NULL) {
return;
}
BOOL shouldDisableVideo = !currentCall || !linphone_call_params_video_enabled(linphone_call_get_current_params(currentCall));
if (videoHidden != shouldDisableVideo) {
if (!shouldDisableVideo) {
[self displayVideoCall:animated];
} else {
[self displayAudioCall:animated];
}
}
if (!shouldDisableVideo && !linphone_core_is_in_conference(LC) && // camera is diabled duiring conference, it must be activated after leaving conference.
[UIApplication sharedApplication].applicationState == UIApplicationStateActive) { // Camera should not be enabled when in background)
linphone_call_enable_camera(call, TRUE);
}
[self updateCallView];
if (state != LinphoneCallPausedByRemote) {
_pausedByRemoteView.hidden = YES;
}
switch (state) {
case LinphoneCallIncomingReceived:
case LinphoneCallOutgoingInit:
case LinphoneCallConnected:
case LinphoneCallStreamsRunning: {
// check video, because video can be disabled because of the low bandwidth.
if (!linphone_call_params_video_enabled(linphone_call_get_current_params(call))) {
const LinphoneCallParams *param = linphone_call_get_current_params(call);
CallAppData *data = [CallManager getAppDataWithCall:call];
if (state == LinphoneCallStreamsRunning && data && data.videoRequested && linphone_call_params_low_bandwidth_enabled(param)) {
// too bad video was not enabled because low bandwidth
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Low bandwidth", nil)
message:NSLocalizedString(@"Video cannot be activated because of low bandwidth "
@"condition, only audio is available",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Continue", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[self presentViewController:errView animated:YES completion:nil];
data.videoRequested = FALSE;
[CallManager setAppDataWithCall:call appData:data];
}
}
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_core_video_display_enabled(LC) && !linphone_call_params_video_enabled(current) &&
linphone_call_params_video_enabled(remote)) &&
(!linphone_core_get_video_policy(LC)->automatically_accept ||
(([UIApplication sharedApplication].applicationState != UIApplicationStateActive) &&
floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max))) {
linphone_core_defer_call_update(LC, call);
[self displayAskToEnableVideoCall:call];
} else if (linphone_call_params_video_enabled(current) && !linphone_call_params_video_enabled(remote)) {
[self displayAudioCall:animated];
}
break;
}
case LinphoneCallPausing:
case LinphoneCallPaused:
[self displayAudioCall:animated];
break;
case LinphoneCallPausedByRemote:
[self displayAudioCall:animated];
if (call == linphone_core_get_current_call(LC)) {
_pausedByRemoteView.hidden = NO;
[self updateInfoView:TRUE];
}
break;
case LinphoneCallEnd:
case LinphoneCallError:
default:
break;
}
}
#pragma mark - ActionSheet Functions
- (void)displayAskToEnableVideoCall:(LinphoneCall *)call {
if (linphone_call_params_get_local_conference_mode(linphone_call_get_current_params(call))) {
return;
}
if (linphone_core_get_video_policy(LC)->automatically_accept &&
!([UIApplication sharedApplication].applicationState != UIApplicationStateActive))
return;
NSString *username = [FastAddressBook displayNameForAddress:linphone_call_get_remote_address(call)];
NSString *title = [NSString stringWithFormat:NSLocalizedString(@"%@ would like to enable video", nil), username];
if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive &&
floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) {
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = NSLocalizedString(@"Video request", nil);
content.body = title;
content.categoryIdentifier = @"video_request";
content.userInfo = @{
@"CallId" : [NSString stringWithUTF8String:linphone_call_log_get_call_id(linphone_call_get_call_log(call))]
};
UNNotificationRequest *req =
[UNNotificationRequest requestWithIdentifier:@"video_request" content:content trigger:NULL];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:req
withCompletionHandler:^(NSError *_Nullable error) {
// Enable or disable features based on authorization.
if (error) {
LOGD(@"Error while adding notification request :");
LOGD(error.description);
}
}];
} else {
UIConfirmationDialog *sheet = [UIConfirmationDialog ShowWithMessage:title
cancelMessage:nil
confirmMessage:NSLocalizedString(@"ACCEPT", nil)
onCancelClick:^() {
LOGI(@"User declined video proposal");
if (call == linphone_core_get_current_call(LC)) {
[CallManager.instance acceptVideoWithCall:call confirm:FALSE];
[videoDismissTimer invalidate];
videoDismissTimer = nil;
}
}
onConfirmationClick:^() {
LOGI(@"User accept video proposal");
if (call == linphone_core_get_current_call(LC)) {
[CallManager.instance acceptVideoWithCall:call confirm:TRUE];
[videoDismissTimer invalidate];
videoDismissTimer = nil;
}
}
inController:self];
videoDismissTimer = [NSTimer scheduledTimerWithTimeInterval:30
target:self
selector:@selector(dismissVideoActionSheet:)
userInfo:sheet
repeats:NO];
}
}
- (void)dismissVideoActionSheet:(NSTimer *)timer {
UIConfirmationDialog *sheet = (UIConfirmationDialog *)timer.userInfo;
[sheet dismiss];
}
#pragma mark VideoPreviewMoving
- (void)moveVideoPreview:(UIPanGestureRecognizer *)dragndrop {
CGPoint center = [dragndrop locationInView:_videoPreview.superview];
_videoPreview.center = center;
if (dragndrop.state == UIGestureRecognizerStateEnded) {
[self previewTouchLift];
}
}
- (CGFloat)coerce:(CGFloat)value betweenMin:(CGFloat)min andMax:(CGFloat)max {
return MAX(min, MIN(value, max));
}
- (void)previewTouchLift {
CGRect previewFrame = _videoPreview.frame;
previewFrame.origin.x = [self coerce:previewFrame.origin.x
betweenMin:5
andMax:(UIScreen.mainScreen.bounds.size.width - 5 - previewFrame.size.width)];
previewFrame.origin.y = [self coerce:previewFrame.origin.y
betweenMin:5
andMax:(UIScreen.mainScreen.bounds.size.height - 5 - previewFrame.size.height)];
if (!CGRectEqualToRect(previewFrame, _videoPreview.frame)) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.3
animations:^{
LOGD(@"Recentering preview to %@", NSStringFromCGRect(previewFrame));
_videoPreview.frame = previewFrame;
}];
});
}
}
#pragma mark - Action Functions
- (IBAction)onNumpadClick:(id)sender {
if ([_numpadView isHidden]) {
[self hidePad:FALSE animated:ANIMATED];
} else {
[self hidePad:TRUE animated:ANIMATED];
}
}
- (IBAction)onChatClick:(id)sender {
const LinphoneCall *currentCall = linphone_core_get_current_call(LC);
const LinphoneAddress *addr = currentCall ? linphone_call_get_remote_address(currentCall) : NULL;
// TODO encrpted or unencrpted
[LinphoneManager.instance lpConfigSetBool:TRUE forKey:@"create_chat"];
[PhoneMainView.instance getOrCreateOneToOneChatRoom:addr waitView:_waitView isEncrypted:FALSE];
}
- (IBAction)onRecordClick:(id)sender {
if (![_optionsView isHidden])
[self hideOptions:TRUE animated:ANIMATED];
if (callRecording) {
[self onRecordOnViewClick:nil];
} else {
LOGD(@"Recording Starts");
[_recordButton setImage:[UIImage imageNamed:@"rec_off_default.png"] forState:UIControlStateNormal];
[_recordButtonOnView setHidden:FALSE];
LinphoneCall *call = linphone_core_get_current_call(LC);
linphone_call_start_recording(call);
callRecording = TRUE;
}
}
- (IBAction)onRecordOnViewClick:(id)sender {
LOGD(@"Recording Stops");
[_recordButton setImage:[UIImage imageNamed:@"rec_on_default.png"] forState:UIControlStateNormal];
[_recordButtonOnView setHidden:TRUE];
LinphoneCall *call = linphone_core_get_current_call(LC);
linphone_call_stop_recording(call);
callRecording = FALSE;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *writablePath = [paths objectAtIndex:0];
writablePath = [writablePath stringByAppendingString:@"/"];
NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:writablePath error:NULL];
if (directoryContent) {
return;
}
}
- (IBAction)onRoutesBluetoothClick:(id)sender {
[self hideRoutes:TRUE animated:TRUE];
[CallManager.instance changeRouteToBluetooth];
}
- (IBAction)onRoutesEarpieceClick:(id)sender {
[self hideRoutes:TRUE animated:TRUE];
[CallManager.instance changeRouteToDefault];
}
- (IBAction)onRoutesSpeakerClick:(id)sender {
[self hideRoutes:TRUE animated:TRUE];
[CallManager.instance changeRouteToSpeaker];
}
- (IBAction)onRoutesClick:(id)sender {
if ([_routesView isHidden]) {
[self hideRoutes:FALSE animated:ANIMATED];
} else {
[self hideRoutes:TRUE animated:ANIMATED];
}
}
- (IBAction)onOptionsClick:(id)sender {
if ([_optionsView isHidden]) {
[self hideOptions:FALSE animated:ANIMATED];
} else {
[self hideOptions:TRUE animated:ANIMATED];
}
}
- (IBAction)onOptionsTransferClick:(id)sender {
[self hideOptions:TRUE animated:TRUE];
DialerView *view = VIEW(DialerView);
[view setAddress:@""];
CallManager.instance.nextCallIsTransfer = TRUE;
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
}
- (IBAction)onOptionsAddClick:(id)sender {
[self hideOptions:TRUE animated:TRUE];
DialerView *view = VIEW(DialerView);
[view setAddress:@""];
CallManager.instance.nextCallIsTransfer = FALSE;
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
}
- (IBAction)onOptionsConferenceClick:(id)sender {
[self hideOptions:TRUE animated:TRUE];
[CallManager.instance groupCall];
}
#pragma mark - Animation
- (void)hideAnimation:(BOOL)hidden forView:(UIView *)target completion:(void (^)(BOOL finished))completion {
if (hidden) {
int original_y = target.frame.origin.y;
CGRect newFrame = target.frame;
newFrame.origin.y = self.view.frame.size.height;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
target.frame = newFrame;
}
completion:^(BOOL finished) {
CGRect originFrame = target.frame;
originFrame.origin.y = original_y;
target.hidden = YES;
target.frame = originFrame;
if (completion)
completion(finished);
}];
} else {
CGRect frame = target.frame;
int original_y = frame.origin.y;
frame.origin.y = self.view.frame.size.height;
target.frame = frame;
frame.origin.y = original_y;
target.hidden = NO;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
target.frame = frame;
}
completion:^(BOOL finished) {
target.frame = frame; // in case application did not finish
if (completion)
completion(finished);
}];
}
}
#pragma mark - Bounce
- (void)messageReceived:(NSNotification *)notif {
[self updateUnreadMessage:TRUE];
}
- (void)updateUnreadMessage:(BOOL)appear {
int unreadMessage = [LinphoneManager unreadMessageCount];
if (unreadMessage > 0) {
_chatNotificationLabel.text = [NSString stringWithFormat:@"%i", unreadMessage];
[_chatNotificationView startAnimating:appear];
} else {
[_chatNotificationView stopAnimating:appear];
}
}
@end