diff options
author | dewittj@chromium.org <dewittj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-30 07:37:24 +0000 |
---|---|---|
committer | dewittj@chromium.org <dewittj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-30 07:37:24 +0000 |
commit | 9f7f7451433f7912e18070df23c5ea4e1623af93 (patch) | |
tree | 8db42fd5d3728ed5398d055ddc28c9e5959b2001 | |
parent | ccc08dc2189aba7fc4a0eb42e122bc65d100d69a (diff) | |
download | chromium_src-9f7f7451433f7912e18070df23c5ea4e1623af93.zip chromium_src-9f7f7451433f7912e18070df23c5ea4e1623af93.tar.gz chromium_src-9f7f7451433f7912e18070df23c5ea4e1623af93.tar.bz2 |
Adds a small icon to notifications, and connects it to synced notifications.
The small icon is a 16x16 image in the bottom right of all notification
templates. Currently it is not exposed to the JS API, but instead is used
only by internal API systems (starting with synced notifications.)
BUG=284592
Review URL: https://codereview.chromium.org/149433005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@247875 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/notifications/notification.h | 3 | ||||
-rw-r--r-- | chrome/browser/notifications/sync_notifier/synced_notification.cc | 31 | ||||
-rw-r--r-- | chrome/browser/notifications/sync_notifier/synced_notification_unittest.cc | 4 | ||||
-rw-r--r-- | ui/message_center/cocoa/notification_controller.h | 4 | ||||
-rw-r--r-- | ui/message_center/cocoa/notification_controller.mm | 41 | ||||
-rw-r--r-- | ui/message_center/cocoa/notification_controller_unittest.mm | 15 | ||||
-rw-r--r-- | ui/message_center/message_center_style.h | 2 | ||||
-rw-r--r-- | ui/message_center/notification.cc | 1 | ||||
-rw-r--r-- | ui/message_center/notification.h | 6 | ||||
-rw-r--r-- | ui/message_center/views/message_view.cc | 28 | ||||
-rw-r--r-- | ui/message_center/views/message_view.h | 4 | ||||
-rw-r--r-- | ui/message_center/views/notification_view.cc | 2 |
12 files changed, 127 insertions, 14 deletions
diff --git a/chrome/browser/notifications/notification.h b/chrome/browser/notifications/notification.h index 1d13c40..70a0ac9 100644 --- a/chrome/browser/notifications/notification.h +++ b/chrome/browser/notifications/notification.h @@ -125,6 +125,9 @@ class Notification : public message_center::Notification { // The URL of a large image to be displayed for a a rich notification. GURL image_url_; + // The URL of a small image to be displayed for a a rich notification. + GURL small_image_url_; + // The user-supplied replace ID for the notification. base::string16 replace_id_; diff --git a/chrome/browser/notifications/sync_notifier/synced_notification.cc b/chrome/browser/notifications/sync_notifier/synced_notification.cc index 266d198..99a6f2a 100644 --- a/chrome/browser/notifications/sync_notifier/synced_notification.cc +++ b/chrome/browser/notifications/sync_notifier/synced_notification.cc @@ -6,6 +6,7 @@ #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" @@ -15,9 +16,16 @@ #include "chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h" #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h" #include "content/public/browser/browser_thread.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/message_center_util.h" #include "ui/message_center/notification_types.h" @@ -304,6 +312,28 @@ void SyncedNotification::Show(NotificationUIManager* notification_manager, 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; @@ -326,7 +356,6 @@ void SyncedNotification::Show(NotificationUIManager* notification_manager, 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_); diff --git a/chrome/browser/notifications/sync_notifier/synced_notification_unittest.cc b/chrome/browser/notifications/sync_notifier/synced_notification_unittest.cc index cbb5c61..6dd0990 100644 --- a/chrome/browser/notifications/sync_notifier/synced_notification_unittest.cc +++ b/chrome/browser/notifications/sync_notifier/synced_notification_unittest.cc @@ -330,6 +330,10 @@ TEST_F(SyncedNotificationTest, OnFetchCompleteTest) { notification1_->OnFetchComplete(GURL(kButtonTwoIconUrl), &bitmap); + // Expect that the app icon has some data in it. + EXPECT_FALSE(notification1_->GetAppIcon().IsEmpty()); + EXPECT_FALSE(notification_manager()->notification().small_image().IsEmpty()); + // Since we check Show() thoroughly in its own test, we only check cursorily. EXPECT_EQ(message_center::NOTIFICATION_TYPE_IMAGE, notification_manager()->notification().type()); diff --git a/ui/message_center/cocoa/notification_controller.h b/ui/message_center/cocoa/notification_controller.h index 47f1e87..61a5e2f 100644 --- a/ui/message_center/cocoa/notification_controller.h +++ b/ui/message_center/cocoa/notification_controller.h @@ -37,6 +37,9 @@ MESSAGE_CENTER_EXPORT // The button that invokes |-close:|, in the upper-right corner. base::scoped_nsobject<HoverImageButton> closeButton_; + // The small icon associated with the notification, on the bottom right. + base::scoped_nsobject<NSImageView> smallImage_; + // The large icon associated with the notification, on the left side. base::scoped_nsobject<NSImageView> icon_; @@ -84,6 +87,7 @@ MESSAGE_CENTER_EXPORT @end @interface MCNotificationController (TestingInterface) +- (NSImageView*)smallImageView; - (NSImageView*)iconView; @end diff --git a/ui/message_center/cocoa/notification_controller.mm b/ui/message_center/cocoa/notification_controller.mm index ade983f..367d957 100644 --- a/ui/message_center/cocoa/notification_controller.mm +++ b/ui/message_center/cocoa/notification_controller.mm @@ -200,11 +200,14 @@ // Creates a box that shows a border when the icon is not big enough to fill the // space. -- (NSBox*)createImageBox; +- (NSBox*)createImageBox:(const gfx::Image&)notificationImage; // Initializes the closeButton_ ivar with the configured button. - (void)configureCloseButtonInFrame:(NSRect)rootFrame; +// Initializes the smallImage_ ivar with the appropriate frame. +- (void)configureSmallImageInFrame:(NSRect)rootFrame; + // Initializes title_ in the given frame. - (void)configureTitleInFrame:(NSRect)rootFrame; @@ -229,7 +232,7 @@ // it is too long. - (base::string16)wrapText:(const base::string16&)text forFont:(NSFont*)font - maxNumberOfLines:(size_t)lines; + maxNumberOfLines:(size_t)lines; @end //////////////////////////////////////////////////////////////////////////////// @@ -264,6 +267,9 @@ [self configureCloseButtonInFrame:rootFrame]; [rootView addSubview:closeButton_]; + [self configureSmallImageInFrame:rootFrame]; + [[self view] addSubview:smallImage_]; + // Create the title. [self configureTitleInFrame:rootFrame]; [rootView addSubview:title_]; @@ -288,6 +294,8 @@ message_center::kNotificationPreferredImageWidth, message_center::kNotificationIconSize); + [smallImage_ setImage:notification_->small_image().AsNSImage()]; + // Update the icon. [icon_ setImage:notification_->icon().AsNSImage()]; @@ -607,7 +615,7 @@ return imageBox.autorelease(); } -- (NSBox*)createImageBox:(gfx::Image)notificationImage { +- (NSBox*)createImageBox:(const gfx::Image&)notificationImage { using message_center::kNotificationImageBorderSize; using message_center::kNotificationPreferredImageWidth; using message_center::kNotificationPreferredImageHeight; @@ -643,11 +651,15 @@ } - (void)configureCloseButtonInFrame:(NSRect)rootFrame { - closeButton_.reset([[HoverImageButton alloc] initWithFrame:NSMakeRect( - NSMaxX(rootFrame) - message_center::kControlButtonSize, - NSMaxY(rootFrame) - message_center::kControlButtonSize, - message_center::kControlButtonSize, - message_center::kControlButtonSize)]); + // The close button is configured to be the same size as the small image. + int closeButtonOriginOffset = + message_center::kSmallImageSize + message_center::kSmallImagePadding; + NSRect closeButtonFrame = + NSMakeRect(NSMaxX(rootFrame) - closeButtonOriginOffset, + NSMaxY(rootFrame) - closeButtonOriginOffset, + message_center::kSmallImageSize, + message_center::kSmallImageSize); + closeButton_.reset([[HoverImageButton alloc] initWithFrame:closeButtonFrame]); ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); [closeButton_ setDefaultImage: rb.GetNativeImageNamed(IDR_NOTIFICATION_CLOSE).ToNSImage()]; @@ -670,6 +682,19 @@ forAttribute:NSAccessibilityTitleAttribute]; } +- (void)configureSmallImageInFrame:(NSRect)rootFrame { + int smallImageXOffset = + message_center::kSmallImagePadding + message_center::kSmallImageSize; + NSRect smallImageFrame = + NSMakeRect(NSMaxX(rootFrame) - smallImageXOffset, + NSMinY(rootFrame) + message_center::kSmallImagePadding, + message_center::kSmallImageSize, + message_center::kSmallImageSize); + smallImage_.reset([[NSImageView alloc] initWithFrame:smallImageFrame]); + [smallImage_ setImageScaling:NSImageScaleProportionallyUpOrDown]; + [smallImage_ setAutoresizingMask:NSViewMinYMargin]; +} + - (void)configureTitleInFrame:(NSRect)rootFrame { NSRect frame = [self currentContentRect]; frame.size.height = 0; diff --git a/ui/message_center/cocoa/notification_controller_unittest.mm b/ui/message_center/cocoa/notification_controller_unittest.mm index 59046aa..e01745a 100644 --- a/ui/message_center/cocoa/notification_controller_unittest.mm +++ b/ui/message_center/cocoa/notification_controller_unittest.mm @@ -65,6 +65,10 @@ class MockMessageCenter : public message_center::FakeMessageCenter { return closeButton_.get(); } +- (NSImageView*)smallImageView { + return smallImage_.get(); +} + - (NSButton*)secondButton { // The buttons are in Cocoa-y-order, so the 2nd button is first. NSView* view = [[bottomView_ subviews] objectAtIndex:0]; @@ -122,7 +126,9 @@ TEST_F(NotificationControllerTest, BasicLayout) { DummyNotifierId(), message_center::RichNotificationData(), NULL)); - notification->set_icon(gfx::Image([TestIcon() retain])); + gfx::Image testIcon([TestIcon() retain]); + notification->set_icon(testIcon); + notification->set_small_image(testIcon); base::scoped_nsobject<MCNotificationController> controller( [[MCNotificationController alloc] initWithNotification:notification.get() @@ -130,6 +136,7 @@ TEST_F(NotificationControllerTest, BasicLayout) { [controller view]; EXPECT_EQ(TestIcon(), [[controller iconView] image]); + EXPECT_EQ(TestIcon(), [[controller smallImageView] image]); EXPECT_EQ(base::SysNSStringToUTF16([[controller titleView] string]), notification->title()); EXPECT_EQ(base::SysNSStringToUTF16([[controller messageView] string]), @@ -209,11 +216,15 @@ TEST_F(NotificationControllerTest, Update) { EXPECT_EQ(NSHeight([[controller view] frame]), message_center::kNotificationIconSize); EXPECT_FALSE([[controller iconView] image]); + EXPECT_FALSE([[controller smallImageView] image]); // Update the icon. - notification->set_icon(gfx::Image([TestIcon() retain])); + gfx::Image testIcon([TestIcon() retain]); + notification->set_icon(testIcon); + notification->set_small_image(testIcon); [controller updateNotification:notification.get()]; EXPECT_EQ(TestIcon(), [[controller iconView] image]); + EXPECT_EQ(TestIcon(), [[controller smallImageView] image]); EXPECT_EQ(NSHeight([[controller view] frame]), message_center::kNotificationIconSize); } diff --git a/ui/message_center/message_center_style.h b/ui/message_center/message_center_style.h index 7aeadde..170d489 100644 --- a/ui/message_center/message_center_style.h +++ b/ui/message_center/message_center_style.h @@ -22,6 +22,8 @@ const int kNotificationImageBorderSize = 10; const int kNotificationPreferredImageWidth = 360; const int kNotificationPreferredImageHeight = 240; const int kSettingsIconSize = 16; +const int kSmallImageSize = 16; +const int kSmallImagePadding = 4; // Limits. const size_t kMaxVisibleMessageCenterNotifications = 100; diff --git a/ui/message_center/notification.cc b/ui/message_center/notification.cc index f18472c..fe16ec9 100644 --- a/ui/message_center/notification.cc +++ b/ui/message_center/notification.cc @@ -39,6 +39,7 @@ RichNotificationData::RichNotificationData(const RichNotificationData& other) expanded_message(other.expanded_message), context_message(other.context_message), image(other.image), + small_image(other.small_image), items(other.items), progress(other.progress), buttons(other.buttons), diff --git a/ui/message_center/notification.h b/ui/message_center/notification.h index 2e151b3..ded9424 100644 --- a/ui/message_center/notification.h +++ b/ui/message_center/notification.h @@ -45,6 +45,7 @@ class MESSAGE_CENTER_EXPORT RichNotificationData { base::string16 expanded_message; base::string16 context_message; gfx::Image image; + gfx::Image small_image; std::vector<NotificationItem> items; int progress; std::vector<ButtonInfo> buttons; @@ -133,6 +134,11 @@ class MESSAGE_CENTER_EXPORT Notification { const gfx::Image& image() const { return optional_fields_.image; } void set_image(const gfx::Image& image) { optional_fields_.image = image; } + const gfx::Image& small_image() const { return optional_fields_.small_image; } + void set_small_image(const gfx::Image& image) { + optional_fields_.small_image = image; + } + // Buttons, with icons fetched asynchronously. const std::vector<ButtonInfo>& buttons() const { return optional_fields_.buttons; diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc index 8657aec..8b59a7d 100644 --- a/ui/message_center/views/message_view.cc +++ b/ui/message_center/views/message_view.cc @@ -18,6 +18,7 @@ #include "ui/message_center/views/padded_button.h" #include "ui/views/background.h" #include "ui/views/controls/button/image_button.h" +#include "ui/views/controls/image_view.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/painter.h" @@ -38,6 +39,7 @@ namespace message_center { MessageView::MessageView(MessageViewController* controller, const std::string& notification_id, const NotifierId& notifier_id, + const gfx::ImageSkia& small_image, const base::string16& display_source) : controller_(controller), notification_id_(notification_id), @@ -53,6 +55,14 @@ MessageView::MessageView(MessageViewController* controller, views::Background::CreateSolidBackground(kNotificationBackgroundColor)); AddChildView(background_view_); + views::ImageView* small_image_view = new views::ImageView(); + small_image_view->SetImage(small_image); + small_image_view->SetImageSize(gfx::Size(kSmallImageSize, kSmallImageSize)); + // The small image view should be added to view hierarchy by the derived + // class. This ensures that it is on top of other views. + small_image_view->set_owned_by_client(); + small_image_view_.reset(small_image_view); + PaddedButton *close = new PaddedButton(this); close->SetPadding(-kCloseIconRightPadding, kCloseIconTopPadding); close->SetNormalImage(IDR_NOTIFICATION_CLOSE); @@ -139,6 +149,8 @@ bool MessageView::OnKeyReleased(const ui::KeyEvent& event) { } void MessageView::OnPaint(gfx::Canvas* canvas) { + DCHECK_EQ(this, close_button_->parent()); + DCHECK_EQ(this, small_image_view_->parent()); SlideOutView::OnPaint(canvas); views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); } @@ -163,9 +175,19 @@ void MessageView::Layout() { // Close button. gfx::Size close_size(close_button_->GetPreferredSize()); - close_button_->SetBounds( - content_bounds.right() - close_size.width(), content_bounds.y(), - close_size.width(), close_size.height()); + gfx::Rect close_rect(content_bounds.right() - close_size.width(), + content_bounds.y(), + close_size.width(), + close_size.height()); + close_button_->SetBoundsRect(close_rect); + + gfx::Size small_image_size(small_image_view_->GetPreferredSize()); + gfx::Rect small_image_rect(small_image_size); + small_image_rect.set_origin(gfx::Point( + content_bounds.right() - small_image_size.width() - kSmallImagePadding, + content_bounds.bottom() - small_image_size.height() - + kSmallImagePadding)); + small_image_view_->SetBoundsRect(small_image_rect); } void MessageView::OnGestureEvent(ui::GestureEvent* event) { diff --git a/ui/message_center/views/message_view.h b/ui/message_center/views/message_view.h index a10a705..10e0911 100644 --- a/ui/message_center/views/message_view.h +++ b/ui/message_center/views/message_view.h @@ -19,6 +19,7 @@ class MenuModel; namespace views { class ImageButton; +class ImageView; class Painter; class ScrollView; } @@ -48,6 +49,7 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, MessageView(MessageViewController* controller, const std::string& notification_id, const NotifierId& notifier_id, + const gfx::ImageSkia& small_image, const base::string16& display_source); virtual ~MessageView(); @@ -90,6 +92,7 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, // Overridden from views::SlideOutView: virtual void OnSlideOut() OVERRIDE; + views::ImageView* small_image() { return small_image_view_.get(); } views::ImageButton* close_button() { return close_button_.get(); } views::ScrollView* scroller() { return scroller_; } @@ -99,6 +102,7 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView, NotifierId notifier_id_; views::View* background_view_; // Owned by views hierarchy. scoped_ptr<views::ImageButton> close_button_; + scoped_ptr<views::ImageView> small_image_view_; views::ScrollView* scroller_; base::string16 accessible_name_; diff --git a/ui/message_center/views/notification_view.cc b/ui/message_center/views/notification_view.cc index 7b258ab..028d9d3 100644 --- a/ui/message_center/views/notification_view.cc +++ b/ui/message_center/views/notification_view.cc @@ -305,6 +305,7 @@ NotificationView::NotificationView(MessageCenterController* controller, : MessageView(this, notification.id(), notification.notifier_id(), + notification.small_image().AsImageSkia(), notification.display_source()), controller_(controller), clickable_(notification.clickable()), @@ -465,6 +466,7 @@ NotificationView::NotificationView(MessageCenterController* controller, AddChildView(top_view_); AddChildView(icon_view_); AddChildView(bottom_view_); + AddChildView(small_image()); AddChildView(close_button()); AddChildView(expand_button_); set_accessible_name(JoinString(accessible_lines, '\n')); |