1 /*
2 * Copyright (c) 2024 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/navigation/navigation_manager.h"
17
18 #include "base/log/dump_log.h"
19 #include "core/components_ng/pattern/container_modal/enhance/container_modal_view_enhance.h"
20 #include "core/components_ng/pattern/dialog/dialog_pattern.h"
21 #include "core/components_ng/pattern/navigation/navigation_pattern.h"
22 #include "core/components_ng/pattern/overlay/sheet_presentation_pattern.h"
23
24 namespace OHOS::Ace::NG {
25 constexpr int32_t INDENT_SIZE = 2;
26
AddNavigationDumpCallback(int32_t nodeId,int32_t depth,const DumpCallback & callback)27 void NavigationManager::AddNavigationDumpCallback(int32_t nodeId, int32_t depth, const DumpCallback& callback)
28 {
29 CHECK_RUN_ON(UI);
30 dumpMap_.emplace(DumpMapKey(nodeId, depth), callback);
31 }
32
RemoveNavigationDumpCallback(int32_t nodeId,int32_t depth)33 void NavigationManager::RemoveNavigationDumpCallback(int32_t nodeId, int32_t depth)
34 {
35 CHECK_RUN_ON(UI);
36 auto it = dumpMap_.find(DumpMapKey(nodeId, depth));
37 if (it != dumpMap_.end()) {
38 dumpMap_.erase(it);
39 }
40 }
41
OnDumpInfo()42 void NavigationManager::OnDumpInfo()
43 {
44 CHECK_RUN_ON(UI);
45 auto pipeline = PipelineContext::GetCurrentContext();
46 CHECK_NULL_VOID(pipeline);
47 auto rootNode = pipeline->GetRootElement();
48 if (!rootNode) {
49 TAG_LOGE(AceLogTag::ACE_NAVIGATION, "navigation dump failed, invalid root node");
50 return;
51 }
52 DumpLog::GetInstance().Print("Navigation number: " + std::to_string(dumpMap_.size()));
53 std::stack<std::pair<RefPtr<UINode>, int32_t>> stack;
54 stack.push({ rootNode, 0 });
55 while (!stack.empty()) {
56 auto [curNode, curDepth] = stack.top();
57 stack.pop();
58 std::string space(INDENT_SIZE * curDepth, ' ');
59 int32_t depth = 0;
60 if (curNode->GetTag() == V2::NAVIGATION_VIEW_ETS_TAG) {
61 auto navigation = AceType::DynamicCast<NavigationGroupNode>(curNode);
62 CHECK_NULL_VOID(navigation);
63 DumpLog::GetInstance().Print(space + navigation->ToDumpString());
64 depth++;
65 } else if (curNode->GetTag() == V2::NAVDESTINATION_VIEW_ETS_TAG) {
66 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(curNode);
67 CHECK_NULL_VOID(navDestination);
68 DumpLog::GetInstance().Print(space + navDestination->ToDumpString());
69 depth++;
70 }
71 const auto& children = curNode->GetChildren();
72 for (auto it = children.rbegin(); it != children.rend(); it++) {
73 if (!(*it)) {
74 continue;
75 }
76 stack.push({ *it, curDepth + depth });
77 }
78 }
79 }
80
FireNavigationUpdateCallback()81 void NavigationManager::FireNavigationUpdateCallback()
82 {
83 for (const auto& func : updateCallbacks_) {
84 func();
85 }
86 updateCallbacks_.clear();
87 }
88
GetNavigationInfo(const RefPtr<AceType> & node)89 std::shared_ptr<NavigationInfo> NavigationManager::GetNavigationInfo(const RefPtr<AceType>& node)
90 {
91 RefPtr<UINode> current = nullptr;
92 auto customNode = AceType::DynamicCast<CustomNode>(node);
93 if (customNode) {
94 current = customNode->GetNavigationNode().Upgrade();
95 }
96
97 if (!current) {
98 current = AceType::DynamicCast<UINode>(node);
99 while (current) {
100 if (current->GetTag() == V2::NAVIGATION_VIEW_ETS_TAG) {
101 break;
102 }
103 current = current->GetParent();
104 }
105 }
106
107 if (!current) {
108 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "find parent navigation node failed");
109 return nullptr;
110 }
111
112 auto navigation = AceType::DynamicCast<NavigationGroupNode>(current);
113 CHECK_NULL_RETURN(navigation, nullptr);
114 auto pattern = navigation->GetPattern<NavigationPattern>();
115 CHECK_NULL_RETURN(pattern, nullptr);
116 auto stack = pattern->GetNavigationStack();
117 CHECK_NULL_RETURN(stack, nullptr);
118 return std::make_shared<NavigationInfo>(navigation->GetInspectorId().value_or(""), stack);
119 }
120
AddInteractiveAnimation(const std::function<void ()> & addCallback)121 bool NavigationManager::AddInteractiveAnimation(const std::function<void()>& addCallback)
122 {
123 if (!isInteractive_) {
124 return false;
125 }
126 auto navigationGroupNode = AceType::DynamicCast<NavigationGroupNode>(
127 FrameNode::GetFrameNode(V2::NAVIGATION_VIEW_ETS_TAG, interactiveAnimationId_));
128 CHECK_NULL_RETURN(navigationGroupNode, false);
129 auto pattern = navigationGroupNode->GetPattern<NavigationPattern>();
130 CHECK_NULL_RETURN(pattern, false);
131 auto proxy = pattern->GetTopNavigationProxy();
132 CHECK_NULL_RETURN(proxy, false);
133 proxy->AddInteractiveAnimation(addCallback);
134 return true;
135 }
136
CheckNodeNeedCache(const RefPtr<FrameNode> & node)137 bool NavigationManager::CheckNodeNeedCache(const RefPtr<FrameNode>& node)
138 {
139 CHECK_NULL_RETURN(node, false);
140
141 auto context = node->GetRenderContext();
142 if ((context && context->GetAnimationsCount() != 0) || node->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG) {
143 return false;
144 }
145 std::stack<RefPtr<FrameNode>> nodeStack;
146 nodeStack.push(node);
147 while (!nodeStack.empty()) {
148 auto curNode = nodeStack.top();
149 nodeStack.pop();
150 std::list<RefPtr<FrameNode>> children;
151 curNode->GenerateOneDepthVisibleFrameWithTransition(children);
152 for (auto& child : children) {
153 if (!child) {
154 continue;
155 }
156 auto childContext = child->GetRenderContext();
157 if ((childContext && childContext->GetAnimationsCount() != 0) ||
158 child->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG) {
159 return false;
160 }
161 nodeStack.push(child);
162 }
163 auto overlayNode = curNode->GetOverlayNode();
164 if (overlayNode) {
165 auto overlayNodeContext = overlayNode->GetRenderContext();
166 if ((overlayNodeContext && overlayNodeContext->GetAnimationsCount() != 0) ||
167 overlayNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG) {
168 return false;
169 }
170 nodeStack.push(overlayNode);
171 }
172 }
173 return true;
174 }
175
GetNavDestContentFrameNode(const RefPtr<FrameNode> & node)176 RefPtr<FrameNode> NavigationManager::GetNavDestContentFrameNode(const RefPtr<FrameNode>& node)
177 {
178 CHECK_NULL_RETURN(node, nullptr);
179 auto navDestinationNodeBase = AceType::DynamicCast<NavDestinationNodeBase>(node);
180 CHECK_NULL_RETURN(navDestinationNodeBase, nullptr);
181 auto navDestContentFrameNode = AceType::DynamicCast<FrameNode>(navDestinationNodeBase->GetContentNode());
182 CHECK_NULL_RETURN(navDestContentFrameNode, nullptr);
183 return navDestContentFrameNode;
184 }
185
UpdatePreNavNodeRenderGroupProperty()186 void NavigationManager::UpdatePreNavNodeRenderGroupProperty()
187 {
188 CHECK_NULL_VOID(preNavNode_);
189 auto preNavDestContentNode = GetNavDestContentFrameNode(preNavNode_);
190 CHECK_NULL_VOID(preNavDestContentNode);
191 auto state = CheckNodeNeedCache(preNavDestContentNode);
192 UpdateAnimationCachedRenderGroup(preNavDestContentNode, state);
193 preNodeAnimationCached_ = state;
194 preNodeNeverSet_ = false;
195 auto preNavPattern = preNavNode_->GetPattern<NavDestinationPattern>();
196 auto name = preNavPattern == nullptr ? "NavBar" : preNavPattern->GetName();
197 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "Cache PreNavNode, name=%{public}s, will cache? %{public}s", name.c_str(),
198 state ? "yes" : "no");
199 }
200
UpdateCurNavNodeRenderGroupProperty()201 void NavigationManager::UpdateCurNavNodeRenderGroupProperty()
202 {
203 CHECK_NULL_VOID(curNavNode_);
204 auto curNavDestContentNode = GetNavDestContentFrameNode(curNavNode_);
205 CHECK_NULL_VOID(curNavDestContentNode);
206 auto state = CheckNodeNeedCache(curNavDestContentNode);
207 UpdateAnimationCachedRenderGroup(curNavDestContentNode, state);
208 curNodeAnimationCached_ = state;
209 currentNodeNeverSet_ = false;
210 auto curNavPattern = curNavNode_->GetPattern<NavDestinationPattern>();
211 auto name = curNavPattern == nullptr ? "NavBar" : curNavPattern->GetName();
212 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "Cache CurNavNode, name=%{public}s, will cache? %{public}s", name.c_str(),
213 state ? "yes" : "no");
214 }
215
ResetCurNavNodeRenderGroupProperty()216 void NavigationManager::ResetCurNavNodeRenderGroupProperty()
217 {
218 CHECK_NULL_VOID(curNavNode_);
219 auto curNavDestContentNode = GetNavDestContentFrameNode(curNavNode_);
220 CHECK_NULL_VOID(curNavDestContentNode);
221 UpdateAnimationCachedRenderGroup(curNavDestContentNode, false);
222 curNodeAnimationCached_ = false;
223 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "Cancel Cache CurNavNode node(id=%{public}d name=%{public}s)",
224 curNavDestContentNode->GetId(), curNavDestContentNode->GetTag().c_str());
225 }
226
CacheNavigationNodeAnimation()227 void NavigationManager::CacheNavigationNodeAnimation()
228 {
229 if (!hasCacheNavigationNodeEnable_) {
230 return;
231 }
232 if (!IsNavigationInAnimation()) {
233 return;
234 }
235 auto pipeline = PipelineContext::GetCurrentContext();
236 CHECK_NULL_VOID(pipeline);
237 /**
238 * If the exit page has not been cached before, or if the already cached exit page changes again,
239 * cache the exit page for future use.
240 */
241 if (preNodeNeverSet_ || (isNodeAddAnimation_ && preNodeAnimationCached_)) {
242 UpdatePreNavNodeRenderGroupProperty();
243 isNodeAddAnimation_ = false;
244 }
245 // Cache the entry page for future use
246 if (currentNodeNeverSet_ && !curNodeAnimationCached_ && !pipeline->GetIsRequestVsync()) {
247 UpdateCurNavNodeRenderGroupProperty();
248 }
249 // If the cached entry page changes again, cancel the previously marked entry page.
250 if (!currentNodeNeverSet_) {
251 ResetCurNavNodeRenderGroupProperty();
252 }
253 }
254
UpdateAnimationCachedRenderGroup(const RefPtr<FrameNode> & node,bool isSet)255 void NavigationManager::UpdateAnimationCachedRenderGroup(const RefPtr<FrameNode>& node, bool isSet)
256 {
257 auto context = node->GetRenderContext();
258 CHECK_NULL_VOID(context);
259 TAG_LOGD(AceLogTag::ACE_NAVIGATION,
260 "UpdateAnimationCachedRenderGroup node(id=%{public}d name=%{public}s), isSet=%{public}d", node->GetId(),
261 node->GetTag().c_str(), isSet);
262 context->UpdateRenderGroup(isSet, false, false);
263 }
264
AddRecoverableNavigation(std::string id,RefPtr<AceType> navigationNode)265 bool NavigationManager::AddRecoverableNavigation(std::string id, RefPtr<AceType> navigationNode)
266 {
267 auto navigation = AceType::DynamicCast<NavigationGroupNode>(navigationNode);
268 CHECK_NULL_RETURN(navigation, false);
269 if (!navigation->CanRecovery() || id != navigation->GetCurId()) {
270 return false;
271 }
272 recoverableNavigationMap_[id] = navigationNode;
273 return true;
274 }
275
GetNavigationJsonInfo()276 std::unique_ptr<JsonValue> NavigationManager::GetNavigationJsonInfo()
277 {
278 auto allNavigationInfo = JsonUtil::CreateArray(true);
279 for (auto iter : recoverableNavigationMap_) {
280 auto node = iter.second.Upgrade();
281 if (!node) {
282 continue;
283 }
284 auto navigation = AceType::DynamicCast<NavigationGroupNode>(node);
285 if (!navigation->CanRecovery()) {
286 continue;
287 }
288 auto navigationPattern = navigation->GetPattern<NavigationPattern>();
289 if (!navigationPattern) {
290 continue;
291 }
292 auto navigationInfo = JsonUtil::Create(true);
293 navigationInfo->Put("id", iter.first.c_str());
294 navigationInfo->Put("stack", navigationPattern->GetNavdestinationJsonArray());
295 allNavigationInfo->Put(navigationInfo);
296 }
297 return allNavigationInfo;
298 }
299
StorageNavigationRecoveryInfo(std::unique_ptr<JsonValue> navigationRecoveryInfo)300 void NavigationManager::StorageNavigationRecoveryInfo(std::unique_ptr<JsonValue> navigationRecoveryInfo)
301 {
302 auto allNavigationInfo = std::move(navigationRecoveryInfo);
303 if (!allNavigationInfo || !allNavigationInfo->IsArray()) {
304 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "Navigation recovery info invalid, can not restore!");
305 return;
306 }
307 auto arraySize = allNavigationInfo->GetArraySize();
308 for (int32_t i = 0; i < arraySize; ++ i) {
309 auto navigationInfo = allNavigationInfo->GetArrayItem(i);
310 auto navigationId = navigationInfo->GetString("id");
311 auto stackInfo = navigationInfo->GetValue("stack");
312 if (!stackInfo->IsArray()) {
313 continue;
314 }
315 std::vector<NavdestinationRecoveryInfo> navdestinationsInfo;
316 auto stackSize = stackInfo->GetArraySize();
317 for (int32_t j = 0; j < stackSize; ++ j) {
318 auto navdestinationInfo = stackInfo->GetArrayItem(j);
319 auto name = navdestinationInfo->GetString("name");
320 auto param = navdestinationInfo->GetString("param");
321 auto mode = navdestinationInfo->GetInt("mode");
322 navdestinationsInfo.emplace_back(NavdestinationRecoveryInfo(name, param, mode));
323 }
324 navigationRecoveryInfo_[navigationId] = navdestinationsInfo;
325 }
326 }
327
GetNavigationRecoveryInfo(std::string navigationId)328 const std::vector<NavdestinationRecoveryInfo> NavigationManager::GetNavigationRecoveryInfo(std::string navigationId)
329 {
330 if (navigationRecoveryInfo_.find(navigationId) == navigationRecoveryInfo_.end()) {
331 return {};
332 }
333 auto ret = navigationRecoveryInfo_[navigationId];
334 navigationRecoveryInfo_.erase(navigationId);
335 return ret;
336 }
337
AddNavigation(int32_t parentId,int32_t navigationId)338 void NavigationManager::AddNavigation(int32_t parentId, int32_t navigationId)
339 {
340 auto iter = navigationMaps_.find(parentId);
341 if (iter == navigationMaps_.end()) {
342 // insert into navigation maps
343 std::vector<int32_t> navigationIds;
344 navigationIds.push_back(navigationId);
345 navigationMaps_[parentId] = navigationIds;
346 return;
347 }
348 auto navigations = iter->second;
349 navigations.push_back(navigationId);
350 iter->second = navigations;
351 }
352
RemoveNavigation(int32_t navigationId)353 void NavigationManager::RemoveNavigation(int32_t navigationId)
354 {
355 for (auto navigationIter = navigationMaps_.begin(); navigationIter != navigationMaps_.end();) {
356 auto navigationIds = navigationIter->second;
357 auto it = std::find(navigationIds.begin(), navigationIds.end(), navigationId);
358 if (it == navigationIds.end()) {
359 navigationIter++;
360 continue;
361 }
362 navigationIds.erase(it);
363 if (navigationIds.empty()) {
364 navigationIter = navigationMaps_.erase(navigationIter);
365 } else {
366 navigationIter++;
367 }
368 }
369 }
370
FindNavigationInTargetParent(int32_t targetId)371 std::vector<int32_t> NavigationManager::FindNavigationInTargetParent(int32_t targetId)
372 {
373 auto it = navigationMaps_.find(targetId);
374 if (it == navigationMaps_.end()) {
375 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "can't find inner navigation");
376 return {};
377 }
378 return it->second;
379 }
380
FireNavigationLifecycle(const RefPtr<UINode> & node,int32_t lifecycle,int32_t reason)381 void NavigationManager::FireNavigationLifecycle(const RefPtr<UINode>& node, int32_t lifecycle, int32_t reason)
382 {
383 NavDestinationActiveReason activeReason = static_cast<NavDestinationActiveReason>(reason);
384 if (activeReason == NavDestinationActiveReason::TRANSITION) {
385 NavigationPattern::FireNavigationLifecycle(node, static_cast<NavDestinationLifecycle>(lifecycle),
386 activeReason);
387 return;
388 }
389 // fire navdestination lifecycle in outer layer
390 FireLowerLayerLifecycle(nullptr, lifecycle, reason);
391 }
392
FireOverlayLifecycle(const RefPtr<UINode> & node,int32_t lifecycle,int32_t reason)393 void NavigationManager::FireOverlayLifecycle(const RefPtr<UINode>& node, int32_t lifecycle, int32_t reason)
394 {
395 CHECK_NULL_VOID(node);
396 NavDestinationActiveReason activeReason = static_cast<NavDestinationActiveReason>(reason);
397 auto currentLifecycle = static_cast<NavDestinationLifecycle>(lifecycle);
398 NavigationPattern::FireNavigationLifecycle(node, currentLifecycle, activeReason);
399 NavDestinationLifecycle lowerLifecycle = NavDestinationLifecycle::ON_ACTIVE;
400 if (lifecycle == NavDestinationLifecycle::ON_ACTIVE) {
401 lowerLifecycle = NavDestinationLifecycle::ON_INACTIVE;
402 }
403 if (lifecycle == NavDestinationLifecycle::ON_INACTIVE) {
404 lowerLifecycle = NavDestinationLifecycle::ON_ACTIVE;
405 }
406 FireLowerLayerLifecycle(node, static_cast<int32_t>(lowerLifecycle), reason);
407 }
408
FireLowerLayerLifecycle(const RefPtr<UINode> & node,int32_t lifecycle,int32_t reason)409 void NavigationManager::FireLowerLayerLifecycle(const RefPtr<UINode>& node, int32_t lifecycle, int32_t reason)
410 {
411 NavDestinationLifecycle lowerLifecycle = static_cast<NavDestinationLifecycle>(lifecycle);
412 NavDestinationActiveReason activeReason = static_cast<NavDestinationActiveReason>(reason);
413 auto pipelineContext = pipeline_.Upgrade();
414 CHECK_NULL_VOID(pipelineContext);
415 auto rootNode = pipelineContext->GetRootElement();
416 CHECK_NULL_VOID(rootNode);
417 RefPtr<UINode> curNode = node;
418 // sheet page node is not attach on root, get parent node sheetWrapper
419 if (node && node->GetTag() == V2::SHEET_PAGE_TAG) {
420 curNode = node->GetParent();
421 }
422 int32_t curNodeIndex = curNode ? rootNode->GetChildIndex(curNode)
423 : static_cast<int32_t>(rootNode->GetChildren().size());
424 // find lower layer node below node
425 for (auto index = curNodeIndex - 1; index >= 0; index--) {
426 auto child = AceType::DynamicCast<FrameNode>(rootNode->GetChildAtIndex(index));
427 if (!child) {
428 continue;
429 }
430 auto tag = child->GetTag();
431 if (tag == V2::SHEET_WRAPPER_TAG) {
432 NavigationPattern::FireNavigationLifecycle(child->GetChildAtIndex(0), lowerLifecycle, activeReason);
433 return;
434 }
435 if (tag == V2::MODAL_PAGE_TAG) {
436 NavigationPattern::FireNavigationLifecycle(child, lowerLifecycle, activeReason);
437 return;
438 }
439 if (IsOverlayValid(child)) {
440 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "overlay has onShow");
441 return;
442 }
443 if (IsCustomDialogValid(child)) {
444 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "custom dialog onShow");
445 return;
446 }
447 }
448 // fire last page lifecycle
449 auto stageManager = pipelineContext->GetStageManager();
450 CHECK_NULL_VOID(stageManager);
451 auto lastPage = stageManager->GetLastPage();
452 CHECK_NULL_VOID(lastPage);
453 NavigationPattern::FireNavigationLifecycle(lastPage, lowerLifecycle, activeReason);
454 }
455
FireSubWindowLifecycle(const RefPtr<UINode> & node,int32_t lifecycle,int32_t reason)456 void NavigationManager::FireSubWindowLifecycle(const RefPtr<UINode>& node, int32_t lifecycle, int32_t reason)
457 {
458 auto context = AceType::DynamicCast<NG::PipelineContext>(PipelineContext::GetMainPipelineContext());
459 CHECK_NULL_VOID(context);
460 auto navigationManager = context->GetNavigationManager();
461 CHECK_NULL_VOID(navigationManager);
462 navigationManager->FireLowerLayerLifecycle(node, lifecycle, reason);
463 }
464
IsOverlayValid(const RefPtr<UINode> & node)465 bool NavigationManager::IsOverlayValid(const RefPtr<UINode>& node)
466 {
467 if (node->GetTag() != V2::OVERLAY_ETS_TAG) {
468 return false;
469 }
470 auto overlays = node->GetChildren();
471 // check overlay is visible, if overlay is visible, don't need fire active lifecycle
472 for (auto index = 0; index < static_cast<int32_t>(overlays.size()); index++) {
473 auto overlay = AceType::DynamicCast<FrameNode>(node->GetChildAtIndex(index));
474 if (!overlay) {
475 continue;
476 }
477 auto layoutProperty = overlay->GetLayoutProperty();
478 if (layoutProperty && layoutProperty->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::VISIBLE) {
479 return true;
480 }
481 }
482 return false;
483 }
484
IsCustomDialogValid(const RefPtr<UINode> & node)485 bool NavigationManager::IsCustomDialogValid(const RefPtr<UINode>& node)
486 {
487 auto frameNode = AceType::DynamicCast<FrameNode>(node);
488 CHECK_NULL_RETURN(frameNode, false);
489 // if lower layer is dialog, don't need to trigger lifecycle
490 auto pattern = frameNode->GetPattern();
491 if (!InstanceOf<DialogPattern>(pattern)) {
492 return false;
493 }
494 auto dialogPattern = AceType::DynamicCast<DialogPattern>(pattern);
495 if (!dialogPattern) {
496 return false;
497 }
498 auto dialogProperty = dialogPattern->GetDialogProperties();
499 // if dialog is custom dialog, don't need to trigger active lifecycle, it triggers when dialog closed
500 return dialogProperty.isUserCreatedDialog;
501 }
502 } // namespace OHOS::Ace::NG
503