Commit 1f5e1d17 authored by nancy's avatar nancy Committed by Commit Bot

Add PauseApps and RecoverApps interfaces in AppServiceProxy.

Design doc: go/appservice-for-per-app-time-limit-design-doc

This CL implements the interface:
PauseApps
RecoverApps

This CL also implements the App Pause Dialog. To make AppServiceProxy
more clean, PauseApps's parameter "PauseData" is defined in
PauseDialog, as that is used for the App Pause Dialog.

The App Pause Dialog should be similar to the Uninstall Dialog.

Based on the requirement, the App Pause Dialog should be implemented
for the ARC running apps only. The app running checking will be added
in a separate CL.

BUG=1011235

Change-Id: If2811f9722ce18494418f3a1c7ff0f12c951b599
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1899629
Commit-Queue: Nancy Wang <nancylingwang@chromium.org>
Reviewed-by: default avatarBret Sepulveda <bsep@chromium.org>
Reviewed-by: default avatarAga Wronska <agawronska@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#714489}
parent 29d349eb
......@@ -10255,6 +10255,32 @@ Please help our engineers fix this problem. Tell us what happened right before y
Verify
</message>
<!-- App pause prompt -->
<message name="IDS_APP_PAUSE_PROMPT_TITLE" desc="Titlebar of the app pause prompt window">
App paused
</message>
<message name="IDS_APP_PAUSE_HEADING_HOURS_AND_MINUTES" desc="Tells the user the app will be paused.">
The limit on "<ph name="APP_NAME">$1<ex>Google Photos</ex></ph>" that your parent set ran out. You can use it for <ph name="HOURS">$2<ex>2 hours</ex></ph> <ph name="MINUTES">$3<ex>30 minutes</ex></ph> tomorrow.
</message>
<message name="IDS_APP_PAUSE_HEADING_HOURS_ONLY" desc="Tells the user the app will be paused.">
The limit on "<ph name="APP_NAME">$1<ex>Google Photos</ex></ph>" that your parent set ran out. You can use it for <ph name="HOURS">$2<ex>2 hours</ex></ph> tomorrow.
</message>
<message name="IDS_APP_PAUSE_HEADING_MINUTES_ONLY" desc="Tells the user the app will be paused.">
The limit on "<ph name="APP_NAME">$1<ex>Google Photos</ex></ph>" that your parent set ran out. You can use it for <ph name="MINUTES">$2<ex>30 minutes</ex></ph> tomorrow.
</message>
<message name="IDS_APP_PAUSE_HEADING_HOURS" desc="Tells the user the app can be run how many hours. [ICU Syntax]">
{HOURS, plural,
=1 {# hour}
other {# hours}}
</message>
<message name="IDS_APP_PAUSE_HEADING_MINUTES" desc="Tells the user the app can be run how many minutes. [ICU Syntax]">
{MINUTES, plural,
=1 {# minute}
other {# minutes}}
</message>
</messages>
</release>
</grit>
b9d8a5bb225403e83014694d7c8693d652003cb8
\ No newline at end of file
ac9f6657699ab90f5ed2c65d5de7b67e4fdc7be8
\ No newline at end of file
8b21c8dff4948451dc44ca53f6c3ab4d4b676921
\ No newline at end of file
b9d8a5bb225403e83014694d7c8693d652003cb8
\ No newline at end of file
19bdab026c688357ed9f0275d0b795fc6f0cb23a
\ No newline at end of file
5b713d24b0c6a493d8cbe52a3bb7e5127290b587
\ No newline at end of file
......@@ -280,6 +280,50 @@ void AppServiceProxy::OnUninstallDialogClosed(
uninstall_dialogs_.erase(it);
}
void AppServiceProxy::PauseApps(
const std::map<std::string, PauseData>& pause_data) {
if (!app_service_.is_connected())
return;
for (auto& data : pause_data) {
apps::mojom::AppType app_type = cache_.GetAppType(data.first);
constexpr bool kPaused = true;
UpdatePausedStatus(app_type, data.first, kPaused);
// TODO(crbug.com/1011235): Add the app running checking. If the app is not
// running, don't create the pause dialog, pause the app directly.
if (app_type != apps::mojom::AppType::kArc) {
// TODO(crbug.com/1011235): Add AppService interface to apply icon
// effects.
continue;
}
cache_.ForOneApp(data.first, [this, &data](const apps::AppUpdate& update) {
this->LoadIconForPauseDialog(update, data.second);
});
}
}
void AppServiceProxy::UnpauseApps(const std::set<std::string>& app_ids) {
if (!app_service_.is_connected())
return;
for (auto& app_id : app_ids) {
apps::mojom::AppType app_type = cache_.GetAppType(app_id);
constexpr bool kPaused = false;
UpdatePausedStatus(app_type, app_id, kPaused);
// TODO(crbug.com/1011235): Add AppService interface to recover icon
// effects.
}
}
void AppServiceProxy::OnPauseDialogClosed(apps::mojom::AppType app_type,
const std::string& app_id) {
// TODO(crbug.com/1011235): Add AppService interface to apply the icon effect
// and stop the running app.
}
void AppServiceProxy::OpenNativeSettings(const std::string& app_id) {
if (app_service_.is_connected()) {
cache_.ForOneApp(app_id, [this](const apps::AppUpdate& update) {
......@@ -404,4 +448,49 @@ void AppServiceProxy::InitializePreferredApps(base::Value preferred_apps) {
std::make_unique<base::Value>(std::move(preferred_apps)));
}
void AppServiceProxy::LoadIconForPauseDialog(const apps::AppUpdate& update,
const PauseData& pause_data) {
apps::mojom::IconKeyPtr icon_key = update.IconKey();
constexpr bool kAllowPlaceholderIcon = false;
constexpr int32_t kPauseIconSize = 48;
LoadIconFromIconKey(
update.AppType(), update.AppId(), std::move(icon_key),
apps::mojom::IconCompression::kUncompressed, kPauseIconSize,
kAllowPlaceholderIcon,
base::BindOnce(&AppServiceProxy::OnLoadIconForPauseDialog,
weak_ptr_factory_.GetWeakPtr(), update.AppType(),
update.AppId(), update.Name(), pause_data));
}
void AppServiceProxy::OnLoadIconForPauseDialog(
apps::mojom::AppType app_type,
const std::string& app_id,
const std::string& app_name,
const PauseData& pause_data,
apps::mojom::IconValuePtr icon_value) {
if (icon_value->icon_compression !=
apps::mojom::IconCompression::kUncompressed) {
OnPauseDialogClosed(app_type, app_id);
return;
}
AppServiceProxy::CreatePauseDialog(
app_name, icon_value->uncompressed, pause_data,
base::BindOnce(&AppServiceProxy::OnPauseDialogClosed,
weak_ptr_factory_.GetWeakPtr(), app_type, app_id));
}
void AppServiceProxy::UpdatePausedStatus(apps::mojom::AppType app_type,
const std::string& app_id,
bool paused) {
std::vector<apps::mojom::AppPtr> apps;
apps::mojom::AppPtr app = apps::mojom::App::New();
app->app_type = app_type;
app->app_id = app_id;
app->paused = (paused) ? apps::mojom::OptionalBool::kTrue
: apps::mojom::OptionalBool::kFalse;
apps.push_back(std::move(app));
cache_.OnApps(std::move(apps));
}
} // namespace apps
......@@ -5,13 +5,13 @@
#ifndef CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_H_
#define CHROME_BROWSER_APPS_APP_SERVICE_APP_SERVICE_PROXY_H_
#include <map>
#include <memory>
#include <set>
#include "base/containers/unique_ptr_adapters.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/apps/app_service/uninstall_dialog.h"
#include "chrome/services/app_service/public/cpp/app_registry_cache.h"
#include "chrome/services/app_service/public/cpp/icon_cache.h"
#include "chrome/services/app_service/public/cpp/icon_coalescer.h"
......@@ -35,6 +35,12 @@ class Profile;
namespace apps {
class AppServiceImpl;
class UninstallDialog;
struct PauseData {
int hours;
int minutes;
};
// Singleton (per Profile) proxy and cache of an App Service's apps.
//
......@@ -46,6 +52,8 @@ class AppServiceProxy : public KeyedService,
public apps::IconLoader,
public apps::mojom::Subscriber {
public:
using OnPauseDialogClosedCallback = base::OnceCallback<void()>;
explicit AppServiceProxy(Profile* profile);
~AppServiceProxy() override;
......@@ -92,6 +100,23 @@ class AppServiceProxy : public KeyedService,
bool clear_site_data,
bool report_abuse,
UninstallDialog* uninstall_dialog);
// Pauses apps. |pause_data|'s key is the app_id. |pause_data|'s PauseData
// is the time limit setting for the app, which is shown in the pause app
// dialog. AppService sets the paused status directly. If the app is running,
// AppService shows the pause app dialog. Otherwise, AppService applies the
// paused app icon effect directly.
void PauseApps(const std::map<std::string, PauseData>& pause_data);
// Unpauses the apps from the paused status. AppService sets the paused status
// as false directly and removes the paused app icon effect.
void UnpauseApps(const std::set<std::string>& app_ids);
// Called when the user clicks the 'OK' button of the pause app dialog.
// AppService stops the running app and applies the paused app icon effect.
void OnPauseDialogClosed(apps::mojom::AppType app_type,
const std::string& app_id);
void OpenNativeSettings(const std::string& app_id);
void FlushMojoCallsForTesting();
......@@ -170,6 +195,11 @@ class AppServiceProxy : public KeyedService,
apps::IconLoader* overriding_icon_loader_for_testing_;
};
static void CreatePauseDialog(const std::string& app_name,
gfx::ImageSkia image,
const PauseData& pause_data,
OnPauseDialogClosedCallback pause_callback);
void Initialize();
void AddAppIconSource(Profile* profile);
......@@ -184,6 +214,20 @@ class AppServiceProxy : public KeyedService,
apps::mojom::IntentFilterPtr intent_filter) override;
void InitializePreferredApps(base::Value preferred_apps) override;
void LoadIconForPauseDialog(const apps::AppUpdate& update,
const PauseData& pause_data);
// Callback invoked when the icon is loaded.
void OnLoadIconForPauseDialog(apps::mojom::AppType app_type,
const std::string& app_id,
const std::string& app_name,
const PauseData& pause_data,
apps::mojom::IconValuePtr icon_value);
void UpdatePausedStatus(apps::mojom::AppType app_type,
const std::string& app_id,
bool paused);
// This proxy privately owns its instance of the App Service. This should not
// be exposed except through the Mojo interface connected to |app_service_|.
std::unique_ptr<apps::AppServiceImpl> app_service_impl_;
......
......@@ -2627,6 +2627,8 @@ jumbo_static_library("ui") {
"views/apps/app_info_dialog/app_info_permissions_panel.h",
"views/apps/app_info_dialog/app_info_summary_panel.cc",
"views/apps/app_info_dialog/app_info_summary_panel.h",
"views/apps/app_pause_dialog_view.cc",
"views/apps/app_pause_dialog_view.h",
"views/apps/app_uninstall_dialog_view.cc",
"views/apps/app_uninstall_dialog_view.h",
"views/apps/chrome_native_app_window_views.cc",
......
// Copyright 2019 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_pause_dialog_view.h"
#include <memory>
#include <utility>
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
// static
void apps::AppServiceProxy::CreatePauseDialog(
const std::string& app_name,
gfx::ImageSkia image,
const apps::PauseData& pause_data,
apps::AppServiceProxy::OnPauseDialogClosedCallback closed_callback) {
constrained_window::CreateBrowserModalDialogViews(
new AppPauseDialogView(app_name, image, pause_data,
std::move(closed_callback)),
nullptr)
->Show();
}
AppPauseDialogView::AppPauseDialogView(
const std::string& app_name,
gfx::ImageSkia image,
const apps::PauseData& pause_data,
apps::AppServiceProxy::OnPauseDialogClosedCallback closed_callback)
: BubbleDialogDelegateView(nullptr, views::BubbleBorder::NONE),
closed_callback_(std::move(closed_callback)) {
ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
provider->GetDialogInsetsForContentType(views::TEXT, views::TEXT),
provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL)));
auto* icon_view = AddChildView(std::make_unique<views::ImageView>());
icon_view->SetImage(image);
base::string16 pause_hours = l10n_util::GetPluralStringFUTF16(
IDS_APP_PAUSE_HEADING_HOURS, pause_data.hours);
base::string16 pause_minutes = l10n_util::GetPluralStringFUTF16(
IDS_APP_PAUSE_HEADING_MINUTES, pause_data.minutes);
base::string16 heading_text;
if (pause_data.hours != 0 && pause_data.minutes != 0) {
heading_text = l10n_util::GetStringFUTF16(
IDS_APP_PAUSE_HEADING_HOURS_AND_MINUTES, base::UTF8ToUTF16(app_name),
pause_hours, pause_minutes);
} else if (pause_data.hours != 0) {
heading_text =
l10n_util::GetStringFUTF16(IDS_APP_PAUSE_HEADING_HOURS_ONLY,
base::UTF8ToUTF16(app_name), pause_hours);
} else if (pause_data.minutes != 0) {
heading_text =
l10n_util::GetStringFUTF16(IDS_APP_PAUSE_HEADING_MINUTES_ONLY,
base::UTF8ToUTF16(app_name), pause_minutes);
}
auto* label = AddChildView(std::make_unique<views::Label>(heading_text));
label->SetMultiLine(true);
label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
}
AppPauseDialogView::~AppPauseDialogView() = default;
bool AppPauseDialogView::Accept() {
std::move(closed_callback_).Run();
return true;
}
gfx::Size AppPauseDialogView::CalculatePreferredSize() const {
const int default_width = views::LayoutProvider::Get()->GetDistanceMetric(
DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) -
margins().width();
return gfx::Size(default_width, GetHeightForWidth(default_width));
}
int AppPauseDialogView::GetDialogButtons() const {
return ui::DIALOG_BUTTON_OK;
}
ui::ModalType AppPauseDialogView::GetModalType() const {
return ui::MODAL_TYPE_SYSTEM;
}
base::string16 AppPauseDialogView::GetWindowTitle() const {
return l10n_util::GetStringUTF16(IDS_APP_PAUSE_PROMPT_TITLE);
}
bool AppPauseDialogView::ShouldShowCloseButton() const {
return false;
}
// Copyright 2019 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.
#ifndef CHROME_BROWSER_UI_VIEWS_APPS_APP_PAUSE_DIALOG_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_APPS_APP_PAUSE_DIALOG_VIEW_H_
#include "base/macros.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
namespace gfx {
class ImageSkia;
}
// The app pause dialog. Once the user clicks the 'OK' button, this class calls
// the callback to notify AppService, which pauses the app.
class AppPauseDialogView : public views::BubbleDialogDelegateView {
public:
AppPauseDialogView(
const std::string& app_name,
gfx::ImageSkia image,
const apps::PauseData& pause_data,
apps::AppServiceProxy::OnPauseDialogClosedCallback callback);
~AppPauseDialogView() override;
// views::BubbleDialogDelegateView:
bool Accept() override;
gfx::Size CalculatePreferredSize() const override;
int GetDialogButtons() const override;
ui::ModalType GetModalType() const override;
base::string16 GetWindowTitle() const override;
bool ShouldShowCloseButton() const override;
private:
// Callback when the dialog closes after the user has clicked the OK.
apps::AppServiceProxy::OnPauseDialogClosedCallback closed_callback_;
};
#endif // CHROME_BROWSER_UI_VIEWS_APPS_APP_PAUSE_DIALOG_VIEW_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment