1 /*
2 * Copyright (c) 2023 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/pattern/app_bar/app_bar_view.h"
17
18 #include <map>
19
20 #include "base/utils/utils.h"
21 #include "base/want/want_wrap.h"
22 #include "core/common/app_bar_helper.h"
23 #include "core/common/container.h"
24 #include "core/common/container_scope.h"
25 #include "core/components_ng/base/view_stack_processor.h"
26 #include "core/components_ng/event/focus_hub.h"
27 #include "core/components_ng/layout/layout_property.h"
28 #include "core/components_ng/pattern/app_bar/app_bar_theme.h"
29 #include "core/components_ng/pattern/app_bar/atomic_service_pattern.h"
30 #include "core/components_ng/pattern/button/button_layout_property.h"
31 #include "core/components_ng/pattern/button/button_pattern.h"
32 #include "core/components_ng/pattern/divider/divider_layout_property.h"
33 #include "core/components_ng/pattern/divider/divider_pattern.h"
34 #include "core/components_ng/pattern/divider/divider_render_property.h"
35 #include "core/components_ng/pattern/image/image_layout_property.h"
36 #include "core/components_ng/pattern/image/image_pattern.h"
37 #include "core/components_ng/pattern/linear_layout/linear_layout_pattern.h"
38 #include "core/components_ng/pattern/linear_layout/linear_layout_property.h"
39 #include "core/components_ng/pattern/stage/stage_pattern.h"
40 #include "core/components_ng/pattern/text/text_layout_property.h"
41 #include "core/components_ng/pattern/text/text_pattern.h"
42 #include "core/components_ng/property/border_property.h"
43 #include "core/components_ng/property/calc_length.h"
44 #include "core/components_ng/property/measure_property.h"
45 #include "core/components_v2/inspector/inspector_constants.h"
46 #include "core/pipeline_ng/pipeline_context.h"
47
48 namespace OHOS::Ace::NG {
49 namespace {
GetAppBarTheme()50 RefPtr<AppBarTheme> GetAppBarTheme()
51 {
52 auto pipeline = PipelineContext::GetCurrentContext();
53 CHECK_NULL_RETURN(pipeline, nullptr);
54 return pipeline->GetTheme<AppBarTheme>();
55 }
56
57 #ifndef PREVIEW
AssembleUiExtensionParams(bool firstTry,std::string & appGalleryBundleName,std::map<std::string,std::string> & params)58 void AssembleUiExtensionParams(
59 bool firstTry, std::string& appGalleryBundleName, std::map<std::string, std::string>& params)
60 {
61 auto missionId = AceApplicationInfo::GetInstance().GetMissionId();
62 params.try_emplace("bundleName", AceApplicationInfo::GetInstance().GetProcessName());
63 params.try_emplace("abilityName", AceApplicationInfo::GetInstance().GetAbilityName());
64 params.try_emplace("module", Container::Current()->GetModuleName());
65 if (missionId != -1) {
66 params.try_emplace("missionId", std::to_string(missionId));
67 }
68
69 if (firstTry) {
70 params.try_emplace("ability.want.params.uiExtensionType", "sysDialog/atomicServicePanel");
71 appGalleryBundleName = OHOS::Ace::SystemProperties::GetAtomicServiceBundleName();
72 } else {
73 params.try_emplace("ability.want.params.uiExtensionType", "sys/commonUI");
74 appGalleryBundleName = OHOS::Ace::AppBarHelper::QueryAppGalleryBundleName();
75 }
76 }
77 #endif
78 } // namespace
79
Create(const RefPtr<FrameNode> & stage)80 RefPtr<FrameNode> AppBarView::Create(const RefPtr<FrameNode>& stage)
81 {
82 auto atom = FrameNode::CreateFrameNode(V2::ATOMIC_SERVICE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
83 AceType::MakeRefPtr<AtomicServicePattern>());
84 // add children
85 auto menuBarRow = BuildMenuBarRow();
86 atom->AddChild(stage);
87 atom->AddChild(menuBarRow);
88 // init
89 atom->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
90 stage->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
91 atomicService_ = atom;
92 auto pattern = atom->GetPattern<AtomicServicePattern>();
93 CHECK_NULL_RETURN(pattern, nullptr);
94 pattern->UpdateColor(std::nullopt);
95 pattern->UpdateLayout();
96 return atom;
97 }
98
BuildMenuBarRow()99 RefPtr<FrameNode> AppBarView::BuildMenuBarRow()
100 {
101 auto menuBarRow = FrameNode::CreateFrameNode(V2::APP_BAR_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
102 AceType::MakeRefPtr<LinearLayoutPattern>(false));
103 auto theme = GetAppBarTheme();
104 CHECK_NULL_RETURN(theme, nullptr);
105
106 auto menuBar = BuildMenuBar();
107 menuBarRow->AddChild(menuBar);
108
109 auto layoutProperty = menuBarRow->GetLayoutProperty<LinearLayoutProperty>();
110 auto renderContext = menuBarRow->GetRenderContext();
111 // size
112 layoutProperty->UpdateUserDefinedIdealSize(
113 CalcSize(CalcLength(1.0, DimensionUnit::PERCENT), CalcLength(theme->GetMenuBarHeight())));
114 // main axis align
115 layoutProperty->UpdateMainAxisAlign(FlexAlign::FLEX_END);
116 // position
117 renderContext->UpdatePosition(OffsetT<Dimension>(0.0_vp, theme->GetMenuBarTopMargin()));
118 // background color
119 renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
120 // hit test mode
121 menuBarRow->SetHitTestMode(HitTestMode::HTMTRANSPARENT_SELF);
122
123 menuBarRow->MarkModifyDone();
124 return menuBarRow;
125 }
126
BuildMenuBar()127 RefPtr<FrameNode> AppBarView::BuildMenuBar()
128 {
129 auto menuBar = FrameNode::CreateFrameNode(V2::ROW_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
130 AceType::MakeRefPtr<LinearLayoutPattern>(false));
131 auto theme = GetAppBarTheme();
132 CHECK_NULL_RETURN(theme, nullptr);
133
134 auto menuButton = BuildButton(true);
135 BindMenuCallback(menuButton);
136 menuBar->AddChild(menuButton);
137 auto divider = BuildDivider();
138 menuBar->AddChild(divider);
139 auto closeButton = BuildButton(false);
140 BindCloseCallback(closeButton);
141 menuBar->AddChild(closeButton);
142
143 auto layoutProperty = menuBar->GetLayoutProperty<LinearLayoutProperty>();
144 auto renderContext = menuBar->GetRenderContext();
145 // main axis align
146 layoutProperty->UpdateMainAxisAlign(FlexAlign::FLEX_START);
147 // border width
148 BorderWidthProperty borderWidth;
149 borderWidth.SetBorderWidth(theme->GetBorderWidth());
150 layoutProperty->UpdateBorderWidth(borderWidth);
151 renderContext->UpdateBorderWidth(borderWidth);
152 // border radius
153 auto bent = theme->GetBentRadius();
154 renderContext->UpdateBorderRadius(BorderRadiusProperty(bent));
155
156 menuBar->MarkModifyDone();
157 return menuBar;
158 }
159
BuildButton(bool isMenuButton)160 RefPtr<FrameNode> AppBarView::BuildButton(bool isMenuButton)
161 {
162 auto button = FrameNode::CreateFrameNode(
163 V2::BUTTON_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ButtonPattern>());
164 auto renderContext = button->GetRenderContext();
165 renderContext->UpdateBackgroundColor(Color::TRANSPARENT);
166 auto theme = GetAppBarTheme();
167 CHECK_NULL_RETURN(theme, nullptr);
168
169 auto icon = BuildIcon(isMenuButton);
170 button->AddChild(icon);
171
172 auto layoutProperty = button->GetLayoutProperty<ButtonLayoutProperty>();
173 // type
174 layoutProperty->UpdateType(ButtonType::NORMAL);
175 // size
176 layoutProperty->UpdateUserDefinedIdealSize(
177 CalcSize(CalcLength(theme->GetButtonWidth()), CalcLength(theme->GetButtonHeight())));
178 // focus style type
179 auto focusHub = button->GetFocusHub();
180 CHECK_NULL_RETURN(focusHub, nullptr);
181 focusHub->SetFocusStyleType(FocusStyleType::INNER_BORDER);
182 // focus border width
183 auto buttonPattern = button->GetPattern<ButtonPattern>();
184 CHECK_NULL_RETURN(buttonPattern, nullptr);
185 buttonPattern->SetFocusBorderWidth(theme->GetFocusedOutlineWidth());
186
187 button->MarkModifyDone();
188 return button;
189 }
190
BuildIcon(bool isMenuIcon)191 RefPtr<FrameNode> AppBarView::BuildIcon(bool isMenuIcon)
192 {
193 auto icon = FrameNode::CreateFrameNode(
194 V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<ImagePattern>());
195 auto theme = GetAppBarTheme();
196 CHECK_NULL_RETURN(theme, nullptr);
197
198 ImageSourceInfo imageSourceInfo;
199 imageSourceInfo.SetResourceId(
200 isMenuIcon ? InternalResource::ResourceId::APP_BAR_MENU_SVG : InternalResource::ResourceId::APP_BAR_CLOSE_SVG);
201 auto layoutProperty = icon->GetLayoutProperty<ImageLayoutProperty>();
202 layoutProperty->UpdateImageSourceInfo(imageSourceInfo);
203 // size
204 layoutProperty->UpdateUserDefinedIdealSize(
205 CalcSize(CalcLength(theme->GetNewIconSize()), CalcLength(theme->GetNewIconSize())));
206 // focusable
207 auto focusHub = icon->GetFocusHub();
208 CHECK_NULL_RETURN(focusHub, nullptr);
209 focusHub->SetFocusable(true);
210
211 icon->MarkModifyDone();
212 return icon;
213 }
214
BuildDivider()215 RefPtr<FrameNode> AppBarView::BuildDivider()
216 {
217 auto divider = FrameNode::CreateFrameNode(
218 V2::DIVIDER_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<DividerPattern>());
219 auto theme = GetAppBarTheme();
220 CHECK_NULL_RETURN(theme, nullptr);
221
222 auto layoutProperty = divider->GetLayoutProperty<DividerLayoutProperty>();
223 // size
224 layoutProperty->UpdateUserDefinedIdealSize(
225 CalcSize(CalcLength(theme->GetDividerWidth()), CalcLength(theme->GetDividerHeight())));
226 // direction
227 layoutProperty->UpdateVertical(true);
228 // stroke width
229 layoutProperty->UpdateStrokeWidth(theme->GetDividerWidth());
230 // marigin
231 MarginProperty margin;
232 margin.left = CalcLength(-(theme->GetDividerWidth()));
233 auto renderProperty = divider->GetPaintProperty<DividerRenderProperty>();
234 // line cap
235 renderProperty->UpdateLineCap(LineCap::ROUND);
236
237 divider->MarkModifyDone();
238 return divider;
239 }
240
BindMenuCallback(const RefPtr<FrameNode> & menuButton)241 void AppBarView::BindMenuCallback(const RefPtr<FrameNode>& menuButton)
242 {
243 auto clickCallback = [wp = WeakClaim(this)](GestureEvent& info) {
244 #ifdef PREVIEW
245 LOGW("[Engine Log] Unable to show the SharePanel in the Previewer. "
246 "Perform this operation on the emulator or a real device instead.");
247 #else
248 auto appbarView = wp.Upgrade();
249 CHECK_NULL_VOID(appbarView);
250 auto node = appbarView->atomicService_.Upgrade();
251 auto pipeline = node->GetContext();
252 CHECK_NULL_VOID(pipeline);
253 auto theme = pipeline->GetTheme<AppBarTheme>();
254 CHECK_NULL_VOID(theme);
255 if (SystemProperties::GetExtSurfaceEnabled()) {
256 LOGI("start panel bundleName is %{public}s, abilityName is %{public}s", theme->GetBundleName().c_str(),
257 theme->GetAbilityName().c_str());
258 pipeline->FireSharePanelCallback(theme->GetBundleName(), theme->GetAbilityName());
259 } else {
260 appbarView->CreateServicePanel(true);
261 }
262 #endif
263 };
264 auto eventHub = menuButton->GetOrCreateGestureEventHub();
265 if (eventHub) {
266 eventHub->AddClickEvent(AceType::MakeRefPtr<ClickEvent>(std::move(clickCallback)));
267 }
268 }
269
BindCloseCallback(const RefPtr<FrameNode> & closeButton)270 void AppBarView::BindCloseCallback(const RefPtr<FrameNode>& closeButton)
271 {
272 auto clickCallback = [](GestureEvent& info) {
273 auto pipeline = PipelineContext::GetCurrentContext();
274 CHECK_NULL_VOID(pipeline);
275 auto container = Container::Current();
276 CHECK_NULL_VOID(container);
277 if (container->IsUIExtensionWindow()) {
278 container->TerminateUIExtension();
279 } else {
280 auto windowManager = pipeline->GetWindowManager();
281 CHECK_NULL_VOID(windowManager);
282 windowManager->WindowPerformBack();
283 }
284 };
285 auto eventHub = closeButton->GetOrCreateGestureEventHub();
286 if (eventHub) {
287 eventHub->AddClickEvent(AceType::MakeRefPtr<ClickEvent>(std::move(clickCallback)));
288 }
289 }
290
DestroyServicePanel()291 void AppBarView::DestroyServicePanel()
292 {
293 auto node = atomicService_.Upgrade();
294 auto pipeline = node->GetContext();
295 CHECK_NULL_VOID(pipeline);
296 auto overlayManager = pipeline->GetOverlayManager();
297 CHECK_NULL_VOID(overlayManager);
298 ContainerScope scope(pipeline->GetInstanceId());
299 overlayManager->CloseModalUIExtension(sessionId_);
300 LOGI("ServicePanel release session:%{public}d", sessionId_);
301 }
302
CreateServicePanel(bool firstTry)303 void AppBarView::CreateServicePanel(bool firstTry)
304 {
305 #ifndef PREVIEW
306 if (OHOS::Ace::SystemProperties::GetAtomicServiceBundleName().empty() &&
307 OHOS::Ace::AppBarHelper::QueryAppGalleryBundleName().empty()) {
308 LOGE("UIExtension BundleName is empty.");
309 return;
310 }
311
312 auto pipeline = PipelineContext::GetCurrentContext();
313 CHECK_NULL_VOID(pipeline);
314 auto overlayManager = pipeline->GetOverlayManager();
315 CHECK_NULL_VOID(overlayManager);
316
317 ModalUIExtensionCallbacks callbacks;
318 callbacks.onRelease = [wp = WeakClaim(this)](int32_t releaseCode) {
319 auto bar = wp.Upgrade();
320 bar->DestroyServicePanel();
321 };
322 callbacks.onError = [wp = WeakClaim(this), firstTry](
323 int32_t code, const std::string& name, const std::string& message) {
324 auto bar = wp.Upgrade();
325 bar->DestroyServicePanel();
326 if (firstTry) {
327 bar->CreateServicePanel(false);
328 }
329 };
330 std::string abilityName;
331 auto theme = pipeline->GetTheme<AppBarTheme>();
332 if (theme) {
333 abilityName = theme->GetStageAbilityName();
334 }
335 std::string appGalleryBundleName;
336 std::map<std::string, std::string> params;
337 AssembleUiExtensionParams(firstTry, appGalleryBundleName, params);
338 auto wantWrap = WantWrap::CreateWantWrap(appGalleryBundleName, abilityName);
339 wantWrap->SetWantParam(params);
340 LOGI("ServicePanel request bundle: %{public}s, ability: %{public}s. "
341 "UIExtension bundle: %{public}s, ability: %{public}s, module: %{public}s",
342 appGalleryBundleName.c_str(), abilityName.c_str(), params["bundleName"].c_str(), params["abilityName"].c_str(),
343 params["module"].c_str());
344 ModalUIExtensionConfig config;
345 sessionId_ = overlayManager->CreateModalUIExtension(wantWrap, callbacks, config);
346 #endif
347 }
348
InitUIExtensionNode(const RefPtr<FrameNode> & uiExtNode)349 void AppBarView::InitUIExtensionNode(const RefPtr<FrameNode>& uiExtNode)
350 {
351 CHECK_NULL_VOID(uiExtNode);
352 // Update ideal size of UIExtension.
353 auto layoutProperty = uiExtNode->GetLayoutProperty();
354 layoutProperty->UpdateUserDefinedIdealSize(CalcSize(
355 CalcLength(Dimension(1.0, DimensionUnit::PERCENT)), CalcLength(Dimension(1.0, DimensionUnit::PERCENT))));
356 uiExtNode->MarkModifyDone();
357 }
358
GetAppBarRect()359 std::optional<RectF> AppBarView::GetAppBarRect()
360 {
361 auto pipeline = PipelineContext::GetCurrentContext();
362 if (!pipeline || !pipeline->GetInstallationFree()) {
363 return std::nullopt;
364 }
365 auto theme = GetAppBarTheme();
366 CHECK_NULL_RETURN(theme, std::nullopt);
367 auto atom = atomicService_.Upgrade();
368 CHECK_NULL_RETURN(atom, std::nullopt);
369 auto pattern = atom->GetPattern<AtomicServicePattern>();
370 CHECK_NULL_RETURN(pattern, std::nullopt);
371 auto menuBar = pattern->GetMenuBar();
372 CHECK_NULL_RETURN(menuBar, std::nullopt);
373 auto size = menuBar->GetGeometryNode()->GetMarginFrameSize();
374 auto offset = menuBar->GetGeometryNode()->GetMarginFrameOffset();
375 auto parent = menuBar->GetParent();
376 while (parent) {
377 auto frameNode = AceType::DynamicCast<FrameNode>(parent);
378 if (frameNode) {
379 offset += frameNode->GetGeometryNode()->GetFrameOffset();
380 }
381 parent = parent->GetParent();
382 }
383 offset.AddY(theme->GetMenuBarTopMargin().ConvertToPx());
384 auto manager = pipeline->GetSafeAreaManager();
385 CHECK_NULL_RETURN(manager, std::nullopt);
386 offset.AddY(manager->GetSystemSafeArea().top_.Length());
387 return RectF(offset, size);
388 }
389
SetStatusBarItemColor(bool isLight)390 void AppBarView::SetStatusBarItemColor(bool isLight)
391 {
392 auto atom = atomicService_.Upgrade();
393 CHECK_NULL_VOID(atom);
394 auto pattern = atom->GetPattern<AtomicServicePattern>();
395 CHECK_NULL_VOID(pattern);
396 auto theme = GetAppBarTheme();
397 auto menuBar = pattern->GetMenuBar();
398 pattern->settedColorMode = isLight;
399 pattern->UpdateMenuBarColor(theme, menuBar, isLight);
400 pattern->UpdateColor(isLight);
401 }
402 } // namespace OHOS::Ace::NG
403