blob: 8eb66734712f1a8e612e96fa79aa2b8cfcbbc6f5 [file] [log] [blame]
Alexandre Lision8521baa2015-03-13 11:08:00 -04001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Alexandre Lision8521baa2015-03-13 11:08:00 -04003 * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Alexandre Lision8521baa2015-03-13 11:08:00 -040018 */
Alexandre Lision5855b6a2015-02-03 11:31:05 -050019#import "AppDelegate.h"
20
Alexandre Lisione4041492015-03-20 18:20:43 -040021#import <callmodel.h>
Edric Milaret81315412015-05-13 15:14:56 -040022#import <qapplication.h>
Alexandre Lision745e4d62015-03-22 20:03:10 -040023#import <accountmodel.h>
24#import <protocolmodel.h>
Alexandre Lision0f66bd32016-01-18 11:30:45 -050025#import <media/recordingmodel.h>
26#import <media/textrecording.h>
Alexandre Lision745e4d62015-03-22 20:03:10 -040027#import <QItemSelectionModel>
28#import <account.h>
29
Alexandre Lision3d4143a2015-06-10 14:27:49 -040030#if ENABLE_SPARKLE
31#import <Sparkle/Sparkle.h>
32#endif
33
Alexandre Lisionc65310c2015-04-23 16:44:23 -040034#import "Constants.h"
Alexandre Lision745e4d62015-03-22 20:03:10 -040035#import "RingWizardWC.h"
36
Alexandre Lision3d4143a2015-06-10 14:27:49 -040037#if ENABLE_SPARKLE
38@interface AppDelegate() <SUUpdaterDelegate>
39#else
Alexandre Lision745e4d62015-03-22 20:03:10 -040040@interface AppDelegate()
Alexandre Lision3d4143a2015-06-10 14:27:49 -040041#endif
Alexandre Lision745e4d62015-03-22 20:03:10 -040042
43@property RingWindowController* ringWindowController;
44@property RingWizardWC* wizard;
45
46@end
47
Alexandre Lision5855b6a2015-02-03 11:31:05 -050048@implementation AppDelegate
49
50- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
Alexandre Lision4a7b95e2015-02-20 10:06:43 -050051 [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints"];
52
Alexandre Lisione4041492015-03-20 18:20:43 -040053 [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
54
Edric Milaret81315412015-05-13 15:14:56 -040055 NSAppleEventManager* appleEventManager = [NSAppleEventManager sharedAppleEventManager];
56 [appleEventManager setEventHandler:self andSelector:@selector(handleQuitEvent:withReplyEvent:) forEventClass:kCoreEventClass andEventID:kAEQuitApplication];
57
Alexandre Lision745e4d62015-03-22 20:03:10 -040058 if([self checkForRingAccount]) {
59 [self showMainWindow];
60 } else {
61 [self showWizard];
62 }
Alexandre Lisione4041492015-03-20 18:20:43 -040063 [self connect];
64}
65
66- (void) connect
67{
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -040068 QObject::connect(&CallModel::instance(),
Alexandre Lisione4041492015-03-20 18:20:43 -040069 &CallModel::incomingCall,
70 [=](Call* call) {
Alexandre Lisionc65310c2015-04-23 16:44:23 -040071 BOOL shouldComeToForeground = [[NSUserDefaults standardUserDefaults] boolForKey:Preferences::WindowBehaviour];
72 BOOL shouldNotify = [[NSUserDefaults standardUserDefaults] boolForKey:Preferences::Notifications];
Alexandre Lision61d78a42015-10-06 11:22:47 -040073 if (shouldComeToForeground) {
Alexandre Lisione4041492015-03-20 18:20:43 -040074 [NSApp activateIgnoringOtherApps:YES];
Alexandre Lision61d78a42015-10-06 11:22:47 -040075 if ([self.ringWindowController.window isMiniaturized]) {
76 [self.ringWindowController.window deminiaturize:self];
77 }
78 }
Alexandre Lisione4041492015-03-20 18:20:43 -040079
80 if(shouldNotify) {
81 [self showIncomingNotification:call];
82 }
83 });
Alexandre Lision0f66bd32016-01-18 11:30:45 -050084
85
Alexandre Lision08e72142016-01-19 13:29:36 -050086 QObject::connect(&Media::RecordingModel::instance(),
87 &Media::RecordingModel::unreadMessagesCountChanged,
88 [=](int unreadCount) {
89 NSDockTile *tile = [[NSApplication sharedApplication] dockTile];
90 NSString* label = unreadCount ? [NSString stringWithFormat:@"%d", unreadCount]: @"";
91 [tile setBadgeLabel:label];
Alexandre Lisionef324562016-01-21 13:23:21 -050092 [NSApp requestUserAttention:NSCriticalRequest];
Alexandre Lision08e72142016-01-19 13:29:36 -050093 });
Alexandre Lision0f66bd32016-01-18 11:30:45 -050094
95 QObject::connect(&Media::RecordingModel::instance(),
96 &Media::RecordingModel::newTextMessage,
97 [=](Media::TextRecording* t, ContactMethod* cm) {
98
99 BOOL shouldNotify = [[NSUserDefaults standardUserDefaults] boolForKey:Preferences::Notifications];
100 auto qIdx = t->instantTextMessagingModel()->index(t->instantTextMessagingModel()->rowCount()-1, 0);
101
102 // Don't show a notification if we are sending the text OR window already has focus OR user disabled notifications
103 if(qvariant_cast<Media::Media::Direction>(qIdx.data((int)Media::TextRecording::Role::Direction)) == Media::Media::Direction::OUT
Alexandre Lision4baba4c2016-02-11 13:00:57 -0500104 || self.ringWindowController.window.isMainWindow || !shouldNotify)
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500105 return;
106
107 NSUserNotification* notification = [[NSUserNotification alloc] init];
108
Alexandre Lisionc8180112016-01-27 11:27:50 -0500109 NSString* localizedTitle = [NSString stringWithFormat:NSLocalizedString(@"Message from %@", @"Message from {Name}"), qIdx.data((int)Media::TextRecording::Role::AuthorDisplayname).toString().toNSString()];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500110
111 [notification setTitle:localizedTitle];
112 [notification setSoundName:NSUserNotificationDefaultSoundName];
113 [notification setSubtitle:qIdx.data().toString().toNSString()];
114
115 [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
116 });
Alexandre Lisione4041492015-03-20 18:20:43 -0400117}
118
119- (void) showIncomingNotification:(Call*) call{
Alexandre Lision34607032016-02-08 16:16:49 -0500120 NSUserNotification* notification = [[NSUserNotification alloc] init];
Alexandre Lisionc8180112016-01-27 11:27:50 -0500121 NSString* localizedTitle = [NSString stringWithFormat:
122 NSLocalizedString(@"Incoming call from %@", @"Incoming call from {Name}"), call->peerName().toNSString()];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500123 [notification setTitle:localizedTitle];
124 [notification setSoundName:NSUserNotificationDefaultSoundName];
Alexandre Lisione4041492015-03-20 18:20:43 -0400125
Alexandre Lision34607032016-02-08 16:16:49 -0500126 // try to activate action button
127 @try {
128 [notification setValue:@YES forKey:@"_showsButtons"];
129 }
130 @catch (NSException *exception) {
131 // private API _showsButtons has changed...
132 NSLog(@"Action button not activable on notification");
133 }
134 [notification setActionButtonTitle:NSLocalizedString(@"Refuse", @"Button Action")];
135
Alexandre Lisione4041492015-03-20 18:20:43 -0400136 [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
Alexandre Lision5855b6a2015-02-03 11:31:05 -0500137}
138
Alexandre Lision34607032016-02-08 16:16:49 -0500139- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
140{
141 if(notification.activationType == NSUserNotificationActivationTypeActionButtonClicked) {
142 CallModel::instance().selectedCall() << Call::Action::REFUSE;
143 } else {
144 [NSApp activateIgnoringOtherApps:YES];
145 if ([self.ringWindowController.window isMiniaturized]) {
146 [self.ringWindowController.window deminiaturize:self];
147 }
148 }
149}
150
Alexandre Lision745e4d62015-03-22 20:03:10 -0400151/**
152 * click in MainMenu "Setup Ring"
153 */
154- (IBAction)showWizard:(id)sender {
155 [self showWizard];
156}
157
158- (void) showWizard
159{
Alexandre Lision745e4d62015-03-22 20:03:10 -0400160 if(self.wizard == nil) {
161 self.wizard = [[RingWizardWC alloc] initWithWindowNibName:@"RingWizard"];
162 }
Alexandre Lision76d59692016-01-20 18:06:05 -0500163 [self.wizard.window makeKeyAndOrderFront:self];
Alexandre Lision745e4d62015-03-22 20:03:10 -0400164}
165
166- (void) showMainWindow
167{
Alexandre Lisionb3f7ed62015-08-17 11:53:13 -0400168 if(self.ringWindowController == nil) {
Alexandre Lision745e4d62015-03-22 20:03:10 -0400169 self.ringWindowController = [[RingWindowController alloc] initWithWindowNibName:@"RingWindow"];
Alexandre Lisionb3f7ed62015-08-17 11:53:13 -0400170 }
Alexandre Lision745e4d62015-03-22 20:03:10 -0400171 [self.ringWindowController.window makeKeyAndOrderFront:self];
172}
173
174- (BOOL) checkForRingAccount
175{
Alexandre Lisiona1f07bf2015-07-28 10:30:55 -0400176 BOOL foundRingAcc = NO;
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400177 for (int i = 0 ; i < AccountModel::instance().rowCount() ; ++i) {
178 QModelIndex idx = AccountModel::instance().index(i);
179 Account* acc = AccountModel::instance().getAccountByModelIndex(idx);
Alexandre Lision76d59692016-01-20 18:06:05 -0500180 if(acc->protocol() == Account::Protocol::RING && !acc->isNew()) {
Alexandre Lisiona1f07bf2015-07-28 10:30:55 -0400181 if (acc->displayName().isEmpty())
182 acc->setDisplayName(acc->alias());
183 foundRingAcc = YES;
Alexandre Lision745e4d62015-03-22 20:03:10 -0400184 }
185 }
Alexandre Lisiona1f07bf2015-07-28 10:30:55 -0400186 return foundRingAcc;
Alexandre Lision745e4d62015-03-22 20:03:10 -0400187}
188
Alexandre Lision18e1fcd2015-08-04 14:38:42 -0400189-(void)applicationWillFinishLaunching:(NSNotification *)aNotification
190{
191 NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
192 [appleEventManager setEventHandler:self
193 andSelector:@selector(handleGetURLEvent:withReplyEvent:)
194 forEventClass:kInternetEventClass andEventID:kAEGetURL];
195}
196
197/**
198 * Recognized patterns:
199 * - ring:<hash>
200 * - ring://<hash>
201 */
202- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
203{
204 NSString* query = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
205 NSURL* url = [[NSURL alloc] initWithString:query];
206 NSString* ringID = [url host];
207 if (!ringID) {
208 //not a valid NSURL, try to parse query directly
209 ringID = [query substringFromIndex:@"ring:".length];
210 }
211
212 // check for a valid ring hash
213 NSCharacterSet *hexSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789abcdefABCDEF"];
214 BOOL valid = [[ringID stringByTrimmingCharactersInSet:hexSet] isEqualToString:@""];
215
216 if(valid && ringID.length == 40) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400217 Call* c = CallModel::instance().dialingCall();
Alexandre Lision18e1fcd2015-08-04 14:38:42 -0400218 c->setDialNumber(QString::fromNSString([NSString stringWithFormat:@"ring:%@",ringID]));
219 c << Call::Action::ACCEPT;
220 } else {
221 NSAlert *alert = [[NSAlert alloc] init];
222 [alert addButtonWithTitle:@"OK"];
223 [alert setMessageText:@"Error"];
224 [alert setInformativeText:@"ringID cannot be read from this URL."];
225 [alert setAlertStyle:NSWarningAlertStyle];
226 [alert runModal];
227 }
228}
229
Alexandre Lision745e4d62015-03-22 20:03:10 -0400230- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
231{
232 if([self checkForRingAccount]) {
233 [self showMainWindow];
234 } else {
235 [self showWizard];
236 }
237 return YES;
238}
239
Edric Milaret81315412015-05-13 15:14:56 -0400240- (void)handleQuitEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent
241{
Alexandre Lision76d59692016-01-20 18:06:05 -0500242 [self cleanExit];
Edric Milaret81315412015-05-13 15:14:56 -0400243}
244
245-(void)applicationWillTerminate:(NSNotification *)notification
246{
Alexandre Lision76d59692016-01-20 18:06:05 -0500247 [self cleanExit];
248}
249
250- (void) cleanExit
251{
252 [self.wizard close];
253 [self.ringWindowController close];
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400254 delete CallModel::instance().QObject::parent();
Edric Milaret81315412015-05-13 15:14:56 -0400255 [[NSApplication sharedApplication] terminate:self];
256}
257
Alexandre Lision3d4143a2015-06-10 14:27:49 -0400258#if ENABLE_SPARKLE
259
Alexandre Lision76d59692016-01-20 18:06:05 -0500260#pragma mark - Sparkle delegate
Alexandre Lision3d4143a2015-06-10 14:27:49 -0400261
262- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update
263{
264 [NSApp activateIgnoringOtherApps:YES];
265}
266
267- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)bundle
268{
269 return YES;
270}
271
272- (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater
273{
274 return YES;
275}
276
277- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error
278{
279 NSLog(@"Error:%@", error.localizedDescription);
280}
281
282#endif
Alexandre Lision5855b6a2015-02-03 11:31:05 -0500283@end