1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/views/payments/payment_method_view_controller.h"
6
7 #include <memory>
8 #include <utility>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/callback_forward.h"
13 #include "base/callback_helpers.h"
14 #include "base/strings/string16.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/ui/views/chrome_typography.h"
17 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
18 #include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
19 #include "chrome/browser/ui/views/payments/payment_request_row_view.h"
20 #include "chrome/browser/ui/views/payments/payment_request_views_util.h"
21 #include "components/payments/content/autofill_payment_app.h"
22 #include "components/payments/content/payment_app.h"
23 #include "components/payments/content/payment_request_state.h"
24 #include "components/payments/core/strings_util.h"
25 #include "components/strings/grit/components_strings.h"
26 #include "third_party/skia/include/core/SkColor.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/gfx/geometry/insets.h"
29 #include "ui/gfx/paint_vector_icon.h"
30 #include "ui/native_theme/native_theme.h"
31 #include "ui/views/border.h"
32 #include "ui/views/controls/button/label_button.h"
33 #include "ui/views/controls/button/md_text_button.h"
34 #include "ui/views/layout/box_layout.h"
35 #include "ui/views/layout/fill_layout.h"
36 #include "ui/views/layout/grid_layout.h"
37 #include "ui/views/vector_icons.h"
38
39 namespace payments {
40
41 namespace {
42
43 class PaymentMethodListItem : public PaymentRequestItemList::Item {
44 public:
45 // Does not take ownership of |app|, which should not be null and should
46 // outlive this object. |list| is the PaymentRequestItemList object that will
47 // own this.
PaymentMethodListItem(base::WeakPtr<PaymentApp> app,base::WeakPtr<PaymentRequestSpec> spec,base::WeakPtr<PaymentRequestState> state,PaymentRequestItemList * list,base::WeakPtr<PaymentRequestDialogView> dialog,bool selected)48 PaymentMethodListItem(base::WeakPtr<PaymentApp> app,
49 base::WeakPtr<PaymentRequestSpec> spec,
50 base::WeakPtr<PaymentRequestState> state,
51 PaymentRequestItemList* list,
52 base::WeakPtr<PaymentRequestDialogView> dialog,
53 bool selected)
54 : PaymentRequestItemList::Item(
55 spec,
56 state,
57 list,
58 selected,
59 /*clickable=*/true,
60 /*show_edit_button=*/app->type() == PaymentApp::Type::AUTOFILL),
61 app_(app),
62 dialog_(dialog) {
63 Init();
64 }
~PaymentMethodListItem()65 ~PaymentMethodListItem() override {}
66
67 private:
ShowEditor()68 void ShowEditor() {
69 if (!app_)
70 return;
71
72 switch (app_->type()) {
73 case PaymentApp::Type::AUTOFILL:
74 // Since we are a list item, we only care about the on_edited callback.
75 dialog_->ShowCreditCardEditor(
76 BackNavigationType::kPaymentSheet,
77 /*on_edited=*/
78 base::BindOnce(&PaymentRequestState::SetSelectedApp, state(), app_),
79 /*on_added=*/
80 base::OnceCallback<void(const autofill::CreditCard&)>(),
81 static_cast<AutofillPaymentApp*>(app_.get())->credit_card());
82 return;
83 case PaymentApp::Type::UNDEFINED:
84 // Intentionally fall through.
85 case PaymentApp::Type::NATIVE_MOBILE_APP:
86 // Intentionally fall through.
87 case PaymentApp::Type::SERVICE_WORKER_APP:
88 // Intentionally fall through.
89 case PaymentApp::Type::INTERNAL:
90 // We cannot edit these types of payment apps.
91 return;
92 }
93 NOTREACHED();
94 }
95
96 // PaymentRequestItemList::Item:
CreateExtraView()97 std::unique_ptr<views::View> CreateExtraView() override {
98 return app_ ? CreateAppIconView(app_->icon_resource_id(),
99 app_->icon_bitmap(), app_->GetLabel())
100 : nullptr;
101 }
102
CreateContentView(base::string16 * accessible_content)103 std::unique_ptr<views::View> CreateContentView(
104 base::string16* accessible_content) override {
105 DCHECK(accessible_content);
106 auto card_info_container = std::make_unique<views::View>();
107 if (!app_)
108 return card_info_container;
109
110 card_info_container->SetCanProcessEventsWithinSubtree(false);
111
112 auto box_layout = std::make_unique<views::BoxLayout>(
113 views::BoxLayout::Orientation::kVertical,
114 gfx::Insets(kPaymentRequestRowVerticalInsets, 0));
115 box_layout->set_cross_axis_alignment(
116 views::BoxLayout::CrossAxisAlignment::kStart);
117 card_info_container->SetLayoutManager(std::move(box_layout));
118
119 base::string16 label = app_->GetLabel();
120 if (!label.empty())
121 card_info_container->AddChildView(new views::Label(label));
122 base::string16 sublabel = app_->GetSublabel();
123 if (!sublabel.empty())
124 card_info_container->AddChildView(new views::Label(sublabel));
125 base::string16 missing_info;
126 if (!app_->IsCompleteForPayment()) {
127 missing_info = app_->GetMissingInfoLabel();
128 auto missing_info_label = std::make_unique<views::Label>(
129 missing_info, CONTEXT_DIALOG_BODY_TEXT_SMALL);
130 missing_info_label->SetEnabledColor(
131 missing_info_label->GetNativeTheme()->GetSystemColor(
132 ui::NativeTheme::kColorId_LinkEnabled));
133 card_info_container->AddChildView(missing_info_label.release());
134 }
135
136 *accessible_content = l10n_util::GetStringFUTF16(
137 IDS_PAYMENTS_PROFILE_LABELS_ACCESSIBLE_FORMAT, label, sublabel,
138 missing_info);
139
140 return card_info_container;
141 }
142
SelectedStateChanged()143 void SelectedStateChanged() override {
144 if (app_ && selected()) {
145 state()->SetSelectedApp(app_);
146 dialog_->GoBack();
147 }
148 }
149
GetNameForDataType()150 base::string16 GetNameForDataType() override {
151 return l10n_util::GetStringUTF16(IDS_PAYMENTS_METHOD_OF_PAYMENT_LABEL);
152 }
153
CanBeSelected()154 bool CanBeSelected() override {
155 // If an app can't be selected because it's not complete,
156 // PerformSelectionFallback is called, where the app can be made complete.
157 // This applies only to AutofillPaymentApp, each one of which is a credit
158 // card, so PerformSelectionFallback will open the card editor.
159 return app_ && app_->IsCompleteForPayment();
160 }
161
PerformSelectionFallback()162 void PerformSelectionFallback() override { ShowEditor(); }
163
EditButtonPressed()164 void EditButtonPressed() override { ShowEditor(); }
165
166 base::WeakPtr<PaymentApp> app_;
167 base::WeakPtr<PaymentRequestDialogView> dialog_;
168
169 DISALLOW_COPY_AND_ASSIGN(PaymentMethodListItem);
170 };
171
172 } // namespace
173
PaymentMethodViewController(base::WeakPtr<PaymentRequestSpec> spec,base::WeakPtr<PaymentRequestState> state,base::WeakPtr<PaymentRequestDialogView> dialog)174 PaymentMethodViewController::PaymentMethodViewController(
175 base::WeakPtr<PaymentRequestSpec> spec,
176 base::WeakPtr<PaymentRequestState> state,
177 base::WeakPtr<PaymentRequestDialogView> dialog)
178 : PaymentRequestSheetController(spec, state, dialog),
179 payment_method_list_(dialog),
180 enable_add_card_(!state->is_retry_called() &&
181 spec->supports_basic_card()) {
182 const std::vector<std::unique_ptr<PaymentApp>>& available_apps =
183 state->available_apps();
184 for (const auto& app : available_apps) {
185 auto item = std::make_unique<PaymentMethodListItem>(
186 app->AsWeakPtr(), spec, state, &payment_method_list_, dialog,
187 app.get() == state->selected_app());
188 payment_method_list_.AddItem(std::move(item));
189 }
190 }
191
~PaymentMethodViewController()192 PaymentMethodViewController::~PaymentMethodViewController() {}
193
GetSheetTitle()194 base::string16 PaymentMethodViewController::GetSheetTitle() {
195 return l10n_util::GetStringUTF16(
196 IDS_PAYMENT_REQUEST_PAYMENT_METHOD_SECTION_NAME);
197 }
198
FillContentView(views::View * content_view)199 void PaymentMethodViewController::FillContentView(views::View* content_view) {
200 auto layout = std::make_unique<views::BoxLayout>(
201 views::BoxLayout::Orientation::kVertical);
202 layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kStart);
203 layout->set_cross_axis_alignment(
204 views::BoxLayout::CrossAxisAlignment::kStretch);
205 content_view->SetLayoutManager(std::move(layout));
206
207 std::unique_ptr<views::View> list_view =
208 payment_method_list_.CreateListView();
209 list_view->SetID(
210 static_cast<int>(DialogViewID::PAYMENT_METHOD_SHEET_LIST_VIEW));
211 content_view->AddChildView(list_view.release());
212 }
213
ShouldShowPrimaryButton()214 bool PaymentMethodViewController::ShouldShowPrimaryButton() {
215 return false;
216 }
217
ShouldShowSecondaryButton()218 bool PaymentMethodViewController::ShouldShowSecondaryButton() {
219 return enable_add_card_;
220 }
221
GetSecondaryButtonLabel()222 base::string16 PaymentMethodViewController::GetSecondaryButtonLabel() {
223 return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_CARD);
224 }
225
226 views::Button::PressedCallback
GetSecondaryButtonCallback()227 PaymentMethodViewController::GetSecondaryButtonCallback() {
228 return base::BindRepeating(
229 &PaymentRequestDialogView::ShowCreditCardEditor, dialog(),
230 BackNavigationType::kPaymentSheet, base::RepeatingClosure(),
231 base::BindRepeating(&PaymentRequestState::AddAutofillPaymentApp, state(),
232 true),
233 nullptr);
234 }
235
GetSecondaryButtonId()236 int PaymentMethodViewController::GetSecondaryButtonId() {
237 return static_cast<int>(DialogViewID::PAYMENT_METHOD_ADD_CARD_BUTTON);
238 }
239
240 } // namespace payments
241