1 /*
2 * Copyright (c) 2022 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/stage/page_pattern.h"
17
18 #include "base/log/jank_frame_report.h"
19 #include "base/perfmonitor/perf_monitor.h"
20 #include "base/utils/utils.h"
21 #include "core/animation/animator.h"
22 #include "core/common/container.h"
23 #include "core/components/common/properties/alignment.h"
24 #include "core/pipeline_ng/pipeline_context.h"
25
26 namespace OHOS::Ace::NG {
27
28 namespace {
IterativeAddToSharedMap(const RefPtr<UINode> & node,SharedTransitionMap & map)29 void IterativeAddToSharedMap(const RefPtr<UINode>& node, SharedTransitionMap& map)
30 {
31 const auto& children = node->GetChildren();
32 for (const auto& child : children) {
33 auto frameChild = AceType::DynamicCast<FrameNode>(child);
34 if (!frameChild) {
35 IterativeAddToSharedMap(child, map);
36 continue;
37 }
38 auto id = frameChild->GetRenderContext()->GetShareId();
39 if (!id.empty()) {
40 LOGD("add id:%{public}s", id.c_str());
41 map[id] = frameChild;
42 }
43 IterativeAddToSharedMap(frameChild, map);
44 }
45 }
46 } // namespace
47
OnAttachToFrameNode()48 void PagePattern::OnAttachToFrameNode()
49 {
50 auto host = GetHost();
51 CHECK_NULL_VOID(host);
52 host->GetLayoutProperty()->UpdateMeasureType(MeasureType::MATCH_PARENT);
53 host->GetLayoutProperty()->UpdateAlignment(Alignment::TOP_LEFT);
54 }
55
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> &,const DirtySwapConfig &)56 bool PagePattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& /*wrapper*/, const DirtySwapConfig& /*config*/)
57 {
58 if (isFirstLoad_) {
59 isFirstLoad_ = false;
60 if (firstBuildCallback_) {
61 firstBuildCallback_();
62 firstBuildCallback_ = nullptr;
63 }
64 }
65 return false;
66 }
67
TriggerPageTransition(PageTransitionType type,const std::function<void ()> & onFinish)68 bool PagePattern::TriggerPageTransition(PageTransitionType type, const std::function<void()>& onFinish)
69 {
70 auto host = GetHost();
71 CHECK_NULL_RETURN(host, false);
72 auto renderContext = host->GetRenderContext();
73 CHECK_NULL_RETURN(renderContext, false);
74 if (pageTransitionFunc_) {
75 pageTransitionFunc_();
76 }
77 auto effect = FindPageTransitionEffect(type);
78 pageTransitionFinish_ = std::make_shared<std::function<void()>>(onFinish);
79 auto wrappedOnFinish = [weak = WeakClaim(this), sharedFinish = pageTransitionFinish_]() {
80 auto pattern = weak.Upgrade();
81 CHECK_NULL_VOID_NOLOG(pattern);
82 if (sharedFinish == pattern->pageTransitionFinish_) {
83 // ensure this is exactly the finish callback saved in pagePattern,
84 // otherwise means new pageTransition started
85 pattern->FirePageTransitionFinish();
86 }
87 };
88 if (effect && effect->GetUserCallback()) {
89 if (!controller_) {
90 controller_ = CREATE_ANIMATOR(PipelineContext::GetCurrentContext());
91 }
92 if (!controller_->IsStopped()) {
93 controller_->Finish();
94 }
95 controller_->ClearInterpolators();
96 RouteType routeType = (type == PageTransitionType::ENTER_POP || type == PageTransitionType::EXIT_POP)
97 ? RouteType::POP
98 : RouteType::PUSH;
99 auto floatAnimation = AceType::MakeRefPtr<CurveAnimation<float>>(0.0f, 1.0f, effect->GetCurve());
100 floatAnimation->AddListener(
101 [routeType, handler = effect->GetUserCallback(), weak = WeakClaim(this)](const float& progress) {
102 auto pattern = weak.Upgrade();
103 CHECK_NULL_VOID(pattern);
104 handler(routeType, progress);
105 });
106 if (effect->GetDelay() >= 0) {
107 controller_->SetStartDelay(effect->GetDelay());
108 }
109 controller_->SetDuration(effect->GetDuration());
110 controller_->AddInterpolator(floatAnimation);
111 controller_->AddStopListener(wrappedOnFinish);
112 controller_->Forward();
113 return renderContext->TriggerPageTransition(type, nullptr);
114 }
115 return renderContext->TriggerPageTransition(type, wrappedOnFinish);
116 }
117
ProcessHideState()118 void PagePattern::ProcessHideState()
119 {
120 auto host = GetHost();
121 CHECK_NULL_VOID(host);
122 host->SetActive(false);
123 host->OnVisibleChange(false);
124 host->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
125 auto parent = host->GetAncestorNodeOfFrame();
126 CHECK_NULL_VOID(parent);
127 parent->MarkNeedSyncRenderTree();
128 parent->RebuildRenderContextTree();
129 }
130
ProcessShowState()131 void PagePattern::ProcessShowState()
132 {
133 auto host = GetHost();
134 CHECK_NULL_VOID(host);
135 host->SetActive(true);
136 host->OnVisibleChange(true);
137 host->GetLayoutProperty()->UpdateVisibility(VisibleType::VISIBLE);
138 auto parent = host->GetAncestorNodeOfFrame();
139 CHECK_NULL_VOID(parent);
140 parent->MarkNeedSyncRenderTree();
141 parent->RebuildRenderContextTree();
142 }
143
OnShow()144 void PagePattern::OnShow()
145 {
146 // Do not invoke onPageShow unless the initialRender function has been executed.
147 CHECK_NULL_VOID_NOLOG(isRenderDone_);
148 CHECK_NULL_VOID_NOLOG(!isOnShow_);
149 auto container = Container::Current();
150 if (!container || !container->WindowIsShow()) {
151 LOGW("no need to trigger onPageShow callback when not in the foreground");
152 return;
153 }
154 auto host = GetHost();
155 CHECK_NULL_VOID(host);
156 host->SetJSViewActive(true);
157 isOnShow_ = true;
158 JankFrameReport::StartRecord(pageInfo_->GetPageUrl());
159 PerfMonitor::GetPerfMonitor()->SetPageUrl(pageInfo_->GetPageUrl());
160 if (onPageShow_) {
161 onPageShow_();
162 }
163 }
164
OnHide()165 void PagePattern::OnHide()
166 {
167 CHECK_NULL_VOID_NOLOG(isOnShow_);
168 JankFrameReport::FlushRecord();
169 auto host = GetHost();
170 CHECK_NULL_VOID(host);
171 host->SetJSViewActive(false);
172 isOnShow_ = false;
173 if (onPageHide_) {
174 onPageHide_();
175 }
176 }
177
BuildSharedTransitionMap()178 void PagePattern::BuildSharedTransitionMap()
179 {
180 auto host = GetHost();
181 CHECK_NULL_VOID(host);
182 sharedTransitionMap_.clear();
183 IterativeAddToSharedMap(host, sharedTransitionMap_);
184 }
185
ReloadPage()186 void PagePattern::ReloadPage()
187 {
188 auto host = GetHost();
189 CHECK_NULL_VOID(host);
190 auto customNode = DynamicCast<CustomNodeBase>(host->GetFirstChild());
191 CHECK_NULL_VOID(customNode);
192 customNode->FireReloadFunction(true);
193 }
194
FindPageTransitionEffect(PageTransitionType type)195 RefPtr<PageTransitionEffect> PagePattern::FindPageTransitionEffect(PageTransitionType type)
196 {
197 RefPtr<PageTransitionEffect> result;
198 for (auto iter = pageTransitionEffects_.rbegin(); iter != pageTransitionEffects_.rend(); ++iter) {
199 auto effect = *iter;
200 if (effect->CanFit(type)) {
201 result = effect;
202 break;
203 }
204 }
205 return result;
206 }
207
ClearPageTransitionEffect()208 void PagePattern::ClearPageTransitionEffect()
209 {
210 pageTransitionEffects_.clear();
211 }
212
GetTopTransition() const213 RefPtr<PageTransitionEffect> PagePattern::GetTopTransition() const
214 {
215 return pageTransitionEffects_.empty() ? nullptr : pageTransitionEffects_.back();
216 }
217
AddPageTransition(const RefPtr<PageTransitionEffect> & effect)218 void PagePattern::AddPageTransition(const RefPtr<PageTransitionEffect>& effect)
219 {
220 pageTransitionEffects_.emplace_back(effect);
221 }
222
AddJsAnimator(const std::string & animatorId,const RefPtr<Framework::AnimatorInfo> & animatorInfo)223 void PagePattern::AddJsAnimator(const std::string& animatorId, const RefPtr<Framework::AnimatorInfo>& animatorInfo)
224 {
225 CHECK_NULL_VOID(animatorInfo);
226 auto animator = animatorInfo->GetAnimator();
227 CHECK_NULL_VOID(animator);
228 animator->AttachScheduler(PipelineContext::GetCurrentContext());
229 jsAnimatorMap_[animatorId] = animatorInfo;
230 }
231
GetJsAnimator(const std::string & animatorId)232 RefPtr<Framework::AnimatorInfo> PagePattern::GetJsAnimator(const std::string& animatorId)
233 {
234 auto iter = jsAnimatorMap_.find(animatorId);
235 if (iter != jsAnimatorMap_.end()) {
236 return iter->second;
237 }
238 return nullptr;
239 }
240
SetFirstBuildCallback(std::function<void ()> && buildCallback)241 void PagePattern::SetFirstBuildCallback(std::function<void()>&& buildCallback)
242 {
243 if (isFirstLoad_) {
244 firstBuildCallback_ = std::move(buildCallback);
245 } else if (buildCallback) {
246 buildCallback();
247 }
248 }
249
FirePageTransitionFinish()250 void PagePattern::FirePageTransitionFinish()
251 {
252 if (pageTransitionFinish_) {
253 auto onFinish = *pageTransitionFinish_;
254 pageTransitionFinish_ = nullptr;
255 if (onFinish) {
256 onFinish();
257 }
258 }
259 }
260
StopPageTransition()261 void PagePattern::StopPageTransition()
262 {
263 if (controller_ && !controller_->IsStopped()) {
264 controller_->Finish();
265 return;
266 }
267 FirePageTransitionFinish();
268 }
269
BeforeCreateLayoutWrapper()270 void PagePattern::BeforeCreateLayoutWrapper()
271 {
272 auto pipeline = PipelineContext::GetCurrentContext();
273 CHECK_NULL_VOID(pipeline);
274 // SafeArea already applied to AppBar
275 if (pipeline->GetInstallationFree()) {
276 return;
277 }
278 auto host = GetHost();
279 CHECK_NULL_VOID(host);
280 auto safeArea = pipeline->GetSafeArea();
281 auto props = host->GetLayoutProperty();
282 if (safeArea.IsValid() || props->GetSafeAreaInsets()) {
283 props->UpdateSafeAreaInsets(safeArea);
284 }
285 }
286 } // namespace OHOS::Ace::NG
287