blob: 7d4920bf46c918d0e4c5343a54301372b683f731 [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 "athena/screen/public/screen_manager.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"
#include "ui/views/window/dialog_delegate.h"
using chromeos::NetworkConfigurationHandler;
using chromeos::NetworkConnectionHandler;
using chromeos::NetworkHandler;
using chromeos::NetworkProfileHandler;
using chromeos::NetworkState;
namespace {
// 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)
: network_(network),
callback_(callback),
connect_(nullptr),
cancel_(nullptr),
textfield_(nullptr),
error_msg_(nullptr),
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);
}
~PasswordView() override {}
private:
void CloseDialog(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();
GetWidget()->GetRootView()->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() { CloseDialog(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_ = nullptr;
}
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_) {
CloseDialog(false);
} else {
NOTREACHED();
}
}
ui::NetworkInfo network_;
base::Callback<void(bool)> callback_;
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)
: network_(network), weak_ptr_(this) {
SetBorder(views::Border::CreateEmptyBorder(10, 5, 10, 5));
SetLayoutManager(
new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 10));
Update(network);
}
~NetworkRow() override {}
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();
GetWidget()->GetRootView()->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())));
password_view_->set_owned_by_client();
AddChildView(password_view_.get());
PreferredSizeChanged();
GetWidget()->GetRootView()->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_;
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 views::DialogDelegate {
public:
NetworkSelector()
: scroll_content_(nullptr), scroller_(nullptr), network_list_(this) {
CreateNetworkList();
CreateWidget();
NetworkHandler::Get()->network_state_handler()->RequestScan();
NetworkHandler::Get()->network_state_handler()->AddObserver(this,
FROM_HERE);
}
~NetworkSelector() override {
NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
FROM_HERE);
}
private:
void CreateWidget() {
// Same as CreateDialogWidgetWithBounds() with an empty |bounds|.
views::Widget* widget = views::DialogDelegate::CreateDialogWidget(
this, athena::ScreenManager::Get()->GetContext(), nullptr);
widget->Show();
widget->CenterWindow(gfx::Size(400, 400));
}
void CreateNetworkList() {
const int kListHeight = 400;
scroller_ = new views::ScrollView();
scroller_->set_background(
views::Background::CreateSolidBackground(SK_ColorWHITE));
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));
network_list_.set_content_view(scroll_content_);
}
void UpdateNetworkList() { network_list_.UpdateNetworkList(); }
// ui::NetworkListDelegate:
virtual views::View* CreateViewForNetwork(
const ui::NetworkInfo& info) override {
return new NetworkRow(info);
}
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 {}
// views::DialogDelegate:
virtual ui::ModalType GetModalType() const override {
return ui::MODAL_TYPE_SYSTEM;
}
virtual void DeleteDelegate() override { delete this; }
virtual views::Widget* GetWidget() override { return scroller_->GetWidget(); }
virtual const views::Widget* GetWidget() const override {
return scroller_->GetWidget();
}
virtual views::View* GetContentsView() override { return scroller_; }
virtual int GetDialogButtons() const override { return ui::DIALOG_BUTTON_OK; }
virtual bool Close() override { return true; }
views::View* scroll_content_;
views::ScrollView* scroller_;
views::View* connect_;
ui::NetworkListView network_list_;
DISALLOW_COPY_AND_ASSIGN(NetworkSelector);
};
} // namespace
namespace athena {
void CreateNetworkSelector() {
new NetworkSelector();
}
} // namespace athena