/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "avoid_area_controller.h" #include #include "display_manager_service_inner.h" #include "window_helper.h" #include "window_manager_hilog.h" namespace OHOS { namespace Rosen { namespace { constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "AvoidAreaController"}; } void AvoidAreaController::UpdateAvoidAreaListener(sptr& windowNode, bool isRegisterListener) { WLOGFE("haveAvoidAreaListener %{public}d", isRegisterListener); if (windowNode == nullptr) { WLOGFE("windowNode is nullptr."); return; } if (isRegisterListener) { avoidAreaListenerNodes_.insert(windowNode); } else { lastUpdatedAvoidArea_.erase(windowNode->GetWindowId()); avoidAreaListenerNodes_.erase(windowNode); } } void AvoidAreaController::ProcessWindowChange(const sptr& windowNode, AvoidControlType avoidType, const std::function)>& checkFunc) { if (isForbidProcessingWindowChange_) { WLOGI("do not process window change."); return; } if (windowNode == nullptr || windowNode->GetWindowToken() == nullptr) { WLOGE("invalid WindowNode."); return; } switch (avoidType) { case AvoidControlType::AVOID_NODE_ADD: case AvoidControlType::AVOID_NODE_REMOVE: AddOrRemoveOverlayWindowIfNeed(windowNode, avoidType == AvoidControlType::AVOID_NODE_ADD); break; case AvoidControlType::AVOID_NODE_UPDATE: UpdateOverlayWindowIfNeed(windowNode, checkFunc); break; default: break; } } void AvoidAreaController::AddOrRemoveOverlayWindowIfNeed(const sptr& overlayNode, bool isAdding) { WindowGravity windowGravity; uint32_t percent; overlayNode->GetWindowGravity(windowGravity, percent); if (!WindowHelper::IsOverlayWindow(overlayNode->GetWindowType()) || windowGravity == WindowGravity::WINDOW_GRAVITY_FLOAT) { WLOGE("IsOverlayWindow Failed."); return; } HITRACE_METER(HITRACE_TAG_WINDOW_MANAGER); uint32_t overlayId = overlayNode->GetWindowId(); bool isRecorded = (overlayWindowMap_.find(overlayId) != overlayWindowMap_.end()); if (isAdding == isRecorded) { WLOGE("error occured in overlay. overlayId %{public}u isAdding %{public}d record flag %{public}d", overlayId, isAdding, isRecorded); return; } if (isAdding) { overlayWindowMap_.insert(std::make_pair(overlayId, overlayNode)); } else { overlayWindowMap_.erase(overlayId); } if (overlayNode->GetWindowType() == WindowType::WINDOW_TYPE_INPUT_METHOD_FLOAT) { AddOrRemoveKeyboard(overlayNode, isAdding); return; } for (auto& node : avoidAreaListenerNodes_) { AvoidArea systemAvoidArea = GetAvoidAreaByType(node, AvoidAreaType::TYPE_SYSTEM); UpdateAvoidAreaIfNeed(systemAvoidArea, node, AvoidAreaType::TYPE_SYSTEM); } } void AvoidAreaController::AddOrRemoveKeyboard(const sptr& keyboardNode, bool isAdding) { const uint32_t callingWindowId = keyboardNode->GetCallingWindow(); sptr callingWindow = nullptr; sptr focusWindow = nullptr; sptr lastKeyboardAreaUpdatedWindow = nullptr; for (auto window : avoidAreaListenerNodes_) { if (window == nullptr || window->GetWindowToken() == nullptr) { continue; } if (window->GetWindowId() == callingWindowId) { callingWindow = window; } if (window->GetWindowId() == focusedWindow_) { focusWindow = window; } if (window->GetWindowId() == lastSoftInputKeyboardAreaUpdatedWindowId_) { lastKeyboardAreaUpdatedWindow = window; } } if (callingWindow == nullptr) { callingWindow = focusWindow; } if (lastKeyboardAreaUpdatedWindow != nullptr && lastKeyboardAreaUpdatedWindow != callingWindow) { const WindowMode windowMode = lastKeyboardAreaUpdatedWindow->GetWindowMode(); if (windowMode == WindowMode::WINDOW_MODE_FULLSCREEN || windowMode == WindowMode::WINDOW_MODE_SPLIT_PRIMARY || windowMode == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) { auto avoidArea = GetAvoidAreaByType(lastKeyboardAreaUpdatedWindow, AvoidAreaType::TYPE_KEYBOARD); UpdateAvoidAreaIfNeed(avoidArea, lastKeyboardAreaUpdatedWindow, AvoidAreaType::TYPE_KEYBOARD); } } if (callingWindow == nullptr) { WLOGFE("callingWindow: %{public}u is nullptr, focusWindow: %{public}u is nullptr.", callingWindowId, focusedWindow_); return; } const WindowMode callingWindowMode = callingWindow->GetWindowMode(); if (callingWindowMode == WindowMode::WINDOW_MODE_FULLSCREEN || callingWindowMode == WindowMode::WINDOW_MODE_SPLIT_PRIMARY || callingWindowMode == WindowMode::WINDOW_MODE_SPLIT_SECONDARY) { auto avoidArea = GetAvoidAreaByType(callingWindow, AvoidAreaType::TYPE_KEYBOARD); bool res = UpdateAvoidAreaIfNeed(avoidArea, callingWindow, AvoidAreaType::TYPE_KEYBOARD); if (res) { lastSoftInputKeyboardAreaUpdatedWindowId_ = callingWindow->GetWindowId(); } return; } WLOGFE("does not have correct callingWindowMode for input method window"); } void AvoidAreaController::UpdateOverlayWindowIfNeed(const sptr& node, const std::function)>& checkFunc) { HITRACE_METER(HITRACE_TAG_WINDOW_MANAGER); WindowGravity windowGravity; uint32_t percent; node->GetWindowGravity(windowGravity, percent); if (WindowHelper::IsOverlayWindow(node->GetWindowType()) && windowGravity == WindowGravity::WINDOW_GRAVITY_BOTTOM) { AvoidAreaType type = WindowHelper::IsSystemBarWindow(node->GetWindowType()) ? AvoidAreaType::TYPE_SYSTEM : AvoidAreaType::TYPE_KEYBOARD; for (auto& appNode : avoidAreaListenerNodes_) { if (checkFunc != nullptr && checkFunc(appNode)) { bool res = UpdateAvoidAreaIfNeed(GetAvoidAreaByType(appNode, type), appNode, type); if (type == AvoidAreaType::TYPE_KEYBOARD && res) { lastSoftInputKeyboardAreaUpdatedWindowId_ = appNode->GetWindowId(); } } } } else { if (avoidAreaListenerNodes_.find(node) == avoidAreaListenerNodes_.end()) { WLOGD("window: %{public}u is not in avoidAreaListenerNodes, don't update avoid area.", node->GetWindowId()); return; } uint32_t start = static_cast(AvoidAreaType::TYPE_SYSTEM); uint32_t end = static_cast(AvoidAreaType::TYPE_KEYBOARD); for (uint32_t type = start; type <= end; type++) { AvoidArea systemAvoidArea = GetAvoidAreaByType(node, static_cast(type)); bool res = UpdateAvoidAreaIfNeed(systemAvoidArea, node, static_cast(type)); if (res && type == static_cast(AvoidAreaType::TYPE_KEYBOARD)) { lastSoftInputKeyboardAreaUpdatedWindowId_ = node->GetWindowId(); } } } } bool AvoidAreaController::UpdateAvoidAreaIfNeed(const AvoidArea& avoidArea, const sptr& node, AvoidAreaType avoidAreaType) { if (!node->currentVisibility_) { WLOGFD("window: %{public}u is not visible, don't update avoid area", node->GetWindowId()); return false; } auto iter = lastUpdatedAvoidArea_.find(node->GetWindowId()); bool needUpdate = true; if (iter != lastUpdatedAvoidArea_.end()) { auto avoidAreaIter = iter->second.find(avoidAreaType); if (avoidAreaIter != iter->second.end()) { needUpdate = avoidAreaIter->second != avoidArea; } else { if (avoidArea.isEmptyAvoidArea()) { needUpdate = false; } } } else { if (avoidArea.isEmptyAvoidArea()) { needUpdate = false; } } if (needUpdate) { lastUpdatedAvoidArea_[node->GetWindowId()][avoidAreaType] = avoidArea; node->GetWindowToken()->UpdateAvoidArea(new AvoidArea(avoidArea), avoidAreaType); } return needUpdate; } AvoidPosType AvoidAreaController::CalculateOverlayRect(const sptr& node, const sptr& overlayNode, Rect& overlayRect) const { if (node->GetWindowId() == overlayNode->GetWindowId()) { WLOGE("overlay not support self. windowId %{public}u", node->GetWindowId()); return AvoidPosType::AVOID_POS_UNKNOWN; } const Rect rect = node->GetWindowRect(); overlayRect = WindowHelper::GetOverlap(overlayNode->GetWindowRect(), rect, rect.posX_, rect.posY_); return GetAvoidPosType(rect, overlayRect); } AvoidPosType AvoidAreaController::GetAvoidPosType(const Rect& windowRect, const Rect& overlayRect) const { if (windowRect.width_ == 0 || windowRect.height_ == 0) { return AvoidPosType::AVOID_POS_UNKNOWN; } uint32_t centerX = overlayRect.posX_ + (overlayRect.width_ >> 1); uint32_t centerY = overlayRect.posY_ + (overlayRect.height_ >> 1); float res1 = float(centerY) - float(windowRect.height_) / float(windowRect.width_) * float(centerX); float res2 = float(centerY) + float(windowRect.height_) / float(windowRect.width_) * float(centerX) - float(windowRect.height_); if (res1 < 0) { if (res2 < 0) { return AvoidPosType::AVOID_POS_TOP; } return AvoidPosType::AVOID_POS_RIGHT; } if (res2 < 0) { return AvoidPosType::AVOID_POS_LEFT; } return AvoidPosType::AVOID_POS_BOTTOM; } void AvoidAreaController::SetAvoidAreaRect(AvoidArea& avoidArea, const Rect& rect, AvoidPosType type) const { switch (type) { case AvoidPosType::AVOID_POS_TOP : { avoidArea.topRect_ = rect; break; } case AvoidPosType::AVOID_POS_LEFT : { avoidArea.leftRect_ = rect; break; } case AvoidPosType::AVOID_POS_RIGHT : { avoidArea.rightRect_ = rect; break; } case AvoidPosType::AVOID_POS_BOTTOM : { avoidArea.bottomRect_ = rect; break; } default : { WLOGFD("default type: %{public}u", type); } } } AvoidArea AvoidAreaController::GetAvoidAreaByType(const sptr& node, AvoidAreaType avoidAreaType) const { WLOGFD("avoidAreaType: %{public}u", avoidAreaType); if (node == nullptr) { WLOGFE("invalid WindowNode."); return {}; } WindowMode windowMode = node->GetWindowMode(); if (avoidAreaType != AvoidAreaType::TYPE_KEYBOARD && windowMode != WindowMode::WINDOW_MODE_FULLSCREEN && windowMode != WindowMode::WINDOW_MODE_SPLIT_PRIMARY && windowMode != WindowMode::WINDOW_MODE_SPLIT_SECONDARY) { WLOGI("avoidAreaType: %{public}u, windowMode: %{public}u, return default avoid area.", avoidAreaType, windowMode); return {}; } switch (avoidAreaType) { case AvoidAreaType::TYPE_SYSTEM : { return GetAvoidAreaSystemType(node); } case AvoidAreaType::TYPE_KEYBOARD : { return GetAvoidAreaKeyboardType(node); } case AvoidAreaType::TYPE_CUTOUT : { sptr cutoutInfo = DisplayManagerServiceInner::GetInstance().GetCutoutInfo(node->GetDisplayId()); if (cutoutInfo == nullptr) { WLOGFE("there is no cutoutInfo"); return {}; } std::vector cutoutAreas = cutoutInfo->GetBoundingRects(); if (cutoutAreas.empty()) { WLOGFE("there is no cutout"); return {}; } // 0 means the index in the vector. Rect cutoutAreaRect { cutoutAreas[0].posX_, cutoutAreas[0].posY_, cutoutAreas[0].width_, cutoutAreas[0].height_ }; auto rect = node->GetWindowRect(); Rect overlayRect = WindowHelper::GetOverlap(cutoutAreaRect, rect, rect.posX_, rect.posY_); auto type = GetAvoidPosType(rect, overlayRect); AvoidArea avoidArea; SetAvoidAreaRect(avoidArea, overlayRect, type); return avoidArea; } default : { WLOGFD("cannot find avoidAreaType: %{public}u", avoidAreaType); return {}; } } } AvoidArea AvoidAreaController::GetAvoidAreaSystemType(const sptr& node) const { AvoidArea systemAvoidArea; Rect statusBarAvoidArea; AvoidPosType statusBarAvoidPosType = AvoidPosType::AVOID_POS_UNKNOWN; Rect navigationBarAvoidArea; AvoidPosType navigationBarAvoidPosType = AvoidPosType::AVOID_POS_UNKNOWN; for (auto& iter : overlayWindowMap_) { if (iter.second != nullptr) { if (iter.second->GetWindowType() == WindowType::WINDOW_TYPE_STATUS_BAR) { statusBarAvoidPosType = CalculateOverlayRect(node, iter.second, statusBarAvoidArea); } if (iter.second->GetWindowType() == WindowType::WINDOW_TYPE_NAVIGATION_BAR) { navigationBarAvoidPosType = CalculateOverlayRect(node, iter.second, navigationBarAvoidArea); } } } SetAvoidAreaRect(systemAvoidArea, statusBarAvoidArea, statusBarAvoidPosType); SetAvoidAreaRect(systemAvoidArea, navigationBarAvoidArea, navigationBarAvoidPosType); return systemAvoidArea; } AvoidArea AvoidAreaController::GetAvoidAreaKeyboardType(const sptr& node) const { for (auto& iter : overlayWindowMap_) { if (iter.second != nullptr && iter.second->GetWindowType() == WindowType::WINDOW_TYPE_INPUT_METHOD_FLOAT) { const uint32_t callingWindowId = iter.second->GetCallingWindow(); if (callingWindowId != node->GetWindowId() && focusedWindow_ != node->GetWindowId()) { WLOGI("windowId: %{public}u is not focusedWindow: %{public}u or callingWindow: %{public}u", node->GetWindowId(), focusedWindow_, callingWindowId); continue; } Rect avoidAreaRect { 0, 0, 0, 0 }; AvoidPosType avoidPosType = CalculateOverlayRect(node, iter.second, avoidAreaRect); AvoidArea avoidArea; SetAvoidAreaRect(avoidArea, avoidAreaRect, avoidPosType); return avoidArea; } } return {}; } } }