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.
5 #include "chrome/browser/notifications/sync_notifier/synced_notification.h"
7 #include "base/basictypes.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/time/time.h"
11 #include "base/values.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/notifications/notification.h"
14 #include "chrome/browser/notifications/notification_ui_manager.h"
15 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "sync/protocol/sync.pb.h"
18 #include "sync/protocol/synced_notification_specifics.pb.h"
19 #include "ui/gfx/image/image.h"
20 #include "ui/message_center/message_center_util.h"
21 #include "ui/message_center/notification_types.h"
24 const char kExtensionScheme[] = "synced-notification://";
25 const char kDefaultSyncedNotificationScheme[] = "https:";
27 // The name of our first synced notification service.
28 // TODO(petewil): remove this hardcoding once we have the synced notification
29 // signalling sync data type set up to provide this.
31 const char kFirstSyncedNotificationServiceId[] = "Google+";
34 // Today rich notifications only supports two buttons, make sure we don't
35 // try to supply them with more than this number of buttons.
36 const unsigned int kMaxNotificationButtonIndex = 2;
38 bool UseRichNotifications() {
39 return message_center::IsRichNotificationEnabled();
42 // Schema-less specs default badly in windows. If we find one, add the schema
43 // we expect instead of allowing windows specific GURL code to make it default
45 GURL AddDefaultSchemaIfNeeded(std::string& url_spec) {
46 if (StartsWithASCII(url_spec, std::string("//"), false))
47 return GURL(std::string(kDefaultSyncedNotificationScheme) + url_spec);
49 return GURL(url_spec);
56 COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
57 SyncedNotification::kUnread) ==
58 sync_pb::CoalescedSyncedNotification_ReadState_UNREAD,
59 local_enum_must_match_protobuf_enum);
60 COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
61 SyncedNotification::kRead) ==
62 sync_pb::CoalescedSyncedNotification_ReadState_READ,
63 local_enum_must_match_protobuf_enum);
64 COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
65 SyncedNotification::kDismissed) ==
66 sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED,
67 local_enum_must_match_protobuf_enum);
69 SyncedNotification::SyncedNotification(const syncer::SyncData& sync_data)
70 : notification_manager_(NULL),
71 notifier_service_(NULL),
73 active_fetcher_count_(0),
78 SyncedNotification::~SyncedNotification() {}
80 void SyncedNotification::Update(const syncer::SyncData& sync_data) {
81 // TODO(petewil): Add checking that the notification looks valid.
82 specifics_.CopyFrom(sync_data.GetSpecifics().synced_notification());
85 sync_pb::EntitySpecifics SyncedNotification::GetEntitySpecifics() const {
86 sync_pb::EntitySpecifics entity_specifics;
87 entity_specifics.mutable_synced_notification()->CopyFrom(specifics_);
88 return entity_specifics;
91 // TODO(petewil): The fetch mechanism appears to be returning two bitmaps on the
92 // mac - perhaps one is regular, one is high dpi? If so, ensure we use the high
93 // dpi bitmap when appropriate.
94 void SyncedNotification::OnFetchComplete(const GURL url,
95 const SkBitmap* bitmap) {
96 // TODO(petewil): Add timeout mechanism in case bitmaps take too long. Do we
97 // already have one built into URLFetcher?
98 // Make sure we are on the thread we expect.
99 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
101 // Match the incoming bitmaps to URLs. In case this is a dup, make sure to
102 // try all potentially matching urls.
103 if (GetAppIconUrl() == url && bitmap != NULL) {
104 app_icon_bitmap_ = gfx::Image::CreateFrom1xBitmap(*bitmap);
106 if (GetImageUrl() == url && bitmap != NULL) {
107 image_bitmap_ = gfx::Image::CreateFrom1xBitmap(*bitmap);
109 if (GetProfilePictureUrl(0) == url && bitmap != NULL) {
110 sender_bitmap_ = gfx::Image::CreateFrom1xBitmap(*bitmap);
113 // If this URL matches one or more button bitmaps, save them off.
114 for (unsigned int i = 0; i < GetButtonCount(); ++i) {
115 if (GetButtonIconUrl(i) == url && bitmap != NULL)
116 button_bitmaps_[i] = gfx::Image::CreateFrom1xBitmap(*bitmap);
119 // Count off the bitmaps as they arrive.
120 --active_fetcher_count_;
122 DVLOG(2) << __FUNCTION__ << " popping bitmap " << url;
123 DVLOG(2) << __FUNCTION__ << " size is " << bitmap->getSize();
125 // Check to see if all images we need are now present.
126 bool app_icon_ready = GetAppIconUrl().is_empty() ||
127 !app_icon_bitmap_.IsEmpty();
128 bool images_ready = GetImageUrl().is_empty() || !image_bitmap_.IsEmpty();
129 bool sender_picture_ready = GetProfilePictureUrl(0).is_empty() ||
130 !sender_bitmap_.IsEmpty();
131 bool button_bitmaps_ready = true;
132 for (unsigned int j = 0; j < GetButtonCount(); ++j) {
133 if (!GetButtonIconUrl(j).is_empty() && button_bitmaps_[j].IsEmpty()) {
134 button_bitmaps_ready = false;
138 // See if all bitmaps are accounted for, if so call Show.
139 if (app_icon_ready && images_ready && sender_picture_ready &&
140 button_bitmaps_ready) {
141 Show(notification_manager_, notifier_service_, profile_);
145 void SyncedNotification::QueueBitmapFetchJobs(
146 NotificationUIManager* notification_manager,
147 ChromeNotifierService* notifier_service,
149 // If we are not using the MessageCenter, call show now, and the existing
150 // code will handle the bitmap fetch for us.
151 if (!UseRichNotifications()) {
152 Show(notification_manager, notifier_service, profile);
156 // Save off the arguments for the call to Show.
157 notification_manager_ = notification_manager;
158 notifier_service_ = notifier_service;
160 DCHECK_EQ(active_fetcher_count_, 0);
162 // Ensure our bitmap vector has as many entries as there are buttons,
163 // so that when the bitmaps arrive the vector has a slot for them.
164 for (unsigned int i = 0; i < GetButtonCount(); ++i) {
165 button_bitmaps_.push_back(gfx::Image());
166 AddBitmapToFetchQueue(GetButtonIconUrl(i));
169 // If there is a profile image bitmap, fetch it
170 if (GetProfilePictureCount() > 0) {
171 // TODO(petewil): When we have the capacity to display more than one bitmap,
172 // modify this code to fetch as many as we can display
173 AddBitmapToFetchQueue(GetProfilePictureUrl(0));
176 // If the URL is non-empty, add it to our queue of URLs to fetch.
177 AddBitmapToFetchQueue(GetAppIconUrl());
178 AddBitmapToFetchQueue(GetImageUrl());
180 // If there are no bitmaps, call show now.
181 if (active_fetcher_count_ == 0) {
182 Show(notification_manager, notifier_service, profile);
186 void SyncedNotification::StartBitmapFetch() {
187 // Now that we have queued and counted them all, start the fetching.
188 ScopedVector<NotificationBitmapFetcher>::iterator iter;
189 for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
190 (*iter)->Start(profile_);
194 void SyncedNotification::AddBitmapToFetchQueue(const GURL& url) {
195 // Check for dups, ignore any request for a dup.
196 ScopedVector<NotificationBitmapFetcher>::iterator iter;
197 for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
198 if ((*iter)->url() == url)
202 if (url.is_valid()) {
203 ++active_fetcher_count_;
204 fetchers_.push_back(new NotificationBitmapFetcher(url, this));
205 DVLOG(2) << __FUNCTION__ << "Pushing bitmap " << url;
209 void SyncedNotification::Show(NotificationUIManager* notification_manager,
210 ChromeNotifierService* notifier_service,
212 // Let NotificationUIManager know that the notification has been dismissed.
213 if (SyncedNotification::kRead == GetReadState() ||
214 SyncedNotification::kDismissed == GetReadState() ) {
215 notification_manager->CancelById(GetKey());
216 DVLOG(2) << "Dismissed or read notification arrived"
217 << GetHeading() << " " << GetText();
221 // Set up the fields we need to send and create a Notification object.
222 GURL image_url = GetImageUrl();
223 string16 text = UTF8ToUTF16(GetText());
224 string16 heading = UTF8ToUTF16(GetHeading());
225 string16 description = UTF8ToUTF16(GetDescription());
226 string16 annotation = UTF8ToUTF16(GetAnnotation());
227 // TODO(petewil): Eventually put the display name of the sending service here.
228 string16 display_source = UTF8ToUTF16(GetAppId());
229 string16 replace_key = UTF8ToUTF16(GetKey());
230 string16 notification_heading = heading;
231 string16 notification_text = description;
232 string16 newline = UTF8ToUTF16("\n");
234 // The delegate will eventually catch calls that the notification
235 // was read or deleted, and send the changes back to the server.
236 scoped_refptr<NotificationDelegate> delegate =
237 new ChromeNotifierDelegate(GetKey(), notifier_service);
239 // Some inputs and fields are only used if there is a notification center.
240 if (UseRichNotifications()) {
241 base::Time creation_time =
242 base::Time::FromDoubleT(static_cast<double>(GetCreationTime()));
243 int priority = GetPriority();
244 unsigned int button_count = GetButtonCount();
246 // Deduce which notification template to use from the data.
247 message_center::NotificationType notification_type =
248 message_center::NOTIFICATION_TYPE_BASE_FORMAT;
249 if (!image_url.is_empty()) {
250 notification_type = message_center::NOTIFICATION_TYPE_IMAGE;
251 } else if (button_count > 0) {
252 notification_type = message_center::NOTIFICATION_TYPE_BASE_FORMAT;
255 // Fill the optional fields with the information we need to make a
257 message_center::RichNotificationData rich_notification_data;
258 rich_notification_data.timestamp = creation_time;
259 if (priority != SyncedNotification::kUndefinedPriority)
260 rich_notification_data.priority = priority;
262 // Fill in the button data.
263 // TODO(petewil): Today Rich notifiations are limited to two buttons.
264 // When rich notifications supports more, remove the
265 // "&& i < kMaxNotificationButtonIndex" clause below.
266 for (unsigned int i = 0;
268 && i < button_bitmaps_.size()
269 && i < kMaxNotificationButtonIndex;
271 // Stop at the first button with no title
272 std::string title = GetButtonTitle(i);
275 message_center::ButtonInfo button_info(UTF8ToUTF16(title));
276 if (!button_bitmaps_[i].IsEmpty())
277 button_info.icon = button_bitmaps_[i];
278 rich_notification_data.buttons.push_back(button_info);
281 // Fill in the bitmap images.
282 if (!image_bitmap_.IsEmpty())
283 rich_notification_data.image = image_bitmap_;
285 // Set the ContextMessage inside the rich notification data for the
287 rich_notification_data.context_message = annotation;
289 // If there is at least one person sending, use the first picture.
290 // TODO(petewil): Someday combine multiple profile photos here.
291 gfx::Image icon_bitmap = app_icon_bitmap_;
292 if (GetProfilePictureCount() >= 1) {
293 icon_bitmap = sender_bitmap_;
296 Notification ui_notification(notification_type,
298 notification_heading,
301 WebKit::WebTextDirectionDefault,
302 message_center::NotifierId(GetOriginUrl()),
305 rich_notification_data,
308 // In case the notification is not supposed to be toasted, pretend that it
309 // has already been shown.
310 ui_notification.set_shown_as_popup(!toast_state_);
312 notification_manager->Add(ui_notification, profile);
314 // In this case we have a Webkit Notification, not a Rich Notification.
315 Notification ui_notification(GetOriginUrl(),
317 notification_heading,
319 WebKit::WebTextDirectionDefault,
324 notification_manager->Add(ui_notification, profile);
327 DVLOG(1) << "Showing Synced Notification! " << heading << " " << text
328 << " " << GetAppIconUrl() << " " << replace_key << " "
329 << GetProfilePictureUrl(0) << " " << GetReadState();
334 // This should detect even small changes in case the server updated the
335 // notification. We ignore the timestamp if other fields match.
336 bool SyncedNotification::EqualsIgnoringReadState(
337 const SyncedNotification& other) const {
338 if (GetTitle() == other.GetTitle() &&
339 GetHeading() == other.GetHeading() &&
340 GetDescription() == other.GetDescription() &&
341 GetAnnotation() == other.GetAnnotation() &&
342 GetAppId() == other.GetAppId() &&
343 GetKey() == other.GetKey() &&
344 GetOriginUrl() == other.GetOriginUrl() &&
345 GetAppIconUrl() == other.GetAppIconUrl() &&
346 GetImageUrl() == other.GetImageUrl() &&
347 GetText() == other.GetText() &&
348 // We intentionally skip read state
349 GetCreationTime() == other.GetCreationTime() &&
350 GetPriority() == other.GetPriority() &&
351 GetDefaultDestinationTitle() == other.GetDefaultDestinationTitle() &&
352 GetDefaultDestinationIconUrl() == other.GetDefaultDestinationIconUrl() &&
353 GetNotificationCount() == other.GetNotificationCount() &&
354 GetButtonCount() == other.GetButtonCount() &&
355 GetProfilePictureCount() == other.GetProfilePictureCount()) {
357 // If all the surface data matched, check, to see if contained data also
358 // matches, titles and messages.
359 size_t count = GetNotificationCount();
360 for (size_t ii = 0; ii < count; ++ii) {
361 if (GetContainedNotificationTitle(ii) !=
362 other.GetContainedNotificationTitle(ii))
364 if (GetContainedNotificationMessage(ii) !=
365 other.GetContainedNotificationMessage(ii))
369 // Make sure buttons match.
370 count = GetButtonCount();
371 for (size_t jj = 0; jj < count; ++jj) {
372 if (GetButtonTitle(jj) != other.GetButtonTitle(jj))
374 if (GetButtonIconUrl(jj) != other.GetButtonIconUrl(jj))
378 // Make sure profile icons match
379 count = GetButtonCount();
380 for (size_t kk = 0; kk < count; ++kk) {
381 if (GetProfilePictureUrl(kk) != other.GetProfilePictureUrl(kk))
385 // If buttons and notifications matched, they are equivalent.
392 void SyncedNotification::LogNotification() {
393 std::string readStateString("Unread");
394 if (SyncedNotification::kRead == GetReadState())
395 readStateString = "Read";
396 else if (SyncedNotification::kDismissed == GetReadState())
397 readStateString = "Dismissed";
399 DVLOG(2) << " Notification: Heading is " << GetHeading()
400 << " description is " << GetDescription()
401 << " key is " << GetKey()
402 << " read state is " << readStateString;
405 // Set the read state on the notification, returns true for success.
406 void SyncedNotification::SetReadState(const ReadState& read_state) {
408 // Convert the read state to the protobuf type for read state.
409 if (kDismissed == read_state)
410 specifics_.mutable_coalesced_notification()->set_read_state(
411 sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED);
412 else if (kUnread == read_state)
413 specifics_.mutable_coalesced_notification()->set_read_state(
414 sync_pb::CoalescedSyncedNotification_ReadState_UNREAD);
415 else if (kRead == read_state)
416 specifics_.mutable_coalesced_notification()->set_read_state(
417 sync_pb::CoalescedSyncedNotification_ReadState_READ);
422 void SyncedNotification::NotificationHasBeenRead() {
426 void SyncedNotification::NotificationHasBeenDismissed() {
427 SetReadState(kDismissed);
430 std::string SyncedNotification::GetTitle() const {
431 if (!specifics_.coalesced_notification().render_info().expanded_info().
432 simple_expanded_layout().has_title())
433 return std::string();
435 return specifics_.coalesced_notification().render_info().expanded_info().
436 simple_expanded_layout().title();
439 std::string SyncedNotification::GetHeading() const {
440 if (!specifics_.coalesced_notification().render_info().collapsed_info().
441 simple_collapsed_layout().has_heading())
442 return std::string();
444 return specifics_.coalesced_notification().render_info().collapsed_info().
445 simple_collapsed_layout().heading();
448 std::string SyncedNotification::GetDescription() const {
449 if (!specifics_.coalesced_notification().render_info().collapsed_info().
450 simple_collapsed_layout().has_description())
451 return std::string();
453 return specifics_.coalesced_notification().render_info().collapsed_info().
454 simple_collapsed_layout().description();
457 std::string SyncedNotification::GetAnnotation() const {
458 if (!specifics_.coalesced_notification().render_info().collapsed_info().
459 simple_collapsed_layout().has_annotation())
460 return std::string();
462 return specifics_.coalesced_notification().render_info().collapsed_info().
463 simple_collapsed_layout().annotation();
466 std::string SyncedNotification::GetAppId() const {
467 if (!specifics_.coalesced_notification().has_app_id())
468 return std::string();
469 return specifics_.coalesced_notification().app_id();
472 std::string SyncedNotification::GetKey() const {
473 if (!specifics_.coalesced_notification().has_key())
474 return std::string();
475 return specifics_.coalesced_notification().key();
478 GURL SyncedNotification::GetOriginUrl() const {
479 std::string origin_url(kExtensionScheme);
480 origin_url += GetAppId();
481 return GURL(origin_url);
484 GURL SyncedNotification::GetAppIconUrl() const {
485 if (!specifics_.coalesced_notification().render_info().collapsed_info().
486 simple_collapsed_layout().has_app_icon())
489 std::string url_spec = specifics_.coalesced_notification().render_info().
490 collapsed_info().simple_collapsed_layout().app_icon().url();
492 return AddDefaultSchemaIfNeeded(url_spec);
495 // TODO(petewil): This ignores all but the first image. If Rich Notifications
496 // supports more images someday, then fetch all images.
497 GURL SyncedNotification::GetImageUrl() const {
498 if (specifics_.coalesced_notification().render_info().collapsed_info().
499 simple_collapsed_layout().media_size() == 0)
502 if (!specifics_.coalesced_notification().render_info().collapsed_info().
503 simple_collapsed_layout().media(0).image().has_url())
506 std::string url_spec = specifics_.coalesced_notification().render_info().
507 collapsed_info().simple_collapsed_layout().media(0).image().url();
509 return AddDefaultSchemaIfNeeded(url_spec);
512 std::string SyncedNotification::GetText() const {
513 if (!specifics_.coalesced_notification().render_info().expanded_info().
514 simple_expanded_layout().has_text())
515 return std::string();
517 return specifics_.coalesced_notification().render_info().expanded_info().
518 simple_expanded_layout().text();
521 SyncedNotification::ReadState SyncedNotification::GetReadState() const {
522 DCHECK(specifics_.coalesced_notification().has_read_state());
524 sync_pb::CoalescedSyncedNotification_ReadState found_read_state =
525 specifics_.coalesced_notification().read_state();
527 if (found_read_state ==
528 sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED) {
530 } else if (found_read_state ==
531 sync_pb::CoalescedSyncedNotification_ReadState_UNREAD) {
533 } else if (found_read_state ==
534 sync_pb::CoalescedSyncedNotification_ReadState_READ) {
538 return static_cast<SyncedNotification::ReadState>(found_read_state);
542 // Time in milliseconds since the unix epoch, or 0 if not available.
543 uint64 SyncedNotification::GetCreationTime() const {
544 if (!specifics_.coalesced_notification().has_creation_time_msec())
547 return specifics_.coalesced_notification().creation_time_msec();
550 int SyncedNotification::GetPriority() const {
551 if (!specifics_.coalesced_notification().has_priority())
552 return kUndefinedPriority;
553 int protobuf_priority = specifics_.coalesced_notification().priority();
555 // Convert the prioroty to the scheme used by the notification center.
556 if (protobuf_priority ==
557 sync_pb::CoalescedSyncedNotification_Priority_LOW) {
558 return message_center::LOW_PRIORITY;
559 } else if (protobuf_priority ==
560 sync_pb::CoalescedSyncedNotification_Priority_STANDARD) {
561 return message_center::DEFAULT_PRIORITY;
562 } else if (protobuf_priority ==
563 sync_pb::CoalescedSyncedNotification_Priority_HIGH) {
564 return message_center::HIGH_PRIORITY;
566 // Complain if this is a new priority we have not seen before.
567 DCHECK(protobuf_priority <
568 sync_pb::CoalescedSyncedNotification_Priority_LOW ||
569 sync_pb::CoalescedSyncedNotification_Priority_HIGH <
571 return kUndefinedPriority;
575 size_t SyncedNotification::GetNotificationCount() const {
576 return specifics_.coalesced_notification().render_info().
577 expanded_info().collapsed_info_size();
580 size_t SyncedNotification::GetButtonCount() const {
581 return specifics_.coalesced_notification().render_info().collapsed_info().
585 size_t SyncedNotification::GetProfilePictureCount() const {
586 return specifics_.coalesced_notification().render_info().collapsed_info().
587 simple_collapsed_layout().profile_image_size();
590 GURL SyncedNotification::GetProfilePictureUrl(unsigned int which_url) const {
591 if (GetProfilePictureCount() <= which_url)
594 std::string url_spec = specifics_.coalesced_notification().render_info().
595 collapsed_info().simple_collapsed_layout().profile_image(which_url).
598 return AddDefaultSchemaIfNeeded(url_spec);
602 std::string SyncedNotification::GetDefaultDestinationTitle() const {
603 if (!specifics_.coalesced_notification().render_info().collapsed_info().
604 default_destination().icon().has_alt_text()) {
605 return std::string();
607 return specifics_.coalesced_notification().render_info().collapsed_info().
608 default_destination().icon().alt_text();
611 GURL SyncedNotification::GetDefaultDestinationIconUrl() const {
612 if (!specifics_.coalesced_notification().render_info().collapsed_info().
613 default_destination().icon().has_url()) {
616 std::string url_spec = specifics_.coalesced_notification().render_info().
617 collapsed_info().default_destination().icon().url();
619 return AddDefaultSchemaIfNeeded(url_spec);
622 GURL SyncedNotification::GetDefaultDestinationUrl() const {
623 if (!specifics_.coalesced_notification().render_info().collapsed_info().
624 default_destination().has_url()) {
627 std::string url_spec = specifics_.coalesced_notification().render_info().
628 collapsed_info().default_destination().url();
630 return AddDefaultSchemaIfNeeded(url_spec);
633 std::string SyncedNotification::GetButtonTitle(
634 unsigned int which_button) const {
635 // Must ensure that we have a target before trying to access it.
636 if (GetButtonCount() <= which_button)
637 return std::string();
638 if (!specifics_.coalesced_notification().render_info().collapsed_info().
639 target(which_button).action().icon().has_alt_text()) {
640 return std::string();
642 return specifics_.coalesced_notification().render_info().collapsed_info().
643 target(which_button).action().icon().alt_text();
646 GURL SyncedNotification::GetButtonIconUrl(unsigned int which_button) const {
647 // Must ensure that we have a target before trying to access it.
648 if (GetButtonCount() <= which_button)
650 if (!specifics_.coalesced_notification().render_info().collapsed_info().
651 target(which_button).action().icon().has_url()) {
654 std::string url_spec = specifics_.coalesced_notification().render_info().
655 collapsed_info().target(which_button).action().icon().url();
657 return AddDefaultSchemaIfNeeded(url_spec);
660 GURL SyncedNotification::GetButtonUrl(unsigned int which_button) const {
661 // Must ensure that we have a target before trying to access it.
662 if (GetButtonCount() <= which_button)
664 if (!specifics_.coalesced_notification().render_info().collapsed_info().
665 target(which_button).action().has_url()) {
668 std::string url_spec = specifics_.coalesced_notification().render_info().
669 collapsed_info().target(which_button).action().url();
671 return AddDefaultSchemaIfNeeded(url_spec);
674 std::string SyncedNotification::GetContainedNotificationTitle(
676 if (specifics_.coalesced_notification().render_info().expanded_info().
677 collapsed_info_size() < index + 1)
678 return std::string();
680 return specifics_.coalesced_notification().render_info().expanded_info().
681 collapsed_info(index).simple_collapsed_layout().heading();
684 std::string SyncedNotification::GetContainedNotificationMessage(
686 if (specifics_.coalesced_notification().render_info().expanded_info().
687 collapsed_info_size() < index + 1)
688 return std::string();
690 return specifics_.coalesced_notification().render_info().expanded_info().
691 collapsed_info(index).simple_collapsed_layout().description();
694 std::string SyncedNotification::GetSendingServiceId() const {
695 // TODO(petewil): We are building a new protocol (a new sync datatype) to send
696 // the service name and icon from the server. For now this method is
697 // hardcoded to the name of our first service using synced notifications.
698 // Once the new protocol is built, remove this hardcoding.
699 return kFirstSyncedNotificationServiceId;
702 void SyncedNotification::SetToastState(bool toast_state) {
703 toast_state_ = toast_state;
706 } // namespace notifier