Commit 41f48f32 authored by li's avatar li Committed by Commit Bot

Add timer deeplinks for stop timer and add time to timer.

Currently, we are sending text query for the timer notification actions.
Improve it by adding timer deeplinks that trigger AlarmTimerManager APIs.

CQ_INCLUDE_TRYBOTS=luci.chrome.try:linux-chromeos-chrome

Bug: b:119116816
Test: Manual
Change-Id: Ife633a5978f9d0af1a5bef8a70782a7f7d364fa5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1531606
Commit-Queue: Li Lin <llin@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#652864}
parent e0d3ac9f
......@@ -26,6 +26,8 @@ namespace {
constexpr char kTimerNotificationGroupingKey[] = "assistant/timer";
constexpr char kTimerNotificationIdPrefix[] = "assistant/timer";
constexpr base::TimeDelta kOneMin = base::TimeDelta::FromMinutes(1);
// Interval at which alarms/timers are ticked.
constexpr base::TimeDelta kTickInterval = base::TimeDelta::FromSeconds(1);
......@@ -66,6 +68,24 @@ chromeos::assistant::mojom::AssistantNotificationPtr CreateTimerNotification(
const GURL action_url = assistant::util::CreateAssistantQueryDeepLink(
l10n_util::GetStringUTF8(IDS_ASSISTANT_TIMER_NOTIFICATION_STOP_QUERY));
base::Optional<GURL> stop_alarm_timer_action_url;
base::Optional<GURL> add_time_to_timer_action_url;
if (chromeos::assistant::features::IsAlarmTimerManagerEnabled()) {
stop_alarm_timer_action_url = assistant::util::CreateAlarmTimerDeepLink(
assistant::util::AlarmTimerAction::kStopRinging,
/*alarm_timer_id=*/base::nullopt,
/*duration=*/base::nullopt);
add_time_to_timer_action_url = assistant::util::CreateAlarmTimerDeepLink(
assistant::util::AlarmTimerAction::kAddTimeToTimer, alarm_timer.id,
kOneMin);
} else {
stop_alarm_timer_action_url = assistant::util::CreateAssistantQueryDeepLink(
l10n_util::GetStringUTF8(IDS_ASSISTANT_TIMER_NOTIFICATION_STOP_QUERY));
add_time_to_timer_action_url =
assistant::util::CreateAssistantQueryDeepLink(l10n_util::GetStringUTF8(
IDS_ASSISTANT_TIMER_NOTIFICATION_ADD_1_MIN_QUERY));
}
AssistantNotificationPtr notification = AssistantNotification::New();
// If in-Assistant notifications are supported, we'll allow alarm/timer
......@@ -78,26 +98,35 @@ chromeos::assistant::mojom::AssistantNotificationPtr CreateTimerNotification(
notification->title = title;
notification->message = message;
notification->action_url = action_url;
notification->client_id = CreateTimerNotificationId(alarm_timer.id);
notification->grouping_key = kTimerNotificationGroupingKey;
// This notification should be able to wake up the display if it was off.
notification->is_high_priority = true;
if (!stop_alarm_timer_action_url.has_value()) {
LOG(ERROR) << "Can't create stop alarm timer action URL";
return notification;
}
notification->action_url = stop_alarm_timer_action_url.value();
// "STOP" button.
notification->buttons.push_back(AssistantNotificationButton::New(
l10n_util::GetStringUTF8(IDS_ASSISTANT_TIMER_NOTIFICATION_STOP_BUTTON),
action_url));
stop_alarm_timer_action_url.value()));
if (!add_time_to_timer_action_url.has_value()) {
LOG(ERROR) << "Can't create add time to timer action URL";
return notification;
}
// "ADD 1 MIN" button.
notification->buttons.push_back(
chromeos::assistant::mojom::AssistantNotificationButton::New(
l10n_util::GetStringUTF8(
IDS_ASSISTANT_TIMER_NOTIFICATION_ADD_1_MIN_BUTTON),
assistant::util::CreateAssistantQueryDeepLink(
l10n_util::GetStringUTF8(
IDS_ASSISTANT_TIMER_NOTIFICATION_ADD_1_MIN_QUERY))));
add_time_to_timer_action_url.value()));
return notification;
}
......@@ -110,9 +139,11 @@ AssistantAlarmTimerController::AssistantAlarmTimerController(
AssistantController* assistant_controller)
: assistant_controller_(assistant_controller), binding_(this) {
AddModelObserver(this);
assistant_controller_->AddObserver(this);
}
AssistantAlarmTimerController::~AssistantAlarmTimerController() {
assistant_controller_->RemoveObserver(this);
RemoveModelObserver(this);
}
......@@ -223,4 +254,62 @@ void AssistantAlarmTimerController::OnAllAlarmsTimersRemoved() {
}
}
void AssistantAlarmTimerController::SetAssistant(
chromeos::assistant::mojom::Assistant* assistant) {
assistant_ = assistant;
}
void AssistantAlarmTimerController::OnDeepLinkReceived(
assistant::util::DeepLinkType type,
const std::map<std::string, std::string>& params) {
using assistant::util::DeepLinkParam;
using assistant::util::DeepLinkType;
if (type != DeepLinkType::kAlarmTimer)
return;
const base::Optional<assistant::util::AlarmTimerAction>& action =
assistant::util::GetDeepLinkParamAsAlarmTimerAction(params);
if (!action.has_value())
return;
// Timer ID is optional. Only used for adding time to timer.
const base::Optional<std::string>& alarm_timer_id =
assistant::util::GetDeepLinkParam(params, DeepLinkParam::kId);
// Duration is optional. Only used for adding time to timer.
const base::Optional<base::TimeDelta>& duration =
assistant::util::GetDeepLinkParamAsTimeDelta(params,
DeepLinkParam::kDurationMs);
PerformAlarmTimerAction(action.value(), alarm_timer_id, duration);
}
void AssistantAlarmTimerController::PerformAlarmTimerAction(
const assistant::util::AlarmTimerAction& action,
const base::Optional<std::string>& alarm_timer_id,
const base::Optional<base::TimeDelta>& duration) {
DCHECK(assistant_);
switch (action) {
case assistant::util::AlarmTimerAction::kAddTimeToTimer:
if (!alarm_timer_id.has_value() || !duration.has_value()) {
LOG(ERROR) << "Ignore add time to timer action without timer ID or "
<< "duration.";
return;
}
// Verify the timer is ringing.
DCHECK(model_.GetAlarmTimerById(alarm_timer_id.value()));
// LibAssistant doesn't currently support adding time to an ringing timer.
// We'll create a new one with the duration specified. Note that we
// currently only support this deep link for an alarm/timer that is
// ringing.
assistant_->StopAlarmTimerRinging();
assistant_->CreateTimer(duration.value());
break;
case assistant::util::AlarmTimerAction::kStopRinging:
assistant_->StopAlarmTimerRinging();
break;
}
}
} // namespace ash
......@@ -5,6 +5,7 @@
#ifndef ASH_ASSISTANT_ASSISTANT_ALARM_TIMER_CONTROLLER_H_
#define ASH_ASSISTANT_ASSISTANT_ALARM_TIMER_CONTROLLER_H_
#include "ash/assistant/assistant_controller_observer.h"
#include "ash/assistant/model/assistant_alarm_timer_model.h"
#include "ash/assistant/model/assistant_alarm_timer_model_observer.h"
#include "ash/public/interfaces/assistant_controller.mojom.h"
......@@ -14,12 +15,19 @@
namespace ash {
namespace assistant {
namespace util {
enum class AlarmTimerAction;
} // namespace util
} // namespace assistant
class AssistantController;
// The AssistantAlarmTimerController is a sub-controller of AssistantController
// tasked with tracking alarm/timer state and providing alarm/timer APIs.
class AssistantAlarmTimerController
: public mojom::AssistantAlarmTimerController,
public AssistantControllerObserver,
public AssistantAlarmTimerModelObserver {
public:
explicit AssistantAlarmTimerController(
......@@ -48,7 +56,20 @@ class AssistantAlarmTimerController
const std::map<std::string, base::TimeDelta>& times_remaining) override;
void OnAllAlarmsTimersRemoved() override;
// Provides a pointer to the |assistant| owned by AssistantController.
void SetAssistant(chromeos::assistant::mojom::Assistant* assistant);
// AssistantControllerObserver:
void OnDeepLinkReceived(
assistant::util::DeepLinkType type,
const std::map<std::string, std::string>& params) override;
private:
void PerformAlarmTimerAction(
const assistant::util::AlarmTimerAction& action,
const base::Optional<std::string>& alarm_timer_id,
const base::Optional<base::TimeDelta>& duration);
AssistantController* const assistant_controller_; // Owned by Shell.
mojo::Binding<mojom::AssistantAlarmTimerController> binding_;
......@@ -57,6 +78,9 @@ class AssistantAlarmTimerController
base::RepeatingTimer timer_;
// Owned by AssistantController.
chromeos::assistant::mojom::Assistant* assistant_;
int next_timer_id_ = 1;
DISALLOW_COPY_AND_ASSIGN(AssistantAlarmTimerController);
......
......@@ -86,6 +86,7 @@ void AssistantController::SetAssistant(
assistant_ = std::move(assistant);
// Provide reference to sub-controllers.
assistant_alarm_timer_controller_.SetAssistant(assistant_.get());
assistant_interaction_controller_.SetAssistant(assistant_.get());
assistant_notification_controller_.SetAssistant(assistant_.get());
assistant_screen_context_controller_.SetAssistant(assistant_.get());
......@@ -187,6 +188,7 @@ void AssistantController::OnDeepLinkReceived(
Shell::Get()->new_window_controller()->ShowTaskManager();
break;
case DeepLinkType::kUnsupported:
case DeepLinkType::kAlarmTimer:
case DeepLinkType::kLists:
case DeepLinkType::kNotes:
case DeepLinkType::kOnboarding:
......
......@@ -8,6 +8,7 @@
#include <string>
#include "base/macros.h"
#include "base/timer/timer.h"
#include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
......@@ -43,6 +44,8 @@ class TestAssistantService : public chromeos::assistant::mojom::Assistant {
void OnAccessibilityStatusChanged(bool spoken_feedback_enabled) override {}
void SendAssistantFeedback(
chromeos::assistant::mojom::AssistantFeedbackPtr feedback) override {}
void StopAlarmTimerRinging() override {}
void CreateTimer(base::TimeDelta duration) override {}
private:
mojo::Binding<chromeos::assistant::mojom::Assistant> binding_;
......
......@@ -9,6 +9,7 @@
#include "ash/assistant/util/i18n_util.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "net/base/escape.h"
......@@ -25,18 +26,24 @@ namespace {
// server. See more details at go/cros-assistant-deeplink.
constexpr char kActionParamKey[] = "action";
constexpr char kClientIdParamKey[] = "clientId";
constexpr char kDurationMsParamKey[] = "durationMs";
constexpr char kIdParamKey[] = "id";
constexpr char kQueryParamKey[] = "q";
constexpr char kPageParamKey[] = "page";
constexpr char kRelaunchParamKey[] = "relaunch";
constexpr char kSourceParamKey[] = "source";
// Supported alarm/timer action deep link param values.
constexpr char kAddTimeToTimer[] = "addTimeToTimer";
constexpr char kStopAlarmTimerRinging[] = "stopAlarmTimerRinging";
// Supported reminder action deep link param values.
constexpr char kCreateReminder[] = "create";
constexpr char kEditReminder[] = "edit";
// Supported deep link prefixes. These values must be kept in sync with the
// server. See more details at go/cros-assistant-deeplink.
constexpr char kAssistantAlarmTimerPrefix[] = "googleassistant://alarm-timer";
constexpr char kChromeSettingsPrefix[] = "googleassistant://chrome-settings";
constexpr char kAssistantFeedbackPrefix[] = "googleassistant://send-feedback";
constexpr char kAssistantListsPrefix[] = "googleassistant://lists";
......@@ -55,6 +62,42 @@ constexpr char kAssistantWhatsOnMyScreenPrefix[] =
// Utilities -------------------------------------------------------------------
base::Optional<GURL> CreateAlarmTimerDeepLink(
AlarmTimerAction action,
base::Optional<std::string> alarm_timer_id,
base::Optional<base::TimeDelta> duration) {
GURL url = GURL(kAssistantAlarmTimerPrefix);
switch (action) {
case assistant::util::AlarmTimerAction::kAddTimeToTimer:
DCHECK(alarm_timer_id.has_value() && duration.has_value());
if (!alarm_timer_id.has_value() || !duration.has_value())
return base::nullopt;
url = net::AppendOrReplaceQueryParameter(url, kActionParamKey,
kAddTimeToTimer);
break;
case assistant::util::AlarmTimerAction::kStopRinging:
DCHECK(!alarm_timer_id.has_value() && !duration.has_value());
if (alarm_timer_id.has_value() || duration.has_value())
return base::nullopt;
url = net::AppendOrReplaceQueryParameter(url, kActionParamKey,
kStopAlarmTimerRinging);
break;
}
if (alarm_timer_id.has_value()) {
url = net::AppendOrReplaceQueryParameter(url, kIdParamKey,
alarm_timer_id.value());
}
if (duration.has_value()) {
url = net::AppendOrReplaceQueryParameter(
url, kDurationMsParamKey,
base::NumberToString(duration->InMilliseconds()));
}
return url;
}
GURL CreateAssistantQueryDeepLink(const std::string& query) {
return net::AppendOrReplaceQueryParameter(GURL(kAssistantQueryPrefix),
kQueryParamKey, query);
......@@ -98,6 +141,7 @@ base::Optional<std::string> GetDeepLinkParam(
static const std::map<DeepLinkParam, std::string> kDeepLinkParamKeys = {
{DeepLinkParam::kAction, kActionParamKey},
{DeepLinkParam::kClientId, kClientIdParamKey},
{DeepLinkParam::kDurationMs, kDurationMsParamKey},
{DeepLinkParam::kId, kIdParamKey},
{DeepLinkParam::kPage, kPageParamKey},
{DeepLinkParam::kQuery, kQueryParamKey},
......@@ -142,6 +186,7 @@ base::Optional<ReminderAction> GetDeepLinkParamAsRemindersAction(
DeepLinkType GetDeepLinkType(const GURL& url) {
// Map of supported deep link types to their prefixes.
static const std::map<DeepLinkType, std::string> kSupportedDeepLinks = {
{DeepLinkType::kAlarmTimer, kAssistantAlarmTimerPrefix},
{DeepLinkType::kChromeSettings, kChromeSettingsPrefix},
{DeepLinkType::kFeedback, kAssistantFeedbackPrefix},
{DeepLinkType::kLists, kAssistantListsPrefix},
......@@ -163,6 +208,49 @@ DeepLinkType GetDeepLinkType(const GURL& url) {
return DeepLinkType::kUnsupported;
}
base::Optional<AlarmTimerAction> GetDeepLinkParamAsAlarmTimerAction(
const std::map<std::string, std::string>& params) {
const base::Optional<std::string>& action_string_value =
GetDeepLinkParam(params, DeepLinkParam::kAction);
if (!action_string_value.has_value())
return base::nullopt;
if (action_string_value.value() == kAddTimeToTimer)
return AlarmTimerAction::kAddTimeToTimer;
if (action_string_value.value() == kStopAlarmTimerRinging)
return AlarmTimerAction::kStopRinging;
return base::nullopt;
}
base::Optional<int64_t> GetDeepLinkParamAsInt64(
const std::map<std::string, std::string>& params,
DeepLinkParam param) {
const base::Optional<std::string>& value = GetDeepLinkParam(params, param);
if (value.has_value()) {
int64_t result;
if (base::StringToInt64(value.value(), &result))
return result;
}
return base::nullopt;
}
base::Optional<base::TimeDelta> GetDeepLinkParamAsTimeDelta(
const std::map<std::string, std::string>& params,
DeepLinkParam param) {
if (param != DeepLinkParam::kDurationMs)
return base::nullopt;
const base::Optional<int64_t>& duration_ms =
GetDeepLinkParamAsInt64(params, DeepLinkParam::kDurationMs);
if (!duration_ms.has_value())
return base::nullopt;
return base::TimeDelta::FromMilliseconds(duration_ms.value());
}
bool IsDeepLinkType(const GURL& url, DeepLinkType type) {
return GetDeepLinkType(url) == type;
}
......@@ -245,6 +333,7 @@ base::Optional<GURL> GetWebUrl(
case DeepLinkType::kSettings:
return CreateLocalizedGURL(kAssistantSettingsWebUrl);
case DeepLinkType::kUnsupported:
case DeepLinkType::kAlarmTimer:
case DeepLinkType::kChromeSettings:
case DeepLinkType::kFeedback:
case DeepLinkType::kOnboarding:
......
......@@ -10,6 +10,7 @@
#include "base/component_export.h"
#include "base/optional.h"
#include "base/timer/timer.h"
class GURL;
......@@ -20,6 +21,7 @@ namespace util {
// Enumeration of deep link types.
enum class DeepLinkType {
kUnsupported,
kAlarmTimer,
kChromeSettings,
kFeedback,
kLists,
......@@ -37,6 +39,7 @@ enum class DeepLinkType {
enum class DeepLinkParam {
kAction,
kClientId,
kDurationMs,
kId,
kPage,
kQuery,
......@@ -49,6 +52,19 @@ enum class ReminderAction {
kEdit,
};
// Enumeration of deep link parameter alarm/timer action.
enum class AlarmTimerAction {
kAddTimeToTimer,
kStopRinging,
};
// Returns a deep link to perform an alarm/timer action.
COMPONENT_EXPORT(ASSISTANT_UTIL)
base::Optional<GURL> CreateAlarmTimerDeepLink(
AlarmTimerAction action,
base::Optional<std::string> alarm_timer_id,
base::Optional<base::TimeDelta> duration);
// Returns a deep link to send an Assistant query.
COMPONENT_EXPORT(ASSISTANT_UTIL)
GURL CreateAssistantQueryDeepLink(const std::string& query);
......@@ -72,6 +88,13 @@ base::Optional<std::string> GetDeepLinkParam(
const std::map<std::string, std::string>& params,
DeepLinkParam param);
// Returns AlarmTimerAction from the given parameters. If the desired
// parameter is not found or is not an AlarmTimerAction, an empty value is
// returned.
COMPONENT_EXPORT(ASSISTANT_UTIL)
base::Optional<AlarmTimerAction> GetDeepLinkParamAsAlarmTimerAction(
const std::map<std::string, std::string>& params);
// Returns a specific bool |param| from the given parameters. If the desired
// parameter is not found or is not a bool, an empty value is returned.
COMPONENT_EXPORT(ASSISTANT_UTIL)
......@@ -86,6 +109,21 @@ base::Optional<ReminderAction> GetDeepLinkParamAsRemindersAction(
const std::map<std::string, std::string> params,
DeepLinkParam param);
// Returns a specific int64 |param| from the given parameters. If the desired
// parameter is not found or is not an int64, an empty value is returned.
COMPONENT_EXPORT(ASSISTANT_UTIL)
base::Optional<int64_t> GetDeepLinkParamAsInt64(
const std::map<std::string, std::string>& params,
DeepLinkParam param);
// Returns TimeDelta from the given parameters. If the desired parameter is not
// found, can't convert to TimeDelta or not a time type parameter, an empty
// value is returned.
COMPONENT_EXPORT(ASSISTANT_UTIL)
base::Optional<base::TimeDelta> GetDeepLinkParamAsTimeDelta(
const std::map<std::string, std::string>& params,
DeepLinkParam param);
// Returns the deep link type of the specified |url|. If the specified url is
// not a supported deep link, DeepLinkType::kUnsupported is returned.
COMPONENT_EXPORT(ASSISTANT_UTIL) DeepLinkType GetDeepLinkType(const GURL& url);
......
......@@ -1435,6 +1435,21 @@ void AssistantManagerServiceImpl::OnAccessibilityStatusChanged(
UpdateInternalOptions(assistant_manager_internal_);
}
void AssistantManagerServiceImpl::StopAlarmTimerRinging() {
if (!assistant_manager_internal_)
return;
assistant_manager_internal_->GetAlarmTimerManager()->StopRinging();
}
void AssistantManagerServiceImpl::CreateTimer(base::TimeDelta duration) {
if (!assistant_manager_internal_)
return;
assistant_manager_internal_->GetAlarmTimerManager()->CreateTimer(
duration.InSeconds(), /*label=*/std::string());
}
void AssistantManagerServiceImpl::CacheAssistantStructure(
base::OnceClosure on_done,
ax::mojom::AssistantExtraPtr assistant_extra,
......
......@@ -135,6 +135,8 @@ class AssistantManagerServiceImpl
void OnAccessibilityStatusChanged(bool spoken_feedback_enabled) override;
void SendAssistantFeedback(
mojom::AssistantFeedbackPtr assistant_feedback) override;
void StopAlarmTimerRinging() override;
void CreateTimer(base::TimeDelta duration) override;
// AssistantActionObserver overrides:
void OnScheduleWait(int id, int time_ms) override;
......
......@@ -90,5 +90,8 @@ void FakeAssistantManagerServiceImpl::OnAccessibilityStatusChanged(
void FakeAssistantManagerServiceImpl::SendAssistantFeedback(
mojom::AssistantFeedbackPtr feedback) {}
void FakeAssistantManagerServiceImpl::StopAlarmTimerRinging() {}
void FakeAssistantManagerServiceImpl::CreateTimer(base::TimeDelta duration) {}
} // namespace assistant
} // namespace chromeos
......@@ -11,6 +11,7 @@
#include "ash/public/interfaces/assistant_controller.mojom.h"
#include "base/component_export.h"
#include "base/macros.h"
#include "base/timer/timer.h"
#include "chromeos/services/assistant/assistant_manager_service.h"
#include "chromeos/services/assistant/fake_assistant_settings_manager_impl.h"
#include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
......@@ -57,6 +58,8 @@ class COMPONENT_EXPORT(ASSISTANT_SERVICE) FakeAssistantManagerServiceImpl
void ClearScreenContextCache() override;
void OnAccessibilityStatusChanged(bool spoken_feedback_enabled) override;
void SendAssistantFeedback(mojom::AssistantFeedbackPtr feedback) override;
void StopAlarmTimerRinging() override;
void CreateTimer(base::TimeDelta duration) override;
private:
State state_ = State::STOPPED;
......
......@@ -118,7 +118,6 @@ COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC) bool IsTimerTicksEnabled();
COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC) bool IsWarmerWelcomeEnabled();
} // namespace features
} // namespace assistant
} // namespace chromeos
......
......@@ -81,6 +81,13 @@ interface Assistant {
// Send Assistant feedback to Assistant server.
SendAssistantFeedback(AssistantFeedback feedback);
// ===== Alarm/Timer management methods =====
// Stops timer or alarm ringing.
StopAlarmTimerRinging();
// Create timer with |duration|.
CreateTimer(mojo_base.mojom.TimeDelta duration);
};
// Subscribes to Assistant's interaction event. These events are server driven
......
......@@ -62,6 +62,9 @@ class MockAssistant : public mojom::Assistant {
MOCK_METHOD1(SendAssistantFeedback,
void(chromeos::assistant::mojom::AssistantFeedbackPtr));
MOCK_METHOD0(StopAlarmTimerRinging, void());
MOCK_METHOD1(CreateTimer, void(base::TimeDelta));
private:
DISALLOW_COPY_AND_ASSIGN(MockAssistant);
};
......
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