tray_sms.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant// Use of this source code is governed by a BSD-style license that can be
3f5256e16dfc425c1d466f6308d4026d529ce9e0bHoward Hinnant// found in the LICENSE file.
4bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
5b64f8b07c104c6cc986570ac8ee0ed16a9f23976Howard Hinnant#include "ash/system/chromeos/network/tray_sms.h"
6b64f8b07c104c6cc986570ac8ee0ed16a9f23976Howard Hinnant
7bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "ash/ash_switches.h"
8bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "ash/shell.h"
9bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "ash/system/tray/system_tray.h"
10bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "ash/system/tray/tray_constants.h"
11bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "ash/system/tray/tray_details_view.h"
12bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "ash/system/tray/tray_item_more.h"
13#include "ash/system/tray/tray_item_view.h"
14#include "ash/system/tray/tray_notification_view.h"
15#include "ash/system/tray/tray_views.h"
16#include "base/command_line.h"
17#include "base/string_number_conversions.h"
18#include "base/utf_string_conversions.h"
19#include "grit/ash_resources.h"
20#include "grit/ash_strings.h"
21#include "ui/base/l10n/l10n_util.h"
22#include "ui/base/resource/resource_bundle.h"
23#include "ui/views/controls/image_view.h"
24#include "ui/views/controls/label.h"
25#include "ui/views/layout/box_layout.h"
26#include "ui/views/layout/fill_layout.h"
27#include "ui/views/layout/grid_layout.h"
28#include "ui/views/view.h"
29
30namespace {
31
32// Min height of the list of messages in the popup.
33const int kMessageListMinHeight = 200;
34// Top/bottom padding of the text items.
35const int kPaddingVertical = 10;
36
37bool GetMessageFromDictionary(const base::DictionaryValue* message,
38                              std::string* number,
39                              std::string* text) {
40  if (!message->GetStringWithoutPathExpansion(ash::kSmsNumberKey, number))
41    return false;
42  if (!message->GetStringWithoutPathExpansion(ash::kSmsTextKey, text))
43    return false;
44  return true;
45}
46
47}  // namespace
48
49namespace ash {
50namespace internal {
51
52class TraySms::SmsDefaultView : public TrayItemMore {
53 public:
54  explicit SmsDefaultView(TraySms* tray)
55      : TrayItemMore(tray, true),
56        tray_(tray) {
57    SetImage(ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
58        IDR_AURA_UBER_TRAY_SMS));
59    Update();
60  }
61
62  virtual ~SmsDefaultView() {}
63
64  void Update() {
65    int message_count = tray_->messages().GetSize();
66    string16 label = l10n_util::GetStringFUTF16(
67        IDS_ASH_STATUS_TRAY_SMS_MESSAGES, base::IntToString16(message_count));
68    SetLabel(label);
69    SetAccessibleName(label);
70  }
71
72 private:
73  TraySms* tray_;
74
75  DISALLOW_COPY_AND_ASSIGN(SmsDefaultView);
76};
77
78// An entry (row) in SmsDetailedView or NotificationView.
79class TraySms::SmsMessageView : public views::View,
80                                public views::ButtonListener {
81 public:
82  enum ViewType {
83    VIEW_DETAILED,
84    VIEW_NOTIFICATION
85  };
86
87  SmsMessageView(TraySms* tray,
88                 ViewType view_type,
89                 size_t index,
90                 const std::string& number,
91                 const std::string& message)
92      : tray_(tray),
93        index_(index) {
94    number_label_ = new views::Label(
95        l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_SMS_NUMBER,
96                                   UTF8ToUTF16(number)));
97    number_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
98    number_label_->SetFont(
99        number_label_->font().DeriveFont(0, gfx::Font::BOLD));
100
101    message_label_ = new views::Label(UTF8ToUTF16(message));
102    message_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
103    message_label_->SetMultiLine(true);
104
105    if (view_type == VIEW_DETAILED)
106      LayoutDetailedView();
107    else
108      LayoutNotificationView();
109  }
110
111  virtual ~SmsMessageView() {
112  }
113
114  // Overridden from ButtonListener.
115  virtual void ButtonPressed(views::Button* sender,
116                             const ui::Event& event) OVERRIDE {
117    tray_->RemoveMessage(index_);
118    tray_->Update(false);
119  }
120
121 private:
122  void LayoutDetailedView() {
123    views::ImageButton* close_button = new views::ImageButton(this);
124    close_button->SetImage(views::CustomButton::BS_NORMAL,
125        ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
126            IDR_AURA_WINDOW_CLOSE));
127
128    int msg_width = kTrayPopupWidth - kNotificationIconWidth -
129        kTrayPopupPaddingHorizontal * 2;
130    message_label_->SizeToFit(msg_width);
131
132    views::GridLayout* layout = new views::GridLayout(this);
133    SetLayoutManager(layout);
134
135    views::ColumnSet* columns = layout->AddColumnSet(0);
136
137    // Message
138    columns->AddPaddingColumn(0, kTrayPopupPaddingHorizontal);
139    columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
140                       0 /* resize percent */,
141                       views::GridLayout::FIXED, msg_width, msg_width);
142
143    // Close button
144    columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
145                       0, /* resize percent */
146                       views::GridLayout::FIXED,
147                       kNotificationIconWidth, kNotificationIconWidth);
148
149
150    layout->AddPaddingRow(0, kPaddingVertical);
151    layout->StartRow(0, 0);
152    layout->AddView(number_label_);
153    layout->AddView(close_button, 1, 2);  // 2 rows for icon
154    layout->StartRow(0, 0);
155    layout->AddView(message_label_);
156
157    layout->AddPaddingRow(0, kPaddingVertical);
158  }
159
160  void LayoutNotificationView() {
161    SetLayoutManager(
162        new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
163    AddChildView(number_label_);
164    message_label_->SizeToFit(kTrayNotificationContentsWidth);
165    AddChildView(message_label_);
166  }
167
168  TraySms* tray_;
169  size_t index_;
170  views::Label* number_label_;
171  views::Label* message_label_;
172
173  DISALLOW_COPY_AND_ASSIGN(SmsMessageView);
174};
175
176class TraySms::SmsDetailedView : public TrayDetailsView,
177                                 public ViewClickListener {
178 public:
179  explicit SmsDetailedView(TraySms* tray)
180      : tray_(tray) {
181    Init();
182    Update();
183  }
184
185  virtual ~SmsDetailedView() {
186  }
187
188  void Init() {
189    CreateScrollableList();
190    CreateSpecialRow(IDS_ASH_STATUS_TRAY_SMS, this);
191  }
192
193  void Update() {
194    UpdateMessageList();
195    Layout();
196    SchedulePaint();
197  }
198
199  // Overridden from views::View.
200  gfx::Size GetPreferredSize() {
201    gfx::Size preferred_size = TrayDetailsView::GetPreferredSize();
202    if (preferred_size.height() < kMessageListMinHeight)
203      preferred_size.set_height(kMessageListMinHeight);
204    return preferred_size;
205  }
206
207 private:
208  void UpdateMessageList() {
209    const base::ListValue& messages = tray_->messages();
210    scroll_content()->RemoveAllChildViews(true);
211    for (size_t index = 0; index < messages.GetSize(); ++index) {
212      const base::DictionaryValue* message = NULL;
213      if (!messages.GetDictionary(index, &message)) {
214        LOG(ERROR) << "SMS message not a dictionary at: " << index;
215        continue;
216      }
217      std::string number, text;
218      if (!GetMessageFromDictionary(message, &number, &text)) {
219        LOG(ERROR) << "Error parsing SMS message";
220        continue;
221      }
222      SmsMessageView* msgview = new SmsMessageView(
223          tray_, SmsMessageView::VIEW_DETAILED, index, number, text);
224      scroll_content()->AddChildView(msgview);
225    }
226    scroller()->Layout();
227  }
228
229  // Overridden from ViewClickListener.
230  virtual void ClickedOn(views::View* sender) OVERRIDE {
231    if (sender == footer()->content())
232      Shell::GetInstance()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
233  }
234
235  TraySms* tray_;
236
237  DISALLOW_COPY_AND_ASSIGN(SmsDetailedView);
238};
239
240class TraySms::SmsNotificationView : public TrayNotificationView {
241 public:
242  SmsNotificationView(TraySms* tray,
243                      size_t message_index,
244                      const std::string& number,
245                      const std::string& text)
246      : TrayNotificationView(tray, IDR_AURA_UBER_TRAY_SMS),
247        message_index_(message_index) {
248    SmsMessageView* message_view = new SmsMessageView(
249        tray, SmsMessageView::VIEW_NOTIFICATION, message_index_, number, text);
250    InitView(message_view);
251  }
252
253  void Update(size_t message_index,
254              const std::string& number,
255              const std::string& text) {
256    SmsMessageView* message_view = new SmsMessageView(
257        tray_sms(), SmsMessageView::VIEW_NOTIFICATION,
258        message_index_, number, text);
259    UpdateView(message_view);
260  }
261
262  // Overridden from TrayNotificationView:
263  virtual void OnClose() OVERRIDE {
264    tray_sms()->RemoveMessage(message_index_);
265  }
266
267  virtual void OnClickAction() OVERRIDE {
268    tray()->PopupDetailedView(0, true);
269  }
270
271 private:
272  TraySms* tray_sms() {
273    return static_cast<TraySms*>(tray());
274  }
275
276  size_t message_index_;
277
278  DISALLOW_COPY_AND_ASSIGN(SmsNotificationView);
279};
280
281TraySms::TraySms()
282    : default_(NULL),
283      detailed_(NULL),
284      notification_(NULL) {
285}
286
287TraySms::~TraySms() {
288}
289
290views::View* TraySms::CreateDefaultView(user::LoginStatus status) {
291  CHECK(default_ == NULL);
292  default_ = new SmsDefaultView(this);
293  default_->SetVisible(!messages_.empty());
294  return default_;
295}
296
297views::View* TraySms::CreateDetailedView(user::LoginStatus status) {
298  CHECK(detailed_ == NULL);
299  HideNotificationView();
300  if (messages_.empty())
301    return NULL;
302  detailed_ = new SmsDetailedView(this);
303  return detailed_;
304}
305
306views::View* TraySms::CreateNotificationView(user::LoginStatus status) {
307  CHECK(notification_ == NULL);
308  size_t index;
309  std::string number, text;
310  if (GetLatestMessage(&index, &number, &text))
311    notification_ = new SmsNotificationView(this, index, number, text);
312  return notification_;
313}
314
315void TraySms::DestroyDefaultView() {
316  default_ = NULL;
317}
318
319void TraySms::DestroyDetailedView() {
320  detailed_ = NULL;
321}
322
323void TraySms::DestroyNotificationView() {
324  notification_ = NULL;
325}
326
327void TraySms::AddMessage(const base::DictionaryValue& message) {
328  messages_.Append(message.DeepCopy());
329  Update(true);
330}
331
332bool TraySms::GetLatestMessage(size_t* index,
333                               std::string* number,
334                               std::string* text) {
335  if (messages_.empty())
336    return false;
337  DictionaryValue* message;
338  size_t message_index = messages_.GetSize() - 1;
339  if (!messages_.GetDictionary(message_index, &message))
340    return false;
341  if (!GetMessageFromDictionary(message, number, text))
342    return false;
343  *index = message_index;
344  return true;
345}
346
347void TraySms::RemoveMessage(size_t index) {
348  if (index < messages_.GetSize())
349    messages_.Remove(index, NULL);
350}
351
352void TraySms::Update(bool notify) {
353  if (messages_.empty()) {
354    if (default_)
355      default_->SetVisible(false);
356    if (detailed_)
357      HideDetailedView();
358    HideNotificationView();
359  } else {
360    if (default_) {
361      default_->SetVisible(true);
362      default_->Update();
363    }
364    if (detailed_)
365      detailed_->Update();
366    if (notification_) {
367      size_t index;
368      std::string number, text;
369      if (GetLatestMessage(&index, &number, &text))
370        notification_->Update(index, number, text);
371    } else if (notify) {
372      ShowNotificationView();
373    }
374  }
375}
376
377}  // namespace internal
378}  // namespace ash
379