* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include <locale>
#include <codecvt>
#include "cj_input_method_controller.h"
#include "cj_input_method_textchanged_listener.h"
#include "cj_lambda.h"
#include "utils.h"
#include "string_ex.h"
namespace OHOS {
namespace MiscServices {
std::mutex CjInputMethodController::controllerMutex_;
std::shared_ptr<CjInputMethodController> CjInputMethodController::controller_{ nullptr };
const int8_t INSERT_TEXT = 0;
const int8_t DELETE_LEFT = 1;
const int8_t DELETE_RIGHT = 2;
const int8_t SEND_KEYBOARD_STATUS = 3;
const int8_t SEND_FUNCTION_KEY = 4;
const int8_t MOVE_CURSOR = 5;
const int8_t HANDLE_EXTEND_ACTION = 6;
const int8_t GET_LEFT_TEXT = 7;
const int8_t GET_RIGHT_TEXT = 8;
const int8_t GET_TEXT_INDEX = 9;
const int8_t SELECT_BY_MOVEMENT = 10;
const int8_t SELECT_BY_RANGE = 11;
std::shared_ptr<CjInputMethodController> CjInputMethodController::GetInstance()
{
if (controller_ == nullptr) {
std::lock_guard<std::mutex> lock(controllerMutex_);
if (controller_ == nullptr) {
auto controller = std::make_shared<CjInputMethodController>();
controller_ = controller;
auto inputMethodController = InputMethodController::GetInstance();
if (inputMethodController != nullptr) {
inputMethodController->SetControllerListener(controller_);
}
}
}
return controller_;
}
int32_t CjInputMethodController::Attach(const CTextConfig &txtCfg, const AttachOptions &attachOptions)
{
auto textListener = CjInputMethodTextChangedListener::GetInstance();
if (textListener == nullptr) {
IMSA_HILOGE("failed to create CjInputMethodTextChangedListener!");
return ERR_NO_MEMORY;
}
TextConfig textCfg;
textCfg.inputAttribute.inputPattern = txtCfg.inputAttrbute.textInputType;
textCfg.inputAttribute.enterKeyType = txtCfg.inputAttrbute.enterKeyType;
textCfg.cursorInfo.left = txtCfg.cursor.left;
textCfg.cursorInfo.top = txtCfg.cursor.top;
textCfg.cursorInfo.width = txtCfg.cursor.width;
textCfg.cursorInfo.height = txtCfg.cursor.height;
textCfg.range.start = txtCfg.range.start;
textCfg.range.end = txtCfg.range.end;
textCfg.windowId = txtCfg.windowId;
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
return controller->Attach(textListener, attachOptions, textCfg, ClientType::CJ);
}
int32_t CjInputMethodController::Detach()
{
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
return controller->Close();
}
int32_t CjInputMethodController::ShowTextInput(const AttachOptions &attachOptions)
{
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
return controller->ShowTextInput(attachOptions, ClientType::CJ);
}
int32_t CjInputMethodController::HideTextInput()
{
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
return controller->HideTextInput();
}
int32_t CjInputMethodController::SetCallingWindow(uint32_t windowId)
{
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
return controller->SetCallingWindow(windowId);
}
int32_t CjInputMethodController::UpdateCursor(const CCursorInfo &cursor)
{
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
CursorInfo cursorInfo;
cursorInfo.left = cursor.left;
cursorInfo.top = cursor.top;
cursorInfo.width = cursor.width;
cursorInfo.height = cursor.height;
return controller->OnCursorUpdate(cursorInfo);
}
int32_t CjInputMethodController::ChangeSelection(const std::string &text, int32_t start, int32_t end)
{
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
std::u16string txt = converter.from_bytes(text);
return controller->OnSelectionChange(txt, start, end);
}
int32_t CjInputMethodController::UpdateAttribute(const CInputAttribute &inputAttribute)
{
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
Configuration config = Configuration();
config.SetTextInputType(static_cast<TextInputType>(inputAttribute.textInputType));
config.SetEnterKeyType(static_cast<EnterKeyType>(inputAttribute.enterKeyType));
return controller->OnConfigurationChange(config);
}
int32_t CjInputMethodController::ShowSoftKeyboard()
{
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
return controller->ShowSoftKeyboard(ClientType::CJ);
}
int32_t CjInputMethodController::HideSoftKeyboard()
{
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
return controller->HideSoftKeyboard();
}
int32_t CjInputMethodController::StopInputSession()
{
auto controller = InputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
return controller->StopInputSession();
}
void CjInputMethodController::RegisterListener(int8_t type, int64_t id)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
switch (type) {
case INSERT_TEXT:
InitInsertText(id);
break;
case DELETE_LEFT:
InitDeleteRight(id);
break;
case DELETE_RIGHT:
InitDeleteLeft(id);
break;
case SEND_KEYBOARD_STATUS:
InitSendKeyboardStatus(id);
break;
case SEND_FUNCTION_KEY:
InitSendFunctionKey(id);
break;
case MOVE_CURSOR:
InitMoveCursor(id);
break;
case HANDLE_EXTEND_ACTION:
InitHandleExtendAction(id);
break;
case GET_LEFT_TEXT:
InitGetLeftText(id);
break;
case GET_RIGHT_TEXT:
InitGetRightText(id);
break;
case GET_TEXT_INDEX:
InitGetTextIndexAtCursor(id);
break;
case SELECT_BY_MOVEMENT:
InitSelectByMovement(id);
break;
case SELECT_BY_RANGE:
InitSelectByRange(id);
break;
default:
return;
}
}
void CjInputMethodController::UnRegisterListener(int8_t type)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
switch (type) {
case INSERT_TEXT:
insertText = nullptr;
break;
case DELETE_LEFT:
deleteLeft = nullptr;
break;
case DELETE_RIGHT:
deleteRight = nullptr;
break;
case SEND_KEYBOARD_STATUS:
sendKeyboardStatus = nullptr;
break;
case SEND_FUNCTION_KEY:
sendFunctionKey = nullptr;
break;
case MOVE_CURSOR:
moveCursor = nullptr;
break;
case HANDLE_EXTEND_ACTION:
handleExtendAction = nullptr;
break;
case GET_LEFT_TEXT:
getLeftText = nullptr;
break;
case GET_RIGHT_TEXT:
getRightText = nullptr;
break;
case GET_TEXT_INDEX:
getTextIndexAtCursor = nullptr;
break;
case SELECT_BY_MOVEMENT:
onSelectByMovement = nullptr;
break;
case SELECT_BY_RANGE:
onSelectByRange = nullptr;
break;
default:
return;
}
}
int32_t CjInputMethodController::Subscribe(int8_t type, int64_t id)
{
auto controller = CjInputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
controller->RegisterListener(type, id);
return SUCCESS_CODE;
}
int32_t CjInputMethodController::Unsubscribe(int8_t type)
{
auto controller = CjInputMethodController::GetInstance();
if (controller == nullptr) {
return ERR_NO_MEMORY;
}
controller->UnRegisterListener(type);
return SUCCESS_CODE;
}
void CjInputMethodController::OnSelectByRange(int32_t start, int32_t end)
{
if (onSelectByRange == nullptr) {
IMSA_HILOGE("onSelectByRange null.");
return;
}
IMSA_HILOGD("onSelelctByRange runs.");
Range range;
range.start = start;
range.end = end;
return onSelectByRange(range);
}
void CjInputMethodController::OnSelectByMovement(int32_t direction)
{
if (onSelectByMovement == nullptr) {
IMSA_HILOGE("onSelectByMovement null.");
return;
}
IMSA_HILOGD("onSelectByMovement runs.");
return onSelectByMovement(direction);
}
void CjInputMethodController::InsertText(const std::u16string &text)
{
char *insertTxt = Utils::MallocCString(Str16ToStr8(text));
if (insertTxt == nullptr) {
IMSA_HILOGE("Failed to excute InsertText callback: out of memory.");
return;
}
if (insertText == nullptr) {
IMSA_HILOGE("insertText null.");
free(insertTxt);
return;
}
IMSA_HILOGD("insertText runs.");
insertText(insertTxt);
free(insertTxt);
return;
}
void CjInputMethodController::DeleteRight(int32_t length)
{
if (deleteRight == nullptr) {
IMSA_HILOGE("deleteRight null.");
return;
}
IMSA_HILOGD("deleteRight runs.");
deleteRight(length);
return;
}
void CjInputMethodController::DeleteLeft(int32_t length)
{
if (deleteLeft == nullptr) {
IMSA_HILOGE("deleteLeft null.");
return;
}
IMSA_HILOGD("deleteLeft runs.");
deleteLeft(length);
return;
}
void CjInputMethodController::SendKeyboardStatus(const KeyboardStatus &status)
{
if (sendKeyboardStatus == nullptr) {
IMSA_HILOGE("sendKeyboardStatus null.");
return;
}
IMSA_HILOGD("sendKeyboardStatus runs.");
auto statusNum = static_cast<int32_t>(status);
sendKeyboardStatus(statusNum);
return;
}
void CjInputMethodController::SendFunctionKey(const FunctionKey &functionKey)
{
if (sendFunctionKey == nullptr) {
IMSA_HILOGE("sendFunctionKey null.");
return;
}
IMSA_HILOGD("sendFunctionKey runs.");
auto type = static_cast<int32_t>(functionKey.GetEnterKeyType());
sendFunctionKey(type);
return;
}
void CjInputMethodController::MoveCursor(const Direction direction)
{
if (moveCursor == nullptr) {
IMSA_HILOGE("moveCursor null.");
return;
}
IMSA_HILOGD("moveCursor runs.");
auto dir = static_cast<int32_t>(direction);
moveCursor(dir);
return;
}
void CjInputMethodController::HandleExtendAction(int32_t action)
{
if (handleExtendAction == nullptr) {
IMSA_HILOGE("handleExtendAction null.");
return;
}
IMSA_HILOGD("handleExtendAction runs.");
handleExtendAction(action);
return;
}
std::u16string CjInputMethodController::GetLeftText(int32_t number)
{
if (getLeftText == nullptr) {
IMSA_HILOGE("getLeftText null.");
return u"";
}
IMSA_HILOGD("getLeftText runs.");
char *text = getLeftText(number);
if (text == nullptr) {
IMSA_HILOGE("text is nullptr.");
return u"";
}
auto ret = Str8ToStr16(std::string(text));
free(text);
return ret;
}
std::u16string CjInputMethodController::GetRightText(int32_t number)
{
if (getRightText == nullptr) {
IMSA_HILOGE("getRightText null.");
return u"";
}
IMSA_HILOGD("getRightText runs.");
char *text = getRightText(number);
if (text == nullptr) {
IMSA_HILOGE("text is nullptr.");
return u"";
}
auto ret = Str8ToStr16(std::string(text));
free(text);
return ret;
}
int32_t CjInputMethodController::GetTextIndexAtCursor()
{
if (getTextIndexAtCursor == nullptr) {
IMSA_HILOGE("getTextIndexAtCursor null.");
return -1;
}
IMSA_HILOGD("getTextIndexAtCursor runs.");
return getTextIndexAtCursor();
}
void CjInputMethodController::InitInsertText(int64_t id)
{
auto callback = reinterpret_cast<void(*)(const char*)>(id);
insertText = [lambda = CJLambda::Create(callback)](const char* text) -> void {
lambda(text);
};
}
void CjInputMethodController::InitDeleteRight(int64_t id)
{
auto callback = reinterpret_cast<void(*)(int32_t)>(id);
deleteLeft = [lambda = CJLambda::Create(callback)](int32_t length) -> void {
lambda(length);
};
}
void CjInputMethodController::InitDeleteLeft(int64_t id)
{
auto callback = reinterpret_cast<void(*)(int32_t)>(id);
deleteRight = [lambda = CJLambda::Create(callback)](int32_t length) -> void {
lambda(length);
};
}
void CjInputMethodController::InitSendKeyboardStatus(int64_t id)
{
auto callback = reinterpret_cast<void(*)(int32_t)>(id);
sendKeyboardStatus = [lambda = CJLambda::Create(callback)](int32_t status) -> void {
lambda(status);
};
}
void CjInputMethodController::InitSendFunctionKey(int64_t id)
{
auto callback = reinterpret_cast<void(*)(int32_t)>(id);
sendFunctionKey = [lambda = CJLambda::Create(callback)](int32_t functionKey) -> void {
lambda(functionKey);
};
}
void CjInputMethodController::InitMoveCursor(int64_t id)
{
auto callback = reinterpret_cast<void(*)(int32_t)>(id);
moveCursor = [lambda = CJLambda::Create(callback)](int32_t direction) -> void {
lambda(direction);
};
}
void CjInputMethodController::InitHandleExtendAction(int64_t id)
{
auto callback = reinterpret_cast<void(*)(int32_t)>(id);
handleExtendAction = [lambda = CJLambda::Create(callback)](int32_t action) -> void {
lambda(action);
};
}
void CjInputMethodController::InitGetLeftText(int64_t id)
{
auto callback = reinterpret_cast<char*(*)(int32_t)>(id);
getLeftText = [lambda = CJLambda::Create(callback)](int32_t number) -> char* {
return lambda(number);
};
}
void CjInputMethodController::InitGetRightText(int64_t id)
{
auto callback = reinterpret_cast<char*(*)(int32_t)>(id);
getRightText = [lambda = CJLambda::Create(callback)](int32_t number) -> char* {
return lambda(number);
};
}
void CjInputMethodController::InitGetTextIndexAtCursor(int64_t id)
{
auto callback = reinterpret_cast<int32_t(*)()>(id);
getTextIndexAtCursor = [lambda = CJLambda::Create(callback)](void) -> int32_t {
return lambda();
};
}
void CjInputMethodController::InitSelectByMovement(int64_t id)
{
auto callback = reinterpret_cast<void(*)(int32_t)>(id);
onSelectByMovement = [lambda = CJLambda::Create(callback)](int32_t direction) -> void {
lambda(direction);
};
}
void CjInputMethodController::InitSelectByRange(int64_t id)
{
auto callback = reinterpret_cast<void(*)(Range)>(id);
onSelectByRange = [lambda = CJLambda::Create(callback)](Range range) -> void {
lambda(range);
};
}
}
}