// // NYTAppDelegate.m // Notifications for YouTube // // Created by Kim Wittenburg on 09.07.13. // Copyright (c) 2013 Kim Wittenburg. All rights reserved. // #import "NYTAppDelegate.h" // Core Imports #import "NYTAuthentication.h" #import "NYTUpdateManager.h" // Other Imports #import "NumberValueTransformer.h" #import "NYTRulesWindowController.h" @interface NYTAppDelegate () // Outlets etc. @property (weak) IBOutlet NSButton *loginButton; @property (readwrite, strong) NSStatusItem *statusItem; // Login @property (readwrite) BOOL loggingIn; @end // Notifications used for KVO static void* const NYTLoginChangedNotification = @"NYTLoginChangedNotification"; static void* const NYTUpdateManagerPropertyDidChangeNotification = @"NYTUpdateManagerPropertyDidChangeNotification"; @implementation NYTAppDelegate { NYTRulesWindowController *_rulesController; } + (void)initialize { [super initialize]; // Register our value transformers [NSValueTransformer setValueTransformer:[NumberValueTransformer new] forName:@"NumberValueTransformer"]; } - (void)dealloc { // Make sure this object gets removed from any observed object NYTUpdateManager *manager = [NYTUpdateManager sharedManager]; [manager removeObserver:self forKeyPath:@"autoRefreshEnabled"]; [manager removeObserver:self forKeyPath:@"autoRefreshInterval"]; [manager removeObserver:self forKeyPath:@"coalescesNotifications"]; [manager removeObserver:self forKeyPath:@"maximumVideoAge"]; [[NYTAuthentication sharedAuthentication] removeObserver:self forKeyPath:@"isLoggedIn"]; } #pragma mark - Application Delegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NYTUpdateManager *manager = [NYTUpdateManager sharedManager]; NYTAuthentication *auth = [NYTAuthentication sharedAuthentication]; // Configure the User Defaults NSDictionary *userDefaults = @{@"autoRefreshEnabled": @YES, @"autoRefreshInterval": @600, @"coalescesNotifications": @YES, @"maximumVideoAge": @3600}; [defaults registerDefaults:userDefaults]; // Configure Notification Centers [NSUserNotificationCenter defaultUserNotificationCenter].delegate = self; // Notification Startup NSUserNotification *startNotification = aNotification.userInfo[NSApplicationLaunchUserNotificationKey]; if (startNotification) { [self userNotificationCenter:[NSUserNotificationCenter defaultUserNotificationCenter] didActivateNotification:startNotification]; } // Configure the status item self.statusItemVisible = YES; // Configure the login (and log in). Do so before configuring notifications or auto refresh because this can take a while [auth addObserver:self forKeyPath:@"isLoggedIn" options:0 context:NYTLoginChangedNotification]; self.loggingIn = YES; [auth tryLoginFromKeychainWithCompletionHandler:^{ self.loggingIn = NO; // If there is no login in the keychain show the preferences if (!auth.isLoggedIn) { [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; [self.window makeKeyAndOrderFront:self]; } }]; // Configure the update manager manager.maximumVideoAge = [defaults integerForKey:@"maximumVideoAge"]; manager.coalescesNotifications = [defaults boolForKey:@"coalescesNotifications"]; manager.autoRefreshInterval = [defaults integerForKey:@"autoRefreshInterval"]; [manager addObserver:self forKeyPath:@"maximumVideoAge" options:NSKeyValueObservingOptionNew context:NYTUpdateManagerPropertyDidChangeNotification]; [manager addObserver:self forKeyPath:@"coalescesNotifications" options:NSKeyValueObservingOptionNew context:NYTUpdateManagerPropertyDidChangeNotification]; [manager addObserver:self forKeyPath:@"autoRefreshInterval" options:NSKeyValueObservingOptionNew context:NYTUpdateManagerPropertyDidChangeNotification]; // Configure (Auto) Refresh manager.autoRefreshEnabled = [defaults boolForKey:@"autoRefreshEnabled"]; [manager addObserver:self forKeyPath:@"autoRefreshEnabled" options:NSKeyValueObservingOptionNew context:NYTUpdateManagerPropertyDidChangeNotification]; } - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag { // Open the preferences on reopen (if no other window is visible) if (!flag) { [self.window makeKeyAndOrderFront:nil]; } return YES; } - (void)applicationWillTerminate:(NSNotification *)notification { [[NSUserDefaults standardUserDefaults] synchronize]; } #pragma mark - User Notification Center Delegate - (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification { // Show the URL NSString *videoURL = notification.userInfo[NYTUserNotificationURLKey]; [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:videoURL]]; // Remove the notification [center removeDeliveredNotification:notification]; } - (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification { return YES; } #pragma mark - #pragma mark *** Handling the Status Item *** - (void)setStatusItemVisible:(BOOL)visible { if (visible) { self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; //NSMutableAttributedString *title = [[NSMutableAttributedString alloc] initWithString:@"(4)"]; //[title addAttribute:NSStrokeWidthAttributeName value:@-5.0 range:NSMakeRange/*(3, 4)*/(1, 1)]; //self.statusItem.attributedTitle = title; self.statusItem.image = [NSImage imageNamed:@"play-icon-bw"]; self.statusItem.alternateImage = [NSImage imageNamed:@"play-icon-s"]; self.statusItem.menu = self.statusMenu; self.statusItem.highlightMode = YES; } else { [[NSStatusBar systemStatusBar] removeStatusItem:self.statusItem]; self.statusItem = nil; } } - (BOOL)isStatusItemVisible { return self.statusItem != nil; } #pragma mark *** Handling Login *** - (IBAction)login:(id)sender { if ([NYTAuthentication sharedAuthentication].isLoggedIn) { // Logout: Present a sheet giving the following options: Logout, Cancel, Switch user NSString *title = NSLocalizedString(@"Do you want to log out or switch to another user?", nil); NSString *switchUser = NSLocalizedString(@"Switch User…", nil); NSString *logout = NSLocalizedString(@"Logout", nil); NSString *cancel = NSLocalizedString(@"Cancel", nil); NSString *msg = NSLocalizedString(@"If you logout the login stored in your keychain will be removed. You would have to log in agin the next time you want to use Notifications for YouTube.", nil); NSBeginAlertSheet(title, switchUser, logout, cancel, self.window, self, NULL, @selector(sheetDidDismiss:returnCode:contextInfo:), NULL, @"%@", msg); } else { [self doLogin]; } } - (void)sheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo { if (returnCode == NSAlertDefaultReturn) { // Switch User [self doLogin]; } else if (returnCode == NSAlertAlternateReturn) { // Logout: Redirect to Update Manager [[NYTAuthentication sharedAuthentication] discardLogin]; } } - (void)doLogin { // Login: Redirect to Update Manager self.loggingIn = YES; [[NYTAuthentication sharedAuthentication] beginLoginSheetForWindow:self.window completionHandler:^(NSError *error) { if (error) { // Show the error as sheet if possible if (self.window.isVisible) { [NSApp presentError:error modalForWindow:self.window delegate:nil didPresentSelector:NULL contextInfo:NULL]; } else { [NSApp presentError:error]; } } self.loggingIn = NO; }]; } #pragma mark *** Handling Refreshing *** - (IBAction)performRefresh:(id)sender { // Refresh with notifications [[NYTUpdateManager sharedManager] refreshFeedsWithErrorHandler:^(NSError *error) { // Show the error if the refresh was triggered manually // Possibly show it in a modal sheet otherwise a modal window if (error) { if ([sender respondsToSelector:@selector(window)] && [sender window].isVisible) { NSAlert *alert = [NSAlert alertWithError:error]; [alert beginSheetModalForWindow:[sender window] modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; } else { [NSApp presentError:error]; } } } notify:YES]; } #pragma mark *** Configure Notifications *** - (IBAction)editRules:(id)sender { _rulesController = [[NYTRulesWindowController alloc] init]; [NSApp beginSheet:_rulesController.window modalForWindow:self.window modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:NULL]; } - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { // For efficiency reasons _rulesController = nil; } #pragma mark *** Other Actions *** - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (context == NYTUpdateManagerPropertyDidChangeNotification) { id value = change[NSKeyValueChangeNewKey]; [[NSUserDefaults standardUserDefaults] setObject:value forKey:keyPath]; }else if (context == NYTLoginChangedNotification) { // Manage the title of the login/logout button based on wether login information is present (using KVO) if ([NYTAuthentication sharedAuthentication].isLoggedIn) { self.loginButton.title = NSLocalizedString(@"Logout…", nil); } else { self.loginButton.title = NSLocalizedString(@"Login…", nil); } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } - (IBAction)showPreferenceWindow:(id)sender { // Activate the app first [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; [self.window makeKeyAndOrderFront:sender]; [self.window makeMainWindow]; } - (IBAction)showAboutPanel:(id)sender { [NSApp activateIgnoringOtherApps:YES]; [NSApp orderFrontStandardAboutPanel:sender]; } - (IBAction)browseYouTube:(id)sender { [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.youtube.com/"]]; } @end