app_info_summary_panel.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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. 4 5#include "chrome/browser/ui/views/apps/app_info_dialog/app_info_summary_panel.h" 6 7#include <vector> 8 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" 36 37// A model for a combobox selecting the launch options for a hosted app. 38// Displays different options depending on the host OS. 39class LaunchOptionsComboboxModel : public ui::ComboboxModel { 40 public: 41 LaunchOptionsComboboxModel(); 42 virtual ~LaunchOptionsComboboxModel(); 43 44 extensions::LaunchType GetLaunchTypeAtIndex(int index) const; 45 int GetIndexForLaunchType(extensions::LaunchType launch_type) const; 46 47 // Overridden from ui::ComboboxModel: 48 virtual int GetItemCount() const OVERRIDE; 49 virtual base::string16 GetItemAt(int index) OVERRIDE; 50 51 private: 52 // A list of the launch types available in the combobox, in order. 53 std::vector<extensions::LaunchType> launch_types_; 54 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_; 58}; 59 60LaunchOptionsComboboxModel::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)); 69 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)); 75 } else { 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)); 79 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)); 83 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)); 89#else 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)); 93 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)); 99#endif 100 } 101} 102 103LaunchOptionsComboboxModel::~LaunchOptionsComboboxModel() { 104} 105 106extensions::LaunchType LaunchOptionsComboboxModel::GetLaunchTypeAtIndex( 107 int index) const { 108 return launch_types_[index]; 109} 110 111int 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) { 115 return i; 116 } 117 } 118 // If the requested launch type is not available, just select the first one. 119 LOG(WARNING) << "Unavailable launch type " << launch_type << " selected."; 120 return 0; 121} 122 123int LaunchOptionsComboboxModel::GetItemCount() const { 124 return launch_types_.size(); 125} 126 127base::string16 LaunchOptionsComboboxModel::GetItemAt(int index) { 128 return launch_type_messages_[index]; 129} 130 131AppInfoSummaryPanel::AppInfoSummaryPanel(Profile* profile, 132 const extensions::Extension* app) 133 : AppInfoPanel(profile, app), 134 description_heading_(NULL), 135 description_label_(NULL), 136 details_heading_(NULL), 137 size_title_(NULL), 138 size_value_(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(); 151 152 // Layout elements. 153 SetLayoutManager( 154 new views::BoxLayout(views::BoxLayout::kVertical, 155 0, 156 0, 157 views::kUnrelatedControlVerticalSpacing)); 158 159 LayoutDescriptionControl(); 160 LayoutDetailsControl(); 161 162 if (launch_options_combobox_) 163 AddChildView(launch_options_combobox_); 164} 165 166AppInfoSummaryPanel::~AppInfoSummaryPanel() { 167 // Destroy view children before their models. 168 RemoveAllChildViews(true); 169} 170 171void AppInfoSummaryPanel::CreateDescriptionControl() { 172 if (!app_->description().empty()) { 173 const size_t kMaxLength = 400; 174 175 base::string16 text = base::UTF8ToUTF16(app_->description()); 176 if (text.length() > kMaxLength) { 177 text = text.substr(0, kMaxLength); 178 text += base::ASCIIToUTF16(" ... "); 179 } 180 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); 186 } 187} 188 189void 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); 195 196 size_value_ = new views::Label( 197 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_SIZE_LOADING_LABEL)); 198 size_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 199 200 StartCalculatingAppSize(); 201 } 202 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); 210 211 version_title_ = new views::Label( 212 l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_VERSION_LABEL)); 213 version_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 214 215 version_value_ = new views::Label(version_str); 216 version_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 217 } 218 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); 224 225 installed_time_value_ = 226 new views::Label(base::TimeFormatShortDate(GetInstalledTime())); 227 installed_time_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 228 } 229 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); 238 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()); 244 245 last_run_time_value_ = new views::Label(last_run_value_str); 246 last_run_time_value_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 247 } 248 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)); 253 } 254} 255 256void 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()); 261 262 launch_options_combobox_->set_listener(this); 263 launch_options_combobox_->SetSelectedIndex( 264 launch_options_combobox_model_->GetIndexForLaunchType(GetLaunchType())); 265 } 266} 267 268void 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); 275 } 276} 277 278void AppInfoSummaryPanel::LayoutDetailsControl() { 279 if (details_heading_) { 280 views::View* details_stack = 281 CreateVerticalStack(views::kRelatedControlSmallVerticalSpacing); 282 283 if (version_title_ && version_value_) { 284 details_stack->AddChildView( 285 CreateKeyValueField(version_title_, version_value_)); 286 } 287 288 if (installed_time_title_ && installed_time_value_) { 289 details_stack->AddChildView( 290 CreateKeyValueField(installed_time_title_, installed_time_value_)); 291 } 292 293 if (last_run_time_title_ && last_run_time_value_) { 294 details_stack->AddChildView( 295 CreateKeyValueField(last_run_time_title_, last_run_time_value_)); 296 } 297 298 if (size_title_ && size_value_) { 299 details_stack->AddChildView( 300 CreateKeyValueField(size_title_, size_value_)); 301 } 302 303 views::View* vertical_stack = CreateVerticalStack(); 304 vertical_stack->AddChildView(details_heading_); 305 vertical_stack->AddChildView(details_stack); 306 AddChildView(vertical_stack); 307 } 308} 309 310void AppInfoSummaryPanel::OnPerformAction(views::Combobox* combobox) { 311 if (combobox == launch_options_combobox_) { 312 SetLaunchType(launch_options_combobox_model_->GetLaunchTypeAtIndex( 313 launch_options_combobox_->selected_index())); 314 } else { 315 NOTREACHED(); 316 } 317} 318 319void AppInfoSummaryPanel::StartCalculatingAppSize() { 320 base::PostTaskAndReplyWithResult( 321 content::BrowserThread::GetBlockingPool(), 322 FROM_HERE, 323 base::Bind(&base::ComputeDirectorySize, app_->path()), 324 base::Bind(&AppInfoSummaryPanel::OnAppSizeCalculated, AsWeakPtr())); 325} 326 327void AppInfoSummaryPanel::OnAppSizeCalculated(int64 app_size_in_bytes) { 328 size_value_->SetText(ui::FormatBytes(app_size_in_bytes)); 329} 330 331base::Time AppInfoSummaryPanel::GetInstalledTime() const { 332 return extensions::ExtensionPrefs::Get(profile_)->GetInstallTime(app_->id()); 333} 334 335base::Time AppInfoSummaryPanel::GetLastLaunchedTime() const { 336 return extensions::ExtensionPrefs::Get(profile_) 337 ->GetLastLaunchTime(app_->id()); 338} 339 340extensions::LaunchType AppInfoSummaryPanel::GetLaunchType() const { 341 return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_), 342 app_); 343} 344 345void 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); 351} 352 353bool 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(); 356} 357