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/files/file_util.h"
11 #include "base/i18n/time_formatting.h"
12 #include "base/logging.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/task_runner_util.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/extension_util.h"
17 #include "chrome/browser/extensions/launch_util.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/extensions/extension_constants.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "extensions/browser/extension_prefs.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/manifest.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/models/combobox_model.h"
28 #include "ui/base/text/bytes_formatting.h"
29 #include "ui/views/controls/combobox/combobox.h"
30 #include "ui/views/controls/label.h"
31 #include "ui/views/layout/box_layout.h"
32 #include "ui/views/layout/layout_constants.h"
33 #include "ui/views/view.h"
34 #include "ui/views/widget/widget.h"
36 // A model for a combobox selecting the launch options for a hosted app.
37 // Displays different options depending on the host OS.
38 class LaunchOptionsComboboxModel : public ui::ComboboxModel {
40 LaunchOptionsComboboxModel();
41 virtual ~LaunchOptionsComboboxModel();
43 extensions::LaunchType GetLaunchTypeAtIndex(int index) const;
44 int GetIndexForLaunchType(extensions::LaunchType launch_type) const;
46 // Overridden from ui::ComboboxModel:
47 virtual int GetItemCount() const OVERRIDE;
48 virtual base::string16 GetItemAt(int index) OVERRIDE;
51 // A list of the launch types available in the combobox, in order.
52 std::vector<extensions::LaunchType> launch_types_;
54 // A list of the messages to display in the combobox, in order. The indexes in
55 // this list correspond to the indexes in launch_types_.
56 std::vector<base::string16> launch_type_messages_;
59 LaunchOptionsComboboxModel::LaunchOptionsComboboxModel() {
60 if (extensions::util::IsStreamlinedHostedAppsEnabled()) {
61 // Streamlined hosted apps can only toggle between LAUNCH_TYPE_WINDOW and
62 // LAUNCH_TYPE_REGULAR.
63 // TODO(sashab): Use a checkbox for this choice instead of combobox.
64 launch_types_.push_back(extensions::LAUNCH_TYPE_REGULAR);
65 launch_type_messages_.push_back(
66 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_TAB));
68 // Although LAUNCH_TYPE_WINDOW doesn't work on Mac, the streamlined hosted
69 // apps flag isn't available on Mac, so we must be on a non-Mac OS.
70 launch_types_.push_back(extensions::LAUNCH_TYPE_WINDOW);
71 launch_type_messages_.push_back(
72 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
74 launch_types_.push_back(extensions::LAUNCH_TYPE_REGULAR);
75 launch_type_messages_.push_back(
76 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_REGULAR));
78 launch_types_.push_back(extensions::LAUNCH_TYPE_PINNED);
79 launch_type_messages_.push_back(
80 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_PINNED));
82 #if defined(OS_MACOSX)
83 // Mac does not support standalone web app browser windows or maximize.
84 launch_types_.push_back(extensions::LAUNCH_TYPE_FULLSCREEN);
85 launch_type_messages_.push_back(
86 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN));
88 launch_types_.push_back(extensions::LAUNCH_TYPE_WINDOW);
89 launch_type_messages_.push_back(
90 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
92 // Even though the launch type is Full Screen, it is more accurately
93 // described as Maximized in non-Mac OSs.
94 launch_types_.push_back(extensions::LAUNCH_TYPE_FULLSCREEN);
95 launch_type_messages_.push_back(
96 l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED));
101 LaunchOptionsComboboxModel::~LaunchOptionsComboboxModel() {
104 extensions::LaunchType LaunchOptionsComboboxModel::GetLaunchTypeAtIndex(
106 return launch_types_[index];
109 int LaunchOptionsComboboxModel::GetIndexForLaunchType(
110 extensions::LaunchType launch_type) const {
111 for (size_t i = 0; i < launch_types_.size(); i++) {
112 if (launch_types_[i] == launch_type) {
116 // If the requested launch type is not available, just select the first one.
117 LOG(WARNING) << "Unavailable launch type " << launch_type << " selected.";
121 int LaunchOptionsComboboxModel::GetItemCount() const {
122 return launch_types_.size();
125 base::string16 LaunchOptionsComboboxModel::GetItemAt(int index) {
126 return launch_type_messages_[index];
129 AppInfoSummaryPanel::AppInfoSummaryPanel(Profile* profile,
130 const extensions::Extension* app)
131 : AppInfoPanel(profile, app),
132 description_heading_(NULL),
133 description_label_(NULL),
134 details_heading_(NULL),
137 version_title_(NULL),
138 version_value_(NULL),
139 installed_time_title_(NULL),
140 installed_time_value_(NULL),
141 last_run_time_title_(NULL),
142 last_run_time_value_(NULL),
143 launch_options_combobox_(NULL),
144 weak_ptr_factory_(this) {
145 // Create UI elements.
146 CreateDescriptionControl();
147 CreateDetailsControl();
148 CreateLaunchOptionControl();
152 new views::BoxLayout(views::BoxLayout::kVertical,
155 views::kUnrelatedControlVerticalSpacing));
157 LayoutDescriptionControl();
158 LayoutDetailsControl();
160 if (launch_options_combobox_)
161 AddChildView(launch_options_combobox_);
164 AppInfoSummaryPanel::~AppInfoSummaryPanel() {
165 // Destroy view children before their models.
166 RemoveAllChildViews(true);
169 void AppInfoSummaryPanel::CreateDescriptionControl() {
170 if (!app_->description().empty()) {
171 const size_t kMaxLength = 400;
173 base::string16 text = base::UTF8ToUTF16(app_->description());
174 if (text.length() > kMaxLength) {
175 text = text.substr(0, kMaxLength);
176 text += base::ASCIIToUTF16(" ... ");
179 description_heading_ = CreateHeading(
180 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_DESCRIPTION_TITLE));
181 description_label_ = new views::Label(text);
182 description_label_->SetMultiLine(true);
183 description_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
187 void AppInfoSummaryPanel::CreateDetailsControl() {
188 // The size doesn't make sense for component apps.
189 if (app_->location() != extensions::Manifest::COMPONENT) {
190 size_title_ = new views::Label(
191 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_SIZE_LABEL));
192 size_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
194 size_value_ = new views::Label(
195 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_SIZE_LOADING_LABEL));
196 size_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
198 StartCalculatingAppSize();
201 // The version doesn't make sense for bookmark apps.
202 if (!app_->from_bookmark()) {
203 // Display 'Version: Built-in' for component apps.
204 base::string16 version_str = base::ASCIIToUTF16(app_->VersionString());
205 if (app_->location() == extensions::Manifest::COMPONENT)
206 version_str = l10n_util::GetStringUTF16(
207 IDS_APPLICATION_INFO_VERSION_BUILT_IN_LABEL);
209 version_title_ = new views::Label(
210 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_VERSION_LABEL));
211 version_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
213 version_value_ = new views::Label(version_str);
214 version_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
217 // The install date doesn't make sense for component apps.
218 if (app_->location() != extensions::Manifest::COMPONENT) {
219 installed_time_title_ = new views::Label(
220 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_INSTALLED_LABEL));
221 installed_time_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
223 installed_time_value_ =
224 new views::Label(base::TimeFormatShortDate(GetInstalledTime()));
225 installed_time_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
228 // The last run time is currently incorrect for component and hosted apps,
229 // since it is not updated when they are accessed outside of their shortcuts.
230 // TODO(sashab): Update the run time for these correctly: crbug.com/398716
231 if (app_->location() != extensions::Manifest::COMPONENT &&
232 !app_->is_hosted_app()) {
233 last_run_time_title_ = new views::Label(
234 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_LAST_RUN_LABEL));
235 last_run_time_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
237 // Display 'Never' if the app has never been run.
238 base::string16 last_run_value_str =
239 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_LAST_RUN_NEVER_LABEL);
240 if (GetLastLaunchedTime() != base::Time())
241 last_run_value_str = base::TimeFormatShortDate(GetLastLaunchedTime());
243 last_run_time_value_ = new views::Label(last_run_value_str);
244 last_run_time_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
247 // Only generate the heading if we have at least one field to display.
248 if (version_title_ || installed_time_title_ || last_run_time_title_) {
249 details_heading_ = CreateHeading(
250 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_DETAILS_TITLE));
254 void AppInfoSummaryPanel::CreateLaunchOptionControl() {
255 if (CanSetLaunchType()) {
256 launch_options_combobox_model_.reset(new LaunchOptionsComboboxModel());
257 launch_options_combobox_ =
258 new views::Combobox(launch_options_combobox_model_.get());
260 launch_options_combobox_->set_listener(this);
261 launch_options_combobox_->SetSelectedIndex(
262 launch_options_combobox_model_->GetIndexForLaunchType(GetLaunchType()));
266 void AppInfoSummaryPanel::LayoutDescriptionControl() {
267 if (description_label_) {
268 DCHECK(description_heading_);
269 views::View* vertical_stack = CreateVerticalStack();
270 vertical_stack->AddChildView(description_heading_);
271 vertical_stack->AddChildView(description_label_);
272 AddChildView(vertical_stack);
276 void AppInfoSummaryPanel::LayoutDetailsControl() {
277 if (details_heading_) {
278 views::View* details_stack =
279 CreateVerticalStack(views::kRelatedControlSmallVerticalSpacing);
281 if (version_title_ && version_value_) {
282 details_stack->AddChildView(
283 CreateKeyValueField(version_title_, version_value_));
286 if (installed_time_title_ && installed_time_value_) {
287 details_stack->AddChildView(
288 CreateKeyValueField(installed_time_title_, installed_time_value_));
291 if (last_run_time_title_ && last_run_time_value_) {
292 details_stack->AddChildView(
293 CreateKeyValueField(last_run_time_title_, last_run_time_value_));
296 if (size_title_ && size_value_) {
297 details_stack->AddChildView(
298 CreateKeyValueField(size_title_, size_value_));
301 views::View* vertical_stack = CreateVerticalStack();
302 vertical_stack->AddChildView(details_heading_);
303 vertical_stack->AddChildView(details_stack);
304 AddChildView(vertical_stack);
308 void AppInfoSummaryPanel::OnPerformAction(views::Combobox* combobox) {
309 if (combobox == launch_options_combobox_) {
310 SetLaunchType(launch_options_combobox_model_->GetLaunchTypeAtIndex(
311 launch_options_combobox_->selected_index()));
317 void AppInfoSummaryPanel::StartCalculatingAppSize() {
318 base::PostTaskAndReplyWithResult(
319 content::BrowserThread::GetBlockingPool(),
321 base::Bind(&base::ComputeDirectorySize, app_->path()),
322 base::Bind(&AppInfoSummaryPanel::OnAppSizeCalculated, AsWeakPtr()));
325 void AppInfoSummaryPanel::OnAppSizeCalculated(int64 app_size_in_bytes) {
326 size_value_->SetText(ui::FormatBytes(app_size_in_bytes));
329 base::Time AppInfoSummaryPanel::GetInstalledTime() const {
330 return extensions::ExtensionPrefs::Get(profile_)->GetInstallTime(app_->id());
333 base::Time AppInfoSummaryPanel::GetLastLaunchedTime() const {
334 return extensions::ExtensionPrefs::Get(profile_)
335 ->GetLastLaunchTime(app_->id());
338 extensions::LaunchType AppInfoSummaryPanel::GetLaunchType() const {
339 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
343 void AppInfoSummaryPanel::SetLaunchType(
344 extensions::LaunchType launch_type) const {
345 DCHECK(CanSetLaunchType());
346 ExtensionService* service =
347 extensions::ExtensionSystem::Get(profile_)->extension_service();
348 extensions::SetLaunchType(service, app_->id(), launch_type);
351 bool AppInfoSummaryPanel::CanSetLaunchType() const {
352 // V2 apps don't have a launch type, and neither does the Chrome app.
353 return app_->id() != extension_misc::kChromeAppId && !app_->is_platform_app();