From 6dfe2d78f5a624637d6801cbe0feb6560654aadf Mon Sep 17 00:00:00 2001 From: pwasowski2 <38784511+pwasowski2@users.noreply.github.com> Date: Tue, 11 May 2021 03:50:55 +0200 Subject: [PATCH] [HapticFeedback] Implement vibrate() (#75) * [HapticFeedback] Implement vibrate() [Before] HapticFeedback.vibrate() was not implemented [After] The code below will cause a short vibration: HapticFeedback.vibrate() Signed-off-by: Pawel Wasowski * Make HapticFeedback.vibrate's dependencies dependent on profile HapticFeedback.vibrate will only work on mobile and wearable profiles so its dependencies should only be included in builds for these devices. Signed-off-by: Pawel Wasowski * Make FeedbackManager a singleton * [HapticFeedback.vibrate] Minfor fixes - use the proper "MOBILE_PROFILE" and "WEARABLE_PROFILE" names instead of "MOBILE" and "WEARABLE" - reorder lines in BUILD.gn Signed-off-by: Pawel Wasowski * Refactor implementation of HapticFeedback.vibrate() and BUILD.gn Signed-off-by: Pawel Wasowski * [HapticFeedback] Refactor FeedbackManager Signed-off-by: Pawel Wasowski * [HapticFeedback] Use FT_LOGE for all error logs --- shell/platform/tizen/BUILD.gn | 18 ++ .../tizen/channels/platform_channel.cc | 175 +++++++++++++++++- 2 files changed, 192 insertions(+), 1 deletion(-) diff --git a/shell/platform/tizen/BUILD.gn b/shell/platform/tizen/BUILD.gn index d0321482c6751..ae49587d59f18 100644 --- a/shell/platform/tizen/BUILD.gn +++ b/shell/platform/tizen/BUILD.gn @@ -52,8 +52,10 @@ config("tizen_rootstrap_include_dirs") { "$custom_sysroot/usr/include/emile-1", "$custom_sysroot/usr/include/eo-1", "$custom_sysroot/usr/include/evas-1", + "$custom_sysroot/usr/include/feedback", "$custom_sysroot/usr/include/system", "$custom_sysroot/usr/include/wayland-extension", + # For Evas_GL. "$custom_sysroot/usr/include/ecore-con-1", "$custom_sysroot/usr/include/ecore-file-1", @@ -77,6 +79,8 @@ config("tizen_rootstrap_include_dirs") { template("embedder_for_profile") { forward_variables_from(invoker, [ "use_evas_gl_renderer" ]) + profile = target_name + if (!defined(use_evas_gl_renderer)) { use_evas_gl_renderer = false } @@ -121,6 +125,20 @@ template("embedder_for_profile") { "wayland-client", ] + if (profile == "mobile") { + libs += [ + "capi-base-common", + "feedback", + ] + } + + if (profile == "wearable") { + libs += [ + "capi-base-common", + "feedback", + ] + } + defines = invoker.defines if (use_evas_gl_renderer) { diff --git a/shell/platform/tizen/channels/platform_channel.cc b/shell/platform/tizen/channels/platform_channel.cc index ccc330d6a09f9..172bd4a942610 100644 --- a/shell/platform/tizen/channels/platform_channel.cc +++ b/shell/platform/tizen/channels/platform_channel.cc @@ -5,6 +5,7 @@ #include "platform_channel.h" #include +#include #include @@ -28,6 +29,146 @@ PlatformChannel::PlatformChannel(flutter::BinaryMessenger* messenger, PlatformChannel::~PlatformChannel() {} +namespace { + +class FeedbackManager { + public: + enum class ResultCode { + kOk, + kNotSupportedError, + kPermissionDeniedError, + kUnknownError + }; + + static std::string GetVibrateVariantName(const char* haptic_feedback_type) { + FT_LOGD( + "Enter FeedbackManager::GetVibrateVariantName(): haptic_feedback_type: " + "(%s)", + haptic_feedback_type); + + if (!haptic_feedback_type) { + return "HapticFeedback.vibrate"; + } + + const size_t kPrefixToRemoveLen = strlen("HapticFeedbackType."); + + assert(strlen(haptic_feedback_type) >= kPrefixToRemoveLen); + + const std::string kHapticFeedbackPrefix = "HapticFeedback."; + + return kHapticFeedbackPrefix + + std::string{haptic_feedback_type + kPrefixToRemoveLen}; + } + + static std::string GetErrorMessage(const std::string& method_name, + ResultCode result_code) { + FT_LOGD( + "Enter FeedbackManager::GetErrorMessage(): method_name: (%s), " + "result_code: [%d]", + method_name.c_str(), static_cast(result_code)); + + switch (result_code) { + case ResultCode::kNotSupportedError: + return method_name + "() is not supported"; + case ResultCode::kPermissionDeniedError: + return std::string{"No permission to run "} + method_name + + "(). Add " + "\"http://tizen.org/privilege/feedback\" privilege to " + "tizen-manifest.xml " + "to use this method"; + case ResultCode::kUnknownError: + default: + return std::string{"An unknown error on "} + method_name + "() call"; + } + } + +#if defined(MOBILE_PROFILE) || defined(WEARABLE_PROFILE) + + static FeedbackManager& GetInstance() { + FT_LOGD("Enter FeedbackManager::GetInstance()"); + + static FeedbackManager instance; + return instance; + } + + FeedbackManager(const FeedbackManager&) = delete; + FeedbackManager& operator=(const FeedbackManager&) = delete; + + ResultCode Vibrate() { + FT_LOGD("Enter FeedbackManager::Vibrate()"); + + if (ResultCode::kOk != initialization_status_) { + FT_LOGE("Cannot run Vibrate(): initialization_status_: [%d]", + static_cast(initialization_status_)); + return initialization_status_; + } + + auto ret = + feedback_play_type(FEEDBACK_TYPE_VIBRATION, FEEDBACK_PATTERN_SIP); + if (FEEDBACK_ERROR_NONE == ret) { + FT_LOGD("feedback_play_type() succeeded"); + return ResultCode::kOk; + } + FT_LOGE("feedback_play_type() failed with error: [%d] (%s)", ret, + get_error_message(ret)); + + return NativeErrorToResultCode(ret); + } + + private: + static ResultCode NativeErrorToResultCode(int native_error_code) { + FT_LOGD("Enter NativeErrorToResultCode: native_error_code: [%d]", + native_error_code); + + switch (native_error_code) { + case FEEDBACK_ERROR_NONE: + return ResultCode::kOk; + case FEEDBACK_ERROR_NOT_SUPPORTED: + return ResultCode::kNotSupportedError; + case FEEDBACK_ERROR_PERMISSION_DENIED: + return ResultCode::kPermissionDeniedError; + case FEEDBACK_ERROR_OPERATION_FAILED: + case FEEDBACK_ERROR_INVALID_PARAMETER: + case FEEDBACK_ERROR_NOT_INITIALIZED: + default: + return ResultCode::kUnknownError; + } + } + + FeedbackManager() { + FT_LOGD("Enter FeedbackManager::FeedbackManager()"); + + auto ret = feedback_initialize(); + if (FEEDBACK_ERROR_NONE != ret) { + FT_LOGE("feedback_initialize() failed with error: [%d] (%s)", ret, + get_error_message(ret)); + initialization_status_ = NativeErrorToResultCode(ret); + return; + } + FT_LOGD("feedback_initialize() succeeded"); + + initialization_status_ = ResultCode::kOk; + } + + ~FeedbackManager() { + FT_LOGD("Enter FeedbackManager::~FeedbackManager"); + + auto ret = feedback_deinitialize(); + if (FEEDBACK_ERROR_NONE != ret) { + FT_LOGE("feedback_deinitialize() failed with error: [%d] (%s)", ret, + get_error_message(ret)); + return; + } + FT_LOGD("feedback_deinitialize() succeeded"); + } + + ResultCode initialization_status_ = ResultCode::kUnknownError; + +#endif // defined(MOBILE_PROFILE) || defined(WEARABLE_PROFILE) +}; + +} // namespace + void PlatformChannel::HandleMethodCall( const flutter::MethodCall& call, std::unique_ptr> result) { @@ -39,7 +180,39 @@ void PlatformChannel::HandleMethodCall( } else if (method == "SystemSound.play") { result->NotImplemented(); } else if (method == "HapticFeedback.vibrate") { - result->NotImplemented(); + FT_LOGD("HapticFeedback.vibrate() call received"); + + const std::string error_message = "Could not vibrate"; + +#if defined(MOBILE_PROFILE) || defined(WEARABLE_PROFILE) + /* + * We use a single type of vibration (FEEDBACK_PATTERN_SIP) to implement + * HapticFeedback's vibrate, lightImpact, mediumImpact, heavyImpact + * and selectionClick methods, because Tizen's "feedback" module + * has no dedicated vibration types for them. + * Thus, we ignore the "arguments" contents for "HapticFeedback.vibrate" + * calls. + */ + + auto ret = FeedbackManager::GetInstance().Vibrate(); + if (FeedbackManager::ResultCode::kOk == ret) { + result->Success(); + return; + } + + const auto vibrate_variant_name = + FeedbackManager::GetVibrateVariantName(call.arguments()[0].GetString()); + const auto error_cause = + FeedbackManager::GetErrorMessage(vibrate_variant_name, ret); + FT_LOGE("%s: %s", error_cause.c_str(), error_message.c_str()); +#else + const auto vibrate_variant_name = + FeedbackManager::GetVibrateVariantName(call.arguments()[0].GetString()); + const auto error_cause = FeedbackManager::GetErrorMessage( + vibrate_variant_name, FeedbackManager::ResultCode::kNotSupportedError); +#endif // defined(MOBILE_PROFILE) || defined(WEARABLE_PROFILE) + + result->Error(error_cause, error_message); } else if (method == "Clipboard.getData") { result->NotImplemented(); } else if (method == "Clipboard.setData") {