• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/common/force_split/force_split_utils.h"
17 
18 #include <string>
19 #include <vector>
20 
21 #include "base/geometry/dimension.h"
22 #include "base/json/json_util.h"
23 #include "core/common/container.h"
24 #include "core/components_ng/pattern/image/image_pattern.h"
25 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
26 #include "core/components_ng/pattern/navigation/nav_bar_node.h"
27 #include "core/components_ng/pattern/navrouter/navdestination_group_node.h"
28 #include "core/components_ng/pattern/navrouter/navdestination_pattern.h"
29 #include "core/components_ng/pattern/stack/stack_pattern.h"
30 #include "core/pipeline/base/element_register.h"
31 
32 namespace OHOS::Ace::NG {
33 
34 namespace {
35 constexpr Dimension APP_ICON_SIZE = 64.0_vp;
36 constexpr Dimension APP_ICON_BORDER_RADIUS = 18.0_vp;
37 constexpr char BG_COLOR_SYS_RES_NAME[] = "sys.color.ohos_id_color_sub_background";
38 const std::vector<std::string> HOME_NAME_KEYWORDS = {"main", "home", "index", "root"};
39 const std::vector<std::string> EXCLUDE_NAME_KEYWORDS = {"guide", "load", "splash", "login", "privacy"};
40 constexpr int32_t HOME_PAGE_CHILD_NODE_DEPTH_THRESHOLD = 30;
41 constexpr int32_t HOME_PAGE_CHILD_NODE_COUNT_THRESHOLD = 100;
42 constexpr char ENABLE_HOOK_KEY[] = "enableHook";
43 constexpr char NAVIGATION_OPTIONS_KEY[] = "navigationOptions";
44 constexpr char NAVIGATION_OPTIONS_ID_KEY[] = "id";
45 constexpr char NAVIGATION_OPTIONS_DEPTH_KEY[] = "depth";
46 }
47 
CreatePlaceHolderContent(const RefPtr<PipelineContext> & context)48 RefPtr<FrameNode> ForceSplitUtils::CreatePlaceHolderContent(const RefPtr<PipelineContext>& context)
49 {
50     CHECK_NULL_RETURN(context, nullptr);
51     auto themeManager = context->GetThemeManager();
52     CHECK_NULL_RETURN(themeManager, nullptr);
53     auto windowManager = context->GetWindowManager();
54     CHECK_NULL_RETURN(windowManager, nullptr);
55     auto stackNode = FrameNode::GetOrCreateFrameNode(
56         V2::STACK_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
57         []() { return AceType::MakeRefPtr<StackPattern>(); });
58     CHECK_NULL_RETURN(stackNode, nullptr);
59     auto stackLayoutProperty = stackNode->GetLayoutProperty();
60     CHECK_NULL_RETURN(stackLayoutProperty, nullptr);
61     stackLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
62 
63     auto imageNode = FrameNode::GetOrCreateFrameNode(
64         V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
65         []() { return AceType::MakeRefPtr<ImagePattern>(); });
66     CHECK_NULL_RETURN(imageNode, nullptr);
67     auto themeConstants = themeManager->GetThemeConstants();
68     CHECK_NULL_RETURN(themeConstants, nullptr);
69     auto id = windowManager->GetAppIconId();
70     auto pixelMap = themeConstants->GetPixelMap(id);
71     auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
72     CHECK_NULL_RETURN(imageLayoutProperty, nullptr);
73     CalcSize imageCalcSize((CalcLength(APP_ICON_SIZE)), CalcLength(APP_ICON_SIZE));
74     imageLayoutProperty->UpdateUserDefinedIdealSize(imageCalcSize);
75     imageLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(PixelMap::CreatePixelMap(&pixelMap)));
76     auto imageRenderContext = imageNode->GetRenderContext();
77     CHECK_NULL_RETURN(imageRenderContext, nullptr);
78     BorderRadiusProperty borderRadius;
79     borderRadius.SetRadius(APP_ICON_BORDER_RADIUS);
80     borderRadius.multiValued = false;
81     imageRenderContext->UpdateBorderRadius(borderRadius);
82     auto paintProperty = imageNode->GetPaintPropertyPtr<ImageRenderProperty>();
83     CHECK_NULL_RETURN(paintProperty, nullptr);
84     paintProperty->UpdateNeedBorderRadius(true);
85     paintProperty->UpdateBorderRadius(borderRadius);
86     auto imagePattern = imageNode->GetPattern<ImagePattern>();
87     CHECK_NULL_RETURN(imagePattern, nullptr);
88     imagePattern->SetNeedBorderRadius(true);
89 
90     imageNode->MountToParent(stackNode);
91     imageNode->MarkModifyDone();
92 
93     return stackNode;
94 }
95 
CreateNavDestinationProxyNode()96 RefPtr<NavDestinationGroupNode> ForceSplitUtils::CreateNavDestinationProxyNode()
97 {
98     auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
99     auto proxyNode = NavDestinationGroupNode::GetOrCreateGroupNode(
100         V2::NAVDESTINATION_VIEW_ETS_TAG, nodeId, []() { return AceType::MakeRefPtr<NavDestinationPattern>(); });
101     CHECK_NULL_RETURN(proxyNode, nullptr);
102     proxyNode->SetNavDestinationType(NavDestinationType::PROXY);
103     auto pattern = proxyNode->GetPattern<NavDestinationPattern>();
104     CHECK_NULL_RETURN(pattern, nullptr);
105     pattern->SetName("__NavDestination_proxy__");
106     auto eventHub = proxyNode->GetOrCreateEventHub<EventHub>();
107     if (eventHub) {
108         eventHub->SetEnabled(false);
109     }
110     auto focusHub = proxyNode->GetOrCreateFocusHub();
111     if (focusHub) {
112         focusHub->SetFocusable(false);
113     }
114     auto property = proxyNode->GetLayoutProperty<NavDestinationLayoutProperty>();
115     CHECK_NULL_RETURN(property, nullptr);
116     property->UpdateHideTitleBar(true);
117     property->UpdateIsAnimatedTitleBar(false);
118     property->UpdateHideToolBar(true);
119     property->UpdateIsAnimatedToolBar(false);
120     property->UpdateVisibility(VisibleType::INVISIBLE);
121     auto contentNode = FrameNode::GetOrCreateFrameNode(
122         V2::NAVDESTINATION_CONTENT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
123         []() { return AceType::MakeRefPtr<LinearLayoutPattern>(true); });
124     CHECK_NULL_RETURN(contentNode, nullptr);
125     proxyNode->AddChild(contentNode);
126     proxyNode->SetContentNode(contentNode);
127     return proxyNode;
128 }
129 
IsHomePageNavBar(const RefPtr<NavBarNode> & navBar)130 bool ForceSplitUtils::IsHomePageNavBar(const RefPtr<NavBarNode>& navBar)
131 {
132     CHECK_NULL_RETURN(navBar, false);
133     int32_t count = 0;
134     int32_t depth = 0;
135     navBar->GetPageNodeCountAndDepth(&count, &depth);
136     if (count > HOME_PAGE_CHILD_NODE_COUNT_THRESHOLD &&
137         depth > HOME_PAGE_CHILD_NODE_DEPTH_THRESHOLD) {
138         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "find NavBar as homePage for node count:%{public}d and depth:%{public}d",
139             count, depth);
140         return true;
141     }
142     return false;
143 }
144 
IsHomePageNavDestination(const RefPtr<NavDestinationGroupNode> & node)145 bool ForceSplitUtils::IsHomePageNavDestination(const RefPtr<NavDestinationGroupNode>& node)
146 {
147     CHECK_NULL_RETURN(node, false);
148     if (node->GetNavDestinationMode() == NavDestinationMode::DIALOG) {
149         return false;
150     }
151 
152     auto context = node->GetContext();
153     CHECK_NULL_RETURN(context, false);
154     auto navManager = context->GetNavigationManager();
155     CHECK_NULL_RETURN(navManager, false);
156     auto pattern = node->GetPattern<NavDestinationPattern>();
157     CHECK_NULL_RETURN(pattern, false);
158     const auto& expectedHomeName = navManager->GetHomePageName();
159     std::string name = pattern->GetName();
160     if (!expectedHomeName.empty()) {
161         if (expectedHomeName == name) {
162             TAG_LOGI(AceLogTag::ACE_NAVIGATION, "find homePage[%{public}s] with expectedName", name.c_str());
163             return true;
164         }
165         return false;
166     }
167     std::transform(name.begin(), name.end(), name.begin(), ::tolower);
168     for (auto it = EXCLUDE_NAME_KEYWORDS.begin(); it != EXCLUDE_NAME_KEYWORDS.end(); it++) {
169         std::string keyword = *it;
170         if (name.find(keyword) != std::string::npos) {
171             return false;
172         }
173     }
174     for (auto it = HOME_NAME_KEYWORDS.begin(); it != HOME_NAME_KEYWORDS.end(); it++) {
175         std::string keyword = *it;
176         if (name.find(keyword) != std::string::npos) {
177             TAG_LOGI(AceLogTag::ACE_NAVIGATION, "find homePage[%{public}s] with primary page keyword", name.c_str());
178             return true;
179         }
180     }
181     int32_t count = 0;
182     int32_t depth = 0;
183     node->GetPageNodeCountAndDepth(&count, &depth);
184     if (count > HOME_PAGE_CHILD_NODE_COUNT_THRESHOLD &&
185         depth > HOME_PAGE_CHILD_NODE_DEPTH_THRESHOLD) {
186         TAG_LOGI(AceLogTag::ACE_NAVIGATION, "find homePage[%{public}s] for node count:%{public}d and depth:%{public}d",
187             name.c_str(), count, depth);
188         return true;
189     }
190     return false;
191 }
192 
CreatePlaceHolderNode()193 RefPtr<FrameNode> ForceSplitUtils::CreatePlaceHolderNode()
194 {
195     int32_t phId = ElementRegister::GetInstance()->MakeUniqueId();
196     auto phNode = FrameNode::GetOrCreateFrameNode(
197         V2::SPLIT_PLACEHOLDER_CONTENT_ETS_TAG, phId, []() { return AceType::MakeRefPtr<Pattern>(); });
198     CHECK_NULL_RETURN(phNode, nullptr);
199     auto context = phNode->GetContextRefPtr();
200     CHECK_NULL_RETURN(context, nullptr);
201     auto navManager = context->GetNavigationManager();
202     CHECK_NULL_RETURN(navManager, nullptr);
203     auto renderContext = phNode->GetRenderContext();
204     CHECK_NULL_RETURN(renderContext, nullptr);
205     Color bgColor;
206     if (navManager->GetSystemColor(BG_COLOR_SYS_RES_NAME, bgColor)) {
207         renderContext->UpdateBackgroundColor(bgColor);
208     }
209     auto property = phNode->GetLayoutProperty();
210     CHECK_NULL_RETURN(property, nullptr);
211     property->UpdateVisibility(VisibleType::INVISIBLE);
212     property->UpdateAlignment(Alignment::TOP_LEFT);
213     SafeAreaExpandOpts opts = { .type = SAFE_AREA_TYPE_SYSTEM | SAFE_AREA_TYPE_CUTOUT,
214         .edges = SAFE_AREA_EDGE_ALL };
215     property->UpdateSafeAreaExpandOpts(opts);
216     auto eventHub = phNode->GetOrCreateEventHub<EventHub>();
217     if (eventHub) {
218         eventHub->SetEnabled(false);
219     }
220     auto focusHub = phNode->GetOrCreateFocusHub();
221     if (focusHub) {
222         focusHub->SetFocusable(false);
223     }
224     auto phContent = ForceSplitUtils::CreatePlaceHolderContent(context);
225     if (phContent) {
226         phContent->MountToParent(phNode);
227     } else {
228         TAG_LOGE(AceLogTag::ACE_NAVIGATION, "failed to create PlaceHolder content");
229     }
230     return phNode;
231 }
232 
ParseForceSplitConfig(const std::string & configJsonStr,ForceSplitConfig & config)233 bool ForceSplitUtils::ParseForceSplitConfig(const std::string& configJsonStr, ForceSplitConfig& config)
234 {
235     auto configJson = JsonUtil::ParseJsonString(configJsonStr);
236     if (!configJson || !configJson->IsObject()) {
237         TAG_LOGE(AceLogTag::ACE_NAVIGATION, "Error, arkUIOptions is an invalid json object!");
238         return false;
239     }
240     config.isArkUIHookEnabled = configJson->GetBool(ENABLE_HOOK_KEY, false);
241     if (!configJson->Contains(NAVIGATION_OPTIONS_KEY)) {
242         return true;
243     }
244     auto navOptions = configJson->GetValue(NAVIGATION_OPTIONS_KEY);
245     if (!navOptions || !navOptions->IsObject()) {
246         TAG_LOGE(AceLogTag::ACE_NAVIGATION, "Error, navigationOptions is an invalid json object!");
247         return false;
248     }
249     if (navOptions->Contains(NAVIGATION_OPTIONS_ID_KEY)) {
250         auto idJson = navOptions->GetValue(NAVIGATION_OPTIONS_ID_KEY);
251         if (!idJson->IsString()) {
252             TAG_LOGE(AceLogTag::ACE_NAVIGATION, "Error, navigationOptions.id is not string!");
253             return false;
254         }
255         auto idStr = idJson->GetString();
256         if (!idStr.empty()) {
257             config.navigationId = idStr;
258         }
259     }
260     if (navOptions->Contains(NAVIGATION_OPTIONS_DEPTH_KEY)) {
261         auto depthJson = navOptions->GetValue(NAVIGATION_OPTIONS_DEPTH_KEY);
262         if (!depthJson->IsNumber()) {
263             TAG_LOGE(AceLogTag::ACE_NAVIGATION, "Error, navigationOptions.depth is not number!");
264             return false;
265         }
266         config.navigationDepth = navOptions->GetInt(NAVIGATION_OPTIONS_DEPTH_KEY);
267     }
268     return true;
269 }
270 } // namespace OHOS::Ace::NG
271 
272