Commit ec5f3f8a authored by Nick Burris's avatar Nick Burris Committed by Commit Bot

[SecurePaymentConfirmation] Add body view

Add the body view to complete the secure payment confirmation UI. This
patch adds the title text and the merchant, instrument, and total rows.

Bug: 1110322
Change-Id: Ied9d2c36cf3581108f811d82aab73c207c37dbd3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2365364
Commit-Queue: Nick Burris <nburris@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800280}
parent 5e79d681
......@@ -7,11 +7,16 @@
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/browser/ui/views/payments/payment_request_views_util.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/payments/content/secure_payment_confirmation_model.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/progress_bar.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/grid_layout.h"
namespace payments {
namespace {
......@@ -22,6 +27,25 @@ constexpr int kHeaderIconHeight = 148;
// Height of the progress bar at the top of the dialog.
constexpr int kProgressBarHeight = 4;
// Size of the instrument icon shown in the payment method row.
constexpr int kInstrumentIconWidth = 32;
constexpr int kInstrumentIconHeight = 20;
// Line height of the title text.
constexpr int kTitleLineHeight = 24;
// Line height of the row text.
constexpr int kRowViewLineHeight = 20;
// Insets of the body content.
constexpr int kBodyInsets = 16;
// Extra inset between the body content and the dialog buttons.
constexpr int kBodyExtraInset = 24;
// Height of each row.
constexpr int kRowHeight = 48;
} // namespace
SecurePaymentConfirmationDialogView::SecurePaymentConfirmationDialogView(
......@@ -97,6 +121,38 @@ void SecurePaymentConfirmationDialogView::OnModelUpdated() {
SetButtonEnabled(ui::DIALOG_BUTTON_OK, model_->verify_button_enabled());
SetButtonLabel(ui::DIALOG_BUTTON_CANCEL, model_->cancel_button_label());
SetButtonEnabled(ui::DIALOG_BUTTON_CANCEL, model_->cancel_button_enabled());
UpdateLabelView(DialogViewID::TITLE, model_->title());
UpdateLabelView(DialogViewID::MERCHANT_LABEL, model_->merchant_label());
UpdateLabelView(DialogViewID::MERCHANT_VALUE, model_->merchant_value());
UpdateLabelView(DialogViewID::INSTRUMENT_LABEL, model_->instrument_label());
UpdateLabelView(DialogViewID::INSTRUMENT_VALUE, model_->instrument_value());
// Update the instrument icon only if it's changed
if (model_->instrument_icon() &&
(model_->instrument_icon() != instrument_icon_ ||
model_->instrument_icon()->getGenerationID() !=
instrument_icon_generation_id_)) {
instrument_icon_generation_id_ =
model_->instrument_icon()->getGenerationID();
gfx::ImageSkia image =
gfx::ImageSkia::CreateFrom1xBitmap(*model_->instrument_icon())
.DeepCopy();
static_cast<views::ImageView*>(
GetViewByID(static_cast<int>(DialogViewID::INSTRUMENT_ICON)))
->SetImage(image);
}
instrument_icon_ = model_->instrument_icon();
UpdateLabelView(DialogViewID::TOTAL_LABEL, model_->total_label());
UpdateLabelView(DialogViewID::TOTAL_VALUE, model_->total_value());
}
void SecurePaymentConfirmationDialogView::UpdateLabelView(
DialogViewID id,
const base::string16& text) {
static_cast<views::Label*>(GetViewByID(static_cast<int>(id)))->SetText(text);
}
void SecurePaymentConfirmationDialogView::HideDialog() {
......@@ -129,12 +185,21 @@ void SecurePaymentConfirmationDialogView::InitChildViews() {
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0));
std::unique_ptr<views::View> header_icon = CreateHeaderView();
AddChildView(header_icon.release());
AddChildView(CreateHeaderView());
AddChildView(CreateBodyView());
InvalidateLayout();
}
// Creates the header view, which is the fingerprint icon and a progress bar.
// The fingerprint icon covers the whole header view and the progress bar is
// overlayed on the top of the header.
// +------------------------------------------+
// |===============progress bar===============|
// | |
// | fingerprint icon |
// +------------------------------------------+
std::unique_ptr<views::View>
SecurePaymentConfirmationDialogView::CreateHeaderView() {
const int header_width = ChromeLayoutProvider::Get()->GetDistanceMetric(
......@@ -153,7 +218,7 @@ SecurePaymentConfirmationDialogView::CreateHeaderView() {
image_view->SetImage(gfx::CreateVectorIcon(icon_description));
image_view->SetSize(header_size);
image_view->SetVerticalAlignment(views::ImageView::Alignment::kLeading);
image_view->SetID(DialogViewID::HEADER_ICON);
image_view->SetID(static_cast<int>(DialogViewID::HEADER_ICON));
header_view->AddChildView(image_view.release());
// Progress bar
......@@ -163,7 +228,7 @@ SecurePaymentConfirmationDialogView::CreateHeaderView() {
progress_bar->SetBackgroundColor(SK_ColorTRANSPARENT);
progress_bar->SetPreferredSize(gfx::Size(header_width, kProgressBarHeight));
progress_bar->SizeToPreferredSize();
progress_bar->SetID(DialogViewID::PROGRESS_BAR);
progress_bar->SetID(static_cast<int>(DialogViewID::PROGRESS_BAR));
progress_bar->SetVisible(model_->progress_bar_visible());
progress_bar_ = progress_bar.get();
header_view->AddChildView(progress_bar.release());
......@@ -171,4 +236,158 @@ SecurePaymentConfirmationDialogView::CreateHeaderView() {
return header_view;
}
// Creates the body.
// +------------------------------------------+
// | Title |
// | |
// | merchant label value |
// +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
// | instrument label value icon |
// +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
// | total label value |
// +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
std::unique_ptr<views::View>
SecurePaymentConfirmationDialogView::CreateBodyView() {
auto body = std::make_unique<views::View>();
body->SetBorder(views::CreateEmptyBorder(
gfx::Insets(kBodyInsets, kBodyInsets, kBodyExtraInset, kBodyInsets)));
views::GridLayout* layout =
body->SetLayoutManager(std::make_unique<views::GridLayout>());
views::ColumnSet* columns = layout->AddColumnSet(0);
columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1.0,
views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
layout->StartRow(views::GridLayout::kFixedSize, 0);
std::unique_ptr<views::Label> title_text = std::make_unique<views::Label>(
model_->title(), views::style::CONTEXT_DIALOG_TITLE,
views::style::STYLE_PRIMARY);
title_text->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
title_text->SetLineHeight(kTitleLineHeight);
title_text->SetBorder(views::CreateEmptyBorder(0, 0, kBodyInsets, 0));
title_text->SetID(static_cast<int>(DialogViewID::TITLE));
layout->AddView(std::move(title_text));
layout->StartRow(views::GridLayout::kFixedSize, 0);
layout->AddView(CreateRows());
return body;
}
// Creates the set of merchant, instrument, and total rows.
// +------------------------------------------+
// | merchant label value |
// +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
// | instrument label value icon |
// +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
// | total label value |
// +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
std::unique_ptr<views::View> SecurePaymentConfirmationDialogView::CreateRows() {
auto rows = std::make_unique<views::View>();
views::GridLayout* layout =
rows->SetLayoutManager(std::make_unique<views::GridLayout>());
views::ColumnSet* columns = layout->AddColumnSet(0);
columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1.0,
views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
layout->StartRow(views::GridLayout::kFixedSize, 0);
layout->AddView(
CreateRowView(model_->merchant_label(), DialogViewID::MERCHANT_LABEL,
model_->merchant_value(), DialogViewID::MERCHANT_VALUE));
layout->StartRow(views::GridLayout::kFixedSize, 0);
layout->AddView(
CreateRowView(model_->instrument_label(), DialogViewID::INSTRUMENT_LABEL,
model_->instrument_value(), DialogViewID::INSTRUMENT_VALUE,
model_->instrument_icon(), DialogViewID::INSTRUMENT_ICON));
layout->StartRow(views::GridLayout::kFixedSize, 0);
layout->AddView(
CreateRowView(model_->total_label(), DialogViewID::TOTAL_LABEL,
model_->total_value(), DialogViewID::TOTAL_VALUE));
return rows;
}
// Creates a row of data with |label|, |value|, and optionally |icon|.
// +------------------------------------------+
// | label value icon |
// +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ <-- border
std::unique_ptr<views::View> SecurePaymentConfirmationDialogView::CreateRowView(
const base::string16& label,
DialogViewID label_id,
const base::string16& value,
DialogViewID value_id,
const SkBitmap* icon,
DialogViewID icon_id) {
std::unique_ptr<views::View> row = std::make_unique<views::View>();
row->SetBorder(
views::CreateSolidSidedBorder(0, 0, 1, 0, gfx::kGoogleGrey200));
views::GridLayout* layout =
row->SetLayoutManager(std::make_unique<views::GridLayout>());
views::ColumnSet* columns = layout->AddColumnSet(0);
// Label column
constexpr int kLabelColumnWidth = 80;
columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
views::GridLayout::kFixedSize,
views::GridLayout::ColumnSize::kFixed, kLabelColumnWidth,
0);
constexpr int kPaddingAfterLabel = 24;
columns->AddPaddingColumn(views::GridLayout::kFixedSize, kPaddingAfterLabel);
// Value column
columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1.0,
views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
// Icon column
if (icon) {
columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
views::GridLayout::kFixedSize,
views::GridLayout::ColumnSize::kFixed,
kInstrumentIconWidth, kInstrumentIconWidth);
}
layout->StartRow(views::GridLayout::kFixedSize, 0, kRowHeight);
std::unique_ptr<views::Label> label_text = std::make_unique<views::Label>(
label, CONTEXT_BODY_TEXT_LARGE, views::style::STYLE_SECONDARY);
label_text->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
label_text->SetLineHeight(kRowViewLineHeight);
label_text->SetID(static_cast<int>(label_id));
layout->AddView(std::move(label_text));
std::unique_ptr<views::Label> value_text = std::make_unique<views::Label>(
value, CONTEXT_BODY_TEXT_LARGE, views::style::STYLE_PRIMARY);
value_text->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
value_text->SetLineHeight(kRowViewLineHeight);
value_text->SetID(static_cast<int>(value_id));
layout->AddView(std::move(value_text));
if (icon) {
std::unique_ptr<views::ImageView> icon_view =
std::make_unique<views::ImageView>();
instrument_icon_ = model_->instrument_icon();
instrument_icon_generation_id_ =
model_->instrument_icon()->getGenerationID();
gfx::ImageSkia image =
gfx::ImageSkia::CreateFrom1xBitmap(*model_->instrument_icon())
.DeepCopy();
icon_view->SetImage(image);
icon_view->SetImageSize(
gfx::Size(kInstrumentIconWidth, kInstrumentIconHeight));
icon_view->SetPaintToLayer();
icon_view->layer()->SetFillsBoundsOpaquely(false);
icon_view->SetID(static_cast<int>(icon_id));
layout->AddView(std::move(icon_view));
}
return row;
}
} // namespace payments
......@@ -36,7 +36,19 @@ class SecurePaymentConfirmationDialogView
// IDs that identify a view within the secure payment confirmation dialog.
// Used to validate views in browsertests.
enum DialogViewID : int { VIEW_ID_NONE = 0, HEADER_ICON, PROGRESS_BAR };
enum class DialogViewID : int {
VIEW_ID_NONE = 0,
HEADER_ICON,
PROGRESS_BAR,
TITLE,
MERCHANT_LABEL,
MERCHANT_VALUE,
INSTRUMENT_LABEL,
INSTRUMENT_VALUE,
INSTRUMENT_ICON,
TOTAL_LABEL,
TOTAL_VALUE
};
explicit SecurePaymentConfirmationDialogView(
ObserverForTest* observer_for_test);
......@@ -68,6 +80,17 @@ class SecurePaymentConfirmationDialogView
void InitChildViews();
std::unique_ptr<views::View> CreateHeaderView();
std::unique_ptr<views::View> CreateBodyView();
std::unique_ptr<views::View> CreateRows();
std::unique_ptr<views::View> CreateRowView(
const base::string16& label,
DialogViewID label_id,
const base::string16& value,
DialogViewID value_id,
const SkBitmap* icon = nullptr,
DialogViewID icon_id = DialogViewID::VIEW_ID_NONE);
void UpdateLabelView(DialogViewID id, const base::string16& text);
// May be null.
ObserverForTest* observer_for_test_ = nullptr;
......@@ -77,6 +100,13 @@ class SecurePaymentConfirmationDialogView
views::ProgressBar* progress_bar_ = nullptr;
// Cache the instrument icon pointer so we don't needlessly update it in
// OnModelUpdated().
const SkBitmap* instrument_icon_ = nullptr;
// Cache the instrument icon generation ID to check if the instrument_icon_
// has changed pixels.
uint32_t instrument_icon_generation_id_ = 0;
base::WeakPtrFactory<SecurePaymentConfirmationDialogView> weak_ptr_factory_{
this};
};
......
......@@ -13,8 +13,23 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/base_event_utils.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
namespace payments {
namespace {
constexpr int kInstrumentIconWidth = 32;
constexpr int kInstrumentIconHeight = 20;
const SkBitmap CreateInstrumentIcon(SkColor color) {
SkBitmap bitmap;
bitmap.allocN32Pixels(kInstrumentIconWidth, kInstrumentIconHeight);
bitmap.eraseColor(color);
return bitmap;
}
} // namespace
class SecurePaymentConfirmationDialogViewTest
: public InProcessBrowserTest,
......@@ -30,6 +45,24 @@ class SecurePaymentConfirmationDialogViewTest
}
void CreateModel() {
model_.set_title(l10n_util::GetStringUTF16(
IDS_SECURE_PAYMENT_CONFIRMATION_VERIFY_PURCHASE));
model_.set_merchant_label(
l10n_util::GetStringUTF16(IDS_SECURE_PAYMENT_CONFIRMATION_STORE_LABEL));
model_.set_merchant_value(base::UTF8ToUTF16("merchant.com"));
model_.set_instrument_label(l10n_util::GetStringUTF16(
IDS_PAYMENT_REQUEST_PAYMENT_METHOD_SECTION_NAME));
model_.set_instrument_value(base::UTF8ToUTF16("Mastercard ****4444"));
instrument_icon_ =
std::make_unique<SkBitmap>(CreateInstrumentIcon(SK_ColorBLUE));
model_.set_instrument_icon(instrument_icon_.get());
model_.set_total_label(
l10n_util::GetStringUTF16(IDS_SECURE_PAYMENT_CONFIRMATION_TOTAL_LABEL));
model_.set_total_value(base::UTF8ToUTF16("$20.00 USD"));
model_.set_verify_button_label(l10n_util::GetStringUTF16(
IDS_SECURE_PAYMENT_CONFIRMATION_VERIFY_BUTTON_LABEL));
model_.set_cancel_button_label(l10n_util::GetStringUTF16(IDS_CANCEL));
......@@ -54,6 +87,15 @@ class SecurePaymentConfirmationDialogViewTest
EXPECT_TRUE(web_contents_modal_dialog_manager->IsDialogActive());
}
void ExpectLabelText(
const base::string16& text,
SecurePaymentConfirmationDialogView::DialogViewID view_id) {
EXPECT_EQ(text, static_cast<views::Label*>(
test_delegate_->dialog_view()->GetViewByID(
static_cast<int>(view_id)))
->GetText());
}
void ExpectViewMatchesModel() {
ASSERT_NE(test_delegate_->dialog_view(), nullptr);
......@@ -65,15 +107,50 @@ class SecurePaymentConfirmationDialogViewTest
test_delegate_->dialog_view()->GetDialogButtonLabel(
ui::DIALOG_BUTTON_CANCEL));
EXPECT_TRUE(test_delegate_->dialog_view()->GetViewByID(
SecurePaymentConfirmationDialogView::DialogViewID::HEADER_ICON));
EXPECT_TRUE(test_delegate_->dialog_view()->GetViewByID(static_cast<int>(
SecurePaymentConfirmationDialogView::DialogViewID::HEADER_ICON)));
EXPECT_EQ(
model_.progress_bar_visible(),
test_delegate_->dialog_view()
->GetViewByID(
SecurePaymentConfirmationDialogView::DialogViewID::PROGRESS_BAR)
->GetViewByID(static_cast<int>(SecurePaymentConfirmationDialogView::
DialogViewID::PROGRESS_BAR))
->GetVisible());
ExpectLabelText(model_.title(),
SecurePaymentConfirmationDialogView::DialogViewID::TITLE);
ExpectLabelText(
model_.merchant_label(),
SecurePaymentConfirmationDialogView::DialogViewID::MERCHANT_LABEL);
ExpectLabelText(
model_.merchant_value(),
SecurePaymentConfirmationDialogView::DialogViewID::MERCHANT_VALUE);
ExpectLabelText(
model_.instrument_label(),
SecurePaymentConfirmationDialogView::DialogViewID::INSTRUMENT_LABEL);
ExpectLabelText(
model_.instrument_value(),
SecurePaymentConfirmationDialogView::DialogViewID::INSTRUMENT_VALUE);
ASSERT_EQ(instrument_icon_.get(), model_.instrument_icon());
EXPECT_TRUE(cc::MatchesBitmap(
*model_.instrument_icon(),
*(static_cast<views::ImageView*>(
test_delegate_->dialog_view()->GetViewByID(
static_cast<int>(SecurePaymentConfirmationDialogView::
DialogViewID::INSTRUMENT_ICON)))
->GetImage()
.bitmap()),
cc::ExactPixelComparator(/*discard_alpha=*/false)));
ExpectLabelText(
model_.total_label(),
SecurePaymentConfirmationDialogView::DialogViewID::TOTAL_LABEL);
ExpectLabelText(
model_.total_value(),
SecurePaymentConfirmationDialogView::DialogViewID::TOTAL_VALUE);
}
void ClickAcceptAndWait() {
......@@ -133,6 +210,8 @@ class SecurePaymentConfirmationDialogViewTest
std::unique_ptr<TestSecurePaymentConfirmationPaymentRequestDelegate>
test_delegate_;
std::unique_ptr<SkBitmap> instrument_icon_;
bool confirm_pressed_ = false;
bool cancel_pressed_ = false;
};
......@@ -200,4 +279,54 @@ IN_PROC_BROWSER_TEST_F(SecurePaymentConfirmationDialogViewTest,
CloseDialogAndWait();
}
IN_PROC_BROWSER_TEST_F(SecurePaymentConfirmationDialogViewTest,
OnModelUpdated) {
CreateModel();
InvokeSecurePaymentConfirmationUI();
ExpectViewMatchesModel();
model_.set_title(base::UTF8ToUTF16("Test Title"));
model_.set_merchant_label(base::UTF8ToUTF16("Test merchant"));
model_.set_merchant_value(base::UTF8ToUTF16("Test merchant value"));
model_.set_instrument_label(base::UTF8ToUTF16("Test instrument"));
model_.set_instrument_value(base::UTF8ToUTF16("Test instrument value"));
model_.set_total_label(base::UTF8ToUTF16("Test total"));
model_.set_total_value(base::UTF8ToUTF16("Test total value"));
model_.set_verify_button_label(base::UTF8ToUTF16("Test verify"));
model_.set_cancel_button_label(base::UTF8ToUTF16("Test cancel"));
test_delegate_->dialog_view()->OnModelUpdated();
ExpectViewMatchesModel();
CloseDialogAndWait();
}
// Test the two reasons an instrument icon is updated: The model's bitmap
// pointer changed, or the bitmap itself changed.
IN_PROC_BROWSER_TEST_F(SecurePaymentConfirmationDialogViewTest,
InstrumentIconUpdated) {
CreateModel();
InvokeSecurePaymentConfirmationUI();
ExpectViewMatchesModel();
// Change the bitmap pointer
instrument_icon_ =
std::make_unique<SkBitmap>(CreateInstrumentIcon(SK_ColorGREEN));
model_.set_instrument_icon(instrument_icon_.get());
test_delegate_->dialog_view()->OnModelUpdated();
ExpectViewMatchesModel();
// Change the bitmap itself without touching the model's pointer
*instrument_icon_ = CreateInstrumentIcon(SK_ColorRED);
test_delegate_->dialog_view()->OnModelUpdated();
ExpectViewMatchesModel();
CloseDialogAndWait();
}
} // namespace payments
......@@ -34,6 +34,19 @@ void SecurePaymentConfirmationController::ShowDialog(
model_.set_cancel_button_label(l10n_util::GetStringUTF16(IDS_CANCEL));
model_.set_progress_bar_visible(false);
model_.set_title(l10n_util::GetStringUTF16(
IDS_SECURE_PAYMENT_CONFIRMATION_VERIFY_PURCHASE));
// TODO(crbug/1110322): Set the field values based on |request|.
model_.set_merchant_label(
l10n_util::GetStringUTF16(IDS_SECURE_PAYMENT_CONFIRMATION_STORE_LABEL));
model_.set_instrument_label(l10n_util::GetStringUTF16(
IDS_PAYMENT_REQUEST_PAYMENT_METHOD_SECTION_NAME));
model_.set_total_label(
l10n_util::GetStringUTF16(IDS_SECURE_PAYMENT_CONFIRMATION_TOTAL_LABEL));
view_->ShowDialog(
request->web_contents(), model_.GetWeakPtr(),
base::BindOnce(&SecurePaymentConfirmationController::OnConfirm,
......
......@@ -126,7 +126,7 @@ class SecurePaymentConfirmationModel {
base::string16 instrument_label_;
base::string16 instrument_value_;
const SkBitmap* instrument_icon_;
const SkBitmap* instrument_icon_ = nullptr;
base::string16 total_label_;
base::string16 total_value_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment