[dali_2.3.19] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / feedback / feedback-style.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/feedback/feedback-style.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/style-monitor.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/public-api/common/vector-wrapper.h>
25 #include <dali/public-api/object/object-registry.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
29 #include <dali-toolkit/devel-api/builder/json-parser.h>
30 #include <dali-toolkit/internal/feedback/feedback-ids.h>
31
32 namespace // unnamed namespace
33 {
34 #if defined(DEBUG_ENABLED)
35 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::General, false, "LOG_FEEDBACK");
36 #endif
37
38 const char* DEFAULT_FEEDBACK_THEME_FILE_NAME = "default-feedback-theme.json";
39
40 // Sets bool and string if the node has a child "name"
41 void GetIfString(const Dali::Toolkit::TreeNode& node, const std::string& name, bool& exists, std::string& str)
42 {
43   const Dali::Toolkit::TreeNode* child = node.GetChild(name);
44   if(child &&
45      Dali::Toolkit::TreeNode::STRING == child->GetType())
46   {
47     exists = true;
48     str    = child->GetString();
49   }
50 }
51
52 } // unnamed namespace
53
54 namespace Dali
55 {
56 namespace Toolkit
57 {
58 namespace Internal
59 {
60 struct SignalFeedbackInfo
61 {
62   /**
63    * Default constructor.
64    */
65   SignalFeedbackInfo()
66   : mHasHapticFeedbackInfo(false),
67     mHasSoundFeedbackInfo(false)
68   {
69   }
70
71   bool        mHasHapticFeedbackInfo;
72   bool        mHasSoundFeedbackInfo;
73   std::string mSignalName;
74   std::string mHapticFeedbackPattern;
75   std::string mSoundFeedbackPattern;
76   std::string mHapticFeedbackFile;
77   std::string mSoundFeedbackFile;
78 };
79
80 typedef std::vector<SignalFeedbackInfo>             SignalFeedbackInfoContainer;
81 typedef SignalFeedbackInfoContainer::const_iterator SignalFeedbackInfoConstIter;
82
83 struct FeedbackStyleInfo
84 {
85   /**
86    * Default constructor.
87    */
88   FeedbackStyleInfo()
89   {
90   }
91
92   std::string mTypeName;
93
94   SignalFeedbackInfoContainer mSignalFeedbackInfoList;
95 };
96
97 FeedbackStyle::FeedbackStyle()
98 {
99   mFeedback = Dali::FeedbackPlayer::Get();
100
101   const std::string styleDirPath         = AssetManager::GetDaliStylePath();
102   const std::string defaultThemeFilePath = styleDirPath + DEFAULT_FEEDBACK_THEME_FILE_NAME;
103
104   std::string defaultTheme;
105
106   if(mFeedback && mFeedback.LoadFile(defaultThemeFilePath, defaultTheme))
107   {
108     LoadTheme(defaultTheme);
109     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "ResourceLoader::LoadTheme(%s) - loaded %d bytes\n", defaultThemeFilePath.c_str(), defaultTheme.size());
110   }
111   else
112   {
113     DALI_LOG_ERROR("ResourceLoader::LoadTheme(%s) - failed to load\n", defaultThemeFilePath.c_str());
114   }
115 }
116
117 FeedbackStyle::~FeedbackStyle()
118 {
119 }
120
121 struct PlayFeedbackFromSignal
122 {
123   PlayFeedbackFromSignal(FeedbackStyle& controller, const std::string& typeName, const std::string& signalName)
124   : mController(controller),
125     mTypeName(typeName),
126     mSignalName(signalName)
127   {
128   }
129
130   void operator()()
131   {
132     mController.PlayFeedback(mTypeName, mSignalName);
133   }
134
135   FeedbackStyle& mController;
136   std::string    mTypeName;
137   std::string    mSignalName;
138 };
139
140 void FeedbackStyle::ObjectCreated(BaseHandle handle)
141 {
142   if(handle)
143   {
144     const std::string& type = handle.GetTypeName();
145
146     const FeedbackStyleInfo styleInfo = GetStyleInfo(type);
147
148     for(SignalFeedbackInfoConstIter iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
149     {
150       const SignalFeedbackInfo& info = *iter;
151
152       if(info.mHasHapticFeedbackInfo || info.mHasSoundFeedbackInfo)
153       {
154         if(!info.mHapticFeedbackPattern.empty() || !info.mHapticFeedbackFile.empty() ||
155            !info.mSoundFeedbackPattern.empty() || !info.mSoundFeedbackFile.empty())
156         {
157           handle.ConnectSignal(this,
158                                info.mSignalName,
159                                PlayFeedbackFromSignal(*this, type, info.mSignalName));
160
161           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "FeedbackStyle::Set found Haptic pattern %s for Object type: %s, Signal Type: %s\n", info.mHapticFeedbackPattern.c_str(), type.c_str(), info.mSignalName.c_str());
162         }
163         else
164         {
165           DALI_LOG_ERROR("FeedbackStyle::Set() Warning Inconsistent data in theme file!\n");
166         }
167       }
168     }
169   }
170 }
171
172 const FeedbackStyleInfo& FeedbackStyle::GetStyleInfo(const std::string& type) const
173 {
174   std::map<const std::string, FeedbackStyleInfo>::const_iterator iter(mStyleInfoLut.find(type));
175   if(iter != mStyleInfoLut.end())
176   {
177     return iter->second;
178   }
179   else
180   {
181     static const FeedbackStyleInfo DEFAULT_FEEDBACK_STYLE_INFO;
182     return DEFAULT_FEEDBACK_STYLE_INFO;
183   }
184 }
185
186 void FeedbackStyle::StyleChanged(const std::string& userDefinedThemePath, Dali::StyleChange::Type styleChange)
187 {
188   if(styleChange == StyleChange::THEME_CHANGE)
189   {
190     std::string userDefinedTheme;
191
192     if(mFeedback && mFeedback.LoadFile(userDefinedThemePath, userDefinedTheme))
193     {
194       if(!LoadTheme(userDefinedTheme))
195       {
196         DALI_LOG_ERROR("FeedbackStyle::StyleChanged() User defined theme failed to load! \n");
197
198         const std::string styleDirPath         = AssetManager::GetDaliStylePath();
199         const std::string defaultThemeFilePath = styleDirPath + DEFAULT_FEEDBACK_THEME_FILE_NAME;
200
201         //If there is any problem is using the user defined theme, then fall back to default theme
202         if(!LoadTheme(defaultThemeFilePath))
203         {
204           //If the default theme fails, Then No luck!
205           DALI_LOG_ERROR("FeedbackStyle::StyleChanged() Default theme failed to load! \n");
206         }
207       }
208       else
209       {
210         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "ResourceLoader::LoadTheme(%s) - loaded %d bytes\n", userDefinedThemePath.c_str(), userDefinedTheme.size());
211       }
212     }
213     else
214     {
215       DALI_LOG_ERROR("ResourceLoader::LoadTheme(%s) - failed to load\n", userDefinedThemePath.c_str());
216     }
217   }
218 }
219
220 bool FeedbackStyle::LoadTheme(const std::string& data)
221 {
222   bool result = false;
223
224   try
225   {
226     LoadFromString(data);
227
228     result = true;
229   }
230   catch(...)
231   {
232     //Problem in user set theme, So fallback to use default theme.
233     DALI_LOG_ERROR("FeedbackStyle::LoadTheme() Failed to load theme\n");
234   }
235
236   return result;
237 }
238
239 void FeedbackStyle::LoadFromString(const std::string& data)
240 {
241   Toolkit::JsonParser      parser = Toolkit::JsonParser::New();
242   const Toolkit::TreeNode* root   = NULL;
243
244   if(!parser.Parse(data))
245   {
246     DALI_LOG_WARNING("JSON Parse Error:'%s'\n", parser.GetErrorDescription().c_str());
247     DALI_LOG_WARNING("JSON Parse Line :'%d (%d)'\n",
248                      parser.GetErrorLineNumber(),
249                      parser.GetErrorColumn());
250   }
251   else
252   {
253     root = parser.GetRoot();
254   }
255
256   if(root)
257   {
258     // Clear previously loaded style
259     mStyleInfoLut.clear();
260
261     // Parse style
262     if(const TreeNode* node = root->GetChild("style"))
263     {
264       Toolkit::TreeNode::ConstIterator iter = node->CBegin();
265       Toolkit::TreeNode::ConstIterator end  = node->CEnd();
266       for(; iter != end; ++iter)
267       {
268         const char*       key = (*iter).first;
269         FeedbackStyleInfo themeInfo;
270         themeInfo.mTypeName = key;
271
272         if(const TreeNode* signals = (*iter).second.GetChild("signals"))
273         {
274           TreeNode::ConstIterator signalIter = signals->CBegin();
275           TreeNode::ConstIterator signalEnd  = signals->CEnd();
276           for(; signalIter != signalEnd; ++signalIter)
277           {
278             SignalFeedbackInfo signalFeedbackInfo;
279
280             const TreeNode* type = (*signalIter).second.GetChild("type");
281             DALI_ASSERT_ALWAYS(type && TreeNode::STRING == type->GetType() && "Signal must have a type");
282             signalFeedbackInfo.mSignalName = type->GetString();
283
284             GetIfString((*signalIter).second, "hapticFeedbackPattern", signalFeedbackInfo.mHasHapticFeedbackInfo, signalFeedbackInfo.mHapticFeedbackPattern);
285
286             GetIfString((*signalIter).second, "hapticFeedbackFile", signalFeedbackInfo.mHasHapticFeedbackInfo, signalFeedbackInfo.mHapticFeedbackFile);
287
288             GetIfString((*signalIter).second, "soundFeedbackPattern", signalFeedbackInfo.mHasSoundFeedbackInfo, signalFeedbackInfo.mSoundFeedbackPattern);
289
290             GetIfString((*signalIter).second, "hapticFeedbackFile", signalFeedbackInfo.mHasSoundFeedbackInfo, signalFeedbackInfo.mSoundFeedbackFile);
291
292             if(signalFeedbackInfo.mHasHapticFeedbackInfo || signalFeedbackInfo.mHasSoundFeedbackInfo)
293             {
294               AddSignalInfo(themeInfo, std::move(signalFeedbackInfo));
295             }
296           }
297         }
298
299         mStyleInfoLut[key] = themeInfo;
300
301       } // for styles
302     }   // if(style)
303   }     // if(root)
304
305 } // LoadFromString()
306
307 void FeedbackStyle::AddSignalInfo(FeedbackStyleInfo& styleInfo, SignalFeedbackInfo&& signalInfo)
308 {
309   bool                                  updated = false;
310   SignalFeedbackInfoContainer::iterator iter;
311
312   // If info exists for the signal then update it, else add new
313   for(iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
314   {
315     if((*iter).mSignalName == signalInfo.mSignalName)
316     {
317       (*iter).mHasHapticFeedbackInfo = signalInfo.mHasHapticFeedbackInfo;
318       (*iter).mHapticFeedbackPattern = signalInfo.mHapticFeedbackPattern;
319       (*iter).mHapticFeedbackFile    = signalInfo.mHapticFeedbackFile;
320       (*iter).mHasSoundFeedbackInfo  = signalInfo.mHasSoundFeedbackInfo;
321       (*iter).mSoundFeedbackPattern  = signalInfo.mSoundFeedbackPattern;
322       (*iter).mSoundFeedbackFile     = signalInfo.mSoundFeedbackFile;
323
324       updated = true;
325       break;
326     }
327   }
328
329   if(!updated)
330   {
331     styleInfo.mSignalFeedbackInfoList.emplace_back(std::move(signalInfo));
332   }
333 }
334
335 void FeedbackStyle::PlayFeedback(const std::string& type, const std::string& signalName)
336 {
337   const FeedbackStyleInfo     styleInfo = GetStyleInfo(type);
338   SignalFeedbackInfoConstIter iter;
339
340   for(iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
341   {
342     const SignalFeedbackInfo& info = *iter;
343
344     if(info.mSignalName == signalName)
345     {
346       if(info.mHasHapticFeedbackInfo)
347       {
348         if(!info.mHapticFeedbackPattern.empty())
349         {
350           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "FeedbackStyle::PlayFeedback Playing Haptic effect: Object type: %s, Signal type: %s, pattern type: %s\n", type.c_str(), signalName.c_str(), info.mHapticFeedbackPattern.c_str());
351
352           mFeedback.PlayFeedbackPattern(FEEDBACK_TYPE_VIBRATION, GetFeedbackPattern(info.mHapticFeedbackPattern));
353         }
354         else if(!info.mHapticFeedbackFile.empty())
355         {
356           mFeedback.PlayFile(info.mHapticFeedbackFile);
357         }
358       }
359
360       if(info.mHasSoundFeedbackInfo)
361       {
362         if(!info.mSoundFeedbackPattern.empty())
363         {
364           DALI_LOG_INFO(gLogFilter, Debug::Verbose, "FeedbackStyle::PlayFeedback Playing Sound effect: Object type: %s, Signal type: %s, pattern type: %s\n", type.c_str(), signalName.c_str(), info.mHapticFeedbackPattern.c_str());
365
366           mFeedback.PlayFeedbackPattern(FEEDBACK_TYPE_SOUND, GetFeedbackPattern(info.mSoundFeedbackPattern));
367         }
368         else if(!info.mSoundFeedbackFile.empty())
369         {
370           mFeedback.PlaySound(info.mSoundFeedbackFile);
371         }
372       }
373
374       break;
375     }
376   }
377 }
378
379 FeedbackPattern FeedbackStyle::GetFeedbackPattern(const std::string& pattern)
380 {
381   if(0 == mFeedbackPatternLut.size())
382   {
383     mFeedbackPatternLut["FEEDBACK_PATTERN_NONE"]                = Dali::FEEDBACK_PATTERN_NONE;
384     mFeedbackPatternLut["FEEDBACK_PATTERN_TAP"]                 = Dali::FEEDBACK_PATTERN_TAP;
385     mFeedbackPatternLut["FEEDBACK_PATTERN_SIP"]                 = Dali::FEEDBACK_PATTERN_SIP;
386     mFeedbackPatternLut["FEEDBACK_PATTERN_SIP_BACKSPACE"]       = Dali::FEEDBACK_PATTERN_SIP_BACKSPACE;
387     mFeedbackPatternLut["FEEDBACK_PATTERN_MAX_CHARACTER"]       = Dali::FEEDBACK_PATTERN_MAX_CHARACTER;
388     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY0"]                = Dali::FEEDBACK_PATTERN_KEY0;
389     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY1"]                = Dali::FEEDBACK_PATTERN_KEY1;
390     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY2"]                = Dali::FEEDBACK_PATTERN_KEY2;
391     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY3"]                = Dali::FEEDBACK_PATTERN_KEY3;
392     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY4"]                = Dali::FEEDBACK_PATTERN_KEY4;
393     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY5"]                = Dali::FEEDBACK_PATTERN_KEY5;
394     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY6"]                = Dali::FEEDBACK_PATTERN_KEY6;
395     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY7"]                = Dali::FEEDBACK_PATTERN_KEY7;
396     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY8"]                = Dali::FEEDBACK_PATTERN_KEY8;
397     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY9"]                = Dali::FEEDBACK_PATTERN_KEY9;
398     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY_STAR"]            = Dali::FEEDBACK_PATTERN_KEY_STAR;
399     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY_SHARP"]           = Dali::FEEDBACK_PATTERN_KEY_SHARP;
400     mFeedbackPatternLut["FEEDBACK_PATTERN_HOLD"]                = Dali::FEEDBACK_PATTERN_HOLD;
401     mFeedbackPatternLut["FEEDBACK_PATTERN_MULTI_TAP"]           = Dali::FEEDBACK_PATTERN_MULTI_TAP;
402     mFeedbackPatternLut["FEEDBACK_PATTERN_HW_TAP"]              = Dali::FEEDBACK_PATTERN_HW_TAP;
403     mFeedbackPatternLut["FEEDBACK_PATTERN_HW_HOLD"]             = Dali::FEEDBACK_PATTERN_HW_HOLD;
404     mFeedbackPatternLut["FEEDBACK_PATTERN_MESSAGE"]             = Dali::FEEDBACK_PATTERN_MESSAGE;
405     mFeedbackPatternLut["FEEDBACK_PATTERN_MESSAGE_ON_CALL"]     = Dali::FEEDBACK_PATTERN_MESSAGE_ON_CALL;
406     mFeedbackPatternLut["FEEDBACK_PATTERN_EMAIL"]               = Dali::FEEDBACK_PATTERN_EMAIL;
407     mFeedbackPatternLut["FEEDBACK_PATTERN_EMAIL_ON_CALL"]       = Dali::FEEDBACK_PATTERN_EMAIL_ON_CALL;
408     mFeedbackPatternLut["FEEDBACK_PATTERN_WAKEUP"]              = Dali::FEEDBACK_PATTERN_WAKEUP;
409     mFeedbackPatternLut["FEEDBACK_PATTERN_WAKEUP_ON_CALL"]      = Dali::FEEDBACK_PATTERN_WAKEUP_ON_CALL;
410     mFeedbackPatternLut["FEEDBACK_PATTERN_SCHEDULE"]            = Dali::FEEDBACK_PATTERN_SCHEDULE;
411     mFeedbackPatternLut["FEEDBACK_PATTERN_SCHEDULE_ON_CALL"]    = Dali::FEEDBACK_PATTERN_SCHEDULE_ON_CALL;
412     mFeedbackPatternLut["FEEDBACK_PATTERN_TIMER"]               = Dali::FEEDBACK_PATTERN_TIMER;
413     mFeedbackPatternLut["FEEDBACK_PATTERN_TIMER_ON_CALL"]       = Dali::FEEDBACK_PATTERN_TIMER_ON_CALL;
414     mFeedbackPatternLut["FEEDBACK_PATTERN_GENERAL"]             = Dali::FEEDBACK_PATTERN_GENERAL;
415     mFeedbackPatternLut["FEEDBACK_PATTERN_GENERAL_ON_CALL"]     = Dali::FEEDBACK_PATTERN_GENERAL_ON_CALL;
416     mFeedbackPatternLut["FEEDBACK_PATTERN_POWER_ON"]            = Dali::FEEDBACK_PATTERN_POWER_ON;
417     mFeedbackPatternLut["FEEDBACK_PATTERN_POWER_OFF"]           = Dali::FEEDBACK_PATTERN_POWER_OFF;
418     mFeedbackPatternLut["FEEDBACK_PATTERN_CHARGERCONN"]         = Dali::FEEDBACK_PATTERN_CHARGERCONN;
419     mFeedbackPatternLut["FEEDBACK_PATTERN_CHARGERCONN_ON_CALL"] = Dali::FEEDBACK_PATTERN_CHARGERCONN_ON_CALL;
420     mFeedbackPatternLut["FEEDBACK_PATTERN_FULLCHARGED"]         = Dali::FEEDBACK_PATTERN_FULLCHARGED;
421     mFeedbackPatternLut["FEEDBACK_PATTERN_FULLCHARGED_ON_CALL"] = Dali::FEEDBACK_PATTERN_FULLCHARGED_ON_CALL;
422     mFeedbackPatternLut["FEEDBACK_PATTERN_LOWBATT"]             = Dali::FEEDBACK_PATTERN_LOWBATT;
423     mFeedbackPatternLut["FEEDBACK_PATTERN_LOWBATT_ON_CALL"]     = Dali::FEEDBACK_PATTERN_LOWBATT_ON_CALL;
424     mFeedbackPatternLut["FEEDBACK_PATTERN_LOCK"]                = Dali::FEEDBACK_PATTERN_LOCK;
425     mFeedbackPatternLut["FEEDBACK_PATTERN_UNLOCK"]              = Dali::FEEDBACK_PATTERN_UNLOCK;
426     mFeedbackPatternLut["FEEDBACK_PATTERN_CALLCONNECT"]         = Dali::FEEDBACK_PATTERN_CALLCONNECT;
427     mFeedbackPatternLut["FEEDBACK_PATTERN_DISCALLCONNECT"]      = Dali::FEEDBACK_PATTERN_DISCALLCONNECT;
428     mFeedbackPatternLut["FEEDBACK_PATTERN_MINUTEMINDER"]        = Dali::FEEDBACK_PATTERN_MINUTEMINDER;
429     mFeedbackPatternLut["FEEDBACK_PATTERN_VIBRATION"]           = Dali::FEEDBACK_PATTERN_VIBRATION;
430     mFeedbackPatternLut["FEEDBACK_PATTERN_SHUTTER"]             = Dali::FEEDBACK_PATTERN_SHUTTER;
431     mFeedbackPatternLut["FEEDBACK_PATTERN_LIST_REORDER"]        = Dali::FEEDBACK_PATTERN_LIST_REORDER;
432     mFeedbackPatternLut["FEEDBACK_PATTERN_SLIDER_SWEEP"]        = Dali::FEEDBACK_PATTERN_SLIDER_SWEEP;
433   }
434
435   std::map<const std::string, FeedbackPattern>::const_iterator iter(mFeedbackPatternLut.find(pattern));
436
437   if(iter != mFeedbackPatternLut.end())
438   {
439     return iter->second;
440   }
441   else
442   {
443     DALI_LOG_ERROR("Unknown feedback pattern type: %s, So Defaulting to FEEDBACK_PATTERN_NONE!\n");
444     return Dali::FEEDBACK_PATTERN_NONE;
445   }
446 }
447
448 } // namespace Internal
449
450 } // namespace Toolkit
451
452 } // namespace Dali