2 * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
22 #include "mmi_iu_log.h"
23 #include "StringUtil.h"
24 #include "json_provider.h"
26 #include "VoiceTouchEngine.h"
31 static const string __SHOW_TOOLTIP_INVOCATION = "show tooltip";
32 static const string __SHOW_NUMBER_INVOCATION = "show numbers";
33 static const string __SHOW_GRID_INVOCATION = "show grid";
35 static const vector<string> __SHOW_NUMBER_KOKR = {"힌트 보여줘", "숫자 힌트 보여줘", "숫자 보여줘", "숫자"};
36 static const vector<string> __SHOW_LABEL_KOKR = {"힌트 메세지 보여줘", "텍스트 보여줘", "텍스트", "텍스트 힌트 보여줘"};
37 static const vector<string> __SHOW_GRID_KOKR = {"그리드", "그리드 모드", "그리드로 보여줘", "그리드 보여줘"};
39 static const int __DEFAULT_DISPLAY_WIDTH = 1920;
40 static const int __DEFAULT_DISPLAY_HEIGHT = 1080;
42 static const int __DEFAULT_GRID_NUM_X1 = 7;
43 static const int __DEFAULT_GRID_NUM_Y1 = 5;
44 static const int __DEFAULT_GRID_NUM_X2 = 4;
45 static const int __DEFAULT_GRID_NUM_Y2 = 3;
48 VoiceTouchEngine::VoiceTouchEngine()
50 _I("[VoiceTouchEngine] Constructor");
51 __touchModality.timestamp = -1;
52 __touchModality.mode = MMI_VOICE_TOUCH_MODE_TOOLTIP;
53 __touchModality.object_id = nullptr;
54 __touchModality.coord_x = 0;
55 __touchModality.coord_y = 0;
57 __currentMode = VOICE_TOUCH_MODE_TOOLTIP_TEXT;
61 __displayWidth = __DEFAULT_DISPLAY_WIDTH;
62 __displayHeight = __DEFAULT_DISPLAY_HEIGHT;
64 __gridX[0] = __DEFAULT_GRID_NUM_X1;
65 __gridY[0] = __DEFAULT_GRID_NUM_Y1;
66 __gridX[1] = __DEFAULT_GRID_NUM_X2;
67 __gridY[1] = __DEFAULT_GRID_NUM_Y2;
69 __outputResultCallback = nullptr;
70 __outputResultUserData = nullptr;
72 __outputModalityCallback = nullptr;
73 __outputModalityUserData = nullptr;
75 __findCommandTimer = nullptr;
76 __makeClickableItemResultTimer = nullptr;
78 resetCurrentGridInfo();
81 VoiceTouchEngine::~VoiceTouchEngine()
83 _I("[VoiceTouchEngine] Destructor");
86 __outputResultCallback = nullptr;
87 __outputResultUserData = nullptr;
89 __outputModalityCallback = nullptr;
90 __outputModalityUserData = nullptr;
92 if (__findCommandTimer != nullptr) {
93 ecore_timer_del(__findCommandTimer);
94 __findCommandTimer = nullptr;
97 if (__makeClickableItemResultTimer != nullptr) {
98 ecore_timer_del(__makeClickableItemResultTimer);
99 __makeClickableItemResultTimer = nullptr;
104 void VoiceTouchEngine::setDisplayInfo(int width, int height)
106 __displayWidth = width;
107 __displayHeight = height;
110 void VoiceTouchEngine::setGridConfig(int numX, int numY, int step)
112 __gridX[step] = numX;
113 __gridY[step] = numY;
116 long VoiceTouchEngine::getCurrentTimestamp()
118 auto currentTime = chrono::steady_clock::now();
119 long timestamp = chrono::time_point_cast<chrono::milliseconds>(currentTime).time_since_epoch().count();
124 bool VoiceTouchEngine::setInputModalityData(std::string appId, int modalityType, void *data)
126 _I("[VoiceTouchEngine] Set input modality data. appId(%s)", appId.c_str());
129 if (modalityType == MMI_PROVIDER_EVENT_VOICE) {
130 _I("[VoiceTouchEngine] Voice modality");
131 mmi_provider_event_voice *event = static_cast<mmi_provider_event_voice *>(data);
133 return handleVoiceInput(event);
136 if (modalityType == MMI_PROVIDER_EVENT_SCREEN_ANALYZER) {
137 _I("[VoiceTouchEngine] Screen analyzer modality");
138 mmi_provider_event_screen_analyzer *event = static_cast<mmi_provider_event_screen_analyzer *>(data);
140 return handleScreenAnalyzerInput(event);
143 _E("[VoiceTouchEngine] This type is not supported by this engine. type(%d)", modalityType);
147 bool VoiceTouchEngine::handleVoiceInput(mmi_provider_event_voice *voiceEvent)
149 _I("[VoiceTouchEngine] Voice modality");
150 if (voiceEvent == nullptr) {
151 _E("[VoiceTouchEngine] Parameter is null");
155 _I("[VoiceTouchEngine] Input data. event(%d), text(%s)", voiceEvent->result_type, voiceEvent->source);
156 int timestamp = getCurrentTimestamp();
158 JsonProvider provider;
159 provider.setInputEvent(MMI_KEY_VOICE_TOUCH);
161 string asrResult = string(voiceEvent->source);
162 voice_result_type_e asrEvent = static_cast<voice_result_type_e>(voiceEvent->result_type);
163 if (asrEvent == VOICE_RESULT_TYPE_PARTIAL) {
164 _I("[VoiceTouchEngine] Partial ASR result");
165 setPartialAsrResult(asrResult, timestamp, provider);
166 invokeOutputResultCallback(provider.jsonToString());
170 _I("[VoiceTouchEngine] Final ASR result");
171 setFinalAsrResult(asrResult, timestamp, provider);
172 invokeOutputResultCallback(provider.jsonToString());
174 bool isModeUpdated = checkVoiceTouchMode(asrResult);
176 _I("[VoiceTouchEngine] Voice touch mode is changed. (%d)", static_cast<int>(__currentMode));
177 resetCurrentGridInfo();
178 __makeClickableItemResultTimer = ecore_timer_add(0.0, makeClickableItemResultCallback, static_cast<void *>(this));
180 __asrResult = asrResult;
181 __findCommandTimer = ecore_timer_add(0.0, findCommandCallback, static_cast<void *>(this));
187 void VoiceTouchEngine::setPartialAsrResult(const std::string &result, int timestamp, JsonProvider &provider)
189 _I("[VoiceTouchEngine] Set partial ASR result into output. timestamp(%d). item(%s)", timestamp, result.c_str());
191 provider.setOutputEvent(MMI_KEY_ASR_PARTIAL_RESULT);
192 provider.setAsrPartialResult(result.c_str(), timestamp);
195 void VoiceTouchEngine::setFinalAsrResult(const std::string &result, int timestamp, JsonProvider &provider)
197 _I("[VoiceTouchEngine] Set final ASR result into output. timestamp(%d). item(%s)", timestamp, result.c_str());
199 provider.setOutputEvent(MMI_KEY_ASR_FINAL_RESULT);
200 provider.setAsrFinalResult(result.c_str(), timestamp);
203 Eina_Bool VoiceTouchEngine::makeClickableItemResultCallback(void *data)
205 VoiceTouchEngine *engine = static_cast<VoiceTouchEngine *>(data);
206 if (engine == nullptr) {
207 _E("[VoiceTouchEngine] Timer data is invalid");
208 return ECORE_CALLBACK_CANCEL;
211 int timestamp = engine->getCurrentTimestamp();
213 JsonProvider provider;
214 provider.setInputEvent(MMI_KEY_VOICE_TOUCH);
216 engine->makeClickableItemInfo(timestamp, provider);
217 engine->invokeOutputResultCallback(provider.jsonToString());
219 return ECORE_CALLBACK_CANCEL;
222 Eina_Bool VoiceTouchEngine::findCommandCallback(void *data)
224 VoiceTouchEngine *engine = static_cast<VoiceTouchEngine *>(data);
225 if (engine == nullptr) {
226 _E("[VoiceTouchEngine] Timer data is invalid");
227 return ECORE_CALLBACK_CANCEL;
230 int timestamp = engine->getCurrentTimestamp();
232 JsonProvider provider;
233 provider.setInputEvent(MMI_KEY_VOICE_TOUCH);
235 ClickableItem *command = engine->findCommand();
236 engine->makeClickedItemResult(command, timestamp, provider);
237 engine->invokeOutputResultCallback(provider.jsonToString());
239 if (command != nullptr) {
240 _I("[VoiceTouchEngine] Command. object ID(%s)", command->objectId.c_str());
241 engine->makeTouchModality(*command, timestamp);
242 engine->invokeOutputModalityCallback(&engine->__touchModality);
245 return ECORE_CALLBACK_CANCEL;
248 ClickableItem * VoiceTouchEngine::findCommand()
250 bool isIndex = (__currentMode != VOICE_TOUCH_MODE_TOOLTIP_TEXT);
251 return __commandManager.findCommand(__asrResult, isIndex);
254 void VoiceTouchEngine::makeClickedItemResult(ClickableItem* command, int timestamp, JsonProvider &provider)
256 if (command == nullptr) {
257 _I("[VoiceTouchEngine] Fail to find command");
258 setRejectedEventResult(timestamp, provider);
260 _I("[VoiceTouchEngine] Command. object ID(%s)", command->objectId.c_str());
261 setSelectedItemResult(*command, timestamp, provider);
265 static bool __is_exist_in_candidates(const string &text, const vector<string> &candidates)
267 for (auto &candidate : candidates) {
268 if (0 == text.compare(candidate)) {
276 bool VoiceTouchEngine::checkVoiceTouchMode(const std::string &text)
278 _I("[VoiceTouchEngine] Input text(%s)", text.c_str());
280 string loweredText = StringUtil::makeLowerCase(text);
281 voice_touch_mode_e mode = __currentMode;
282 if (__is_exist_in_candidates(loweredText, __SHOW_LABEL_KOKR)) {
283 mode = VOICE_TOUCH_MODE_TOOLTIP_TEXT;
284 } else if (__is_exist_in_candidates(loweredText, __SHOW_NUMBER_KOKR)) {
285 mode = VOICE_TOUCH_MODE_TOOLTIP_NUMBER;
286 } else if (__is_exist_in_candidates(loweredText, __SHOW_GRID_KOKR)) {
287 mode = VOICE_TOUCH_MODE_GRID;
290 if (__currentMode != mode) {
291 _I("[VoiceTouchEngine] mode is changed from (%d) to (%d)", __currentMode, mode);
292 __currentMode = mode;
296 _I("[VoiceTouchEngine] Mode is not changed");
300 void VoiceTouchEngine::resetCurrentGridInfo()
302 __currentGridInfo.currentStep = 0;
303 __currentGridInfo.coordX = 0;
304 __currentGridInfo.coordY = 0;
305 __currentGridInfo.width = __displayWidth;
306 __currentGridInfo.height = __displayHeight;
309 void VoiceTouchEngine::makeClickableItemInfo(int timestamp, JsonProvider &provider)
311 _I("[VoiceTouchEngine] Make clickable object list output. timestamp(%d), Current mode(%d)", timestamp, __currentMode);
313 list<ClickableItem> *itemList = nullptr;
314 const char *resultType = nullptr;
315 const char *tooltipType = nullptr;
317 switch (__currentMode)
319 case VOICE_TOUCH_MODE_GRID:
320 makeGridItemList(__currentGridInfo, __gridList);
321 itemList = &__gridList;
322 resultType = MMI_KEY_SHOW_GRIDS;
323 tooltipType = MMI_KEY_TOOLTIP_TYPE_NUMBER;
326 case VOICE_TOUCH_MODE_TOOLTIP_NUMBER:
327 itemList = &__itemList;
328 resultType = MMI_KEY_SHOW_TOOLTIPS;
329 tooltipType = MMI_KEY_TOOLTIP_TYPE_NUMBER;
332 case VOICE_TOUCH_MODE_TOOLTIP_TEXT:
333 itemList = &__itemList;
334 resultType = MMI_KEY_SHOW_TOOLTIPS;
335 tooltipType = MMI_KEY_TOOLTIP_TYPE_TEXT;
339 _E("[VoiceTouchEngine] Invalid mode classfier. Update item as lable tooltip.");
340 itemList = &__itemList;
341 resultType = MMI_KEY_SHOW_TOOLTIPS;
342 tooltipType = MMI_KEY_TOOLTIP_TYPE_TEXT;
346 if (itemList == nullptr || itemList->size() == 0) {
347 _I("[VoiceTouchEngine] There is no item in the list.");
348 provider.setOutputEvent(MMI_KEY_ERROR);
349 provider.setErrorEvent(MMI_REASON_NO_CLICKABLE_OBJECTS, timestamp);
353 provider.setOutputEvent(MMI_KEY_UI_CLICKABLE_OBJECT);
354 provider.setUiClickableObject(resultType, __appId.c_str(), tooltipType, __itemList.size(), timestamp);
355 provider.setGridDepth(__currentGridInfo.currentStep + 1);
357 for (auto &item : *itemList) {
358 auto label = (item.label.empty() ? to_string(item.index) : item.label);
359 provider.addInfoClickableObject(item.index, item.coordX, item.coordY, item.width, item.height, label.c_str());
363 void VoiceTouchEngine::makeTouchModality(const ClickableItem &item, int timestamp)
365 _I("[VoiceTouchEngine] Make clicked object output. timestamp(%d). item(%s)", timestamp, item.objectId.c_str());
366 if (__currentMode != VOICE_TOUCH_MODE_GRID) {
367 _D("[VoiceTouchEngine] Not grid mode.");
368 setTouchModality(item, timestamp);
372 if (__currentGridInfo.currentStep == 0) {
373 __currentGridInfo.currentStep++;
374 __currentGridInfo.coordX = item.coordX;
375 __currentGridInfo.coordY = item.coordY;
376 __currentGridInfo.width = item.width;
377 __currentGridInfo.height = item.height;
381 resetCurrentGridInfo();
382 setTouchModality(item, timestamp);
385 void VoiceTouchEngine::setRejectedEventResult(int timestamp, JsonProvider &provider)
387 _I("[VoiceTouchEngine] Set command rejected event into output. timestamp(%d)", timestamp);
389 provider.setOutputEvent(MMI_KEY_REJECT);
390 provider.setRejectEvent(MMI_REASON_NO_MATCHED_COMMANDS, timestamp);
393 void VoiceTouchEngine::setSelectedItemResult(const ClickableItem &item, int timestamp, JsonProvider &provider)
395 _I("[VoiceTouchEngine] Set clicked object information into output. timestamp(%d). item(%s)", timestamp, item.objectId.c_str());
397 provider.setOutputEvent(MMI_KEY_UI_CLICKED_OBJECT);
398 provider.setUiClickedObject(__appId.c_str(), timestamp, item.index, item.coordX, item.coordY, item.width, item.height, item.label.c_str());
401 void VoiceTouchEngine::makeGridItemList(const grid_info_s &parentInfo, std::list<ClickableItem> &list)
403 _I("[VoiceTouchEngine] Set grid information into item list. Grid Step(%d)", parentInfo.currentStep);
404 _D("[VoiceTouchEngine] Parent info. X(%d), Y(%d), W(%d), H(%d)", parentInfo.coordX, parentInfo.coordY, parentInfo.width, parentInfo.height);
406 int currentStep = parentInfo.currentStep;
407 if (currentStep >= __MAXIMUM_GRID_STEP) {
411 int numX = __gridX[currentStep];
412 int numY = __gridY[currentStep];
413 int coordX = parentInfo.coordX;
414 int coordY = parentInfo.coordY;
415 int gridWidth = parentInfo.width / numX;
416 int gridHeight = parentInfo.height / numY;
418 int remainedWidth = parentInfo.width % numX;
419 int remainedHeight = parentInfo.height % numY;
424 for (int y = 0; y < numY; y++) {
425 for (int x = 0; x < numX; x++) {
427 grid.coordX = coordX + (x * gridWidth);
428 grid.coordY = coordY + (y * gridHeight);
430 grid.width = (x == numX - 1 ? gridWidth + remainedWidth : gridWidth);
431 grid.height = (y == numY - 1 ? gridHeight + remainedHeight : gridHeight);
433 grid.index = index++;
435 list.push_back(grid);
440 void VoiceTouchEngine::setTouchModality(const ClickableItem &item, int timestamp)
442 __touchModality.timestamp = timestamp;
443 __touchModality.mode = (__currentMode == VOICE_TOUCH_MODE_GRID ? MMI_VOICE_TOUCH_MODE_GRID : MMI_VOICE_TOUCH_MODE_TOOLTIP);
444 __touchModality.object_id = item.objectId.c_str();
445 __touchModality.coord_x = item.coordX + (item.width / 2);
446 __touchModality.coord_y = item.coordY + (item.height / 2);
449 bool VoiceTouchEngine::handleScreenAnalyzerInput(mmi_provider_event_screen_analyzer *screenAnalyzerEvent)
451 _I("[VoiceTouchEngine] Screen analyzer modality");
452 if (screenAnalyzerEvent == nullptr) {
453 _E("[VoiceTouchEngine] Parameter is null");
457 _I("[VoiceTouchEngine] Input data. num of items(%d)", screenAnalyzerEvent->n_items);
459 int timestamp = getCurrentTimestamp();
461 JsonProvider provider;
462 provider.setInputEvent(MMI_KEY_VOICE_TOUCH);
464 __commandManager.cleanCommands();
466 g_list_foreach(screenAnalyzerEvent->list, iterateObjectCallback, static_cast<gpointer>(this));
468 makeClickableItemInfo(timestamp, provider);
469 invokeOutputResultCallback(provider.jsonToString());
474 void VoiceTouchEngine::iterateObjectCallback(gpointer data, gpointer userData)
476 clickable_item* info = static_cast<clickable_item*>(data);
477 VoiceTouchEngine *engine = static_cast<VoiceTouchEngine*>(userData);
480 item.coordX = info->coord_x;
481 item.coordY = info->coord_y;
482 item.width = info->width;
483 item.height = info->height;
484 item.objectId = string(info->object_id);
485 item.label = string(info->label);
486 item.index = engine->__itemList.size() + 1;
488 engine->__itemList.push_back(item);
489 engine->__commandManager.addCommand(item);
492 void VoiceTouchEngine::invokeOutputResultCallback(std::string outputResult)
494 if (__outputResultCallback == nullptr) {
498 __outputResultCallback(__appId.c_str(), outputResult.c_str(), __outputResultUserData);
501 void VoiceTouchEngine::invokeOutputModalityCallback(void* outputModality)
503 if (__outputModalityCallback == nullptr) {
507 __outputModalityCallback(__appId.c_str(), outputModality, __outputModalityUserData);
510 void VoiceTouchEngine::setOutputResultCallback(voice_touch_engine_output_result_cb callback, void *userData)
512 __outputResultCallback = callback;
513 __outputResultUserData = userData;
516 void VoiceTouchEngine::setOutputModalityCallback(voice_touch_engine_output_modality_cb callback, void *userData)
518 __outputModalityCallback = callback;
519 __outputModalityUserData = userData;