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