blob: ad795a0820c86b7780b4bfd68c3280050cc95931 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/notifications/sync_notifier/synced_notification.h"
#include "base/basictypes.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/notifications/notification.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h"
#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/load_flags.h"
#include "skia/ext/image_operations.h"
#include "sync/protocol/sync.pb.h"
#include "sync/protocol/synced_notification_specifics.pb.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/size.h"
#include "ui/gfx/skbitmap_operations.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/notification_types.h"
namespace {
const char kExtensionScheme[] = "synced-notification://";
const char kDefaultSyncedNotificationScheme[] = "https:";
// Today rich notifications only supports two buttons, make sure we don't
// try to supply them with more than this number of buttons.
const unsigned int kMaxNotificationButtonIndex = 2;
// Schema-less specs default badly in windows. If we find one, add the schema
// we expect instead of allowing windows specific GURL code to make it default
// to "file:".
GURL AddDefaultSchemaIfNeeded(std::string& url_spec) {
if (StartsWithASCII(url_spec, std::string("//"), false))
return GURL(std::string(kDefaultSyncedNotificationScheme) + url_spec);
return GURL(url_spec);
}
} // namespace
namespace notifier {
COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
SyncedNotification::kUnread) ==
sync_pb::CoalescedSyncedNotification_ReadState_UNREAD,
local_enum_must_match_protobuf_enum);
COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
SyncedNotification::kRead) ==
sync_pb::CoalescedSyncedNotification_ReadState_READ,
local_enum_must_match_protobuf_enum);
COMPILE_ASSERT(static_cast<sync_pb::CoalescedSyncedNotification_ReadState>(
SyncedNotification::kDismissed) ==
sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED,
local_enum_must_match_protobuf_enum);
SyncedNotification::SyncedNotification(
const syncer::SyncData& sync_data,
ChromeNotifierService* notifier_service,
NotificationUIManager* notification_manager)
: notification_manager_(notification_manager),
notifier_service_(notifier_service),
profile_(NULL),
toast_state_(true),
app_icon_bitmap_fetch_pending_(true),
sender_bitmap_fetch_pending_(true),
image_bitmap_fetch_pending_(true) {
Update(sync_data);
}
SyncedNotification::~SyncedNotification() {}
void SyncedNotification::Update(const syncer::SyncData& sync_data) {
// TODO(petewil): Add checking that the notification looks valid.
specifics_.CopyFrom(sync_data.GetSpecifics().synced_notification());
}
void SyncedNotification::Show(Profile* profile) {
// Let NotificationUIManager know that the notification has been dismissed.
if (SyncedNotification::kRead == GetReadState() ||
SyncedNotification::kDismissed == GetReadState() ) {
notification_manager_->CancelById(GetKey());
DVLOG(2) << "Dismissed or read notification arrived"
<< GetHeading() << " " << GetText();
return;
}
// |notifier_service| can be NULL in tests.
if (notifier_service_) {
notifier_service_->ShowWelcomeToastIfNecessary(this, notification_manager_);
}
// Set up the fields we need to send and create a Notification object.
GURL image_url = GetImageUrl();
base::string16 text = base::UTF8ToUTF16(GetText());
base::string16 heading = base::UTF8ToUTF16(GetHeading());
base::string16 description = base::UTF8ToUTF16(GetDescription());
base::string16 annotation = base::UTF8ToUTF16(GetAnnotation());
// TODO(petewil): Eventually put the display name of the sending service here.
base::string16 display_source = base::UTF8ToUTF16(GetAppId());
base::string16 replace_key = base::UTF8ToUTF16(GetKey());
base::string16 notification_heading = heading;
base::string16 notification_text = description;
base::string16 newline = base::UTF8ToUTF16("\n");
// The delegate will eventually catch calls that the notification
// was read or deleted, and send the changes back to the server.
scoped_refptr<NotificationDelegate> delegate =
new ChromeNotifierDelegate(GetKey(), notifier_service_);
// Some inputs and fields are only used if there is a notification center.
base::Time creation_time =
base::Time::FromDoubleT(static_cast<double>(GetCreationTime()));
int priority = GetPriority();
unsigned int button_count = GetButtonCount();
// Deduce which notification template to use from the data.
message_center::NotificationType notification_type =
message_center::NOTIFICATION_TYPE_BASE_FORMAT;
if (!image_url.is_empty()) {
notification_type = message_center::NOTIFICATION_TYPE_IMAGE;
} else if (button_count > 0) {
notification_type = message_center::NOTIFICATION_TYPE_BASE_FORMAT;
}
// Fill the optional fields with the information we need to make a
// notification.
message_center::RichNotificationData rich_notification_data;
rich_notification_data.timestamp = creation_time;
if (priority != SyncedNotification::kUndefinedPriority)
rich_notification_data.priority = priority;
// Fill in the button data.
// TODO(petewil): Today Rich notifiations are limited to two buttons.
// When rich notifications supports more, remove the
// "&& i < kMaxNotificationButtonIndex" clause below.
for (unsigned int i = 0;
i < button_count
&& i < button_bitmaps_.size()
&& i < kMaxNotificationButtonIndex;
++i) {
// Stop at the first button with no title
std::string title = GetButtonTitle(i);
if (title.empty())
break;
message_center::ButtonInfo button_info(base::UTF8ToUTF16(title));
if (!button_bitmaps_[i].IsEmpty())
button_info.icon = button_bitmaps_[i];
rich_notification_data.buttons.push_back(button_info);
}
// Fill in the bitmap images.
if (!image_bitmap_.IsEmpty())
rich_notification_data.image = image_bitmap_;
if (!app_icon_bitmap_.IsEmpty()) {
// Since we can't control the size of images we download, resize using a
// high quality filter down to the appropriate icon size.
// TODO(dewittj): Remove this when correct resources are sent via the
// protobuf.
SkBitmap new_app_icon =
skia::ImageOperations::Resize(app_icon_bitmap_.AsBitmap(),
skia::ImageOperations::RESIZE_BEST,
message_center::kSmallImageSize,
message_center::kSmallImageSize);
// The app icon should be in grayscale.
// TODO(dewittj): Remove this when correct resources are sent via the
// protobuf.
color_utils::HSL shift = {-1, 0, 0.6};
SkBitmap grayscale =
SkBitmapOperations::CreateHSLShiftedBitmap(new_app_icon, shift);
gfx::Image small_image =
gfx::Image(gfx::ImageSkia(gfx::ImageSkiaRep(grayscale, 1.0f)));
rich_notification_data.small_image = small_image;
}
// Set the ContextMessage inside the rich notification data for the
// annotation.
rich_notification_data.context_message = annotation;
// Set the clickable flag to change the cursor on hover if a valid
// destination is found.
rich_notification_data.clickable = GetDefaultDestinationUrl().is_valid();
// If there is at least one person sending, use the first picture.
// TODO(petewil): Someday combine multiple profile photos here.
gfx::Image icon_bitmap = app_icon_bitmap_;
if (GetProfilePictureCount() >= 1) {
icon_bitmap = sender_bitmap_;
}
Notification ui_notification(notification_type,
GetOriginUrl(),
notification_heading,
notification_text,
icon_bitmap,
blink::WebTextDirectionDefault,
message_center::NotifierId(GetOriginUrl()),
display_source,
replace_key,
rich_notification_data,
delegate.get());
// In case the notification is not supposed to be toasted, pretend that it
// has already been shown.
ui_notification.set_shown_as_popup(!toast_state_);
notification_manager_->Add(ui_notification, profile);
DVLOG(1) << "Showing Synced Notification! " << heading << " " << text
<< " " << GetAppIconUrl() << " " << replace_key << " "
<< GetProfilePictureUrl(0) << " " << GetReadState();
return;
}
sync_pb::EntitySpecifics SyncedNotification::GetEntitySpecifics() const {
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.mutable_synced_notification()->CopyFrom(specifics_);
return entity_specifics;
}
// Display the notification if it has the specified app_id_name.
void SyncedNotification::ShowAllForAppId(Profile* profile,
std::string app_id_name) {
if (app_id_name == GetAppId())
Show(profile);
}
// Remove the notification if it has the specified app_id_name.
void SyncedNotification::HideAllForAppId(std::string app_id_name) {
if (app_id_name == GetAppId()) {
notification_manager_->CancelById(GetKey());
}
}
void SyncedNotification::QueueBitmapFetchJobs(
ChromeNotifierService* notifier_service,
Profile* profile) {
// Save off the arguments for the call to Show.
notifier_service_ = notifier_service;
profile_ = profile;
// Ensure our bitmap vector has as many entries as there are buttons,
// so that when the bitmaps arrive the vector has a slot for them.
for (unsigned int i = 0; i < GetButtonCount(); ++i) {
button_bitmaps_.push_back(gfx::Image());
button_bitmaps_fetch_pending_.push_back(true);
CreateBitmapFetcher(GetButtonIconUrl(i));
}
// If there is a profile image bitmap, fetch it
if (GetProfilePictureCount() > 0) {
// TODO(petewil): When we have the capacity to display more than one bitmap,
// modify this code to fetch as many as we can display
CreateBitmapFetcher(GetProfilePictureUrl(0));
}
// If the URL is non-empty, add it to our queue of URLs to fetch.
CreateBitmapFetcher(GetAppIconUrl());
CreateBitmapFetcher(GetImageUrl());
// Check to see if we don't need to fetch images, either because we already
// did, or because the URLs are empty. If so, we can display the notification.
// See if all bitmaps are accounted for, if so call Show().
if (AreAllBitmapsFetched()) {
Show(profile_);
}
}
void SyncedNotification::StartBitmapFetch() {
// Now that we have queued and counted them all, start the fetching.
ScopedVector<chrome::BitmapFetcher>::iterator iter;
for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
(*iter)->Start(
profile_->GetRequestContext(),
std::string(),
net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
net::LOAD_NORMAL);
}
}
// This should detect even small changes in case the server updated the
// notification. We ignore the timestamp if other fields match.
bool SyncedNotification::EqualsIgnoringReadState(
const SyncedNotification& other) const {
if (GetTitle() == other.GetTitle() &&
GetHeading() == other.GetHeading() &&
GetDescription() == other.GetDescription() &&
GetAnnotation() == other.GetAnnotation() &&
GetAppId() == other.GetAppId() &&
GetKey() == other.GetKey() &&
GetOriginUrl() == other.GetOriginUrl() &&
GetAppIconUrl() == other.GetAppIconUrl() &&
GetImageUrl() == other.GetImageUrl() &&
GetText() == other.GetText() &&
// We intentionally skip read state
GetCreationTime() == other.GetCreationTime() &&
GetPriority() == other.GetPriority() &&
GetDefaultDestinationTitle() == other.GetDefaultDestinationTitle() &&
GetDefaultDestinationIconUrl() == other.GetDefaultDestinationIconUrl() &&
GetNotificationCount() == other.GetNotificationCount() &&
GetButtonCount() == other.GetButtonCount() &&
GetProfilePictureCount() == other.GetProfilePictureCount()) {
// If all the surface data matched, check, to see if contained data also
// matches, titles and messages.
size_t count = GetNotificationCount();
for (size_t ii = 0; ii < count; ++ii) {
if (GetContainedNotificationTitle(ii) !=
other.GetContainedNotificationTitle(ii))
return false;
if (GetContainedNotificationMessage(ii) !=
other.GetContainedNotificationMessage(ii))
return false;
}
// Make sure buttons match.
count = GetButtonCount();
for (size_t jj = 0; jj < count; ++jj) {
if (GetButtonTitle(jj) != other.GetButtonTitle(jj))
return false;
if (GetButtonIconUrl(jj) != other.GetButtonIconUrl(jj))
return false;
}
// Make sure profile icons match
count = GetButtonCount();
for (size_t kk = 0; kk < count; ++kk) {
if (GetProfilePictureUrl(kk) != other.GetProfilePictureUrl(kk))
return false;
}
// If buttons and notifications matched, they are equivalent.
return true;
}
return false;
}
void SyncedNotification::NotificationHasBeenRead() {
SetReadState(kRead);
}
void SyncedNotification::NotificationHasBeenDismissed() {
SetReadState(kDismissed);
}
std::string SyncedNotification::GetTitle() const {
if (!specifics_.coalesced_notification().render_info().expanded_info().
simple_expanded_layout().has_title())
return std::string();
return specifics_.coalesced_notification().render_info().expanded_info().
simple_expanded_layout().title();
}
std::string SyncedNotification::GetHeading() const {
if (!specifics_.coalesced_notification().render_info().collapsed_info().
simple_collapsed_layout().has_heading())
return std::string();
return specifics_.coalesced_notification().render_info().collapsed_info().
simple_collapsed_layout().heading();
}
std::string SyncedNotification::GetDescription() const {
if (!specifics_.coalesced_notification().render_info().collapsed_info().
simple_collapsed_layout().has_description())
return std::string();
return specifics_.coalesced_notification().render_info().collapsed_info().
simple_collapsed_layout().description();
}
std::string SyncedNotification::GetAnnotation() const {
if (!specifics_.coalesced_notification().render_info().collapsed_info().
simple_collapsed_layout().has_annotation())
return std::string();
return specifics_.coalesced_notification().render_info().collapsed_info().
simple_collapsed_layout().annotation();
}
std::string SyncedNotification::GetAppId() const {
if (!specifics_.coalesced_notification().has_app_id())
return std::string();
return specifics_.coalesced_notification().app_id();
}
std::string SyncedNotification::GetKey() const {
if (!specifics_.coalesced_notification().has_key())
return std::string();
return specifics_.coalesced_notification().key();
}
GURL SyncedNotification::GetOriginUrl() const {
std::string origin_url(kExtensionScheme);
origin_url += GetAppId();
return GURL(origin_url);
}
GURL SyncedNotification::GetAppIconUrl() const {
if (!specifics_.coalesced_notification().render_info().collapsed_info().
simple_collapsed_layout().has_app_icon())
return GURL();
std::string url_spec = specifics_.coalesced_notification().render_info().
collapsed_info().simple_collapsed_layout().app_icon().url();
return AddDefaultSchemaIfNeeded(url_spec);
}
// TODO(petewil): This ignores all but the first image. If Rich Notifications
// supports more images someday, then fetch all images.
GURL SyncedNotification::GetImageUrl() const {
if (specifics_.coalesced_notification().render_info().collapsed_info().
simple_collapsed_layout().media_size() == 0)
return GURL();
if (!specifics_.coalesced_notification().render_info().collapsed_info().
simple_collapsed_layout().media(0).image().has_url())
return GURL();
std::string url_spec = specifics_.coalesced_notification().render_info().
collapsed_info().simple_collapsed_layout().media(0).image().url();
return AddDefaultSchemaIfNeeded(url_spec);
}
std::string SyncedNotification::GetText() const {
if (!specifics_.coalesced_notification().render_info().expanded_info().
simple_expanded_layout().has_text())
return std::string();
return specifics_.coalesced_notification().render_info().expanded_info().
simple_expanded_layout().text();
}
SyncedNotification::ReadState SyncedNotification::GetReadState() const {
DCHECK(specifics_.coalesced_notification().has_read_state());
sync_pb::CoalescedSyncedNotification_ReadState found_read_state =
specifics_.coalesced_notification().read_state();
if (found_read_state ==
sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED) {
return kDismissed;
} else if (found_read_state ==
sync_pb::CoalescedSyncedNotification_ReadState_UNREAD) {
return kUnread;
} else if (found_read_state ==
sync_pb::CoalescedSyncedNotification_ReadState_READ) {
return kRead;
} else {
NOTREACHED();
return static_cast<SyncedNotification::ReadState>(found_read_state);
}
}
// Time in milliseconds since the unix epoch, or 0 if not available.
uint64 SyncedNotification::GetCreationTime() const {
if (!specifics_.coalesced_notification().has_creation_time_msec())
return 0;
return specifics_.coalesced_notification().creation_time_msec();
}
int SyncedNotification::GetPriority() const {
if (!specifics_.coalesced_notification().has_priority())
return kUndefinedPriority;
int protobuf_priority = specifics_.coalesced_notification().priority();
// Convert the prioroty to the scheme used by the notification center.
if (protobuf_priority ==
sync_pb::CoalescedSyncedNotification_Priority_INVISIBLE) {
return message_center::LOW_PRIORITY;
} else if (protobuf_priority ==
sync_pb::CoalescedSyncedNotification_Priority_LOW) {
return message_center::DEFAULT_PRIORITY;
} else if (protobuf_priority ==
sync_pb::CoalescedSyncedNotification_Priority_HIGH) {
return message_center::HIGH_PRIORITY;
} else {
// Complain if this is a new priority we have not seen before.
DCHECK(protobuf_priority <
sync_pb::CoalescedSyncedNotification_Priority_INVISIBLE ||
sync_pb::CoalescedSyncedNotification_Priority_HIGH <
protobuf_priority);
return kUndefinedPriority;
}
}
std::string SyncedNotification::GetDefaultDestinationTitle() const {
if (!specifics_.coalesced_notification().render_info().collapsed_info().
default_destination().icon().has_alt_text()) {
return std::string();
}
return specifics_.coalesced_notification().render_info().collapsed_info().
default_destination().icon().alt_text();
}
GURL SyncedNotification::GetDefaultDestinationIconUrl() const {
if (!specifics_.coalesced_notification().render_info().collapsed_info().
default_destination().icon().has_url()) {
return GURL();
}
std::string url_spec = specifics_.coalesced_notification().render_info().
collapsed_info().default_destination().icon().url();
return AddDefaultSchemaIfNeeded(url_spec);
}
GURL SyncedNotification::GetDefaultDestinationUrl() const {
if (!specifics_.coalesced_notification().render_info().collapsed_info().
default_destination().has_url()) {
return GURL();
}
std::string url_spec = specifics_.coalesced_notification().render_info().
collapsed_info().default_destination().url();
return AddDefaultSchemaIfNeeded(url_spec);
}
std::string SyncedNotification::GetButtonTitle(
unsigned int which_button) const {
// Must ensure that we have a target before trying to access it.
if (GetButtonCount() <= which_button)
return std::string();
if (!specifics_.coalesced_notification().render_info().collapsed_info().
target(which_button).action().icon().has_alt_text()) {
return std::string();
}
return specifics_.coalesced_notification().render_info().collapsed_info().
target(which_button).action().icon().alt_text();
}
GURL SyncedNotification::GetButtonIconUrl(unsigned int which_button) const {
// Must ensure that we have a target before trying to access it.
if (GetButtonCount() <= which_button)
return GURL();
if (!specifics_.coalesced_notification().render_info().collapsed_info().
target(which_button).action().icon().has_url()) {
return GURL();
}
std::string url_spec = specifics_.coalesced_notification().render_info().
collapsed_info().target(which_button).action().icon().url();
return AddDefaultSchemaIfNeeded(url_spec);
}
GURL SyncedNotification::GetButtonUrl(unsigned int which_button) const {
// Must ensure that we have a target before trying to access it.
if (GetButtonCount() <= which_button)
return GURL();
if (!specifics_.coalesced_notification().render_info().collapsed_info().
target(which_button).action().has_url()) {
return GURL();
}
std::string url_spec = specifics_.coalesced_notification().render_info().
collapsed_info().target(which_button).action().url();
return AddDefaultSchemaIfNeeded(url_spec);
}
GURL SyncedNotification::GetProfilePictureUrl(unsigned int which_url) const {
if (GetProfilePictureCount() <= which_url)
return GURL();
std::string url_spec = specifics_.coalesced_notification().render_info().
collapsed_info().simple_collapsed_layout().profile_image(which_url).
image_url();
return AddDefaultSchemaIfNeeded(url_spec);
}
size_t SyncedNotification::GetProfilePictureCount() const {
return specifics_.coalesced_notification().render_info().collapsed_info().
simple_collapsed_layout().profile_image_size();
}
size_t SyncedNotification::GetNotificationCount() const {
return specifics_.coalesced_notification().render_info().
expanded_info().collapsed_info_size();
}
size_t SyncedNotification::GetButtonCount() const {
return specifics_.coalesced_notification().render_info().collapsed_info().
target_size();
}
std::string SyncedNotification::GetContainedNotificationTitle(
int index) const {
if (specifics_.coalesced_notification().render_info().expanded_info().
collapsed_info_size() < index + 1)
return std::string();
return specifics_.coalesced_notification().render_info().expanded_info().
collapsed_info(index).simple_collapsed_layout().heading();
}
std::string SyncedNotification::GetContainedNotificationMessage(
int index) const {
if (specifics_.coalesced_notification().render_info().expanded_info().
collapsed_info_size() < index + 1)
return std::string();
return specifics_.coalesced_notification().render_info().expanded_info().
collapsed_info(index).simple_collapsed_layout().description();
}
const gfx::Image& SyncedNotification::GetAppIcon() const {
return app_icon_bitmap_;
}
void SyncedNotification::set_toast_state(bool toast_state) {
toast_state_ = toast_state;
}
void SyncedNotification::LogNotification() {
std::string readStateString("Unread");
if (SyncedNotification::kRead == GetReadState())
readStateString = "Read";
else if (SyncedNotification::kDismissed == GetReadState())
readStateString = "Dismissed";
DVLOG(2) << " Notification: Heading is " << GetHeading()
<< " description is " << GetDescription()
<< " key is " << GetKey()
<< " read state is " << readStateString;
}
// TODO(petewil): The fetch mechanism appears to be returning two bitmaps on the
// mac - perhaps one is regular, one is high dpi? If so, ensure we use the high
// dpi bitmap when appropriate.
void SyncedNotification::OnFetchComplete(const GURL url,
const SkBitmap* bitmap) {
// Make sure we are on the thread we expect.
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
gfx::Image downloaded_image;
if (bitmap != NULL)
downloaded_image = gfx::Image::CreateFrom1xBitmap(*bitmap);
// Match the incoming bitmaps to URLs. In case this is a dup, make sure to
// try all potentially matching urls.
if (GetAppIconUrl() == url) {
app_icon_bitmap_ = downloaded_image;
if (app_icon_bitmap_.IsEmpty())
app_icon_bitmap_fetch_pending_ = false;
}
if (GetImageUrl() == url) {
image_bitmap_ = downloaded_image;
if (image_bitmap_.IsEmpty())
image_bitmap_fetch_pending_ = false;
}
if (GetProfilePictureUrl(0) == url) {
sender_bitmap_ = downloaded_image;
if (sender_bitmap_.IsEmpty())
sender_bitmap_fetch_pending_ = false;
}
// If this URL matches one or more button bitmaps, save them off.
for (unsigned int i = 0; i < GetButtonCount(); ++i) {
if (GetButtonIconUrl(i) == url) {
if (bitmap != NULL) {
button_bitmaps_[i] = gfx::Image::CreateFrom1xBitmap(*bitmap);
}
button_bitmaps_fetch_pending_[i] = false;
}
}
DVLOG(2) << __FUNCTION__ << " popping bitmap " << url;
// See if all bitmaps are already accounted for, if so call Show.
if (AreAllBitmapsFetched()) {
Show(profile_);
}
}
void SyncedNotification::CreateBitmapFetcher(const GURL& url) {
// Check for dups, ignore any request for a dup.
ScopedVector<chrome::BitmapFetcher>::iterator iter;
for (iter = fetchers_.begin(); iter != fetchers_.end(); ++iter) {
if ((*iter)->url() == url)
return;
}
if (url.is_valid()) {
fetchers_.push_back(new chrome::BitmapFetcher(url, this));
DVLOG(2) << __FUNCTION__ << "Pushing bitmap " << url;
}
}
// Check that we have either fetched or gotten an error on all the bitmaps we
// asked for.
bool SyncedNotification::AreAllBitmapsFetched() {
bool app_icon_ready = GetAppIconUrl().is_empty() ||
!app_icon_bitmap_.IsEmpty() || !app_icon_bitmap_fetch_pending_;
bool images_ready = GetImageUrl().is_empty() || !image_bitmap_.IsEmpty() ||
!image_bitmap_fetch_pending_;
bool sender_picture_ready = GetProfilePictureUrl(0).is_empty() ||
!sender_bitmap_.IsEmpty() || !sender_bitmap_fetch_pending_;
bool button_bitmaps_ready = true;
for (unsigned int j = 0; j < GetButtonCount(); ++j) {
if (!GetButtonIconUrl(j).is_empty()
&& button_bitmaps_[j].IsEmpty()
&& button_bitmaps_fetch_pending_[j]) {
button_bitmaps_ready = false;
break;
}
}
return app_icon_ready && images_ready && sender_picture_ready &&
button_bitmaps_ready;
}
// Set the read state on the notification, returns true for success.
void SyncedNotification::SetReadState(const ReadState& read_state) {
// Convert the read state to the protobuf type for read state.
if (kDismissed == read_state)
specifics_.mutable_coalesced_notification()->set_read_state(
sync_pb::CoalescedSyncedNotification_ReadState_DISMISSED);
else if (kUnread == read_state)
specifics_.mutable_coalesced_notification()->set_read_state(
sync_pb::CoalescedSyncedNotification_ReadState_UNREAD);
else if (kRead == read_state)
specifics_.mutable_coalesced_notification()->set_read_state(
sync_pb::CoalescedSyncedNotification_ReadState_READ);
else
NOTREACHED();
}
} // namespace notifier