Skip to content

Refactor PlatformChannel #163

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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