1 // Copyright 2016 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/app_list/arc/arc_app_dialog.h"
6 
7 #include "base/bind.h"
8 #include "base/macros.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
12 #include "chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.h"
13 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
14 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
15 #include "chrome/browser/ui/app_list/arc/arc_usb_host_permission_manager.h"
16 #include "chrome/browser/ui/browser_dialogs.h"
17 #include "chrome/browser/ui/views/chrome_layout_provider.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "components/constrained_window/constrained_window_views.h"
20 #include "components/strings/grit/components_strings.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/ui_base_types.h"
23 #include "ui/gfx/geometry/insets.h"
24 #include "ui/views/controls/image_view.h"
25 #include "ui/views/controls/label.h"
26 #include "ui/views/layout/box_layout.h"
27 #include "ui/views/view.h"
28 #include "ui/views/widget/widget.h"
29 #include "ui/views/window/dialog_delegate.h"
30 
31 namespace arc {
32 
33 namespace {
34 
35 const int kArcAppIconSize = 64;
36 // Currenty ARC apps only support 48*48 native icon.
37 const int kIconSourceSize = 48;
38 
39 using ArcAppConfirmCallback = base::OnceCallback<void(bool accept)>;
40 
41 class ArcAppDialogView : public views::DialogDelegateView,
42                          public AppIconLoaderDelegate {
43  public:
44   ArcAppDialogView(Profile* profile,
45                    AppListControllerDelegate* controller,
46                    const std::string& app_id,
47                    const base::string16& window_title,
48                    const base::string16& heading_text,
49                    const base::string16& subheading_text,
50                    const base::string16& confirm_button_text,
51                    const base::string16& cancel_button_text,
52                    ArcAppConfirmCallback confirm_callback);
53   ~ArcAppDialogView() override;
54 
55   // Public method used for test only.
56   void ConfirmOrCancelForTest(bool confirm);
57 
58  private:
59   // AppIconLoaderDelegate:
60   void OnAppImageUpdated(const std::string& app_id,
61                          const gfx::ImageSkia& image) override;
62 
63   void AddMultiLineLabel(views::View* parent, const base::string16& label_text);
64 
65   void OnDialogAccepted();
66   void OnDialogCancelled();
67 
68   views::ImageView* icon_view_ = nullptr;
69 
70   std::unique_ptr<AppServiceAppIconLoader> icon_loader_;
71 
72   Profile* const profile_;
73 
74   const std::string app_id_;
75   ArcAppConfirmCallback confirm_callback_;
76 
77   DISALLOW_COPY_AND_ASSIGN(ArcAppDialogView);
78 };
79 
80 // Browsertest use only. Global pointer of currently shown ArcAppDialogView.
81 ArcAppDialogView* g_current_arc_app_dialog_view = nullptr;
82 
ArcAppDialogView(Profile * profile,AppListControllerDelegate * controller,const std::string & app_id,const base::string16 & window_title,const base::string16 & heading_text,const base::string16 & subheading_text,const base::string16 & confirm_button_text,const base::string16 & cancel_button_text,ArcAppConfirmCallback confirm_callback)83 ArcAppDialogView::ArcAppDialogView(Profile* profile,
84                                    AppListControllerDelegate* controller,
85                                    const std::string& app_id,
86                                    const base::string16& window_title,
87                                    const base::string16& heading_text,
88                                    const base::string16& subheading_text,
89                                    const base::string16& confirm_button_text,
90                                    const base::string16& cancel_button_text,
91                                    ArcAppConfirmCallback confirm_callback)
92     : profile_(profile),
93       app_id_(app_id),
94       confirm_callback_(std::move(confirm_callback)) {
95   SetTitle(window_title);
96   SetButtonLabel(ui::DIALOG_BUTTON_OK, confirm_button_text);
97   SetButtonLabel(ui::DIALOG_BUTTON_CANCEL, cancel_button_text);
98   SetAcceptCallback(base::BindOnce(&ArcAppDialogView::OnDialogAccepted,
99                                    base::Unretained(this)));
100   SetCancelCallback(base::BindOnce(&ArcAppDialogView::OnDialogCancelled,
101                                    base::Unretained(this)));
102 
103   ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
104 
105   SetLayoutManager(std::make_unique<views::BoxLayout>(
106       views::BoxLayout::Orientation::kHorizontal,
107       provider->GetDialogInsetsForContentType(views::TEXT, views::TEXT),
108       provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL)));
109 
110   SetModalType(ui::MODAL_TYPE_WINDOW);
111   SetShowCloseButton(false);
112   set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
113       views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));
114 
115   auto icon_view = std::make_unique<views::ImageView>();
116   icon_view->SetPreferredSize(gfx::Size(kArcAppIconSize, kArcAppIconSize));
117   icon_view_ = AddChildView(std::move(icon_view));
118 
119   auto text_container = std::make_unique<views::View>();
120   auto text_container_layout = std::make_unique<views::BoxLayout>(
121       views::BoxLayout::Orientation::kVertical);
122   text_container_layout->set_main_axis_alignment(
123       views::BoxLayout::MainAxisAlignment::kCenter);
124   text_container_layout->set_cross_axis_alignment(
125       views::BoxLayout::CrossAxisAlignment::kStart);
126   text_container->SetLayoutManager(std::move(text_container_layout));
127 
128   auto* text_container_ptr = AddChildView(std::move(text_container));
129   DCHECK(!heading_text.empty());
130   AddMultiLineLabel(text_container_ptr, heading_text);
131   if (!subheading_text.empty())
132     AddMultiLineLabel(text_container_ptr, subheading_text);
133 
134   // The icon should be loaded asynchronously.
135   icon_loader_ = std::make_unique<AppServiceAppIconLoader>(
136       profile_, kIconSourceSize, this);
137   icon_loader_->FetchImage(app_id_);
138 
139   g_current_arc_app_dialog_view = this;
140   gfx::NativeWindow parent =
141       controller ? controller->GetAppListWindow() : nullptr;
142   constrained_window::CreateBrowserModalDialogViews(this, parent)->Show();
143 
144   chrome::RecordDialogCreation(chrome::DialogIdentifier::ARC_APP);
145 }
146 
~ArcAppDialogView()147 ArcAppDialogView::~ArcAppDialogView() {
148   if (g_current_arc_app_dialog_view == this)
149     g_current_arc_app_dialog_view = nullptr;
150 }
151 
AddMultiLineLabel(views::View * parent,const base::string16 & label_text)152 void ArcAppDialogView::AddMultiLineLabel(views::View* parent,
153                                          const base::string16& label_text) {
154   auto label = std::make_unique<views::Label>(label_text);
155   label->SetMultiLine(true);
156   label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
157   label->SetAllowCharacterBreak(true);
158   parent->AddChildView(std::move(label));
159 }
160 
ConfirmOrCancelForTest(bool confirm)161 void ArcAppDialogView::ConfirmOrCancelForTest(bool confirm) {
162   if (confirm) {
163     AcceptDialog();
164   } else {
165     CancelDialog();
166   }
167 }
168 
OnDialogAccepted()169 void ArcAppDialogView::OnDialogAccepted() {
170   // The dialog can either be accepted or cancelled, but never both.
171   DCHECK(confirm_callback_);
172   std::move(confirm_callback_).Run(true);
173 }
174 
OnDialogCancelled()175 void ArcAppDialogView::OnDialogCancelled() {
176   // The dialog can either be accepted or cancelled, but never both.
177   DCHECK(confirm_callback_);
178   std::move(confirm_callback_).Run(false);
179 }
180 
OnAppImageUpdated(const std::string & app_id,const gfx::ImageSkia & image)181 void ArcAppDialogView::OnAppImageUpdated(const std::string& app_id,
182                                          const gfx::ImageSkia& image) {
183   DCHECK_EQ(app_id, app_id_);
184   DCHECK(!image.isNull());
185   DCHECK_EQ(image.width(), kIconSourceSize);
186   DCHECK_EQ(image.height(), kIconSourceSize);
187   icon_view_->SetImageSize(image.size());
188   icon_view_->SetImage(image);
189 }
190 
GetArcAppInfo(Profile * profile,const std::string & app_id)191 std::unique_ptr<ArcAppListPrefs::AppInfo> GetArcAppInfo(
192     Profile* profile,
193     const std::string& app_id) {
194   ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile);
195   DCHECK(arc_prefs);
196   return arc_prefs->GetApp(app_id);
197 }
198 
199 }  // namespace
200 
ShowUsbScanDeviceListPermissionDialog(Profile * profile,const std::string & app_id,ArcUsbConfirmCallback callback)201 void ShowUsbScanDeviceListPermissionDialog(Profile* profile,
202                                            const std::string& app_id,
203                                            ArcUsbConfirmCallback callback) {
204   std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
205       GetArcAppInfo(profile, app_id);
206   if (!app_info) {
207     std::move(callback).Run(false);
208     return;
209   }
210 
211   base::string16 window_title =
212       l10n_util::GetStringUTF16(IDS_ARC_USB_PERMISSION_TITLE);
213 
214   base::string16 heading_text = l10n_util::GetStringFUTF16(
215       IDS_ARC_USB_SCAN_DEVICE_LIST_PERMISSION_HEADING,
216       base::UTF8ToUTF16(app_info->name));
217 
218   base::string16 confirm_button_text = l10n_util::GetStringUTF16(IDS_OK);
219 
220   base::string16 cancel_button_text = l10n_util::GetStringUTF16(IDS_CANCEL);
221 
222   new ArcAppDialogView(profile, nullptr /* controller */, app_id, window_title,
223                        heading_text, base::string16() /*subheading_text*/,
224                        confirm_button_text, cancel_button_text,
225                        std::move(callback));
226 }
227 
ShowUsbAccessPermissionDialog(Profile * profile,const std::string & app_id,const base::string16 & device_name,ArcUsbConfirmCallback callback)228 void ShowUsbAccessPermissionDialog(Profile* profile,
229                                    const std::string& app_id,
230                                    const base::string16& device_name,
231                                    ArcUsbConfirmCallback callback) {
232   std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
233       GetArcAppInfo(profile, app_id);
234   if (!app_info) {
235     std::move(callback).Run(false);
236     return;
237   }
238 
239   base::string16 window_title =
240       l10n_util::GetStringUTF16(IDS_ARC_USB_PERMISSION_TITLE);
241 
242   base::string16 heading_text = l10n_util::GetStringFUTF16(
243       IDS_ARC_USB_ACCESS_PERMISSION_HEADING, base::UTF8ToUTF16(app_info->name));
244 
245   base::string16 subheading_text = device_name;
246 
247   base::string16 confirm_button_text = l10n_util::GetStringUTF16(IDS_OK);
248 
249   base::string16 cancel_button_text = l10n_util::GetStringUTF16(IDS_CANCEL);
250 
251   new ArcAppDialogView(profile, nullptr /* controller */, app_id, window_title,
252                        heading_text, subheading_text, confirm_button_text,
253                        cancel_button_text, std::move(callback));
254 }
255 
IsArcAppDialogViewAliveForTest()256 bool IsArcAppDialogViewAliveForTest() {
257   return g_current_arc_app_dialog_view != nullptr;
258 }
259 
CloseAppDialogViewAndConfirmForTest(bool confirm)260 bool CloseAppDialogViewAndConfirmForTest(bool confirm) {
261   if (!g_current_arc_app_dialog_view)
262     return false;
263 
264   g_current_arc_app_dialog_view->ConfirmOrCancelForTest(confirm);
265   return true;
266 }
267 
268 }  // namespace arc
269