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 24namespace OHOS { 25namespace Rosen { 26namespace { 27constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "AvoidAreaController"}; 28} 29 30void 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 45void 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 69void 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 105void 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 155void 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 191bool AvoidAreaController::UpdateAvoidAreaIfNeed(const AvoidArea& avoidArea, const sptr<WindowNode>& node, 192 AvoidAreaType avoidAreaType) 193{ 194 auto iter = lastUpdatedAvoidArea_.find(node->GetWindowId()); 195 bool needUpdate = true; 196 if (iter != lastUpdatedAvoidArea_.end()) { 197 auto avoidAreaIter = iter->second.find(avoidAreaType); 198 if (avoidAreaIter != iter->second.end()) { 199 needUpdate = avoidAreaIter->second != avoidArea; 200 } else { 201 if (avoidArea.isEmptyAvoidArea()) { 202 needUpdate = false; 203 } 204 } 205 } else { 206 if (avoidArea.isEmptyAvoidArea()) { 207 needUpdate = false; 208 } 209 } 210 if (needUpdate) { 211 lastUpdatedAvoidArea_[node->GetWindowId()][avoidAreaType] = avoidArea; 212 node->GetWindowToken()->UpdateAvoidArea(new AvoidArea(avoidArea), avoidAreaType); 213 } 214 return needUpdate; 215} 216 217AvoidPosType AvoidAreaController::CalculateOverlayRect(const sptr<WindowNode>& node, 218 const sptr<WindowNode>& overlayNode, Rect& overlayRect) const 219{ 220 if (node->GetWindowId() == overlayNode->GetWindowId()) { 221 WLOGE("overlay not support self. windowId %{public}u", node->GetWindowId()); 222 return AvoidPosType::AVOID_POS_UNKNOWN; 223 } 224 const Rect rect = node->GetWindowRect(); 225 overlayRect = WindowHelper::GetOverlap(overlayNode->GetWindowRect(), rect, rect.posX_, rect.posY_); 226 return GetAvoidPosType(rect, overlayRect); 227} 228 229AvoidPosType AvoidAreaController::GetAvoidPosType(const Rect& windowRect, const Rect& overlayRect) const 230{ 231 if (windowRect.width_ == 0 || windowRect.height_ == 0) { 232 return AvoidPosType::AVOID_POS_UNKNOWN; 233 } 234 uint32_t centerX = overlayRect.posX_ + (overlayRect.width_ >> 1); 235 uint32_t centerY = overlayRect.posY_ + (overlayRect.height_ >> 1); 236 float res1 = float(centerY) - float(windowRect.height_) / float(windowRect.width_) * float(centerX); 237 float res2 = float(centerY) + float(windowRect.height_) / float(windowRect.width_) * float(centerX) - 238 float(windowRect.height_); 239 if (res1 < 0) { 240 if (res2 < 0) { 241 return AvoidPosType::AVOID_POS_TOP; 242 } 243 return AvoidPosType::AVOID_POS_RIGHT; 244 } 245 if (res2 < 0) { 246 return AvoidPosType::AVOID_POS_LEFT; 247 } 248 return AvoidPosType::AVOID_POS_BOTTOM; 249} 250 251void AvoidAreaController::SetAvoidAreaRect(AvoidArea& avoidArea, const Rect& rect, AvoidPosType type) const 252{ 253 switch (type) { 254 case AvoidPosType::AVOID_POS_TOP : { 255 avoidArea.topRect_ = rect; 256 break; 257 } 258 case AvoidPosType::AVOID_POS_LEFT : { 259 avoidArea.leftRect_ = rect; 260 break; 261 } 262 case AvoidPosType::AVOID_POS_RIGHT : { 263 avoidArea.rightRect_ = rect; 264 break; 265 } 266 case AvoidPosType::AVOID_POS_BOTTOM : { 267 avoidArea.bottomRect_ = rect; 268 break; 269 } 270 default : { 271 WLOGFD("default type: %{public}u", type); 272 } 273 } 274} 275 276AvoidArea AvoidAreaController::GetAvoidAreaByType(const sptr<WindowNode>& node, AvoidAreaType avoidAreaType) const 277{ 278 WLOGFD("avoidAreaType: %{public}u", avoidAreaType); 279 if (node == nullptr) { 280 WLOGFE("invalid WindowNode."); 281 return {}; 282 } 283 WindowMode windowMode = node->GetWindowMode(); 284 if (avoidAreaType != AvoidAreaType::TYPE_KEYBOARD && 285 windowMode != WindowMode::WINDOW_MODE_FULLSCREEN && 286 windowMode != WindowMode::WINDOW_MODE_SPLIT_PRIMARY && 287 windowMode != WindowMode::WINDOW_MODE_SPLIT_SECONDARY) { 288 WLOGI("avoidAreaType: %{public}u, windowMode: %{public}u, return default avoid area.", 289 avoidAreaType, windowMode); 290 return {}; 291 } 292 switch (avoidAreaType) { 293 case AvoidAreaType::TYPE_SYSTEM : { 294 return GetAvoidAreaSystemType(node); 295 } 296 case AvoidAreaType::TYPE_KEYBOARD : { 297 return GetAvoidAreaKeyboardType(node); 298 } 299 case AvoidAreaType::TYPE_CUTOUT : { 300 sptr<CutoutInfo> cutoutInfo = DisplayManagerServiceInner::GetInstance().GetCutoutInfo(node->GetDisplayId()); 301 if (cutoutInfo == nullptr) { 302 WLOGFE("there is no cutoutInfo"); 303 return {}; 304 } 305 std::vector<DMRect> cutoutAreas = cutoutInfo->GetBoundingRects(); 306 if (cutoutAreas.empty()) { 307 WLOGFE("there is no cutout"); 308 return {}; 309 } 310 // 0 means the index in the vector. 311 Rect cutoutAreaRect { cutoutAreas[0].posX_, cutoutAreas[0].posY_, 312 cutoutAreas[0].width_, cutoutAreas[0].height_ }; 313 auto rect = node->GetWindowRect(); 314 Rect overlayRect = WindowHelper::GetOverlap(cutoutAreaRect, rect, rect.posX_, rect.posY_); 315 auto type = GetAvoidPosType(rect, overlayRect); 316 AvoidArea avoidArea; 317 SetAvoidAreaRect(avoidArea, overlayRect, type); 318 return avoidArea; 319 } 320 default : { 321 WLOGFD("cannot find avoidAreaType: %{public}u", avoidAreaType); 322 return {}; 323 } 324 } 325} 326 327AvoidArea AvoidAreaController::GetAvoidAreaSystemType(const sptr<WindowNode>& node) const 328{ 329 AvoidArea systemAvoidArea; 330 Rect statusBarAvoidArea; 331 AvoidPosType statusBarAvoidPosType = AvoidPosType::AVOID_POS_UNKNOWN; 332 Rect navigationBarAvoidArea; 333 AvoidPosType navigationBarAvoidPosType = AvoidPosType::AVOID_POS_UNKNOWN; 334 for (auto& iter : overlayWindowMap_) { 335 if (iter.second != nullptr) { 336 if (iter.second->GetWindowType() == WindowType::WINDOW_TYPE_STATUS_BAR) { 337 statusBarAvoidPosType = CalculateOverlayRect(node, iter.second, statusBarAvoidArea); 338 } 339 if (iter.second->GetWindowType() == WindowType::WINDOW_TYPE_NAVIGATION_BAR) { 340 navigationBarAvoidPosType = CalculateOverlayRect(node, iter.second, navigationBarAvoidArea); 341 } 342 } 343 } 344 SetAvoidAreaRect(systemAvoidArea, statusBarAvoidArea, statusBarAvoidPosType); 345 SetAvoidAreaRect(systemAvoidArea, navigationBarAvoidArea, navigationBarAvoidPosType); 346 return systemAvoidArea; 347} 348 349AvoidArea AvoidAreaController::GetAvoidAreaKeyboardType(const sptr<WindowNode>& node) const 350{ 351 for (auto& iter : overlayWindowMap_) { 352 if (iter.second != nullptr && 353 iter.second->GetWindowType() == WindowType::WINDOW_TYPE_INPUT_METHOD_FLOAT) { 354 const uint32_t callingWindowId = iter.second->GetCallingWindow(); 355 if (callingWindowId != node->GetWindowId() && focusedWindow_ != node->GetWindowId()) { 356 WLOGI("windowId: %{public}u is not focusedWindow: %{public}u or callingWindow: %{public}u", 357 node->GetWindowId(), focusedWindow_, callingWindowId); 358 continue; 359 } 360 Rect avoidAreaRect { 0, 0, 0, 0 }; 361 AvoidPosType avoidPosType = CalculateOverlayRect(node, iter.second, avoidAreaRect); 362 AvoidArea avoidArea; 363 SetAvoidAreaRect(avoidArea, avoidAreaRect, avoidPosType); 364 return avoidArea; 365 } 366 } 367 return {}; 368} 369} 370} 371