2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/feedback/feedback-style.h>
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>
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>
32 namespace // unnamed namespace
34 #if defined(DEBUG_ENABLED)
35 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::General, false, "LOG_FEEDBACK");
38 const char* DEFAULT_FEEDBACK_THEME_FILE_NAME = "default-feedback-theme.json";
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)
43 const Dali::Toolkit::TreeNode* child = node.GetChild(name);
45 Dali::Toolkit::TreeNode::STRING == child->GetType())
48 str = child->GetString();
52 } // unnamed namespace
60 struct SignalFeedbackInfo
63 * Default constructor.
66 : mHasHapticFeedbackInfo(false),
67 mHasSoundFeedbackInfo(false)
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;
80 typedef std::vector<SignalFeedbackInfo> SignalFeedbackInfoContainer;
81 typedef SignalFeedbackInfoContainer::const_iterator SignalFeedbackInfoConstIter;
83 struct FeedbackStyleInfo
86 * Default constructor.
92 std::string mTypeName;
94 SignalFeedbackInfoContainer mSignalFeedbackInfoList;
97 static const FeedbackStyleInfo DEFAULT_FEEDBACK_STYLE_INFO;
99 FeedbackStyle::FeedbackStyle()
101 mFeedback = Dali::FeedbackPlayer::Get();
103 const std::string styleDirPath = AssetManager::GetDaliStylePath();
104 const std::string defaultThemeFilePath = styleDirPath + DEFAULT_FEEDBACK_THEME_FILE_NAME;
106 std::string defaultTheme;
108 if(mFeedback && mFeedback.LoadFile(defaultThemeFilePath, defaultTheme))
110 LoadTheme(defaultTheme);
111 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "ResourceLoader::LoadTheme(%s) - loaded %d bytes\n", defaultThemeFilePath.c_str(), defaultTheme.size());
115 DALI_LOG_ERROR("ResourceLoader::LoadTheme(%s) - failed to load\n", defaultThemeFilePath.c_str());
119 FeedbackStyle::~FeedbackStyle()
123 struct PlayFeedbackFromSignal
125 PlayFeedbackFromSignal(FeedbackStyle& controller, const std::string& typeName, const std::string& signalName)
126 : mController(controller),
128 mSignalName(signalName)
134 mController.PlayFeedback(mTypeName, mSignalName);
137 FeedbackStyle& mController;
138 std::string mTypeName;
139 std::string mSignalName;
142 void FeedbackStyle::ObjectCreated(BaseHandle handle)
146 const std::string& type = handle.GetTypeName();
148 const FeedbackStyleInfo styleInfo = GetStyleInfo(type);
150 for(SignalFeedbackInfoConstIter iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
152 const SignalFeedbackInfo& info = *iter;
154 if(info.mHasHapticFeedbackInfo || info.mHasSoundFeedbackInfo)
156 if(!info.mHapticFeedbackPattern.empty() || !info.mHapticFeedbackFile.empty() ||
157 !info.mSoundFeedbackPattern.empty() || !info.mSoundFeedbackFile.empty())
159 handle.ConnectSignal(this,
161 PlayFeedbackFromSignal(*this, type, info.mSignalName));
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());
167 DALI_LOG_ERROR("FeedbackStyle::Set() Warning Inconsistent data in theme file!\n");
174 const FeedbackStyleInfo& FeedbackStyle::GetStyleInfo(const std::string& type) const
176 std::map<const std::string, FeedbackStyleInfo>::const_iterator iter(mStyleInfoLut.find(type));
177 if(iter != mStyleInfoLut.end())
183 return DEFAULT_FEEDBACK_STYLE_INFO;
187 void FeedbackStyle::StyleChanged(const std::string& userDefinedThemePath, Dali::StyleChange::Type styleChange)
189 if(styleChange == StyleChange::THEME_CHANGE)
191 std::string userDefinedTheme;
193 if(mFeedback && mFeedback.LoadFile(userDefinedThemePath, userDefinedTheme))
195 if(!LoadTheme(userDefinedTheme))
197 DALI_LOG_ERROR("FeedbackStyle::StyleChanged() User defined theme failed to load! \n");
199 const std::string styleDirPath = AssetManager::GetDaliStylePath();
200 const std::string defaultThemeFilePath = styleDirPath + DEFAULT_FEEDBACK_THEME_FILE_NAME;
202 //If there is any problem is using the user defined theme, then fall back to default theme
203 if(!LoadTheme(defaultThemeFilePath))
205 //If the default theme fails, Then No luck!
206 DALI_LOG_ERROR("FeedbackStyle::StyleChanged() Default theme failed to load! \n");
211 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "ResourceLoader::LoadTheme(%s) - loaded %d bytes\n", userDefinedThemePath.c_str(), userDefinedTheme.size());
216 DALI_LOG_ERROR("ResourceLoader::LoadTheme(%s) - failed to load\n", userDefinedThemePath.c_str());
221 bool FeedbackStyle::LoadTheme(const std::string& data)
227 LoadFromString(data);
233 //Problem in user set theme, So fallback to use default theme.
234 DALI_LOG_ERROR("FeedbackStyle::LoadTheme() Failed to load theme\n");
240 void FeedbackStyle::LoadFromString(const std::string& data)
242 Toolkit::JsonParser parser = Toolkit::JsonParser::New();
243 const Toolkit::TreeNode* root = NULL;
245 if(!parser.Parse(data))
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());
254 root = parser.GetRoot();
259 // Clear previously loaded style
260 mStyleInfoLut.clear();
263 if(const TreeNode* node = root->GetChild("style"))
265 Toolkit::TreeNode::ConstIterator iter = node->CBegin();
266 Toolkit::TreeNode::ConstIterator end = node->CEnd();
267 for(; iter != end; ++iter)
269 const char* key = (*iter).first;
270 FeedbackStyleInfo themeInfo;
271 themeInfo.mTypeName = key;
273 if(const TreeNode* signals = (*iter).second.GetChild("signals"))
275 TreeNode::ConstIterator signalIter = signals->CBegin();
276 TreeNode::ConstIterator signalEnd = signals->CEnd();
277 for(; signalIter != signalEnd; ++signalIter)
279 SignalFeedbackInfo signalFeedbackInfo;
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();
285 GetIfString((*signalIter).second, "hapticFeedbackPattern", signalFeedbackInfo.mHasHapticFeedbackInfo, signalFeedbackInfo.mHapticFeedbackPattern);
287 GetIfString((*signalIter).second, "hapticFeedbackFile", signalFeedbackInfo.mHasHapticFeedbackInfo, signalFeedbackInfo.mHapticFeedbackFile);
289 GetIfString((*signalIter).second, "soundFeedbackPattern", signalFeedbackInfo.mHasSoundFeedbackInfo, signalFeedbackInfo.mSoundFeedbackPattern);
291 GetIfString((*signalIter).second, "hapticFeedbackFile", signalFeedbackInfo.mHasSoundFeedbackInfo, signalFeedbackInfo.mSoundFeedbackFile);
293 if(signalFeedbackInfo.mHasHapticFeedbackInfo || signalFeedbackInfo.mHasSoundFeedbackInfo)
295 AddSignalInfo(themeInfo, std::move(signalFeedbackInfo));
300 mStyleInfoLut[key] = themeInfo;
306 } // LoadFromString()
308 void FeedbackStyle::AddSignalInfo(FeedbackStyleInfo& styleInfo, SignalFeedbackInfo&& signalInfo)
310 bool updated = false;
311 SignalFeedbackInfoContainer::iterator iter;
313 // If info exists for the signal then update it, else add new
314 for(iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
316 if((*iter).mSignalName == signalInfo.mSignalName)
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;
332 styleInfo.mSignalFeedbackInfoList.emplace_back(std::move(signalInfo));
336 void FeedbackStyle::PlayFeedback(const std::string& type, const std::string& signalName)
338 const FeedbackStyleInfo styleInfo = GetStyleInfo(type);
339 SignalFeedbackInfoConstIter iter;
341 for(iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
343 const SignalFeedbackInfo& info = *iter;
345 if(info.mSignalName == signalName)
347 if(info.mHasHapticFeedbackInfo)
349 if(!info.mHapticFeedbackPattern.empty())
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());
353 mFeedback.PlayFeedbackPattern(FEEDBACK_TYPE_VIBRATION, GetFeedbackPattern(info.mHapticFeedbackPattern));
355 else if(!info.mHapticFeedbackFile.empty())
357 mFeedback.PlayFile(info.mHapticFeedbackFile);
361 if(info.mHasSoundFeedbackInfo)
363 if(!info.mSoundFeedbackPattern.empty())
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());
367 mFeedback.PlayFeedbackPattern(FEEDBACK_TYPE_SOUND, GetFeedbackPattern(info.mSoundFeedbackPattern));
369 else if(!info.mSoundFeedbackFile.empty())
371 mFeedback.PlaySound(info.mSoundFeedbackFile);
380 FeedbackPattern FeedbackStyle::GetFeedbackPattern(const std::string& pattern)
382 if(0 == mFeedbackPatternLut.size())
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;
436 std::map<const std::string, FeedbackPattern>::const_iterator iter(mFeedbackPatternLut.find(pattern));
438 if(iter != mFeedbackPatternLut.end())
444 DALI_LOG_ERROR("Unknown feedback pattern type: %s, So Defaulting to FEEDBACK_PATTERN_NONE!\n");
445 return Dali::FEEDBACK_PATTERN_NONE;
449 } // namespace Internal
451 } // namespace Toolkit