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

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: Change notification_map_ 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
« no previous file with comments | « chrome/browser/notifications/notification_ui_manager_mac.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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, provide the interface defined as a protocol and
19 // use that instead, because sizeof(id<Protocol>) == sizeof(Class*). In order to
20 // instantiate, use NSClassFromString and simply assign the alloc/init'd result
21 // to an instance of the proper protocol. This way the compiler, linker, and
22 // 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::ControllerNotification::ControllerNotification(
102 id<CrUserNotification> a_view, Notification* a_model)
jianli 2012/04/13 00:03:35 nit: you don't need to use different name for the
Robert Sesek 2012/04/16 14:20:27 Clang does not agree and reports "error: field is
Avi (use Gerrit) 2012/04/16 14:33:45 I was about to lay down some standard smackdown, b
jianli 2012/04/17 17:14:27 The problem might be due to the type of view: id<C
Robert Sesek 2012/04/17 17:31:12 Also produces the warning.
Nico 2012/04/17 23:58:10 FWIW, I can't see this warning locally or on the b
103 : view(a_view),
104 model(a_model) {
105 }
106
107 NotificationUIManagerMac::ControllerNotification::~ControllerNotification() {
108 [view release];
109 delete model;
110 }
111
112 ////////////////////////////////////////////////////////////////////////////////
113
114 NotificationUIManagerMac::NotificationUIManagerMac(PrefService* local_state)
115 : builtin_manager_(new NotificationUIManagerImpl(local_state)),
116 delegate_(ALLOW_THIS_IN_INITIALIZER_LIST(
117 [[NotificationCenterDelegate alloc] initWithManager:this])) {
118 DCHECK(!GetNotificationCenter().delegate);
119 GetNotificationCenter().delegate = delegate_.get();
120 }
121
122 NotificationUIManagerMac::~NotificationUIManagerMac() {
jianli 2012/04/13 00:03:35 Do we want to clean up notification_map_?
Robert Sesek 2012/04/16 14:20:27 Done.
123 }
124
125 void NotificationUIManagerMac::Add(const Notification& notification,
126 Profile* profile) {
127 if (notification.is_html()) {
128 builtin_manager_->Add(notification, profile);
129 } else {
130 id<CrUserNotification> replacee = FindNotificationWithReplacementId(
131 notification.replace_id());
132 if (replacee)
133 RemoveNotification(replacee);
134
135 // Owned by ControllerNotification.
136 id<CrUserNotification> ns_notification =
137 [[NSClassFromString(@"NSUserNotification") alloc] init];
138
139 ns_notification.title = base::SysUTF16ToNSString(notification.title());
140 ns_notification.subtitle =
141 base::SysUTF16ToNSString(notification.display_source());
142 ns_notification.informativeText =
143 base::SysUTF16ToNSString(notification.body());
144 ns_notification.userInfo =
145 [NSDictionary dictionaryWithObject:base::SysUTF8ToNSString(
146 notification.notification_id())
147 forKey:kNotificationIDKey];
148 ns_notification.hasActionButton = NO;
149
150 notification_map_.insert(
151 std::make_pair(notification.notification_id(),
152 new ControllerNotification(ns_notification,
153 new Notification(notification))));
154
155 [GetNotificationCenter() deliverNotification:ns_notification];
156 }
157 }
158
159 bool NotificationUIManagerMac::CancelById(const std::string& notification_id) {
160 NotificationMap::iterator it = notification_map_.find(notification_id);
161 if (it == notification_map_.end())
162 return builtin_manager_->CancelById(notification_id);
163
164 return RemoveNotification(it->second->view);
165 }
166
167 bool NotificationUIManagerMac::CancelAllBySourceOrigin(
168 const GURL& source_origin) {
169 bool success = builtin_manager_->CancelAllBySourceOrigin(source_origin);
170
171 for (NotificationMap::iterator it = notification_map_.begin();
172 it != notification_map_.end();
173 ++it) {
174 if (it->second->model->origin_url() == source_origin) {
175 success |= RemoveNotification(it->second->view);
jianli 2012/04/13 00:03:35 RemoveNotification calls erase, which might invali
Robert Sesek 2012/04/16 14:20:27 Done.
176 }
177 }
178
179 return success;
180 }
181
182 void NotificationUIManagerMac::CancelAll() {
183 id<CrUserNotificationCenter> center = GetNotificationCenter();
184
185 // Calling RemoveNotification would loop many times over, so just replicate
186 // a small bit of its logic here.
187 for (NotificationMap::iterator it = notification_map_.begin();
188 it != notification_map_.end();
189 ++it) {
190 it->second->model->Close(false);
191 delete it->second;
192 }
193 notification_map_.clear();
194
195 // Clean up any lingering ones in the system tray.
196 [center removeAllDeliveredNotifications];
197
198 builtin_manager_->CancelAll();
199 }
200
201 BalloonCollection* NotificationUIManagerMac::balloon_collection() {
202 return builtin_manager_->balloon_collection();
203 }
204
205 NotificationPrefsManager* NotificationUIManagerMac::prefs_manager() {
206 return builtin_manager_.get();
207 }
208
209 void NotificationUIManagerMac::GetQueuedNotificationsForTesting(
210 std::vector<const Notification*>* notifications) {
211 return builtin_manager_->GetQueuedNotificationsForTesting(notifications);
212 }
213
214 const Notification*
215 NotificationUIManagerMac::FindNotificationWithCocoaNotification(
216 id<CrUserNotification> notification) const {
217 std::string notification_id = base::SysNSStringToUTF8(
218 [notification.userInfo objectForKey:kNotificationIDKey]);
219
220 NotificationMap::const_iterator it = notification_map_.find(notification_id);
221 if (it == notification_map_.end())
222 return NULL;
223
224 return it->second->model;
225 }
226
227 bool NotificationUIManagerMac::RemoveNotification(
228 id<CrUserNotification> notification) {
229 std::string notification_id = base::SysNSStringToUTF8(
230 [notification.userInfo objectForKey:kNotificationIDKey]);
231 id<CrUserNotificationCenter> center = GetNotificationCenter();
232
233 // First remove all Cocoa notifications from the center that match the
234 // notification. Notifications in the system tray do not share pointer
235 // equality with the balloons or any other message delievered to the
236 // delegate, so this loop must be run through every time to clean up stale
237 // notifications.
238 NSArray* delivered_notifications = center.deliveredNotifications;
239 for (id<CrUserNotification> delivered in delivered_notifications) {
240 if ([delivered isEqual:notification]) {
241 [center removeDeliveredNotification:delivered];
242 }
243 }
244
245 // Then clean up the C++ model side.
246 NotificationMap::iterator it = notification_map_.find(notification_id);
247 if (it == notification_map_.end())
248 return false;
249
250 it->second->model->Close(false);
251 delete it->second;
252 notification_map_.erase(it);
253
254 return true;
255 }
256
257 id<CrUserNotification>
258 NotificationUIManagerMac::FindNotificationWithReplacementId(
259 const string16& replacement_id) const {
260 for (NotificationMap::const_iterator it = notification_map_.begin();
261 it != notification_map_.end();
262 ++it) {
263 if (it->second->model->replace_id() == replacement_id)
264 return it->second->view;
265 }
266 return nil;
267 }
268
269 ////////////////////////////////////////////////////////////////////////////////
270
271 @implementation NotificationCenterDelegate
272
273 - (id)initWithManager:(NotificationUIManagerMac*)manager {
274 if ((self = [super init])) {
275 CHECK(manager);
276 manager_ = manager;
277 }
278 return self;
279 }
280
281 - (void)userNotificationCenter:(NSUserNotificationCenter*)center
282 didDeliverNotification:(id<CrUserNotification>)nsNotification {
283 const Notification* notification =
284 manager_->FindNotificationWithCocoaNotification(nsNotification);
285 if (notification)
286 notification->Display();
287 }
288
289 - (void)userNotificationCenter:(NSUserNotificationCenter*)center
290 didActivateNotification:(id<CrUserNotification>)nsNotification {
291 const Notification* notification =
292 manager_->FindNotificationWithCocoaNotification(nsNotification);
293 if (notification)
294 notification->Click();
295 }
296
297 - (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center
298 shouldPresentNotification:(id<CrUserNotification>)nsNotification {
299 // Always display notifications, regardless of whether the app is foreground.
300 return YES;
301 }
302
303 @end
OLDNEW
« no previous file with comments | « chrome/browser/notifications/notification_ui_manager_mac.h ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698