blob: 6fb4f87f956862721243d251884056a3c0ddfa2f [file] [log] [blame]
// Copyright 2020 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 "chromeos/services/assistant/assistant_device_settings_delegate.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "ash/public/cpp/assistant/controller/assistant_notification_controller.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/assistant/internal/internal_util.h"
#include "chromeos/assistant/internal/proto/google3/assistant/api/client_op/device_args.pb.h"
#include "chromeos/services/assistant/public/cpp/assistant_service.h"
#include "chromeos/services/assistant/public/cpp/device_actions.h"
#include "chromeos/services/assistant/public/cpp/migration/cros_platform_api.h"
#include "chromeos/services/assistant/service_context.h"
#include "libassistant/shared/public/platform_audio_output.h"
namespace client_op = ::assistant::api::client_op;
namespace chromeos {
namespace assistant {
class Setting {
public:
Setting() = default;
Setting(Setting&) = delete;
Setting& operator=(Setting&) = delete;
virtual ~Setting() = default;
virtual const char* setting_id() const = 0;
virtual void Modify(const client_op::ModifySettingArgs& request) = 0;
};
namespace {
constexpr char kWiFiDeviceSettingId[] = "WIFI";
constexpr char kBluetoothDeviceSettingId[] = "BLUETOOTH";
constexpr char kScreenBrightnessDeviceSettingId[] = "BRIGHTNESS_LEVEL";
constexpr char kDoNotDisturbDeviceSettingId[] = "DO_NOT_DISTURB";
constexpr char kNightLightDeviceSettingId[] = "NIGHT_LIGHT_SWITCH";
constexpr char kSwitchAccessDeviceSettingId[] = "SWITCH_ACCESS";
constexpr float kDefaultSliderStep = 0.1f;
void LogUnsupportedChange(client_op::ModifySettingArgs args) {
LOG(ERROR) << "Unsupported change operation: " << args.change()
<< " for setting " << args.setting_id();
}
void HandleOnOffChange(client_op::ModifySettingArgs modify_setting_args,
std::function<void(bool)> on_off_handler) {
switch (modify_setting_args.change()) {
case client_op::ModifySettingArgs_Change_ON:
on_off_handler(true);
return;
case client_op::ModifySettingArgs_Change_OFF:
on_off_handler(false);
return;
// Currently there are no use-cases for toggling. This could change in the
// future.
case client_op::ModifySettingArgs_Change_TOGGLE:
break;
case client_op::ModifySettingArgs_Change_SET:
case client_op::ModifySettingArgs_Change_INCREASE:
case client_op::ModifySettingArgs_Change_DECREASE:
case client_op::ModifySettingArgs_Change_UNSPECIFIED:
// This shouldn't happen.
break;
}
LogUnsupportedChange(modify_setting_args);
}
// Helper function that converts a slider value sent from the server, either
// absolute or a delta, from a given unit (e.g., STEP), to a percentage.
double ConvertSliderValueToLevel(double value,
client_op::ModifySettingArgs_Unit unit,
double default_value) {
switch (unit) {
case client_op::ModifySettingArgs_Unit_RANGE:
// "set brightness to 20%".
return value;
case client_op::ModifySettingArgs_Unit_STEP:
// "set brightness to 20". Treat the step as a percentage.
return value / 100.0f;
// Currently, factor (e.g., 'double the brightness') and decibel units
// aren't handled by the backend. This could change in the future.
case client_op::ModifySettingArgs_Unit_FACTOR:
case client_op::ModifySettingArgs_Unit_DECIBEL:
break;
case client_op::ModifySettingArgs_Unit_NATIVE:
case client_op::ModifySettingArgs_Unit_UNKNOWN_UNIT:
// This shouldn't happen.
break;
}
LOG(ERROR) << "Unsupported slider unit: " << unit;
return default_value;
}
void HandleSliderChange(client_op::ModifySettingArgs request,
std::function<void(double)> set_value_handler,
std::function<double()> get_value_handler) {
switch (request.change()) {
case client_op::ModifySettingArgs_Change_SET: {
// For unsupported units, set the value to the current value, for
// visual feedback.
double new_value = ConvertSliderValueToLevel(
request.numeric_value(), request.unit(), get_value_handler());
set_value_handler(new_value);
return;
}
case client_op::ModifySettingArgs_Change_INCREASE:
case client_op::ModifySettingArgs_Change_DECREASE: {
double current_value = get_value_handler();
double step = kDefaultSliderStep;
if (request.numeric_value() != 0.0f) {
// For unsupported units, use the default step percentage.
step = ConvertSliderValueToLevel(request.numeric_value(),
request.unit(), kDefaultSliderStep);
}
double new_value =
(request.change() == client_op::ModifySettingArgs_Change_INCREASE)
? std::min(current_value + step, 1.0)
: std::max(current_value - step, 0.0);
set_value_handler(new_value);
return;
}
case client_op::ModifySettingArgs_Change_ON:
case client_op::ModifySettingArgs_Change_OFF:
case client_op::ModifySettingArgs_Change_TOGGLE:
case client_op::ModifySettingArgs_Change_UNSPECIFIED:
// This shouldn't happen.
break;
}
LogUnsupportedChange(request);
}
class SettingWithDeviceAction : public Setting {
public:
explicit SettingWithDeviceAction(ServiceContext* context)
: context_(context) {}
DeviceActions* device_actions() { return context_->device_actions(); }
private:
ServiceContext* context_;
};
class WifiSetting : public SettingWithDeviceAction {
public:
using SettingWithDeviceAction::SettingWithDeviceAction;
const char* setting_id() const override { return kWiFiDeviceSettingId; }
void Modify(const client_op::ModifySettingArgs& request) override {
HandleOnOffChange(request, [&](bool enabled) {
this->device_actions()->SetWifiEnabled(enabled);
});
}
};
class BluetoothSetting : public SettingWithDeviceAction {
public:
using SettingWithDeviceAction::SettingWithDeviceAction;
const char* setting_id() const override { return kBluetoothDeviceSettingId; }
void Modify(const client_op::ModifySettingArgs& request) override {
HandleOnOffChange(request, [&](bool enabled) {
this->device_actions()->SetBluetoothEnabled(enabled);
});
}
};
class DoNotDisturbSetting : public Setting {
public:
explicit DoNotDisturbSetting(ServiceContext* context) : context_(context) {}
const char* setting_id() const override {
return kDoNotDisturbDeviceSettingId;
}
void Modify(const client_op::ModifySettingArgs& request) override {
HandleOnOffChange(request, [&](bool enabled) {
this->assistant_notification_controller()->SetQuietMode(enabled);
});
}
private:
ash::AssistantNotificationController* assistant_notification_controller() {
return context_->assistant_notification_controller();
}
ServiceContext* context_;
};
class SwitchAccessSetting : public SettingWithDeviceAction {
public:
using SettingWithDeviceAction::SettingWithDeviceAction;
const char* setting_id() const override {
return kSwitchAccessDeviceSettingId;
}
void Modify(const client_op::ModifySettingArgs& request) override {
HandleOnOffChange(request, [&](bool enabled) {
this->device_actions()->SetSwitchAccessEnabled(enabled);
});
}
};
class NightLightSetting : public SettingWithDeviceAction {
public:
using SettingWithDeviceAction::SettingWithDeviceAction;
const char* setting_id() const override { return kNightLightDeviceSettingId; }
void Modify(const client_op::ModifySettingArgs& request) override {
HandleOnOffChange(request, [&](bool enabled) {
this->device_actions()->SetNightLightEnabled(enabled);
});
}
};
class BrightnessSetting : public SettingWithDeviceAction {
public:
explicit BrightnessSetting(ServiceContext* context)
: SettingWithDeviceAction(context), weak_factory_(this) {}
const char* setting_id() const override {
return kScreenBrightnessDeviceSettingId;
}
void Modify(const client_op::ModifySettingArgs& request) override {
device_actions()->GetScreenBrightnessLevel(base::BindOnce(
[](base::WeakPtr<BrightnessSetting> this_,
client_op::ModifySettingArgs request, bool success,
double current_value) {
if (!success || !this_) {
LOG(WARNING) << "Failed to get brightness level";
return;
}
HandleSliderChange(
request,
[&this_](double new_value) {
this_->device_actions()->SetScreenBrightnessLevel(new_value,
true);
},
[current_value]() { return current_value; });
},
weak_factory_.GetWeakPtr(), request));
}
private:
base::WeakPtrFactory<BrightnessSetting> weak_factory_;
};
} // namespace
AssistantDeviceSettingsDelegate::AssistantDeviceSettingsDelegate(
ServiceContext* context) {
AddSetting(std::make_unique<WifiSetting>(context));
AddSetting(std::make_unique<BluetoothSetting>(context));
AddSetting(std::make_unique<NightLightSetting>(context));
AddSetting(std::make_unique<DoNotDisturbSetting>(context));
AddSetting(std::make_unique<BrightnessSetting>(context));
AddSetting(std::make_unique<SwitchAccessSetting>(context));
}
AssistantDeviceSettingsDelegate::~AssistantDeviceSettingsDelegate() = default;
bool AssistantDeviceSettingsDelegate::IsSettingSupported(
const std::string& setting_id) const {
return std::any_of(settings_.begin(), settings_.end(),
[&setting_id](const auto& setting) {
return setting->setting_id() == setting_id;
});
}
void AssistantDeviceSettingsDelegate::HandleModifyDeviceSetting(
const client_op::ModifySettingArgs& modify_setting_args) {
VLOG(1) << "Assistant: Modifying Device Setting '"
<< modify_setting_args.setting_id() << "'";
DCHECK(IsSettingSupported(modify_setting_args.setting_id()));
for (const auto& setting : settings_) {
if (setting->setting_id() == modify_setting_args.setting_id()) {
setting->Modify(modify_setting_args);
return;
}
}
NOTREACHED();
}
std::vector<DeviceSetting> AssistantDeviceSettingsDelegate::GetDeviceSettings(
const ::assistant::api::client_op::GetDeviceSettingsArgs& args) const {
std::vector<DeviceSetting> result;
for (const std::string& setting_id : args.setting_ids())
result.emplace_back(setting_id, IsSettingSupported(setting_id));
return result;
}
void AssistantDeviceSettingsDelegate::AddSetting(
std::unique_ptr<Setting> setting) {
settings_.push_back(std::move(setting));
}
} // namespace assistant
} // namespace chromeos