1 // Copyright 2014 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/notification_conversion_helper.h"
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/common/extensions/api/notification_provider.h"
11 #include "chrome/common/extensions/api/notifications/notification_style.h"
12 #include "ui/gfx/image/image_skia.h"
13 #include "ui/gfx/image/image_skia_rep.h"
14 #include "ui/gfx/skia_util.h"
16 void NotificationConversionHelper::NotificationToNotificationOptions(
17 const Notification
& notification
,
18 extensions::api::notifications::NotificationOptions
* options
) {
19 // Extract required fields: type, title, message, and icon.
20 std::string type
= MapTypeToString(notification
.type());
21 options
->type
= extensions::api::notifications::ParseTemplateType(type
);
23 if (!notification
.icon().IsEmpty()) {
24 scoped_ptr
<extensions::api::notifications::NotificationBitmap
> icon(
25 new extensions::api::notifications::NotificationBitmap());
26 GfxImageToNotificationBitmap(¬ification
.icon(), icon
.get());
27 options
->icon_bitmap
= icon
.Pass();
31 new std::string(base::UTF16ToUTF8(notification
.title())));
32 options
->message
.reset(
33 new std::string(base::UTF16ToUTF8(notification
.message())));
35 // Handle optional data provided.
36 const message_center::RichNotificationData
* rich_data
=
37 ¬ification
.rich_notification_data();
39 if (!rich_data
->small_image
.IsEmpty()) {
40 scoped_ptr
<extensions::api::notifications::NotificationBitmap
> icon_mask(
41 new extensions::api::notifications::NotificationBitmap());
42 GfxImageToNotificationBitmap(&rich_data
->small_image
, icon_mask
.get());
43 options
->app_icon_mask_bitmap
= icon_mask
.Pass();
46 options
->priority
.reset(new int(rich_data
->priority
));
48 options
->is_clickable
.reset(new bool(rich_data
->clickable
));
50 options
->event_time
.reset(new double(rich_data
->timestamp
.ToDoubleT()));
52 if (!rich_data
->context_message
.empty())
53 options
->context_message
.reset(
54 new std::string(base::UTF16ToUTF8(rich_data
->context_message
)));
56 if (!rich_data
->buttons
.empty()) {
57 scoped_ptr
<std::vector
<
58 linked_ptr
<extensions::api::notifications::NotificationButton
> > >
59 button_list(new std::vector
<
60 linked_ptr
<extensions::api::notifications::NotificationButton
> >);
61 for (size_t i
= 0; i
< rich_data
->buttons
.size(); i
++) {
62 linked_ptr
<extensions::api::notifications::NotificationButton
> button(
63 new extensions::api::notifications::NotificationButton
);
64 button
->title
= base::UTF16ToUTF8(rich_data
->buttons
[i
].title
);
66 if (!rich_data
->buttons
[i
].icon
.IsEmpty()) {
67 scoped_ptr
<extensions::api::notifications::NotificationBitmap
> icon(
68 new extensions::api::notifications::NotificationBitmap());
69 GfxImageToNotificationBitmap(&rich_data
->buttons
[i
].icon
, icon
.get());
70 button
->icon_bitmap
= icon
.Pass();
72 button_list
->push_back(button
);
74 options
->buttons
= button_list
.Pass();
77 // Only image type notifications should have images.
78 if (type
== "image" && !rich_data
->image
.IsEmpty()) {
79 scoped_ptr
<extensions::api::notifications::NotificationBitmap
> image(
80 new extensions::api::notifications::NotificationBitmap());
81 GfxImageToNotificationBitmap(¬ification
.image(), image
.get());
82 options
->image_bitmap
= image
.Pass();
83 } else if (type
!= "image" && !rich_data
->image
.IsEmpty()) {
84 DVLOG(1) << "Only image type notifications should have images.";
87 // Only progress type notifications should have progress bars.
88 if (type
== "progress")
89 options
->progress
.reset(new int(rich_data
->progress
));
90 else if (rich_data
->progress
!= 0)
91 DVLOG(1) << "Only progress type notifications should have progress.";
93 // Only list type notifications should have lists.
94 if (type
== "list" && !rich_data
->items
.empty()) {
95 scoped_ptr
<std::vector
<
96 linked_ptr
<extensions::api::notifications::NotificationItem
> > >
98 linked_ptr
<extensions::api::notifications::NotificationItem
> >);
99 for (size_t j
= 0; j
< rich_data
->items
.size(); j
++) {
100 linked_ptr
<extensions::api::notifications::NotificationItem
> item(
101 new extensions::api::notifications::NotificationItem
);
102 item
->title
= base::UTF16ToUTF8(rich_data
->items
[j
].title
);
103 item
->message
= base::UTF16ToUTF8(rich_data
->items
[j
].message
);
104 list
->push_back(item
);
106 options
->items
= list
.Pass();
107 } else if (type
!= "list" && !rich_data
->items
.empty()) {
108 DVLOG(1) << "Only list type notifications should have lists.";
112 void NotificationConversionHelper::GfxImageToNotificationBitmap(
113 const gfx::Image
* gfx_image
,
114 extensions::api::notifications::NotificationBitmap
* notification_bitmap
) {
115 SkBitmap sk_bitmap
= gfx_image
->AsBitmap();
116 sk_bitmap
.lockPixels();
118 notification_bitmap
->width
= sk_bitmap
.width();
119 notification_bitmap
->height
= sk_bitmap
.height();
120 int pixel_count
= sk_bitmap
.width() * sk_bitmap
.height();
121 const int BYTES_PER_PIXEL
= 4;
123 uint32_t* bitmap_pixels
= sk_bitmap
.getAddr32(0, 0);
124 const unsigned char* bitmap
=
125 reinterpret_cast<const unsigned char*>(bitmap_pixels
);
126 scoped_ptr
<unsigned char[]> rgba_bitmap_data(
127 new unsigned char[pixel_count
* BYTES_PER_PIXEL
]);
129 gfx::ConvertSkiaToRGBA(bitmap
, pixel_count
, rgba_bitmap_data
.get());
130 sk_bitmap
.unlockPixels();
132 notification_bitmap
->data
.reset(new std::string(
133 rgba_bitmap_data
.get(),
134 (rgba_bitmap_data
.get() + pixel_count
* BYTES_PER_PIXEL
)));
138 bool NotificationConversionHelper::NotificationBitmapToGfxImage(
140 const gfx::Size
& target_size_dips
,
141 extensions::api::notifications::NotificationBitmap
* notification_bitmap
,
142 gfx::Image
* return_image
) {
143 if (!notification_bitmap
)
146 const int max_device_pixel_width
= target_size_dips
.width() * max_scale
;
147 const int max_device_pixel_height
= target_size_dips
.height() * max_scale
;
149 const int BYTES_PER_PIXEL
= 4;
151 const int width
= notification_bitmap
->width
;
152 const int height
= notification_bitmap
->height
;
154 if (width
< 0 || height
< 0 || width
> max_device_pixel_width
||
155 height
> max_device_pixel_height
)
158 // Ensure we have rgba data.
159 std::string
* rgba_data
= notification_bitmap
->data
.get();
163 const size_t rgba_data_length
= rgba_data
->length();
164 const size_t rgba_area
= width
* height
;
166 if (rgba_data_length
!= rgba_area
* BYTES_PER_PIXEL
)
170 // Allocate the actual backing store with the sanitized dimensions.
171 if (!bitmap
.allocN32Pixels(width
, height
))
174 // Ensure that our bitmap and our data now refer to the same number of pixels.
175 if (rgba_data_length
!= bitmap
.getSafeSize())
178 uint32_t* pixels
= bitmap
.getAddr32(0, 0);
179 const char* c_rgba_data
= rgba_data
->data();
181 for (size_t t
= 0; t
< rgba_area
; ++t
) {
182 // |c_rgba_data| is RGBA, pixels is ARGB.
183 size_t rgba_index
= t
* BYTES_PER_PIXEL
;
185 SkPreMultiplyColor(((c_rgba_data
[rgba_index
+ 3] & 0xFF) << 24) |
186 ((c_rgba_data
[rgba_index
+ 0] & 0xFF) << 16) |
187 ((c_rgba_data
[rgba_index
+ 1] & 0xFF) << 8) |
188 ((c_rgba_data
[rgba_index
+ 2] & 0xFF) << 0));
191 // TODO(dewittj): Handle HiDPI images with more than one scale factor
193 gfx::ImageSkia
skia(gfx::ImageSkiaRep(bitmap
, 1.0f
));
194 *return_image
= gfx::Image(skia
);
198 std::string
NotificationConversionHelper::MapTypeToString(
199 message_center::NotificationType type
) {
201 case message_center::NOTIFICATION_TYPE_BASE_FORMAT
:
203 case message_center::NOTIFICATION_TYPE_IMAGE
:
205 case message_center::NOTIFICATION_TYPE_MULTIPLE
:
207 case message_center::NOTIFICATION_TYPE_PROGRESS
: