diff --git a/shell/platform/tizen/BUILD.gn b/shell/platform/tizen/BUILD.gn index 675623d5419c0..baa854ccbf63f 100644 --- a/shell/platform/tizen/BUILD.gn +++ b/shell/platform/tizen/BUILD.gn @@ -115,7 +115,9 @@ template("embedder") { "channels/key_event_channel.cc", "channels/lifecycle_channel.cc", "channels/navigation_channel.cc", + "channels/platform_channel.cc", "channels/platform_view_channel.cc", + "channels/settings_channel.cc", "channels/text_input_channel.cc", "flutter_project_bundle.cc", "flutter_tizen.cc", @@ -152,8 +154,7 @@ template("embedder") { sources += [ "channels/app_control.cc", "channels/app_control_channel.cc", - "channels/platform_channel.cc", - "channels/settings_channel.cc", + "channels/platform_channel_tizen.cc", "channels/settings_channel_tizen.cc", "external_texture_pixel_gl.cc", "external_texture_surface_gl.cc", @@ -177,8 +178,7 @@ template("embedder") { ] } else { sources += [ - "channels/platform_channel_stub.cc", - "channels/settings_channel.cc", + "channels/platform_channel_linux.cc", "channels/settings_channel_linux.cc", "external_texture_pixel_gl_stub.cc", "external_texture_surface_gl_stub.cc", diff --git a/shell/platform/tizen/channels/platform_channel.cc b/shell/platform/tizen/channels/platform_channel.cc index e22fbaa1748b6..71173589ebf39 100644 --- a/shell/platform/tizen/channels/platform_channel.cc +++ b/shell/platform/tizen/channels/platform_channel.cc @@ -4,11 +4,6 @@ #include "platform_channel.h" -#include -#include - -#include - #include "flutter/shell/platform/common/json_method_codec.h" #include "flutter/shell/platform/tizen/logger.h" @@ -34,176 +29,18 @@ constexpr char kRestoreSystemUIOverlaysMethod[] = constexpr char kSetSystemUIOverlayStyleMethod[] = "SystemChrome.setSystemUIOverlayStyle"; -constexpr char kSoundTypeClick[] = "SystemSoundType.click"; - -class FeedbackManager { - public: - enum class ResultCode { - kOk, - kNotSupportedError, - kPermissionDeniedError, - kUnknownError - }; - - enum class FeedbackPattern { - kClick = FEEDBACK_PATTERN_TAP, - kAlert = FEEDBACK_PATTERN_GENERAL, - kSip = FEEDBACK_PATTERN_SIP - }; - - enum class FeedbackType { - kVibration = FEEDBACK_TYPE_VIBRATION, - kSound = FEEDBACK_TYPE_SOUND - }; - - static std::string GetVibrateVariantName(const char* 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(ResultCode result_code, - const std::string& method_name, - const std::string& args = "") { - const auto method_name_with_args = method_name + "(" + args + ")"; - - switch (result_code) { - case ResultCode::kNotSupportedError: - return method_name_with_args + " is not supported"; - case ResultCode::kPermissionDeniedError: - return std::string{"No permission to run "} + method_name_with_args + - ". Add \"http://tizen.org/privilege/haptic\" privilege to " - "tizen-manifest.xml to use this method"; - case ResultCode::kUnknownError: - default: - return std::string{"An unknown error on "} + method_name_with_args + - " call"; - } - } - - static FeedbackManager& GetInstance() { - static FeedbackManager instance; - return instance; - } - - FeedbackManager(const FeedbackManager&) = delete; - FeedbackManager& operator=(const FeedbackManager&) = delete; - - ResultCode Play(FeedbackType type, FeedbackPattern pattern) { - if (ResultCode::kOk != initialization_status_) { - return initialization_status_; - } - - auto ret = feedback_play_type(static_cast(type), - static_cast(pattern)); - if (FEEDBACK_ERROR_NONE == ret) { - return ResultCode::kOk; - } - FT_LOG(Error) << "feedback_play_type() failed with error: " - << get_error_message(ret); - - return NativeErrorToResultCode(ret); - } - - private: - static ResultCode NativeErrorToResultCode(int 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() { - auto ret = feedback_initialize(); - if (FEEDBACK_ERROR_NONE != ret) { - FT_LOG(Error) << "feedback_initialize() failed with error: " - << get_error_message(ret); - initialization_status_ = NativeErrorToResultCode(ret); - return; - } - - initialization_status_ = ResultCode::kOk; - } - - ~FeedbackManager() { - auto ret = feedback_deinitialize(); - if (FEEDBACK_ERROR_NONE != ret) { - FT_LOG(Error) << "feedback_deinitialize() failed with error: " - << get_error_message(ret); - return; - } - } - - ResultCode initialization_status_ = ResultCode::kUnknownError; -}; - -} // namespace - -// Clipboard constants and variables -namespace clipboard { - -// naive implementation using std::string as a container of internal clipboard -// data -std::string string_clipboard = ""; - -static constexpr char kTextKey[] = "text"; -static constexpr char kTextPlainFormat[] = "text/plain"; -static constexpr char kUnknownClipboardFormatError[] = +constexpr char kTextKey[] = "text"; +constexpr char kTextPlainFormat[] = "text/plain"; +constexpr char kUnknownClipboardFormatError[] = "Unknown clipboard format error"; -static constexpr char kUnknownClipboardError[] = +constexpr char kUnknownClipboardError[] = "Unknown error during clipboard data retrieval"; -void GetData(const MethodCall& call, - std::unique_ptr> result) { - const rapidjson::Value& format = call.arguments()[0]; +// Naive implementation using std::string as a container of internal clipboard +// data. +std::string text_clipboard = ""; - // https://api.flutter.dev/flutter/services/Clipboard/kTextPlain-constant.html - // API supports only kTextPlain format - if (strcmp(format.GetString(), kTextPlainFormat) != 0) { - result->Error(kUnknownClipboardFormatError, - "Clipboard API only supports text."); - return; - } - - rapidjson::Document document; - document.SetObject(); - rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); - document.AddMember(rapidjson::Value(kTextKey, allocator), - rapidjson::Value(string_clipboard, allocator), allocator); - result->Success(document); -} - -void SetData(const MethodCall& call, - std::unique_ptr> result) { - const rapidjson::Value& document = *call.arguments(); - rapidjson::Value::ConstMemberIterator itr = document.FindMember(kTextKey); - if (itr == document.MemberEnd()) { - result->Error(kUnknownClipboardError, "Invalid message format"); - return; - } - string_clipboard = itr->value.GetString(); - result->Success(); -} - -} // namespace clipboard +} // namespace PlatformChannel::PlatformChannel(BinaryMessenger* messenger, TizenRenderer* renderer) @@ -225,97 +62,52 @@ void PlatformChannel::HandleMethodCall( const MethodCall& call, std::unique_ptr> result) { const auto method = call.method_name(); + const auto arguments = call.arguments(); if (method == kSystemNavigatorPopMethod) { - ui_app_exit(); + SystemNavigatorPop(); result->Success(); } else if (method == kPlaySoundMethod) { - const std::string pattern_str = call.arguments()[0].GetString(); - - const FeedbackManager::FeedbackPattern pattern = - (pattern_str == kSoundTypeClick) - ? FeedbackManager::FeedbackPattern::kClick - : FeedbackManager::FeedbackPattern::kAlert; - - auto ret = FeedbackManager::GetInstance().Play( - FeedbackManager::FeedbackType::kSound, pattern); - if (FeedbackManager::ResultCode::kOk == ret) { - result->Success(); - return; - } - - const auto error_cause = - FeedbackManager::GetErrorMessage(ret, kPlaySoundMethod, pattern_str); - const std::string error_message = "Could not play sound"; - FT_LOG(Error) << error_cause << ": " << error_message; - - result->Error(error_cause, error_message); - + PlaySystemSound(arguments[0].GetString()); + result->Success(); } else if (method == kHapticFeedbackVibrateMethod) { - /* - * 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().Play( - FeedbackManager::FeedbackType::kVibration, - FeedbackManager::FeedbackPattern::kSip); - if (FeedbackManager::ResultCode::kOk == ret) { - result->Success(); - return; + std::string type; + if (arguments->IsString()) { + type = arguments[0].GetString(); } - - const auto vibrate_variant_name = - FeedbackManager::GetVibrateVariantName(call.arguments()[0].GetString()); - const auto error_cause = - FeedbackManager::GetErrorMessage(ret, vibrate_variant_name); - const std::string error_message = "Could not vibrate"; - - FT_LOG(Error) << error_cause << ": " << error_message; - - result->Error(error_cause, error_message); + HapticFeedbackVibrate(type); + result->Success(); } else if (method == kGetClipboardDataMethod) { - clipboard::GetData(call, std::move(result)); + // https://api.flutter.dev/flutter/services/Clipboard/kTextPlain-constant.html + // The API supports only kTextPlain format. + if (strcmp(arguments[0].GetString(), kTextPlainFormat) != 0) { + result->Error(kUnknownClipboardFormatError, + "Clipboard API only supports text."); + return; + } + rapidjson::Document document; + document.SetObject(); + rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); + document.AddMember(rapidjson::Value(kTextKey, allocator), + rapidjson::Value(text_clipboard, allocator), allocator); + result->Success(document); } else if (method == kSetClipboardDataMethod) { - clipboard::SetData(call, std::move(result)); + const rapidjson::Value& document = *arguments; + auto iter = document.FindMember(kTextKey); + if (iter == document.MemberEnd()) { + result->Error(kUnknownClipboardError, "Invalid message format."); + return; + } + text_clipboard = iter->value.GetString(); + result->Success(); } else if (method == kSetPreferredOrientationsMethod) { - if (renderer_) { - static const std::string kPortraitUp = "DeviceOrientation.portraitUp"; - static const std::string kPortraitDown = "DeviceOrientation.portraitDown"; - static const std::string kLandscapeLeft = - "DeviceOrientation.landscapeLeft"; - static const std::string kLandscapeRight = - "DeviceOrientation.landscapeRight"; - static const std::map orientation_mapping = { - {kPortraitUp, 0}, - {kLandscapeLeft, 90}, - {kPortraitDown, 180}, - {kLandscapeRight, 270}, - }; - - const auto& list = call.arguments()[0]; - std::vector rotations; - for (rapidjson::Value::ConstValueIterator itr = list.Begin(); - itr != list.End(); ++itr) { - const std::string& rot = itr->GetString(); - rotations.push_back(orientation_mapping.at(rot)); - } - if (rotations.size() == 0) { - // According do docs - // https://api.flutter.dev/flutter/services/SystemChrome/setPreferredOrientations.html - // "The empty list causes the application to defer to the operating - // system default." - rotations = {0, 90, 180, 270}; - } - renderer_->SetPreferredOrientations(rotations); - result->Success(); - } else { - result->Error("Not supported for service applications"); + const auto& list = arguments[0]; + std::vector orientations; + for (auto iter = list.Begin(); iter != list.End(); ++iter) { + orientations.push_back(iter->GetString()); } + SetPreferredOrientations(orientations); + result->Success(); } else if (method == kSetApplicationSwitcherDescriptionMethod) { result->NotImplemented(); } else if (method == kSetEnabledSystemUIOverlaysMethod) { diff --git a/shell/platform/tizen/channels/platform_channel.h b/shell/platform/tizen/channels/platform_channel.h index d70085b5441fc..cb96da4737fe6 100644 --- a/shell/platform/tizen/channels/platform_channel.h +++ b/shell/platform/tizen/channels/platform_channel.h @@ -6,6 +6,8 @@ #define EMBEDDER_PLATFORM_CHANNEL_H_ #include +#include +#include #include "flutter/shell/platform/common/client_wrapper/include/flutter/binary_messenger.h" #include "flutter/shell/platform/common/client_wrapper/include/flutter/method_channel.h" @@ -20,17 +22,20 @@ class PlatformChannel { virtual ~PlatformChannel(); private: -#ifndef __X64_SHELL__ void HandleMethodCall( const MethodCall& call, std::unique_ptr> result); + void SystemNavigatorPop(); + void PlaySystemSound(const std::string& sound_type); + void HapticFeedbackVibrate(const std::string& feedback_type); + void SetPreferredOrientations(const std::vector& orientations); + std::unique_ptr> channel_; // A reference to the renderer object managed by FlutterTizenEngine. // This can be nullptr if the engine is running in headless mode. TizenRenderer* renderer_; -#endif }; } // namespace flutter diff --git a/shell/platform/tizen/channels/platform_channel_linux.cc b/shell/platform/tizen/channels/platform_channel_linux.cc new file mode 100644 index 0000000000000..74b76df3d58a7 --- /dev/null +++ b/shell/platform/tizen/channels/platform_channel_linux.cc @@ -0,0 +1,30 @@ +// Copyright 2021 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform_channel.h" + +#include + +#include "flutter/shell/platform/tizen/logger.h" + +namespace flutter { + +void PlatformChannel::SystemNavigatorPop() { + exit(EXIT_SUCCESS); +} + +void PlatformChannel::PlaySystemSound(const std::string& sound_type) { + FT_UNIMPLEMENTED(); +} + +void PlatformChannel::HapticFeedbackVibrate(const std::string& feedback_type) { + FT_UNIMPLEMENTED(); +} + +void PlatformChannel::SetPreferredOrientations( + const std::vector& orientations) { + FT_UNIMPLEMENTED(); +} + +} // namespace flutter diff --git a/shell/platform/tizen/channels/platform_channel_stub.cc b/shell/platform/tizen/channels/platform_channel_stub.cc deleted file mode 100644 index d5891d060b687..0000000000000 --- a/shell/platform/tizen/channels/platform_channel_stub.cc +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2021 Samsung Electronics Co., Ltd. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "platform_channel.h" - -namespace flutter { - -PlatformChannel::PlatformChannel(BinaryMessenger* messenger, - TizenRenderer* renderer) {} - -PlatformChannel::~PlatformChannel() {} - -} // namespace flutter diff --git a/shell/platform/tizen/channels/platform_channel_tizen.cc b/shell/platform/tizen/channels/platform_channel_tizen.cc new file mode 100644 index 0000000000000..8bb49470a50a5 --- /dev/null +++ b/shell/platform/tizen/channels/platform_channel_tizen.cc @@ -0,0 +1,126 @@ +// Copyright 2021 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform_channel.h" + +#include +#include + +#include + +#include "flutter/shell/platform/tizen/logger.h" + +namespace flutter { + +namespace { + +constexpr char kSoundTypeClick[] = "SystemSoundType.click"; +constexpr char kPortraitUp[] = "DeviceOrientation.portraitUp"; +constexpr char kPortraitDown[] = "DeviceOrientation.portraitDown"; +constexpr char kLandscapeLeft[] = "DeviceOrientation.landscapeLeft"; +constexpr char kLandscapeRight[] = "DeviceOrientation.landscapeRight"; + +class FeedbackManager { + public: + static FeedbackManager& GetInstance() { + static FeedbackManager instance; + return instance; + } + + FeedbackManager(const FeedbackManager&) = delete; + FeedbackManager& operator=(const FeedbackManager&) = delete; + + void PlaySound(const std::string& sound_type) { + auto pattern = (sound_type == kSoundTypeClick) ? FEEDBACK_PATTERN_TAP + : FEEDBACK_PATTERN_GENERAL; + Play(FEEDBACK_TYPE_SOUND, pattern); + } + + void Vibrate(const std::string& feedback_type) { + // 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 feedback_type argument for "HapticFeedback.vibrate" + // calls. + Play(FEEDBACK_TYPE_VIBRATION, FEEDBACK_PATTERN_SIP); + } + + private: + FeedbackManager() { + auto ret = feedback_initialize(); + if (FEEDBACK_ERROR_NONE != ret) { + FT_LOG(Error) << "feedback_initialize() failed with error: " + << get_error_message(ret); + return; + } + initialized_ = true; + } + + ~FeedbackManager() { + auto ret = feedback_deinitialize(); + if (FEEDBACK_ERROR_NONE != ret) { + FT_LOG(Error) << "feedback_deinitialize() failed with error: " + << get_error_message(ret); + } + } + + void Play(feedback_type_e type, feedback_pattern_e pattern) { + if (!initialized_) { + return; + } + auto ret = feedback_play_type(type, pattern); + if (FEEDBACK_ERROR_PERMISSION_DENIED == ret) { + FT_LOG(Error) + << "Permission denied. Add \"http://tizen.org/privilege/haptic\" " + "privilege to tizen-manifest.xml to use haptic feedbacks."; + } else if (FEEDBACK_ERROR_NONE != ret) { + FT_LOG(Error) << "feedback_play_type() failed with error: " + << get_error_message(ret); + } + } + + bool initialized_ = false; +}; + +} // namespace + +void PlatformChannel::SystemNavigatorPop() { + ui_app_exit(); +} + +void PlatformChannel::PlaySystemSound(const std::string& sound_type) { + FeedbackManager::GetInstance().PlaySound(sound_type); +} + +void PlatformChannel::HapticFeedbackVibrate(const std::string& feedback_type) { + FeedbackManager::GetInstance().Vibrate(feedback_type); +} + +void PlatformChannel::SetPreferredOrientations( + const std::vector& orientations) { + if (!renderer_) { + return; + } + static const std::map orientation_mapping = { + {kPortraitUp, 0}, + {kLandscapeLeft, 90}, + {kPortraitDown, 180}, + {kLandscapeRight, 270}, + }; + std::vector rotations; + for (auto orientation : orientations) { + rotations.push_back(orientation_mapping.at(orientation)); + } + if (rotations.size() == 0) { + // According do docs + // https://api.flutter.dev/flutter/services/SystemChrome/setPreferredOrientations.html + // "The empty list causes the application to defer to the operating + // system default." + rotations = {0, 90, 180, 270}; + } + renderer_->SetPreferredOrientations(rotations); +} + +} // namespace flutter