Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(519)

Side by Side Diff: chrome/browser/notifications/notification_ui_manager_mac.mm

Issue 10021026: [Mac] Implement a NotificationUIManager that uses Notification Center on 10.8 for text notifications (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/notifications/notification_ui_manager_mac.h"
6
7 #include "base/mac/cocoa_protocols.h"
8 #include "base/mac/mac_util.h"
9 #include "base/sys_string_conversions.h"
10 #include "chrome/browser/notifications/notification.h"
11 #include "chrome/browser/notifications/notification_ui_manager_impl.h"
12
13 @class NSUserNotificationCenter;
14
15 // Since NSUserNotification and NSUserNotificationCenter are new classes in
16 // 10.8, they cannot simply be declared with an @interface. An @implementation
17 // is needed to link, but providing one would cause a runtime conflict when
18 // running on 10.8. Instead, we provide the interface defined as a protocol and
Mark Mentovai 2012/04/09 16:39:41 we -> (null)
Robert Sesek 2012/04/09 21:36:38 Done.
19 // use that instead, because sizeof(id<Protocol>) == sizeof(Class*). In order to
20 // instantiate, you use NSClassFromString and simply assign the alloc/init'd
Mark Mentovai 2012/04/09 16:39:41 you -> (null)
Robert Sesek 2012/04/09 21:36:38 Done.
21 // result to an instance of the proper protocol. This way the compiler, linker,
22 // and loader are all happy. And the code isn't full of objc_msgSend.
23 @protocol CrUserNotification <NSObject>
24 @property(copy) NSString* title;
25 @property(copy) NSString* subtitle;
26 @property(copy) NSString* informativeText;
27 @property(copy) NSString* actionButtonTitle;
28 @property(copy) NSDictionary* userInfo;
29 @property(copy) NSDate* deliveryDate;
30 @property(copy) NSTimeZone* deliveryTimeZone;
31 @property(copy) NSDateComponents* deliveryRepeatInterval;
32 @property(readonly) NSDate* actualDeliveryDate;
33 @property(readonly, getter=isPresented) BOOL presented;
34 @property(readonly, getter=isRemote) BOOL remote;
35 @property(copy) NSString* soundName;
36 @property BOOL hasActionButton;
37 @end
38
39 @protocol CrUserNotificationCenter
40 + (NSUserNotificationCenter*)defaultUserNotificationCenter;
41 @property(assign) id<NSUserNotificationCenterDelegate> delegate;
42 @property(copy) NSArray* scheduledNotifications;
43 - (void)scheduleNotification:(id<CrUserNotification>)notification;
44 - (void)removeScheduledNotification:(id<CrUserNotification>)notification;
45 @property(readonly) NSArray* deliveredNotifications;
46 - (void)deliverNotification:(id<CrUserNotification>)notification;
47 - (void)removeDeliveredNotification:(id<CrUserNotification>)notification;
48 - (void)removeAllDeliveredNotifications;
49 @end
50
51 ////////////////////////////////////////////////////////////////////////////////
52
53 namespace {
54
55 // A "fun" way of saying:
56 // +[NSUserNotificationCenter defaultUserNotificationCenter].
57 id<CrUserNotificationCenter> GetNotificationCenter() {
58 return [NSClassFromString(@"NSUserNotificationCenter")
59 performSelector:@selector(defaultUserNotificationCenter)];
60 }
61
62 // The key in NSUserNotification.userInfo that stores the C++ notification_id.
63 NSString* const kNotificationIDKey = @"notification_id";
64
65 } // namespace
66
67 // A Cocoa class that can be the delegate of NSUserNotificationCenter that
68 // forwards commands to C++.
69 @interface NotificationCenterDelegate : NSObject
70 <NSUserNotificationCenterDelegate> {
71 @private
72 NotificationUIManagerMac* manager_; // Weak, owns self.
73 }
74 - (id)initWithManager:(NotificationUIManagerMac*)manager;
75 @end
76
77 ////////////////////////////////////////////////////////////////////////////////
78
79 // static
80 NotificationUIManager* NotificationUIManager::Create(
81 PrefService* local_state,
82 BalloonCollection* balloons) {
83 NotificationUIManager* instance = NULL;
84 NotificationUIManagerImpl* impl = NULL;
85
86 if (base::mac::IsOSMountainLionOrLater()) {
87 NotificationUIManagerMac* mac_instance =
88 new NotificationUIManagerMac(local_state);
89 instance = mac_instance;
90 impl = mac_instance->builtin_manager();
91 } else {
92 instance = impl = new NotificationUIManagerImpl(local_state);
93 }
94
95 impl->Initialize(balloons);
96 balloons->set_space_change_listener(impl);
97
98 return instance;
99 }
100
101 NotificationUIManagerMac::NotificationUIManagerMac(PrefService* local_state)
102 : builtin_manager_(new NotificationUIManagerImpl(local_state)),
103 delegate_(ALLOW_THIS_IN_INITIALIZER_LIST(
104 [[NotificationCenterDelegate alloc] initWithManager:this])) {
105 DCHECK(!GetNotificationCenter().delegate);
106 GetNotificationCenter().delegate = delegate_.get();
107 }
108
109 NotificationUIManagerMac::~NotificationUIManagerMac() {
Mark Mentovai 2012/04/09 16:39:41 Remove any dangling notifications here?
Robert Sesek 2012/04/09 21:36:38 No, clients do that with CancelAll() at shutdown.
110 }
111
112 void NotificationUIManagerMac::Add(const Notification& notification,
113 Profile* profile) {
114 if (notification.is_html()) {
115 builtin_manager_->Add(notification, profile);
116 } else {
117 id<CrUserNotification> replacee = FindNotificationWithReplacementId(
118 notification.replace_id());
119 if (replacee)
120 RemoveNotification(replacee);
121
122 // Owned by notification_map_.
123 id<CrUserNotification> notif =
Mark Mentovai 2012/04/09 16:39:41 notif again, throughout this file :/
124 [[NSClassFromString(@"NSUserNotification") alloc] init];
125
126 notif.title = base::SysUTF16ToNSString(notification.title());
127 notif.subtitle = base::SysUTF16ToNSString(notification.display_source());
128 notif.informativeText = base::SysUTF16ToNSString(notification.body());
129 notif.userInfo =
130 [NSDictionary dictionaryWithObject:base::SysUTF8ToNSString(
131 notification.notification_id())
132 forKey:kNotificationIDKey];
133 notif.hasActionButton = NO;
134
135 notification_map_.insert(
136 std::make_pair(notif, new Notification(notification)));
137
138 [GetNotificationCenter() deliverNotification:notif];
139 }
140 }
141
142 bool NotificationUIManagerMac::CancelById(const std::string& notification_id) {
143 for (NotificationMap::iterator it = notification_map_.begin();
144 it != notification_map_.end();
145 ++it) {
146 if (it->second->notification_id() == notification_id) {
147 return RemoveNotification(it->first);
148 }
149 }
150
151 return builtin_manager_->CancelById(notification_id);
152 }
153
154 bool NotificationUIManagerMac::CancelAllBySourceOrigin(
155 const GURL& source_origin) {
156 bool success = builtin_manager_->CancelAllBySourceOrigin(source_origin);
157
158 for (NotificationMap::iterator it = notification_map_.begin();
159 it != notification_map_.end();
160 ++it) {
161 if (it->second->origin_url() == source_origin) {
162 success |= RemoveNotification(it->first);
163 }
164 }
165
166 return success;
167 }
168
169 void NotificationUIManagerMac::CancelAll() {
170 id<CrUserNotificationCenter> center = GetNotificationCenter();
171
172 // Calling RemoveNotification would loop many times over, so just replicate
173 // a small bit of its logic here.
174 for (NotificationMap::iterator it = notification_map_.begin();
175 it != notification_map_.end();
176 ++it) {
177 [center removeDeliveredNotification:it->first];
178 [it->first release];
179
180 it->second->Close(false);
181 delete it->second;
182 }
183 notification_map_.clear();
184
185 // Clean up any lingering ones in the system tray.
186 for (id<CrUserNotification> notif in center.deliveredNotifications) {
187 [center removeDeliveredNotification:notif];
188 }
189
190 builtin_manager_->CancelAll();
191 }
192
193 BalloonCollection* NotificationUIManagerMac::balloon_collection() {
194 return builtin_manager_->balloon_collection();
195 }
196
197 NotificationPrefsManager* NotificationUIManagerMac::prefs_manager() {
198 return builtin_manager_.get();
199 }
200
201 void NotificationUIManagerMac::GetQueuedNotificationsForTesting(
202 std::vector<const Notification*>* notifications) {
203 return builtin_manager_->GetQueuedNotificationsForTesting(notifications);
204 }
205
206 const Notification*
207 NotificationUIManagerMac::FindNotificationWithCocoaNotification(
208 id<CrUserNotification> notif) {
209 std::string notification_id = base::SysNSStringToUTF8(
210 [notif.userInfo objectForKey:kNotificationIDKey]);
211
212 for (NotificationMap::iterator it = notification_map_.begin();
213 it != notification_map_.end();
214 ++it) {
215 if (it->second->notification_id() == notification_id)
216 return it->second;
217 }
218 return NULL;
219 }
220
221 bool NotificationUIManagerMac::RemoveNotification(
222 id<CrUserNotification> notif) {
223 std::string notification_id = base::SysNSStringToUTF8(
224 [notif.userInfo objectForKey:kNotificationIDKey]);
225 id<CrUserNotificationCenter> center = GetNotificationCenter();
226
227 // First remove all Cocoa notifications from the center that match the
228 // notification. Notifications in the system tray do not share pointer
229 // equality with the balloons or any other message delievered to the
230 // delegate, so this loop must be run through every time to clean up stale
231 // notifications.
232 NSArray* delivered_notifications = center.deliveredNotifications;
233 for (id<CrUserNotification> delivered_notif in delivered_notifications) {
234 if ([delivered_notif isEqual:notif]) {
235 [center removeDeliveredNotification:delivered_notif];
236 }
237 }
238
239 bool did_remove = false;
240
241 // Then go through and remove any C++ notifications that match the
242 // notification ID, and release any ObjC notifications to which this still
243 // owns a reference.
244 for (NotificationMap::iterator it = notification_map_.begin();
245 it != notification_map_.end();
246 ++it) {
247 if (it->second->notification_id() == notification_id) {
248 it->second->Close(false);
249 delete it->second;
250
251 [it->first release];
252
253 notification_map_.erase(it);
254
255 did_remove = true;
256 }
257 }
258
259 return did_remove;
260 }
261
262 id<CrUserNotification>
263 NotificationUIManagerMac::FindNotificationWithReplacementId(
264 const string16& replacement_id) {
265 for (NotificationMap::iterator it = notification_map_.begin();
266 it != notification_map_.end();
267 ++it) {
268 if (it->second->replace_id() == replacement_id)
269 return it->first;
270 }
271 return nil;
272 }
273
274 ////////////////////////////////////////////////////////////////////////////////
275
276 @implementation NotificationCenterDelegate
277
278 - (id)initWithManager:(NotificationUIManagerMac*)manager {
279 if ((self = [super init])) {
280 CHECK(manager);
281 manager_ = manager;
282 }
283 return self;
284 }
285
286 - (void)userNotificationCenter:(NSUserNotificationCenter*)center
287 didDeliverNotification:(id<CrUserNotification>)notification {
288 const Notification* notif = manager_->FindNotificationWithCocoaNotification(
289 notification);
290 if (notif)
291 notif->Display();
292 }
293
294 - (void)userNotificationCenter:(NSUserNotificationCenter*)center
295 didActivateNotification:(id<CrUserNotification>)notification {
296 const Notification* notif = manager_->FindNotificationWithCocoaNotification(
297 notification);
298 if (notif)
299 notif->Click();
300 }
301
302 - (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center
303 shouldPresentNotification:(id<CrUserNotification>)notification {
304 return YES;
305 }
306
307 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698