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