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