blob: 7519968c9c63c9abc8f93e00d678e972640e118a [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "athena/system/network_selector.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/network/network_configuration_handler.h"
#include "chromeos/network/network_connection_handler.h"
#include "chromeos/network/network_handler.h"
#include "chromeos/network/network_profile_handler.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_state_handler_observer.h"
#include "chromeos/network/network_type_pattern.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/aura/window.h"
#include "ui/chromeos/network/network_icon.h"
#include "ui/chromeos/network/network_info.h"
#include "ui/chromeos/network/network_list.h"
#include "ui/chromeos/network/network_list_delegate.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/text_constants.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/blue_button.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
using chromeos::NetworkConfigurationHandler;
using chromeos::NetworkConnectionHandler;
using chromeos::NetworkHandler;
using chromeos::NetworkProfileHandler;
using chromeos::NetworkState;
namespace {
const int kBackgroundColor = SkColorSetARGB(0x7f, 0, 0, 0);
// The View for the user to enter the password for connceting to a network. This
// view also shows an error message if the network connection fails.
class PasswordView : public views::View, public views::ButtonListener {
public:
PasswordView(const ui::NetworkInfo& network,
const base::Callback<void(bool)>& callback,
views::View* parent_container)
: network_(network),
callback_(callback),
parent_container_(parent_container),
connect_(NULL),
cancel_(NULL),
textfield_(NULL),
error_msg_(NULL),
weak_ptr_(this) {
const int kHorizontal = 5;
const int kVertical = 0;
const int kPadding = 0;
views::BoxLayout* layout = new views::BoxLayout(
views::BoxLayout::kVertical, kHorizontal, kVertical, kPadding);
layout->set_main_axis_alignment(
views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
layout->set_cross_axis_alignment(
views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH);
SetLayoutManager(layout);
views::View* container = new views::View;
layout = new views::BoxLayout(
views::BoxLayout::kHorizontal, kHorizontal, kVertical, kPadding);
layout->set_main_axis_alignment(
views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
container->SetLayoutManager(layout);
textfield_ = new views::Textfield();
textfield_->set_placeholder_text(base::ASCIIToUTF16("Password"));
textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
textfield_->set_default_width_in_chars(35);
container->AddChildView(textfield_);
connect_ = new views::BlueButton(this, base::ASCIIToUTF16("Connect"));
container->AddChildView(connect_);
cancel_ = new views::LabelButton(this, base::ASCIIToUTF16("Cancel"));
cancel_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
container->AddChildView(cancel_);
AddChildView(container);
}
virtual ~PasswordView() {}
private:
void Close(bool successful) { callback_.Run(successful); }
void OnKnownError(const std::string& error_name,
scoped_ptr<base::DictionaryValue> error_data) {
std::string message;
if (!error_data->GetString(chromeos::network_handler::kDbusErrorMessage,
&message))
message = error_name;
if (message.empty())
message = std::string("Unknown error.");
if (!error_msg_) {
error_msg_ = new views::Label();
error_msg_->SetFontList(
error_msg_->font_list().Derive(0, gfx::Font::BOLD));
error_msg_->SetEnabledColor(SK_ColorRED);
}
error_msg_->SetText(base::UTF8ToUTF16(message));
if (!error_msg_->parent()) {
AddChildView(error_msg_);
InvalidateLayout();
parent_container_->Layout();
ScrollRectToVisible(error_msg_->bounds());
}
connect_->SetEnabled(true);
}
void OnSetProfileSucceed(const base::string16& password) {
base::DictionaryValue properties;
properties.SetStringWithoutPathExpansion(shill::kPassphraseProperty,
textfield_->text());
NetworkHandler::Get()->network_configuration_handler()->SetProperties(
network_.service_path,
properties,
base::Bind(&PasswordView::OnSetPropertiesSucceed,
weak_ptr_.GetWeakPtr()),
base::Bind(&PasswordView::OnKnownError, weak_ptr_.GetWeakPtr()));
}
void OnSetPropertiesSucceed() {
const bool check_error_state = false;
NetworkHandler::Get()->network_connection_handler()->ConnectToNetwork(
network_.service_path,
base::Bind(&PasswordView::OnConnectionSucceed, weak_ptr_.GetWeakPtr()),
base::Bind(&PasswordView::OnKnownError, weak_ptr_.GetWeakPtr()),
check_error_state);
}
void OnConnectionSucceed() { Close(true); }
// views::View:
virtual void ViewHierarchyChanged(
const views::View::ViewHierarchyChangedDetails& details) override {
if (details.is_add && details.child == this)
textfield_->RequestFocus();
}
// views::ButtonListener:
virtual void ButtonPressed(views::Button* sender,
const ui::Event& event) override {
if (sender == connect_) {
if (error_msg_) {
RemoveChildView(error_msg_);
delete error_msg_;
error_msg_ = NULL;
}
connect_->SetEnabled(false);
NetworkHandler::Get()->network_configuration_handler()->SetNetworkProfile(
network_.service_path,
NetworkProfileHandler::GetSharedProfilePath(),
base::Bind(&PasswordView::OnSetProfileSucceed,
weak_ptr_.GetWeakPtr(),
textfield_->text()),
base::Bind(&PasswordView::OnKnownError, weak_ptr_.GetWeakPtr()));
} else if (sender == cancel_) {
Close(false);
} else {
NOTREACHED();
}
}
ui::NetworkInfo network_;
base::Callback<void(bool)> callback_;
views::View* parent_container_;
views::BlueButton* connect_;
views::LabelButton* cancel_;
views::Textfield* textfield_;
views::Label* error_msg_;
base::WeakPtrFactory<PasswordView> weak_ptr_;
DISALLOW_COPY_AND_ASSIGN(PasswordView);
};
// A View that represents a single row in the network list. This row also
// contains the View for taking password for password-protected networks.
class NetworkRow : public views::View {
public:
NetworkRow(const ui::NetworkInfo& network, views::View* container)
: network_(network), container_(container), weak_ptr_(this) {
SetBorder(views::Border::CreateEmptyBorder(10, 5, 10, 5));
SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 10));
Update(network);
}
virtual ~NetworkRow() {}
void Update(const ui::NetworkInfo& network) {
network_ = network;
views::ImageView* icon = new views::ImageView();
icon->SetImage(network.image);
icon->SetBounds(0, 0, network.image.width(), network.image.height());
views::Label* label = new views::Label(network.label);
if (network.highlight)
label->SetFontList(label->font_list().Derive(0, gfx::Font::BOLD));
AddChildView(icon);
AddChildView(label);
if (password_view_)
AddChildView(password_view_.get());
}
bool has_password_view() const { return password_view_; }
private:
void OnPasswordComplete(bool successful) {
password_view_.reset();
InvalidateLayout();
container_->Layout();
ScrollRectToVisible(GetContentsBounds());
}
void ShowPasswordView(const std::string& service_path) {
const NetworkState* network =
NetworkHandler::Get()->network_state_handler()->GetNetworkState(
service_path);
if (!network)
return;
// If this is not a wifi network that needs a password, then ignore.
if (network->type() != shill::kTypeWifi ||
network->security() == shill::kSecurityNone) {
return;
}
password_view_.reset(new PasswordView(
network_,
base::Bind(&NetworkRow::OnPasswordComplete, weak_ptr_.GetWeakPtr()),
container_));
password_view_->set_owned_by_client();
AddChildView(password_view_.get());
PreferredSizeChanged();
container_->Layout();
ScrollRectToVisible(password_view_->bounds());
}
void OnNetworkConnectionError(const std::string& service_path,
const std::string& error_name,
scoped_ptr<base::DictionaryValue> error_data) {
if (error_name == NetworkConnectionHandler::kErrorConnectCanceled)
return;
if (error_name == shill::kErrorBadPassphrase ||
error_name == NetworkConnectionHandler::kErrorPassphraseRequired ||
error_name == NetworkConnectionHandler::kErrorConfigurationRequired ||
error_name == NetworkConnectionHandler::kErrorAuthenticationRequired) {
ShowPasswordView(service_path);
}
}
void ActivateNetwork() {
const chromeos::NetworkState* network =
NetworkHandler::Get()->network_state_handler()->GetNetworkState(
network_.service_path);
if (!network)
return;
if (network->IsConnectedState()) {
NetworkHandler::Get()->network_connection_handler()->DisconnectNetwork(
network_.service_path,
base::Closure(),
chromeos::network_handler::ErrorCallback());
} else if (!network->IsConnectingState()) {
// |network| is not connected, and not already trying to connect.
NetworkHandler::Get()->network_connection_handler()->ConnectToNetwork(
network_.service_path,
base::Closure(),
base::Bind(&NetworkRow::OnNetworkConnectionError,
weak_ptr_.GetWeakPtr(),
network_.service_path),
false);
}
}
// views::View:
virtual void OnMouseEvent(ui::MouseEvent* event) override {
if (event->type() != ui::ET_MOUSE_PRESSED)
return;
ActivateNetwork();
event->SetHandled();
}
virtual void OnGestureEvent(ui::GestureEvent* gesture) override {
if (gesture->type() != ui::ET_GESTURE_TAP)
return;
ActivateNetwork();
gesture->SetHandled();
}
ui::NetworkInfo network_;
views::View* container_;
scoped_ptr<views::View> password_view_;
base::WeakPtrFactory<NetworkRow> weak_ptr_;
DISALLOW_COPY_AND_ASSIGN(NetworkRow);
};
class NetworkSelector : public ui::NetworkListDelegate,
public chromeos::NetworkStateHandlerObserver,
public ui::EventHandler {
public:
explicit NetworkSelector(aura::Window* container)
: background_view_(NULL),
scroll_content_(NULL),
scroller_(NULL),
network_list_(this) {
CreateWidget(container);
CreateNetworkList();
NetworkHandler::Get()->network_state_handler()->RequestScan();
NetworkHandler::Get()->network_state_handler()->AddObserver(this,
FROM_HERE);
}
virtual ~NetworkSelector() {
NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
FROM_HERE);
}
private:
void CreateWidget(aura::Window* container) {
views::Widget::InitParams params;
params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
params.activatable = views::Widget::InitParams::ACTIVATABLE_DEFAULT;
params.accept_events = true;
params.bounds = gfx::Rect(container->bounds().size());
params.parent = container;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget_.reset(new views::Widget());
widget_->Init(params);
widget_->Show();
background_view_ = new views::View;
background_view_->set_background(
views::Background::CreateSolidBackground(kBackgroundColor));
background_view_->SetBorder(
views::Border::CreateEmptyBorder(100, 300, 300, 300));
background_view_->SetLayoutManager(new views::FillLayout());
background_view_->set_target_handler(this);
widget_->SetContentsView(background_view_);
}
void CreateNetworkList() {
const int kListHeight = 500;
scroller_ = new views::ScrollView();
scroller_->set_background(
views::Background::CreateSolidBackground(SK_ColorWHITE));
scroller_->SetBounds(0, 0, 400, kListHeight);
scroll_content_ = new views::View;
scroll_content_->SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
scroller_->SetContents(scroll_content_);
scroller_->ClipHeightTo(kListHeight, kListHeight);
scroller_->SetVerticalScrollBar(new views::OverlayScrollBar(false));
background_view_->AddChildView(scroller_);
background_view_->Layout();
network_list_.set_content_view(scroll_content_);
}
void UpdateNetworkList() { network_list_.UpdateNetworkList(); }
void Close() { delete this; }
// ui::NetworkListDelegate:
virtual views::View* CreateViewForNetwork(
const ui::NetworkInfo& info) override {
return new NetworkRow(info, background_view_);
}
virtual bool IsViewHovered(views::View* view) override {
return static_cast<NetworkRow*>(view)->has_password_view();
}
virtual chromeos::NetworkTypePattern GetNetworkTypePattern() const override {
return chromeos::NetworkTypePattern::NonVirtual();
}
virtual void UpdateViewForNetwork(views::View* view,
const ui::NetworkInfo& info) override {
static_cast<NetworkRow*>(view)->Update(info);
}
virtual views::Label* CreateInfoLabel() override {
views::Label* label = new views::Label();
return label;
}
virtual void RelayoutScrollList() override { scroller_->Layout(); }
// chromeos::NetworkStateHandlerObserver:
virtual void NetworkListChanged() override { UpdateNetworkList(); }
virtual void DeviceListChanged() override {}
virtual void DefaultNetworkChanged(
const chromeos::NetworkState* network) override {}
virtual void NetworkConnectionStateChanged(
const chromeos::NetworkState* network) override {}
virtual void NetworkPropertiesUpdated(
const chromeos::NetworkState* network) override {}
// ui::EventHandler:
virtual void OnMouseEvent(ui::MouseEvent* mouse) override {
CHECK_EQ(background_view_, mouse->target());
if (mouse->type() == ui::ET_MOUSE_PRESSED && !mouse->handled()) {
Close();
mouse->SetHandled();
}
}
virtual void OnGestureEvent(ui::GestureEvent* gesture) override {
CHECK_EQ(background_view_, gesture->target());
if (gesture->type() == ui::ET_GESTURE_TAP && !gesture->handled()) {
Close();
gesture->SetHandled();
}
}
scoped_ptr<views::Widget> widget_;
views::View* background_view_;
views::View* scroll_content_;
views::ScrollView* scroller_;
views::View* connect_;
ui::NetworkListView network_list_;
DISALLOW_COPY_AND_ASSIGN(NetworkSelector);
};
} // namespace
namespace athena {
void CreateNetworkSelector(aura::Window* container) {
new NetworkSelector(container);
}
} // namespace athena