Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6839)

Unified Diff: chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.cc

Issue 327743002: Re-styled the App Info Dialog according to UI feedback (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.cc
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2e3719c48c451e704c8200933bba28fd590a721b
--- /dev/null
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.cc
@@ -0,0 +1,457 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.h"
+
+#include <string>
+#include <vector>
+
+#include "apps/app_load_service.h"
+#include "apps/app_restore_service.h"
+#include "apps/saved_files_service.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/launch_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/permissions/api_permission.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/combobox_model.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/text_constants.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/combobox/combobox.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/layout_constants.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+// Create a heading label with the given text.
+views::Label* CreateHeading(const base::string16& text) {
+ views::Label* label = new views::Label(text);
+ label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
+ ui::ResourceBundle::MediumFont));
+ return label;
+}
+
+// Create a view with a vertically-stacked box layout, which can have child
+// views appended to it.
+views::View* CreateVerticalStack() {
+ views::View* vertically_stacked_view = new views::View();
+ vertically_stacked_view->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kVertical,
+ 0,
+ 0,
+ views::kRelatedControlVerticalSpacing));
+ return vertically_stacked_view;
+}
+
+// Given a list of strings, returns a view containing a list of these strings
+// as bulleted items.
+views::View* CreateBulletedListView(
+ const std::vector<base::string16>& messages) {
+ const int kSpacingBetweenBulletAndStartOfText = 5;
+ views::View* list_view = CreateVerticalStack();
+
+ for (std::vector<base::string16>::const_iterator it = messages.begin();
+ it != messages.end();
+ ++it) {
+ views::Label* permission_label = new views::Label(*it);
+ permission_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ permission_label->SetMultiLine(true);
+
+ // Extract only the bullet from the IDS_EXTENSION_PERMISSION_LINE text, and
+ // place it in it's own view so it doesn't align vertically with the
+ // multilined permissions text.
+ views::Label* bullet_label = new views::Label(l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PERMISSION_LINE, base::string16()));
+ views::View* bullet_label_top_aligned = CreateVerticalStack();
+ bullet_label_top_aligned->AddChildView(bullet_label);
+
+ // Place the bullet and the text so all permissions line up at the bullet.
+ views::View* bulleted_list_item = new views::View();
+ bulleted_list_item->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kHorizontal,
+ 0,
+ 0,
+ kSpacingBetweenBulletAndStartOfText));
+ bulleted_list_item->AddChildView(bullet_label_top_aligned);
+ bulleted_list_item->AddChildView(permission_label);
+
+ list_view->AddChildView(bulleted_list_item);
+ }
+
+ return list_view;
+}
+
+} // namespace
+
+// A model for a combobox selecting the launch options for a hosted app.
+// Displays different options depending on the host OS.
+class LaunchOptionsComboboxModel : public ui::ComboboxModel {
+ public:
+ LaunchOptionsComboboxModel();
+ virtual ~LaunchOptionsComboboxModel();
+
+ extensions::LaunchType GetLaunchTypeAtIndex(int index) const;
+ int GetIndexForLaunchType(extensions::LaunchType launch_type) const;
+
+ // Overridden from ui::ComboboxModel:
+ virtual int GetItemCount() const OVERRIDE;
+ virtual base::string16 GetItemAt(int index) OVERRIDE;
+
+ private:
+ // A list of the launch types available in the combobox, in order.
+ std::vector<extensions::LaunchType> launch_types_;
+
+ // A list of the messages to display in the combobox, in order. The indexes in
+ // this list correspond to the indexes in launch_types_.
+ std::vector<base::string16> launch_type_messages_;
+};
+
+LaunchOptionsComboboxModel::LaunchOptionsComboboxModel() {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableStreamlinedHostedApps)) {
+ // Streamlined hosted apps can only toggle between LAUNCH_TYPE_WINDOW and
+ // LAUNCH_TYPE_REGULAR.
+ // TODO(sashab): Use a checkbox for this choice instead of combobox.
+ launch_types_.push_back(extensions::LAUNCH_TYPE_REGULAR);
+ launch_type_messages_.push_back(
+ l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_TAB));
+
+ // Although LAUNCH_TYPE_WINDOW doesn't work on Mac, the streamlined hosted
+ // apps flag isn't available on Mac, so we must be on a non-Mac OS.
+ launch_types_.push_back(extensions::LAUNCH_TYPE_WINDOW);
+ launch_type_messages_.push_back(
+ l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
+ } else {
+ launch_types_.push_back(extensions::LAUNCH_TYPE_REGULAR);
+ launch_type_messages_.push_back(
+ l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_REGULAR));
+
+ launch_types_.push_back(extensions::LAUNCH_TYPE_PINNED);
+ launch_type_messages_.push_back(
+ l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_PINNED));
+
+#if defined(OS_MACOSX)
+ // Mac does not support standalone web app browser windows or maximize.
+ launch_types_.push_back(extensions::LAUNCH_TYPE_FULLSCREEN);
+ launch_type_messages_.push_back(
+ l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN));
+#else
+ launch_types_.push_back(extensions::LAUNCH_TYPE_WINDOW);
+ launch_type_messages_.push_back(
+ l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
+
+ // Even though the launch type is Full Screen, it is more accurately
+ // described as Maximized in non-Mac OSs.
+ launch_types_.push_back(extensions::LAUNCH_TYPE_FULLSCREEN);
+ launch_type_messages_.push_back(
+ l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED));
+#endif
+ }
+}
+
+LaunchOptionsComboboxModel::~LaunchOptionsComboboxModel() {
+}
+
+extensions::LaunchType LaunchOptionsComboboxModel::GetLaunchTypeAtIndex(
+ int index) const {
+ return launch_types_[index];
+}
+
+int LaunchOptionsComboboxModel::GetIndexForLaunchType(
+ extensions::LaunchType launch_type) const {
+ for (size_t i = 0; i < launch_types_.size(); i++) {
+ if (launch_types_[i] == launch_type) {
+ return i;
+ }
+ }
+ // If the requested launch type is not available, just select the first one.
+ LOG(WARNING) << "Unavailable launch type " << launch_type << " selected.";
+ return 0;
+}
+
+int LaunchOptionsComboboxModel::GetItemCount() const {
+ return launch_types_.size();
+}
+
+base::string16 LaunchOptionsComboboxModel::GetItemAt(int index) {
+ return launch_type_messages_[index];
+}
+
+AppInfoDetailsPanel::AppInfoDetailsPanel(gfx::NativeWindow parent_window,
+ Profile* profile,
+ const extensions::Extension* app)
+ : parent_window_(parent_window),
+ profile_(profile),
+ app_(app),
+ description_heading_(NULL),
+ description_label_(NULL),
+ active_permissions_heading_(NULL),
+ active_permissions_list_(NULL),
+ retained_files_heading_(NULL),
+ retained_files_list_(NULL),
+ revoke_file_permissions_button_(NULL),
+ launch_options_combobox_(NULL) {
+ // Create UI elements.
+ CreateDescriptionControl();
+ CreateActivePermissionsControl();
+ CreateRetainedFilesControl();
+ CreateLaunchOptionControl();
+ CreateShortcutsButton();
+
+ // Layout elements.
+ SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kVertical,
+ views::kButtonHEdgeMarginNew,
+ views::kPanelVertMargin,
+ views::kUnrelatedControlVerticalSpacing));
+
+ LayoutDescriptionControl();
+
+ if (launch_options_combobox_)
+ AddChildView(launch_options_combobox_);
+
+ LayoutActivePermissionsControl();
+ LayoutRetainedFilesControl();
+ LayoutShortcutsButton();
+}
+
+AppInfoDetailsPanel::~AppInfoDetailsPanel() {
+ // Destroy view children before their models.
+ RemoveAllChildViews(true);
+}
+
+void AppInfoDetailsPanel::CreateDescriptionControl() {
+ if (!app_->description().empty()) {
+ const size_t kMaxLength = 400;
+
+ base::string16 text = base::UTF8ToUTF16(app_->description());
+ if (text.length() > kMaxLength) {
+ text = text.substr(0, kMaxLength);
+ text += base::ASCIIToUTF16(" ... ");
+ }
+
+ description_heading_ = CreateHeading(
+ l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_DESCRIPTION_TITLE));
+
+ description_label_ = new views::Label(text);
+ description_label_->SetMultiLine(true);
+ description_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ }
+}
+
+void AppInfoDetailsPanel::CreateActivePermissionsControl() {
+ std::vector<base::string16> permission_strings =
+ GetActivePermissionMessages();
+ if (permission_strings.empty()) {
+ views::Label* no_permissions_text = new views::Label(
+ l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_NO_PERMISSIONS_TEXT));
+ no_permissions_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ active_permissions_list_ = no_permissions_text;
+ } else {
+ active_permissions_heading_ = CreateHeading(l10n_util::GetStringUTF16(
+ IDS_APPLICATION_INFO_ACTIVE_PERMISSIONS_TEXT));
+ active_permissions_list_ = CreateBulletedListView(permission_strings);
+ }
+}
+
+void AppInfoDetailsPanel::CreateRetainedFilesControl() {
+ const std::vector<base::string16> retained_file_permission_messages =
+ GetRetainedFilePaths();
+
+ if (!retained_file_permission_messages.empty()) {
+ revoke_file_permissions_button_ = new views::LabelButton(
+ this,
+ l10n_util::GetStringUTF16(
+ IDS_APPLICATION_INFO_REVOKE_RETAINED_FILE_PERMISSIONS_BUTTON_TEXT));
+ revoke_file_permissions_button_->SetStyle(views::Button::STYLE_BUTTON);
+
+ retained_files_heading_ = CreateHeading(l10n_util::GetStringUTF16(
+ IDS_APPLICATION_INFO_RETAINED_FILE_PERMISSIONS_TEXT));
+ retained_files_list_ =
+ CreateBulletedListView(retained_file_permission_messages);
+ }
+}
+
+void AppInfoDetailsPanel::CreateLaunchOptionControl() {
+ if (CanSetLaunchType()) {
+ launch_options_combobox_model_.reset(new LaunchOptionsComboboxModel());
+ launch_options_combobox_ =
+ new views::Combobox(launch_options_combobox_model_.get());
+
+ launch_options_combobox_->set_listener(this);
+ launch_options_combobox_->SetSelectedIndex(
+ launch_options_combobox_model_->GetIndexForLaunchType(GetLaunchType()));
+ }
+}
+
+void AppInfoDetailsPanel::CreateShortcutsButton() {
+ if (CanCreateShortcuts()) {
+ create_shortcuts_button_ = new views::LabelButton(
+ this,
+ l10n_util::GetStringUTF16(
+ IDS_APPLICATION_INFO_CREATE_SHORTCUTS_BUTTON_TEXT));
+ create_shortcuts_button_->SetStyle(views::Button::STYLE_BUTTON);
+ }
+}
+
+void AppInfoDetailsPanel::LayoutDescriptionControl() {
+ if (description_label_) {
+ DCHECK(description_heading_);
+ views::View* vertical_stack = CreateVerticalStack();
+ vertical_stack->AddChildView(description_heading_);
+ vertical_stack->AddChildView(description_label_);
+
+ AddChildView(vertical_stack);
+ }
+}
+
+void AppInfoDetailsPanel::LayoutActivePermissionsControl() {
+ if (active_permissions_list_) {
+ views::View* vertical_stack = CreateVerticalStack();
+ if (active_permissions_heading_)
+ vertical_stack->AddChildView(active_permissions_heading_);
+ vertical_stack->AddChildView(active_permissions_list_);
+
+ AddChildView(vertical_stack);
+ }
+}
+
+void AppInfoDetailsPanel::LayoutRetainedFilesControl() {
+ if (retained_files_list_) {
+ DCHECK(retained_files_heading_);
+ DCHECK(revoke_file_permissions_button_);
+
+ // Add a sub-view so the revoke button is right-aligned.
+ views::View* right_aligned_button = new views::View();
+ views::BoxLayout* right_aligned_horizontal_layout =
+ new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
+ right_aligned_horizontal_layout->set_main_axis_alignment(
+ views::BoxLayout::MAIN_AXIS_ALIGNMENT_END);
+ right_aligned_button->SetLayoutManager(right_aligned_horizontal_layout);
+ right_aligned_button->AddChildView(revoke_file_permissions_button_);
+
+ views::View* vertical_stack = CreateVerticalStack();
+ vertical_stack->AddChildView(retained_files_heading_);
+ vertical_stack->AddChildView(retained_files_list_);
+ vertical_stack->AddChildView(right_aligned_button);
+
+ AddChildView(vertical_stack);
+ }
+}
+
+void AppInfoDetailsPanel::LayoutShortcutsButton() {
+ if (create_shortcuts_button_) {
+ // Add a sub-view so the shortcuts button is left-aligned.
+ views::View* left_aligned_button = new views::View();
+ left_aligned_button->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
+ left_aligned_button->AddChildView(create_shortcuts_button_);
+
+ AddChildView(left_aligned_button);
+ }
+}
+
+void AppInfoDetailsPanel::OnPerformAction(views::Combobox* combobox) {
+ if (combobox == launch_options_combobox_) {
+ SetLaunchType(launch_options_combobox_model_->GetLaunchTypeAtIndex(
+ launch_options_combobox_->selected_index()));
+ } else {
+ NOTREACHED();
+ }
+}
+
+void AppInfoDetailsPanel::ButtonPressed(views::Button* sender,
+ const ui::Event& event) {
+ if (sender == revoke_file_permissions_button_) {
+ RevokeFilePermissions();
+ } else if (sender == create_shortcuts_button_) {
+ CreateShortcuts();
+ } else {
+ NOTREACHED();
+ }
+}
+
+extensions::LaunchType AppInfoDetailsPanel::GetLaunchType() const {
+ return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
+ app_);
+}
+
+void AppInfoDetailsPanel::SetLaunchType(
+ extensions::LaunchType launch_type) const {
+ DCHECK(CanSetLaunchType());
+ ExtensionService* service =
+ extensions::ExtensionSystem::Get(profile_)->extension_service();
+ extensions::SetLaunchType(service, app_->id(), launch_type);
+}
+
+bool AppInfoDetailsPanel::CanSetLaunchType() const {
+ // V2 apps don't have a launch type.
+ return !app_->is_platform_app();
+}
+
+void AppInfoDetailsPanel::CreateShortcuts() {
+ DCHECK(CanCreateShortcuts());
+ chrome::ShowCreateChromeAppShortcutsDialog(
+ parent_window_, profile_, app_, base::Callback<void(bool)>());
+}
+
+bool AppInfoDetailsPanel::CanCreateShortcuts() const {
+// ChromeOS can pin apps to the app launcher, but can't create shortcuts.
+#if defined(OS_CHROMEOS)
+ return false;
+#else
+ return true;
+#endif
+}
+
+const std::vector<base::string16>
+AppInfoDetailsPanel::GetActivePermissionMessages() const {
+ return app_->permissions_data()->GetPermissionMessageStrings();
+}
+
+const std::vector<base::string16> AppInfoDetailsPanel::GetRetainedFilePaths()
+ const {
+ std::vector<base::string16> retained_file_paths;
+ if (app_->permissions_data()->HasAPIPermission(
+ extensions::APIPermission::kFileSystem)) {
+ std::vector<apps::SavedFileEntry> retained_file_entries =
+ apps::SavedFilesService::Get(profile_)->GetAllFileEntries(app_->id());
+ for (std::vector<apps::SavedFileEntry>::const_iterator it =
+ retained_file_entries.begin();
+ it != retained_file_entries.end();
+ ++it) {
+ retained_file_paths.push_back(it->path.LossyDisplayName());
+ }
+ }
+ return retained_file_paths;
+}
+
+void AppInfoDetailsPanel::RevokeFilePermissions() {
+ apps::SavedFilesService::Get(profile_)->ClearQueue(app_);
+
+ // TODO(benwells): Fix this to call something like
+ // AppLoadService::RestartApplicationIfRunning.
+ if (apps::AppRestoreService::Get(profile_)->IsAppRestorable(app_->id()))
+ apps::AppLoadService::Get(profile_)->RestartApplication(app_->id());
+
+ GetWidget()->Close();
+}

Powered by Google App Engine
This is Rietveld 408576698