• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         WLOGFI("do not process window change.");
50         return;
51     }
52     if (windowNode == nullptr || windowNode->GetWindowToken() == nullptr) {
53         WLOGFE("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     if (!WindowHelper::IsOverlayWindow(overlayNode->GetWindowType())) {
72         WLOGFE("IsOverlayWindow Failed.");
73         return;
74     }
75     HITRACE_METER(HITRACE_TAG_WINDOW_MANAGER);
76 
77     uint32_t overlayId = overlayNode->GetWindowId();
78     bool isRecorded = (overlayWindowMap_.find(overlayId) != overlayWindowMap_.end());
79     if (isAdding == isRecorded) {
80         WLOGFE("error occured in overlay. overlayId %{public}u isAdding %{public}d record flag %{public}d",
81             overlayId, isAdding, isRecorded);
82         return;
83     }
84     if (isAdding) {
85         overlayWindowMap_.insert(std::make_pair(overlayId, overlayNode));
86     } else {
87         overlayWindowMap_.erase(overlayId);
88     }
89 
90     if (overlayNode->GetWindowType() == WindowType::WINDOW_TYPE_INPUT_METHOD_FLOAT) {
91         AddOrRemoveKeyboard(overlayNode, isAdding);
92         return;
93     }
94 
95     for (auto& node : avoidAreaListenerNodes_) {
96         AvoidArea systemAvoidArea = GetAvoidAreaByType(node, AvoidAreaType::TYPE_SYSTEM);
97         UpdateAvoidAreaIfNeed(systemAvoidArea, node, AvoidAreaType::TYPE_SYSTEM);
98     }
99 }
100 
AddOrRemoveKeyboard(const sptr<WindowNode> & keyboardNode,bool isAdding)101 void AvoidAreaController::AddOrRemoveKeyboard(const sptr<WindowNode>& keyboardNode, bool isAdding)
102 {
103     const uint32_t callingWindowId = keyboardNode->GetCallingWindow();
104     sptr<WindowNode> callingWindow = nullptr;
105     sptr<WindowNode> focusWindow = nullptr;
106     sptr<WindowNode> lastKeyboardAreaUpdatedWindow = nullptr;
107     for (auto window : avoidAreaListenerNodes_) {
108         if (window == nullptr || window->GetWindowToken() == nullptr) {
109             continue;
110         }
111         if (window->GetWindowId() == callingWindowId) {
112             callingWindow = window;
113         }
114         if (window->GetWindowId() == focusedWindow_) {
115             focusWindow = window;
116         }
117         if (window->GetWindowId() == lastSoftInputKeyboardAreaUpdatedWindowId_) {
118             lastKeyboardAreaUpdatedWindow = window;
119         }
120     }
121     if (callingWindow == nullptr) {
122         callingWindow = focusWindow;
123     }
124     if (lastKeyboardAreaUpdatedWindow != nullptr && lastKeyboardAreaUpdatedWindow != callingWindow) {
125         const WindowMode windowMode = lastKeyboardAreaUpdatedWindow->GetWindowMode();
126         if (windowMode == WindowMode::WINDOW_MODE_FULLSCREEN || windowMode == WindowMode::WINDOW_MODE_SPLIT_PRIMARY ||
127             windowMode == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
128             auto avoidArea = GetAvoidAreaByType(lastKeyboardAreaUpdatedWindow, AvoidAreaType::TYPE_KEYBOARD);
129             UpdateAvoidAreaIfNeed(avoidArea, lastKeyboardAreaUpdatedWindow, AvoidAreaType::TYPE_KEYBOARD);
130         }
131     }
132     if (callingWindow == nullptr) {
133         WLOGFE("callingWindow: %{public}u is nullptr, focusWindow: %{public}u is nullptr.",
134             callingWindowId, focusedWindow_);
135         return;
136     }
137     const WindowMode callingWindowMode = callingWindow->GetWindowMode();
138     if (callingWindowMode == WindowMode::WINDOW_MODE_FULLSCREEN ||
139         callingWindowMode == WindowMode::WINDOW_MODE_SPLIT_PRIMARY ||
140         callingWindowMode == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
141         auto avoidArea = GetAvoidAreaByType(callingWindow, AvoidAreaType::TYPE_KEYBOARD);
142         bool res = UpdateAvoidAreaIfNeed(avoidArea, callingWindow, AvoidAreaType::TYPE_KEYBOARD);
143         if (res) {
144             lastSoftInputKeyboardAreaUpdatedWindowId_ = callingWindow->GetWindowId();
145         }
146         return;
147     }
148     WLOGFE("does not have correct callingWindowMode for input method window");
149 }
150 
UpdateOverlayWindowIfNeed(const sptr<WindowNode> & node,const std::function<bool (sptr<WindowNode>)> & checkFunc)151 void AvoidAreaController::UpdateOverlayWindowIfNeed(const sptr<WindowNode>& node,
152     const std::function<bool(sptr<WindowNode>)>& checkFunc)
153 {
154     HITRACE_METER(HITRACE_TAG_WINDOW_MANAGER);
155     if (WindowHelper::IsOverlayWindow(node->GetWindowType())) {
156         AvoidAreaType type = WindowHelper::IsSystemBarWindow(node->GetWindowType()) ?
157             AvoidAreaType::TYPE_SYSTEM : AvoidAreaType::TYPE_KEYBOARD;
158         for (auto& appNode : avoidAreaListenerNodes_) {
159             if (checkFunc != nullptr && checkFunc(appNode)) {
160                 bool res = UpdateAvoidAreaIfNeed(GetAvoidAreaByType(appNode, type), appNode, type);
161                 if (type == AvoidAreaType::TYPE_KEYBOARD && res) {
162                     lastSoftInputKeyboardAreaUpdatedWindowId_ = appNode->GetWindowId();
163                 }
164             }
165         }
166     } else {
167         if (avoidAreaListenerNodes_.find(node) == avoidAreaListenerNodes_.end()) {
168             WLOGE("window: %{public}u is not in avoidAreaListenerNodes, don't update avoid area.", node->GetWindowId());
169             return;
170         }
171         uint32_t start = static_cast<uint32_t>(AvoidAreaType::TYPE_SYSTEM);
172         uint32_t end = static_cast<uint32_t>(AvoidAreaType::TYPE_KEYBOARD);
173         for (uint32_t type = start; type <= end; type++) {
174             AvoidArea systemAvoidArea = GetAvoidAreaByType(node, static_cast<AvoidAreaType>(type));
175             bool res = UpdateAvoidAreaIfNeed(systemAvoidArea, node, static_cast<AvoidAreaType>(type));
176             if (res && type == static_cast<uint32_t>(AvoidAreaType::TYPE_KEYBOARD)) {
177                 lastSoftInputKeyboardAreaUpdatedWindowId_ = node->GetWindowId();
178             }
179         }
180     }
181 }
182 
UpdateAvoidAreaIfNeed(const AvoidArea & avoidArea,const sptr<WindowNode> & node,AvoidAreaType avoidAreaType)183 bool AvoidAreaController::UpdateAvoidAreaIfNeed(const AvoidArea& avoidArea, const sptr<WindowNode>& node,
184     AvoidAreaType avoidAreaType)
185 {
186     auto iter = lastUpdatedAvoidArea_.find(node->GetWindowId());
187     bool needUpdate = true;
188     if (iter != lastUpdatedAvoidArea_.end()) {
189         auto avoidAreaIter = iter->second.find(avoidAreaType);
190         if (avoidAreaIter != iter->second.end()) {
191             needUpdate = avoidAreaIter->second != avoidArea;
192         } else {
193             if (avoidArea.isEmptyAvoidArea()) {
194                 needUpdate = false;
195             }
196         }
197     } else {
198         if (avoidArea.isEmptyAvoidArea()) {
199             needUpdate = false;
200         }
201     }
202     if (needUpdate) {
203         lastUpdatedAvoidArea_[node->GetWindowId()][avoidAreaType] = avoidArea;
204         node->GetWindowToken()->UpdateAvoidArea(new AvoidArea(avoidArea), avoidAreaType);
205     }
206     return needUpdate;
207 }
208 
CalculateOverlayRect(const sptr<WindowNode> & node,const sptr<WindowNode> & overlayNode,Rect & overlayRect) const209 AvoidPosType AvoidAreaController::CalculateOverlayRect(const sptr<WindowNode>& node,
210     const sptr<WindowNode>& overlayNode, Rect& overlayRect) const
211 {
212     if (node->GetWindowId() == overlayNode->GetWindowId()) {
213         WLOGE("overlay not support self. windowId %{public}u", node->GetWindowId());
214         return AvoidPosType::AVOID_POS_UNKNOWN;
215     }
216     const Rect rect = node->GetWindowRect();
217     overlayRect = WindowHelper::GetOverlap(overlayNode->GetWindowRect(), rect, rect.posX_, rect.posY_);
218     return  GetAvoidPosType(rect, overlayRect);
219 }
220 
GetAvoidPosType(const Rect & windowRect,const Rect & overlayRect) const221 AvoidPosType AvoidAreaController::GetAvoidPosType(const Rect& windowRect, const Rect& overlayRect) const
222 {
223     if (windowRect.width_ == 0 || windowRect.height_ == 0) {
224         return AvoidPosType::AVOID_POS_UNKNOWN;
225     }
226     uint32_t centerX = overlayRect.posX_ + (overlayRect.width_ >> 1);
227     uint32_t centerY = overlayRect.posY_ + (overlayRect.height_ >> 1);
228     float res1 = float(centerY) - float(windowRect.height_) / float(windowRect.width_) * float(centerX);
229     float res2 = float(centerY) + float(windowRect.height_) / float(windowRect.width_)  * float(centerX)
230         - float(windowRect.height_);
231     if (res1 < 0) {
232         if (res2 < 0) {
233             return AvoidPosType::AVOID_POS_TOP;
234         }
235         return AvoidPosType::AVOID_POS_RIGHT;
236     }
237     if (res2 < 0) {
238         return AvoidPosType::AVOID_POS_LEFT;
239     }
240     return AvoidPosType::AVOID_POS_BOTTOM;
241 }
242 
SetAvoidAreaRect(AvoidArea & avoidArea,const Rect & rect,AvoidPosType type) const243 void AvoidAreaController::SetAvoidAreaRect(AvoidArea& avoidArea, const Rect& rect, AvoidPosType type) const
244 {
245     switch (type) {
246         case AvoidPosType::AVOID_POS_TOP : {
247             avoidArea.topRect_ = rect;
248             break;
249         }
250         case AvoidPosType::AVOID_POS_LEFT : {
251             avoidArea.leftRect_ = rect;
252             break;
253         }
254         case AvoidPosType::AVOID_POS_RIGHT : {
255             avoidArea.rightRect_ = rect;
256             break;
257         }
258         case AvoidPosType::AVOID_POS_BOTTOM : {
259             avoidArea.bottomRect_ = rect;
260             break;
261         }
262         default : {
263             WLOGFI("default type: %{public}u", type);
264         }
265     }
266 }
267 
GetAvoidAreaByType(const sptr<WindowNode> & node,AvoidAreaType avoidAreaType) const268 AvoidArea AvoidAreaController::GetAvoidAreaByType(const sptr<WindowNode>& node, AvoidAreaType avoidAreaType) const
269 {
270     WLOGFI("avoidAreaType: %{public}u", avoidAreaType);
271     if (node == nullptr) {
272         WLOGFE("invalid WindowNode.");
273         return {};
274     }
275     WindowMode windowMode = node->GetWindowMode();
276     if (avoidAreaType != AvoidAreaType::TYPE_KEYBOARD &&
277         windowMode != WindowMode::WINDOW_MODE_FULLSCREEN &&
278         windowMode != WindowMode::WINDOW_MODE_SPLIT_PRIMARY &&
279         windowMode != WindowMode::WINDOW_MODE_SPLIT_SECONDARY) {
280         WLOGFI("avoidAreaType: %{public}u, windowMode: %{public}u, return default avoid area.",
281             avoidAreaType, windowMode);
282         return {};
283     }
284     switch (avoidAreaType) {
285         case AvoidAreaType::TYPE_SYSTEM : {
286             return GetAvoidAreaSystemType(node);
287         }
288         case AvoidAreaType::TYPE_KEYBOARD : {
289             return GetAvoidAreaKeyboardType(node);
290         }
291         case AvoidAreaType::TYPE_CUTOUT : {
292             sptr<CutoutInfo> cutoutInfo = DisplayManagerServiceInner::GetInstance().GetCutoutInfo(node->GetDisplayId());
293             if (cutoutInfo == nullptr) {
294                 WLOGFE("there is no cutoutInfo");
295                 return {};
296             }
297             std::vector<DMRect> cutoutAreas = cutoutInfo->GetBoundingRects();
298             if (cutoutAreas.empty()) {
299                 WLOGFE("there is no cutout");
300                 return {};
301             }
302             // 0 means the index in the vector.
303             Rect cutoutAreaRect { cutoutAreas[0].posX_, cutoutAreas[0].posY_,
304                 cutoutAreas[0].width_, cutoutAreas[0].height_ };
305             auto rect = node->GetWindowRect();
306             Rect overlayRect = WindowHelper::GetOverlap(cutoutAreaRect, rect, rect.posX_, rect.posY_);
307             auto type = GetAvoidPosType(rect, overlayRect);
308             AvoidArea avoidArea;
309             SetAvoidAreaRect(avoidArea, overlayRect, type);
310             return avoidArea;
311         }
312         default : {
313             WLOGFI("cannot find avoidAreaType: %{public}u", avoidAreaType);
314             return {};
315         }
316     }
317 }
318 
GetAvoidAreaSystemType(const sptr<WindowNode> & node) const319 AvoidArea AvoidAreaController::GetAvoidAreaSystemType(const sptr<WindowNode>& node) const
320 {
321     AvoidArea systemAvoidArea;
322     Rect statusBarAvoidArea;
323     AvoidPosType statusBarAvoidPosType = AvoidPosType::AVOID_POS_UNKNOWN;
324     Rect navigationBarAvoidArea;
325     AvoidPosType navigationBarAvoidPosType = AvoidPosType::AVOID_POS_UNKNOWN;
326     for (auto& iter : overlayWindowMap_) {
327         if (iter.second != nullptr) {
328             if (iter.second->GetWindowType() == WindowType::WINDOW_TYPE_STATUS_BAR) {
329                 statusBarAvoidPosType = CalculateOverlayRect(node, iter.second, statusBarAvoidArea);
330             }
331             if (iter.second->GetWindowType() == WindowType::WINDOW_TYPE_NAVIGATION_BAR) {
332                 navigationBarAvoidPosType = CalculateOverlayRect(node, iter.second, navigationBarAvoidArea);
333             }
334         }
335     }
336     SetAvoidAreaRect(systemAvoidArea, statusBarAvoidArea, statusBarAvoidPosType);
337     SetAvoidAreaRect(systemAvoidArea, navigationBarAvoidArea, navigationBarAvoidPosType);
338     return systemAvoidArea;
339 }
340 
GetAvoidAreaKeyboardType(const sptr<WindowNode> & node) const341 AvoidArea AvoidAreaController::GetAvoidAreaKeyboardType(const sptr<WindowNode>& node) const
342 {
343     for (auto& iter : overlayWindowMap_) {
344         if (iter.second != nullptr &&
345             iter.second->GetWindowType() == WindowType::WINDOW_TYPE_INPUT_METHOD_FLOAT) {
346             const uint32_t callingWindowId = iter.second->GetCallingWindow();
347             if (callingWindowId != node->GetWindowId() && focusedWindow_ != node->GetWindowId()) {
348                 WLOGFI("windowId: %{public}u is not focusedWindow: %{public}u or callingWindow: %{public}u",
349                        node->GetWindowId(), focusedWindow_, callingWindowId);
350                 continue;
351             }
352             Rect avoidAreaRect { 0, 0, 0, 0 };
353             AvoidPosType avoidPosType = CalculateOverlayRect(node, iter.second, avoidAreaRect);
354             AvoidArea avoidArea;
355             SetAvoidAreaRect(avoidArea, avoidAreaRect, avoidPosType);
356             return avoidArea;
357         }
358     }
359     return {};
360 }
361 }
362 }
363