1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "input_method_panel.h"
17
18 #include "display_manager.h"
19 #include "global.h"
20 #include "input_method_ability_utils.h"
21 #include "ui/rs_surface_node.h"
22
23 namespace OHOS {
24 namespace MiscServices {
25 using WMError = OHOS::Rosen::WMError;
26 using WindowGravity = OHOS::Rosen::WindowGravity;
27 using WindowState = OHOS::Rosen::WindowState;
28 constexpr float FIXED_SOFT_KEYBOARD_PANEL_RATIO = 0.7;
29 constexpr float NON_FIXED_SOFT_KEYBOARD_PANEL_RATIO = 1;
30 std::atomic<uint32_t> InputMethodPanel::sequenceId_{ 0 };
31 InputMethodPanel::~InputMethodPanel() = default;
32
CreatePanel(const std::shared_ptr<AbilityRuntime::Context> & context,const PanelInfo & panelInfo)33 int32_t InputMethodPanel::CreatePanel(
34 const std::shared_ptr<AbilityRuntime::Context> &context, const PanelInfo &panelInfo)
35 {
36 IMSA_HILOGD(
37 "start, type/flag: %{public}d/%{public}d", static_cast<int32_t>(panelType_), static_cast<int32_t>(panelFlag_));
38 panelType_ = panelInfo.panelType;
39 panelFlag_ = panelInfo.panelFlag;
40 winOption_ = new (std::nothrow) OHOS::Rosen::WindowOption();
41 if (winOption_ == nullptr) {
42 return ErrorCode::ERROR_NULL_POINTER;
43 }
44 if (panelInfo.panelType == PanelType::STATUS_BAR) {
45 winOption_->SetWindowType(OHOS::Rosen::WindowType::WINDOW_TYPE_INPUT_METHOD_STATUS_BAR);
46 } else {
47 winOption_->SetWindowType(OHOS::Rosen::WindowType::WINDOW_TYPE_INPUT_METHOD_FLOAT);
48 }
49 WMError wmError = WMError::WM_OK;
50 window_ = OHOS::Rosen::Window::Create(GeneratePanelName(), winOption_, context, wmError);
51 if (wmError == WMError::WM_ERROR_INVALID_PERMISSION || wmError == WMError::WM_ERROR_NOT_SYSTEM_APP) {
52 IMSA_HILOGE("Create window failed, permission denied, %{public}d", wmError);
53 return ErrorCode::ERROR_NOT_IME;
54 }
55 if (window_ == nullptr || wmError != WMError::WM_OK) {
56 IMSA_HILOGE("Create window failed: %{public}d", wmError);
57 return ErrorCode::ERROR_OPERATE_PANEL;
58 }
59 if (SetPanelProperties() != ErrorCode::NO_ERROR) {
60 wmError = window_->Destroy();
61 IMSA_HILOGI("Destroy window end, wmError is %{public}d.", wmError);
62 return ErrorCode::ERROR_OPERATE_PANEL;
63 }
64 windowId_ = window_->GetWindowId();
65 IMSA_HILOGI("success, type/flag/windowId: %{public}d/%{public}d/%{public}u", static_cast<int32_t>(panelType_),
66 static_cast<int32_t>(panelFlag_), windowId_);
67 return ErrorCode::NO_ERROR;
68 }
69
GeneratePanelName()70 std::string InputMethodPanel::GeneratePanelName()
71 {
72 uint32_t sequenceId = GenerateSequenceId();
73 std::string windowName = panelType_ == SOFT_KEYBOARD ? "softKeyboard" + std::to_string(sequenceId)
74 : "statusBar" + std::to_string(sequenceId);
75 IMSA_HILOGD("InputMethodPanel, windowName = %{public}s", windowName.c_str());
76 return windowName;
77 }
78
SetPanelProperties()79 int32_t InputMethodPanel::SetPanelProperties()
80 {
81 if (window_ == nullptr) {
82 IMSA_HILOGE("window is not exist.");
83 return ErrorCode::ERROR_OPERATE_PANEL;
84 }
85 WindowGravity gravity = WindowGravity::WINDOW_GRAVITY_FLOAT;
86 if (panelType_ == SOFT_KEYBOARD && panelFlag_ == FLG_FIXED) {
87 gravity = WindowGravity::WINDOW_GRAVITY_BOTTOM;
88 } else if (panelType_ == SOFT_KEYBOARD && panelFlag_ == FLG_FLOATING) {
89 window_->GetSurfaceNode()->SetFrameGravity(Rosen::Gravity::TOP_LEFT);
90 Rosen::RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
91 } else if (panelType_ == STATUS_BAR) {
92 window_->GetSurfaceNode()->SetFrameGravity(Rosen::Gravity::TOP_LEFT);
93 Rosen::RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
94 return ErrorCode::NO_ERROR;
95 }
96 WMError wmError = window_->SetWindowGravity(gravity, invalidGravityPercent);
97 if (wmError != WMError::WM_OK) {
98 IMSA_HILOGE("SetWindowGravity failed, wmError is %{public}d, start destroy window.", wmError);
99 return ErrorCode::ERROR_OPERATE_PANEL;
100 }
101 return ErrorCode::NO_ERROR;
102 }
103
DestroyPanel()104 int32_t InputMethodPanel::DestroyPanel()
105 {
106 auto ret = HidePanel();
107 if (ret != ErrorCode::NO_ERROR) {
108 IMSA_HILOGE("InputMethodPanel, hide panel failed, ret = %{public}d.", ret);
109 return ret;
110 }
111 auto result = window_->Destroy();
112 IMSA_HILOGI("ret = %{public}d", result);
113 return result == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
114 }
115
Resize(uint32_t width,uint32_t height)116 int32_t InputMethodPanel::Resize(uint32_t width, uint32_t height)
117 {
118 if (window_ == nullptr) {
119 IMSA_HILOGE("window is nullptr");
120 return ErrorCode::ERROR_NULL_POINTER;
121 }
122 if (!IsSizeValid(width, height)) {
123 IMSA_HILOGE("invalid size");
124 return ErrorCode::ERROR_BAD_PARAMETERS;
125 }
126 auto ret = window_->Resize(width, height);
127 if (ret != WMError::WM_OK) {
128 IMSA_HILOGE("failed to resize, ret: %{public}d", ret);
129 return ErrorCode::ERROR_OPERATE_PANEL;
130 }
131 IMSA_HILOGI("success, width/height: %{public}u/%{public}u", width, height);
132 {
133 std::lock_guard<std::mutex> lock(heightLock_);
134 panelHeight_ = height;
135 }
136 return ErrorCode::NO_ERROR;
137 }
138
MoveTo(int32_t x,int32_t y)139 int32_t InputMethodPanel::MoveTo(int32_t x, int32_t y)
140 {
141 if (window_ == nullptr) {
142 IMSA_HILOGE("window_ is nullptr.");
143 return ErrorCode::ERROR_NULL_POINTER;
144 }
145 if (panelType_ == SOFT_KEYBOARD && panelFlag_ == FLG_FIXED) {
146 IMSA_HILOGE("FLG_FIXED panel can not moveTo.");
147 return ErrorCode::NO_ERROR;
148 }
149 auto ret = window_->MoveTo(x, y);
150 IMSA_HILOGI("x/y: %{public}d/%{public}d, ret = %{public}d", x, y, ret);
151 return ret == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
152 }
153
ChangePanelFlag(PanelFlag panelFlag)154 int32_t InputMethodPanel::ChangePanelFlag(PanelFlag panelFlag)
155 {
156 if (window_ == nullptr) {
157 IMSA_HILOGE("window_ is nullptr.");
158 return ErrorCode::ERROR_NULL_POINTER;
159 }
160 if (panelFlag_ == panelFlag) {
161 return ErrorCode::NO_ERROR;
162 }
163 if (panelType_ == STATUS_BAR) {
164 IMSA_HILOGE("STATUS_BAR cannot ChangePanelFlag.");
165 return ErrorCode::ERROR_BAD_PARAMETERS;
166 }
167 panelFlag_ = panelFlag;
168 WindowGravity gravity = WindowGravity::WINDOW_GRAVITY_FLOAT;
169 if (panelFlag == FLG_FIXED) {
170 gravity = WindowGravity::WINDOW_GRAVITY_BOTTOM;
171 } else {
172 window_->GetSurfaceNode()->SetFrameGravity(Rosen::Gravity::TOP_LEFT);
173 Rosen::RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
174 }
175 auto ret = window_->SetWindowGravity(gravity, invalidGravityPercent);
176 IMSA_HILOGI("flag: %{public}d, ret = %{public}d", panelFlag, ret);
177 return ret == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
178 }
179
GetPanelType()180 PanelType InputMethodPanel::GetPanelType()
181 {
182 return panelType_;
183 }
184
GetPanelFlag()185 PanelFlag InputMethodPanel::GetPanelFlag()
186 {
187 return panelFlag_;
188 }
189
ShowPanel()190 int32_t InputMethodPanel::ShowPanel()
191 {
192 IMSA_HILOGD("InputMethodPanel, run in");
193 if (window_ == nullptr) {
194 IMSA_HILOGE("window_ is nullptr.");
195 return ErrorCode::ERROR_NULL_POINTER;
196 }
197 if (IsShowing()) {
198 IMSA_HILOGI("Panel already shown.");
199 return ErrorCode::NO_ERROR;
200 }
201 auto ret = window_->Show();
202 if (ret != WMError::WM_OK) {
203 IMSA_HILOGE("ShowPanel error, err = %{public}d", ret);
204 return ErrorCode::ERROR_OPERATE_PANEL;
205 }
206 IMSA_HILOGI("success, type/flag: %{public}d/%{public}d", static_cast<int32_t>(panelType_),
207 static_cast<int32_t>(panelFlag_));
208 PanelStatusChange(InputWindowStatus::SHOW);
209 return ErrorCode::NO_ERROR;
210 }
211
SetTextFieldAvoidInfo(double positionY,double height)212 int32_t InputMethodPanel::SetTextFieldAvoidInfo(double positionY, double height)
213 {
214 if (window_ == nullptr) {
215 IMSA_HILOGE("window_ is nullptr.");
216 return ErrorCode::ERROR_NULL_POINTER;
217 }
218 auto ret = window_->SetTextFieldAvoidInfo(positionY, height);
219 if (ret != WMError::WM_OK) {
220 IMSA_HILOGE("SetTextFieldAvoidInfo error, err = %{public}d", ret);
221 return ErrorCode::ERROR_OPERATE_PANEL;
222 }
223 return ErrorCode::NO_ERROR;
224 }
225
HidePanel()226 int32_t InputMethodPanel::HidePanel()
227 {
228 IMSA_HILOGD("InputMethodPanel, run in");
229 if (window_ == nullptr) {
230 IMSA_HILOGE("window_ is nullptr.");
231 return ErrorCode::ERROR_NULL_POINTER;
232 }
233 if (IsHidden()) {
234 IMSA_HILOGI("Panel already hidden.");
235 return ErrorCode::NO_ERROR;
236 }
237 auto ret = window_->Hide();
238 if (ret != WMError::WM_OK) {
239 IMSA_HILOGE("HidePanel error, err = %{public}d", ret);
240 return ErrorCode::ERROR_OPERATE_PANEL;
241 }
242 IMSA_HILOGI("success, type/flag: %{public}d/%{public}d", static_cast<int32_t>(panelType_),
243 static_cast<int32_t>(panelFlag_));
244 PanelStatusChange(InputWindowStatus::HIDE);
245 return ErrorCode::NO_ERROR;
246 }
247
SetCallingWindow(uint32_t windowId)248 int32_t InputMethodPanel::SetCallingWindow(uint32_t windowId)
249 {
250 IMSA_HILOGD("InputMethodPanel run in, windowId: %{public}d", windowId);
251 if (window_ == nullptr) {
252 IMSA_HILOGE("window_ is nullptr.");
253 return ErrorCode::ERROR_NULL_POINTER;
254 }
255 auto ret = window_->SetCallingWindow(windowId);
256 IMSA_HILOGI("ret = %{public}d, windowId = %{public}u", ret, windowId);
257 return ret == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
258 }
259
SetPrivacyMode(bool isPrivacyMode)260 int32_t InputMethodPanel::SetPrivacyMode(bool isPrivacyMode)
261 {
262 IMSA_HILOGD("isPrivacyMode: %{public}d", isPrivacyMode);
263 if (window_ == nullptr) {
264 IMSA_HILOGE("window_ is nullptr.");
265 return ErrorCode::ERROR_NULL_POINTER;
266 }
267 auto ret = window_->SetPrivacyMode(isPrivacyMode);
268 if (ret != WMError::WM_OK) {
269 IMSA_HILOGE("SetWindowPrivacyMode error, ret = %{public}d", ret);
270 return static_cast<int32_t>(ret);
271 }
272 IMSA_HILOGI("end, isPrivacyMode: %{public}d", isPrivacyMode);
273 return ErrorCode::NO_ERROR;
274 }
275
PanelStatusChange(const InputWindowStatus & status)276 void InputMethodPanel::PanelStatusChange(const InputWindowStatus &status)
277 {
278 if (status == InputWindowStatus::SHOW && showRegistered_ && panelStatusListener_ != nullptr) {
279 IMSA_HILOGD("ShowPanel panelStatusListener_ is not nullptr");
280 panelStatusListener_->OnPanelStatus(windowId_, true);
281 }
282 if (status == InputWindowStatus::HIDE && hideRegistered_ && panelStatusListener_ != nullptr) {
283 IMSA_HILOGD("HidePanel panelStatusListener_ is not nullptr");
284 panelStatusListener_->OnPanelStatus(windowId_, false);
285 }
286 auto imsa = ImaUtils::GetImsaProxy();
287 if (imsa == nullptr) {
288 IMSA_HILOGE("imsa is nullptr");
289 return;
290 }
291 if (panelType_ == SOFT_KEYBOARD && panelFlag_ == FLG_FIXED) {
292 auto rect = window_->GetRect();
293 IMSA_HILOGD("InputMethodPanel::x:%{public}d, y:%{public}d, w:%{public}u, h:%{public}u", rect.posX_, rect.posY_,
294 rect.width_, rect.height_);
295 std::string name = window_->GetWindowName() + "/" + std::to_string(window_->GetWindowId());
296 imsa->PanelStatusChange(status, { std::move(name), rect.posX_, rect.posY_, rect.width_, rect.height_ });
297 }
298 }
299
IsShowing()300 bool InputMethodPanel::IsShowing()
301 {
302 WindowState windowState = window_->GetWindowState();
303 if (windowState == WindowState::STATE_SHOWN) {
304 return true;
305 }
306 IMSA_HILOGD("windowState = %{public}d", static_cast<int>(windowState));
307 return false;
308 }
309
IsHidden()310 bool InputMethodPanel::IsHidden()
311 {
312 WindowState windowState = window_->GetWindowState();
313 if (windowState == WindowState::STATE_HIDDEN) {
314 return true;
315 }
316 IMSA_HILOGD("windowState = %{public}d", static_cast<int>(windowState));
317 return false;
318 }
319
SetUiContent(const std::string & contentInfo,napi_env env,std::shared_ptr<NativeReference> storage)320 int32_t InputMethodPanel::SetUiContent(
321 const std::string &contentInfo, napi_env env, std::shared_ptr<NativeReference> storage)
322 {
323 if (window_ == nullptr) {
324 IMSA_HILOGE("window_ is nullptr, can not SetUiContent.");
325 return ErrorCode::ERROR_NULL_POINTER;
326 }
327 WMError ret = WMError::WM_OK;
328 if (storage == nullptr) {
329 ret = window_->NapiSetUIContent(contentInfo, env, nullptr);
330 } else {
331 ret = window_->NapiSetUIContent(contentInfo, env, storage->GetNapiValue());
332 }
333 WMError wmError = window_->SetTransparent(true);
334 IMSA_HILOGI("SetTransparent ret = %{public}u", wmError);
335 IMSA_HILOGI("NapiSetUIContent ret = %{public}d", ret);
336 return ret == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
337 }
338
SetPanelStatusListener(std::shared_ptr<PanelStatusListener> statusListener,const std::string & type)339 void InputMethodPanel::SetPanelStatusListener(
340 std::shared_ptr<PanelStatusListener> statusListener, const std::string &type)
341 {
342 IMSA_HILOGD("SetPanelStatusListener start.");
343 if (!MarkListener(type, true)) {
344 return;
345 }
346 if (panelStatusListener_ != nullptr) {
347 IMSA_HILOGD("PanelStatusListener already set.");
348 return;
349 }
350 panelStatusListener_ = std::move(statusListener);
351 if (window_ != nullptr && IsShowing()) {
352 panelStatusListener_->OnPanelStatus(windowId_, true);
353 }
354 }
355
ClearPanelListener(const std::string & type)356 void InputMethodPanel::ClearPanelListener(const std::string &type)
357 {
358 if (!MarkListener(type, false)) {
359 return;
360 }
361 if (panelStatusListener_ == nullptr) {
362 IMSA_HILOGD("PanelStatusListener not set, don't need to remove.");
363 return;
364 }
365 if (showRegistered_ || hideRegistered_) {
366 return;
367 }
368 panelStatusListener_ = nullptr;
369 }
370
MarkListener(const std::string & type,bool isRegister)371 bool InputMethodPanel::MarkListener(const std::string &type, bool isRegister)
372 {
373 if (type == "show") {
374 showRegistered_ = isRegister;
375 } else if (type == "hide") {
376 hideRegistered_ = isRegister;
377 } else {
378 IMSA_HILOGE("type error.");
379 return false;
380 }
381 return true;
382 }
383
GenerateSequenceId()384 uint32_t InputMethodPanel::GenerateSequenceId()
385 {
386 uint32_t seqId = ++sequenceId_;
387 if (seqId == std::numeric_limits<uint32_t>::max()) {
388 return ++sequenceId_;
389 }
390 return seqId;
391 }
392
IsSizeValid(uint32_t width,uint32_t height)393 bool InputMethodPanel::IsSizeValid(uint32_t width, uint32_t height)
394 {
395 if (width > INT32_MAX || height > INT32_MAX) {
396 IMSA_HILOGE("width or height over maximum");
397 return ErrorCode::ERROR_BAD_PARAMETERS;
398 }
399 auto defaultDisplay = Rosen::DisplayManager::GetInstance().GetDefaultDisplay();
400 if (defaultDisplay == nullptr) {
401 IMSA_HILOGE("GetDefaultDisplay failed.");
402 return ErrorCode::ERROR_NULL_POINTER;
403 }
404 float ratio = panelType_ == PanelType::SOFT_KEYBOARD && panelFlag_ == PanelFlag::FLG_FIXED
405 ? FIXED_SOFT_KEYBOARD_PANEL_RATIO
406 : NON_FIXED_SOFT_KEYBOARD_PANEL_RATIO;
407 if (static_cast<float>(height) > defaultDisplay->GetHeight() * ratio) {
408 IMSA_HILOGE("height invalid, defaultDisplay height = %{public}d, target height = %{public}u",
409 defaultDisplay->GetHeight(), height);
410 return false;
411 }
412 if (static_cast<int32_t>(width) > defaultDisplay->GetWidth()) {
413 IMSA_HILOGE("width invalid, defaultDisplay width = %{public}d, target width = %{public}u",
414 defaultDisplay->GetWidth(), width);
415 return false;
416 }
417 return true;
418 }
419
GetHeight()420 uint32_t InputMethodPanel::GetHeight()
421 {
422 std::lock_guard<std::mutex> lock(heightLock_);
423 return panelHeight_;
424 }
425 } // namespace MiscServices
426 } // namespace OHOS
427