// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/auth/views/active_session_auth_view.h"
#include <memory>
#include <string>
#include "ash/auth/views/auth_input_row_view.h"
#include "ash/auth/views/auth_view_utils.h"
#include "ash/login/ui/login_button.h"
#include "ash/login/ui/non_accessible_view.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "components/account_id/account_id.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/background.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/layout/layout_types.h"
#include "ui/views/vector_icons.h"
#include "ui/views/view.h"
namespace ash {
namespace {
// The in session view width.
constexpr int kActiveSessionAuthViewWidthDp = 322;
// The in session view corner radius.
constexpr int kActiveSessionAuthViewCornerRadiusDp = 20;
// Distance between the top of the view and the close button.
constexpr int kCloseButtonTopDistanceDp = 16;
// Distance between the right hand side of the view and the close button.
constexpr int kCloseButtonRightDistanceDp = 16;
// Distance between the top of the view and the header.
constexpr int kHeaderTopDistanceDp = 32;
// Distance between the left hand side of the view and the header.
constexpr int kHeaderHorizontalDistanceDp = 32;
// Distance between the header and the auth container.
constexpr int kHeaderAuthContainerDistanceDp = 28;
// Distance between the bottom of the view and the auth container.
constexpr int kAuthContainerBottomDistanceDp = 32;
} // namespace
ActiveSessionAuthView::TestApi::TestApi(ActiveSessionAuthView* view)
: view_(view) {}
ActiveSessionAuthView::TestApi::~TestApi() {}
raw_ptr<AuthHeaderView> ActiveSessionAuthView::TestApi::GetAuthHeaderView() {
return view_->auth_header_;
}
raw_ptr<AuthContainerView>
ActiveSessionAuthView::TestApi::GetAuthContainerView() {
return view_->auth_container_;
}
raw_ptr<views::Button> ActiveSessionAuthView::TestApi::GetCloseButton() {
return view_->close_button_;
}
raw_ptr<ActiveSessionAuthView> ActiveSessionAuthView::TestApi::GetView() {
return view_;
}
ActiveSessionAuthView::ActiveSessionAuthView(const AccountId& account_id,
const std::u16string& title,
const std::u16string& description,
AuthFactorSet auth_factors)
: account_id_(account_id) {
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
// Initialize layout.
auto layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical);
layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kStart);
layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
layout_ = SetLayoutManager(std::move(layout));
// Add the header and a close icon on the right top corner.
AddHeaderAndCloseButton(title, description);
// Add vertical space between the header and the auth container.
AddVerticalSpace(this, kHeaderAuthContainerDistanceDp);
// Add auth container view and register to observe the events.
AddAuthContainer(auth_factors);
// Add vertical space to the bottom of the view.
AddVerticalSpace(this, kAuthContainerBottomDistanceDp);
// Set the background.
SetBackground(views::CreateThemedRoundedRectBackground(
cros_tokens::kCrosSysBaseElevated, kActiveSessionAuthViewCornerRadiusDp));
// Set the view as a dialog for a11y purposes.
GetViewAccessibility().SetRole(ax::mojom::Role::kDialog);
GetViewAccessibility().SetName(l10n_util::GetStringUTF16(
IDS_ASH_IN_SESSION_AUTH_DIALOG_ACCESSIBLE_NAME));
GetViewAccessibility().SetDescription(l10n_util::GetStringUTF16(
IDS_ASH_IN_SESSION_AUTH_DIALOG_ACCESSIBLE_DESCRIPTION));
}
ActiveSessionAuthView::~ActiveSessionAuthView() {
auth_container_->RemoveObserver(this);
auth_container_ = nullptr;
auth_header_ = nullptr;
close_button_ = nullptr;
}
void ActiveSessionAuthView::AddHeaderAndCloseButton(
const std::u16string& title,
const std::u16string& description) {
CHECK_EQ(auth_header_, nullptr);
CHECK_EQ(close_button_, nullptr);
auto header_layout = std::make_unique<views::FillLayout>();
views::View* header = AddChildView(std::make_unique<views::View>());
header->SetLayoutManager(std::move(header_layout));
header->SetPaintToLayer();
header->layer()->SetFillsBoundsOpaquely(false);
// Auth header position and add.
views::View* auth_header_container =
AddChildView(std::make_unique<views::View>());
auto auth_header_container_layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical,
gfx::Insets::TLBR(kHeaderTopDistanceDp, kHeaderHorizontalDistanceDp, 0,
kHeaderHorizontalDistanceDp));
auth_header_container_layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kStart);
auth_header_container_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStart);
auth_header_container->SetLayoutManager(
std::move(auth_header_container_layout));
auth_header_ = auth_header_container->AddChildView(
std::make_unique<AuthHeaderView>(account_id_, title, description));
header_observation_.Observe(auth_header_);
header->AddChildView(auth_header_container);
// Close button position and creation.
views::View* close_button_view =
AddChildView(std::make_unique<views::View>());
auto close_button_layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical,
gfx::Insets::VH(kCloseButtonTopDistanceDp, kCloseButtonRightDistanceDp));
close_button_layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kStart);
close_button_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kEnd);
close_button_view->SetLayoutManager(std::move(close_button_layout));
header->AddChildView(close_button_view);
IconButton::Builder builder;
builder.SetType(IconButton::Type::kXSmall)
.SetAccessibleNameId(
IDS_ASH_LOGIN_LOCAL_AUTHENTICATION_CLOSE_DIALOG_BUTTON)
.SetTogglable(false)
.SetEnabled(true)
.SetCallback(base::BindRepeating(&ActiveSessionAuthView::Close,
base::Unretained(this)))
.SetVectorIcon(&views::kIcCloseIcon);
close_button_ = close_button_view->AddChildView(builder.Build());
}
void ActiveSessionAuthView::AddAuthContainer(AuthFactorSet auth_factors) {
CHECK_EQ(auth_container_, nullptr);
auth_container_ =
AddChildView(std::make_unique<AuthContainerView>(auth_factors));
auth_container_->AddObserver(this);
}
gfx::Size ActiveSessionAuthView::CalculatePreferredSize(
const views::SizeBounds& available_size) const {
// The header part of the view.
int preferred_height = kHeaderTopDistanceDp;
preferred_height += auth_header_->GetPreferredSize(available_size).height();
preferred_height += kHeaderAuthContainerDistanceDp;
// The auth container part of the view.
preferred_height +=
auth_container_->GetPreferredSize(available_size).height();
preferred_height += kAuthContainerBottomDistanceDp;
return gfx::Size(kActiveSessionAuthViewWidthDp, preferred_height);
}
void ActiveSessionAuthView::ChildPreferredSizeChanged(views::View* child) {
PreferredSizeChanged();
}
std::string ActiveSessionAuthView::GetObjectName() const {
return "ActiveSessionAuthView";
}
void ActiveSessionAuthView::RequestFocus() {
auth_container_->RequestFocus();
}
void ActiveSessionAuthView::SetHasPassword(bool has_password) {
auth_container_->SetHasPassword(has_password);
}
bool ActiveSessionAuthView::HasPassword() const {
return auth_container_->HasPassword();
}
void ActiveSessionAuthView::SetHasPin(bool has_pin) {
auth_container_->SetHasPin(has_pin);
}
bool ActiveSessionAuthView::HasPin() const {
return auth_container_->HasPin();
}
void ActiveSessionAuthView::SetPinStatus(const std::u16string& status_str) {
auth_container_->SetPinStatus(status_str);
}
void ActiveSessionAuthView::SetInputEnabled(bool enabled) {
auth_container_->SetInputEnabled(enabled);
if (enabled) {
RequestFocus();
}
}
void ActiveSessionAuthView::OnPinSubmit(const std::u16string& pin) {
for (auto& observer : observers_) {
observer.OnPinSubmit(pin);
}
}
void ActiveSessionAuthView::OnPasswordSubmit(const std::u16string& password) {
for (auto& observer : observers_) {
observer.OnPasswordSubmit(password);
}
}
void ActiveSessionAuthView::SetErrorTitle(const std::u16string& error_str) {
auth_header_->SetErrorTitle(error_str);
}
void ActiveSessionAuthView::OnEscape() {
Close();
}
void ActiveSessionAuthView::Close() {
for (auto& observer : observers_) {
observer.OnClose();
}
}
void ActiveSessionAuthView::SetFingerprintState(FingerprintState state) {
auth_container_->SetFingerprintState(state);
}
void ActiveSessionAuthView::NotifyFingerprintAuthFailure() {
auth_container_->NotifyFingerprintAuthFailure();
}
void ActiveSessionAuthView::OnContentsChanged() {
// If something changes on the UI e.g:
// - user change the text of the input text
// - the user switched PIN/password
// - the input text visibility changed
// then we would like to restore the original header and not show the error
// anymore.
auth_header_->RestoreTitle();
}
void ActiveSessionAuthView::ResetInputfields() {
auth_container_->ResetInputfields();
}
void ActiveSessionAuthView::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void ActiveSessionAuthView::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void ActiveSessionAuthView::OnTitleChanged(const std::u16string& error_str) {
GetViewAccessibility().SetName(error_str);
}
BEGIN_METADATA(ActiveSessionAuthView)
END_METADATA
} // namespace ash