DALi Version 2.1.5
[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 static const FeedbackStyleInfo DEFAULT_FEEDBACK_STYLE_INFO;
98
99 FeedbackStyle::FeedbackStyle()
100 {
101   mFeedback = Dali::FeedbackPlayer::Get();
102
103   const std::string styleDirPath         = AssetManager::GetDaliStylePath();
104   const std::string defaultThemeFilePath = styleDirPath + DEFAULT_FEEDBACK_THEME_FILE_NAME;
105
106   std::string defaultTheme;
107
108   if(mFeedback && mFeedback.LoadFile(defaultThemeFilePath, defaultTheme))
109   {
110     LoadTheme(defaultTheme);
111     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "ResourceLoader::LoadTheme(%s) - loaded %d bytes\n", defaultThemeFilePath.c_str(), defaultTheme.size());
112   }
113   else
114   {
115     DALI_LOG_ERROR("ResourceLoader::LoadTheme(%s) - failed to load\n", defaultThemeFilePath.c_str());
116   }
117 }
118
119 FeedbackStyle::~FeedbackStyle()
120 {
121 }
122
123 struct PlayFeedbackFromSignal
124 {
125   PlayFeedbackFromSignal(FeedbackStyle& controller, const std::string& typeName, const std::string& signalName)
126   : mController(controller),
127     mTypeName(typeName),
128     mSignalName(signalName)
129   {
130   }
131
132   void operator()()
133   {
134     mController.PlayFeedback(mTypeName, mSignalName);
135   }
136
137   FeedbackStyle& mController;
138   std::string    mTypeName;
139   std::string    mSignalName;
140 };
141
142 void FeedbackStyle::ObjectCreated(BaseHandle handle)
143 {
144   if(handle)
145   {
146     const std::string& type = handle.GetTypeName();
147
148     const FeedbackStyleInfo styleInfo = GetStyleInfo(type);
149
150     for(SignalFeedbackInfoConstIter iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
151     {
152       const SignalFeedbackInfo& info = *iter;
153
154       if(info.mHasHapticFeedbackInfo || info.mHasSoundFeedbackInfo)
155       {
156         if(!info.mHapticFeedbackPattern.empty() || !info.mHapticFeedbackFile.empty() ||
157            !info.mSoundFeedbackPattern.empty() || !info.mSoundFeedbackFile.empty())
158         {
159           handle.ConnectSignal(this,
160                                info.mSignalName,
161                                PlayFeedbackFromSignal(*this, type, info.mSignalName));
162
163           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());
164         }
165         else
166         {
167           DALI_LOG_ERROR("FeedbackStyle::Set() Warning Inconsistent data in theme file!\n");
168         }
169       }
170     }
171   }
172 }
173
174 const FeedbackStyleInfo& FeedbackStyle::GetStyleInfo(const std::string& type) const
175 {
176   std::map<const std::string, FeedbackStyleInfo>::const_iterator iter(mStyleInfoLut.find(type));
177   if(iter != mStyleInfoLut.end())
178   {
179     return iter->second;
180   }
181   else
182   {
183     return DEFAULT_FEEDBACK_STYLE_INFO;
184   }
185 }
186
187 void FeedbackStyle::StyleChanged(const std::string& userDefinedThemePath, Dali::StyleChange::Type styleChange)
188 {
189   if(styleChange == StyleChange::THEME_CHANGE)
190   {
191     std::string userDefinedTheme;
192
193     if(mFeedback && mFeedback.LoadFile(userDefinedThemePath, userDefinedTheme))
194     {
195       if(!LoadTheme(userDefinedTheme))
196       {
197         DALI_LOG_ERROR("FeedbackStyle::StyleChanged() User defined theme failed to load! \n");
198
199         const std::string styleDirPath         = AssetManager::GetDaliStylePath();
200         const std::string defaultThemeFilePath = styleDirPath + DEFAULT_FEEDBACK_THEME_FILE_NAME;
201
202         //If there is any problem is using the user defined theme, then fall back to default theme
203         if(!LoadTheme(defaultThemeFilePath))
204         {
205           //If the default theme fails, Then No luck!
206           DALI_LOG_ERROR("FeedbackStyle::StyleChanged() Default theme failed to load! \n");
207         }
208       }
209       else
210       {
211         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "ResourceLoader::LoadTheme(%s) - loaded %d bytes\n", userDefinedThemePath.c_str(), userDefinedTheme.size());
212       }
213     }
214     else
215     {
216       DALI_LOG_ERROR("ResourceLoader::LoadTheme(%s) - failed to load\n", userDefinedThemePath.c_str());
217     }
218   }
219 }
220
221 bool FeedbackStyle::LoadTheme(const std::string& data)
222 {
223   bool result = false;
224
225   try
226   {
227     LoadFromString(data);
228
229     result = true;
230   }
231   catch(...)
232   {
233     //Problem in user set theme, So fallback to use default theme.
234     DALI_LOG_ERROR("FeedbackStyle::LoadTheme() Failed to load theme\n");
235   }
236
237   return result;
238 }
239
240 void FeedbackStyle::LoadFromString(const std::string& data)
241 {
242   Toolkit::JsonParser      parser = Toolkit::JsonParser::New();
243   const Toolkit::TreeNode* root   = NULL;
244
245   if(!parser.Parse(data))
246   {
247     DALI_LOG_WARNING("JSON Parse Error:'%s'\n", parser.GetErrorDescription().c_str());
248     DALI_LOG_WARNING("JSON Parse Line :'%d (%d)'\n",
249                      parser.GetErrorLineNumber(),
250                      parser.GetErrorColumn());
251   }
252   else
253   {
254     root = parser.GetRoot();
255   }
256
257   if(root)
258   {
259     // Clear previously loaded style
260     mStyleInfoLut.clear();
261
262     // Parse style
263     if(const TreeNode* node = root->GetChild("style"))
264     {
265       Toolkit::TreeNode::ConstIterator iter = node->CBegin();
266       Toolkit::TreeNode::ConstIterator end  = node->CEnd();
267       for(; iter != end; ++iter)
268       {
269         const char*       key = (*iter).first;
270         FeedbackStyleInfo themeInfo;
271         themeInfo.mTypeName = key;
272
273         if(const TreeNode* signals = (*iter).second.GetChild("signals"))
274         {
275           TreeNode::ConstIterator signalIter = signals->CBegin();
276           TreeNode::ConstIterator signalEnd  = signals->CEnd();
277           for(; signalIter != signalEnd; ++signalIter)
278           {
279             SignalFeedbackInfo signalFeedbackInfo;
280
281             const TreeNode* type = (*signalIter).second.GetChild("type");
282             DALI_ASSERT_ALWAYS(type && TreeNode::STRING == type->GetType() && "Signal must have a type");
283             signalFeedbackInfo.mSignalName = type->GetString();
284
285             GetIfString((*signalIter).second, "hapticFeedbackPattern", signalFeedbackInfo.mHasHapticFeedbackInfo, signalFeedbackInfo.mHapticFeedbackPattern);
286
287             GetIfString((*signalIter).second, "hapticFeedbackFile", signalFeedbackInfo.mHasHapticFeedbackInfo, signalFeedbackInfo.mHapticFeedbackFile);
288
289             GetIfString((*signalIter).second, "soundFeedbackPattern", signalFeedbackInfo.mHasSoundFeedbackInfo, signalFeedbackInfo.mSoundFeedbackPattern);
290
291             GetIfString((*signalIter).second, "hapticFeedbackFile", signalFeedbackInfo.mHasSoundFeedbackInfo, signalFeedbackInfo.mSoundFeedbackFile);
292
293             if(signalFeedbackInfo.mHasHapticFeedbackInfo || signalFeedbackInfo.mHasSoundFeedbackInfo)
294             {
295               AddSignalInfo(themeInfo, std::move(signalFeedbackInfo));
296             }
297           }
298         }
299
300         mStyleInfoLut[key] = themeInfo;
301
302       } // for styles
303     }   // if(style)
304   }     // if(root)
305
306 } // LoadFromString()
307
308 void FeedbackStyle::AddSignalInfo(FeedbackStyleInfo& styleInfo, SignalFeedbackInfo&& signalInfo)
309 {
310   bool                                  updated = false;
311   SignalFeedbackInfoContainer::iterator iter;
312
313   // If info exists for the signal then update it, else add new
314   for(iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
315   {
316     if((*iter).mSignalName == signalInfo.mSignalName)
317     {
318       (*iter).mHasHapticFeedbackInfo = signalInfo.mHasHapticFeedbackInfo;
319       (*iter).mHapticFeedbackPattern = signalInfo.mHapticFeedbackPattern;
320       (*iter).mHapticFeedbackFile    = signalInfo.mHapticFeedbackFile;
321       (*iter).mHasSoundFeedbackInfo  = signalInfo.mHasSoundFeedbackInfo;
322       (*iter).mSoundFeedbackPattern  = signalInfo.mSoundFeedbackPattern;
323       (*iter).mSoundFeedbackFile     = signalInfo.mSoundFeedbackFile;
324
325       updated = true;
326       break;
327     }
328   }
329
330   if(!updated)
331   {
332     styleInfo.mSignalFeedbackInfoList.emplace_back(std::move(signalInfo));
333   }
334 }
335
336 void FeedbackStyle::PlayFeedback(const std::string& type, const std::string& signalName)
337 {
338   const FeedbackStyleInfo     styleInfo = GetStyleInfo(type);
339   SignalFeedbackInfoConstIter iter;
340
341   for(iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
342   {
343     const SignalFeedbackInfo& info = *iter;
344
345     if(info.mSignalName == signalName)
346     {
347       if(info.mHasHapticFeedbackInfo)
348       {
349         if(!info.mHapticFeedbackPattern.empty())
350         {
351           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());
352
353           mFeedback.PlayFeedbackPattern(FEEDBACK_TYPE_VIBRATION, GetFeedbackPattern(info.mHapticFeedbackPattern));
354         }
355         else if(!info.mHapticFeedbackFile.empty())
356         {
357           mFeedback.PlayFile(info.mHapticFeedbackFile);
358         }
359       }
360
361       if(info.mHasSoundFeedbackInfo)
362       {
363         if(!info.mSoundFeedbackPattern.empty())
364         {
365           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());
366
367           mFeedback.PlayFeedbackPattern(FEEDBACK_TYPE_SOUND, GetFeedbackPattern(info.mSoundFeedbackPattern));
368         }
369         else if(!info.mSoundFeedbackFile.empty())
370         {
371           mFeedback.PlaySound(info.mSoundFeedbackFile);
372         }
373       }
374
375       break;
376     }
377   }
378 }
379
380 FeedbackPattern FeedbackStyle::GetFeedbackPattern(const std::string& pattern)
381 {
382   if(0 == mFeedbackPatternLut.size())
383   {
384     mFeedbackPatternLut["FEEDBACK_PATTERN_NONE"]                = Dali::FEEDBACK_PATTERN_NONE;
385     mFeedbackPatternLut["FEEDBACK_PATTERN_TAP"]                 = Dali::FEEDBACK_PATTERN_TAP;
386     mFeedbackPatternLut["FEEDBACK_PATTERN_SIP"]                 = Dali::FEEDBACK_PATTERN_SIP;
387     mFeedbackPatternLut["FEEDBACK_PATTERN_SIP_BACKSPACE"]       = Dali::FEEDBACK_PATTERN_SIP_BACKSPACE;
388     mFeedbackPatternLut["FEEDBACK_PATTERN_MAX_CHARACTER"]       = Dali::FEEDBACK_PATTERN_MAX_CHARACTER;
389     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY0"]                = Dali::FEEDBACK_PATTERN_KEY0;
390     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY1"]                = Dali::FEEDBACK_PATTERN_KEY1;
391     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY2"]                = Dali::FEEDBACK_PATTERN_KEY2;
392     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY3"]                = Dali::FEEDBACK_PATTERN_KEY3;
393     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY4"]                = Dali::FEEDBACK_PATTERN_KEY4;
394     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY5"]                = Dali::FEEDBACK_PATTERN_KEY5;
395     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY6"]                = Dali::FEEDBACK_PATTERN_KEY6;
396     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY7"]                = Dali::FEEDBACK_PATTERN_KEY7;
397     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY8"]                = Dali::FEEDBACK_PATTERN_KEY8;
398     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY9"]                = Dali::FEEDBACK_PATTERN_KEY9;
399     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY_STAR"]            = Dali::FEEDBACK_PATTERN_KEY_STAR;
400     mFeedbackPatternLut["FEEDBACK_PATTERN_KEY_SHARP"]           = Dali::FEEDBACK_PATTERN_KEY_SHARP;
401     mFeedbackPatternLut["FEEDBACK_PATTERN_HOLD"]                = Dali::FEEDBACK_PATTERN_HOLD;
402     mFeedbackPatternLut["FEEDBACK_PATTERN_MULTI_TAP"]           = Dali::FEEDBACK_PATTERN_MULTI_TAP;
403     mFeedbackPatternLut["FEEDBACK_PATTERN_HW_TAP"]              = Dali::FEEDBACK_PATTERN_HW_TAP;
404     mFeedbackPatternLut["FEEDBACK_PATTERN_HW_HOLD"]             = Dali::FEEDBACK_PATTERN_HW_HOLD;
405     mFeedbackPatternLut["FEEDBACK_PATTERN_MESSAGE"]             = Dali::FEEDBACK_PATTERN_MESSAGE;
406     mFeedbackPatternLut["FEEDBACK_PATTERN_MESSAGE_ON_CALL"]     = Dali::FEEDBACK_PATTERN_MESSAGE_ON_CALL;
407     mFeedbackPatternLut["FEEDBACK_PATTERN_EMAIL"]               = Dali::FEEDBACK_PATTERN_EMAIL;
408     mFeedbackPatternLut["FEEDBACK_PATTERN_EMAIL_ON_CALL"]       = Dali::FEEDBACK_PATTERN_EMAIL_ON_CALL;
409     mFeedbackPatternLut["FEEDBACK_PATTERN_WAKEUP"]              = Dali::FEEDBACK_PATTERN_WAKEUP;
410     mFeedbackPatternLut["FEEDBACK_PATTERN_WAKEUP_ON_CALL"]      = Dali::FEEDBACK_PATTERN_WAKEUP_ON_CALL;
411     mFeedbackPatternLut["FEEDBACK_PATTERN_SCHEDULE"]            = Dali::FEEDBACK_PATTERN_SCHEDULE;
412     mFeedbackPatternLut["FEEDBACK_PATTERN_SCHEDULE_ON_CALL"]    = Dali::FEEDBACK_PATTERN_SCHEDULE_ON_CALL;
413     mFeedbackPatternLut["FEEDBACK_PATTERN_TIMER"]               = Dali::FEEDBACK_PATTERN_TIMER;
414     mFeedbackPatternLut["FEEDBACK_PATTERN_TIMER_ON_CALL"]       = Dali::FEEDBACK_PATTERN_TIMER_ON_CALL;
415     mFeedbackPatternLut["FEEDBACK_PATTERN_GENERAL"]             = Dali::FEEDBACK_PATTERN_GENERAL;
416     mFeedbackPatternLut["FEEDBACK_PATTERN_GENERAL_ON_CALL"]     = Dali::FEEDBACK_PATTERN_GENERAL_ON_CALL;
417     mFeedbackPatternLut["FEEDBACK_PATTERN_POWER_ON"]            = Dali::FEEDBACK_PATTERN_POWER_ON;
418     mFeedbackPatternLut["FEEDBACK_PATTERN_POWER_OFF"]           = Dali::FEEDBACK_PATTERN_POWER_OFF;
419     mFeedbackPatternLut["FEEDBACK_PATTERN_CHARGERCONN"]         = Dali::FEEDBACK_PATTERN_CHARGERCONN;
420     mFeedbackPatternLut["FEEDBACK_PATTERN_CHARGERCONN_ON_CALL"] = Dali::FEEDBACK_PATTERN_CHARGERCONN_ON_CALL;
421     mFeedbackPatternLut["FEEDBACK_PATTERN_FULLCHARGED"]         = Dali::FEEDBACK_PATTERN_FULLCHARGED;
422     mFeedbackPatternLut["FEEDBACK_PATTERN_FULLCHARGED_ON_CALL"] = Dali::FEEDBACK_PATTERN_FULLCHARGED_ON_CALL;
423     mFeedbackPatternLut["FEEDBACK_PATTERN_LOWBATT"]             = Dali::FEEDBACK_PATTERN_LOWBATT;
424     mFeedbackPatternLut["FEEDBACK_PATTERN_LOWBATT_ON_CALL"]     = Dali::FEEDBACK_PATTERN_LOWBATT_ON_CALL;
425     mFeedbackPatternLut["FEEDBACK_PATTERN_LOCK"]                = Dali::FEEDBACK_PATTERN_LOCK;
426     mFeedbackPatternLut["FEEDBACK_PATTERN_UNLOCK"]              = Dali::FEEDBACK_PATTERN_UNLOCK;
427     mFeedbackPatternLut["FEEDBACK_PATTERN_CALLCONNECT"]         = Dali::FEEDBACK_PATTERN_CALLCONNECT;
428     mFeedbackPatternLut["FEEDBACK_PATTERN_DISCALLCONNECT"]      = Dali::FEEDBACK_PATTERN_DISCALLCONNECT;
429     mFeedbackPatternLut["FEEDBACK_PATTERN_MINUTEMINDER"]        = Dali::FEEDBACK_PATTERN_MINUTEMINDER;
430     mFeedbackPatternLut["FEEDBACK_PATTERN_VIBRATION"]           = Dali::FEEDBACK_PATTERN_VIBRATION;
431     mFeedbackPatternLut["FEEDBACK_PATTERN_SHUTTER"]             = Dali::FEEDBACK_PATTERN_SHUTTER;
432     mFeedbackPatternLut["FEEDBACK_PATTERN_LIST_REORDER"]        = Dali::FEEDBACK_PATTERN_LIST_REORDER;
433     mFeedbackPatternLut["FEEDBACK_PATTERN_SLIDER_SWEEP"]        = Dali::FEEDBACK_PATTERN_SLIDER_SWEEP;
434   }
435
436   std::map<const std::string, FeedbackPattern>::const_iterator iter(mFeedbackPatternLut.find(pattern));
437
438   if(iter != mFeedbackPatternLut.end())
439   {
440     return iter->second;
441   }
442   else
443   {
444     DALI_LOG_ERROR("Unknown feedback pattern type: %s, So Defaulting to FEEDBACK_PATTERN_NONE!\n");
445     return Dali::FEEDBACK_PATTERN_NONE;
446   }
447 }
448
449 } // namespace Internal
450
451 } // namespace Toolkit
452
453 } // namespace Dali