1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chromeos/services/assistant/assistant_device_settings_delegate.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10
11 #include "ash/public/cpp/assistant/controller/assistant_notification_controller.h"
12 #include "base/logging.h"
13 #include "base/memory/weak_ptr.h"
14 #include "chromeos/assistant/internal/internal_util.h"
15 #include "chromeos/assistant/internal/proto/google3/assistant/api/client_op/device_args.pb.h"
16 #include "chromeos/services/assistant/cros_platform_api.h"
17 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
18 #include "chromeos/services/assistant/public/cpp/device_actions.h"
19 #include "chromeos/services/assistant/service_context.h"
20 #include "libassistant/shared/public/platform_audio_output.h"
21
22 namespace client_op = ::assistant::api::client_op;
23
24 namespace chromeos {
25 namespace assistant {
26
27 class Setting {
28 public:
29 Setting() = default;
30 Setting(Setting&) = delete;
31 Setting& operator=(Setting&) = delete;
32 virtual ~Setting() = default;
33
34 virtual const char* setting_id() const = 0;
35 virtual void Modify(const client_op::ModifySettingArgs& request) = 0;
36 };
37
38 namespace {
39
40 constexpr char kWiFiDeviceSettingId[] = "WIFI";
41 constexpr char kBluetoothDeviceSettingId[] = "BLUETOOTH";
42 constexpr char kScreenBrightnessDeviceSettingId[] = "BRIGHTNESS_LEVEL";
43 constexpr char kDoNotDisturbDeviceSettingId[] = "DO_NOT_DISTURB";
44 constexpr char kNightLightDeviceSettingId[] = "NIGHT_LIGHT_SWITCH";
45 constexpr char kSwitchAccessDeviceSettingId[] = "SWITCH_ACCESS";
46
47 constexpr float kDefaultSliderStep = 0.1f;
48
LogUnsupportedChange(client_op::ModifySettingArgs args)49 void LogUnsupportedChange(client_op::ModifySettingArgs args) {
50 LOG(ERROR) << "Unsupported change operation: " << args.change()
51 << " for setting " << args.setting_id();
52 }
53
HandleOnOffChange(client_op::ModifySettingArgs modify_setting_args,std::function<void (bool)> on_off_handler)54 void HandleOnOffChange(client_op::ModifySettingArgs modify_setting_args,
55 std::function<void(bool)> on_off_handler) {
56 switch (modify_setting_args.change()) {
57 case client_op::ModifySettingArgs_Change_ON:
58 on_off_handler(true);
59 return;
60 case client_op::ModifySettingArgs_Change_OFF:
61 on_off_handler(false);
62 return;
63
64 // Currently there are no use-cases for toggling. This could change in the
65 // future.
66 case client_op::ModifySettingArgs_Change_TOGGLE:
67 break;
68
69 case client_op::ModifySettingArgs_Change_SET:
70 case client_op::ModifySettingArgs_Change_INCREASE:
71 case client_op::ModifySettingArgs_Change_DECREASE:
72 case client_op::ModifySettingArgs_Change_UNSPECIFIED:
73 // This shouldn't happen.
74 break;
75 }
76 LogUnsupportedChange(modify_setting_args);
77 }
78
79 // Helper function that converts a slider value sent from the server, either
80 // absolute or a delta, from a given unit (e.g., STEP), to a percentage.
ConvertSliderValueToLevel(double value,client_op::ModifySettingArgs_Unit unit,double default_value)81 double ConvertSliderValueToLevel(double value,
82 client_op::ModifySettingArgs_Unit unit,
83 double default_value) {
84 switch (unit) {
85 case client_op::ModifySettingArgs_Unit_RANGE:
86 // "set brightness to 20%".
87 return value;
88 case client_op::ModifySettingArgs_Unit_STEP:
89 // "set brightness to 20". Treat the step as a percentage.
90 return value / 100.0f;
91
92 // Currently, factor (e.g., 'double the brightness') and decibel units
93 // aren't handled by the backend. This could change in the future.
94 case client_op::ModifySettingArgs_Unit_FACTOR:
95 case client_op::ModifySettingArgs_Unit_DECIBEL:
96 break;
97
98 case client_op::ModifySettingArgs_Unit_NATIVE:
99 case client_op::ModifySettingArgs_Unit_UNKNOWN_UNIT:
100 // This shouldn't happen.
101 break;
102 }
103 LOG(ERROR) << "Unsupported slider unit: " << unit;
104 return default_value;
105 }
106
HandleSliderChange(client_op::ModifySettingArgs request,std::function<void (double)> set_value_handler,std::function<double ()> get_value_handler)107 void HandleSliderChange(client_op::ModifySettingArgs request,
108 std::function<void(double)> set_value_handler,
109 std::function<double()> get_value_handler) {
110 switch (request.change()) {
111 case client_op::ModifySettingArgs_Change_SET: {
112 // For unsupported units, set the value to the current value, for
113 // visual feedback.
114 double new_value = ConvertSliderValueToLevel(
115 request.numeric_value(), request.unit(), get_value_handler());
116 set_value_handler(new_value);
117 return;
118 }
119
120 case client_op::ModifySettingArgs_Change_INCREASE:
121 case client_op::ModifySettingArgs_Change_DECREASE: {
122 double current_value = get_value_handler();
123 double step = kDefaultSliderStep;
124 if (request.numeric_value() != 0.0f) {
125 // For unsupported units, use the default step percentage.
126 step = ConvertSliderValueToLevel(request.numeric_value(),
127 request.unit(), kDefaultSliderStep);
128 }
129 double new_value =
130 (request.change() == client_op::ModifySettingArgs_Change_INCREASE)
131 ? std::min(current_value + step, 1.0)
132 : std::max(current_value - step, 0.0);
133 set_value_handler(new_value);
134 return;
135 }
136
137 case client_op::ModifySettingArgs_Change_ON:
138 case client_op::ModifySettingArgs_Change_OFF:
139 case client_op::ModifySettingArgs_Change_TOGGLE:
140 case client_op::ModifySettingArgs_Change_UNSPECIFIED:
141 // This shouldn't happen.
142 break;
143 }
144 LogUnsupportedChange(request);
145 }
146
147 class SettingWithDeviceAction : public Setting {
148 public:
SettingWithDeviceAction(ServiceContext * context)149 explicit SettingWithDeviceAction(ServiceContext* context)
150 : context_(context) {}
151
device_actions()152 DeviceActions* device_actions() { return context_->device_actions(); }
153
154 private:
155 ServiceContext* context_;
156 };
157
158 class WifiSetting : public SettingWithDeviceAction {
159 public:
160 using SettingWithDeviceAction::SettingWithDeviceAction;
161
setting_id() const162 const char* setting_id() const override { return kWiFiDeviceSettingId; }
163
Modify(const client_op::ModifySettingArgs & request)164 void Modify(const client_op::ModifySettingArgs& request) override {
165 HandleOnOffChange(request, [&](bool enabled) {
166 this->device_actions()->SetWifiEnabled(enabled);
167 });
168 }
169 };
170
171 class BluetoothSetting : public SettingWithDeviceAction {
172 public:
173 using SettingWithDeviceAction::SettingWithDeviceAction;
174
setting_id() const175 const char* setting_id() const override { return kBluetoothDeviceSettingId; }
176
Modify(const client_op::ModifySettingArgs & request)177 void Modify(const client_op::ModifySettingArgs& request) override {
178 HandleOnOffChange(request, [&](bool enabled) {
179 this->device_actions()->SetBluetoothEnabled(enabled);
180 });
181 }
182 };
183
184 class DoNotDisturbSetting : public Setting {
185 public:
DoNotDisturbSetting(ServiceContext * context)186 explicit DoNotDisturbSetting(ServiceContext* context) : context_(context) {}
187
setting_id() const188 const char* setting_id() const override {
189 return kDoNotDisturbDeviceSettingId;
190 }
191
Modify(const client_op::ModifySettingArgs & request)192 void Modify(const client_op::ModifySettingArgs& request) override {
193 HandleOnOffChange(request, [&](bool enabled) {
194 this->assistant_notification_controller()->SetQuietMode(enabled);
195 });
196 }
197
198 private:
assistant_notification_controller()199 ash::AssistantNotificationController* assistant_notification_controller() {
200 return context_->assistant_notification_controller();
201 }
202
203 ServiceContext* context_;
204 };
205
206 class SwitchAccessSetting : public SettingWithDeviceAction {
207 public:
208 using SettingWithDeviceAction::SettingWithDeviceAction;
209
setting_id() const210 const char* setting_id() const override {
211 return kSwitchAccessDeviceSettingId;
212 }
213
Modify(const client_op::ModifySettingArgs & request)214 void Modify(const client_op::ModifySettingArgs& request) override {
215 HandleOnOffChange(request, [&](bool enabled) {
216 this->device_actions()->SetSwitchAccessEnabled(enabled);
217 });
218 }
219 };
220
221 class NightLightSetting : public SettingWithDeviceAction {
222 public:
223 using SettingWithDeviceAction::SettingWithDeviceAction;
224
setting_id() const225 const char* setting_id() const override { return kNightLightDeviceSettingId; }
226
Modify(const client_op::ModifySettingArgs & request)227 void Modify(const client_op::ModifySettingArgs& request) override {
228 HandleOnOffChange(request, [&](bool enabled) {
229 this->device_actions()->SetNightLightEnabled(enabled);
230 });
231 }
232 };
233
234 class BrightnessSetting : public SettingWithDeviceAction {
235 public:
BrightnessSetting(ServiceContext * context)236 explicit BrightnessSetting(ServiceContext* context)
237 : SettingWithDeviceAction(context), weak_factory_(this) {}
238
setting_id() const239 const char* setting_id() const override {
240 return kScreenBrightnessDeviceSettingId;
241 }
242
Modify(const client_op::ModifySettingArgs & request)243 void Modify(const client_op::ModifySettingArgs& request) override {
244 device_actions()->GetScreenBrightnessLevel(base::BindOnce(
245 [](base::WeakPtr<BrightnessSetting> this_,
246 client_op::ModifySettingArgs request, bool success,
247 double current_value) {
248 if (!success || !this_) {
249 LOG(WARNING) << "Failed to get brightness level";
250 return;
251 }
252 HandleSliderChange(
253 request,
254 [&this_](double new_value) {
255 this_->device_actions()->SetScreenBrightnessLevel(new_value,
256 true);
257 },
258 [current_value]() { return current_value; });
259 },
260 weak_factory_.GetWeakPtr(), request));
261 }
262
263 private:
264 base::WeakPtrFactory<BrightnessSetting> weak_factory_;
265 };
266
267 } // namespace
268
AssistantDeviceSettingsDelegate(ServiceContext * context)269 AssistantDeviceSettingsDelegate::AssistantDeviceSettingsDelegate(
270 ServiceContext* context) {
271 AddSetting(std::make_unique<WifiSetting>(context));
272 AddSetting(std::make_unique<BluetoothSetting>(context));
273 AddSetting(std::make_unique<NightLightSetting>(context));
274 AddSetting(std::make_unique<DoNotDisturbSetting>(context));
275 AddSetting(std::make_unique<BrightnessSetting>(context));
276 AddSetting(std::make_unique<SwitchAccessSetting>(context));
277 }
278
279 AssistantDeviceSettingsDelegate::~AssistantDeviceSettingsDelegate() = default;
280
IsSettingSupported(const std::string & setting_id) const281 bool AssistantDeviceSettingsDelegate::IsSettingSupported(
282 const std::string& setting_id) const {
283 return std::any_of(settings_.begin(), settings_.end(),
284 [&setting_id](const auto& setting) {
285 return setting->setting_id() == setting_id;
286 });
287 }
288
HandleModifyDeviceSetting(const client_op::ModifySettingArgs & modify_setting_args)289 void AssistantDeviceSettingsDelegate::HandleModifyDeviceSetting(
290 const client_op::ModifySettingArgs& modify_setting_args) {
291 VLOG(1) << "Assistant: Modifying Device Setting '"
292 << modify_setting_args.setting_id() << "'";
293 DCHECK(IsSettingSupported(modify_setting_args.setting_id()));
294
295 for (const auto& setting : settings_) {
296 if (setting->setting_id() == modify_setting_args.setting_id()) {
297 setting->Modify(modify_setting_args);
298 return;
299 }
300 }
301
302 NOTREACHED();
303 }
304
GetDeviceSettings(const::assistant::api::client_op::GetDeviceSettingsArgs & args) const305 std::vector<DeviceSetting> AssistantDeviceSettingsDelegate::GetDeviceSettings(
306 const ::assistant::api::client_op::GetDeviceSettingsArgs& args) const {
307 std::vector<DeviceSetting> result;
308 for (const std::string& setting_id : args.setting_ids())
309 result.emplace_back(setting_id, IsSettingSupported(setting_id));
310 return result;
311 }
312
AddSetting(std::unique_ptr<Setting> setting)313 void AssistantDeviceSettingsDelegate::AddSetting(
314 std::unique_ptr<Setting> setting) {
315 settings_.push_back(std::move(setting));
316 }
317
318 } // namespace assistant
319 } // namespace chromeos
320