1 /*
2 * Copyright (c) 2025 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 "core/components_ng/manager/avoid_info/avoid_info_manager.h"
17
18 #include "core/components_ng/base/frame_node.h"
19 #include "core/components_ng/pattern/container_modal/enhance/container_modal_view_enhance.h"
20 #include "core/components_ng/pattern/navigation/navigation_pattern.h"
21
22 namespace OHOS::Ace::NG {
23
ToString() const24 std::string ContainerModalAvoidInfo::ToString() const
25 {
26 std::string str("needAvoid: ");
27 str.append(needAvoid ? "true" : "false");
28 str.append(", titleHeight: ");
29 str.append(std::to_string(titleHeight));
30 str.append(", rect: ");
31 str.append(controlBottonsRect.ToString());
32 return str;
33 }
34
RegisterListenerIfNeeded()35 void AvoidInfoManager::RegisterListenerIfNeeded()
36 {
37 if (hasRegisterListener_) {
38 return;
39 }
40
41 auto pipeline = pipeline_.Upgrade();
42 CHECK_NULL_VOID(pipeline);
43 auto container = Container::GetContainer(instanceId_);
44 CHECK_NULL_VOID(container);
45 if (!container->IsUIExtensionWindow()) {
46 auto containerModalListener =
47 [weakMgr = WeakClaim(this)](const RectF&, const RectF&) {
48 auto mgr = weakMgr.Upgrade();
49 CHECK_NULL_VOID(mgr);
50 mgr->OnContainerModalInfoChange();
51 };
52 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "AvoidInfoManager register listener");
53 containerModalListenerId_ = ContainerModalViewEnhance::AddButtonsRectChangeListener(
54 AceType::RawPtr(pipeline), std::move(containerModalListener));
55 // init avoid info
56 ContainerModalAvoidInfo info;
57 GetContainerModalAvoidInfo(info);
58 SetAvoidInfo(info);
59 hasRegisterListener_ = true;
60 return;
61 }
62
63 if (!registerUECConsumerCallback_ || !requestAvoidInfoCallback_) {
64 TAG_LOGE(AceLogTag::ACE_NAVIGATION, "AvoidInfoManager did not set register callback.");
65 return;
66 }
67
68 auto consumer = [weakMgr = WeakClaim(this), instanceId = instanceId_](const AAFwk::Want& data) -> int32_t {
69 ContainerScope scope(instanceId);
70 auto mgr = weakMgr.Upgrade();
71 CHECK_NULL_RETURN(mgr, 0);
72 return mgr->OnUECAvoidInfoReceived(data);
73 };
74 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "AvoidInfoManager register listener for UEC");
75 registerUECConsumerCallback_(std::move(consumer));
76 // request avoid info for first time.
77 requestAvoidInfoCallback_();
78 hasRegisterListener_ = true;
79 }
80
OnContainerModalInfoChange()81 void AvoidInfoManager::OnContainerModalInfoChange()
82 {
83 ContainerModalAvoidInfo newInfo;
84 GetContainerModalAvoidInfo(newInfo);
85 bool needNotify = CheckIfNeedNotifyAvoidInfoChange(avoidInfo_, newInfo);
86 SetAvoidInfo(newInfo);
87 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "AvoidInfo change to: %{public}s", newInfo.ToString().c_str());
88 if (needNotify) {
89 OnAvoidInfoChange(newInfo);
90 }
91 }
92
OnUECAvoidInfoReceived(const AAFwk::Want & data)93 int32_t AvoidInfoManager::OnUECAvoidInfoReceived(const AAFwk::Want& data)
94 {
95 ContainerModalAvoidInfo info;
96 if (!ParseAvoidInfo(data, info)) {
97 return -1;
98 }
99 SetAvoidInfoForUEC(info);
100 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "[UEC]AvoidInfo change to: %{public}s", info.ToString().c_str());
101 OnAvoidInfoChange(info);
102 return 0;
103 }
104
UnregisterListenerIfNeeded()105 void AvoidInfoManager::UnregisterListenerIfNeeded()
106 {
107 if (!hasRegisterListener_) {
108 return;
109 }
110
111 auto container = Container::GetContainer(instanceId_);
112 CHECK_NULL_VOID(container);
113 if (container->IsUIExtensionWindow()) {
114 hasRegisterListener_ = false;
115 return;
116 }
117
118 auto pipeline = pipeline_.Upgrade();
119 CHECK_NULL_VOID(pipeline);
120 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "AvoidInfoManager unregister listener");
121 ContainerModalViewEnhance::RemoveButtonsRectChangeListener(
122 AceType::RawPtr(pipeline), containerModalListenerId_);
123 hasRegisterListener_ = false;
124 }
125
GetContainerModalTitleHeight()126 int32_t AvoidInfoManager::GetContainerModalTitleHeight()
127 {
128 auto container = Container::GetContainer(instanceId_);
129 CHECK_NULL_RETURN(container, 0);
130 if (container->IsUIExtensionWindow()) {
131 return avoidInfoForUEC_.titleHeight;
132 }
133 return avoidInfo_.titleHeight;
134 }
135
GetContainerModalButtonsRect(RectF & containerModal,RectF & buttonsRect)136 bool AvoidInfoManager::GetContainerModalButtonsRect(RectF& containerModal, RectF& buttonsRect)
137 {
138 auto container = Container::GetContainer(instanceId_);
139 CHECK_NULL_RETURN(container, false);
140 RectF* rect = nullptr;
141 if (container->IsUIExtensionWindow()) {
142 rect = &avoidInfoForUEC_.controlBottonsRect;
143 } else {
144 rect = &avoidInfo_.controlBottonsRect;
145 }
146 if (rect->IsValid()) {
147 buttonsRect = *rect;
148 return true;
149 }
150 return false;
151 }
152
NeedAvoidContainerModal()153 bool AvoidInfoManager::NeedAvoidContainerModal()
154 {
155 auto container = Container::GetContainer(instanceId_);
156 CHECK_NULL_RETURN(container, false);
157 if (container->IsUIExtensionWindow()) {
158 return avoidInfoForUEC_.needAvoid;
159 }
160 return avoidInfo_.needAvoid;
161 }
162
OnAvoidInfoChange(const ContainerModalAvoidInfo & info)163 void AvoidInfoManager::OnAvoidInfoChange(const ContainerModalAvoidInfo& info)
164 {
165 for (auto& weak : listeners_) {
166 auto listener = weak.Upgrade();
167 if (listener) {
168 listener->OnAvoidInfoChange(info);
169 }
170 }
171 }
172
AddAvoidInfoListener(WeakPtr<IAvoidInfoListener> listener)173 void AvoidInfoManager::AddAvoidInfoListener(WeakPtr<IAvoidInfoListener> listener)
174 {
175 RegisterListenerIfNeeded();
176 listeners_.emplace(listener);
177 }
178
RemoveAvoidInfoListener(WeakPtr<IAvoidInfoListener> listener)179 void AvoidInfoManager::RemoveAvoidInfoListener(WeakPtr<IAvoidInfoListener> listener)
180 {
181 auto it = listeners_.find(listener);
182 if (it != listeners_.end()) {
183 listeners_.erase(it);
184 }
185 if (listeners_.empty()) {
186 UnregisterListenerIfNeeded();
187 }
188 }
189
GetContainerModalAvoidInfoForUEC(const RefPtr<FrameNode> & uecNode,ContainerModalAvoidInfo & info)190 void AvoidInfoManager::GetContainerModalAvoidInfoForUEC(const RefPtr<FrameNode>& uecNode, ContainerModalAvoidInfo& info)
191 {
192 info.needAvoid = false;
193 CHECK_NULL_VOID(uecNode);
194 auto context = uecNode->GetContext();
195 CHECK_NULL_VOID(context);
196 if (context->GetContainerCustomTitleVisible() || !context->GetContainerControlButtonVisible()) {
197 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "uec don't need avoid control button");
198 return;
199 }
200 auto globalOffset = uecNode->GetPaintRectOffsetNG();
201 auto uecGeometryNode = uecNode->GetGeometryNode();
202 CHECK_NULL_VOID(uecGeometryNode);
203 auto uecRect = uecGeometryNode->GetFrameRect();
204 uecRect.SetOffset(globalOffset);
205 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "uec rect: %{public}s", uecRect.ToString().c_str());
206 RectF buttonsRect;
207 RectF containerModal;
208 if (!context->GetContainerModalButtonsRect(containerModal, buttonsRect)) {
209 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "failed to get buttonRect");
210 return;
211 }
212 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "control button rect: %{public}s", buttonsRect.ToString().c_str());
213 auto height = context->GetContainerModalTitleHeight();
214 if (height <= 0) {
215 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "failed to get titleHeight");
216 return;
217 }
218 if (!uecRect.IsIntersectWith(buttonsRect)) {
219 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "UEC isn't intersect with control button");
220 return;
221 }
222 info.needAvoid = true;
223 info.titleHeight = height;
224 info.controlBottonsRect = uecRect.IntersectRectT(buttonsRect);
225 info.controlBottonsRect -= globalOffset;
226 }
227
CheckIfNeedNotifyAvoidInfoChange(const ContainerModalAvoidInfo & preInfo,const ContainerModalAvoidInfo & curInfo)228 bool AvoidInfoManager::CheckIfNeedNotifyAvoidInfoChange(
229 const ContainerModalAvoidInfo& preInfo, const ContainerModalAvoidInfo& curInfo)
230 {
231 return (curInfo.needAvoid != preInfo.needAvoid) ||
232 (curInfo.needAvoid && (curInfo.titleHeight != preInfo.titleHeight ||
233 curInfo.controlBottonsRect != preInfo.controlBottonsRect));
234 }
235
GetContainerModalAvoidInfo(ContainerModalAvoidInfo & info)236 void AvoidInfoManager::GetContainerModalAvoidInfo(ContainerModalAvoidInfo& info)
237 {
238 info.needAvoid = false;
239 auto context = PipelineContext::GetContextByContainerId(instanceId_);
240 CHECK_NULL_VOID(context);
241 if (context->GetContainerCustomTitleVisible() || !context->GetContainerControlButtonVisible()) {
242 return;
243 }
244 RectF buttonsRect;
245 RectF containerModal;
246 if (!context->GetContainerModalButtonsRect(containerModal, buttonsRect)) {
247 return;
248 }
249 auto height = context->GetContainerModalTitleHeight();
250 if (height <= 0) {
251 return;
252 }
253 info.needAvoid = true;
254 info.titleHeight = height;
255 info.controlBottonsRect = buttonsRect;
256 }
257 } // namespace OHOS::Ace::NG
258