blob: d6d43c70d2ed6780341df6b9caeee7faa2cfadec [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 Lisionb9f3f942016-07-23 14:29:33 -040019#import <SystemConfiguration/SystemConfiguration.h>
20
Alexandre Lision5855b6a2015-02-03 11:31:05 -050021#import "AppDelegate.h"
22
Kateryna Kostiuke3503842018-12-12 16:39:45 -050023//lrc
Alexandre Lisione4041492015-03-20 18:20:43 -040024#import <callmodel.h>
Kateryna Kostiukecaa3952018-07-13 16:00:34 -040025#import <api/lrc.h>
Kateryna Kostiuk23222fe2018-11-16 14:28:02 -050026#import <api/newaccountmodel.h>
Kateryna Kostiuke3503842018-12-12 16:39:45 -050027#import <api/behaviorcontroller.h>
28#import <api/conversation.h>
29#import <api/newcallmodel.h>
Kateryna Kostiukabf4e272017-04-18 14:18:00 -040030
Alexandre Lision745e4d62015-03-22 20:03:10 -040031
Alexandre Lision3d4143a2015-06-10 14:27:49 -040032#if ENABLE_SPARKLE
33#import <Sparkle/Sparkle.h>
34#endif
35
Alexandre Lisionc65310c2015-04-23 16:44:23 -040036#import "Constants.h"
Alexandre Lision745e4d62015-03-22 20:03:10 -040037#import "RingWizardWC.h"
Alexandre Lision62005312016-01-28 15:55:16 -050038#import "DialpadWC.h"
Kateryna Kostiuke3503842018-12-12 16:39:45 -050039#import "utils.h"
Alexandre Lision745e4d62015-03-22 20:03:10 -040040
Alexandre Lision3d4143a2015-06-10 14:27:49 -040041#if ENABLE_SPARKLE
42@interface AppDelegate() <SUUpdaterDelegate>
43#else
Alexandre Lision745e4d62015-03-22 20:03:10 -040044@interface AppDelegate()
Alexandre Lision3d4143a2015-06-10 14:27:49 -040045#endif
Alexandre Lision745e4d62015-03-22 20:03:10 -040046
47@property RingWindowController* ringWindowController;
48@property RingWizardWC* wizard;
Alexandre Lision62005312016-01-28 15:55:16 -050049@property DialpadWC* dialpad;
Alexandre Lisionb9f3f942016-07-23 14:29:33 -040050@property (nonatomic, strong) dispatch_queue_t scNetworkQueue;
51@property (nonatomic, assign) SCNetworkReachabilityRef currentReachability;
Adrien Béraud022b57a2018-09-15 15:49:07 -040052@property (strong) id activity;
Alexandre Lision745e4d62015-03-22 20:03:10 -040053
54@end
55
Kateryna Kostiuke3503842018-12-12 16:39:45 -050056NSString * const MESSAGE_NOTIFICATION = @"message_notification_type";
57NSString * const CALL_NOTIFICATION = @"call_notification_type";
58NSString * const CONTACT_REQUEST_NOTIFICATION = @"contact_request_notification_type";
59
60NSString * const ACCOUNT_ID = @"account_id_notification_info";
61NSString * const CALL_ID = @"call_id_notification_info";
62NSString * const CONVERSATION_ID = @"conversation_id_notification_info";
63NSString * const CONTACT_URI = @"contact_uri_notification_info";
64NSString * const NOTIFICATION_TYPE = @"contact_type_notification_info";
65
66
Kateryna Kostiukecaa3952018-07-13 16:00:34 -040067@implementation AppDelegate {
Alexandre Lision5855b6a2015-02-03 11:31:05 -050068
Kateryna Kostiukecaa3952018-07-13 16:00:34 -040069std::unique_ptr<lrc::api::Lrc> lrc;
70}
Andreas Traczyke4d6e782018-03-22 17:51:30 -040071
Alexandre Lision5855b6a2015-02-03 11:31:05 -050072- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
Kateryna Kostiuk0bc4c592018-06-05 13:35:19 -040073
74 // hide "Check for update" menu item when sparkle is disabled
75 NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu];
76 NSMenu *ringMenu = [[mainMenu itemAtIndex:0] submenu];
77 NSMenuItem *updateItem = [ringMenu itemAtIndex:1];
78#if ENABLE_SPARKLE
79 updateItem.hidden = false;
80#else
81 updateItem.hidden = true;
82#endif
83
Alexandre Lision4a7b95e2015-02-20 10:06:43 -050084 [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints"];
85
Alexandre Lisione4041492015-03-20 18:20:43 -040086 [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
87
Edric Milaret81315412015-05-13 15:14:56 -040088 NSAppleEventManager* appleEventManager = [NSAppleEventManager sharedAppleEventManager];
89 [appleEventManager setEventHandler:self andSelector:@selector(handleQuitEvent:withReplyEvent:) forEventClass:kCoreEventClass andEventID:kAEQuitApplication];
Kateryna Kostiukecaa3952018-07-13 16:00:34 -040090 lrc = std::make_unique<lrc::api::Lrc>();
Edric Milaret81315412015-05-13 15:14:56 -040091
Alexandre Lision745e4d62015-03-22 20:03:10 -040092 if([self checkForRingAccount]) {
93 [self showMainWindow];
94 } else {
95 [self showWizard];
96 }
Alexandre Lisione4041492015-03-20 18:20:43 -040097 [self connect];
Alexandre Lisionb9f3f942016-07-23 14:29:33 -040098
99 dispatch_queue_t queue = NULL;
100 queue = dispatch_queue_create("scNetworkReachability", DISPATCH_QUEUE_SERIAL);
101 [self setScNetworkQueue:queue];
102 [self beginObservingReachabilityStatus];
Adrien Béraud022b57a2018-09-15 15:49:07 -0400103 NSActivityOptions options = NSActivitySuddenTerminationDisabled | NSActivityAutomaticTerminationDisabled | NSActivityBackground;
104 self.activity = [[NSProcessInfo processInfo] beginActivityWithOptions:options reason:@"Receiving calls and messages"];
Alexandre Lisionb9f3f942016-07-23 14:29:33 -0400105}
106
107- (void) beginObservingReachabilityStatus
108{
109 SCNetworkReachabilityRef reachabilityRef = NULL;
110
111 void (^callbackBlock)(SCNetworkReachabilityFlags) = ^(SCNetworkReachabilityFlags flags) {
112 BOOL reachable = (flags & kSCNetworkReachabilityFlagsReachable) != 0;
113 [[NSOperationQueue mainQueue] addOperationWithBlock:^{
Kateryna Kostiuk44432012018-11-28 09:39:10 -0500114 lrc->connectivityChanged();
Alexandre Lisionb9f3f942016-07-23 14:29:33 -0400115 }];
116 };
117
118 SCNetworkReachabilityContext context = {
119 .version = 0,
120 .info = (void *)CFBridgingRetain(callbackBlock),
121 .release = CFRelease
122 };
123
124 reachabilityRef = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, "test");
125 if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)){
126 if (!SCNetworkReachabilitySetDispatchQueue(reachabilityRef, [self scNetworkQueue]) ){
127 // Remove our callback if we can't use the queue
128 SCNetworkReachabilitySetCallback(reachabilityRef, NULL, NULL);
129 }
130 [self setCurrentReachability:reachabilityRef];
131 }
132}
133
134- (void) endObsvervingReachabilityStatusForHost:(NSString *)__unused host
135{
136 // Un-set the dispatch queue
137 if (SCNetworkReachabilitySetDispatchQueue([self currentReachability], NULL) ){
138 SCNetworkReachabilitySetCallback([self currentReachability], NULL, NULL);
139 }
140}
141
142static void ReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkConnectionFlags flags, void* info)
143{
144 void (^callbackBlock)(SCNetworkReachabilityFlags) = (__bridge id)info;
145 callbackBlock(flags);
Alexandre Lisione4041492015-03-20 18:20:43 -0400146}
147
148- (void) connect
149{
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500150 QObject::connect(&lrc->getBehaviorController(),
151 &lrc::api::BehaviorController::newTrustRequest,
152 [self] (const std::string& accountId, const std::string& contactUri) {
153 BOOL shouldNotify = [[NSUserDefaults standardUserDefaults] boolForKey:Preferences::ContactRequestNotifications];
154 if(!shouldNotify) {
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500155 return;
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500156 }
157 NSUserNotification* notification = [[NSUserNotification alloc] init];
158 auto contactModel = lrc->getAccountModel()
159 .getAccountInfo(accountId).contactModel.get();
160 NSString* name = contactModel->getContact(contactUri)
161 .registeredName.empty() ?
162 @(contactUri.c_str()) :
163 @(contactModel->getContact(contactUri).registeredName.c_str());
164 NSString* localizedMessage =
165 NSLocalizedString(@"Send you a contact request",
166 @"Notification message");
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500167
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500168 NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
169 userInfo[ACCOUNT_ID] = @(accountId.c_str());
170 userInfo[CONTACT_URI] = @(contactUri.c_str());
171 userInfo[NOTIFICATION_TYPE] = CONTACT_REQUEST_NOTIFICATION;
172
173 [notification setTitle: name];
174 notification.informativeText = localizedMessage;
175 [notification setSoundName:NSUserNotificationDefaultSoundName];
176 [notification setUserInfo: userInfo];
177
178 [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
179
180 });
181
182 QObject::connect(&lrc->getBehaviorController(),
183 &lrc::api::BehaviorController::showIncomingCallView,
184 [self] (const std::string& accountId, lrc::api::conversation::Info conversationInfo) {
185 BOOL shouldNotify = [[NSUserDefaults standardUserDefaults] boolForKey:Preferences::CallNotifications];
186 if(!shouldNotify) {
187 return;
188 }
189 NSString* name = bestIDForConversation(conversationInfo, *lrc->getAccountModel().getAccountInfo(accountId).conversationModel.get());
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500190 NSUserNotification* notification = [[NSUserNotification alloc] init];
191
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500192 NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
193 userInfo[ACCOUNT_ID] = @(accountId.c_str());
194 userInfo[CALL_ID] = @(conversationInfo.callId.c_str());
195 userInfo[CONVERSATION_ID] = @(conversationInfo.uid.c_str());
196 userInfo[NOTIFICATION_TYPE] = CALL_NOTIFICATION;
197
198 NSString* localizedTitle = [NSString stringWithFormat:
199 NSLocalizedString(@"Incoming call from %@", @"Incoming call from {Name}"),
200 name];
201 // try to activate action button
202 @try {
203 [notification setValue:@YES forKey:@"_showsButtons"];
204 }
205 @catch (NSException *exception) {
206 NSLog(@"Action button not activable on notification");
207 }
208 [notification setUserInfo: userInfo];
209 [notification setOtherButtonTitle:NSLocalizedString(@"Refuse", @"Button Action")];
210 [notification setActionButtonTitle:NSLocalizedString(@"Accept", @"Button Action")];
211 [notification setTitle:localizedTitle];
212 [notification setSoundName:NSUserNotificationDefaultSoundName];
213
214 [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
215 });
216
217 QObject::connect(&lrc->getBehaviorController(),
218 &lrc::api::BehaviorController::newUnreadInteraction,
219 [self] (const std::string& accountId, const std::string& conversation,
220 uint64_t interactionId, const lrc::api::interaction::Info& interaction) {
221 BOOL shouldNotify = [[NSUserDefaults standardUserDefaults] boolForKey:Preferences::MessagesNotifications];
222 if(!shouldNotify) {
223 return;
224 }
225 NSUserNotification* notification = [[NSUserNotification alloc] init];
226
227 NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
228 userInfo[ACCOUNT_ID] = @(accountId.c_str());
229 userInfo[CONVERSATION_ID] = @(conversation.c_str());
230 userInfo[NOTIFICATION_TYPE] = MESSAGE_NOTIFICATION;
231 NSString* name = @(interaction.authorUri.c_str());
232 auto convIt = getConversationFromUid(conversation, *lrc->getAccountModel().getAccountInfo(accountId).conversationModel.get());
233 auto convQueue = lrc->getAccountModel().getAccountInfo(accountId).conversationModel.get()->allFilteredConversations();
234 if (convIt != convQueue.end()) {
235 name = bestIDForConversation(*convIt, *lrc->getAccountModel().getAccountInfo(accountId).conversationModel.get());
236 }
237 NSString* localizedTitle = [NSString stringWithFormat:
238 NSLocalizedString(@"Incoming message from %@",@"Incoming message from {Name}"),
239 name];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500240
241 [notification setTitle:localizedTitle];
242 [notification setSoundName:NSUserNotificationDefaultSoundName];
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500243 [notification setSubtitle:@(interaction.body.c_str())];
244 [notification setUserInfo: userInfo];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500245
246 [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
247 });
Alexandre Lisione4041492015-03-20 18:20:43 -0400248}
249
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500250- (void)userNotificationCenter:(NSUserNotificationCenter *)center didDismissAlert:(NSUserNotification *)alert {
251 // check if user click refuse on incoming call notifications
252 if(alert.activationType != NSUserNotificationActivationTypeNone) {
253 return;
Alexandre Lision34607032016-02-08 16:16:49 -0500254 }
Alexandre Lision34607032016-02-08 16:16:49 -0500255
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500256 auto info = alert.userInfo;
257 if(!info) {
258 return;
259 }
260 NSString* identifier = info[NOTIFICATION_TYPE];
261 NSString* callId = info[CALL_ID];
262 NSString* accountId = info[ACCOUNT_ID];
263 if(!identifier || !callId || !accountId) {
264 return;
265 }
266 if([identifier isEqualToString: CALL_NOTIFICATION]) {
267 auto accountInfo = &lrc->getAccountModel().getAccountInfo([accountId UTF8String]);
268 if (accountInfo == nil)
269 return;
270 auto* callModel = accountInfo->callModel.get();
271 callModel->hangUp([callId UTF8String]);
272 }
Alexandre Lision5855b6a2015-02-03 11:31:05 -0500273}
274
Alexandre Lision34607032016-02-08 16:16:49 -0500275- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
276{
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500277 auto info = notification.userInfo;
278 if(!info) {
279 return;
280 }
281 NSString* identifier = info[NOTIFICATION_TYPE];
282 if([identifier isEqualToString: CALL_NOTIFICATION]) {
283 if(notification.activationType == NSUserNotificationActivationTypeActionButtonClicked
284 || notification.activationType == NSUserNotificationActivationTypeContentsClicked) {
285 NSString* callId = info[CALL_ID];
286 NSString* accountId = info[ACCOUNT_ID];
287 NSString *conversationId = info[CONVERSATION_ID];
288 auto accountInfo = &lrc->getAccountModel().getAccountInfo([accountId UTF8String]);
289 if (accountInfo == nil)
290 return;
291 auto* callModel = accountInfo->callModel.get();
292 callModel->accept([callId UTF8String]);
Alexandre Lision34607032016-02-08 16:16:49 -0500293 [self.ringWindowController.window deminiaturize:self];
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500294 [_ringWindowController showCall:callId forAccount:accountId forConversation:conversationId];
295 }
296 } else if(notification.activationType == NSUserNotificationActivationTypeContentsClicked) {
297 [self.ringWindowController.window deminiaturize:self];
298 if([identifier isEqualToString: MESSAGE_NOTIFICATION]) {
299 NSString* accountId = info[ACCOUNT_ID];
300 NSString *conversationId = info[CONVERSATION_ID];
301 [_ringWindowController showConversation:conversationId forAccount:accountId];
302 } else if([identifier isEqualToString: CONTACT_REQUEST_NOTIFICATION]) {
303 NSString* accountId = info[ACCOUNT_ID];
304 NSString *contactUri = info[CONTACT_URI];
305 [_ringWindowController showContactRequestFor:accountId contactUri: contactUri];
Alexandre Lision34607032016-02-08 16:16:49 -0500306 }
307 }
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500308 [[NSUserNotificationCenter defaultUserNotificationCenter] removeAllDeliveredNotifications];
Alexandre Lision34607032016-02-08 16:16:49 -0500309}
310
Alexandre Lision745e4d62015-03-22 20:03:10 -0400311- (void) showWizard
312{
Alexandre Lision745e4d62015-03-22 20:03:10 -0400313 if(self.wizard == nil) {
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400314 self.wizard = [[RingWizardWC alloc] initWithNibName:@"RingWizard" bundle: nil accountmodel: &lrc->getAccountModel()];
Alexandre Lision745e4d62015-03-22 20:03:10 -0400315 }
Alexandre Lision76d59692016-01-20 18:06:05 -0500316 [self.wizard.window makeKeyAndOrderFront:self];
Alexandre Lision745e4d62015-03-22 20:03:10 -0400317}
318
319- (void) showMainWindow
320{
Alexandre Lisionb3f7ed62015-08-17 11:53:13 -0400321 if(self.ringWindowController == nil) {
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400322 self.ringWindowController = [[RingWindowController alloc] initWithWindowNibName:@"RingWindow" bundle: nil accountModel:&lrc->getAccountModel() dataTransferModel:&lrc->getDataTransferModel() behaviourController:&lrc->getBehaviorController()];
Alexandre Lisionb3f7ed62015-08-17 11:53:13 -0400323 }
Kateryna Kostiuk465cfbe2018-06-05 16:13:05 -0400324 [[NSApplication sharedApplication] removeWindowsItem:self.wizard.window];
Alexandre Lision745e4d62015-03-22 20:03:10 -0400325 [self.ringWindowController.window makeKeyAndOrderFront:self];
326}
327
Alexandre Lision62005312016-01-28 15:55:16 -0500328- (void) showDialpad
329{
330 if (self.dialpad == nil) {
331 self.dialpad = [[DialpadWC alloc] initWithWindowNibName:@"Dialpad"];
332 }
333 [self.dialpad.window makeKeyAndOrderFront:self];
334}
335
336
Alexandre Lision745e4d62015-03-22 20:03:10 -0400337- (BOOL) checkForRingAccount
338{
Kateryna Kostiuk23222fe2018-11-16 14:28:02 -0500339 return !lrc->getAccountModel().getAccountList().empty();
Alexandre Lision745e4d62015-03-22 20:03:10 -0400340}
341
Alexandre Lision18e1fcd2015-08-04 14:38:42 -0400342-(void)applicationWillFinishLaunching:(NSNotification *)aNotification
343{
344 NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
345 [appleEventManager setEventHandler:self
346 andSelector:@selector(handleGetURLEvent:withReplyEvent:)
347 forEventClass:kInternetEventClass andEventID:kAEGetURL];
348}
349
350/**
351 * Recognized patterns:
352 * - ring:<hash>
353 * - ring://<hash>
354 */
355- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
356{
357 NSString* query = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
358 NSURL* url = [[NSURL alloc] initWithString:query];
359 NSString* ringID = [url host];
360 if (!ringID) {
361 //not a valid NSURL, try to parse query directly
362 ringID = [query substringFromIndex:@"ring:".length];
363 }
364
365 // check for a valid ring hash
366 NSCharacterSet *hexSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789abcdefABCDEF"];
367 BOOL valid = [[ringID stringByTrimmingCharactersInSet:hexSet] isEqualToString:@""];
368
369 if(valid && ringID.length == 40) {
Alexandre Lisiond3aa3ad2015-10-23 14:28:41 -0400370 Call* c = CallModel::instance().dialingCall();
Alexandre Lision18e1fcd2015-08-04 14:38:42 -0400371 c->setDialNumber(QString::fromNSString([NSString stringWithFormat:@"ring:%@",ringID]));
372 c << Call::Action::ACCEPT;
373 } else {
374 NSAlert *alert = [[NSAlert alloc] init];
375 [alert addButtonWithTitle:@"OK"];
376 [alert setMessageText:@"Error"];
377 [alert setInformativeText:@"ringID cannot be read from this URL."];
378 [alert setAlertStyle:NSWarningAlertStyle];
379 [alert runModal];
380 }
381}
382
Alexandre Lision745e4d62015-03-22 20:03:10 -0400383- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
384{
385 if([self checkForRingAccount]) {
386 [self showMainWindow];
387 } else {
388 [self showWizard];
389 }
390 return YES;
391}
392
Edric Milaret81315412015-05-13 15:14:56 -0400393- (void)handleQuitEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent
394{
Kateryna Kostiuk0c0344a2018-07-27 10:35:01 -0400395 [[NSApplication sharedApplication] terminate:self];
Edric Milaret81315412015-05-13 15:14:56 -0400396}
397
398-(void)applicationWillTerminate:(NSNotification *)notification
399{
Alexandre Lision76d59692016-01-20 18:06:05 -0500400 [self cleanExit];
401}
402
403- (void) cleanExit
404{
Adrien Béraud022b57a2018-09-15 15:49:07 -0400405 if (self.activity != nil) {
406 [[NSProcessInfo processInfo] endActivity:self.activity];
407 self.activity = nil;
Andreas Traczyke4d6e782018-03-22 17:51:30 -0400408 }
Alexandre Lision76d59692016-01-20 18:06:05 -0500409 [self.wizard close];
410 [self.ringWindowController close];
Edric Milaret81315412015-05-13 15:14:56 -0400411 [[NSApplication sharedApplication] terminate:self];
412}
413
Alexandre Lision3d4143a2015-06-10 14:27:49 -0400414#if ENABLE_SPARKLE
415
Alexandre Lision76d59692016-01-20 18:06:05 -0500416#pragma mark - Sparkle delegate
Alexandre Lision3d4143a2015-06-10 14:27:49 -0400417
418- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update
419{
420 [NSApp activateIgnoringOtherApps:YES];
421}
422
423- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)bundle
424{
425 return YES;
426}
427
428- (BOOL)updaterShouldRelaunchApplication:(SUUpdater *)updater
429{
430 return YES;
431}
432
433- (void)updater:(SUUpdater *)updater didAbortWithError:(NSError *)error
434{
435 NSLog(@"Error:%@", error.localizedDescription);
436}
437
438#endif
Alexandre Lision5855b6a2015-02-03 11:31:05 -0500439@end