1 // Copyright 2014 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.
5 #include "chrome/browser/ui/views/apps/app_info_dialog/app_info_summary_panel.h"
9 #include "base/callback_forward.h"
10 #include "base/command_line.h"
11 #include "base/file_util.h"
12 #include "base/i18n/time_formatting.h"
13 #include "base/logging.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/task_runner_util.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/launch_util.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/extensions/extension_constants.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/manifest.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/base/models/combobox_model.h"
29 #include "ui/base/text/bytes_formatting.h"
30 #include "ui/views/controls/combobox/combobox.h"
31 #include "ui/views/controls/label.h"
32 #include "ui/views/layout/box_layout.h"
33 #include "ui/views/layout/layout_constants.h"
34 #include "ui/views/view.h"
35 #include "ui/views/widget/widget.h"
37 // A model for a combobox selecting the launch options for a hosted app.
38 // Displays different options depending on the host OS.
39 class LaunchOptionsComboboxModel : public ui::ComboboxModel {
41 LaunchOptionsComboboxModel();
42 virtual ~LaunchOptionsComboboxModel();
44 extensions::LaunchType GetLaunchTypeAtIndex(int index) const;
45 int GetIndexForLaunchType(extensions::LaunchType launch_type) const;
47 // Overridden from ui::ComboboxModel:
48 virtual int GetItemCount() const OVERRIDE;
49 virtual base::string16 GetItemAt(int index) OVERRIDE;
52 // A list of the launch types available in the combobox, in order.
53 std::vector<extensions::LaunchType> launch_types_;
55 // A list of the messages to display in the combobox, in order. The indexes in
56 // this list correspond to the indexes in launch_types_.
57 std::vector<base::string16> launch_type_messages_;
60 LaunchOptionsComboboxModel::LaunchOptionsComboboxModel() {
61 if (CommandLine::ForCurrentProcess()->HasSwitch(
62 switches::kEnableStreamlinedHostedApps)) {
63 // Streamlined hosted apps can only toggle between LAUNCH_TYPE_WINDOW and
64 // LAUNCH_TYPE_REGULAR.
65 // TODO(sashab): Use a checkbox for this choice instead of combobox.
66 launch_types_.push_back(extensions::LAUNCH_TYPE_REGULAR);
67 launch_type_messages_.push_back(
68 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_TAB));
70 // Although LAUNCH_TYPE_WINDOW doesn't work on Mac, the streamlined hosted
71 // apps flag isn't available on Mac, so we must be on a non-Mac OS.
72 launch_types_.push_back(extensions::LAUNCH_TYPE_WINDOW);
73 launch_type_messages_.push_back(
74 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
76 launch_types_.push_back(extensions::LAUNCH_TYPE_REGULAR);
77 launch_type_messages_.push_back(
78 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_REGULAR));
80 launch_types_.push_back(extensions::LAUNCH_TYPE_PINNED);
81 launch_type_messages_.push_back(
82 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_PINNED));
84 #if defined(OS_MACOSX)
85 // Mac does not support standalone web app browser windows or maximize.
86 launch_types_.push_back(extensions::LAUNCH_TYPE_FULLSCREEN);
87 launch_type_messages_.push_back(
88 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN));
90 launch_types_.push_back(extensions::LAUNCH_TYPE_WINDOW);
91 launch_type_messages_.push_back(
92 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
94 // Even though the launch type is Full Screen, it is more accurately
95 // described as Maximized in non-Mac OSs.
96 launch_types_.push_back(extensions::LAUNCH_TYPE_FULLSCREEN);
97 launch_type_messages_.push_back(
98 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED));
103 LaunchOptionsComboboxModel::~LaunchOptionsComboboxModel() {
106 extensions::LaunchType LaunchOptionsComboboxModel::GetLaunchTypeAtIndex(
108 return launch_types_[index];
111 int LaunchOptionsComboboxModel::GetIndexForLaunchType(
112 extensions::LaunchType launch_type) const {
113 for (size_t i = 0; i < launch_types_.size(); i++) {
114 if (launch_types_[i] == launch_type) {
118 // If the requested launch type is not available, just select the first one.
119 LOG(WARNING) << "Unavailable launch type " << launch_type << " selected.";
123 int LaunchOptionsComboboxModel::GetItemCount() const {
124 return launch_types_.size();
127 base::string16 LaunchOptionsComboboxModel::GetItemAt(int index) {
128 return launch_type_messages_[index];
131 AppInfoSummaryPanel::AppInfoSummaryPanel(Profile* profile,
132 const extensions::Extension* app)
133 : AppInfoPanel(profile, app),
134 description_heading_(NULL),
135 description_label_(NULL),
136 details_heading_(NULL),
139 version_title_(NULL),
140 version_value_(NULL),
141 installed_time_title_(NULL),
142 installed_time_value_(NULL),
143 last_run_time_title_(NULL),
144 last_run_time_value_(NULL),
145 launch_options_combobox_(NULL),
146 weak_ptr_factory_(this) {
147 // Create UI elements.
148 CreateDescriptionControl();
149 CreateDetailsControl();
150 CreateLaunchOptionControl();
154 new views::BoxLayout(views::BoxLayout::kVertical,
157 views::kUnrelatedControlVerticalSpacing));
159 LayoutDescriptionControl();
160 LayoutDetailsControl();
162 if (launch_options_combobox_)
163 AddChildView(launch_options_combobox_);
166 AppInfoSummaryPanel::~AppInfoSummaryPanel() {
167 // Destroy view children before their models.
168 RemoveAllChildViews(true);
171 void AppInfoSummaryPanel::CreateDescriptionControl() {
172 if (!app_->description().empty()) {
173 const size_t kMaxLength = 400;
175 base::string16 text = base::UTF8ToUTF16(app_->description());
176 if (text.length() > kMaxLength) {
177 text = text.substr(0, kMaxLength);
178 text += base::ASCIIToUTF16(" ... ");
181 description_heading_ = CreateHeading(
182 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_DESCRIPTION_TITLE));
183 description_label_ = new views::Label(text);
184 description_label_->SetMultiLine(true);
185 description_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
189 void AppInfoSummaryPanel::CreateDetailsControl() {
190 // The size doesn't make sense for component apps.
191 if (app_->location() != extensions::Manifest::COMPONENT) {
192 size_title_ = new views::Label(
193 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_SIZE_LABEL));
194 size_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
196 size_value_ = new views::Label(
197 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_SIZE_LOADING_LABEL));
198 size_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
200 StartCalculatingAppSize();
203 // The version doesn't make sense for bookmark apps.
204 if (!app_->from_bookmark()) {
205 // Display 'Version: Built-in' for component apps.
206 base::string16 version_str = base::ASCIIToUTF16(app_->VersionString());
207 if (app_->location() == extensions::Manifest::COMPONENT)
208 version_str = l10n_util::GetStringUTF16(
209 IDS_APPLICATION_INFO_VERSION_BUILT_IN_LABEL);
211 version_title_ = new views::Label(
212 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_VERSION_LABEL));
213 version_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
215 version_value_ = new views::Label(version_str);
216 version_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
219 // The install date doesn't make sense for component apps.
220 if (app_->location() != extensions::Manifest::COMPONENT) {
221 installed_time_title_ = new views::Label(
222 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_INSTALLED_LABEL));
223 installed_time_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
225 installed_time_value_ =
226 new views::Label(base::TimeFormatShortDate(GetInstalledTime()));
227 installed_time_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
230 // The last run time is currently incorrect for component and hosted apps,
231 // since it is not updated when they are accessed outside of their shortcuts.
232 // TODO(sashab): Update the run time for these correctly: crbug.com/398716
233 if (app_->location() != extensions::Manifest::COMPONENT &&
234 !app_->is_hosted_app()) {
235 last_run_time_title_ = new views::Label(
236 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_LAST_RUN_LABEL));
237 last_run_time_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
239 // Display 'Never' if the app has never been run.
240 base::string16 last_run_value_str =
241 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_LAST_RUN_NEVER_LABEL);
242 if (GetLastLaunchedTime() != base::Time())
243 last_run_value_str = base::TimeFormatShortDate(GetLastLaunchedTime());
245 last_run_time_value_ = new views::Label(last_run_value_str);
246 last_run_time_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
249 // Only generate the heading if we have at least one field to display.
250 if (version_title_ || installed_time_title_ || last_run_time_title_) {
251 details_heading_ = CreateHeading(
252 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_DETAILS_TITLE));
256 void AppInfoSummaryPanel::CreateLaunchOptionControl() {
257 if (CanSetLaunchType()) {
258 launch_options_combobox_model_.reset(new LaunchOptionsComboboxModel());
259 launch_options_combobox_ =
260 new views::Combobox(launch_options_combobox_model_.get());
262 launch_options_combobox_->set_listener(this);
263 launch_options_combobox_->SetSelectedIndex(
264 launch_options_combobox_model_->GetIndexForLaunchType(GetLaunchType()));
268 void AppInfoSummaryPanel::LayoutDescriptionControl() {
269 if (description_label_) {
270 DCHECK(description_heading_);
271 views::View* vertical_stack = CreateVerticalStack();
272 vertical_stack->AddChildView(description_heading_);
273 vertical_stack->AddChildView(description_label_);
274 AddChildView(vertical_stack);
278 void AppInfoSummaryPanel::LayoutDetailsControl() {
279 if (details_heading_) {
280 views::View* details_stack =
281 CreateVerticalStack(views::kRelatedControlSmallVerticalSpacing);
283 if (version_title_ && version_value_) {
284 details_stack->AddChildView(
285 CreateKeyValueField(version_title_, version_value_));
288 if (installed_time_title_ && installed_time_value_) {
289 details_stack->AddChildView(
290 CreateKeyValueField(installed_time_title_, installed_time_value_));
293 if (last_run_time_title_ && last_run_time_value_) {
294 details_stack->AddChildView(
295 CreateKeyValueField(last_run_time_title_, last_run_time_value_));
298 if (size_title_ && size_value_) {
299 details_stack->AddChildView(
300 CreateKeyValueField(size_title_, size_value_));
303 views::View* vertical_stack = CreateVerticalStack();
304 vertical_stack->AddChildView(details_heading_);
305 vertical_stack->AddChildView(details_stack);
306 AddChildView(vertical_stack);
310 void AppInfoSummaryPanel::OnPerformAction(views::Combobox* combobox) {
311 if (combobox == launch_options_combobox_) {
312 SetLaunchType(launch_options_combobox_model_->GetLaunchTypeAtIndex(
313 launch_options_combobox_->selected_index()));
319 void AppInfoSummaryPanel::StartCalculatingAppSize() {
320 base::PostTaskAndReplyWithResult(
321 content::BrowserThread::GetBlockingPool(),
323 base::Bind(&base::ComputeDirectorySize, app_->path()),
324 base::Bind(&AppInfoSummaryPanel::OnAppSizeCalculated, AsWeakPtr()));
327 void AppInfoSummaryPanel::OnAppSizeCalculated(int64 app_size_in_bytes) {
328 size_value_->SetText(ui::FormatBytes(app_size_in_bytes));
331 base::Time AppInfoSummaryPanel::GetInstalledTime() const {
332 return extensions::ExtensionPrefs::Get(profile_)->GetInstallTime(app_->id());
335 base::Time AppInfoSummaryPanel::GetLastLaunchedTime() const {
336 return extensions::ExtensionPrefs::Get(profile_)
337 ->GetLastLaunchTime(app_->id());
340 extensions::LaunchType AppInfoSummaryPanel::GetLaunchType() const {
341 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
345 void AppInfoSummaryPanel::SetLaunchType(
346 extensions::LaunchType launch_type) const {
347 DCHECK(CanSetLaunchType());
348 ExtensionService* service =
349 extensions::ExtensionSystem::Get(profile_)->extension_service();
350 extensions::SetLaunchType(service, app_->id(), launch_type);
353 bool AppInfoSummaryPanel::CanSetLaunchType() const {
354 // V2 apps don't have a launch type, and neither does the Chrome app.
355 return app_->id() != extension_misc::kChromeAppId && !app_->is_platform_app();