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 = {"그리드", "그리드 모드", "그리드로 보여줘", "그리드 보여줘"};
38 static const vector<string> __REFRESH_WINDOW_INFO = {"화면 갱신해줘", "화면 업데이트", "리프레시", "새로고침", "새로 고침"};
40 static const int __DEFAULT_DISPLAY_WIDTH = 1920;
41 static const int __DEFAULT_DISPLAY_HEIGHT = 1080;
43 static const int __DEFAULT_GRID_NUM_X1 = 7;
44 static const int __DEFAULT_GRID_NUM_Y1 = 5;
45 static const int __DEFAULT_GRID_NUM_X2 = 4;
46 static const int __DEFAULT_GRID_NUM_Y2 = 3;
49 VoiceTouchEngine::VoiceTouchEngine()
51 _I("[VoiceTouchEngine] Constructor");
52 __touchModality.timestamp = -1;
53 __touchModality.mode = MMI_VOICE_TOUCH_MODE_TOOLTIP;
54 __touchModality.object_id = nullptr;
55 __touchModality.coord_x = 0;
56 __touchModality.coord_y = 0;
58 __currentMode = VOICE_TOUCH_MODE_TOOLTIP_TEXT;
62 __displayWidth = __DEFAULT_DISPLAY_WIDTH;
63 __displayHeight = __DEFAULT_DISPLAY_HEIGHT;
65 __gridX[0] = __DEFAULT_GRID_NUM_X1;
66 __gridY[0] = __DEFAULT_GRID_NUM_Y1;
67 __gridX[1] = __DEFAULT_GRID_NUM_X2;
68 __gridY[1] = __DEFAULT_GRID_NUM_Y2;
70 __outputResultCallback = nullptr;
71 __outputResultUserData = nullptr;
73 __outputModalityCallback = nullptr;
74 __outputModalityUserData = nullptr;
76 __findCommandTimer = nullptr;
77 __makeClickableItemResultTimer = nullptr;
79 resetCurrentGridInfo();
82 VoiceTouchEngine::~VoiceTouchEngine()
84 _I("[VoiceTouchEngine] Destructor");
87 __outputResultCallback = nullptr;
88 __outputResultUserData = nullptr;
90 __outputModalityCallback = nullptr;
91 __outputModalityUserData = nullptr;
93 if (__findCommandTimer != nullptr) {
94 ecore_timer_del(__findCommandTimer);
95 __findCommandTimer = nullptr;
98 if (__makeClickableItemResultTimer != nullptr) {
99 ecore_timer_del(__makeClickableItemResultTimer);
100 __makeClickableItemResultTimer = nullptr;
105 void VoiceTouchEngine::setDisplayInfo(int width, int height)
107 __displayWidth = width;
108 __displayHeight = height;
111 void VoiceTouchEngine::setGridConfig(int numX, int numY, int step)
113 __gridX[step] = numX;
114 __gridY[step] = numY;
117 long VoiceTouchEngine::getCurrentTimestamp()
119 auto currentTime = chrono::steady_clock::now();
120 long timestamp = chrono::time_point_cast<chrono::milliseconds>(currentTime).time_since_epoch().count();
125 bool VoiceTouchEngine::setInputModalityData(std::string appId, int modalityType, void *data)
127 _I("[VoiceTouchEngine] Set input modality data. appId(%s)", appId.c_str());
130 if (modalityType == MMI_PROVIDER_EVENT_VOICE) {
131 _I("[VoiceTouchEngine] Voice modality");
132 mmi_provider_event_voice *event = static_cast<mmi_provider_event_voice *>(data);
134 return handleVoiceInput(event);
137 if (modalityType == MMI_PROVIDER_EVENT_SCREEN_ANALYZER) {
138 _I("[VoiceTouchEngine] Screen analyzer modality");
139 mmi_provider_event_screen_analyzer *event = static_cast<mmi_provider_event_screen_analyzer *>(data);
141 return handleScreenAnalyzerInput(event);
144 _E("[VoiceTouchEngine] This type is not supported by this engine. type(%d)", modalityType);
148 static bool __is_exist_in_candidates(const string &text, const vector<string> &candidates)
150 for (auto &candidate : candidates) {
151 if (0 == text.compare(candidate)) {
159 static void __refresh_screen_inform_event_free(void *data EINA_UNUSED, void *event)
161 mmi_event_refresh_screen_inform *ev = (mmi_event_refresh_screen_inform *)event;
165 static void __invoke_event_for_refreshing_window()
167 mmi_event_refresh_screen_inform *ev = (mmi_event_refresh_screen_inform *)calloc(1, sizeof(mmi_event_refresh_screen_inform));
169 LOGE("mmi_event_refresh_screen_inform malloc failed");
174 ecore_event_add(MMI_EVENT_REFRESH_SCREEN_INFORM, ev, __refresh_screen_inform_event_free, nullptr);
177 bool VoiceTouchEngine::handleVoiceInput(mmi_provider_event_voice *voiceEvent)
179 _I("[VoiceTouchEngine] Voice modality");
180 if (voiceEvent == nullptr) {
181 _E("[VoiceTouchEngine] Parameter is null");
185 _I("[VoiceTouchEngine] Input data. event(%d), text(%s)", voiceEvent->result_type, voiceEvent->source);
186 int timestamp = getCurrentTimestamp();
188 JsonProvider provider;
189 provider.setInputEvent(MMI_KEY_VOICE_TOUCH);
191 string asrResult = string(voiceEvent->source);
192 voice_result_type_e asrEvent = static_cast<voice_result_type_e>(voiceEvent->result_type);
193 if (asrEvent == VOICE_RESULT_TYPE_PARTIAL) {
194 _I("[VoiceTouchEngine] Partial ASR result");
195 setPartialAsrResult(asrResult, timestamp, provider);
196 invokeOutputResultCallback(provider.jsonToString());
200 _I("[VoiceTouchEngine] Final ASR result");
201 setFinalAsrResult(asrResult, timestamp, provider);
202 invokeOutputResultCallback(provider.jsonToString());
204 bool isRefreshNeed = __is_exist_in_candidates(asrResult, __REFRESH_WINDOW_INFO);
206 _I("[VoiceTouchEngine] Request to refresh clickable object information.");
207 __invoke_event_for_refreshing_window();
211 bool isModeUpdated = checkVoiceTouchMode(asrResult);
213 _I("[VoiceTouchEngine] Voice touch mode is changed. (%d)", static_cast<int>(__currentMode));
214 resetCurrentGridInfo();
215 __makeClickableItemResultTimer = ecore_timer_add(0.0, makeClickableItemResultCallback, static_cast<void *>(this));
217 __asrResult = asrResult;
218 __findCommandTimer = ecore_timer_add(0.0, findCommandCallback, static_cast<void *>(this));
224 void VoiceTouchEngine::setPartialAsrResult(const std::string &result, int timestamp, JsonProvider &provider)
226 _I("[VoiceTouchEngine] Set partial ASR result into output. timestamp(%d). item(%s)", timestamp, result.c_str());
228 provider.setOutputEvent(MMI_KEY_ASR_PARTIAL_RESULT);
229 provider.setAsrPartialResult(result.c_str(), timestamp);
232 void VoiceTouchEngine::setFinalAsrResult(const std::string &result, int timestamp, JsonProvider &provider)
234 _I("[VoiceTouchEngine] Set final ASR result into output. timestamp(%d). item(%s)", timestamp, result.c_str());
236 provider.setOutputEvent(MMI_KEY_ASR_FINAL_RESULT);
237 provider.setAsrFinalResult(result.c_str(), timestamp);
240 Eina_Bool VoiceTouchEngine::makeClickableItemResultCallback(void *data)
242 VoiceTouchEngine *engine = static_cast<VoiceTouchEngine *>(data);
243 if (engine == nullptr) {
244 _E("[VoiceTouchEngine] Timer data is invalid");
245 return ECORE_CALLBACK_CANCEL;
248 int timestamp = engine->getCurrentTimestamp();
250 JsonProvider provider;
251 provider.setInputEvent(MMI_KEY_VOICE_TOUCH);
253 engine->makeClickableItemInfo(timestamp, provider);
254 engine->invokeOutputResultCallback(provider.jsonToString());
256 return ECORE_CALLBACK_CANCEL;
259 Eina_Bool VoiceTouchEngine::findCommandCallback(void *data)
261 VoiceTouchEngine *engine = static_cast<VoiceTouchEngine *>(data);
262 if (engine == nullptr) {
263 _E("[VoiceTouchEngine] Timer data is invalid");
264 return ECORE_CALLBACK_CANCEL;
267 int timestamp = engine->getCurrentTimestamp();
269 JsonProvider provider;
270 provider.setInputEvent(MMI_KEY_VOICE_TOUCH);
272 ClickableItem *command = engine->findCommand();
273 engine->makeClickedItemResult(command, timestamp, provider);
274 engine->invokeOutputResultCallback(provider.jsonToString());
276 if (command != nullptr) {
277 _I("[VoiceTouchEngine] Command. object ID(%s)", command->objectId.c_str());
278 engine->makeTouchModality(*command, timestamp);
279 engine->invokeOutputModalityCallback(&engine->__touchModality);
282 return ECORE_CALLBACK_CANCEL;
285 ClickableItem * VoiceTouchEngine::findCommand()
287 bool isIndex = (__currentMode != VOICE_TOUCH_MODE_TOOLTIP_TEXT);
288 return __commandManager.findCommand(__asrResult, isIndex);
291 void VoiceTouchEngine::makeClickedItemResult(ClickableItem* command, int timestamp, JsonProvider &provider)
293 if (command == nullptr) {
294 _I("[VoiceTouchEngine] Fail to find command");
295 setRejectedEventResult(timestamp, provider);
297 _I("[VoiceTouchEngine] Command. object ID(%s)", command->objectId.c_str());
298 setSelectedItemResult(*command, timestamp, provider);
302 bool VoiceTouchEngine::checkVoiceTouchMode(const std::string &text)
304 _I("[VoiceTouchEngine] Input text(%s)", text.c_str());
306 string loweredText = StringUtil::makeLowerCase(text);
307 voice_touch_mode_e mode = __currentMode;
308 if (__is_exist_in_candidates(loweredText, __SHOW_LABEL_KOKR)) {
309 mode = VOICE_TOUCH_MODE_TOOLTIP_TEXT;
310 } else if (__is_exist_in_candidates(loweredText, __SHOW_NUMBER_KOKR)) {
311 mode = VOICE_TOUCH_MODE_TOOLTIP_NUMBER;
312 } else if (__is_exist_in_candidates(loweredText, __SHOW_GRID_KOKR)) {
313 mode = VOICE_TOUCH_MODE_GRID;
316 if (__currentMode != mode) {
317 _I("[VoiceTouchEngine] mode is changed from (%d) to (%d)", __currentMode, mode);
318 __currentMode = mode;
322 _I("[VoiceTouchEngine] Mode is not changed");
326 void VoiceTouchEngine::resetCurrentGridInfo()
328 __currentGridInfo.currentStep = 0;
329 __currentGridInfo.coordX = 0;
330 __currentGridInfo.coordY = 0;
331 __currentGridInfo.width = __displayWidth;
332 __currentGridInfo.height = __displayHeight;
335 void VoiceTouchEngine::makeClickableItemInfo(int timestamp, JsonProvider &provider)
337 _I("[VoiceTouchEngine] Make clickable object list output. timestamp(%d), Current mode(%d)", timestamp, __currentMode);
339 list<ClickableItem> *itemList = nullptr;
340 const char *resultType = nullptr;
341 const char *tooltipType = nullptr;
343 switch (__currentMode)
345 case VOICE_TOUCH_MODE_GRID:
346 makeGridItemList(__currentGridInfo, __gridList);
347 itemList = &__gridList;
348 resultType = MMI_KEY_SHOW_GRIDS;
349 tooltipType = MMI_KEY_TOOLTIP_TYPE_NUMBER;
352 case VOICE_TOUCH_MODE_TOOLTIP_NUMBER:
353 itemList = &__itemList;
354 resultType = MMI_KEY_SHOW_TOOLTIPS;
355 tooltipType = MMI_KEY_TOOLTIP_TYPE_NUMBER;
358 case VOICE_TOUCH_MODE_TOOLTIP_TEXT:
359 itemList = &__itemList;
360 resultType = MMI_KEY_SHOW_TOOLTIPS;
361 tooltipType = MMI_KEY_TOOLTIP_TYPE_TEXT;
365 _E("[VoiceTouchEngine] Invalid mode classfier. Update item as lable tooltip.");
366 itemList = &__itemList;
367 resultType = MMI_KEY_SHOW_TOOLTIPS;
368 tooltipType = MMI_KEY_TOOLTIP_TYPE_TEXT;
372 if (itemList == nullptr || itemList->size() == 0) {
373 _I("[VoiceTouchEngine] There is no item in the list.");
374 provider.setOutputEvent(MMI_KEY_ERROR);
375 provider.setErrorEvent(MMI_REASON_NO_CLICKABLE_OBJECTS, timestamp);
379 provider.setOutputEvent(MMI_KEY_UI_CLICKABLE_OBJECT);
380 provider.setUiClickableObject(resultType, __appId.c_str(), tooltipType, __itemList.size(), timestamp);
381 provider.setGridDepth(__currentGridInfo.currentStep + 1);
383 for (auto &item : *itemList) {
384 auto label = (item.label.empty() ? to_string(item.index) : item.label);
385 provider.addInfoClickableObject(item.index, item.coordX, item.coordY, item.width, item.height, label.c_str());
389 void VoiceTouchEngine::makeTouchModality(const ClickableItem &item, int timestamp)
391 _I("[VoiceTouchEngine] Make clicked object output. timestamp(%d). item(%s)", timestamp, item.objectId.c_str());
392 if (__currentMode != VOICE_TOUCH_MODE_GRID) {
393 _D("[VoiceTouchEngine] Not grid mode.");
394 setTouchModality(item, timestamp);
398 if (__currentGridInfo.currentStep == 0) {
399 __currentGridInfo.currentStep++;
400 __currentGridInfo.coordX = item.coordX;
401 __currentGridInfo.coordY = item.coordY;
402 __currentGridInfo.width = item.width;
403 __currentGridInfo.height = item.height;
407 resetCurrentGridInfo();
408 setTouchModality(item, timestamp);
411 void VoiceTouchEngine::setRejectedEventResult(int timestamp, JsonProvider &provider)
413 _I("[VoiceTouchEngine] Set command rejected event into output. timestamp(%d)", timestamp);
415 provider.setOutputEvent(MMI_KEY_REJECT);
416 provider.setRejectEvent(MMI_REASON_NO_MATCHED_COMMANDS, timestamp);
419 void VoiceTouchEngine::setSelectedItemResult(const ClickableItem &item, int timestamp, JsonProvider &provider)
421 _I("[VoiceTouchEngine] Set clicked object information into output. timestamp(%d). item(%s)", timestamp, item.objectId.c_str());
423 provider.setOutputEvent(MMI_KEY_UI_CLICKED_OBJECT);
424 provider.setUiClickedObject(__appId.c_str(), timestamp, item.index, item.coordX, item.coordY, item.width, item.height, item.label.c_str());
427 void VoiceTouchEngine::makeGridItemList(const grid_info_s &parentInfo, std::list<ClickableItem> &list)
429 _I("[VoiceTouchEngine] Set grid information into item list. Grid Step(%d)", parentInfo.currentStep);
430 _D("[VoiceTouchEngine] Parent info. X(%d), Y(%d), W(%d), H(%d)", parentInfo.coordX, parentInfo.coordY, parentInfo.width, parentInfo.height);
432 int currentStep = parentInfo.currentStep;
433 if (currentStep >= __MAXIMUM_GRID_STEP) {
437 int numX = __gridX[currentStep];
438 int numY = __gridY[currentStep];
439 int coordX = parentInfo.coordX;
440 int coordY = parentInfo.coordY;
441 int gridWidth = parentInfo.width / numX;
442 int gridHeight = parentInfo.height / numY;
444 int remainedWidth = parentInfo.width % numX;
445 int remainedHeight = parentInfo.height % numY;
450 for (int y = 0; y < numY; y++) {
451 for (int x = 0; x < numX; x++) {
453 grid.coordX = coordX + (x * gridWidth);
454 grid.coordY = coordY + (y * gridHeight);
456 grid.width = (x == numX - 1 ? gridWidth + remainedWidth : gridWidth);
457 grid.height = (y == numY - 1 ? gridHeight + remainedHeight : gridHeight);
459 grid.index = index++;
461 list.push_back(grid);
466 void VoiceTouchEngine::setTouchModality(const ClickableItem &item, int timestamp)
468 __touchModality.timestamp = timestamp;
469 __touchModality.mode = (__currentMode == VOICE_TOUCH_MODE_GRID ? MMI_VOICE_TOUCH_MODE_GRID : MMI_VOICE_TOUCH_MODE_TOOLTIP);
470 __touchModality.object_id = item.objectId.c_str();
471 __touchModality.coord_x = item.coordX + (item.width / 2);
472 __touchModality.coord_y = item.coordY + (item.height / 2);
475 bool VoiceTouchEngine::handleScreenAnalyzerInput(mmi_provider_event_screen_analyzer *screenAnalyzerEvent)
477 _I("[VoiceTouchEngine] Screen analyzer modality");
478 if (screenAnalyzerEvent == nullptr) {
479 _E("[VoiceTouchEngine] Parameter is null");
483 _I("[VoiceTouchEngine] Input data. num of items(%d)", screenAnalyzerEvent->n_items);
485 int timestamp = getCurrentTimestamp();
487 JsonProvider provider;
488 provider.setInputEvent(MMI_KEY_VOICE_TOUCH);
490 __commandManager.cleanCommands();
492 g_list_foreach(screenAnalyzerEvent->list, iterateObjectCallback, static_cast<gpointer>(this));
494 makeClickableItemInfo(timestamp, provider);
495 invokeOutputResultCallback(provider.jsonToString());
500 void VoiceTouchEngine::iterateObjectCallback(gpointer data, gpointer userData)
502 clickable_item* info = static_cast<clickable_item*>(data);
503 VoiceTouchEngine *engine = static_cast<VoiceTouchEngine*>(userData);
506 item.coordX = info->coord_x;
507 item.coordY = info->coord_y;
508 item.width = info->width;
509 item.height = info->height;
510 item.objectId = string(info->object_id);
511 item.label = string(info->label);
512 item.index = engine->__itemList.size() + 1;
514 engine->__itemList.push_back(item);
515 engine->__commandManager.addCommand(item);
518 void VoiceTouchEngine::invokeOutputResultCallback(std::string outputResult)
520 if (__outputResultCallback == nullptr) {
524 __outputResultCallback(__appId.c_str(), outputResult.c_str(), __outputResultUserData);
527 void VoiceTouchEngine::invokeOutputModalityCallback(void* outputModality)
529 if (__outputModalityCallback == nullptr) {
533 __outputModalityCallback(__appId.c_str(), outputModality, __outputModalityUserData);
536 void VoiceTouchEngine::setOutputResultCallback(voice_touch_engine_output_result_cb callback, void *userData)
538 __outputResultCallback = callback;
539 __outputResultUserData = userData;
542 void VoiceTouchEngine::setOutputModalityCallback(voice_touch_engine_output_modality_cb callback, void *userData)
544 __outputModalityCallback = callback;
545 __outputModalityUserData = userData;