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