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/stringprintf.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/time/time.h"
12 #include "base/values.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/notifications/notification.h"
15 #include "chrome/browser/notifications/notification_ui_manager.h"
16 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h"
17 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "skia/ext/image_operations.h"
20 #include "sync/protocol/sync.pb.h"
21 #include "sync/protocol/synced_notification_specifics.pb.h"
22 #include "third_party/skia/include/core/SkPaint.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/gfx/color_utils.h"
25 #include "ui/gfx/image/image.h"
26 #include "ui/gfx/size.h"
27 #include "ui/gfx/skbitmap_operations.h"
28 #include "ui/message_center/message_center_style.h"
29 #include "ui/message_center/message_center_util.h"
30 #include "ui/message_center/notification_types.h"
33 const char kExtensionScheme[] = "synced-notification://";
34 const char kDefaultSyncedNotificationScheme[] = "https:";
36 // Today rich notifications only supports two buttons, make sure we don't
37 // try to supply them with more than this number of buttons.
38 const unsigned int kMaxNotificationButtonIndex = 2;
40 bool UseRichNotifications() {
41 return message_center::IsRichNotificationEnabled();
44 // Schema-less specs default badly in windows. If we find one, add the schema
45 // we expect instead of allowing windows specific GURL code to make it default
47 GURL AddDefaultSchemaIfNeeded(std::string& url_spec) {
48 if (StartsWithASCII(url_spec, std::string("//"), false))
49 return GURL(std::string(kDefaultSyncedNotificationScheme) + url_spec);
51 return GURL(url_spec);
58 COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
59 SyncedNotification::kUnread) ==
60 sync_pb::CoalescedSyncedNotification_ReadState_UNREAD,
61 local_enum_must_match_protobuf_enum);
62 COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
63 SyncedNotification::kRead) ==
64 sync_pb::CoalescedSyncedNotification_ReadState_READ,
65 local_enum_must_match_protobuf_enum);
66 COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
67 SyncedNotification::kDismissed) ==
68 sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED,
69 local_enum_must_match_protobuf_enum);
71 SyncedNotification::SyncedNotification(const syncer::SyncData& sync_data)
72 : notification_manager_(NULL),
73 notifier_service_(NULL),
76 app_icon_bitmap_fetch_pending_(true),
77 sender_bitmap_fetch_pending_(true),
78 image_bitmap_fetch_pending_(true) {
82 SyncedNotification::~SyncedNotification() {}
84 void SyncedNotification::Update(const syncer::SyncData& sync_data) {
85 // TODO(petewil): Add checking that the notification looks valid.
86 specifics_.CopyFrom(sync_data.GetSpecifics().synced_notification());
89 sync_pb::EntitySpecifics SyncedNotification::GetEntitySpecifics() const {
90 sync_pb::EntitySpecifics entity_specifics;
91 entity_specifics.mutable_synced_notification()->CopyFrom(specifics_);
92 return entity_specifics;
95 // Check that we have either fetched or gotten an error on all the bitmaps we
97 bool SyncedNotification::AreAllBitmapsFetched() {
98 bool app_icon_ready = GetAppIconUrl().is_empty() ||
99 !app_icon_bitmap_.IsEmpty() || !app_icon_bitmap_fetch_pending_;
100 bool images_ready = GetImageUrl().is_empty() || !image_bitmap_.IsEmpty() ||
101 !image_bitmap_fetch_pending_;
102 bool sender_picture_ready = GetProfilePictureUrl(0).is_empty() ||
103 !sender_bitmap_.IsEmpty() || !sender_bitmap_fetch_pending_;
104 bool button_bitmaps_ready = true;
105 for (unsigned int j = 0; j < GetButtonCount(); ++j) {
106 if (!GetButtonIconUrl(j).is_empty()
107 && button_bitmaps_[j].IsEmpty()
108 && button_bitmaps_fetch_pending_[j]) {
109 button_bitmaps_ready = false;
114 return app_icon_ready && images_ready && sender_picture_ready &&
115 button_bitmaps_ready;
118 // TODO(petewil): The fetch mechanism appears to be returning two bitmaps on the
119 // mac - perhaps one is regular, one is high dpi? If so, ensure we use the high
120 // dpi bitmap when appropriate.
121 void SyncedNotification::OnFetchComplete(const GURL url,
122 const SkBitmap* bitmap) {
123 // TODO(petewil): Add timeout mechanism in case bitmaps take too long. Do we
124 // already have one built into URLFetcher?
125 // Make sure we are on the thread we expect.
126 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
128 gfx::Image downloaded_image;
130 downloaded_image = gfx::Image::CreateFrom1xBitmap(*bitmap);
132 // Match the incoming bitmaps to URLs. In case this is a dup, make sure to
133 // try all potentially matching urls.
134 if (GetAppIconUrl() == url) {
135 app_icon_bitmap_ = downloaded_image;
136 if (app_icon_bitmap_.IsEmpty())
137 app_icon_bitmap_fetch_pending_ = false;
139 if (GetImageUrl() == url) {
140 image_bitmap_ = downloaded_image;
141 if (image_bitmap_.IsEmpty())
142 image_bitmap_fetch_pending_ = false;
144 if (GetProfilePictureUrl(0) == url) {
145 sender_bitmap_ = downloaded_image;
146 if (sender_bitmap_.IsEmpty())
147 sender_bitmap_fetch_pending_ = false;
150 // If this URL matches one or more button bitmaps, save them off.
151 for (unsigned int i = 0; i < GetButtonCount(); ++i) {
152 if (GetButtonIconUrl(i) == url) {
153 if (bitmap != NULL) {
154 button_bitmaps_[i] = gfx::Image::CreateFrom1xBitmap(*bitmap);
156 button_bitmaps_fetch_pending_[i] = false;
160 DVLOG(2) << __FUNCTION__ << " popping bitmap " << url;
162 // See if all bitmaps are already accounted for, if so call Show.
163 if (AreAllBitmapsFetched()) {
164 Show(notification_manager_, notifier_service_, profile_);
168 void SyncedNotification::QueueBitmapFetchJobs(
169 NotificationUIManager* notification_manager,
170 ChromeNotifierService* notifier_service,
172 // If we are not using the MessageCenter, call show now, and the existing
173 // code will handle the bitmap fetch for us.
174 if (!UseRichNotifications()) {
175 Show(notification_manager, notifier_service, profile);
179 // Save off the arguments for the call to Show.
180 notification_manager_ = notification_manager;
181 notifier_service_ = notifier_service;
184 // Ensure our bitmap vector has as many entries as there are buttons,
185 // so that when the bitmaps arrive the vector has a slot for them.
186 for (unsigned int i = 0; i < GetButtonCount(); ++i) {
187 button_bitmaps_.push_back(gfx::Image());
188 button_bitmaps_fetch_pending_.push_back(true);
189 AddBitmapToFetchQueue(GetButtonIconUrl(i));
192 // If there is a profile image bitmap, fetch it
193 if (GetProfilePictureCount() > 0) {
194 // TODO(petewil): When we have the capacity to display more than one bitmap,
195 // modify this code to fetch as many as we can display
196 AddBitmapToFetchQueue(GetProfilePictureUrl(0));
199 // If the URL is non-empty, add it to our queue of URLs to fetch.
200 AddBitmapToFetchQueue(GetAppIconUrl());
201 AddBitmapToFetchQueue(GetImageUrl());
203 // Check to see if we don't need to fetch images, either because we already
204 // did, or because the URLs are empty. If so, we can display the notification.
206 // See if all bitmaps are accounted for, if so call Show().
207 if (AreAllBitmapsFetched()) {
208 Show(notification_manager_, notifier_service_, profile_);
212 void SyncedNotification::StartBitmapFetch() {
213 // Now that we have queued and counted them all, start the fetching.
214 ScopedVector<chrome::BitmapFetcher>::iterator iter;
215 for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
216 (*iter)->Start(profile_);
220 void SyncedNotification::AddBitmapToFetchQueue(const GURL& url) {
221 // Check for dups, ignore any request for a dup.
222 ScopedVector<chrome::BitmapFetcher>::iterator iter;
223 for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
224 if ((*iter)->url() == url)
228 if (url.is_valid()) {
229 fetchers_.push_back(new chrome::BitmapFetcher(url, this));
230 DVLOG(2) << __FUNCTION__ << "Pushing bitmap " << url;
234 void SyncedNotification::Show(NotificationUIManager* notification_manager,
235 ChromeNotifierService* notifier_service,
237 // Let NotificationUIManager know that the notification has been dismissed.
238 if (SyncedNotification::kRead == GetReadState() ||
239 SyncedNotification::kDismissed == GetReadState() ) {
240 notification_manager->CancelById(GetKey());
241 DVLOG(2) << "Dismissed or read notification arrived"
242 << GetHeading() << " " << GetText();
246 // |notifier_service| can be NULL in tests.
247 if (UseRichNotifications() && notifier_service) {
248 notifier_service->ShowWelcomeToastIfNecessary(this, notification_manager);
251 // Set up the fields we need to send and create a Notification object.
252 GURL image_url = GetImageUrl();
253 base::string16 text = base::UTF8ToUTF16(GetText());
254 base::string16 heading = base::UTF8ToUTF16(GetHeading());
255 base::string16 description = base::UTF8ToUTF16(GetDescription());
256 base::string16 annotation = base::UTF8ToUTF16(GetAnnotation());
257 // TODO(petewil): Eventually put the display name of the sending service here.
258 base::string16 display_source = base::UTF8ToUTF16(GetAppId());
259 base::string16 replace_key = base::UTF8ToUTF16(GetKey());
260 base::string16 notification_heading = heading;
261 base::string16 notification_text = description;
262 base::string16 newline = base::UTF8ToUTF16("\n");
264 // The delegate will eventually catch calls that the notification
265 // was read or deleted, and send the changes back to the server.
266 scoped_refptr<NotificationDelegate> delegate =
267 new ChromeNotifierDelegate(GetKey(), notifier_service);
269 // Some inputs and fields are only used if there is a notification center.
270 if (UseRichNotifications()) {
271 base::Time creation_time =
272 base::Time::FromDoubleT(static_cast<double>(GetCreationTime()));
273 int priority = GetPriority();
274 unsigned int button_count = GetButtonCount();
276 // Deduce which notification template to use from the data.
277 message_center::NotificationType notification_type =
278 message_center::NOTIFICATION_TYPE_BASE_FORMAT;
279 if (!image_url.is_empty()) {
280 notification_type = message_center::NOTIFICATION_TYPE_IMAGE;
281 } else if (button_count > 0) {
282 notification_type = message_center::NOTIFICATION_TYPE_BASE_FORMAT;
285 // Fill the optional fields with the information we need to make a
287 message_center::RichNotificationData rich_notification_data;
288 rich_notification_data.timestamp = creation_time;
289 if (priority != SyncedNotification::kUndefinedPriority)
290 rich_notification_data.priority = priority;
292 // Fill in the button data.
293 // TODO(petewil): Today Rich notifiations are limited to two buttons.
294 // When rich notifications supports more, remove the
295 // "&& i < kMaxNotificationButtonIndex" clause below.
296 for (unsigned int i = 0;
298 && i < button_bitmaps_.size()
299 && i < kMaxNotificationButtonIndex;
301 // Stop at the first button with no title
302 std::string title = GetButtonTitle(i);
305 message_center::ButtonInfo button_info(base::UTF8ToUTF16(title));
306 if (!button_bitmaps_[i].IsEmpty())
307 button_info.icon = button_bitmaps_[i];
308 rich_notification_data.buttons.push_back(button_info);
311 // Fill in the bitmap images.
312 if (!image_bitmap_.IsEmpty())
313 rich_notification_data.image = image_bitmap_;
315 if (!app_icon_bitmap_.IsEmpty()) {
316 // Since we can't control the size of images we download, resize using a
317 // high quality filter down to the appropriate icon size.
318 // TODO(dewittj): Remove this when correct resources are sent via the
320 SkBitmap new_app_icon =
321 skia::ImageOperations::Resize(app_icon_bitmap_.AsBitmap(),
322 skia::ImageOperations::RESIZE_BEST,
323 message_center::kSmallImageSize,
324 message_center::kSmallImageSize);
326 // The app icon should be in grayscale.
327 // TODO(dewittj): Remove this when correct resources are sent via the
329 color_utils::HSL shift = {-1, 0, 0.6};
331 SkBitmapOperations::CreateHSLShiftedBitmap(new_app_icon, shift);
332 gfx::Image small_image =
333 gfx::Image(gfx::ImageSkia(gfx::ImageSkiaRep(grayscale, 1.0f)));
334 rich_notification_data.small_image = small_image;
337 // Set the ContextMessage inside the rich notification data for the
339 rich_notification_data.context_message = annotation;
341 // Set the clickable flag to change the cursor on hover if a valid
342 // destination is found.
343 rich_notification_data.clickable = GetDefaultDestinationUrl().is_valid();
345 // If there is at least one person sending, use the first picture.
346 // TODO(petewil): Someday combine multiple profile photos here.
347 gfx::Image icon_bitmap = app_icon_bitmap_;
348 if (GetProfilePictureCount() >= 1) {
349 icon_bitmap = sender_bitmap_;
352 Notification ui_notification(notification_type,
354 notification_heading,
357 blink::WebTextDirectionDefault,
358 message_center::NotifierId(GetOriginUrl()),
361 rich_notification_data,
363 // In case the notification is not supposed to be toasted, pretend that it
364 // has already been shown.
365 ui_notification.set_shown_as_popup(!toast_state_);
367 notification_manager->Add(ui_notification, profile);
369 // In this case we have a Webkit Notification, not a Rich Notification.
370 Notification ui_notification(GetOriginUrl(),
372 notification_heading,
374 blink::WebTextDirectionDefault,
379 notification_manager->Add(ui_notification, profile);
382 DVLOG(1) << "Showing Synced Notification! " << heading << " " << text
383 << " " << GetAppIconUrl() << " " << replace_key << " "
384 << GetProfilePictureUrl(0) << " " << GetReadState();
389 // This should detect even small changes in case the server updated the
390 // notification. We ignore the timestamp if other fields match.
391 bool SyncedNotification::EqualsIgnoringReadState(
392 const SyncedNotification& other) const {
393 if (GetTitle() == other.GetTitle() &&
394 GetHeading() == other.GetHeading() &&
395 GetDescription() == other.GetDescription() &&
396 GetAnnotation() == other.GetAnnotation() &&
397 GetAppId() == other.GetAppId() &&
398 GetKey() == other.GetKey() &&
399 GetOriginUrl() == other.GetOriginUrl() &&
400 GetAppIconUrl() == other.GetAppIconUrl() &&
401 GetImageUrl() == other.GetImageUrl() &&
402 GetText() == other.GetText() &&
403 // We intentionally skip read state
404 GetCreationTime() == other.GetCreationTime() &&
405 GetPriority() == other.GetPriority() &&
406 GetDefaultDestinationTitle() == other.GetDefaultDestinationTitle() &&
407 GetDefaultDestinationIconUrl() == other.GetDefaultDestinationIconUrl() &&
408 GetNotificationCount() == other.GetNotificationCount() &&
409 GetButtonCount() == other.GetButtonCount() &&
410 GetProfilePictureCount() == other.GetProfilePictureCount()) {
412 // If all the surface data matched, check, to see if contained data also
413 // matches, titles and messages.
414 size_t count = GetNotificationCount();
415 for (size_t ii = 0; ii < count; ++ii) {
416 if (GetContainedNotificationTitle(ii) !=
417 other.GetContainedNotificationTitle(ii))
419 if (GetContainedNotificationMessage(ii) !=
420 other.GetContainedNotificationMessage(ii))
424 // Make sure buttons match.
425 count = GetButtonCount();
426 for (size_t jj = 0; jj < count; ++jj) {
427 if (GetButtonTitle(jj) != other.GetButtonTitle(jj))
429 if (GetButtonIconUrl(jj) != other.GetButtonIconUrl(jj))
433 // Make sure profile icons match
434 count = GetButtonCount();
435 for (size_t kk = 0; kk < count; ++kk) {
436 if (GetProfilePictureUrl(kk) != other.GetProfilePictureUrl(kk))
440 // If buttons and notifications matched, they are equivalent.
447 void SyncedNotification::LogNotification() {
448 std::string readStateString("Unread");
449 if (SyncedNotification::kRead == GetReadState())
450 readStateString = "Read";
451 else if (SyncedNotification::kDismissed == GetReadState())
452 readStateString = "Dismissed";
454 DVLOG(2) << " Notification: Heading is " << GetHeading()
455 << " description is " << GetDescription()
456 << " key is " << GetKey()
457 << " read state is " << readStateString;
460 // Set the read state on the notification, returns true for success.
461 void SyncedNotification::SetReadState(const ReadState& read_state) {
463 // Convert the read state to the protobuf type for read state.
464 if (kDismissed == read_state)
465 specifics_.mutable_coalesced_notification()->set_read_state(
466 sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED);
467 else if (kUnread == read_state)
468 specifics_.mutable_coalesced_notification()->set_read_state(
469 sync_pb::CoalescedSyncedNotification_ReadState_UNREAD);
470 else if (kRead == read_state)
471 specifics_.mutable_coalesced_notification()->set_read_state(
472 sync_pb::CoalescedSyncedNotification_ReadState_READ);
477 void SyncedNotification::NotificationHasBeenRead() {
481 void SyncedNotification::NotificationHasBeenDismissed() {
482 SetReadState(kDismissed);
485 std::string SyncedNotification::GetTitle() const {
486 if (!specifics_.coalesced_notification().render_info().expanded_info().
487 simple_expanded_layout().has_title())
488 return std::string();
490 return specifics_.coalesced_notification().render_info().expanded_info().
491 simple_expanded_layout().title();
494 std::string SyncedNotification::GetHeading() const {
495 if (!specifics_.coalesced_notification().render_info().collapsed_info().
496 simple_collapsed_layout().has_heading())
497 return std::string();
499 return specifics_.coalesced_notification().render_info().collapsed_info().
500 simple_collapsed_layout().heading();
503 std::string SyncedNotification::GetDescription() const {
504 if (!specifics_.coalesced_notification().render_info().collapsed_info().
505 simple_collapsed_layout().has_description())
506 return std::string();
508 return specifics_.coalesced_notification().render_info().collapsed_info().
509 simple_collapsed_layout().description();
512 std::string SyncedNotification::GetAnnotation() const {
513 if (!specifics_.coalesced_notification().render_info().collapsed_info().
514 simple_collapsed_layout().has_annotation())
515 return std::string();
517 return specifics_.coalesced_notification().render_info().collapsed_info().
518 simple_collapsed_layout().annotation();
521 std::string SyncedNotification::GetAppId() const {
522 if (!specifics_.coalesced_notification().has_app_id())
523 return std::string();
524 return specifics_.coalesced_notification().app_id();
527 std::string SyncedNotification::GetKey() const {
528 if (!specifics_.coalesced_notification().has_key())
529 return std::string();
530 return specifics_.coalesced_notification().key();
533 GURL SyncedNotification::GetOriginUrl() const {
534 std::string origin_url(kExtensionScheme);
535 origin_url += GetAppId();
536 return GURL(origin_url);
539 GURL SyncedNotification::GetAppIconUrl() const {
540 if (!specifics_.coalesced_notification().render_info().collapsed_info().
541 simple_collapsed_layout().has_app_icon())
544 std::string url_spec = specifics_.coalesced_notification().render_info().
545 collapsed_info().simple_collapsed_layout().app_icon().url();
547 return AddDefaultSchemaIfNeeded(url_spec);
550 // TODO(petewil): This ignores all but the first image. If Rich Notifications
551 // supports more images someday, then fetch all images.
552 GURL SyncedNotification::GetImageUrl() const {
553 if (specifics_.coalesced_notification().render_info().collapsed_info().
554 simple_collapsed_layout().media_size() == 0)
557 if (!specifics_.coalesced_notification().render_info().collapsed_info().
558 simple_collapsed_layout().media(0).image().has_url())
561 std::string url_spec = specifics_.coalesced_notification().render_info().
562 collapsed_info().simple_collapsed_layout().media(0).image().url();
564 return AddDefaultSchemaIfNeeded(url_spec);
567 std::string SyncedNotification::GetText() const {
568 if (!specifics_.coalesced_notification().render_info().expanded_info().
569 simple_expanded_layout().has_text())
570 return std::string();
572 return specifics_.coalesced_notification().render_info().expanded_info().
573 simple_expanded_layout().text();
576 SyncedNotification::ReadState SyncedNotification::GetReadState() const {
577 DCHECK(specifics_.coalesced_notification().has_read_state());
579 sync_pb::CoalescedSyncedNotification_ReadState found_read_state =
580 specifics_.coalesced_notification().read_state();
582 if (found_read_state ==
583 sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED) {
585 } else if (found_read_state ==
586 sync_pb::CoalescedSyncedNotification_ReadState_UNREAD) {
588 } else if (found_read_state ==
589 sync_pb::CoalescedSyncedNotification_ReadState_READ) {
593 return static_cast<SyncedNotification::ReadState>(found_read_state);
597 // Time in milliseconds since the unix epoch, or 0 if not available.
598 uint64 SyncedNotification::GetCreationTime() const {
599 if (!specifics_.coalesced_notification().has_creation_time_msec())
602 return specifics_.coalesced_notification().creation_time_msec();
605 int SyncedNotification::GetPriority() const {
606 if (!specifics_.coalesced_notification().has_priority())
607 return kUndefinedPriority;
608 int protobuf_priority = specifics_.coalesced_notification().priority();
610 // Convert the prioroty to the scheme used by the notification center.
611 if (protobuf_priority ==
612 sync_pb::CoalescedSyncedNotification_Priority_LOW) {
613 return message_center::LOW_PRIORITY;
614 } else if (protobuf_priority ==
615 sync_pb::CoalescedSyncedNotification_Priority_STANDARD) {
616 return message_center::DEFAULT_PRIORITY;
617 } else if (protobuf_priority ==
618 sync_pb::CoalescedSyncedNotification_Priority_HIGH) {
619 return message_center::HIGH_PRIORITY;
621 // Complain if this is a new priority we have not seen before.
622 DCHECK(protobuf_priority <
623 sync_pb::CoalescedSyncedNotification_Priority_LOW ||
624 sync_pb::CoalescedSyncedNotification_Priority_HIGH <
626 return kUndefinedPriority;
630 size_t SyncedNotification::GetNotificationCount() const {
631 return specifics_.coalesced_notification().render_info().
632 expanded_info().collapsed_info_size();
635 size_t SyncedNotification::GetButtonCount() const {
636 return specifics_.coalesced_notification().render_info().collapsed_info().
640 size_t SyncedNotification::GetProfilePictureCount() const {
641 return specifics_.coalesced_notification().render_info().collapsed_info().
642 simple_collapsed_layout().profile_image_size();
645 GURL SyncedNotification::GetProfilePictureUrl(unsigned int which_url) const {
646 if (GetProfilePictureCount() <= which_url)
649 std::string url_spec = specifics_.coalesced_notification().render_info().
650 collapsed_info().simple_collapsed_layout().profile_image(which_url).
653 return AddDefaultSchemaIfNeeded(url_spec);
657 std::string SyncedNotification::GetDefaultDestinationTitle() const {
658 if (!specifics_.coalesced_notification().render_info().collapsed_info().
659 default_destination().icon().has_alt_text()) {
660 return std::string();
662 return specifics_.coalesced_notification().render_info().collapsed_info().
663 default_destination().icon().alt_text();
666 GURL SyncedNotification::GetDefaultDestinationIconUrl() const {
667 if (!specifics_.coalesced_notification().render_info().collapsed_info().
668 default_destination().icon().has_url()) {
671 std::string url_spec = specifics_.coalesced_notification().render_info().
672 collapsed_info().default_destination().icon().url();
674 return AddDefaultSchemaIfNeeded(url_spec);
677 GURL SyncedNotification::GetDefaultDestinationUrl() const {
678 if (!specifics_.coalesced_notification().render_info().collapsed_info().
679 default_destination().has_url()) {
682 std::string url_spec = specifics_.coalesced_notification().render_info().
683 collapsed_info().default_destination().url();
685 return AddDefaultSchemaIfNeeded(url_spec);
688 std::string SyncedNotification::GetButtonTitle(
689 unsigned int which_button) const {
690 // Must ensure that we have a target before trying to access it.
691 if (GetButtonCount() <= which_button)
692 return std::string();
693 if (!specifics_.coalesced_notification().render_info().collapsed_info().
694 target(which_button).action().icon().has_alt_text()) {
695 return std::string();
697 return specifics_.coalesced_notification().render_info().collapsed_info().
698 target(which_button).action().icon().alt_text();
701 GURL SyncedNotification::GetButtonIconUrl(unsigned int which_button) const {
702 // Must ensure that we have a target before trying to access it.
703 if (GetButtonCount() <= which_button)
705 if (!specifics_.coalesced_notification().render_info().collapsed_info().
706 target(which_button).action().icon().has_url()) {
709 std::string url_spec = specifics_.coalesced_notification().render_info().
710 collapsed_info().target(which_button).action().icon().url();
712 return AddDefaultSchemaIfNeeded(url_spec);
715 GURL SyncedNotification::GetButtonUrl(unsigned int which_button) const {
716 // Must ensure that we have a target before trying to access it.
717 if (GetButtonCount() <= which_button)
719 if (!specifics_.coalesced_notification().render_info().collapsed_info().
720 target(which_button).action().has_url()) {
723 std::string url_spec = specifics_.coalesced_notification().render_info().
724 collapsed_info().target(which_button).action().url();
726 return AddDefaultSchemaIfNeeded(url_spec);
729 std::string SyncedNotification::GetContainedNotificationTitle(
731 if (specifics_.coalesced_notification().render_info().expanded_info().
732 collapsed_info_size() < index + 1)
733 return std::string();
735 return specifics_.coalesced_notification().render_info().expanded_info().
736 collapsed_info(index).simple_collapsed_layout().heading();
739 std::string SyncedNotification::GetContainedNotificationMessage(
741 if (specifics_.coalesced_notification().render_info().expanded_info().
742 collapsed_info_size() < index + 1)
743 return std::string();
745 return specifics_.coalesced_notification().render_info().expanded_info().
746 collapsed_info(index).simple_collapsed_layout().description();
749 std::string SyncedNotification::GetSendingServiceId() const {
750 // TODO(petewil): We are building a new protocol (a new sync datatype) to send
751 // the service name and icon from the server. For now this method is
752 // hardcoded to the name of our first service using synced notifications.
753 // Once the new protocol is built, remove this hardcoding.
754 return kFirstSyncedNotificationServiceId;
757 const gfx::Image& SyncedNotification::GetAppIcon() const {
758 return app_icon_bitmap_;
761 void SyncedNotification::SetToastState(bool toast_state) {
762 toast_state_ = toast_state;
765 } // namespace notifier