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 SCREEN_RATIO = 0.6;
29 std::atomic<uint32_t> InputMethodPanel::sequenceId_{ 0 };
30 InputMethodPanel::~InputMethodPanel() = default;
31
CreatePanel(const std::shared_ptr<AbilityRuntime::Context> & context,const PanelInfo & panelInfo)32 int32_t InputMethodPanel::CreatePanel(
33 const std::shared_ptr<AbilityRuntime::Context> &context, const PanelInfo &panelInfo)
34 {
35 IMSA_HILOGD("InputMethodPanel start to create panel.");
36 panelType_ = panelInfo.panelType;
37 panelFlag_ = panelInfo.panelFlag;
38 winOption_ = new (std::nothrow) OHOS::Rosen::WindowOption();
39 if (winOption_ == nullptr) {
40 return ErrorCode::ERROR_NULL_POINTER;
41 }
42 if (panelInfo.panelType == PanelType::STATUS_BAR) {
43 winOption_->SetWindowType(OHOS::Rosen::WindowType::WINDOW_TYPE_INPUT_METHOD_STATUS_BAR);
44 } else {
45 winOption_->SetWindowType(OHOS::Rosen::WindowType::WINDOW_TYPE_INPUT_METHOD_FLOAT);
46 }
47 WMError wmError = WMError::WM_OK;
48 window_ = OHOS::Rosen::Window::Create(GeneratePanelName(), winOption_, context, wmError);
49 if (wmError == WMError::WM_ERROR_INVALID_PERMISSION || wmError == WMError::WM_ERROR_NOT_SYSTEM_APP) {
50 IMSA_HILOGE("Create window failed, permission denied, %{public}d", wmError);
51 return ErrorCode::ERROR_NOT_IME;
52 }
53 if (window_ == nullptr || wmError != WMError::WM_OK) {
54 return ErrorCode::ERROR_OPERATE_PANEL;
55 }
56 windowId_ = window_->GetWindowId();
57 IMSA_HILOGD("GetWindowId, windowId = %{public}u", windowId_);
58 if (SetPanelProperties() != ErrorCode::NO_ERROR) {
59 wmError = window_->Destroy();
60 IMSA_HILOGI("Destroy window end, wmError is %{public}d.", wmError);
61 return ErrorCode::ERROR_OPERATE_PANEL;
62 }
63 return ErrorCode::NO_ERROR;
64 }
65
GeneratePanelName()66 std::string InputMethodPanel::GeneratePanelName()
67 {
68 uint32_t sequenceId = GenerateSequenceId();
69 std::string windowName = panelType_ == SOFT_KEYBOARD ? "softKeyboard" + std::to_string(sequenceId)
70 : "statusBar" + std::to_string(sequenceId);
71 IMSA_HILOGD("InputMethodPanel, windowName = %{public}s", windowName.c_str());
72 return windowName;
73 }
74
SetPanelProperties()75 int32_t InputMethodPanel::SetPanelProperties()
76 {
77 if (window_ == nullptr) {
78 IMSA_HILOGE("window is not exist.");
79 return ErrorCode::ERROR_OPERATE_PANEL;
80 }
81 WindowGravity gravity = WindowGravity::WINDOW_GRAVITY_FLOAT;
82 if (panelType_ == SOFT_KEYBOARD && panelFlag_ == FLG_FIXED) {
83 gravity = WindowGravity::WINDOW_GRAVITY_BOTTOM;
84 } else if (panelType_ == SOFT_KEYBOARD && panelFlag_ == FLG_FLOATING) {
85 window_->GetSurfaceNode()->SetFrameGravity(Rosen::Gravity::TOP_LEFT);
86 Rosen::RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
87 } else if (panelType_ == STATUS_BAR) {
88 window_->GetSurfaceNode()->SetFrameGravity(Rosen::Gravity::TOP_LEFT);
89 Rosen::RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
90 return ErrorCode::NO_ERROR;
91 }
92 WMError wmError = window_->SetWindowGravity(gravity, invalidGravityPercent);
93 if (wmError != WMError::WM_OK) {
94 IMSA_HILOGE("SetWindowGravity failed, wmError is %{public}d, start destroy window.", wmError);
95 return ErrorCode::ERROR_OPERATE_PANEL;
96 }
97 return ErrorCode::NO_ERROR;
98 }
99
DestroyPanel()100 int32_t InputMethodPanel::DestroyPanel()
101 {
102 auto ret = HidePanel();
103 if (ret != ErrorCode::NO_ERROR) {
104 IMSA_HILOGE("InputMethodPanel, hide panel failed, ret = %{public}d.", ret);
105 return ret;
106 }
107 auto result = window_->Destroy();
108 IMSA_HILOGI("InputMethodPanel, Destroy end, ret = %{public}d", result);
109 return result == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
110 }
111
Resize(uint32_t width,uint32_t height)112 int32_t InputMethodPanel::Resize(uint32_t width, uint32_t height)
113 {
114 if (window_ == nullptr) {
115 return ErrorCode::ERROR_NULL_POINTER;
116 }
117 auto defaultDisplay = Rosen::DisplayManager::GetInstance().GetDefaultDisplay();
118 if (defaultDisplay == nullptr) {
119 IMSA_HILOGE("GetDefaultDisplay failed.");
120 return ErrorCode::ERROR_NULL_POINTER;
121 }
122 if (width > INT32_MAX || height > INT32_MAX) {
123 IMSA_HILOGE("width or height over maximum");
124 return ErrorCode::ERROR_BAD_PARAMETERS;
125 }
126 if (static_cast<int32_t>(width) > defaultDisplay->GetWidth() ||
127 static_cast<float>(height) > defaultDisplay->GetHeight() * SCREEN_RATIO) {
128 IMSA_HILOGE("GetDefaultDisplay, defaultDisplay->width = %{public}d, defaultDisplay->height = %{public}d, "
129 "width = %{public}u, height = %{public}u",
130 defaultDisplay->GetWidth(), defaultDisplay->GetHeight(), width, height);
131 return ErrorCode::ERROR_BAD_PARAMETERS;
132 }
133 auto ret = window_->Resize(width, height);
134 IMSA_HILOGI("InputMethodPanel, Resize ret = %{public}d", ret);
135 return ret == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
136 }
137
MoveTo(int32_t x,int32_t y)138 int32_t InputMethodPanel::MoveTo(int32_t x, int32_t y)
139 {
140 if (window_ == nullptr) {
141 IMSA_HILOGE("window_ is nullptr.");
142 return ErrorCode::ERROR_NULL_POINTER;
143 }
144 if (panelType_ == SOFT_KEYBOARD && panelFlag_ == FLG_FIXED) {
145 IMSA_HILOGE("FLG_FIXED panel can not moveTo.");
146 return ErrorCode::NO_ERROR;
147 }
148 auto ret = window_->MoveTo(x, y);
149 IMSA_HILOGI("InputMethodPanel, MoveTo ret = %{public}d", ret);
150 return ret == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
151 }
152
ChangePanelFlag(PanelFlag panelFlag)153 int32_t InputMethodPanel::ChangePanelFlag(PanelFlag panelFlag)
154 {
155 if (window_ == nullptr) {
156 IMSA_HILOGE("window_ is nullptr.");
157 return ErrorCode::ERROR_NULL_POINTER;
158 }
159 if (panelFlag_ == panelFlag) {
160 return ErrorCode::NO_ERROR;
161 }
162 if (panelType_ == STATUS_BAR) {
163 IMSA_HILOGE("STATUS_BAR cannot ChangePanelFlag.");
164 return ErrorCode::ERROR_BAD_PARAMETERS;
165 }
166 panelFlag_ = panelFlag;
167 WindowGravity gravity = WindowGravity::WINDOW_GRAVITY_FLOAT;
168 if (panelFlag == FLG_FIXED) {
169 gravity = WindowGravity::WINDOW_GRAVITY_BOTTOM;
170 } else {
171 window_->GetSurfaceNode()->SetFrameGravity(Rosen::Gravity::TOP_LEFT);
172 Rosen::RSTransactionProxy::GetInstance()->FlushImplicitTransaction();
173 }
174 auto ret = window_->SetWindowGravity(gravity, invalidGravityPercent);
175 IMSA_HILOGI("InputMethodPanel, ChangePanelFlag end, ret = %{public}d", ret);
176 return ret == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
177 }
178
GetPanelType()179 PanelType InputMethodPanel::GetPanelType()
180 {
181 return panelType_;
182 }
183
GetPanelFlag()184 PanelFlag InputMethodPanel::GetPanelFlag()
185 {
186 return panelFlag_;
187 }
188
ShowPanel()189 int32_t InputMethodPanel::ShowPanel()
190 {
191 if (window_ == nullptr) {
192 IMSA_HILOGE("window_ is nullptr.");
193 return ErrorCode::ERROR_NULL_POINTER;
194 }
195 if (IsShowing()) {
196 IMSA_HILOGE("Panel already shown.");
197 return ErrorCode::NO_ERROR;
198 }
199 auto ret = window_->Show();
200 if (ret != WMError::WM_OK) {
201 IMSA_HILOGE("ShowPanel error, err = %{public}d", ret);
202 return ErrorCode::ERROR_OPERATE_PANEL;
203 }
204 IMSA_HILOGI("InputMethodPanel, ShowPanel success.");
205 PanelStatusChange(InputWindowStatus::SHOW);
206 return ErrorCode::NO_ERROR;
207 }
208
HidePanel()209 int32_t InputMethodPanel::HidePanel()
210 {
211 if (window_ == nullptr) {
212 IMSA_HILOGE("window_ is nullptr.");
213 return ErrorCode::ERROR_NULL_POINTER;
214 }
215 if (IsHidden()) {
216 IMSA_HILOGE("Panel already hidden.");
217 return ErrorCode::NO_ERROR;
218 }
219 auto ret = window_->Hide();
220 if (ret != WMError::WM_OK) {
221 IMSA_HILOGE("HidePanel error, err = %{public}d", ret);
222 return ErrorCode::ERROR_OPERATE_PANEL;
223 }
224 IMSA_HILOGI("InputMethodPanel, HidePanel success.");
225 PanelStatusChange(InputWindowStatus::HIDE);
226 return ErrorCode::NO_ERROR;
227 }
228
SetCallingWindow(uint32_t windowId)229 int32_t InputMethodPanel::SetCallingWindow(uint32_t windowId)
230 {
231 if (window_ == nullptr) {
232 IMSA_HILOGE("window_ is nullptr.");
233 return ErrorCode::ERROR_NULL_POINTER;
234 }
235 auto ret = window_->SetCallingWindow(windowId);
236 IMSA_HILOGI("InputMethodPanel, SetCallingWindow ret = %{public}d, windowId = %{public}u", ret, windowId);
237 return ret == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
238 }
239
PanelStatusChange(const InputWindowStatus & status)240 void InputMethodPanel::PanelStatusChange(const InputWindowStatus &status)
241 {
242 if (status == InputWindowStatus::SHOW && showRegistered_ && panelStatusListener_ != nullptr) {
243 IMSA_HILOGE("InputMethodPanel::ShowPanel panelStatusListener_ is not nullptr");
244 panelStatusListener_->OnPanelStatus(windowId_, true);
245 }
246 if (status == InputWindowStatus::HIDE && hideRegistered_ && panelStatusListener_ != nullptr) {
247 IMSA_HILOGE("InputMethodPanel::HidePanel panelStatusListener_ is not nullptr");
248 panelStatusListener_->OnPanelStatus(windowId_, false);
249 }
250 auto imsa = ImaUtils::GetImsaProxy();
251 if (imsa == nullptr) {
252 IMSA_HILOGE("imsa is nullptr");
253 return;
254 }
255 if (panelType_ == SOFT_KEYBOARD && panelFlag_ == FLG_FIXED) {
256 auto rect = window_->GetRect();
257 IMSA_HILOGD("InputMethodPanel::x:%{public}d, y:%{public}d, w:%{public}u, h:%{public}u", rect.posX_, rect.posY_,
258 rect.width_, rect.height_);
259 std::string name = window_->GetWindowName() + "/" + std::to_string(window_->GetWindowId());
260 imsa->PanelStatusChange(status, { std::move(name), rect.posX_, rect.posY_, rect.width_, rect.height_ });
261 }
262 }
263
IsShowing()264 bool InputMethodPanel::IsShowing()
265 {
266 WindowState windowState = window_->GetWindowState();
267 if (windowState == WindowState::STATE_SHOWN) {
268 return true;
269 }
270 IMSA_HILOGD("InputMethodPanel windowState = %{public}d", static_cast<int>(windowState));
271 return false;
272 }
273
IsHidden()274 bool InputMethodPanel::IsHidden()
275 {
276 WindowState windowState = window_->GetWindowState();
277 if (windowState == WindowState::STATE_HIDDEN) {
278 return true;
279 }
280 IMSA_HILOGD("InputMethodPanel windowState = %{public}d", static_cast<int>(windowState));
281 return false;
282 }
283
SetUiContent(const std::string & contentInfo,NativeEngine & engine,std::shared_ptr<NativeReference> storage)284 int32_t InputMethodPanel::SetUiContent(
285 const std::string &contentInfo, NativeEngine &engine, std::shared_ptr<NativeReference> storage)
286 {
287 if (window_ == nullptr) {
288 IMSA_HILOGE("window_ is nullptr, can not SetUiContent.");
289 return ErrorCode::ERROR_NULL_POINTER;
290 }
291 WMError ret = WMError::WM_OK;
292 if (storage == nullptr) {
293 ret = window_->SetUIContent(contentInfo, &engine, nullptr);
294 } else {
295 ret = window_->SetUIContent(contentInfo, &engine, storage->Get());
296 }
297 WMError wmError = window_->SetTransparent(true);
298 IMSA_HILOGI("SetTransparent end, wmError = %{public}u", wmError);
299 IMSA_HILOGI("InputMethodPanel, SetUiContent ret = %{public}d", ret);
300 return ret == WMError::WM_OK ? ErrorCode::NO_ERROR : ErrorCode::ERROR_OPERATE_PANEL;
301 }
302
SetPanelStatusListener(std::shared_ptr<PanelStatusListener> statusListener,const std::string & type)303 void InputMethodPanel::SetPanelStatusListener(
304 std::shared_ptr<PanelStatusListener> statusListener, const std::string &type)
305 {
306 IMSA_HILOGD("SetPanelStatusListener start.");
307 if (!MarkListener(type, true)) {
308 return;
309 }
310 if (panelStatusListener_ != nullptr) {
311 IMSA_HILOGE("PanelStatusListener already set.");
312 return;
313 }
314 panelStatusListener_ = std::move(statusListener);
315 if (window_ != nullptr && IsShowing()) {
316 panelStatusListener_->OnPanelStatus(windowId_, true);
317 }
318 }
319
ClearPanelListener(const std::string & type)320 void InputMethodPanel::ClearPanelListener(const std::string &type)
321 {
322 if (!MarkListener(type, false)) {
323 return;
324 }
325 if (panelStatusListener_ == nullptr) {
326 IMSA_HILOGE("PanelStatusListener not set, don't need to remove.");
327 return;
328 }
329 if (showRegistered_ || hideRegistered_) {
330 return;
331 }
332 panelStatusListener_ = nullptr;
333 }
334
MarkListener(const std::string & type,bool isRegister)335 bool InputMethodPanel::MarkListener(const std::string &type, bool isRegister)
336 {
337 if (type == "show") {
338 showRegistered_ = isRegister;
339 } else if (type == "hide") {
340 hideRegistered_ = isRegister;
341 } else {
342 IMSA_HILOGE("type error.");
343 return false;
344 }
345 return true;
346 }
347
GenerateSequenceId()348 uint32_t InputMethodPanel::GenerateSequenceId()
349 {
350 uint32_t seqId = ++sequenceId_;
351 if (seqId == std::numeric_limits<uint32_t>::max()) {
352 return ++sequenceId_;
353 }
354 return seqId;
355 }
356 } // namespace MiscServices
357 } // namespace OHOS
358