root/trunk/lilypad/Sources/LPEventNotificationsHandler.m @ 166

Revision 166, 17.8 KB (checked in by jppavao, 5 years ago)

Added a badge to the app dock icon that displays the number of unread offline messages. Also added a dock menu. References #24 and #76.

Line 
1//
2//  LPEventNotificationsHandler.m
3//  Lilypad
4//
5//      Copyright (C) 2006-2007 PT.COM,  All rights reserved.
6//      Author: Joao Pavao <jppavao@criticalsoftware.com>
7//
8//      For more information on licensing, read the README file.
9//      Para mais informa›es sobre o licenciamento, leia o ficheiro README.
10//
11
12#import "LPEventNotificationsHandler.h"
13#import "LPAccount.h"
14#import "LPContact.h"
15#import "LPContactEntry.h"
16#import "LPPresenceSubscription.h"
17
18
19// Notification Names for Growl
20static NSString *LPContactAvailabilityChangedNotificationName   = @"Contact Availability Changed";
21static NSString *LPFirstChatMessageReceivedNotificationName             = @"First Chat Message Received";
22static NSString *LPChatMessageReceivedNotificationName                  = @"Chat Message Received";
23static NSString *LPSMSMessageReceivedNotificationName                   = @"SMS Message Received";
24static NSString *LPHeadlineMessageReceivedNotificationName              = @"Notification Headline Received";
25static NSString *LPOfflineMessagesReceivedNotificationName              = @"Offline Messages Received";
26static NSString *LPPresenceSubscriptionReceivedNotificationName = @"Presence Subscription Received";
27static NSString *LPFileTransferEventNotificationName                    = @"File Transfer Event";
28
29
30// Key names for the Growl click context
31static NSString *LPClickContextKindKey                  = @"Kind";
32static NSString *LPClickContextContactIDKey             = @"ContactID";
33static NSString *LPClickContextMessageURIKey    = @"MessageURI";
34
35static NSString *LPClickContextContactKindValue                                 = @"Contact";
36static NSString *LPClickContextHeadlineMessageKindValue                 = @"HeadlineMessage";
37static NSString *LPClickContextOfflineMessagesKindValue                 = @"OfflineMessages";
38static NSString *LPClickContextPresenceSubscriptionKindValue    = @"PresenceSubscription";
39static NSString *LPClickContextFileTransferKindValue                    = @"FileTransfer";
40
41@implementation LPEventNotificationsHandler
42
43+ (void)registerWithGrowl
44{
45        // This forces the LPEventNotificationsHandler to initialize and register with Growl
46        [LPEventNotificationsHandler defaultHandler];
47}
48
49+ defaultHandler
50{
51        static LPEventNotificationsHandler *defaultHandler = nil;
52
53        if (defaultHandler == nil) {
54                defaultHandler = [[LPEventNotificationsHandler alloc] init];
55                [GrowlApplicationBridge setGrowlDelegate:defaultHandler];
56        }
57        return defaultHandler;
58}
59
60
61- init
62{
63        if (self = [super init]) {
64                m_contactAvailabilityNotificationsReenableDatesByAccountUUID = [[NSMutableDictionary alloc] init];
65        }
66        return self;
67}
68
69
70- (void)dealloc
71{
72        [m_contactAvailabilityNotificationsReenableDatesByAccountUUID release];
73        [super dealloc];
74}
75
76
77- (id)delegate
78{
79        return m_delegate;
80}
81
82
83- (void)setDelegate:(id)delegate
84{
85        m_delegate = delegate;
86}
87
88
89- (id)p_clickContextForContact:(LPContact *)contact
90{
91        return [NSDictionary dictionaryWithObjectsAndKeys:
92                LPClickContextContactKindValue, LPClickContextKindKey,
93                [NSNumber numberWithInt:[contact ID]], LPClickContextContactIDKey,
94                nil];
95}
96
97- (id)p_clickContextForHeadlineMessage:(id)message
98{
99        return [NSDictionary dictionaryWithObjectsAndKeys:
100                LPClickContextHeadlineMessageKindValue, LPClickContextKindKey,
101                [[[message objectID] URIRepresentation] absoluteString], LPClickContextMessageURIKey,
102                nil];
103}
104
105- (id)p_clickContextForOfflineMessages
106{
107        return [NSDictionary dictionaryWithObjectsAndKeys:
108                LPClickContextOfflineMessagesKindValue, LPClickContextKindKey,
109                nil];
110}
111
112- (id)p_clickContextForPresenceSubscription:(id)presSub
113{
114        return [NSDictionary dictionaryWithObjectsAndKeys:
115                LPClickContextPresenceSubscriptionKindValue, LPClickContextKindKey,
116                nil];
117}
118
119- (id)p_clickContextForFileTransfer
120{
121        return [NSDictionary dictionaryWithObjectsAndKeys:
122                LPClickContextFileTransferKindValue, LPClickContextKindKey,
123                nil];
124}
125
126
127#pragma mark Public Methods to Request Posting of Notifications
128
129
130- (void)disableContactAvailabilityNotificationsForAccount:(LPAccount *)account untilDate:(NSDate *)date
131{
132        [m_contactAvailabilityNotificationsReenableDatesByAccountUUID setObject:date forKey:[account UUID]];
133}
134
135
136- (void)notifyContactAvailabilityDidChange:(LPContact *)contact
137{
138        BOOL            shouldNotify = YES;
139        NSString        *statusChangeSourceAccountUUID = [[[contact lastContactEntryToChangeStatus] account] UUID];
140        NSDate          *notificationsReenableDate = [m_contactAvailabilityNotificationsReenableDatesByAccountUUID objectForKey:statusChangeSourceAccountUUID];
141       
142        if (notificationsReenableDate != nil) {
143                NSDate *now = [NSDate date];
144               
145                if ([now compare:notificationsReenableDate] == NSOrderedAscending) {
146                        // We're still in the time interval where notifications should be disabled.
147                        shouldNotify = NO;
148                }
149                else {
150                        // Clear the "timeout" date
151                        [m_contactAvailabilityNotificationsReenableDatesByAccountUUID removeObjectForKey:statusChangeSourceAccountUUID];
152                }
153        }
154       
155       
156        if (shouldNotify) {
157                // Allow a small delay to avoid having everything happen at the same time. This also allows the avatar to be updated
158                // (if it exists) from cache before actually displaying the notification.
159                [self performSelector:@selector(p_displayContactAvailabilityDidChangeNotification:)
160                                   withObject:contact
161                                   afterDelay:0.5];
162        }
163}
164
165
166- (void)p_displayContactAvailabilityDidChangeNotification:(LPContact *)contact
167{
168        // Avoid notifying about just added (in the last n seconds) and just deleted contacts
169        if ([contact roster] != nil && [[contact creationDate] timeIntervalSinceNow] < (-10.0)) {
170                NSString *description = ( ([contact status] == LPStatusOffline) ?
171                                                                  NSLocalizedString(@"went Offline", @"contact availability change notifications") :
172                                                                  NSLocalizedString(@"is now Online", @"contact availability change notifications")  );
173                // Make "contact went offline" notifications non-clickable
174                id clickContext = ( ([contact status] == LPStatusOffline) ?
175                                                        nil :
176                                                        [self p_clickContextForContact:contact] );
177               
178                [GrowlApplicationBridge notifyWithTitle:[contact name]
179                                                                        description:description
180                                                           notificationName:LPContactAvailabilityChangedNotificationName
181                                                                           iconData:[[contact avatar] TIFFRepresentation]
182                                                                           priority:0
183                                                                           isSticky:NO
184                                                                   clickContext:clickContext];
185        }
186}
187
188
189- (void)notifyReceptionOfFirstMessage:(NSString *)message fromContact:(LPContact *)contact
190{
191        NSString *title = [NSString stringWithFormat:
192                NSLocalizedString(@"First Message from %@", @"chat messages notifications"),
193                [contact name]];
194       
195        NSNumber *contactIDNr = [NSNumber numberWithInt:[contact ID]];
196        NSString *identifier = [[contactIDNr stringValue] stringByAppendingString:message];
197        NSData *iconData = [[contact avatar] TIFFRepresentation];
198       
199        /*
200         * Shoot the two kinds of notifications coalesced under the same identifier. This way, if the user
201         * has "first message notifications" disabled in the prefs but regular "message notifications" enabled,
202         * then the first message will still be able to have a notification displayed.
203         */
204       
205        [GrowlApplicationBridge notifyWithTitle:title
206                                                                description:message
207                                                   notificationName:LPChatMessageReceivedNotificationName
208                                                                   iconData:iconData
209                                                                   priority:1
210                                                                   isSticky:NO
211                                                           clickContext:[self p_clickContextForContact:contact]
212                                                                 identifier:identifier];
213       
214        [NSApp requestUserAttention:NSInformationalRequest];
215       
216        [GrowlApplicationBridge notifyWithTitle:title
217                                                                description:message
218                                                   notificationName:LPFirstChatMessageReceivedNotificationName
219                                                                   iconData:iconData
220                                                                   priority:1
221                                                                   isSticky:NO
222                                                           clickContext:[self p_clickContextForContact:contact]
223                                                                 identifier:identifier];
224       
225        // Play the "Received Message" sound
226        if ([[NSUserDefaults standardUserDefaults] boolForKey:@"UIPlaySounds"]) {
227                [[NSSound soundNamed:@"received"] play];
228        }
229}
230
231
232- (void)notifyReceptionOfMessage:(NSString *)message fromContact:(LPContact *)contact
233{
234        NSString *title = [NSString stringWithFormat:NSLocalizedString(@"Message from %@", @"chat messages notifications"),
235                [contact name]];
236       
237        [NSApp requestUserAttention:NSInformationalRequest];
238       
239        [GrowlApplicationBridge notifyWithTitle:title
240                                                                description:message
241                                                   notificationName:LPChatMessageReceivedNotificationName
242                                                                   iconData:[[contact avatar] TIFFRepresentation]
243                                                                   priority:1
244                                                                   isSticky:NO
245                                                           clickContext:[self p_clickContextForContact:contact]];
246       
247        // Play the "Received Message" sound
248        if ([[NSUserDefaults standardUserDefaults] boolForKey:@"UIPlaySounds"]) {
249                [[NSSound soundNamed:@"received"] play];
250        }
251}
252
253
254- (void)notifyReceptionOfSMSMessage:(NSString *)message fromContact:(LPContact *)contact
255{
256        NSString *title = [NSString stringWithFormat:NSLocalizedString(@"SMS Message from %@", @"chat messages notifications"),
257                [contact name]];
258       
259        [NSApp requestUserAttention:NSInformationalRequest];
260       
261        [GrowlApplicationBridge notifyWithTitle:title
262                                                                description:message
263                                                   notificationName:LPSMSMessageReceivedNotificationName
264                                                                   iconData:[[contact avatar] TIFFRepresentation]
265                                                                   priority:1
266                                                                   isSticky:NO
267                                                           clickContext:[self p_clickContextForContact:contact]];
268}
269
270
271- (void)notifyReceptionOfHeadlineMessage:(id)message
272{
273        NSString *title = [NSString stringWithFormat:NSLocalizedString(@"Notification Headline", @"messages notifications")];
274       
275        [GrowlApplicationBridge notifyWithTitle:title
276                                                                description:[message valueForKey:@"subject"]
277                                                   notificationName:LPHeadlineMessageReceivedNotificationName
278                                                                   iconData:nil
279                                                                   priority:1
280                                                                   isSticky:NO
281                                                           clickContext:[self p_clickContextForHeadlineMessage:message]];
282}
283
284
285- (void)p_notifyReceptionOfOfflineMessages
286{
287        NSString *title = [NSString stringWithFormat:NSLocalizedString(@"Offline Messages", @"messages notifications")];
288        NSString *descr = [NSString stringWithFormat:(m_nrOfOfflineMessagesForDelayedNotification == 1 ?
289                                                                                                  NSLocalizedString(@"You have received %d message while you were offline.", @"messages notifications") :
290                                                                                                  NSLocalizedString(@"You have received %d messages while you were offline.", @"messages notifications") ),
291                                           m_nrOfOfflineMessagesForDelayedNotification];
292       
293        [NSApp requestUserAttention:NSInformationalRequest];
294       
295        [GrowlApplicationBridge notifyWithTitle:title
296                                                                description:descr
297                                                   notificationName:LPOfflineMessagesReceivedNotificationName
298                                                                   iconData:nil
299                                                                   priority:1
300                                                                   isSticky:NO
301                                                           clickContext:[self p_clickContextForOfflineMessages]];
302       
303        m_nrOfOfflineMessagesForDelayedNotification = 0;
304}
305
306- (void)notifyReceptionOfOfflineMessage:(id)message
307{
308        [self notifyReceptionOfOfflineMessagesCount:1];
309}
310
311
312- (void)notifyReceptionOfOfflineMessagesCount:(int)messageCount
313{
314        if (m_nrOfOfflineMessagesForDelayedNotification == 0 && messageCount > 0) {
315                [self performSelector:@selector(p_notifyReceptionOfOfflineMessages) withObject:nil afterDelay:3.0];
316        }
317        m_nrOfOfflineMessagesForDelayedNotification += messageCount;
318}
319
320
321- (void)notifyReceptionOfPresenceSubscription:(LPPresenceSubscription *)presSub
322{
323        NSString *title = [NSString stringWithFormat:NSLocalizedString(@"Presence Subscription", @"messages notifications")];
324        NSString *descr = nil;
325       
326        switch ([presSub state]) {
327                case LPAuthorizationGranted:
328                        descr = [NSString stringWithFormat:
329                                NSLocalizedString(@"%@ was added to your buddy list. You can now see the online status of this contact.", @"messages notifications"),
330                                [presSub valueForKeyPath:@"contactEntry.address"]];
331                        break;
332                       
333                case LPAuthorizationRequested:
334                        descr = [NSString stringWithFormat:
335                                NSLocalizedString(@"%@ wants to add you as a buddy.", @"messages notifications"),
336                                [presSub valueForKeyPath:@"contactEntry.address"]];
337                        break;
338                       
339                case LPAuthorizationLost:
340                        descr = [NSString stringWithFormat:
341                                NSLocalizedString(@"Permission to see the online status of contact %@ was lost.", @"messages notifications"),
342                                [presSub valueForKeyPath:@"contactEntry.address"]];
343                        break;
344        }
345       
346        [GrowlApplicationBridge notifyWithTitle:title
347                                                                description:descr
348                                                   notificationName:LPPresenceSubscriptionReceivedNotificationName
349                                                                   iconData:nil
350                                                                   priority:1
351                                                                   isSticky:YES
352                                                           clickContext:[self p_clickContextForPresenceSubscription:presSub]];
353}
354
355
356- (void)notifyReceptionOfFileTransferOfferWithFileName:(NSString *)filename fromContact:(LPContact *)contact
357{
358        NSString *title = [NSString stringWithFormat:NSLocalizedString(@"File Transfer Offer", @"file transfer notifications")];
359        NSString *description = [NSString stringWithFormat:
360                NSLocalizedString(@"%@ is offering you the file \"%@\"", @"file transfer notifications"),
361                [contact name], filename];
362       
363        [NSApp requestUserAttention:NSCriticalRequest];
364       
365        [GrowlApplicationBridge notifyWithTitle:title
366                                                                description:description
367                                                   notificationName:LPFileTransferEventNotificationName
368                                                                   iconData:nil
369                                                                   priority:1
370                                                                   isSticky:YES
371                                                           clickContext:[self p_clickContextForFileTransfer]];
372}
373
374- (void)notifyAcceptanceOfFileTransferWithFileName:(NSString *)filename fromContact:(LPContact *)contact
375{
376        NSString *title = [NSString stringWithFormat:NSLocalizedString(@"File Transfer Accepted", @"file transfer notifications")];
377        NSString *description = [NSString stringWithFormat:
378                NSLocalizedString(@"%@ accepted your file named \"%@\"", @"file transfer notifications"),
379                [contact name], filename];
380       
381        [GrowlApplicationBridge notifyWithTitle:title
382                                                                description:description
383                                                   notificationName:LPFileTransferEventNotificationName
384                                                                   iconData:nil
385                                                                   priority:1
386                                                                   isSticky:NO
387                                                           clickContext:[self p_clickContextForFileTransfer]];
388}
389
390- (void)notifyFailureOfFileTransferWithFileName:(NSString *)filename fromContact:(LPContact *)contact withErrorMessage:(NSString *)errorMsg
391{
392        NSString *title = [NSString stringWithFormat:NSLocalizedString(@"File Transfer Failed", @"file transfer notifications")];
393        NSString *description = ( (errorMsg != nil) ?
394                                                          [NSString stringWithFormat:
395                                                                  NSLocalizedString(@"The file transfer of \"%@\" with %@ has failed with the error: %@",
396                                                                                                        @"file transfer notifications"),
397                                                                  filename, [contact name], errorMsg] :
398                                                          [NSString stringWithFormat:
399                                                                  NSLocalizedString(@"The file transfer of \"%@\" with %@ has failed", @"file transfer notifications"),
400                                                                  filename, [contact name]] );
401       
402        [NSApp requestUserAttention:NSInformationalRequest];
403       
404        [GrowlApplicationBridge notifyWithTitle:title
405                                                                description:description
406                                                   notificationName:LPFileTransferEventNotificationName
407                                                                   iconData:nil
408                                                                   priority:1
409                                                                   isSticky:YES
410                                                           clickContext:[self p_clickContextForFileTransfer]];
411}
412
413- (void)notifyCompletionOfFileTransferWithFileName:(NSString *)filename withContact:(LPContact *)contact
414{
415        NSString *title = [NSString stringWithFormat:NSLocalizedString(@"File Transfer Completed", @"file transfer notifications")];
416        NSString *description = [NSString stringWithFormat:
417                NSLocalizedString(@"Transfer of the file \"%@\" with %@ has completed", @"file transfer notifications"),
418                filename, [contact name]];
419       
420        [NSApp requestUserAttention:NSInformationalRequest];
421       
422        [GrowlApplicationBridge notifyWithTitle:title
423                                                                description:description
424                                                   notificationName:LPFileTransferEventNotificationName
425                                                                   iconData:nil
426                                                                   priority:1
427                                                                   isSticky:NO
428                                                           clickContext:[self p_clickContextForFileTransfer]];
429}
430
431
432#pragma mark GrowlApplicationBridge Delegate
433
434
435- (NSDictionary *)registrationDictionaryForGrowl
436{
437        return [NSDictionary dictionaryWithObjectsAndKeys:
438               
439                [NSArray arrayWithObjects:
440                        LPContactAvailabilityChangedNotificationName,
441                        LPFirstChatMessageReceivedNotificationName,
442                        LPChatMessageReceivedNotificationName,
443                        LPSMSMessageReceivedNotificationName,
444                        LPHeadlineMessageReceivedNotificationName,
445                        LPOfflineMessagesReceivedNotificationName,
446                        LPPresenceSubscriptionReceivedNotificationName,
447                        LPFileTransferEventNotificationName,
448                        nil],
449                GROWL_NOTIFICATIONS_ALL,
450               
451                [NSArray arrayWithObjects:
452                        LPContactAvailabilityChangedNotificationName,
453                        LPFirstChatMessageReceivedNotificationName,
454                        LPSMSMessageReceivedNotificationName,
455                        LPHeadlineMessageReceivedNotificationName,
456                        LPOfflineMessagesReceivedNotificationName,
457                        LPPresenceSubscriptionReceivedNotificationName,
458                        LPFileTransferEventNotificationName,
459                        nil],
460                GROWL_NOTIFICATIONS_DEFAULT,
461               
462                nil];
463}
464
465
466- (void)growlNotificationWasClicked:(id)clickContext
467{
468        NSString *kind = [clickContext objectForKey:@"Kind"];
469       
470        if ([kind isEqualToString: LPClickContextContactKindValue]) {
471                if ([m_delegate respondsToSelector:@selector(notificationsHandler:userDidClickNotificationForContactWithID:)]) {
472                        unsigned int contactID = [[clickContext objectForKey: LPClickContextContactIDKey] intValue];
473                        [m_delegate notificationsHandler:self userDidClickNotificationForContactWithID:contactID];
474                }
475        }
476        else if ([kind isEqualToString: LPClickContextHeadlineMessageKindValue]) {
477                if ([m_delegate respondsToSelector:@selector(notificationsHandler:userDidClickNotificationForHeadlineMessageWithURI:)]) {
478                        NSString *messageURI = [clickContext objectForKey: LPClickContextMessageURIKey];
479                        [m_delegate notificationsHandler:self userDidClickNotificationForHeadlineMessageWithURI:messageURI];
480                }
481        }
482        else if ([kind isEqualToString: LPClickContextOfflineMessagesKindValue]) {
483                if ([m_delegate respondsToSelector:@selector(notificationsHandlerUserDidClickNotificationForOfflineMessages:)]) {
484                        [m_delegate notificationsHandlerUserDidClickNotificationForOfflineMessages:self];
485                }
486        }
487        else if ([kind isEqualToString: LPClickContextPresenceSubscriptionKindValue]) {
488                if ([m_delegate respondsToSelector:@selector(notificationsHandlerUserDidClickNotificationForPresenceSubscriptions:)]) {
489                        [m_delegate notificationsHandlerUserDidClickNotificationForPresenceSubscriptions:self];
490                }
491        }
492        else if ([kind isEqualToString: LPClickContextFileTransferKindValue]) {
493                if ([m_delegate respondsToSelector:@selector(notificationsHandlerUserDidClickNotificationForFileTransfer:)]) {
494                        [m_delegate notificationsHandlerUserDidClickNotificationForFileTransfer:self];
495                }
496        }
497}
498
499
500@end
Note: See TracBrowser for help on using the browser.