Skip to content

Commit

Permalink
Refactor PlatformChannel (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
swift-kim committed Feb 11, 2022
1 parent 03e8622 commit bf43429
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 272 deletions.
8 changes: 4 additions & 4 deletions shell/platform/tizen/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down
296 changes: 44 additions & 252 deletions shell/platform/tizen/channels/platform_channel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@

#include "platform_channel.h"

#include <app.h>
#include <feedback.h>

#include <map>

#include "flutter/shell/platform/common/json_method_codec.h"
#include "flutter/shell/platform/tizen/logger.h"

Expand All @@ -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<feedback_type_e>(type),
static_cast<feedback_pattern_e>(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<rapidjson::Document>& call,
std::unique_ptr<MethodResult<rapidjson::Document>> 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<rapidjson::Document>& call,
std::unique_ptr<MethodResult<rapidjson::Document>> 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)
Expand All @@ -225,97 +62,52 @@ void PlatformChannel::HandleMethodCall(
const MethodCall<rapidjson::Document>& call,
std::unique_ptr<MethodResult<rapidjson::Document>> 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<std::string, int> orientation_mapping = {
{kPortraitUp, 0},
{kLandscapeLeft, 90},
{kPortraitDown, 180},
{kLandscapeRight, 270},
};

const auto& list = call.arguments()[0];
std::vector<int> 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<std::string> 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) {
Expand Down
9 changes: 7 additions & 2 deletions shell/platform/tizen/channels/platform_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define EMBEDDER_PLATFORM_CHANNEL_H_

#include <memory>
#include <string>
#include <vector>

#include "flutter/shell/platform/common/client_wrapper/include/flutter/binary_messenger.h"
#include "flutter/shell/platform/common/client_wrapper/include/flutter/method_channel.h"
Expand All @@ -20,17 +22,20 @@ class PlatformChannel {
virtual ~PlatformChannel();

private:
#ifndef __X64_SHELL__
void HandleMethodCall(
const MethodCall<rapidjson::Document>& call,
std::unique_ptr<MethodResult<rapidjson::Document>> result);

void SystemNavigatorPop();
void PlaySystemSound(const std::string& sound_type);
void HapticFeedbackVibrate(const std::string& feedback_type);
void SetPreferredOrientations(const std::vector<std::string>& orientations);

std::unique_ptr<MethodChannel<rapidjson::Document>> 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
Expand Down
Loading

0 comments on commit bf43429

Please sign in to comment.