Home | History | Annotate | Download | only in system
      1 // Copyright 2014 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 "athena/system/network_selector.h"
      6 
      7 #include "base/memory/weak_ptr.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "chromeos/network/network_configuration_handler.h"
     10 #include "chromeos/network/network_connection_handler.h"
     11 #include "chromeos/network/network_handler.h"
     12 #include "chromeos/network/network_profile_handler.h"
     13 #include "chromeos/network/network_state.h"
     14 #include "chromeos/network/network_state_handler.h"
     15 #include "chromeos/network/network_state_handler_observer.h"
     16 #include "chromeos/network/network_type_pattern.h"
     17 #include "third_party/cros_system_api/dbus/service_constants.h"
     18 #include "ui/aura/window.h"
     19 #include "ui/chromeos/network/network_icon.h"
     20 #include "ui/chromeos/network/network_info.h"
     21 #include "ui/chromeos/network/network_list.h"
     22 #include "ui/chromeos/network/network_list_delegate.h"
     23 #include "ui/compositor/layer.h"
     24 #include "ui/gfx/canvas.h"
     25 #include "ui/gfx/font.h"
     26 #include "ui/gfx/font_list.h"
     27 #include "ui/gfx/geometry/rect.h"
     28 #include "ui/gfx/text_constants.h"
     29 #include "ui/views/background.h"
     30 #include "ui/views/border.h"
     31 #include "ui/views/controls/button/blue_button.h"
     32 #include "ui/views/controls/button/button.h"
     33 #include "ui/views/controls/image_view.h"
     34 #include "ui/views/controls/label.h"
     35 #include "ui/views/controls/scroll_view.h"
     36 #include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
     37 #include "ui/views/controls/textfield/textfield.h"
     38 #include "ui/views/layout/box_layout.h"
     39 #include "ui/views/layout/fill_layout.h"
     40 #include "ui/views/widget/widget.h"
     41 
     42 using chromeos::NetworkConfigurationHandler;
     43 using chromeos::NetworkConnectionHandler;
     44 using chromeos::NetworkHandler;
     45 using chromeos::NetworkProfileHandler;
     46 using chromeos::NetworkState;
     47 
     48 namespace {
     49 
     50 const int kBackgroundColor = SkColorSetARGB(0x7f, 0, 0, 0);
     51 
     52 // The View for the user to enter the password for connceting to a network. This
     53 // view also shows an error message if the network connection fails.
     54 class PasswordView : public views::View, public views::ButtonListener {
     55  public:
     56   PasswordView(const ui::NetworkInfo& network,
     57                const base::Callback<void(bool)>& callback,
     58                views::View* parent_container)
     59       : network_(network),
     60         callback_(callback),
     61         parent_container_(parent_container),
     62         connect_(NULL),
     63         cancel_(NULL),
     64         textfield_(NULL),
     65         error_msg_(NULL),
     66         weak_ptr_(this) {
     67     const int kHorizontal = 5;
     68     const int kVertical = 0;
     69     const int kPadding = 0;
     70 
     71     views::BoxLayout* layout = new views::BoxLayout(
     72         views::BoxLayout::kVertical, kHorizontal, kVertical, kPadding);
     73     layout->set_main_axis_alignment(
     74         views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
     75     layout->set_cross_axis_alignment(
     76         views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH);
     77     SetLayoutManager(layout);
     78 
     79     views::View* container = new views::View;
     80     layout = new views::BoxLayout(
     81         views::BoxLayout::kHorizontal, kHorizontal, kVertical, kPadding);
     82     layout->set_main_axis_alignment(
     83         views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
     84     container->SetLayoutManager(layout);
     85 
     86     textfield_ = new views::Textfield();
     87     textfield_->set_placeholder_text(base::ASCIIToUTF16("Password"));
     88     textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
     89     textfield_->set_default_width_in_chars(35);
     90     container->AddChildView(textfield_);
     91 
     92     connect_ = new views::BlueButton(this, base::ASCIIToUTF16("Connect"));
     93     container->AddChildView(connect_);
     94 
     95     cancel_ = new views::LabelButton(this, base::ASCIIToUTF16("Cancel"));
     96     cancel_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
     97     container->AddChildView(cancel_);
     98 
     99     AddChildView(container);
    100   }
    101 
    102   virtual ~PasswordView() {}
    103 
    104  private:
    105   void Close(bool successful) { callback_.Run(successful); }
    106 
    107   void OnKnownError(const std::string& error_name,
    108                     scoped_ptr<base::DictionaryValue> error_data) {
    109     std::string message;
    110     if (!error_data->GetString(chromeos::network_handler::kDbusErrorMessage,
    111                                &message))
    112       message = error_name;
    113     if (message.empty())
    114       message = std::string("Unknown error.");
    115     if (!error_msg_) {
    116       error_msg_ = new views::Label();
    117       error_msg_->SetFontList(
    118           error_msg_->font_list().Derive(0, gfx::Font::BOLD));
    119       error_msg_->SetEnabledColor(SK_ColorRED);
    120     }
    121     error_msg_->SetText(base::UTF8ToUTF16(message));
    122     if (!error_msg_->parent()) {
    123       AddChildView(error_msg_);
    124       InvalidateLayout();
    125       parent_container_->Layout();
    126       ScrollRectToVisible(error_msg_->bounds());
    127     }
    128     connect_->SetEnabled(true);
    129   }
    130 
    131   void OnSetProfileSucceed(const base::string16& password) {
    132     base::DictionaryValue properties;
    133     properties.SetStringWithoutPathExpansion(shill::kPassphraseProperty,
    134                                              textfield_->text());
    135     NetworkHandler::Get()->network_configuration_handler()->SetProperties(
    136         network_.service_path,
    137         properties,
    138         base::Bind(&PasswordView::OnSetPropertiesSucceed,
    139                    weak_ptr_.GetWeakPtr()),
    140         base::Bind(&PasswordView::OnKnownError, weak_ptr_.GetWeakPtr()));
    141   }
    142 
    143   void OnSetPropertiesSucceed() {
    144     const bool check_error_state = false;
    145     NetworkHandler::Get()->network_connection_handler()->ConnectToNetwork(
    146         network_.service_path,
    147         base::Bind(&PasswordView::OnConnectionSucceed, weak_ptr_.GetWeakPtr()),
    148         base::Bind(&PasswordView::OnKnownError, weak_ptr_.GetWeakPtr()),
    149         check_error_state);
    150   }
    151 
    152   void OnConnectionSucceed() { Close(true); }
    153 
    154   // views::View:
    155   virtual void ViewHierarchyChanged(
    156       const views::View::ViewHierarchyChangedDetails& details) OVERRIDE {
    157     if (details.is_add && details.child == this)
    158       textfield_->RequestFocus();
    159   }
    160 
    161   // views::ButtonListener:
    162   virtual void ButtonPressed(views::Button* sender,
    163                              const ui::Event& event) OVERRIDE {
    164     if (sender == connect_) {
    165       if (error_msg_) {
    166         RemoveChildView(error_msg_);
    167         delete error_msg_;
    168         error_msg_ = NULL;
    169       }
    170       connect_->SetEnabled(false);
    171       NetworkHandler::Get()->network_configuration_handler()->SetNetworkProfile(
    172           network_.service_path,
    173           NetworkProfileHandler::GetSharedProfilePath(),
    174           base::Bind(&PasswordView::OnSetProfileSucceed,
    175                      weak_ptr_.GetWeakPtr(),
    176                      textfield_->text()),
    177           base::Bind(&PasswordView::OnKnownError, weak_ptr_.GetWeakPtr()));
    178     } else if (sender == cancel_) {
    179       Close(false);
    180     } else {
    181       NOTREACHED();
    182     }
    183   }
    184 
    185   ui::NetworkInfo network_;
    186   base::Callback<void(bool)> callback_;
    187   views::View* parent_container_;
    188 
    189   views::BlueButton* connect_;
    190   views::LabelButton* cancel_;
    191   views::Textfield* textfield_;
    192   views::Label* error_msg_;
    193   base::WeakPtrFactory<PasswordView> weak_ptr_;
    194 
    195   DISALLOW_COPY_AND_ASSIGN(PasswordView);
    196 };
    197 
    198 // A View that represents a single row in the network list. This row also
    199 // contains the View for taking password for password-protected networks.
    200 class NetworkRow : public views::View {
    201  public:
    202   NetworkRow(const ui::NetworkInfo& network, views::View* container)
    203       : network_(network), container_(container), weak_ptr_(this) {
    204     SetBorder(views::Border::CreateEmptyBorder(10, 5, 10, 5));
    205     SetLayoutManager(
    206         new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 10));
    207     Update(network);
    208   }
    209 
    210   virtual ~NetworkRow() {}
    211 
    212   void Update(const ui::NetworkInfo& network) {
    213     network_ = network;
    214     views::ImageView* icon = new views::ImageView();
    215     icon->SetImage(network.image);
    216     icon->SetBounds(0, 0, network.image.width(), network.image.height());
    217 
    218     views::Label* label = new views::Label(network.label);
    219     if (network.highlight)
    220       label->SetFontList(label->font_list().Derive(0, gfx::Font::BOLD));
    221     AddChildView(icon);
    222     AddChildView(label);
    223     if (password_view_)
    224       AddChildView(password_view_.get());
    225   }
    226 
    227   bool has_password_view() const { return password_view_; }
    228 
    229  private:
    230   void OnPasswordComplete(bool successful) {
    231     password_view_.reset();
    232     InvalidateLayout();
    233     container_->Layout();
    234     ScrollRectToVisible(GetContentsBounds());
    235   }
    236 
    237   void ShowPasswordView(const std::string& service_path) {
    238     const NetworkState* network =
    239         NetworkHandler::Get()->network_state_handler()->GetNetworkState(
    240             service_path);
    241     if (!network)
    242       return;
    243 
    244     // If this is not a wifi network that needs a password, then ignore.
    245     if (network->type() != shill::kTypeWifi ||
    246         network->security() == shill::kSecurityNone) {
    247       return;
    248     }
    249 
    250     password_view_.reset(new PasswordView(
    251         network_,
    252         base::Bind(&NetworkRow::OnPasswordComplete, weak_ptr_.GetWeakPtr()),
    253         container_));
    254     password_view_->set_owned_by_client();
    255     AddChildView(password_view_.get());
    256     PreferredSizeChanged();
    257     container_->Layout();
    258     ScrollRectToVisible(password_view_->bounds());
    259   }
    260 
    261   void OnNetworkConnectionError(const std::string& service_path,
    262                                 const std::string& error_name,
    263                                 scoped_ptr<base::DictionaryValue> error_data) {
    264     if (error_name == NetworkConnectionHandler::kErrorConnectCanceled)
    265       return;
    266     if (error_name == shill::kErrorBadPassphrase ||
    267         error_name == NetworkConnectionHandler::kErrorPassphraseRequired ||
    268         error_name == NetworkConnectionHandler::kErrorConfigurationRequired ||
    269         error_name == NetworkConnectionHandler::kErrorAuthenticationRequired) {
    270       ShowPasswordView(service_path);
    271     }
    272   }
    273 
    274   void ActivateNetwork() {
    275     const chromeos::NetworkState* network =
    276         NetworkHandler::Get()->network_state_handler()->GetNetworkState(
    277             network_.service_path);
    278     if (!network)
    279       return;
    280     if (network->IsConnectedState()) {
    281       NetworkHandler::Get()->network_connection_handler()->DisconnectNetwork(
    282           network_.service_path,
    283           base::Closure(),
    284           chromeos::network_handler::ErrorCallback());
    285     } else if (!network->IsConnectingState()) {
    286       // |network| is not connected, and not already trying to connect.
    287       NetworkHandler::Get()->network_connection_handler()->ConnectToNetwork(
    288           network_.service_path,
    289           base::Closure(),
    290           base::Bind(&NetworkRow::OnNetworkConnectionError,
    291                      weak_ptr_.GetWeakPtr(),
    292                      network_.service_path),
    293           false);
    294     }
    295   }
    296 
    297   // views::View:
    298   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
    299     if (event->type() != ui::ET_MOUSE_PRESSED)
    300       return;
    301     ActivateNetwork();
    302     event->SetHandled();
    303   }
    304 
    305   virtual void OnGestureEvent(ui::GestureEvent* gesture) OVERRIDE {
    306     if (gesture->type() != ui::ET_GESTURE_TAP)
    307       return;
    308     ActivateNetwork();
    309     gesture->SetHandled();
    310   }
    311 
    312   ui::NetworkInfo network_;
    313   views::View* container_;
    314   scoped_ptr<views::View> password_view_;
    315   base::WeakPtrFactory<NetworkRow> weak_ptr_;
    316 
    317   DISALLOW_COPY_AND_ASSIGN(NetworkRow);
    318 };
    319 
    320 class NetworkSelector : public ui::NetworkListDelegate,
    321                         public chromeos::NetworkStateHandlerObserver,
    322                         public ui::EventHandler {
    323  public:
    324   explicit NetworkSelector(aura::Window* container)
    325       : background_view_(NULL),
    326         scroll_content_(NULL),
    327         scroller_(NULL),
    328         network_list_(this) {
    329     CreateWidget(container);
    330     CreateNetworkList();
    331 
    332     NetworkHandler::Get()->network_state_handler()->RequestScan();
    333     NetworkHandler::Get()->network_state_handler()->AddObserver(this,
    334                                                                 FROM_HERE);
    335   }
    336 
    337   virtual ~NetworkSelector() {
    338     NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
    339                                                                    FROM_HERE);
    340   }
    341 
    342  private:
    343   void CreateWidget(aura::Window* container) {
    344     views::Widget::InitParams params;
    345     params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
    346     params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
    347     params.activatable = views::Widget::InitParams::ACTIVATABLE_DEFAULT;
    348     params.accept_events = true;
    349     params.bounds = gfx::Rect(container->bounds().size());
    350     params.parent = container;
    351     params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
    352     widget_.reset(new views::Widget());
    353     widget_->Init(params);
    354     widget_->Show();
    355 
    356     background_view_ = new views::View;
    357     background_view_->set_background(
    358         views::Background::CreateSolidBackground(kBackgroundColor));
    359     background_view_->SetBorder(
    360         views::Border::CreateEmptyBorder(100, 300, 300, 300));
    361     background_view_->SetLayoutManager(new views::FillLayout());
    362     background_view_->set_target_handler(this);
    363 
    364     widget_->SetContentsView(background_view_);
    365   }
    366 
    367   void CreateNetworkList() {
    368     const int kListHeight = 500;
    369     scroller_ = new views::ScrollView();
    370     scroller_->set_background(
    371         views::Background::CreateSolidBackground(SK_ColorWHITE));
    372     scroller_->SetBounds(0, 0, 400, kListHeight);
    373 
    374     scroll_content_ = new views::View;
    375     scroll_content_->SetLayoutManager(
    376         new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
    377     scroller_->SetContents(scroll_content_);
    378 
    379     scroller_->ClipHeightTo(kListHeight, kListHeight);
    380     scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false));
    381     background_view_->AddChildView(scroller_);
    382 
    383     background_view_->Layout();
    384 
    385     network_list_.set_content_view(scroll_content_);
    386   }
    387 
    388   void UpdateNetworkList() { network_list_.UpdateNetworkList(); }
    389 
    390   void Close() { delete this; }
    391 
    392   // ui::NetworkListDelegate:
    393   virtual views::View* CreateViewForNetwork(
    394       const ui::NetworkInfo& info) OVERRIDE {
    395     return new NetworkRow(info, background_view_);
    396   }
    397 
    398   virtual bool IsViewHovered(views::View* view) OVERRIDE {
    399     return static_cast<NetworkRow*>(view)->has_password_view();
    400   }
    401 
    402   virtual chromeos::NetworkTypePattern GetNetworkTypePattern() const OVERRIDE {
    403     return chromeos::NetworkTypePattern::NonVirtual();
    404   }
    405 
    406   virtual void UpdateViewForNetwork(views::View* view,
    407                                     const ui::NetworkInfo& info) OVERRIDE {
    408     static_cast<NetworkRow*>(view)->Update(info);
    409   }
    410 
    411   virtual views::Label* CreateInfoLabel() OVERRIDE {
    412     views::Label* label = new views::Label();
    413     return label;
    414   }
    415 
    416   virtual void RelayoutScrollList() OVERRIDE { scroller_->Layout(); }
    417 
    418   // chromeos::NetworkStateHandlerObserver:
    419   virtual void NetworkListChanged() OVERRIDE { UpdateNetworkList(); }
    420 
    421   virtual void DeviceListChanged() OVERRIDE {}
    422 
    423   virtual void DefaultNetworkChanged(
    424       const chromeos::NetworkState* network) OVERRIDE {}
    425 
    426   virtual void NetworkConnectionStateChanged(
    427       const chromeos::NetworkState* network) OVERRIDE {}
    428 
    429   virtual void NetworkPropertiesUpdated(
    430       const chromeos::NetworkState* network) OVERRIDE {}
    431 
    432   // ui::EventHandler:
    433   virtual void OnMouseEvent(ui::MouseEvent* mouse) OVERRIDE {
    434     CHECK_EQ(background_view_, mouse->target());
    435     if (mouse->type() == ui::ET_MOUSE_PRESSED && !mouse->handled()) {
    436       Close();
    437       mouse->SetHandled();
    438     }
    439   }
    440 
    441   virtual void OnGestureEvent(ui::GestureEvent* gesture) OVERRIDE {
    442     CHECK_EQ(background_view_, gesture->target());
    443     if (gesture->type() == ui::ET_GESTURE_TAP && !gesture->handled()) {
    444       Close();
    445       gesture->SetHandled();
    446     }
    447   }
    448 
    449   scoped_ptr<views::Widget> widget_;
    450   views::View* background_view_;
    451   views::View* scroll_content_;
    452   views::ScrollView* scroller_;
    453 
    454   views::View* connect_;
    455 
    456   ui::NetworkListView network_list_;
    457 
    458   DISALLOW_COPY_AND_ASSIGN(NetworkSelector);
    459 };
    460 
    461 }  // namespace
    462 
    463 namespace athena {
    464 
    465 void CreateNetworkSelector(aura::Window* container) {
    466   new NetworkSelector(container);
    467 }
    468 
    469 }  // namespace athena
    470