Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(143)

Side by Side Diff: ui/arc/notification/arc_notification_item.cc

Issue 2845003002: Merge ArcNotificationItem and ArcCustomNotificationItem (Closed)
Patch Set: Rebased Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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.
4
5 #include "ui/arc/notification/arc_notification_item.h"
6
7 #include <algorithm>
8 #include <utility>
9 #include <vector>
10
11 #include "base/memory/ptr_util.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/task_runner.h"
15 #include "base/task_scheduler/post_task.h"
16 #include "third_party/skia/include/core/SkCanvas.h"
17 #include "third_party/skia/include/core/SkPaint.h"
18 #include "ui/gfx/codec/png_codec.h"
19 #include "ui/gfx/geometry/size.h"
20 #include "ui/gfx/image/image.h"
21 #include "ui/gfx/text_elider.h"
22 #include "ui/message_center/message_center_style.h"
23 #include "ui/message_center/notification.h"
24 #include "ui/message_center/notification_types.h"
25 #include "ui/message_center/notifier_settings.h"
26
27 namespace arc {
28
29 namespace {
30
31 constexpr char kNotifierId[] = "ARC_NOTIFICATION";
32 constexpr char kNotificationIdPrefix[] = "ARC_NOTIFICATION_";
33
34 SkBitmap DecodeImage(const std::vector<uint8_t>& data) {
35 DCHECK(!data.empty()); // empty string should be handled in caller.
36
37 // We may decode an image in the browser process since it has been generated
38 // in NotificationListerService in Android and should be safe.
39 SkBitmap bitmap;
40 gfx::PNGCodec::Decode(&data[0], data.size(), &bitmap);
41 return bitmap;
42 }
43
44 // Crops the image to proper size for Chrome Notification. It accepts only
45 // specified aspect ratio. Otherwise, it might be letterboxed.
46 SkBitmap CropImage(const SkBitmap& original_bitmap) {
47 DCHECK_NE(0, original_bitmap.width());
48 DCHECK_NE(0, original_bitmap.height());
49
50 const SkSize container_size = SkSize::Make(
51 message_center::kNotificationPreferredImageWidth,
52 message_center::kNotificationPreferredImageHeight);
53 const float container_aspect_ratio =
54 static_cast<float>(message_center::kNotificationPreferredImageWidth) /
55 message_center::kNotificationPreferredImageHeight;
56 const float image_aspect_ratio =
57 static_cast<float>(original_bitmap.width()) / original_bitmap.height();
58
59 SkRect source_rect;
60 if (image_aspect_ratio > container_aspect_ratio) {
61 float width = original_bitmap.height() * container_aspect_ratio;
62 source_rect = SkRect::MakeXYWH((original_bitmap.width() - width) / 2,
63 0,
64 width,
65 original_bitmap.height());
66 } else {
67 float height = original_bitmap.width() / container_aspect_ratio;
68 source_rect = SkRect::MakeXYWH(0,
69 (original_bitmap.height() - height) / 2,
70 original_bitmap.width(),
71 height);
72 }
73
74 SkBitmap container_bitmap;
75 container_bitmap.allocN32Pixels(container_size.width(),
76 container_size.height());
77 SkPaint paint;
78 paint.setFilterQuality(kHigh_SkFilterQuality);
79 SkCanvas container_image(container_bitmap);
80 container_image.drawColor(message_center::kImageBackgroundColor);
81 container_image.drawBitmapRect(
82 original_bitmap, source_rect, SkRect::MakeSize(container_size), &paint);
83
84 return container_bitmap;
85 }
86
87 class ArcNotificationDelegate : public message_center::NotificationDelegate {
88 public:
89 explicit ArcNotificationDelegate(base::WeakPtr<ArcNotificationItem> item)
90 : item_(item) {}
91
92 void Close(bool by_user) override {
93 if (item_)
94 item_->Close(by_user);
95 }
96
97 // Indicates all notifications have a click handler. This changes the mouse
98 // cursor on hover.
99 // TODO(yoshiki): Return the correct value according to the content intent
100 // and the flags.
101 bool HasClickedListener() override { return true; }
102
103 void Click() override {
104 if (item_)
105 item_->Click();
106 }
107
108 void ButtonClick(int button_index) override {
109 if (item_)
110 item_->ButtonClick(button_index);
111 }
112
113 private:
114 // The destructor is private since this class is ref-counted.
115 ~ArcNotificationDelegate() override {}
116
117 base::WeakPtr<ArcNotificationItem> item_;
118
119 DISALLOW_COPY_AND_ASSIGN(ArcNotificationDelegate);
120 };
121
122 } // anonymous namespace
123
124 ArcNotificationItem::ArcNotificationItem(
125 ArcNotificationManager* manager,
126 message_center::MessageCenter* message_center,
127 const std::string& notification_key,
128 const AccountId& profile_id)
129 : manager_(manager),
130 message_center_(message_center),
131 profile_id_(profile_id),
132 notification_key_(notification_key),
133 notification_id_(kNotificationIdPrefix + notification_key_),
134 weak_ptr_factory_(this) {}
135
136 void ArcNotificationItem::UpdateWithArcNotificationData(
137 mojom::ArcNotificationDataPtr data) {
138 DCHECK(thread_checker_.CalledOnValidThread());
139 DCHECK(notification_key_ == data->key);
140
141 // Check if a decode task is on-going or not. If |notification_| is non-null,
142 // a decode task is on-going asynchronously. Otherwise, there is no task and
143 // cache the latest data to the |newer_data_| property.
144 // TODO(yoshiki): Refactor and remove this check by omitting image decoding
145 // from here.
146 if (HasPendingNotification()) {
147 CacheArcNotificationData(std::move(data));
148 return;
149 }
150
151 message_center::RichNotificationData rich_data;
152 message_center::NotificationType type;
153
154 switch (data->type) {
155 case mojom::ArcNotificationType::BASIC:
156 type = message_center::NOTIFICATION_TYPE_BASE_FORMAT;
157 break;
158 case mojom::ArcNotificationType::LIST:
159 type = message_center::NOTIFICATION_TYPE_MULTIPLE;
160
161 if (!data->texts.has_value())
162 break;
163
164 for (size_t i = 0;
165 i < std::min(data->texts->size(),
166 message_center::kNotificationMaximumItems - 1);
167 i++) {
168 rich_data.items.emplace_back(base::string16(),
169 base::UTF8ToUTF16(data->texts->at(i)));
170 }
171
172 if (data->texts->size() > message_center::kNotificationMaximumItems) {
173 // Show an elipsis as the 5th item if there are more than 5 items.
174 rich_data.items.emplace_back(base::string16(), gfx::kEllipsisUTF16);
175 } else if (data->texts->size() ==
176 message_center::kNotificationMaximumItems) {
177 // Show the 5th item if there are exact 5 items.
178 rich_data.items.emplace_back(
179 base::string16(),
180 base::UTF8ToUTF16(data->texts->at(data->texts->size() - 1)));
181 }
182 break;
183 case mojom::ArcNotificationType::IMAGE:
184 type = message_center::NOTIFICATION_TYPE_IMAGE;
185
186 if (data->big_picture && !data->big_picture->isNull()) {
187 rich_data.image = gfx::Image::CreateFrom1xBitmap(
188 CropImage(*data->big_picture));
189 }
190 break;
191 case mojom::ArcNotificationType::PROGRESS:
192 type = message_center::NOTIFICATION_TYPE_PROGRESS;
193 rich_data.timestamp = base::Time::UnixEpoch() +
194 base::TimeDelta::FromMilliseconds(data->time);
195 rich_data.progress = std::max(
196 0, std::min(100, static_cast<int>(std::round(
197 static_cast<float>(data->progress_current) /
198 data->progress_max * 100))));
199 break;
200 }
201 DCHECK(IsKnownEnumValue(data->type)) << "Unsupported notification type: "
202 << data->type;
203
204 for (size_t i = 0; i < data->buttons.size(); i++) {
205 rich_data.buttons.emplace_back(
206 base::UTF8ToUTF16(data->buttons.at(i)->label));
207 }
208
209 // If the client is old (version < 1), both |no_clear| and |ongoing_event|
210 // are false.
211 rich_data.pinned = (data->no_clear || data->ongoing_event);
212
213 rich_data.priority = ConvertAndroidPriority(data->priority);
214 if (data->small_icon)
215 rich_data.small_image = gfx::Image::CreateFrom1xBitmap(*data->small_icon);
216
217 // The identifier of the notifier, which is used to distinguish the notifiers
218 // in the message center.
219 message_center::NotifierId notifier_id(
220 message_center::NotifierId::SYSTEM_COMPONENT, kNotifierId);
221 notifier_id.profile_id = profile_id_.GetUserEmail();
222
223 SetNotification(base::MakeUnique<message_center::Notification>(
224 type, notification_id_, base::UTF8ToUTF16(data->title),
225 base::UTF8ToUTF16(data->message),
226 gfx::Image(), // icon image: Will be overriden later.
227 base::UTF8ToUTF16("arc"), // display source
228 GURL(), // empty origin url, for system component
229 notifier_id, rich_data,
230 new ArcNotificationDelegate(weak_ptr_factory_.GetWeakPtr())));
231
232 if (data->icon_data.size() == 0) {
233 OnImageDecoded(SkBitmap()); // Pass an empty bitmap.
234 return;
235 }
236
237 // TODO(yoshiki): Remove decoding by passing a bitmap directly from Android.
238 base::PostTaskWithTraitsAndReplyWithResult(
239 FROM_HERE, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
240 base::Bind(&DecodeImage, data->icon_data),
241 base::Bind(&ArcNotificationItem::OnImageDecoded,
242 weak_ptr_factory_.GetWeakPtr()));
243 }
244
245 ArcNotificationItem::~ArcNotificationItem() {}
246
247 void ArcNotificationItem::OnClosedFromAndroid() {
248 being_removed_by_manager_ = true; // Closing is initiated by the manager.
249 message_center_->RemoveNotification(notification_id_, false /* by_user */);
250 }
251
252 void ArcNotificationItem::Close(bool by_user) {
253 if (being_removed_by_manager_) {
254 // Closing is caused by the manager, so we don't need to nofify a close
255 // event to the manager.
256 return;
257 }
258
259 // Do not touch its any members afterwards, because this instance will be
260 // destroyed in the following call
261 manager_->SendNotificationRemovedFromChrome(notification_key_);
262 }
263
264 void ArcNotificationItem::Click() {
265 manager_->SendNotificationClickedOnChrome(notification_key_);
266 }
267
268 void ArcNotificationItem::ButtonClick(int button_index) {
269 manager_->SendNotificationButtonClickedOnChrome(
270 notification_key_, button_index);
271 }
272
273 void ArcNotificationItem::OpenSettings() {
274 manager_->OpenNotificationSettings(notification_key_);
275 }
276
277 bool ArcNotificationItem::IsOpeningSettingsSupported() const {
278 return manager_->IsOpeningSettingsSupported();
279 }
280
281 void ArcNotificationItem::ToggleExpansion() {
282 manager_->SendNotificationToggleExpansionOnChrome(notification_key_);
283 }
284
285 // Converts from Android notification priority to Chrome notification priority.
286 // On Android, PRIORITY_DEFAULT does not pop up, so this maps PRIORITY_DEFAULT
287 // to Chrome's -1 to adapt that behavior. Also, this maps PRIORITY_LOW and _HIGH
288 // to -2 and 0 respectively to adjust the value with keeping the order among
289 // _LOW, _DEFAULT and _HIGH.
290 // static
291 // TODO(yoshiki): rewrite this conversion as typemap
292 int ArcNotificationItem::ConvertAndroidPriority(
293 mojom::ArcNotificationPriority android_priority) {
294 switch (android_priority) {
295 case mojom::ArcNotificationPriority::MIN:
296 case mojom::ArcNotificationPriority::LOW:
297 return message_center::MIN_PRIORITY;
298 case mojom::ArcNotificationPriority::DEFAULT:
299 return message_center::LOW_PRIORITY;
300 case mojom::ArcNotificationPriority::HIGH:
301 return message_center::DEFAULT_PRIORITY;
302 case mojom::ArcNotificationPriority::MAX:
303 return message_center::MAX_PRIORITY;
304
305 // fall-through
306 default:
307 NOTREACHED() << "Invalid Priority: " << android_priority;
308 return message_center::DEFAULT_PRIORITY;
309 }
310 }
311
312 bool ArcNotificationItem::HasPendingNotification() {
313 return (notification_ != nullptr);
314 }
315
316 void ArcNotificationItem::CacheArcNotificationData(
317 mojom::ArcNotificationDataPtr data) {
318 // If old |newer_data_| has been stored, discard the old one.
319 newer_data_ = std::move(data);
320 }
321
322 void ArcNotificationItem::SetNotification(
323 std::unique_ptr<message_center::Notification> notification) {
324 notification_ = std::move(notification);
325 }
326
327 void ArcNotificationItem::AddToMessageCenter() {
328 DCHECK(notification_);
329 message_center_->AddNotification(std::move(notification_));
330
331 if (newer_data_) {
332 // There is the newer data, so updates again.
333 UpdateWithArcNotificationData(std::move(newer_data_));
334 }
335 }
336
337 bool ArcNotificationItem::CalledOnValidThread() const {
338 return thread_checker_.CalledOnValidThread();
339 }
340
341 void ArcNotificationItem::OnImageDecoded(const SkBitmap& bitmap) {
342 DCHECK(thread_checker_.CalledOnValidThread());
343
344 gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmap);
345 notification_->set_icon(image);
346 AddToMessageCenter();
347 }
348
349 } // namespace arc
OLDNEW
« no previous file with comments | « ui/arc/notification/arc_notification_item.h ('k') | ui/arc/notification/arc_notification_item_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698