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

Side by Side Diff: ash/common/system/chromeos/network/tray_sms.cc

Issue 2583393002: Send notification to users upon receiving sms messages (Closed)
Patch Set: update return type Created 3 years, 10 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 (c) 2012 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 "ash/common/system/chromeos/network/tray_sms.h"
6
7 #include <utility>
8
9 #include "ash/common/material_design/material_design_controller.h"
10 #include "ash/common/metrics/user_metrics_action.h"
11 #include "ash/common/system/tray/fixed_sized_scroll_view.h"
12 #include "ash/common/system/tray/system_tray.h"
13 #include "ash/common/system/tray/system_tray_bubble.h"
14 #include "ash/common/system/tray/tray_constants.h"
15 #include "ash/common/system/tray/tray_details_view.h"
16 #include "ash/common/system/tray/tray_item_more.h"
17 #include "ash/common/system/tray/tray_item_view.h"
18 #include "ash/common/system/tray/tray_notification_view.h"
19 #include "ash/common/wm_shell.h"
20 #include "ash/resources/vector_icons/vector_icons.h"
21 #include "base/memory/ptr_util.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "chromeos/network/network_event_log.h"
25 #include "chromeos/network/network_handler.h"
26 #include "grit/ash_resources.h"
27 #include "grit/ash_strings.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/base/resource/resource_bundle.h"
30 #include "ui/gfx/paint_vector_icon.h"
31 #include "ui/views/bubble/tray_bubble_view.h"
32 #include "ui/views/controls/image_view.h"
33 #include "ui/views/controls/label.h"
34 #include "ui/views/layout/box_layout.h"
35 #include "ui/views/layout/fill_layout.h"
36 #include "ui/views/layout/grid_layout.h"
37 #include "ui/views/view.h"
38
39 namespace {
40
41 // Min height of the list of messages in the popup.
42 const int kMessageListMinHeight = 200;
43 // Top/bottom padding of the text items.
44 const int kPaddingVertical = 10;
45
46 const char kSmsNumberKey[] = "number";
47 const char kSmsTextKey[] = "text";
48
49 bool GetMessageFromDictionary(const base::DictionaryValue* message,
50 std::string* number,
51 std::string* text) {
52 if (!message->GetStringWithoutPathExpansion(kSmsNumberKey, number))
53 return false;
54 if (!message->GetStringWithoutPathExpansion(kSmsTextKey, text))
55 return false;
56 return true;
57 }
58
59 } // namespace
60
61 namespace ash {
62
63 class TraySms::SmsDefaultView : public TrayItemMore {
64 public:
65 explicit SmsDefaultView(TraySms* owner) : TrayItemMore(owner, true) {
66 if (MaterialDesignController::UseMaterialDesignSystemIcons()) {
67 SetImage(gfx::CreateVectorIcon(kSystemMenuSmsIcon, kMenuIconSize,
68 kMenuIconColor));
69 } else {
70 SetImage(*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
71 IDR_AURA_UBER_TRAY_SMS));
72 }
73 Update();
74 }
75
76 ~SmsDefaultView() override {}
77
78 void Update() {
79 int message_count = static_cast<TraySms*>(owner())->messages().GetSize();
80 // TODO(jshin): Currently, a tabular format is used ("SMS Messages:
81 // <count>"). Check with UX if '<count> SMS messages' with a proper plural
82 // support is desired.
83 base::string16 label = l10n_util::GetStringFUTF16Int(
84 IDS_ASH_STATUS_TRAY_SMS_MESSAGES, message_count);
85 SetLabel(label);
86 SetAccessibleName(label);
87 }
88
89 private:
90 DISALLOW_COPY_AND_ASSIGN(SmsDefaultView);
91 };
92
93 // An entry (row) in SmsDetailedView or NotificationView.
94 class TraySms::SmsMessageView : public views::View,
95 public views::ButtonListener {
96 public:
97 enum ViewType { VIEW_DETAILED, VIEW_NOTIFICATION };
98
99 SmsMessageView(TraySms* owner,
100 ViewType view_type,
101 size_t index,
102 const std::string& number,
103 const std::string& message)
104 : owner_(owner), index_(index) {
105 // TODO(jshin): Convert ASCII digits in |number| (phone number) to native
106 // digits if necessary. |number| can contain non-digit characters and may
107 // have to be converted one-by-one or use libphonenumber's formating API.
108 number_label_ = new views::Label(
109 l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_SMS_NUMBER,
110 base::UTF8ToUTF16(number)),
111 ui::ResourceBundle::GetSharedInstance().GetFontList(
112 ui::ResourceBundle::BoldFont));
113 number_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
114
115 message_label_ = new views::Label(base::UTF8ToUTF16(message));
116 message_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
117 message_label_->SetMultiLine(true);
118
119 if (view_type == VIEW_DETAILED)
120 LayoutDetailedView();
121 else
122 LayoutNotificationView();
123 }
124
125 ~SmsMessageView() override {}
126
127 // Overridden from ButtonListener.
128 void ButtonPressed(views::Button* sender, const ui::Event& event) override {
129 if (owner_->RemoveMessage(index_)) {
130 WmShell::Get()->RecordUserMetricsAction(
131 UMA_STATUS_AREA_SMS_DETAILED_DISMISS_MSG);
132 }
133 owner_->Update(false);
134 }
135
136 private:
137 void LayoutDetailedView() {
138 views::ImageButton* close_button = new views::ImageButton(this);
139 close_button->SetImage(
140 views::CustomButton::STATE_NORMAL,
141 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
142 IDR_AURA_UBER_TRAY_SMS_DISMISS));
143 const int msg_width =
144 owner_->system_tray()
145 ->GetSystemBubble()
146 ->bubble_view()
147 ->GetPreferredSize()
148 .width() -
149 (kNotificationIconWidth + kTrayPopupPaddingHorizontal * 2);
150 message_label_->SizeToFit(msg_width);
151
152 views::GridLayout* layout = new views::GridLayout(this);
153 SetLayoutManager(layout);
154
155 views::ColumnSet* columns = layout->AddColumnSet(0);
156
157 // Message
158 columns->AddPaddingColumn(0, kTrayPopupPaddingHorizontal);
159 columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
160 0 /* resize percent */, views::GridLayout::FIXED,
161 msg_width, msg_width);
162
163 // Close button
164 columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
165 0, /* resize percent */
166 views::GridLayout::FIXED, kNotificationIconWidth,
167 kNotificationIconWidth);
168
169 layout->AddPaddingRow(0, kPaddingVertical);
170 layout->StartRow(0, 0);
171 layout->AddView(number_label_);
172 layout->AddView(close_button, 1, 2); // 2 rows for icon
173 layout->StartRow(0, 0);
174 layout->AddView(message_label_);
175
176 layout->AddPaddingRow(0, kPaddingVertical);
177 }
178
179 void LayoutNotificationView() {
180 SetLayoutManager(
181 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
182 AddChildView(number_label_);
183 message_label_->SizeToFit(kTrayNotificationContentsWidth);
184 AddChildView(message_label_);
185 }
186
187 TraySms* owner_;
188 size_t index_;
189 views::Label* number_label_;
190 views::Label* message_label_;
191
192 DISALLOW_COPY_AND_ASSIGN(SmsMessageView);
193 };
194
195 class TraySms::SmsDetailedView : public TrayDetailsView {
196 public:
197 explicit SmsDetailedView(TraySms* owner) : TrayDetailsView(owner) {
198 Init();
199 Update();
200 }
201
202 ~SmsDetailedView() override {}
203
204 void Init() {
205 CreateScrollableList();
206 CreateTitleRow(IDS_ASH_STATUS_TRAY_SMS);
207 }
208
209 void Update() {
210 UpdateMessageList();
211 Layout();
212 SchedulePaint();
213 }
214
215 // Overridden from views::View.
216 gfx::Size GetPreferredSize() const override {
217 gfx::Size preferred_size = TrayDetailsView::GetPreferredSize();
218 if (preferred_size.height() < kMessageListMinHeight)
219 preferred_size.set_height(kMessageListMinHeight);
220 return preferred_size;
221 }
222
223 private:
224 void UpdateMessageList() {
225 const base::ListValue& messages =
226 static_cast<TraySms*>(owner())->messages();
227 scroll_content()->RemoveAllChildViews(true);
228 for (size_t index = 0; index < messages.GetSize(); ++index) {
229 const base::DictionaryValue* message = nullptr;
230 if (!messages.GetDictionary(index, &message)) {
231 LOG(ERROR) << "SMS message not a dictionary at: " << index;
232 continue;
233 }
234 std::string number, text;
235 if (!GetMessageFromDictionary(message, &number, &text)) {
236 LOG(ERROR) << "Error parsing SMS message";
237 continue;
238 }
239 SmsMessageView* msgview = new SmsMessageView(
240 static_cast<TraySms*>(owner()), SmsMessageView::VIEW_DETAILED, index,
241 number, text);
242 scroll_content()->AddChildView(msgview);
243 }
244 scroller()->Layout();
245 }
246
247 DISALLOW_COPY_AND_ASSIGN(SmsDetailedView);
248 };
249
250 class TraySms::SmsNotificationView : public TrayNotificationView {
251 public:
252 SmsNotificationView(TraySms* owner,
253 size_t message_index,
254 const std::string& number,
255 const std::string& text)
256 : TrayNotificationView(owner, IDR_AURA_UBER_TRAY_SMS),
257 message_index_(message_index) {
258 SmsMessageView* message_view = new SmsMessageView(
259 owner, SmsMessageView::VIEW_NOTIFICATION, message_index_, number, text);
260 InitView(message_view);
261 }
262
263 void Update(size_t message_index,
264 const std::string& number,
265 const std::string& text) {
266 SmsMessageView* message_view =
267 new SmsMessageView(tray_sms(), SmsMessageView::VIEW_NOTIFICATION,
268 message_index_, number, text);
269 UpdateView(message_view);
270 }
271
272 // Overridden from TrayNotificationView:
273 void OnClose() override {
274 if (tray_sms()->RemoveMessage(message_index_)) {
275 WmShell::Get()->RecordUserMetricsAction(
276 UMA_STATUS_AREA_SMS_NOTIFICATION_DISMISS_MSG);
277 }
278 }
279
280 void OnClickAction() override { owner()->PopupDetailedView(0, true); }
281
282 private:
283 TraySms* tray_sms() { return static_cast<TraySms*>(owner()); }
284
285 size_t message_index_;
286
287 DISALLOW_COPY_AND_ASSIGN(SmsNotificationView);
288 };
289
290 TraySms::TraySms(SystemTray* system_tray)
291 : SystemTrayItem(system_tray, UMA_SMS),
292 default_(nullptr),
293 detailed_(nullptr),
294 notification_(nullptr) {
295 // TODO(armansito): SMS could be a special case for cellular that requires a
296 // user (perhaps the owner) to be logged in. If that is the case, then an
297 // additional check should be done before subscribing for SMS notifications.
298 if (chromeos::NetworkHandler::IsInitialized())
299 chromeos::NetworkHandler::Get()->network_sms_handler()->AddObserver(this);
300 }
301
302 TraySms::~TraySms() {
303 if (chromeos::NetworkHandler::IsInitialized()) {
304 chromeos::NetworkHandler::Get()->network_sms_handler()->RemoveObserver(
305 this);
306 }
307 }
308
309 views::View* TraySms::CreateDefaultView(LoginStatus status) {
310 CHECK(default_ == nullptr);
311 default_ = new SmsDefaultView(this);
312 default_->SetVisible(!messages_.empty());
313 return default_;
314 }
315
316 views::View* TraySms::CreateDetailedView(LoginStatus status) {
317 CHECK(detailed_ == nullptr);
318 HideNotificationView();
319 if (messages_.empty())
320 return nullptr;
321 detailed_ = new SmsDetailedView(this);
322 WmShell::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_DETAILED_SMS_VIEW);
323 return detailed_;
324 }
325
326 views::View* TraySms::CreateNotificationView(LoginStatus status) {
327 CHECK(notification_ == nullptr);
328 if (detailed_)
329 return nullptr;
330 size_t index;
331 std::string number, text;
332 if (GetLatestMessage(&index, &number, &text))
333 notification_ = new SmsNotificationView(this, index, number, text);
334 return notification_;
335 }
336
337 void TraySms::DestroyDefaultView() {
338 default_ = nullptr;
339 }
340
341 void TraySms::DestroyDetailedView() {
342 detailed_ = nullptr;
343 }
344
345 void TraySms::DestroyNotificationView() {
346 notification_ = nullptr;
347 }
348
349 void TraySms::MessageReceived(const base::DictionaryValue& message) {
350 std::string message_text;
351 if (!message.GetStringWithoutPathExpansion(
352 chromeos::NetworkSmsHandler::kTextKey, &message_text)) {
353 NET_LOG_ERROR("SMS message contains no content.", "");
354 return;
355 }
356 // TODO(armansito): A message might be due to a special "Message Waiting"
357 // state that the message is in. Once SMS handling moves to shill, such
358 // messages should be filtered there so that this check becomes unnecessary.
359 if (message_text.empty()) {
360 NET_LOG_DEBUG("SMS has empty content text. Ignoring.", "");
361 return;
362 }
363 std::string message_number;
364 if (!message.GetStringWithoutPathExpansion(
365 chromeos::NetworkSmsHandler::kNumberKey, &message_number)) {
366 NET_LOG_DEBUG("SMS contains no number. Ignoring.", "");
367 return;
368 }
369
370 NET_LOG_DEBUG(
371 "Received SMS from: " + message_number + " with text: " + message_text,
372 "");
373
374 auto dict = base::MakeUnique<base::DictionaryValue>();
375 dict->SetString(kSmsNumberKey, message_number);
376 dict->SetString(kSmsTextKey, message_text);
377 messages_.Append(std::move(dict));
378 Update(true);
379 }
380
381 bool TraySms::GetLatestMessage(size_t* index,
382 std::string* number,
383 std::string* text) {
384 if (messages_.empty())
385 return false;
386 base::DictionaryValue* message;
387 size_t message_index = messages_.GetSize() - 1;
388 if (!messages_.GetDictionary(message_index, &message))
389 return false;
390 if (!GetMessageFromDictionary(message, number, text))
391 return false;
392 *index = message_index;
393 return true;
394 }
395
396 bool TraySms::RemoveMessage(size_t index) {
397 if (index >= messages_.GetSize())
398 return false;
399 messages_.Remove(index, nullptr);
400 return true;
401 }
402
403 void TraySms::Update(bool notify) {
404 if (messages_.empty()) {
405 if (default_)
406 default_->SetVisible(false);
407 if (detailed_)
408 HideDetailedView(true /* animate */);
409 HideNotificationView();
410 } else {
411 if (default_) {
412 default_->SetVisible(true);
413 default_->Update();
414 }
415 if (detailed_)
416 detailed_->Update();
417 if (notification_) {
418 size_t index;
419 std::string number, text;
420 if (GetLatestMessage(&index, &number, &text))
421 notification_->Update(index, number, text);
422 } else if (notify) {
423 ShowNotificationView();
424 }
425 }
426 }
427
428 } // namespace ash
OLDNEW
« no previous file with comments | « ash/common/system/chromeos/network/tray_sms.h ('k') | ash/common/system/chromeos/screen_security/screen_tray_item.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698