chromium/ash/auth/views/pin_keyboard_view.cc

// 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/pin_keyboard_view.h"

#include <memory>
#include <string>
#include <string_view>

#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "ash/style/icon_button.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/containers/flat_map.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.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/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/layout/layout_types.h"
#include "ui/views/layout/table_layout.h"
#include "ui/views/view.h"

namespace ash {

namespace {

constexpr int kButtonSize = 48;
constexpr int kButtonsVerticalPadding = 12;
constexpr int kButtonsHorizontalPadding = 16;
constexpr int kPinKeyboardHeightDp =
    4 * kButtonSize + 3 * kButtonsVerticalPadding;
constexpr int kPinKeyboardWidthDp =
    3 * kButtonSize + 2 * kButtonsHorizontalPadding;

constexpr ui::ColorId kButtonBackgroundColorId =
    cros_tokens::kCrosSysSystemBaseElevated;
constexpr ui::ColorId kButtonContentColorId = cros_tokens::kCrosSysOnSurface;

void StyleButton(IconButton* button_ptr) {
  button_ptr->SetBackgroundColor(kButtonBackgroundColorId);
  button_ptr->SetIconColor(kButtonContentColorId);
}

}  // namespace

//----------------------- PinKeyboardView Test API ------------------------

PinKeyboardView::TestApi::TestApi(PinKeyboardView* view) : view_(view) {
  CHECK(view_);
}

PinKeyboardView::TestApi::~TestApi() = default;

views::Button* PinKeyboardView::TestApi::backspace_button() {
  return view_->backspace_button_;
}

views::Button* PinKeyboardView::TestApi::digit_button(int digit) {
  CHECK(view_->digit_buttons_.contains(digit));
  CHECK(view_->digit_buttons_[digit]);
  return view_->digit_buttons_[digit];
}

bool PinKeyboardView::TestApi::GetEnabled() {
  return view_->GetEnabled();
}

void PinKeyboardView::TestApi::SetEnabled(bool enabled) {
  view_->SetEnabled(enabled);
}

void PinKeyboardView::TestApi::AddObserver(
    PinKeyboardView::Observer* observer) {
  view_->AddObserver(observer);
}

void PinKeyboardView::TestApi::RemoveObserver(
    PinKeyboardView::Observer* observer) {
  view_->RemoveObserver(observer);
}

PinKeyboardView* PinKeyboardView::TestApi::GetView() {
  return view_;
}

//----------------------- PinKeyboardView ------------------------

PinKeyboardView::PinKeyboardView() {
  GetViewAccessibility().SetRole(ax::mojom::Role::kKeyboard);
  GetViewAccessibility().SetName(
      l10n_util::GetStringUTF16(IDS_ASH_AUTH_PIN_KEYBOARD));
  // The pin pad is always LTR.
  SetFlipCanvasOnPaintForRTLUI(false);

  // 3x4 with the following layout
  // 1 2 3
  // 4 5 6
  // 7 8 9
  //<- 0
  SetLayoutManager(std::make_unique<views::TableLayout>())
      ->AddColumn(views::LayoutAlignment::kStart,
                  views::LayoutAlignment::kStart,
                  views::TableLayout::kFixedSize,
                  views::TableLayout::ColumnSize::kFixed, kButtonSize, 0)
      .AddPaddingColumn(views::TableLayout::kFixedSize,
                        kButtonsHorizontalPadding)
      .AddColumn(views::LayoutAlignment::kStart, views::LayoutAlignment::kStart,
                 views::TableLayout::kFixedSize,
                 views::TableLayout::ColumnSize::kFixed, kButtonSize, 0)
      .AddPaddingColumn(views::TableLayout::kFixedSize,
                        kButtonsHorizontalPadding)
      .AddColumn(views::LayoutAlignment::kStart, views::LayoutAlignment::kStart,
                 views::TableLayout::kFixedSize,
                 views::TableLayout::ColumnSize::kFixed, kButtonSize, 0)
      .AddRows(1, 0, kButtonSize)
      .AddPaddingRow(0, kButtonsVerticalPadding)
      .AddRows(1, 0, kButtonSize)
      .AddPaddingRow(0, kButtonsVerticalPadding)
      .AddRows(1, 0, kButtonSize)
      .AddPaddingRow(0, kButtonsVerticalPadding)
      .AddRows(1, 0, kButtonSize);

  //----------------------- First row ------------------------

  AddDigitButton(1);
  AddDigitButton(2);
  AddDigitButton(3);

  //----------------------- Second row ------------------------

  AddDigitButton(4);
  AddDigitButton(5);
  AddDigitButton(6);

  //----------------------- Third row ------------------------

  AddDigitButton(7);
  AddDigitButton(8);
  AddDigitButton(9);

  //----------------------- Fourth row ------------------------

  backspace_button_ = AddChildView(std::make_unique<IconButton>(
      base::BindRepeating(&PinKeyboardView::OnBackspacePressed,
                          weak_ptr_factory_.GetWeakPtr()),
      IconButton::Type::kXLarge, &kLockScreenBackspaceIcon,
      IDS_ASH_PIN_KEYBOARD_DELETE_ACCESSIBLE_NAME));
  StyleButton(backspace_button_);
  AddDigitButton(0);
}

PinKeyboardView::~PinKeyboardView() = default;

gfx::Size PinKeyboardView::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  return gfx::Size(kPinKeyboardWidthDp, kPinKeyboardHeightDp);
}

void PinKeyboardView::OnDigitButtonPressed(int digit) {
  CHECK_GE(digit, 0);
  CHECK_LT(digit, 10);
  CHECK(GetEnabled());
  for (auto& observer : observers_) {
    observer.OnDigitButtonPressed(digit);
  }
}

void PinKeyboardView::OnBackspacePressed() {
  CHECK(GetEnabled());
  for (auto& observer : observers_) {
    observer.OnBackspacePressed();
  }
}

void PinKeyboardView::AddDigitButton(int digit) {
  CHECK_GE(digit, 0);
  CHECK_LT(digit, 10);

  IconButton::Builder builder;
  builder.SetType(IconButton::Type::kXLarge)
      .SetAccessibleName(base::NumberToString16(digit))
      .SetCallback(base::BindRepeating(&PinKeyboardView::OnDigitButtonPressed,
                                       weak_ptr_factory_.GetWeakPtr(), digit))
      .SetTogglable(false)
      .SetEnabled(true)
      .SetSymbol(static_cast<char>('0' + digit));

  auto* button_ptr = AddChildView(builder.Build());

  digit_buttons_[digit] = button_ptr;
  StyleButton(button_ptr);
}

void PinKeyboardView::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void PinKeyboardView::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

BEGIN_METADATA(PinKeyboardView)
END_METADATA

}  // namespace ash