1 /*
2 * Copyright (c) 2021-2022 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 "avoid_area_controller.h"
17
18 #include <hitrace_meter.h>
19
20 #include "display_manager_service_inner.h"
21 #include "window_helper.h"
22 #include "window_manager_hilog.h"
23
24 namespace OHOS {
25 namespace Rosen {
26 namespace {
27 constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "AvoidAreaController"};
28 }
29
UpdateAvoidAreaListener(sptr<WindowNode> & windowNode,bool isRegisterListener)30 void AvoidAreaController::UpdateAvoidAreaListener(sptr<WindowNode>& windowNode, bool isRegisterListener)
31 {
32 WLOGFE("haveAvoidAreaListener %{public}d", isRegisterListener);
33 if (windowNode == nullptr) {
34 WLOGFE("windowNode is nullptr.");
35 return;
36 }
37 if (isRegisterListener) {
38 avoidAreaListenerNodes_.insert(windowNode);
39 } else {
40 lastUpdatedAvoidArea_.erase(windowNode->GetWindowId());
41 avoidAreaListenerNodes_.erase(windowNode);
42 }
43 }
44
ProcessWindowChange(const sptr<WindowNode> & windowNode,AvoidControlType avoidType,const std::function<bool (sptr<WindowNode>)> & checkFunc)45 void AvoidAreaController::ProcessWindowChange(const sptr<WindowNode>& windowNode, AvoidControlType avoidType,
46 const std::function<bool(sptr<WindowNode>)>& checkFunc)
47 {
48 if (isForbidProcessingWindowChange_) {
49 WLOGI("do not process window change.");
50 return;
51 }
52 if (windowNode == nullptr || windowNode->GetWindowToken() == nullptr) {
53 WLOGE("invalid WindowNode.");
54 return;
55 }
56 switch (avoidType) {
57 case AvoidControlType::AVOID_NODE_ADD:
58 case AvoidControlType::AVOID_NODE_REMOVE:
59 AddOrRemoveOverlayWindowIfNeed(windowNode, avoidType == AvoidControlType::AVOID_NODE_ADD);
60 break;
61 case AvoidControlType::AVOID_NODE_UPDATE:
62 UpdateOverlayWindowIfNeed(windowNode, checkFunc);
63 break;
64 default:
65 break;
66 }
67 }
68
AddOrRemoveOverlayWindowIfNeed(const sptr<WindowNode> & overlayNode,bool isAdding)69 void AvoidAreaController::AddOrRemoveOverlayWindowIfNeed(const sptr<WindowNode>& overlayNode, bool isAdding)
70 {
71 WindowGravity windowGravity;
72 uint32_t percent;
73 overlayNode->GetWindowGravity(windowGravity, percent);
74 if (!WindowHelper::IsOverlayWindow(overlayNode->GetWindowType()) ||
75 windowGravity == WindowGravity::WINDOW_GRAVITY_FLOAT) {
76 WLOGE("IsOverlayWindow Failed.");
77 return;
78 }
79 HITRACE_METER(HITRACE_TAG_WINDOW_MANAGER);
80
81 uint32_t overlayId = overlayNode->GetWindowId();
82 bool isRecorded = (overlayWindowMap_.find(overlayId) != overlayWindowMap_.end());
83 if (isAdding == isRecorded) {
84 WLOGE("error occured in overlay. overlayId %{public}u isAdding %{public}d record flag %{public}d",
85 overlayId, isAdding, isRecorded);
86 return;
87 }
88 if (isAdding) {
89 overlayWindowMap_.insert(std::make_pair(overlayId, overlayNode));
90 } else {
91 overlayWindowMap_.erase(overlayId);
92 }
93
94 if (overlayNode->GetWindowType() == WindowType::WINDOW_TYPE_INPUT_METHOD_FLOAT) {
95 AddOrRemoveKeyboard(overlayNode, isAdding);
96 return;
97 }
98
99 for (auto& node : avoidAreaListenerNodes_) {
100 AvoidArea systemAvoidArea = GetAvoidAreaByType(node, AvoidAreaType::TYPE_SYSTEM);
101 UpdateAvoidAreaIfNeed(systemAvoidArea, node, AvoidAreaType::TYPE_SYSTEM);
102 }
103 }
104
AddOrRemoveKeyboard(const sptr<WindowNode> & keyboardNode,bool isAdding)105 void AvoidAreaController::AddOrRemoveKeyboard(const sptr<WindowNode>& keyboardNode, bool isAdding)
106 {
107 const uint32_t callingWindowId = keyboardNode->GetCallingWindow();
108 sptr<WindowNode> callingWindow = nullptr;
109 sptr<WindowNode> focusWindow = nullptr;
110 sptr<WindowNode> lastKeyboardAreaUpdatedWindow = nullptr;
111 for (auto window : avoidAreaListenerNodes_) {
112 if (window == nullptr || window->GetWindowToken() == nullptr) {
113 continue;
114 }
115 if (window->GetWindowId() == callingWindowId) {
116 callingWindow = window;
117 }
118 if (window->GetWindowId() == focusedWindow_) {
119 focusWindow = window;
120 }
121 if (window->GetWindowId() == lastSoftInputKeyboardAreaUpdatedWindowId_) {
122 lastKeyboardAreaUpdatedWindow = window;
123 }
124 }
125 if (callingWindow == nullptr) {
126 callingWindow = focusWindow;
127 }
128 if (lastKeyboardAreaUpdatedWindow != nullptr && lastKeyboardAreaUpdatedWindow != callingWindow) {
129 const WindowMode windowMode = lastKeyboardAreaUpdatedWindow->GetWindowMode();
130 if (windowMode == WindowMode::WINDOW_MODE_FULLSCREEN || windowMode == WindowMode::WINDOW_MODE_SPLIT_PRIMARY ||
131 windowMode == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
132 auto avoidArea = GetAvoidAreaByType(lastKeyboardAreaUpdatedWindow, AvoidAreaType::TYPE_KEYBOARD);
133 UpdateAvoidAreaIfNeed(avoidArea, lastKeyboardAreaUpdatedWindow, AvoidAreaType::TYPE_KEYBOARD);
134 }
135 }
136 if (callingWindow == nullptr) {
137 WLOGFE("callingWindow: %{public}u is nullptr, focusWindow: %{public}u is nullptr.",
138 callingWindowId, focusedWindow_);
139 return;
140 }
141 const WindowMode callingWindowMode = callingWindow->GetWindowMode();
142 if (callingWindowMode == WindowMode::WINDOW_MODE_FULLSCREEN ||
143 callingWindowMode == WindowMode::WINDOW_MODE_SPLIT_PRIMARY ||
144 callingWindowMode == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
145 auto avoidArea = GetAvoidAreaByType(callingWindow, AvoidAreaType::TYPE_KEYBOARD);
146 bool res = UpdateAvoidAreaIfNeed(avoidArea, callingWindow, AvoidAreaType::TYPE_KEYBOARD);
147 if (res) {
148 lastSoftInputKeyboardAreaUpdatedWindowId_ = callingWindow->GetWindowId();
149 }
150 return;
151 }
152 WLOGFE("does not have correct callingWindowMode for input method window");
153 }
154
UpdateOverlayWindowIfNeed(const sptr<WindowNode> & node,const std::function<bool (sptr<WindowNode>)> & checkFunc)155 void AvoidAreaController::UpdateOverlayWindowIfNeed(const sptr<WindowNode>& node,
156 const std::function<bool(sptr<WindowNode>)>& checkFunc)
157 {
158 HITRACE_METER(HITRACE_TAG_WINDOW_MANAGER);
159 WindowGravity windowGravity;
160 uint32_t percent;
161 node->GetWindowGravity(windowGravity, percent);
162 if (WindowHelper::IsOverlayWindow(node->GetWindowType()) &&
163 windowGravity == WindowGravity::WINDOW_GRAVITY_BOTTOM) {
164 AvoidAreaType type = WindowHelper::IsSystemBarWindow(node->GetWindowType()) ?
165 AvoidAreaType::TYPE_SYSTEM : AvoidAreaType::TYPE_KEYBOARD;
166 for (auto& appNode : avoidAreaListenerNodes_) {
167 if (checkFunc != nullptr && checkFunc(appNode)) {
168 bool res = UpdateAvoidAreaIfNeed(GetAvoidAreaByType(appNode, type), appNode, type);
169 if (type == AvoidAreaType::TYPE_KEYBOARD && res) {
170 lastSoftInputKeyboardAreaUpdatedWindowId_ = appNode->GetWindowId();
171 }
172 }
173 }
174 } else {
175 if (avoidAreaListenerNodes_.find(node) == avoidAreaListenerNodes_.end()) {
176 WLOGD("window: %{public}u is not in avoidAreaListenerNodes, don't update avoid area.", node->GetWindowId());
177 return;
178 }
179 uint32_t start = static_cast<uint32_t>(AvoidAreaType::TYPE_SYSTEM);
180 uint32_t end = static_cast<uint32_t>(AvoidAreaType::TYPE_KEYBOARD);
181 for (uint32_t type = start; type <= end; type++) {
182 AvoidArea systemAvoidArea = GetAvoidAreaByType(node, static_cast<AvoidAreaType>(type));
183 bool res = UpdateAvoidAreaIfNeed(systemAvoidArea, node, static_cast<AvoidAreaType>(type));
184 if (res && type == static_cast<uint32_t>(AvoidAreaType::TYPE_KEYBOARD)) {
185 lastSoftInputKeyboardAreaUpdatedWindowId_ = node->GetWindowId();
186 }
187 }
188 }
189 }
190
UpdateAvoidAreaIfNeed(const AvoidArea & avoidArea,const sptr<WindowNode> & node,AvoidAreaType avoidAreaType)191 bool AvoidAreaController::UpdateAvoidAreaIfNeed(const AvoidArea& avoidArea, const sptr<WindowNode>& node,
192 AvoidAreaType avoidAreaType)
193 {
194 if (!node->currentVisibility_) {
195 WLOGFD("window: %{public}u is not visible, don't update avoid area", node->GetWindowId());
196 return false;
197 }
198 auto iter = lastUpdatedAvoidArea_.find(node->GetWindowId());
199 bool needUpdate = true;
200 if (iter != lastUpdatedAvoidArea_.end()) {
201 auto avoidAreaIter = iter->second.find(avoidAreaType);
202 if (avoidAreaIter != iter->second.end()) {
203 needUpdate = avoidAreaIter->second != avoidArea;
204 } else {
205 if (avoidArea.isEmptyAvoidArea()) {
206 needUpdate = false;
207 }
208 }
209 } else {
210 if (avoidArea.isEmptyAvoidArea()) {
211 needUpdate = false;
212 }
213 }
214 if (needUpdate) {
215 lastUpdatedAvoidArea_[node->GetWindowId()][avoidAreaType] = avoidArea;
216 node->GetWindowToken()->UpdateAvoidArea(new AvoidArea(avoidArea), avoidAreaType);
217 }
218 return needUpdate;
219 }
220
CalculateOverlayRect(const sptr<WindowNode> & node,const sptr<WindowNode> & overlayNode,Rect & overlayRect) const221 AvoidPosType AvoidAreaController::CalculateOverlayRect(const sptr<WindowNode>& node,
222 const sptr<WindowNode>& overlayNode, Rect& overlayRect) const
223 {
224 if (node->GetWindowId() == overlayNode->GetWindowId()) {
225 WLOGE("overlay not support self. windowId %{public}u", node->GetWindowId());
226 return AvoidPosType::AVOID_POS_UNKNOWN;
227 }
228 const Rect rect = node->GetWindowRect();
229 overlayRect = WindowHelper::GetOverlap(overlayNode->GetWindowRect(), rect, rect.posX_, rect.posY_);
230 return GetAvoidPosType(rect, overlayRect);
231 }
232
GetAvoidPosType(const Rect & windowRect,const Rect & overlayRect) const233 AvoidPosType AvoidAreaController::GetAvoidPosType(const Rect& windowRect, const Rect& overlayRect) const
234 {
235 if (windowRect.width_ == 0 || windowRect.height_ == 0) {
236 return AvoidPosType::AVOID_POS_UNKNOWN;
237 }
238 uint32_t centerX = overlayRect.posX_ + (overlayRect.width_ >> 1);
239 uint32_t centerY = overlayRect.posY_ + (overlayRect.height_ >> 1);
240 float res1 = float(centerY) - float(windowRect.height_) / float(windowRect.width_) * float(centerX);
241 float res2 = float(centerY) + float(windowRect.height_) / float(windowRect.width_) * float(centerX) -
242 float(windowRect.height_);
243 if (res1 < 0) {
244 if (res2 < 0) {
245 return AvoidPosType::AVOID_POS_TOP;
246 }
247 return AvoidPosType::AVOID_POS_RIGHT;
248 }
249 if (res2 < 0) {
250 return AvoidPosType::AVOID_POS_LEFT;
251 }
252 return AvoidPosType::AVOID_POS_BOTTOM;
253 }
254
SetAvoidAreaRect(AvoidArea & avoidArea,const Rect & rect,AvoidPosType type) const255 void AvoidAreaController::SetAvoidAreaRect(AvoidArea& avoidArea, const Rect& rect, AvoidPosType type) const
256 {
257 switch (type) {
258 case AvoidPosType::AVOID_POS_TOP : {
259 avoidArea.topRect_ = rect;
260 break;
261 }
262 case AvoidPosType::AVOID_POS_LEFT : {
263 avoidArea.leftRect_ = rect;
264 break;
265 }
266 case AvoidPosType::AVOID_POS_RIGHT : {
267 avoidArea.rightRect_ = rect;
268 break;
269 }
270 case AvoidPosType::AVOID_POS_BOTTOM : {
271 avoidArea.bottomRect_ = rect;
272 break;
273 }
274 default : {
275 WLOGFD("default type: %{public}u", type);
276 }
277 }
278 }
279
GetAvoidAreaByType(const sptr<WindowNode> & node,AvoidAreaType avoidAreaType) const280 AvoidArea AvoidAreaController::GetAvoidAreaByType(const sptr<WindowNode>& node, AvoidAreaType avoidAreaType) const
281 {
282 WLOGFD("avoidAreaType: %{public}u", avoidAreaType);
283 if (node == nullptr) {
284 WLOGFE("invalid WindowNode.");
285 return {};
286 }
287 WindowMode windowMode = node->GetWindowMode();
288 if (avoidAreaType != AvoidAreaType::TYPE_KEYBOARD &&
289 windowMode != WindowMode::WINDOW_MODE_FULLSCREEN &&
290 windowMode != WindowMode::WINDOW_MODE_SPLIT_PRIMARY &&
291 windowMode != WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
292 WLOGI("avoidAreaType: %{public}u, windowMode: %{public}u, return default avoid area.",
293 avoidAreaType, windowMode);
294 return {};
295 }
296 switch (avoidAreaType) {
297 case AvoidAreaType::TYPE_SYSTEM : {
298 return GetAvoidAreaSystemType(node);
299 }
300 case AvoidAreaType::TYPE_KEYBOARD : {
301 return GetAvoidAreaKeyboardType(node);
302 }
303 case AvoidAreaType::TYPE_CUTOUT : {
304 sptr<CutoutInfo> cutoutInfo = DisplayManagerServiceInner::GetInstance().GetCutoutInfo(node->GetDisplayId());
305 if (cutoutInfo == nullptr) {
306 WLOGFE("there is no cutoutInfo");
307 return {};
308 }
309 std::vector<DMRect> cutoutAreas = cutoutInfo->GetBoundingRects();
310 if (cutoutAreas.empty()) {
311 WLOGFE("there is no cutout");
312 return {};
313 }
314 // 0 means the index in the vector.
315 Rect cutoutAreaRect { cutoutAreas[0].posX_, cutoutAreas[0].posY_,
316 cutoutAreas[0].width_, cutoutAreas[0].height_ };
317 auto rect = node->GetWindowRect();
318 Rect overlayRect = WindowHelper::GetOverlap(cutoutAreaRect, rect, rect.posX_, rect.posY_);
319 auto type = GetAvoidPosType(rect, overlayRect);
320 AvoidArea avoidArea;
321 SetAvoidAreaRect(avoidArea, overlayRect, type);
322 return avoidArea;
323 }
324 default : {
325 WLOGFD("cannot find avoidAreaType: %{public}u", avoidAreaType);
326 return {};
327 }
328 }
329 }
330
GetAvoidAreaSystemType(const sptr<WindowNode> & node) const331 AvoidArea AvoidAreaController::GetAvoidAreaSystemType(const sptr<WindowNode>& node) const
332 {
333 AvoidArea systemAvoidArea;
334 Rect statusBarAvoidArea;
335 AvoidPosType statusBarAvoidPosType = AvoidPosType::AVOID_POS_UNKNOWN;
336 Rect navigationBarAvoidArea;
337 AvoidPosType navigationBarAvoidPosType = AvoidPosType::AVOID_POS_UNKNOWN;
338 for (auto& iter : overlayWindowMap_) {
339 if (iter.second != nullptr) {
340 if (iter.second->GetWindowType() == WindowType::WINDOW_TYPE_STATUS_BAR) {
341 statusBarAvoidPosType = CalculateOverlayRect(node, iter.second, statusBarAvoidArea);
342 }
343 if (iter.second->GetWindowType() == WindowType::WINDOW_TYPE_NAVIGATION_BAR) {
344 navigationBarAvoidPosType = CalculateOverlayRect(node, iter.second, navigationBarAvoidArea);
345 }
346 }
347 }
348 SetAvoidAreaRect(systemAvoidArea, statusBarAvoidArea, statusBarAvoidPosType);
349 SetAvoidAreaRect(systemAvoidArea, navigationBarAvoidArea, navigationBarAvoidPosType);
350 return systemAvoidArea;
351 }
352
GetAvoidAreaKeyboardType(const sptr<WindowNode> & node) const353 AvoidArea AvoidAreaController::GetAvoidAreaKeyboardType(const sptr<WindowNode>& node) const
354 {
355 for (auto& iter : overlayWindowMap_) {
356 if (iter.second != nullptr &&
357 iter.second->GetWindowType() == WindowType::WINDOW_TYPE_INPUT_METHOD_FLOAT) {
358 const uint32_t callingWindowId = iter.second->GetCallingWindow();
359 if (callingWindowId != node->GetWindowId() && focusedWindow_ != node->GetWindowId()) {
360 WLOGI("windowId: %{public}u is not focusedWindow: %{public}u or callingWindow: %{public}u",
361 node->GetWindowId(), focusedWindow_, callingWindowId);
362 continue;
363 }
364 Rect avoidAreaRect { 0, 0, 0, 0 };
365 AvoidPosType avoidPosType = CalculateOverlayRect(node, iter.second, avoidAreaRect);
366 AvoidArea avoidArea;
367 SetAvoidAreaRect(avoidArea, avoidAreaRect, avoidPosType);
368 return avoidArea;
369 }
370 }
371 return {};
372 }
373 }
374 }
375