/* LinphoneAppDelegate.m * * 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 "PhoneMainView.h" #import "linphoneAppDelegate.h" #import "AddressBook/ABPerson.h" #import "CoreTelephony/CTCallCenter.h" #import "CoreTelephony/CTCall.h" #import "ConsoleViewController.h" #import "LinphoneCoreSettingsStore.h" #include "LinphoneManager.h" #include "linphonecore.h" @implementation UILinphoneWindow @end @implementation LinphoneAppDelegate @synthesize started; #pragma mark - Lifecycle Functions - (id)init { self = [super init]; if(self != nil) { self->started = FALSE; } return self; } - (void)dealloc { [super dealloc]; } #pragma mark - - (void)applicationDidEnterBackground:(UIApplication *)application{ [LinphoneLogger logc:LinphoneLoggerLog format:"applicationDidEnterBackground"]; if(![LinphoneManager isLcReady]) return; [[LinphoneManager instance] enterBackgroundMode]; } - (void)applicationWillResignActive:(UIApplication *)application { [LinphoneLogger logc:LinphoneLoggerLog format:"applicationWillResignActive"]; if(![LinphoneManager isLcReady]) return; LinphoneCore* lc = [LinphoneManager getLc]; LinphoneCall* call = linphone_core_get_current_call(lc); if (call){ /* save call context */ LinphoneManager* instance = [LinphoneManager instance]; instance->currentCallContextBeforeGoingBackground.call = call; instance->currentCallContextBeforeGoingBackground.cameraIsEnabled = linphone_call_camera_enabled(call); const LinphoneCallParams* params = linphone_call_get_current_params(call); if (linphone_call_params_video_enabled(params)) { linphone_call_enable_camera(call, false); } } if (![[LinphoneManager instance] resignActive]) { } } - (void)applicationDidBecomeActive:(UIApplication *)application { [LinphoneLogger logc:LinphoneLoggerLog format:"applicationDidBecomeActive"]; [self startApplication]; LinphoneManager* instance = [LinphoneManager instance]; [instance becomeActive]; LinphoneCore* lc = [LinphoneManager getLc]; LinphoneCall* call = linphone_core_get_current_call(lc); if (call){ if (call == instance->currentCallContextBeforeGoingBackground.call) { const LinphoneCallParams* params = linphone_call_get_current_params(call); if (linphone_call_params_video_enabled(params)) { linphone_call_enable_camera( call, instance->currentCallContextBeforeGoingBackground.cameraIsEnabled); } instance->currentCallContextBeforeGoingBackground.call = 0; } else if ( linphone_call_get_state(call) == LinphoneCallIncomingReceived ) { [[PhoneMainView instance ] displayIncomingCall:call]; // in this case, the ringing sound comes from the notification. // To stop it we have to do the iOS7 ring fix... [self fixRing]; } } } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeNewsstandContentAvailability]; //work around until we can access lpconfig without linphonecore NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys: @"YES", @"start_at_boot_preference", @"YES", @"backgroundmode_preference", #ifdef DEBUG @"YES",@"debugenable_preference", #else @"NO",@"debugenable_preference", #endif nil]; [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; BOOL background_mode = [[NSUserDefaults standardUserDefaults] boolForKey:@"backgroundmode_preference"]; BOOL start_at_boot = [[NSUserDefaults standardUserDefaults] boolForKey:@"start_at_boot_preference"]; if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)] && [UIApplication sharedApplication].applicationState == UIApplicationStateBackground) { // we've been woken up directly to background; if( !start_at_boot || !background_mode ) { // autoboot disabled or no background, and no push: do nothing and wait for a real launch return YES; } } [self startApplication]; NSDictionary *remoteNotif =[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; if (remoteNotif){ [LinphoneLogger log:LinphoneLoggerLog format:@"PushNotification from launch received."]; [self processRemoteNotification:remoteNotif]; } [[PhoneMainView instance] updateStatusBar:nil]; return YES; } - (void)startApplication { // Restart Linphone Core if needed if(![LinphoneManager isLcReady]) { [[LinphoneManager instance] startLibLinphone]; } if([LinphoneManager isLcReady]) { // Only execute one time at application start if(!started) { started = TRUE; [[PhoneMainView instance] startUp]; } } } - (void)applicationWillTerminate:(UIApplication *)application { [LinphoneLogger log:LinphoneLoggerLog format:@"Application Will Terminate"]; } - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { [self startApplication]; if([LinphoneManager isLcReady]) { if([[url scheme] isEqualToString:@"sip"]) { // Go to Dialer view DialerViewController *controller = DYNAMIC_CAST([[PhoneMainView instance] changeCurrentView:[DialerViewController compositeViewDescription]], DialerViewController); if(controller != nil) { [controller setAddress:[url absoluteString]]; } } } return YES; } - (void)fixRing{ if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) { // iOS7 fix for notification sound not stopping. // see http://stackoverflow.com/questions/19124882/stopping-ios-7-remote-notification-sound [[UIApplication sharedApplication] setApplicationIconBadgeNumber: 1]; [[UIApplication sharedApplication] setApplicationIconBadgeNumber: 0]; } } - (void)processRemoteNotification:(NSDictionary*)userInfo{ NSDictionary *aps = [userInfo objectForKey:@"aps"]; if(aps != nil) { NSDictionary *alert = [aps objectForKey:@"alert"]; if(alert != nil) { NSString *loc_key = [alert objectForKey:@"loc-key"]; /*if we receive a remote notification, it is because our TCP background socket was no more working. As a result, break it and refresh registers in order to make sure to receive incoming INVITE or MESSAGE*/ LinphoneCore *lc = [LinphoneManager getLc]; linphone_core_set_network_reachable(lc, FALSE); [LinphoneManager instance].connectivity=none; /*force connectivity to be discovered again*/ if(loc_key != nil) { if([loc_key isEqualToString:@"IM_MSG"]) { [[PhoneMainView instance] addInhibitedEvent:kLinphoneTextReceived]; [[PhoneMainView instance] changeCurrentView:[ChatViewController compositeViewDescription]]; } else if([loc_key isEqualToString:@"IC_MSG"]) { //it's a call NSString *callid=[userInfo objectForKey:@"call-id"]; if (callid) [[LinphoneManager instance] enableAutoAnswerForCallId:callid]; else [LinphoneLogger log:LinphoneLoggerError format:@"PushNotification: does not have call-id yet, fix it !"]; [self fixRing]; } } } } } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { [LinphoneLogger log:LinphoneLoggerLog format:@"PushNotification: Receive %@", userInfo]; [self processRemoteNotification:userInfo]; } - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { [[UIApplication sharedApplication] cancelLocalNotification:notification]; if([notification.userInfo objectForKey:@"callId"] != nil) { [[LinphoneManager instance] acceptCallForCallId:[notification.userInfo objectForKey:@"callId"]]; } else if([notification.userInfo objectForKey:@"chat"] != nil) { NSString *remoteContact = (NSString*)[notification.userInfo objectForKey:@"chat"]; // Go to ChatRoom view [[PhoneMainView instance] changeCurrentView:[ChatViewController compositeViewDescription]]; ChatRoomViewController *controller = DYNAMIC_CAST([[PhoneMainView instance] changeCurrentView:[ChatRoomViewController compositeViewDescription] push:TRUE], ChatRoomViewController); if(controller != nil) { [controller setRemoteAddress:remoteContact]; } } else if([notification.userInfo objectForKey:@"callLog"] != nil) { NSString *callLog = (NSString*)[notification.userInfo objectForKey:@"callLog"]; // Go to HistoryDetails view [[PhoneMainView instance] changeCurrentView:[HistoryViewController compositeViewDescription]]; HistoryDetailsViewController *controller = DYNAMIC_CAST([[PhoneMainView instance] changeCurrentView:[HistoryDetailsViewController compositeViewDescription] push:TRUE], HistoryDetailsViewController); if(controller != nil) { [controller setCallLogId:callLog]; } } } // this method is implemented for iOS7. It is invoked when receiving a push notification for a call and it has "content-available" in the aps section. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { LinphoneManager* lm = [LinphoneManager instance]; // check that linphone is still running if( ![LinphoneManager isLcReady] ) [lm startLibLinphone]; [LinphoneLogger log:LinphoneLoggerLog format:@"Silent PushNotification; userInfo %@", userInfo]; // save the completion handler for later execution. // 2 outcomes: // - if a new call/message is received, the completion handler will be called with "NEWDATA" // - if nothing happens for 15 seconds, the completion handler will be called with "NODATA" lm.silentPushCompletion = completionHandler; [NSTimer scheduledTimerWithTimeInterval:15.0 target:lm selector:@selector(silentPushFailed:) userInfo:nil repeats:FALSE]; // Force Linphone to drop the current socket, this will trigger a refresh registers linphone_core_set_network_reachable([LinphoneManager getLc], FALSE); lm.connectivity=none; /*force connectivity to be discovered again*/ [lm refreshRegisters]; } #pragma mark - PushNotification Functions - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { [LinphoneLogger log:LinphoneLoggerLog format:@"PushNotification: Token %@", deviceToken]; [[LinphoneManager instance] setPushNotificationToken:deviceToken]; } - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error { [LinphoneLogger log:LinphoneLoggerError format:@"PushNotification: Error %@", [error localizedDescription]]; [[LinphoneManager instance] setPushNotificationToken:nil]; } @end