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"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/stl_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/common/extensions/api/notification_provider.h"
15 #include "chrome/common/extensions/api/notifications/notification_style.h"
16 #include "ui/gfx/image/image_skia.h"
17 #include "ui/gfx/image/image_skia_rep.h"
18 #include "ui/gfx/skia_util.h"
20 void NotificationConversionHelper::NotificationToNotificationOptions(
21 const Notification
& notification
,
22 extensions::api::notifications::NotificationOptions
* options
) {
23 // Extract required fields: type, title, message, and icon.
24 std::string type
= MapTypeToString(notification
.type());
25 options
->type
= extensions::api::notifications::ParseTemplateType(type
);
27 if (!notification
.icon().IsEmpty()) {
28 scoped_ptr
<extensions::api::notifications::NotificationBitmap
> icon(
29 new extensions::api::notifications::NotificationBitmap());
30 GfxImageToNotificationBitmap(¬ification
.icon(), icon
.get());
31 options
->icon_bitmap
= icon
.Pass();
35 new std::string(base::UTF16ToUTF8(notification
.title())));
36 options
->message
.reset(
37 new std::string(base::UTF16ToUTF8(notification
.message())));
39 // Handle optional data provided.
40 const message_center::RichNotificationData
* rich_data
=
41 ¬ification
.rich_notification_data();
43 if (!rich_data
->small_image
.IsEmpty()) {
44 scoped_ptr
<extensions::api::notifications::NotificationBitmap
> icon_mask(
45 new extensions::api::notifications::NotificationBitmap());
46 GfxImageToNotificationBitmap(&rich_data
->small_image
, icon_mask
.get());
47 options
->app_icon_mask_bitmap
= icon_mask
.Pass();
50 options
->priority
.reset(new int(rich_data
->priority
));
52 options
->is_clickable
.reset(new bool(rich_data
->clickable
));
54 options
->event_time
.reset(new double(rich_data
->timestamp
.ToDoubleT()));
56 if (!rich_data
->context_message
.empty())
57 options
->context_message
.reset(
58 new std::string(base::UTF16ToUTF8(rich_data
->context_message
)));
60 if (!rich_data
->buttons
.empty()) {
61 scoped_ptr
<std::vector
<
62 linked_ptr
<extensions::api::notifications::NotificationButton
> > >
63 button_list(new std::vector
<
64 linked_ptr
<extensions::api::notifications::NotificationButton
> >);
65 for (size_t i
= 0; i
< rich_data
->buttons
.size(); i
++) {
66 linked_ptr
<extensions::api::notifications::NotificationButton
> button(
67 new extensions::api::notifications::NotificationButton
);
68 button
->title
= base::UTF16ToUTF8(rich_data
->buttons
[i
].title
);
70 if (!rich_data
->buttons
[i
].icon
.IsEmpty()) {
71 scoped_ptr
<extensions::api::notifications::NotificationBitmap
> icon(
72 new extensions::api::notifications::NotificationBitmap());
73 GfxImageToNotificationBitmap(&rich_data
->buttons
[i
].icon
, icon
.get());
74 button
->icon_bitmap
= icon
.Pass();
76 button_list
->push_back(button
);
78 options
->buttons
= button_list
.Pass();
81 // Only image type notifications should have images.
82 if (type
== "image" && !rich_data
->image
.IsEmpty()) {
83 scoped_ptr
<extensions::api::notifications::NotificationBitmap
> image(
84 new extensions::api::notifications::NotificationBitmap());
85 GfxImageToNotificationBitmap(¬ification
.image(), image
.get());
86 options
->image_bitmap
= image
.Pass();
87 } else if (type
!= "image" && !rich_data
->image
.IsEmpty()) {
88 DVLOG(1) << "Only image type notifications should have images.";
91 // Only progress type notifications should have progress bars.
92 if (type
== "progress")
93 options
->progress
.reset(new int(rich_data
->progress
));
94 else if (rich_data
->progress
!= 0)
95 DVLOG(1) << "Only progress type notifications should have progress.";
97 // Only list type notifications should have lists.
98 if (type
== "list" && !rich_data
->items
.empty()) {
99 scoped_ptr
<std::vector
<
100 linked_ptr
<extensions::api::notifications::NotificationItem
> > >
101 list(new std::vector
<
102 linked_ptr
<extensions::api::notifications::NotificationItem
> >);
103 for (size_t j
= 0; j
< rich_data
->items
.size(); j
++) {
104 linked_ptr
<extensions::api::notifications::NotificationItem
> item(
105 new extensions::api::notifications::NotificationItem
);
106 item
->title
= base::UTF16ToUTF8(rich_data
->items
[j
].title
);
107 item
->message
= base::UTF16ToUTF8(rich_data
->items
[j
].message
);
108 list
->push_back(item
);
110 options
->items
= list
.Pass();
111 } else if (type
!= "list" && !rich_data
->items
.empty()) {
112 DVLOG(1) << "Only list type notifications should have lists.";
116 void NotificationConversionHelper::GfxImageToNotificationBitmap(
117 const gfx::Image
* gfx_image
,
118 extensions::api::notifications::NotificationBitmap
* notification_bitmap
) {
119 SkBitmap sk_bitmap
= gfx_image
->AsBitmap();
120 sk_bitmap
.lockPixels();
122 notification_bitmap
->width
= sk_bitmap
.width();
123 notification_bitmap
->height
= sk_bitmap
.height();
124 int pixel_count
= sk_bitmap
.width() * sk_bitmap
.height();
125 const int BYTES_PER_PIXEL
= 4;
127 uint32_t* bitmap_pixels
= sk_bitmap
.getAddr32(0, 0);
128 const unsigned char* bitmap
=
129 reinterpret_cast<const unsigned char*>(bitmap_pixels
);
130 scoped_ptr
<std::vector
<char>> rgba_bitmap_data(
131 new std::vector
<char>(pixel_count
* BYTES_PER_PIXEL
));
133 gfx::ConvertSkiaToRGBA(bitmap
, pixel_count
,
134 reinterpret_cast<unsigned char*>(
135 vector_as_array(rgba_bitmap_data
.get())));
136 sk_bitmap
.unlockPixels();
138 notification_bitmap
->data
= rgba_bitmap_data
.Pass();
142 bool NotificationConversionHelper::NotificationBitmapToGfxImage(
144 const gfx::Size
& target_size_dips
,
145 extensions::api::notifications::NotificationBitmap
* notification_bitmap
,
146 gfx::Image
* return_image
) {
147 if (!notification_bitmap
)
150 const int max_device_pixel_width
= target_size_dips
.width() * max_scale
;
151 const int max_device_pixel_height
= target_size_dips
.height() * max_scale
;
153 const int BYTES_PER_PIXEL
= 4;
155 const int width
= notification_bitmap
->width
;
156 const int height
= notification_bitmap
->height
;
158 if (width
< 0 || height
< 0 || width
> max_device_pixel_width
||
159 height
> max_device_pixel_height
)
162 // Ensure we have rgba data.
163 std::vector
<char>* rgba_data
= notification_bitmap
->data
.get();
167 const size_t rgba_data_length
= rgba_data
->size();
168 const size_t rgba_area
= width
* height
;
170 if (rgba_data_length
!= rgba_area
* BYTES_PER_PIXEL
)
174 // Allocate the actual backing store with the sanitized dimensions.
175 if (!bitmap
.tryAllocN32Pixels(width
, height
))
178 // Ensure that our bitmap and our data now refer to the same number of pixels.
179 if (rgba_data_length
!= bitmap
.getSafeSize())
182 uint32_t* pixels
= bitmap
.getAddr32(0, 0);
183 const char* c_rgba_data
= rgba_data
->data();
185 for (size_t t
= 0; t
< rgba_area
; ++t
) {
186 // |c_rgba_data| is RGBA, pixels is ARGB.
187 size_t rgba_index
= t
* BYTES_PER_PIXEL
;
189 SkPreMultiplyColor(((c_rgba_data
[rgba_index
+ 3] & 0xFF) << 24) |
190 ((c_rgba_data
[rgba_index
+ 0] & 0xFF) << 16) |
191 ((c_rgba_data
[rgba_index
+ 1] & 0xFF) << 8) |
192 ((c_rgba_data
[rgba_index
+ 2] & 0xFF) << 0));
195 // TODO(dewittj): Handle HiDPI images with more than one scale factor
197 gfx::ImageSkia
skia(gfx::ImageSkiaRep(bitmap
, 1.0f
));
198 *return_image
= gfx::Image(skia
);
202 std::string
NotificationConversionHelper::MapTypeToString(
203 message_center::NotificationType type
) {
205 case message_center::NOTIFICATION_TYPE_BASE_FORMAT
:
207 case message_center::NOTIFICATION_TYPE_IMAGE
:
209 case message_center::NOTIFICATION_TYPE_MULTIPLE
:
211 case message_center::NOTIFICATION_TYPE_PROGRESS
: