blob: 05ad12f7fde6f234694de784fffe3e3e6667859f [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// 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/create_application_shortcut_view.h"
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/web_applications/extensions/web_app_extension_shortcut.h"
#include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
#include "chrome/browser/web_applications/os_integration/os_integration_sub_manager.h"
#include "chrome/browser/web_applications/os_integration/web_app_shortcut.h"
#include "chrome/browser/web_applications/web_app_command_scheduler.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/prefs/pref_service.h"
#include "extensions/common/extension.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/shortcut.h"
#include "chrome/installer/util/taskbar_util.h"
#endif // BUILDFLAG(IS_WIN)
namespace chrome {
void ShowCreateChromeAppShortcutsDialog(
gfx::NativeWindow parent_window,
Profile* profile,
const extensions::Extension* app,
base::OnceCallback<void(bool)> close_callback) {
constrained_window::CreateBrowserModalDialogViews(
new CreateChromeApplicationShortcutView(profile, app,
std::move(close_callback)),
parent_window)
->Show();
}
void ShowCreateChromeAppShortcutsDialog(
gfx::NativeWindow parent_window,
Profile* profile,
const std::string& web_app_id,
base::OnceCallback<void(bool)> close_callback) {
constrained_window::CreateBrowserModalDialogViews(
new CreateChromeApplicationShortcutView(profile, web_app_id,
std::move(close_callback)),
parent_window)
->Show();
}
} // namespace chrome
CreateChromeApplicationShortcutView::CreateChromeApplicationShortcutView(
Profile* profile,
const extensions::Extension* app,
base::OnceCallback<void(bool)> close_callback)
: CreateChromeApplicationShortcutView(profile,
/*is_extension=*/true,
std::move(close_callback)) {
// Get shortcut and icon information; needed for creating the shortcut.
web_app::GetShortcutInfoForApp(
app, profile,
base::BindRepeating(&CreateChromeApplicationShortcutView::OnAppInfoLoaded,
weak_ptr_factory_.GetWeakPtr()));
}
CreateChromeApplicationShortcutView::CreateChromeApplicationShortcutView(
Profile* profile,
const std::string& web_app_id,
base::OnceCallback<void(bool)> close_callback)
: CreateChromeApplicationShortcutView(profile,
/*is_extension=*/false,
std::move(close_callback)) {
web_app::WebAppProvider* provider =
web_app::WebAppProvider::GetForWebApps(profile);
provider->os_integration_manager().GetShortcutInfoForAppFromRegistrar(
web_app_id,
base::BindRepeating(&CreateChromeApplicationShortcutView::OnAppInfoLoaded,
weak_ptr_factory_.GetWeakPtr()));
}
CreateChromeApplicationShortcutView::CreateChromeApplicationShortcutView(
Profile* profile,
bool is_extension,
base::OnceCallback<void(bool)> close_callback)
: profile_(profile),
prefs_(profile->GetPrefs()),
is_extension_(is_extension),
close_callback_(std::move(close_callback)) {
SetModalType(ui::mojom::ModalType::kWindow);
SetButtonLabel(ui::mojom::DialogButton::kOk,
l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_COMMIT));
set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
views::DialogContentType::kText, views::DialogContentType::kText));
SetAcceptCallback(
base::BindOnce(&CreateChromeApplicationShortcutView::OnDialogAccepted,
base::Unretained(this)));
auto canceled = [](CreateChromeApplicationShortcutView* dialog) {
if (!dialog->close_callback_.is_null()) {
std::move(dialog->close_callback_).Run(false);
}
};
SetCancelCallback(base::BindOnce(canceled, base::Unretained(this)));
SetCloseCallback(base::BindOnce(canceled, base::Unretained(this)));
InitControls();
}
CreateChromeApplicationShortcutView::~CreateChromeApplicationShortcutView() =
default;
void CreateChromeApplicationShortcutView::InitControls() {
auto create_shortcuts_label = std::make_unique<views::Label>(
l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_LABEL));
create_shortcuts_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
create_shortcuts_label->SetMultiLine(true);
std::unique_ptr<views::Checkbox> desktop_check_box = AddCheckbox(
l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_DESKTOP_CHKBOX),
prefs::kWebAppCreateOnDesktop);
std::unique_ptr<views::Checkbox> menu_check_box;
std::unique_ptr<views::Checkbox> pin_to_taskbar_checkbox;
#if BUILDFLAG(IS_WIN)
menu_check_box = AddCheckbox(
l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_START_MENU_CHKBOX),
prefs::kWebAppCreateInAppsMenu);
// Only include the pin-to-taskbar option when running on versions of Windows
// that support pinning.
if (CanPinShortcutToTaskbar()) {
pin_to_taskbar_checkbox =
AddCheckbox(l10n_util::GetStringUTF16(IDS_PIN_TO_TASKBAR_CHKBOX),
prefs::kWebAppCreateInQuickLaunchBar);
}
#elif BUILDFLAG(IS_POSIX)
menu_check_box =
AddCheckbox(l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_MENU_CHKBOX),
prefs::kWebAppCreateInAppsMenu);
#endif
ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(),
provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));
AddChildView(std::move(create_shortcuts_label));
desktop_check_box_ = AddChildView(std::move(desktop_check_box));
if (menu_check_box) {
menu_check_box_ = AddChildView(std::move(menu_check_box));
}
if (pin_to_taskbar_checkbox) {
quick_launch_check_box_ = AddChildView(std::move(pin_to_taskbar_checkbox));
}
}
gfx::Size CreateChromeApplicationShortcutView::CalculatePreferredSize(
const views::SizeBounds& available_size) const {
static const int kDialogWidth = 360;
int height =
GetLayoutManager()->GetPreferredHeightForWidth(this, kDialogWidth);
return gfx::Size(kDialogWidth, height);
}
bool CreateChromeApplicationShortcutView::IsDialogButtonEnabled(
ui::mojom::DialogButton button) const {
if (button != ui::mojom::DialogButton::kOk) {
return true; // It's always possible to cancel out of creating a shortcut.
}
if (!shortcut_info_) {
return false; // Dialog's not ready because app info hasn't been loaded.
}
// One of the three location checkboxes must be checked:
return desktop_check_box_->GetChecked() ||
(menu_check_box_ && menu_check_box_->GetChecked()) ||
(quick_launch_check_box_ && quick_launch_check_box_->GetChecked());
}
std::u16string CreateChromeApplicationShortcutView::GetWindowTitle() const {
return l10n_util::GetStringUTF16(IDS_CREATE_SHORTCUTS_TITLE);
}
void CreateChromeApplicationShortcutView::OnDialogAccepted() {
DCHECK(IsDialogButtonEnabled(ui::mojom::DialogButton::kOk));
if (!close_callback_.is_null()) {
std::move(close_callback_).Run(/*success=*/shortcut_info_ != nullptr);
}
// Shortcut can't be created because app info hasn't been loaded.
if (!shortcut_info_) {
return;
}
web_app::ShortcutLocations creation_locations;
creation_locations.on_desktop = desktop_check_box_->GetChecked();
if (menu_check_box_ && menu_check_box_->GetChecked()) {
creation_locations.applications_menu_location =
web_app::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS;
}
#if BUILDFLAG(IS_WIN)
creation_locations.in_quick_launch_bar =
quick_launch_check_box_ && quick_launch_check_box_->GetChecked();
#elif BUILDFLAG(IS_POSIX)
// Create shortcut in Mac dock or as Linux (gnome/kde) application launcher
// are not implemented yet.
creation_locations.in_quick_launch_bar = false;
#endif
// If the dialog has been triggered from a web_app, then we need to perform OS
// integration using sub managers so that shortcuts can be properly added,
// updated or deleted. Otherwise, shortcuts created need not be tracked as
// they will not be tied to an app_id.
if (!shortcut_info_->app_id.empty() && !is_extension_) {
auto* provider = web_app::WebAppProvider::GetForWebApps(profile_);
CHECK(provider);
provider->scheduler().SynchronizeOsIntegration(
shortcut_info_->app_id, base::DoNothing(),
web_app::ConvertShortcutLocationsToSynchronizeOptions(
creation_locations, web_app::SHORTCUT_CREATION_BY_USER),
/*upgrade_to_fully_installed_if_installed=*/true);
} else {
web_app::CreateShortcutsWithInfo(web_app::SHORTCUT_CREATION_BY_USER,
creation_locations, base::DoNothing(),
std::move(shortcut_info_));
}
}
std::unique_ptr<views::Checkbox>
CreateChromeApplicationShortcutView::AddCheckbox(const std::u16string& text,
const std::string& pref_path) {
auto checkbox =
std::make_unique<views::Checkbox>(text, views::Button::PressedCallback());
checkbox->SetCallback(base::BindRepeating(
&CreateChromeApplicationShortcutView::CheckboxPressed,
base::Unretained(this), pref_path, base::Unretained(checkbox.get())));
checkbox->SetChecked(prefs_->GetBoolean(pref_path));
return checkbox;
}
void CreateChromeApplicationShortcutView::CheckboxPressed(
std::string pref_path,
views::Checkbox* checkbox) {
prefs_->SetBoolean(pref_path, checkbox->GetChecked());
DialogModelChanged();
}
void CreateChromeApplicationShortcutView::OnAppInfoLoaded(
std::unique_ptr<web_app::ShortcutInfo> shortcut_info) {
// GetShortcutInfoForApp request may return nullptr |shortcut_info| to this
// callback if web app was uninstalled during that asynchronous request.
shortcut_info_ = std::move(shortcut_info);
// This may cause there to be shortcut info when there was none before, so
// make sure the accept button gets enabled.
DialogModelChanged();
}
BEGIN_METADATA(CreateChromeApplicationShortcutView)
END_METADATA