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