/*
* 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 "LinphoneManager.h"
#import "PhoneMainView.h"
@implementation DialerView
#pragma mark - UICompositeViewDelegate Functions
static UICompositeViewDescription *compositeDescription = nil;
+ (UICompositeViewDescription *)compositeViewDescription {
if (compositeDescription == nil) {
compositeDescription = [[UICompositeViewDescription alloc] init:self.class
statusBar:StatusBarView.class
tabBar:TabBarView.class
sideMenu:SideMenuView.class
fullscreen:false
isLeftFragment:YES
fragmentWith:nil];
compositeDescription.darkBackground = true;
}
return compositeDescription;
}
- (UICompositeViewDescription *)compositeViewDescription {
return self.class.compositeViewDescription;
}
#pragma mark - ViewController Functions
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_padView.hidden =
!IPAD && UIInterfaceOrientationIsLandscape(PhoneMainView.instance.mainViewController.currentOrientation);
// Set observer
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(callUpdateEvent:)
name:kLinphoneCallUpdate
object:nil];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(coreUpdateEvent:)
name:kLinphoneCoreUpdate
object:nil];
// Update on show
LinphoneManager *mgr = LinphoneManager.instance;
LinphoneCall *call = linphone_core_get_current_call(LC);
LinphoneCallState state = (call != NULL) ? linphone_call_get_state(call) : 0;
[self callUpdate:call state:state];
if (IPAD) {
BOOL videoEnabled = linphone_core_video_display_enabled(LC);
BOOL previewPref = [mgr lpConfigBoolForKey:@"preview_preference"];
if (videoEnabled && previewPref) {
linphone_core_set_native_preview_window_id(LC, (__bridge void *)(_videoPreview));
if (!linphone_core_video_preview_enabled(LC)) {
linphone_core_enable_video_preview(LC, TRUE);
}
[_backgroundView setHidden:FALSE];
[_videoCameraSwitch setHidden:FALSE];
} else {
linphone_core_set_native_preview_window_id(LC, NULL);
linphone_core_enable_video_preview(LC, FALSE);
[_backgroundView setHidden:TRUE];
[_videoCameraSwitch setHidden:TRUE];
}
} else {
linphone_core_enable_video_preview(LC, FALSE);
}
[_addressField setText:@""];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[NSNotificationCenter.defaultCenter removeObserver:self];
}
- (void)viewDidLoad {
[super viewDidLoad];
[_zeroButton setDigit:'0'];
[_oneButton setDigit:'1'];
[_twoButton setDigit:'2'];
[_threeButton setDigit:'3'];
[_fourButton setDigit:'4'];
[_fiveButton setDigit:'5'];
[_sixButton setDigit:'6'];
[_sevenButton setDigit:'7'];
[_eightButton setDigit:'8'];
[_nineButton setDigit:'9'];
[_starButton setDigit:'*'];
[_hashButton setDigit:'#'];
[_addressField setAdjustsFontSizeToFitWidth:TRUE]; // Not put it in IB: issue with placeholder size
UILongPressGestureRecognizer *backspaceLongGesture =
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onBackspaceLongClick:)];
[_backspaceButton addGestureRecognizer:backspaceLongGesture];
UILongPressGestureRecognizer *zeroLongGesture =
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onZeroLongClick:)];
[_zeroButton addGestureRecognizer:zeroLongGesture];
UILongPressGestureRecognizer *oneLongGesture =
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onOneLongClick:)];
[_oneButton addGestureRecognizer:oneLongGesture];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(dismissKeyboards)];
[self.view addGestureRecognizer:tap];
if (IPAD) {
if (LinphoneManager.instance.frontCamId != nil) {
// only show camera switch button if we have more than 1 camera
[_videoCameraSwitch setHidden:FALSE];
}
}
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
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;
}
CGRect frame = self.view.frame;
frame.origin = CGPointMake(0, 0);
_videoPreview.frame = frame;
_padView.hidden = !IPAD && UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
if (linphone_core_get_calls_nb(LC)) {
_backButton.hidden = FALSE;
_addContactButton.hidden = TRUE;
} else {
_backButton.hidden = TRUE;
_addContactButton.hidden = FALSE;
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[LinphoneManager.instance shouldPresentLinkPopup];
}
#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];
}
- (void)coreUpdateEvent:(NSNotification *)notif {
@try {
if (IPAD) {
if (linphone_core_video_display_enabled(LC) && linphone_core_video_preview_enabled(LC)) {
linphone_core_set_native_preview_window_id(LC, (__bridge void *)(_videoPreview));
[_backgroundView setHidden:FALSE];
[_videoCameraSwitch setHidden:FALSE];
} else {
linphone_core_set_native_preview_window_id(LC, NULL);
[_backgroundView setHidden:TRUE];
[_videoCameraSwitch setHidden:TRUE];
}
}
} @catch (NSException *exception) {
if ([exception.name isEqualToString:@"LinphoneCoreException"]) {
LOGE(@"Core already destroyed");
return;
}
LOGE(@"Uncaught exception : %@", exception.description);
abort();
}
}
#pragma mark - Debug Functions
- (void)presentMailViewWithTitle:(NSString *)subject forRecipients:(NSArray *)recipients attachLogs:(BOOL)attachLogs {
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *controller = [[MFMailComposeViewController alloc] init];
if (controller) {
controller.mailComposeDelegate = self;
[controller setSubject:subject];
[controller setToRecipients:recipients];
if (attachLogs) {
char *filepath = linphone_core_compress_log_collection();
if (filepath == NULL) {
LOGE(@"Cannot sent logs: file is NULL");
return;
}
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
NSString *filename = [appName stringByAppendingString:@".gz"];
NSString *mimeType = @"text/plain";
if ([filename hasSuffix:@".gz"]) {
mimeType = @"application/gzip";
filename = [appName stringByAppendingString:@".gz"];
} else {
LOGE(@"Unknown extension type: %@, cancelling email", filename);
return;
}
[controller setMessageBody:NSLocalizedString(@"Application logs", nil) isHTML:NO];
[controller addAttachmentData:[NSData dataWithContentsOfFile:[NSString stringWithUTF8String:filepath]]
mimeType:mimeType
fileName:filename];
ms_free(filepath);
}
self.modalPresentationStyle = UIModalPresentationPageSheet;
[self.view.window.rootViewController presentViewController:controller
animated:TRUE
completion:^{
}];
}
} else {
UIAlertController *errView = [UIAlertController alertControllerWithTitle:subject
message:NSLocalizedString(@"Error: no mail account configured",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[self presentViewController:errView animated:YES completion:nil];
}
}
- (BOOL)displayDebugPopup:(NSString *)address {
LinphoneManager *mgr = LinphoneManager.instance;
NSString *debugAddress = [mgr lpConfigStringForKey:@"debug_popup_magic" withDefault:@""];
if (![debugAddress isEqualToString:@""] && [address isEqualToString:debugAddress]) {
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Debug", nil)
message:NSLocalizedString(@"Choose an action", nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
int debugLevel = [LinphoneManager.instance lpConfigIntForKey:@"debugenable_preference"];
BOOL debugEnabled = (debugLevel >= ORTP_DEBUG && debugLevel < ORTP_ERROR);
if (debugEnabled) {
UIAlertAction* continueAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Send logs", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
NSString *appName =
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
NSString *logsAddress = [mgr lpConfigStringForKey:@"debug_popup_email" withDefault:@""];
[self presentMailViewWithTitle:appName forRecipients:@[ logsAddress ] attachLogs:true];
}];
[errView addAction:continueAction];
}
NSString *actionLog =
(debugEnabled ? NSLocalizedString(@"Disable logs", nil) : NSLocalizedString(@"Enable logs", nil));
UIAlertAction* logAction = [UIAlertAction actionWithTitle:actionLog
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
int newDebugLevel = debugEnabled ? 0 : ORTP_DEBUG;
[LinphoneManager.instance lpConfigSetInt:newDebugLevel forKey:@"debugenable_preference"];
[Log enableLogs:newDebugLevel];
}];
[errView addAction:logAction];
UIAlertAction* remAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Remove account(s) and self destruct", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
linphone_core_clear_accounts([LinphoneManager getLc]);
linphone_core_clear_all_auth_info([LinphoneManager getLc]);
@try {
[LinphoneManager.instance destroyLinphoneCore];
} @catch (NSException *e) {
LOGW(@"Exception while destroying linphone core: %@", e);
} @finally {
if ([NSFileManager.defaultManager
isDeletableFileAtPath:[LinphoneManager preferenceFile:@"linphonerc"]] == YES) {
[NSFileManager.defaultManager
removeItemAtPath:[LinphoneManager preferenceFile:@"linphonerc"]
error:nil];
}
#ifdef DEBUG
[LinphoneManager instanceRelease];
#endif
}
[UIApplication sharedApplication].keyWindow.rootViewController = nil;
// make the application crash to be sure that user restart it properly
LOGF(@"Self-destructing in 3..2..1..0!");
}];
[errView addAction:remAction];
[self presentViewController:errView animated:YES completion:nil];
return true;
}
return false;
}
#pragma mark -
- (void)callUpdate:(LinphoneCall *)call state:(LinphoneCallState)state {
BOOL callInProgress = (linphone_core_get_calls_nb(LC) > 0);
_addContactButton.hidden = callInProgress;
_backButton.hidden = !callInProgress;
[_callButton updateIcon];
}
- (void)setAddress:(NSString *)address {
[_addressField setText:address];
}
#pragma mark - UITextFieldDelegate Functions
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
//[textField performSelector:@selector() withObject:nil afterDelay:0];
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == _addressField) {
[_addressField resignFirstResponder];
}
if (textField.text.length > 0) {
LinphoneAddress *addr = [LinphoneUtils normalizeSipOrPhoneAddress:textField.text];
[LinphoneManager.instance call:addr];
if (addr)
linphone_address_destroy(addr);
}
return YES;
}
#pragma mark - MFComposeMailDelegate
- (void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError *)error {
[controller dismissViewControllerAnimated:TRUE completion:nil];
[self.navigationController setNavigationBarHidden:TRUE animated:FALSE];
}
#pragma mark - Action Functions
- (IBAction)onAddContactClick:(id)event {
[ContactSelection setSelectionMode:ContactSelectionModeEdit];
[ContactSelection setAddAddress:[_addressField text]];
[ContactSelection setSipFilter:nil];
[ContactSelection setNameOrEmailFilter:nil];
[ContactSelection enableEmailFilter:FALSE];
[PhoneMainView.instance changeCurrentView:ContactsListView.compositeViewDescription];
}
- (IBAction)onBackClick:(id)event {
[PhoneMainView.instance popToView:CallView.compositeViewDescription];
}
- (IBAction)onAddressChange:(id)sender {
if ([self displayDebugPopup:_addressField.text]) {
_addressField.text = @"";
}
_addContactButton.enabled = _backspaceButton.enabled = ([[_addressField text] length] > 0);
if ([_addressField.text length] == 0) {
[self.view endEditing:YES];
}
}
- (IBAction)onBackspaceClick:(id)sender {
if ([_addressField.text length] > 0) {
[_addressField setText:[_addressField.text substringToIndex:[_addressField.text length] - 1]];
}
}
- (void)onBackspaceLongClick:(id)sender {
[_addressField setText:@""];
}
- (void)onZeroLongClick:(id)sender {
// replace last character with a '+'
NSString *newAddress =
[[_addressField.text substringToIndex:[_addressField.text length] - 1] stringByAppendingString:@"+"];
[_addressField setText:newAddress];
linphone_core_stop_dtmf(LC);
}
- (void)onOneLongClick:(id)sender {
LinphoneManager *lm = LinphoneManager.instance;
NSString *voiceMail = [lm lpConfigStringForKey:@"voice_mail_uri"];
LinphoneAddress *addr = [LinphoneUtils normalizeSipOrPhoneAddress:voiceMail];
if (addr) {
linphone_address_set_display_name(addr, NSLocalizedString(@"Voice mail", nil).UTF8String);
[lm call:addr];
linphone_address_destroy(addr);
} else {
LOGE(@"Cannot call voice mail because URI not set or invalid!");
}
linphone_core_stop_dtmf(LC);
}
- (void)dismissKeyboards {
[self.addressField resignFirstResponder];
}
@end