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