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 FeedbackStyle::FeedbackStyle()
99 mFeedback = Dali::FeedbackPlayer::Get();
101 const std::string styleDirPath = AssetManager::GetDaliStylePath();
102 const std::string defaultThemeFilePath = styleDirPath + DEFAULT_FEEDBACK_THEME_FILE_NAME;
104 std::string defaultTheme;
106 if(mFeedback && mFeedback.LoadFile(defaultThemeFilePath, defaultTheme))
108 LoadTheme(defaultTheme);
109 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "ResourceLoader::LoadTheme(%s) - loaded %d bytes\n", defaultThemeFilePath.c_str(), defaultTheme.size());
113 DALI_LOG_ERROR("ResourceLoader::LoadTheme(%s) - failed to load\n", defaultThemeFilePath.c_str());
117 FeedbackStyle::~FeedbackStyle()
121 struct PlayFeedbackFromSignal
123 PlayFeedbackFromSignal(FeedbackStyle& controller, const std::string& typeName, const std::string& signalName)
124 : mController(controller),
126 mSignalName(signalName)
132 mController.PlayFeedback(mTypeName, mSignalName);
135 FeedbackStyle& mController;
136 std::string mTypeName;
137 std::string mSignalName;
140 void FeedbackStyle::ObjectCreated(BaseHandle handle)
144 const std::string& type = handle.GetTypeName();
146 const FeedbackStyleInfo styleInfo = GetStyleInfo(type);
148 for(SignalFeedbackInfoConstIter iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
150 const SignalFeedbackInfo& info = *iter;
152 if(info.mHasHapticFeedbackInfo || info.mHasSoundFeedbackInfo)
154 if(!info.mHapticFeedbackPattern.empty() || !info.mHapticFeedbackFile.empty() ||
155 !info.mSoundFeedbackPattern.empty() || !info.mSoundFeedbackFile.empty())
157 handle.ConnectSignal(this,
159 PlayFeedbackFromSignal(*this, type, info.mSignalName));
161 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());
165 DALI_LOG_ERROR("FeedbackStyle::Set() Warning Inconsistent data in theme file!\n");
172 const FeedbackStyleInfo& FeedbackStyle::GetStyleInfo(const std::string& type) const
174 std::map<const std::string, FeedbackStyleInfo>::const_iterator iter(mStyleInfoLut.find(type));
175 if(iter != mStyleInfoLut.end())
181 static const FeedbackStyleInfo DEFAULT_FEEDBACK_STYLE_INFO;
182 return DEFAULT_FEEDBACK_STYLE_INFO;
186 void FeedbackStyle::StyleChanged(const std::string& userDefinedThemePath, Dali::StyleChange::Type styleChange)
188 if(styleChange == StyleChange::THEME_CHANGE)
190 std::string userDefinedTheme;
192 if(mFeedback && mFeedback.LoadFile(userDefinedThemePath, userDefinedTheme))
194 if(!LoadTheme(userDefinedTheme))
196 DALI_LOG_ERROR("FeedbackStyle::StyleChanged() User defined theme failed to load! \n");
198 const std::string styleDirPath = AssetManager::GetDaliStylePath();
199 const std::string defaultThemeFilePath = styleDirPath + DEFAULT_FEEDBACK_THEME_FILE_NAME;
201 //If there is any problem is using the user defined theme, then fall back to default theme
202 if(!LoadTheme(defaultThemeFilePath))
204 //If the default theme fails, Then No luck!
205 DALI_LOG_ERROR("FeedbackStyle::StyleChanged() Default theme failed to load! \n");
210 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "ResourceLoader::LoadTheme(%s) - loaded %d bytes\n", userDefinedThemePath.c_str(), userDefinedTheme.size());
215 DALI_LOG_ERROR("ResourceLoader::LoadTheme(%s) - failed to load\n", userDefinedThemePath.c_str());
220 bool FeedbackStyle::LoadTheme(const std::string& data)
226 LoadFromString(data);
232 //Problem in user set theme, So fallback to use default theme.
233 DALI_LOG_ERROR("FeedbackStyle::LoadTheme() Failed to load theme\n");
239 void FeedbackStyle::LoadFromString(const std::string& data)
241 Toolkit::JsonParser parser = Toolkit::JsonParser::New();
242 const Toolkit::TreeNode* root = NULL;
244 if(!parser.Parse(data))
246 DALI_LOG_WARNING("JSON Parse Error:'%s'\n", parser.GetErrorDescription().c_str());
247 DALI_LOG_WARNING("JSON Parse Line :'%d (%d)'\n",
248 parser.GetErrorLineNumber(),
249 parser.GetErrorColumn());
253 root = parser.GetRoot();
258 // Clear previously loaded style
259 mStyleInfoLut.clear();
262 if(const TreeNode* node = root->GetChild("style"))
264 Toolkit::TreeNode::ConstIterator iter = node->CBegin();
265 Toolkit::TreeNode::ConstIterator end = node->CEnd();
266 for(; iter != end; ++iter)
268 const char* key = (*iter).first;
269 FeedbackStyleInfo themeInfo;
270 themeInfo.mTypeName = key;
272 if(const TreeNode* signals = (*iter).second.GetChild("signals"))
274 TreeNode::ConstIterator signalIter = signals->CBegin();
275 TreeNode::ConstIterator signalEnd = signals->CEnd();
276 for(; signalIter != signalEnd; ++signalIter)
278 SignalFeedbackInfo signalFeedbackInfo;
280 const TreeNode* type = (*signalIter).second.GetChild("type");
281 DALI_ASSERT_ALWAYS(type && TreeNode::STRING == type->GetType() && "Signal must have a type");
282 signalFeedbackInfo.mSignalName = type->GetString();
284 GetIfString((*signalIter).second, "hapticFeedbackPattern", signalFeedbackInfo.mHasHapticFeedbackInfo, signalFeedbackInfo.mHapticFeedbackPattern);
286 GetIfString((*signalIter).second, "hapticFeedbackFile", signalFeedbackInfo.mHasHapticFeedbackInfo, signalFeedbackInfo.mHapticFeedbackFile);
288 GetIfString((*signalIter).second, "soundFeedbackPattern", signalFeedbackInfo.mHasSoundFeedbackInfo, signalFeedbackInfo.mSoundFeedbackPattern);
290 GetIfString((*signalIter).second, "hapticFeedbackFile", signalFeedbackInfo.mHasSoundFeedbackInfo, signalFeedbackInfo.mSoundFeedbackFile);
292 if(signalFeedbackInfo.mHasHapticFeedbackInfo || signalFeedbackInfo.mHasSoundFeedbackInfo)
294 AddSignalInfo(themeInfo, std::move(signalFeedbackInfo));
299 mStyleInfoLut[key] = themeInfo;
305 } // LoadFromString()
307 void FeedbackStyle::AddSignalInfo(FeedbackStyleInfo& styleInfo, SignalFeedbackInfo&& signalInfo)
309 bool updated = false;
310 SignalFeedbackInfoContainer::iterator iter;
312 // If info exists for the signal then update it, else add new
313 for(iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
315 if((*iter).mSignalName == signalInfo.mSignalName)
317 (*iter).mHasHapticFeedbackInfo = signalInfo.mHasHapticFeedbackInfo;
318 (*iter).mHapticFeedbackPattern = signalInfo.mHapticFeedbackPattern;
319 (*iter).mHapticFeedbackFile = signalInfo.mHapticFeedbackFile;
320 (*iter).mHasSoundFeedbackInfo = signalInfo.mHasSoundFeedbackInfo;
321 (*iter).mSoundFeedbackPattern = signalInfo.mSoundFeedbackPattern;
322 (*iter).mSoundFeedbackFile = signalInfo.mSoundFeedbackFile;
331 styleInfo.mSignalFeedbackInfoList.emplace_back(std::move(signalInfo));
335 void FeedbackStyle::PlayFeedback(const std::string& type, const std::string& signalName)
337 const FeedbackStyleInfo styleInfo = GetStyleInfo(type);
338 SignalFeedbackInfoConstIter iter;
340 for(iter = styleInfo.mSignalFeedbackInfoList.begin(); iter != styleInfo.mSignalFeedbackInfoList.end(); ++iter)
342 const SignalFeedbackInfo& info = *iter;
344 if(info.mSignalName == signalName)
346 if(info.mHasHapticFeedbackInfo)
348 if(!info.mHapticFeedbackPattern.empty())
350 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 mFeedback.PlayFeedbackPattern(FEEDBACK_TYPE_VIBRATION, GetFeedbackPattern(info.mHapticFeedbackPattern));
354 else if(!info.mHapticFeedbackFile.empty())
356 mFeedback.PlayFile(info.mHapticFeedbackFile);
360 if(info.mHasSoundFeedbackInfo)
362 if(!info.mSoundFeedbackPattern.empty())
364 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 mFeedback.PlayFeedbackPattern(FEEDBACK_TYPE_SOUND, GetFeedbackPattern(info.mSoundFeedbackPattern));
368 else if(!info.mSoundFeedbackFile.empty())
370 mFeedback.PlaySound(info.mSoundFeedbackFile);
379 FeedbackPattern FeedbackStyle::GetFeedbackPattern(const std::string& pattern)
381 if(0 == mFeedbackPatternLut.size())
383 mFeedbackPatternLut["FEEDBACK_PATTERN_NONE"] = Dali::FEEDBACK_PATTERN_NONE;
384 mFeedbackPatternLut["FEEDBACK_PATTERN_TAP"] = Dali::FEEDBACK_PATTERN_TAP;
385 mFeedbackPatternLut["FEEDBACK_PATTERN_SIP"] = Dali::FEEDBACK_PATTERN_SIP;
386 mFeedbackPatternLut["FEEDBACK_PATTERN_SIP_BACKSPACE"] = Dali::FEEDBACK_PATTERN_SIP_BACKSPACE;
387 mFeedbackPatternLut["FEEDBACK_PATTERN_MAX_CHARACTER"] = Dali::FEEDBACK_PATTERN_MAX_CHARACTER;
388 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY0"] = Dali::FEEDBACK_PATTERN_KEY0;
389 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY1"] = Dali::FEEDBACK_PATTERN_KEY1;
390 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY2"] = Dali::FEEDBACK_PATTERN_KEY2;
391 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY3"] = Dali::FEEDBACK_PATTERN_KEY3;
392 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY4"] = Dali::FEEDBACK_PATTERN_KEY4;
393 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY5"] = Dali::FEEDBACK_PATTERN_KEY5;
394 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY6"] = Dali::FEEDBACK_PATTERN_KEY6;
395 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY7"] = Dali::FEEDBACK_PATTERN_KEY7;
396 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY8"] = Dali::FEEDBACK_PATTERN_KEY8;
397 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY9"] = Dali::FEEDBACK_PATTERN_KEY9;
398 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY_STAR"] = Dali::FEEDBACK_PATTERN_KEY_STAR;
399 mFeedbackPatternLut["FEEDBACK_PATTERN_KEY_SHARP"] = Dali::FEEDBACK_PATTERN_KEY_SHARP;
400 mFeedbackPatternLut["FEEDBACK_PATTERN_HOLD"] = Dali::FEEDBACK_PATTERN_HOLD;
401 mFeedbackPatternLut["FEEDBACK_PATTERN_MULTI_TAP"] = Dali::FEEDBACK_PATTERN_MULTI_TAP;
402 mFeedbackPatternLut["FEEDBACK_PATTERN_HW_TAP"] = Dali::FEEDBACK_PATTERN_HW_TAP;
403 mFeedbackPatternLut["FEEDBACK_PATTERN_HW_HOLD"] = Dali::FEEDBACK_PATTERN_HW_HOLD;
404 mFeedbackPatternLut["FEEDBACK_PATTERN_MESSAGE"] = Dali::FEEDBACK_PATTERN_MESSAGE;
405 mFeedbackPatternLut["FEEDBACK_PATTERN_MESSAGE_ON_CALL"] = Dali::FEEDBACK_PATTERN_MESSAGE_ON_CALL;
406 mFeedbackPatternLut["FEEDBACK_PATTERN_EMAIL"] = Dali::FEEDBACK_PATTERN_EMAIL;
407 mFeedbackPatternLut["FEEDBACK_PATTERN_EMAIL_ON_CALL"] = Dali::FEEDBACK_PATTERN_EMAIL_ON_CALL;
408 mFeedbackPatternLut["FEEDBACK_PATTERN_WAKEUP"] = Dali::FEEDBACK_PATTERN_WAKEUP;
409 mFeedbackPatternLut["FEEDBACK_PATTERN_WAKEUP_ON_CALL"] = Dali::FEEDBACK_PATTERN_WAKEUP_ON_CALL;
410 mFeedbackPatternLut["FEEDBACK_PATTERN_SCHEDULE"] = Dali::FEEDBACK_PATTERN_SCHEDULE;
411 mFeedbackPatternLut["FEEDBACK_PATTERN_SCHEDULE_ON_CALL"] = Dali::FEEDBACK_PATTERN_SCHEDULE_ON_CALL;
412 mFeedbackPatternLut["FEEDBACK_PATTERN_TIMER"] = Dali::FEEDBACK_PATTERN_TIMER;
413 mFeedbackPatternLut["FEEDBACK_PATTERN_TIMER_ON_CALL"] = Dali::FEEDBACK_PATTERN_TIMER_ON_CALL;
414 mFeedbackPatternLut["FEEDBACK_PATTERN_GENERAL"] = Dali::FEEDBACK_PATTERN_GENERAL;
415 mFeedbackPatternLut["FEEDBACK_PATTERN_GENERAL_ON_CALL"] = Dali::FEEDBACK_PATTERN_GENERAL_ON_CALL;
416 mFeedbackPatternLut["FEEDBACK_PATTERN_POWER_ON"] = Dali::FEEDBACK_PATTERN_POWER_ON;
417 mFeedbackPatternLut["FEEDBACK_PATTERN_POWER_OFF"] = Dali::FEEDBACK_PATTERN_POWER_OFF;
418 mFeedbackPatternLut["FEEDBACK_PATTERN_CHARGERCONN"] = Dali::FEEDBACK_PATTERN_CHARGERCONN;
419 mFeedbackPatternLut["FEEDBACK_PATTERN_CHARGERCONN_ON_CALL"] = Dali::FEEDBACK_PATTERN_CHARGERCONN_ON_CALL;
420 mFeedbackPatternLut["FEEDBACK_PATTERN_FULLCHARGED"] = Dali::FEEDBACK_PATTERN_FULLCHARGED;
421 mFeedbackPatternLut["FEEDBACK_PATTERN_FULLCHARGED_ON_CALL"] = Dali::FEEDBACK_PATTERN_FULLCHARGED_ON_CALL;
422 mFeedbackPatternLut["FEEDBACK_PATTERN_LOWBATT"] = Dali::FEEDBACK_PATTERN_LOWBATT;
423 mFeedbackPatternLut["FEEDBACK_PATTERN_LOWBATT_ON_CALL"] = Dali::FEEDBACK_PATTERN_LOWBATT_ON_CALL;
424 mFeedbackPatternLut["FEEDBACK_PATTERN_LOCK"] = Dali::FEEDBACK_PATTERN_LOCK;
425 mFeedbackPatternLut["FEEDBACK_PATTERN_UNLOCK"] = Dali::FEEDBACK_PATTERN_UNLOCK;
426 mFeedbackPatternLut["FEEDBACK_PATTERN_CALLCONNECT"] = Dali::FEEDBACK_PATTERN_CALLCONNECT;
427 mFeedbackPatternLut["FEEDBACK_PATTERN_DISCALLCONNECT"] = Dali::FEEDBACK_PATTERN_DISCALLCONNECT;
428 mFeedbackPatternLut["FEEDBACK_PATTERN_MINUTEMINDER"] = Dali::FEEDBACK_PATTERN_MINUTEMINDER;
429 mFeedbackPatternLut["FEEDBACK_PATTERN_VIBRATION"] = Dali::FEEDBACK_PATTERN_VIBRATION;
430 mFeedbackPatternLut["FEEDBACK_PATTERN_SHUTTER"] = Dali::FEEDBACK_PATTERN_SHUTTER;
431 mFeedbackPatternLut["FEEDBACK_PATTERN_LIST_REORDER"] = Dali::FEEDBACK_PATTERN_LIST_REORDER;
432 mFeedbackPatternLut["FEEDBACK_PATTERN_SLIDER_SWEEP"] = Dali::FEEDBACK_PATTERN_SLIDER_SWEEP;
435 std::map<const std::string, FeedbackPattern>::const_iterator iter(mFeedbackPatternLut.find(pattern));
437 if(iter != mFeedbackPatternLut.end())
443 DALI_LOG_ERROR("Unknown feedback pattern type: %s, So Defaulting to FEEDBACK_PATTERN_NONE!\n");
444 return Dali::FEEDBACK_PATTERN_NONE;
448 } // namespace Internal
450 } // namespace Toolkit