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 42using chromeos::NetworkConfigurationHandler; 43using chromeos::NetworkConnectionHandler; 44using chromeos::NetworkHandler; 45using chromeos::NetworkProfileHandler; 46using chromeos::NetworkState; 47 48namespace { 49 50const 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. 54class 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. 200class 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 320class 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 463namespace athena { 464 465void CreateNetworkSelector(aura::Window* container) { 466 new NetworkSelector(container); 467} 468 469} // namespace athena 470