tray_sms.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/system/chromeos/network/tray_sms.h" 6 7#include "ash/ash_switches.h" 8#include "ash/shell.h" 9#include "ash/system/tray/system_tray.h" 10#include "ash/system/tray/tray_constants.h" 11#include "ash/system/tray/tray_details_view.h" 12#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