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 constexpr int32_t INVALID_NODE_ID = -1;
27 constexpr char INTENT_PARAM_KEY[] = "ohos.insightIntent.executeParam.param";
28 constexpr char INTENT_NAVIGATION_ID_KEY[] = "ohos.insightIntent.pageParam.navigationId";
29 constexpr char INTENT_NAVDESTINATION_NAME_KEY[] = "ohos.insightIntent.pageParam.navDestinationName";
30
IsOuterMostNavigation(int32_t nodeId,int32_t depth)31 bool NavigationManager::IsOuterMostNavigation(int32_t nodeId, int32_t depth)
32 {
33 if (dumpMap_.empty()) {
34 return false;
35 }
36 auto outerMostKey = dumpMap_.begin()->first;
37 return outerMostKey == DumpMapKey(nodeId, depth);
38 }
39
SetForceSplitNavState(bool isTargetForceSplitNav,const RefPtr<FrameNode> & navigationNode)40 void NavigationManager::SetForceSplitNavState(bool isTargetForceSplitNav, const RefPtr<FrameNode>& navigationNode)
41 {
42 auto pattern = navigationNode->GetPattern<NavigationPattern>();
43 CHECK_NULL_VOID(pattern);
44 // Notification target navigation can attempt to force split.
45 pattern->SetIsTargetForceSplitNav(isTargetForceSplitNav);
46 // Record that the force split has been done in navigation manager
47 SetExistForceSplitNav(isTargetForceSplitNav, isTargetForceSplitNav ? navigationNode->GetId() : INVALID_NODE_ID);
48 }
49
RemoveForceSplitNavStateIfNeed(int32_t nodeId)50 void NavigationManager::RemoveForceSplitNavStateIfNeed(int32_t nodeId)
51 {
52 auto existForceSplitNav = GetExistForceSplitNav();
53 if (existForceSplitNav.first && existForceSplitNav.second == nodeId) {
54 SetExistForceSplitNav(false, INVALID_NODE_ID);
55 }
56 }
57
IsTargetForceSplitNav(const RefPtr<FrameNode> & navigationNode)58 void NavigationManager::IsTargetForceSplitNav(const RefPtr<FrameNode>& navigationNode)
59 {
60 /**
61 * If it does not support force split,
62 * or if there is already a force split navigation,
63 * return directly.
64 */
65 auto existForceSplitNav = GetExistForceSplitNav();
66 if (!IsForceSplitSupported() || existForceSplitNav.first) {
67 return;
68 }
69
70 /**
71 * If id and depth are not configured, the target force split navigation is the outermost navigation.
72 * It is necessary to determine whether the current navigation is the outermost navigation.
73 * Current navigation determines whether to force split before dumpMap information is stored,
74 * if the map is empty, the current navigation is the outermost navigation.
75 */
76 if (!TargetIdOrDepthExists()) {
77 bool isOuterMostNavigation = dumpMap_.begin() == dumpMap_.end();
78 SetForceSplitNavState(isOuterMostNavigation, navigationNode);
79 return;
80 }
81
82 // Prioritize whether the configured id matches the id of the current navigation, when configuring id.
83 auto targetInspectorId = GetTargetNavigationId();
84 if (targetInspectorId.has_value()) {
85 auto currInspectorId = navigationNode->GetInspectorId().value_or("");
86 bool isTargetForceSplitNav = currInspectorId == targetInspectorId.value();
87 SetForceSplitNavState(isTargetForceSplitNav, navigationNode);
88 return;
89 }
90 auto targetNestedDepth = GetTargetNavigationDepth();
91 if (!targetNestedDepth.has_value()) {
92 return;
93 }
94
95 /**
96 * If the current navigation depth is greater than the maximum depth stored in the dumpMap_,
97 * this means that there are no nodes at the same level in the current navigation node,
98 * and nested depth increased by one.
99 * After get the nested depth of the current navigation,
100 * compare whether it is the target nested depth navigation.
101 */
102 auto currDeepest = dumpMap_.rbegin();
103 auto currNodeDepth = navigationNode->GetDepth();
104 currNestedDepth_ =
105 currDeepest != dumpMap_.rend() && currNodeDepth > currDeepest->first.depth
106 ? ++currNestedDepth_
107 : currNestedDepth_;
108 bool isTargetForceSplitNav = currNestedDepth_ == targetNestedDepth.value();
109 SetForceSplitNavState(isTargetForceSplitNav, navigationNode);
110 }
111
AddNavigationDumpCallback(const RefPtr<FrameNode> & navigationNode,const DumpCallback & callback)112 void NavigationManager::AddNavigationDumpCallback(const RefPtr<FrameNode>& navigationNode, const DumpCallback& callback)
113 {
114 CHECK_RUN_ON(UI);
115 IsTargetForceSplitNav(navigationNode);
116 dumpMap_.emplace(DumpMapKey(navigationNode->GetId(), navigationNode->GetDepth()), callback);
117 }
118
RemoveNavigationDumpCallback(int32_t nodeId,int32_t depth)119 void NavigationManager::RemoveNavigationDumpCallback(int32_t nodeId, int32_t depth)
120 {
121 CHECK_RUN_ON(UI);
122 auto it = dumpMap_.find(DumpMapKey(nodeId, depth));
123 if (it != dumpMap_.end()) {
124 dumpMap_.erase(it);
125 }
126 RemoveForceSplitNavStateIfNeed(nodeId);
127 }
128
OnDumpInfo()129 void NavigationManager::OnDumpInfo()
130 {
131 CHECK_RUN_ON(UI);
132 auto pipeline = PipelineContext::GetCurrentContext();
133 CHECK_NULL_VOID(pipeline);
134 auto rootNode = pipeline->GetRootElement();
135 if (!rootNode) {
136 TAG_LOGE(AceLogTag::ACE_NAVIGATION, "navigation dump failed, invalid root node");
137 return;
138 }
139 DumpLog::GetInstance().Print("Navigation number: " + std::to_string(dumpMap_.size()));
140 std::stack<std::pair<RefPtr<UINode>, int32_t>> stack;
141 stack.push({ rootNode, 0 });
142 while (!stack.empty()) {
143 auto [curNode, curDepth] = stack.top();
144 stack.pop();
145 std::string space(INDENT_SIZE * curDepth, ' ');
146 int32_t depth = 0;
147 if (curNode->GetTag() == V2::NAVIGATION_VIEW_ETS_TAG) {
148 auto navigation = AceType::DynamicCast<NavigationGroupNode>(curNode);
149 CHECK_NULL_VOID(navigation);
150 DumpLog::GetInstance().Print(space + navigation->ToDumpString());
151 depth++;
152 } else if (curNode->GetTag() == V2::NAVDESTINATION_VIEW_ETS_TAG) {
153 auto navDestination = AceType::DynamicCast<NavDestinationGroupNode>(curNode);
154 CHECK_NULL_VOID(navDestination);
155 DumpLog::GetInstance().Print(space + navDestination->ToDumpString());
156 depth++;
157 auto parent = curNode->GetParent();
158 if (!stack.empty() && parent && parent->GetTag() == V2::PRIMARY_CONTENT_NODE_ETS_TAG &&
159 stack.top().first->GetTag() != V2::NAVDESTINATION_VIEW_ETS_TAG) {
160 DumpLog::GetInstance().Print("----------------------------------------------------------");
161 }
162 } else if (curNode->GetTag() == V2::NAVBAR_ETS_TAG) {
163 auto navBar = AceType::DynamicCast<NavBarNode>(curNode);
164 CHECK_NULL_VOID(navBar);
165 DumpLog::GetInstance().Print(space + navBar->ToDumpString());
166 DumpLog::GetInstance().Print("----------------------------------------------------------");
167 }
168 const auto& children = curNode->GetChildren();
169 for (auto it = children.rbegin(); it != children.rend(); it++) {
170 if (!(*it)) {
171 continue;
172 }
173 stack.push({ *it, curDepth + depth });
174 }
175 }
176 }
177
FireNavigationUpdateCallback()178 void NavigationManager::FireNavigationUpdateCallback()
179 {
180 for (const auto& func : updateCallbacks_) {
181 func();
182 }
183 updateCallbacks_.clear();
184 }
185
GetNavigationInfo(const RefPtr<AceType> & node)186 std::shared_ptr<NavigationInfo> NavigationManager::GetNavigationInfo(const RefPtr<AceType>& node)
187 {
188 RefPtr<UINode> current = nullptr;
189 auto customNode = AceType::DynamicCast<CustomNode>(node);
190 if (customNode) {
191 current = customNode->GetNavigationNode().Upgrade();
192 }
193
194 if (!current) {
195 current = AceType::DynamicCast<UINode>(node);
196 while (current) {
197 if (current->GetTag() == V2::NAVIGATION_VIEW_ETS_TAG) {
198 break;
199 }
200 current = current->GetParent();
201 }
202 }
203
204 if (!current) {
205 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "find parent navigation node failed");
206 return nullptr;
207 }
208
209 auto navigation = AceType::DynamicCast<NavigationGroupNode>(current);
210 CHECK_NULL_RETURN(navigation, nullptr);
211 auto pattern = navigation->GetPattern<NavigationPattern>();
212 CHECK_NULL_RETURN(pattern, nullptr);
213 auto stack = pattern->GetNavigationStack();
214 CHECK_NULL_RETURN(stack, nullptr);
215 return std::make_shared<NavigationInfo>(navigation->GetInspectorId().value_or(""), stack, navigation->GetId());
216 }
217
AddInteractiveAnimation(const std::function<void ()> & addCallback)218 bool NavigationManager::AddInteractiveAnimation(const std::function<void()>& addCallback)
219 {
220 if (!isInteractive_) {
221 return false;
222 }
223 auto navigationGroupNode = AceType::DynamicCast<NavigationGroupNode>(
224 FrameNode::GetFrameNode(V2::NAVIGATION_VIEW_ETS_TAG, interactiveAnimationId_));
225 CHECK_NULL_RETURN(navigationGroupNode, false);
226 auto pattern = navigationGroupNode->GetPattern<NavigationPattern>();
227 CHECK_NULL_RETURN(pattern, false);
228 auto proxy = pattern->GetTopNavigationProxy();
229 CHECK_NULL_RETURN(proxy, false);
230 proxy->AddInteractiveAnimation(addCallback);
231 return true;
232 }
233
CheckNodeNeedCache(const RefPtr<FrameNode> & node)234 bool NavigationManager::CheckNodeNeedCache(const RefPtr<FrameNode>& node)
235 {
236 CHECK_NULL_RETURN(node, false);
237
238 auto context = node->GetRenderContext();
239 if ((context && context->GetAnimationsCount() != 0) || node->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG) {
240 return false;
241 }
242 std::stack<RefPtr<FrameNode>> nodeStack;
243 nodeStack.push(node);
244 while (!nodeStack.empty()) {
245 auto curNode = nodeStack.top();
246 nodeStack.pop();
247 std::list<RefPtr<FrameNode>> children;
248 curNode->GenerateOneDepthVisibleFrameWithTransition(children);
249 for (auto& child : children) {
250 if (!child) {
251 continue;
252 }
253 auto childContext = child->GetRenderContext();
254 if ((childContext && childContext->GetAnimationsCount() != 0) ||
255 child->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG) {
256 return false;
257 }
258 nodeStack.push(child);
259 }
260 auto overlayNode = curNode->GetOverlayNode();
261 if (overlayNode) {
262 auto overlayNodeContext = overlayNode->GetRenderContext();
263 if ((overlayNodeContext && overlayNodeContext->GetAnimationsCount() != 0) ||
264 overlayNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG) {
265 return false;
266 }
267 nodeStack.push(overlayNode);
268 }
269 }
270 return true;
271 }
272
GetNavDestContentFrameNode(const RefPtr<FrameNode> & node)273 RefPtr<FrameNode> NavigationManager::GetNavDestContentFrameNode(const RefPtr<FrameNode>& node)
274 {
275 CHECK_NULL_RETURN(node, nullptr);
276 auto navDestinationNodeBase = AceType::DynamicCast<NavDestinationNodeBase>(node);
277 CHECK_NULL_RETURN(navDestinationNodeBase, nullptr);
278 auto navDestContentFrameNode = AceType::DynamicCast<FrameNode>(navDestinationNodeBase->GetContentNode());
279 CHECK_NULL_RETURN(navDestContentFrameNode, nullptr);
280 return navDestContentFrameNode;
281 }
282
UpdatePreNavNodeRenderGroupProperty()283 void NavigationManager::UpdatePreNavNodeRenderGroupProperty()
284 {
285 auto preNavNode = preNavNode_.Upgrade();
286 CHECK_NULL_VOID(preNavNode);
287 auto preNavDestContentNode = GetNavDestContentFrameNode(preNavNode);
288 CHECK_NULL_VOID(preNavDestContentNode);
289 auto state = CheckNodeNeedCache(preNavDestContentNode);
290 UpdateAnimationCachedRenderGroup(preNavDestContentNode, state);
291 preNodeAnimationCached_ = state;
292 preNodeNeverSet_ = false;
293 auto preNavPattern = preNavNode->GetPattern<NavDestinationPattern>();
294 auto name = preNavPattern == nullptr ? "NavBar" : preNavPattern->GetName();
295 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "Cache PreNavNode, name=%{public}s, will cache? %{public}s", name.c_str(),
296 state ? "yes" : "no");
297 }
298
UpdateCurNavNodeRenderGroupProperty()299 void NavigationManager::UpdateCurNavNodeRenderGroupProperty()
300 {
301 auto curNavNode = curNavNode_.Upgrade();
302 CHECK_NULL_VOID(curNavNode);
303 auto curNavDestContentNode = GetNavDestContentFrameNode(curNavNode);
304 CHECK_NULL_VOID(curNavDestContentNode);
305 auto state = CheckNodeNeedCache(curNavDestContentNode);
306 UpdateAnimationCachedRenderGroup(curNavDestContentNode, state);
307 curNodeAnimationCached_ = state;
308 currentNodeNeverSet_ = false;
309 auto curNavPattern = curNavNode->GetPattern<NavDestinationPattern>();
310 auto name = curNavPattern == nullptr ? "NavBar" : curNavPattern->GetName();
311 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "Cache CurNavNode, name=%{public}s, will cache? %{public}s", name.c_str(),
312 state ? "yes" : "no");
313 }
314
SetForceSplitEnable(bool isForceSplit,const std::string & homePage,bool ignoreOrientation)315 void NavigationManager::SetForceSplitEnable(bool isForceSplit, const std::string& homePage, bool ignoreOrientation)
316 {
317 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "set navigation force split %{public}s, homePage:%{public}s, "
318 "ignoreOrientation:%{public}d", (isForceSplit ? "enable" : "disable"), homePage.c_str(), ignoreOrientation);
319 /**
320 * As long as the application supports force split, regardless of whether it is enabled or not,
321 * the SetForceSplitleEnable interface will be called.
322 */
323 isForceSplitSupported_ = true;
324 if (isForceSplitEnable_ == isForceSplit && homePageName_ == homePage && ignoreOrientation_ == ignoreOrientation) {
325 return;
326 }
327 isForceSplitEnable_ = isForceSplit;
328 homePageName_ = homePage;
329 ignoreOrientation_ = ignoreOrientation;
330
331 auto listeners = forceSplitListeners_;
332 for (auto& listener : listeners) {
333 if (listener.second) {
334 listener.second();
335 }
336 }
337 }
338
GetIgnoreOrientation() const339 bool NavigationManager::GetIgnoreOrientation() const
340 {
341 if (SystemProperties::GetForceSplitIgnoreOrientationEnabled()) {
342 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "Navigation forceSplit ignore Orientation");
343 return true;
344 }
345 return ignoreOrientation_;
346 }
347
AddForceSplitListener(int32_t nodeId,std::function<void ()> && listener)348 void NavigationManager::AddForceSplitListener(int32_t nodeId, std::function<void()>&& listener)
349 {
350 forceSplitListeners_[nodeId] = std::move(listener);
351 }
352
RemoveForceSplitListener(int32_t nodeId)353 void NavigationManager::RemoveForceSplitListener(int32_t nodeId)
354 {
355 auto it = forceSplitListeners_.find(nodeId);
356 if (it != forceSplitListeners_.end()) {
357 forceSplitListeners_.erase(it);
358 }
359 }
360
ResetCurNavNodeRenderGroupProperty()361 void NavigationManager::ResetCurNavNodeRenderGroupProperty()
362 {
363 auto curNavNode = curNavNode_.Upgrade();
364 CHECK_NULL_VOID(curNavNode);
365 auto curNavDestContentNode = GetNavDestContentFrameNode(curNavNode);
366 CHECK_NULL_VOID(curNavDestContentNode);
367 UpdateAnimationCachedRenderGroup(curNavDestContentNode, false);
368 curNodeAnimationCached_ = false;
369 TAG_LOGD(AceLogTag::ACE_NAVIGATION, "Cancel Cache CurNavNode node(id=%{public}d name=%{public}s)",
370 curNavDestContentNode->GetId(), curNavDestContentNode->GetTag().c_str());
371 }
372
CacheNavigationNodeAnimation()373 void NavigationManager::CacheNavigationNodeAnimation()
374 {
375 if (!hasCacheNavigationNodeEnable_) {
376 return;
377 }
378 if (!IsNavigationInAnimation()) {
379 return;
380 }
381 auto pipeline = PipelineContext::GetCurrentContext();
382 CHECK_NULL_VOID(pipeline);
383 /**
384 * If the exit page has not been cached before, or if the already cached exit page changes again,
385 * cache the exit page for future use.
386 */
387 if (preNodeNeverSet_ || (isNodeAddAnimation_ && preNodeAnimationCached_)) {
388 UpdatePreNavNodeRenderGroupProperty();
389 isNodeAddAnimation_ = false;
390 }
391 // Cache the entry page for future use
392 if (currentNodeNeverSet_ && !curNodeAnimationCached_ && !pipeline->GetIsRequestVsync()) {
393 UpdateCurNavNodeRenderGroupProperty();
394 }
395 // If the cached entry page changes again, cancel the previously marked entry page.
396 if (!currentNodeNeverSet_) {
397 ResetCurNavNodeRenderGroupProperty();
398 }
399 }
400
UpdateAnimationCachedRenderGroup(const RefPtr<FrameNode> & node,bool isSet)401 void NavigationManager::UpdateAnimationCachedRenderGroup(const RefPtr<FrameNode>& node, bool isSet)
402 {
403 auto context = node->GetRenderContext();
404 CHECK_NULL_VOID(context);
405 TAG_LOGD(AceLogTag::ACE_NAVIGATION,
406 "UpdateAnimationCachedRenderGroup node(id=%{public}d name=%{public}s), isSet=%{public}d", node->GetId(),
407 node->GetTag().c_str(), isSet);
408 context->UpdateRenderGroup(isSet, false, false);
409 }
410
AddRecoverableNavigation(std::string id,RefPtr<AceType> navigationNode)411 bool NavigationManager::AddRecoverableNavigation(std::string id, RefPtr<AceType> navigationNode)
412 {
413 auto navigation = AceType::DynamicCast<NavigationGroupNode>(navigationNode);
414 CHECK_NULL_RETURN(navigation, false);
415 if (!navigation->CanRecovery() || id != navigation->GetCurId()) {
416 return false;
417 }
418 recoverableNavigationMap_[id] = navigationNode;
419 return true;
420 }
421
GetNavigationJsonInfo()422 std::unique_ptr<JsonValue> NavigationManager::GetNavigationJsonInfo()
423 {
424 auto allNavigationInfo = JsonUtil::CreateArray(true);
425 for (auto iter : recoverableNavigationMap_) {
426 auto node = iter.second.Upgrade();
427 if (!node) {
428 continue;
429 }
430 auto navigation = AceType::DynamicCast<NavigationGroupNode>(node);
431 if (!navigation->CanRecovery()) {
432 continue;
433 }
434 auto navigationPattern = navigation->GetPattern<NavigationPattern>();
435 if (!navigationPattern) {
436 continue;
437 }
438 auto navigationInfo = JsonUtil::Create(true);
439 navigationInfo->Put("id", iter.first.c_str());
440 navigationInfo->Put("stack", navigationPattern->GetNavdestinationJsonArray());
441 allNavigationInfo->Put(navigationInfo);
442 }
443 return allNavigationInfo;
444 }
445
StorageNavigationRecoveryInfo(std::unique_ptr<JsonValue> navigationRecoveryInfo)446 void NavigationManager::StorageNavigationRecoveryInfo(std::unique_ptr<JsonValue> navigationRecoveryInfo)
447 {
448 auto allNavigationInfo = std::move(navigationRecoveryInfo);
449 if (!allNavigationInfo || !allNavigationInfo->IsArray()) {
450 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "Navigation recovery info invalid, can not restore!");
451 return;
452 }
453 auto arraySize = allNavigationInfo->GetArraySize();
454 for (int32_t i = 0; i < arraySize; ++ i) {
455 auto navigationInfo = allNavigationInfo->GetArrayItem(i);
456 auto navigationId = navigationInfo->GetString("id");
457 auto stackInfo = navigationInfo->GetValue("stack");
458 if (!stackInfo->IsArray()) {
459 continue;
460 }
461 std::vector<NavdestinationRecoveryInfo> navdestinationsInfo;
462 auto stackSize = stackInfo->GetArraySize();
463 for (int32_t j = 0; j < stackSize; ++ j) {
464 auto navdestinationInfo = stackInfo->GetArrayItem(j);
465 auto name = navdestinationInfo->GetString("name");
466 auto param = navdestinationInfo->GetString("param");
467 auto mode = navdestinationInfo->GetInt("mode");
468 navdestinationsInfo.emplace_back(NavdestinationRecoveryInfo(name, param, mode));
469 }
470 navigationRecoveryInfo_[navigationId] = navdestinationsInfo;
471 }
472 }
473
GetNavigationRecoveryInfo(std::string navigationId)474 const std::vector<NavdestinationRecoveryInfo> NavigationManager::GetNavigationRecoveryInfo(std::string navigationId)
475 {
476 if (navigationRecoveryInfo_.find(navigationId) == navigationRecoveryInfo_.end()) {
477 return {};
478 }
479 auto ret = navigationRecoveryInfo_[navigationId];
480 navigationRecoveryInfo_.erase(navigationId);
481 return ret;
482 }
483
AddNavigation(int32_t parentNodeId,const RefPtr<FrameNode> & navigationNode)484 void NavigationManager::AddNavigation(int32_t parentNodeId, const RefPtr<FrameNode>& navigationNode)
485 {
486 auto navigation = AceType::DynamicCast<NavigationGroupNode>(navigationNode);
487 CHECK_NULL_VOID(navigation);
488 auto navigationInfo = NavigationInfo(navigation->GetId(), navigation->GetCurId(), navigation);
489 if (navigationMap_.find(parentNodeId) != navigationMap_.end()) {
490 navigationMap_[parentNodeId].push_back(navigationInfo);
491 return;
492 }
493 navigationMap_[parentNodeId] = { navigationInfo };
494 }
495
RemoveNavigation(int32_t navigationNodeId)496 void NavigationManager::RemoveNavigation(int32_t navigationNodeId)
497 {
498 for (auto navigationIter = navigationMap_.begin(); navigationIter != navigationMap_.end();) {
499 auto& navigationInfos = navigationIter->second;
500 auto it = std::find_if(navigationInfos.begin(), navigationInfos.end(), [navigationNodeId](auto info) {
501 return navigationNodeId == info.nodeId;
502 });
503 if (it == navigationInfos.end()) {
504 navigationIter++;
505 continue;
506 }
507 navigationInfos.erase(it);
508 if (navigationInfos.empty()) {
509 navigationIter = navigationMap_.erase(navigationIter);
510 } else {
511 navigationIter++;
512 }
513 }
514 }
515
FindNavigationInTargetParent(int32_t targetId)516 std::vector<int32_t> NavigationManager::FindNavigationInTargetParent(int32_t targetId)
517 {
518 auto it = navigationMap_.find(targetId);
519 if (it == navigationMap_.end()) {
520 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "can't find inner navigation");
521 return {};
522 }
523 std::vector<int32_t> navigationIds = {};
524 auto navigationInfos = it->second;
525 for (auto navigationInfo : navigationInfos) {
526 navigationIds.push_back(navigationInfo.nodeId);
527 }
528 return navigationIds;
529 }
530
FireNavigationLifecycle(const RefPtr<UINode> & node,int32_t lifecycle,int32_t reason)531 void NavigationManager::FireNavigationLifecycle(const RefPtr<UINode>& node, int32_t lifecycle, int32_t reason)
532 {
533 NavDestinationActiveReason activeReason = static_cast<NavDestinationActiveReason>(reason);
534 if (activeReason == NavDestinationActiveReason::TRANSITION) {
535 NavigationPattern::FireNavigationLifecycle(node, static_cast<NavDestinationLifecycle>(lifecycle),
536 activeReason);
537 return;
538 }
539 // fire navdestination lifecycle in outer layer
540 FireLowerLayerLifecycle(nullptr, lifecycle, reason);
541 }
542
FireOverlayLifecycle(const RefPtr<UINode> & node,int32_t lifecycle,int32_t reason)543 void NavigationManager::FireOverlayLifecycle(const RefPtr<UINode>& node, int32_t lifecycle, int32_t reason)
544 {
545 CHECK_NULL_VOID(node);
546 NavDestinationActiveReason activeReason = static_cast<NavDestinationActiveReason>(reason);
547 auto currentLifecycle = static_cast<NavDestinationLifecycle>(lifecycle);
548 NavigationPattern::FireNavigationLifecycle(node, currentLifecycle, activeReason);
549 NavDestinationLifecycle lowerLifecycle = NavDestinationLifecycle::ON_ACTIVE;
550 if (lifecycle == NavDestinationLifecycle::ON_ACTIVE) {
551 lowerLifecycle = NavDestinationLifecycle::ON_INACTIVE;
552 }
553 if (lifecycle == NavDestinationLifecycle::ON_INACTIVE) {
554 lowerLifecycle = NavDestinationLifecycle::ON_ACTIVE;
555 }
556 FireLowerLayerLifecycle(node, static_cast<int32_t>(lowerLifecycle), reason);
557 }
558
FireLowerLayerLifecycle(const RefPtr<UINode> & node,int32_t lifecycle,int32_t reason)559 void NavigationManager::FireLowerLayerLifecycle(const RefPtr<UINode>& node, int32_t lifecycle, int32_t reason)
560 {
561 NavDestinationLifecycle lowerLifecycle = static_cast<NavDestinationLifecycle>(lifecycle);
562 NavDestinationActiveReason activeReason = static_cast<NavDestinationActiveReason>(reason);
563 auto pipelineContext = pipeline_.Upgrade();
564 CHECK_NULL_VOID(pipelineContext);
565 auto rootNode = pipelineContext->GetRootElement();
566 CHECK_NULL_VOID(rootNode);
567 RefPtr<UINode> curNode = node;
568 // sheet page node is not attach on root, get parent node sheetWrapper
569 if (node && node->GetTag() == V2::SHEET_PAGE_TAG) {
570 curNode = node->GetParent();
571 }
572 int32_t curNodeIndex = curNode ? rootNode->GetChildIndex(curNode)
573 : static_cast<int32_t>(rootNode->GetChildren().size());
574 // find lower layer node below node
575 for (auto index = curNodeIndex - 1; index >= 0; index--) {
576 auto child = AceType::DynamicCast<FrameNode>(rootNode->GetChildAtIndex(index));
577 if (!child) {
578 continue;
579 }
580 auto tag = child->GetTag();
581 if (tag == V2::SHEET_WRAPPER_TAG) {
582 NavigationPattern::FireNavigationLifecycle(child->GetChildAtIndex(0), lowerLifecycle, activeReason);
583 return;
584 }
585 if (tag == V2::MODAL_PAGE_TAG) {
586 NavigationPattern::FireNavigationLifecycle(child, lowerLifecycle, activeReason);
587 return;
588 }
589 if (IsOverlayValid(child)) {
590 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "overlay has onShow");
591 return;
592 }
593 if (IsCustomDialogValid(child)) {
594 TAG_LOGI(AceLogTag::ACE_NAVIGATION, "custom dialog onShow");
595 return;
596 }
597 }
598 // fire last page lifecycle
599 auto stageManager = pipelineContext->GetStageManager();
600 CHECK_NULL_VOID(stageManager);
601 auto lastPage = stageManager->GetLastPage();
602 CHECK_NULL_VOID(lastPage);
603 NavigationPattern::FireNavigationLifecycle(lastPage, lowerLifecycle, activeReason);
604 }
605
FireSubWindowLifecycle(const RefPtr<UINode> & node,int32_t lifecycle,int32_t reason)606 void NavigationManager::FireSubWindowLifecycle(const RefPtr<UINode>& node, int32_t lifecycle, int32_t reason)
607 {
608 auto context = AceType::DynamicCast<NG::PipelineContext>(PipelineContext::GetMainPipelineContext());
609 CHECK_NULL_VOID(context);
610 auto navigationManager = context->GetNavigationManager();
611 CHECK_NULL_VOID(navigationManager);
612 navigationManager->FireLowerLayerLifecycle(node, lifecycle, reason);
613 }
614
IsOverlayValid(const RefPtr<UINode> & node)615 bool NavigationManager::IsOverlayValid(const RefPtr<UINode>& node)
616 {
617 if (node->GetTag() != V2::OVERLAY_ETS_TAG) {
618 return false;
619 }
620 auto overlays = node->GetChildren();
621 // check overlay is visible, if overlay is visible, don't need fire active lifecycle
622 for (auto index = 0; index < static_cast<int32_t>(overlays.size()); index++) {
623 auto overlay = AceType::DynamicCast<FrameNode>(node->GetChildAtIndex(index));
624 if (!overlay) {
625 continue;
626 }
627 auto layoutProperty = overlay->GetLayoutProperty();
628 if (layoutProperty && layoutProperty->GetVisibilityValue(VisibleType::VISIBLE) == VisibleType::VISIBLE) {
629 return true;
630 }
631 }
632 return false;
633 }
634
IsCustomDialogValid(const RefPtr<UINode> & node)635 bool NavigationManager::IsCustomDialogValid(const RefPtr<UINode>& node)
636 {
637 auto frameNode = AceType::DynamicCast<FrameNode>(node);
638 CHECK_NULL_RETURN(frameNode, false);
639 // if lower layer is dialog, don't need to trigger lifecycle
640 auto pattern = frameNode->GetPattern();
641 if (!InstanceOf<DialogPattern>(pattern)) {
642 return false;
643 }
644 auto dialogPattern = AceType::DynamicCast<DialogPattern>(pattern);
645 if (!dialogPattern) {
646 return false;
647 }
648 auto dialogProperty = dialogPattern->GetDialogProperties();
649 // if dialog is custom dialog, don't need to trigger active lifecycle, it triggers when dialog closed
650 return dialogProperty.isUserCreatedDialog;
651 }
652
AddBeforeOrientationChangeTask(const std::function<void ()> && task)653 void NavigationManager::AddBeforeOrientationChangeTask(const std::function<void()>&& task)
654 {
655 beforeOrientationChangeTasks_.emplace_back(std::move(task));
656 }
657
ClearBeforeOrientationChangeTask()658 void NavigationManager::ClearBeforeOrientationChangeTask()
659 {
660 beforeOrientationChangeTasks_.clear();
661 }
662
OnOrientationChanged()663 void NavigationManager::OnOrientationChanged()
664 {
665 auto tasks = std::move(beforeOrientationChangeTasks_);
666 for (auto& task : tasks) {
667 if (task) {
668 task();
669 }
670 }
671 }
672
SetNavigationIntentInfo(const std::string & intentInfoSerialized,bool isColdStart)673 void NavigationManager::SetNavigationIntentInfo(const std::string& intentInfoSerialized, bool isColdStart)
674 {
675 if (intentInfoSerialized.empty()) {
676 TAG_LOGE(AceLogTag::ACE_NAVIGATION, "error, serialized intent info is empty!");
677 return;
678 }
679 navigationIntentInfo_ = ParseNavigationIntentInfo(intentInfoSerialized);
680 navigationIntentInfo_.value().isColdStart = isColdStart;
681 }
682
ParseNavigationIntentInfo(const std::string & intentInfoSerialized)683 NavigationIntentInfo NavigationManager::ParseNavigationIntentInfo(const std::string& intentInfoSerialized)
684 {
685 NavigationIntentInfo intentInfo;
686 auto intentJson = JsonUtil::ParseJsonString(intentInfoSerialized);
687 if (!intentJson || !intentJson->IsObject()) {
688 TAG_LOGE(AceLogTag::ACE_NAVIGATION, "error, intent info is an invalid json object!");
689 return intentInfo;
690 }
691 intentInfo.param = intentJson->GetObject(INTENT_PARAM_KEY)->ToString();
692 intentInfo.navigationInspectorId = intentJson->GetString(INTENT_NAVIGATION_ID_KEY, "");
693 intentInfo.navDestinationName = intentJson->GetString(INTENT_NAVDESTINATION_NAME_KEY, "");
694 return intentInfo;
695 }
696
FireNavigationIntentActively(int32_t pageId,bool needTransition)697 bool NavigationManager::FireNavigationIntentActively(int32_t pageId, bool needTransition)
698 {
699 if (navigationMap_.find(pageId) == navigationMap_.end()) {
700 TAG_LOGE(AceLogTag::ACE_NAVIGATION, "error, no navigation in current page(id: %{public}d)", pageId);
701 return false;
702 }
703 if (!navigationIntentInfo_.has_value()) {
704 TAG_LOGE(AceLogTag::ACE_NAVIGATION, "error, navigation intent info is empty!");
705 return false;
706 }
707 auto navigationInfos = navigationMap_[pageId];
708 // find target navigation
709 for (auto navigationInfo : navigationInfos) {
710 if (navigationInfo.navigationId == navigationIntentInfo_.value().navigationInspectorId) {
711 auto navigationNode = navigationInfo.navigationNode.Upgrade();
712 if (!navigationNode) {
713 return false;
714 }
715 auto pattern = navigationNode->GetPattern<NavigationPattern>();
716 if (!pattern) {
717 return false;
718 }
719 return pattern->HandleIntent(needTransition);
720 }
721 }
722 TAG_LOGE(AceLogTag::ACE_NAVIGATION,
723 "error, specified navigation(id: %{public}s) doesn't exist in current page(id: %{public}d)",
724 navigationIntentInfo_.value().navigationInspectorId.c_str(), pageId);
725 return false;
726 }
727
GetTopNavDestinationInfo(int32_t pageId,bool onlyFullScreen,bool needParam)728 std::string NavigationManager::GetTopNavDestinationInfo(int32_t pageId, bool onlyFullScreen, bool needParam)
729 {
730 if (navigationMap_.find(pageId) == navigationMap_.end()) {
731 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "no navigation in current page(id: %{public}d)", pageId);
732 return "{}";
733 }
734 auto navigationInfos = navigationMap_[pageId];
735 for (int32_t index = static_cast<int32_t>(navigationInfos.size()) -1; index >= 0; index--) {
736 auto navigationInfo = navigationInfos[index];
737 auto navigationNode = navigationInfo.navigationNode.Upgrade();
738 if (!navigationNode) {
739 continue;
740 }
741 auto pattern = navigationNode->GetPattern<NavigationPattern>();
742 if (!pattern) {
743 continue;
744 }
745 if (onlyFullScreen && !pattern->IsFullPageNavigation()) {
746 continue;
747 }
748 return pattern->GetTopNavdestinationJson(needParam)->ToString();
749 }
750 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "no valid navigation in current page(id: %{public}d)", pageId);
751 return "{}";
752 }
753
RestoreNavDestinationInfo(const std::string & navDestinationInfo,bool isColdStart)754 void NavigationManager::RestoreNavDestinationInfo(const std::string& navDestinationInfo, bool isColdStart)
755 {
756 auto navDestinationJson = JsonUtil::ParseJsonString(navDestinationInfo);
757 if (!navDestinationJson || !navDestinationJson->IsObject()) {
758 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "restore navdestination info is an invalid json object!");
759 return;
760 }
761 auto name = navDestinationJson->GetString("name");
762 auto param = navDestinationJson->GetString("param");
763 auto mode = navDestinationJson->GetInt("mode");
764 if (isColdStart) {
765 // for cold start case
766 auto navigationId = navDestinationJson->GetString("navigationId", "");
767 if (navigationId.empty()) {
768 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "will restore %{public}s without navigationId, "
769 "it may lead to error in multi-navigation case!", name.c_str());
770 }
771 navigationRecoveryInfo_[navigationId] = { NavdestinationRecoveryInfo(name, param, mode) };
772 return;
773 }
774 // for hot start case
775 if (navigationMap_.empty()) {
776 TAG_LOGW(AceLogTag::ACE_NAVIGATION, "restore navdestination failed cause is an invagitlid json object!");
777 return;
778 }
779 auto navigationNode = GetNavigationByInspectorId(navDestinationJson->GetString("navigationId", ""));
780 CHECK_NULL_VOID(navigationNode);
781 auto pattern = navigationNode->GetPattern<NavigationPattern>();
782 CHECK_NULL_VOID(pattern);
783 auto navigationStack = pattern->GetNavigationStack();
784 CHECK_NULL_VOID(navigationStack);
785 navigationStack->CallPushDestinationInner(NavdestinationRecoveryInfo(name, param, mode));
786 }
787
GetNavigationByInspectorId(const std::string & id) const788 RefPtr<FrameNode> NavigationManager::GetNavigationByInspectorId(const std::string& id) const
789 {
790 for (auto iter : navigationMap_) {
791 auto allNavigations = iter.second;
792 for (int32_t index = static_cast<int32_t>(allNavigations.size()) - 1; index >= 0; index--) {
793 auto navigationInfo = allNavigations[index];
794 if (navigationInfo.navigationId == id && navigationInfo.navigationNode.Upgrade()) {
795 return navigationInfo.navigationNode.Upgrade();
796 }
797 }
798 }
799 return nullptr;
800 }
801 } // namespace OHOS::Ace::NG
802