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