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