1 /*
2 * Copyright (c) 2022-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/manager/shared_overlay/shared_overlay_manager.h"
17
18 #include <sstream>
19
20 #include "base/memory/referenced.h"
21 #include "base/utils/utils.h"
22 #include "core/components_ng/base/frame_node.h"
23 #include "core/components_ng/pattern/stage/page_pattern.h"
24 #include "core/components_ng/property/calc_length.h"
25 #include "core/pipeline/base/element_register.h"
26 #include "core/pipeline/pipeline_base.h"
27
28 namespace OHOS::Ace::NG {
29
30 namespace {
GetSharedEffect(const ShareId & shareId,const WeakPtr<FrameNode> & destWeak,const WeakPtr<FrameNode> & srcWeak)31 RefPtr<SharedTransitionEffect> GetSharedEffect(
32 const ShareId& shareId, const WeakPtr<FrameNode>& destWeak, const WeakPtr<FrameNode>& srcWeak)
33 {
34 auto dest = destWeak.Upgrade();
35 auto src = srcWeak.Upgrade();
36 if ((!src) && (!dest)) {
37 return nullptr;
38 }
39 std::shared_ptr<SharedTransitionOption> options;
40 if (dest && dest->GetRenderContext()->HasSharedTransitionOption()) {
41 options = dest->GetRenderContext()->GetSharedTransitionOption();
42 } else if (src && src->GetRenderContext()->HasSharedTransitionOption()) {
43 options = src->GetRenderContext()->GetSharedTransitionOption();
44 }
45 if (!options) {
46 // use default transition params
47 const int32_t defaultDuration = 1000;
48 options = std::make_shared<SharedTransitionOption>();
49 options->curve = Curves::LINEAR;
50 options->duration = defaultDuration;
51 }
52 if (options->type == SharedTransitionEffectType::SHARED_EFFECT_EXCHANGE && (!src || !dest)) {
53 return nullptr;
54 }
55 return SharedTransitionEffect::GetSharedTransitionEffect(shareId, options);
56 }
57
CreateBlankFrameNode(const RefPtr<FrameNode> & node)58 RefPtr<FrameNode> CreateBlankFrameNode(const RefPtr<FrameNode>& node)
59 {
60 auto pattern = AceType::MakeRefPtr<Pattern>();
61 auto nodeId = ElementRegister::GetInstance()->MakeUniqueId();
62 auto newNode = FrameNode::CreateFrameNode(node->GetTag(), nodeId, pattern);
63 newNode->SetGeometryNode(node->GetGeometryNode()->Clone());
64 auto frameSize = node->GetGeometryNode()->GetFrameSize();
65 // set size so the node will keep its size
66 newNode->GetLayoutProperty()->UpdateUserDefinedIdealSize(
67 CalcSize(CalcLength(frameSize.Width()), CalcLength(frameSize.Height())));
68 return newNode;
69 }
70
ReplaceFrameNode(const RefPtr<FrameNode> & node,const RefPtr<FrameNode> & newNode)71 bool ReplaceFrameNode(const RefPtr<FrameNode>& node, const RefPtr<FrameNode>& newNode)
72 {
73 auto parent = node->GetParent();
74 CHECK_NULL_RETURN(parent, false);
75 parent->ReplaceChild(node, newNode);
76 parent->RebuildRenderContextTree();
77 return true;
78 }
79
80 } // namespace
81
StartSharedTransition(const RefPtr<FrameNode> & pageSrc,const RefPtr<FrameNode> & pageDest)82 void SharedOverlayManager::StartSharedTransition(const RefPtr<FrameNode>& pageSrc, const RefPtr<FrameNode>& pageDest)
83 {
84 CHECK_NULL_VOID(pageSrc);
85 CHECK_NULL_VOID(pageDest);
86 auto patternSrc = pageSrc->GetPattern<PagePattern>();
87 CHECK_NULL_VOID(patternSrc);
88 auto patternDest = pageDest->GetPattern<PagePattern>();
89 CHECK_NULL_VOID(patternDest);
90 patternSrc->BuildSharedTransitionMap();
91 patternDest->BuildSharedTransitionMap();
92 PrepareSharedTransition(pageSrc, pageDest);
93 auto pipeline = PipelineBase::GetCurrentContext();
94 for (const auto& effect : effects_) {
95 auto controller = effect->GetController();
96 if (controller) {
97 TAG_LOGI(AceLogTag::ACE_ANIMATION, "Animation effect start, shareId = %{public}s, id = %{public}d",
98 effect->GetShareId().c_str(), effect->GetController()->GetId());
99 controller->SetFillMode(FillMode::FORWARDS);
100 controller->SetAllowRunningAsynchronously(true);
101 controller->AttachScheduler(pipeline);
102 controller->Forward();
103 }
104 }
105 }
106
PrepareSharedTransition(const RefPtr<FrameNode> & pageSrc,const RefPtr<FrameNode> & pageDest)107 void SharedOverlayManager::PrepareSharedTransition(const RefPtr<FrameNode>& pageSrc, const RefPtr<FrameNode>& pageDest)
108 {
109 ClearAllEffects();
110 auto patternDest = pageDest->GetPattern<PagePattern>();
111 CHECK_NULL_VOID(patternDest);
112 auto patternSrc = pageSrc->GetPattern<PagePattern>();
113 CHECK_NULL_VOID(patternSrc);
114 pageOffset_ = pageDest->GetRenderContext()->GetPaintRectWithoutTransform().GetOffset();
115 const auto& srcMap = patternSrc->GetSharedTransitionMap();
116 const auto& destMap = patternDest->GetSharedTransitionMap();
117 std::list<RefPtr<SharedTransitionEffect>> effects;
118 std::list<RefPtr<SharedTransitionEffect>> anchorEffects;
119
120 // find out all exchange effect or static effect in dest page
121 for (const auto& item : destMap) {
122 const auto& shareId = item.first;
123 const auto& destWeak = item.second;
124 auto srcSharedIter = srcMap.find(shareId);
125 WeakPtr<FrameNode> srcWeak;
126 if (srcSharedIter != srcMap.end()) {
127 srcWeak = srcSharedIter->second;
128 }
129 RefPtr<SharedTransitionEffect> effect = GetSharedEffect(shareId, destWeak, srcWeak);
130 if (!effect) {
131 continue;
132 }
133 effect->SetSharedNode(srcWeak, destWeak);
134 if (effect->GetType() == SharedTransitionEffectType::SHARED_EFFECT_STATIC) {
135 anchorEffects.emplace_back(effect);
136 } else {
137 effects.emplace_back(effect);
138 }
139 }
140 // find out all static effect in source page
141 for (const auto& item : srcMap) {
142 const auto& sharedId = item.first;
143 const auto& sourceWeak = item.second;
144 RefPtr<SharedTransitionEffect> effect = GetSharedEffect(sharedId, nullptr, sourceWeak);
145 if (!effect || effect->GetType() != SharedTransitionEffectType::SHARED_EFFECT_STATIC) {
146 continue;
147 }
148 auto destSharedIter = destMap.find(sharedId);
149 if (destSharedIter != destMap.end()) {
150 // src is static, it has been added to effects in previous for loop
151 continue;
152 }
153 effect->SetSharedNode(sourceWeak, nullptr);
154 anchorEffects.emplace_back(effect);
155 }
156 TAG_LOGI(AceLogTag::ACE_ANIMATION,
157 "Animation prepare transition, effectSize:%{public}zu, anchorEffectSize:%{public}zu, srcMap "
158 "size:%{public}zu, "
159 "destMap size:%{public}zu",
160 effects.size(), anchorEffects.size(), srcMap.size(), destMap.size());
161 // prepare each sharedTransition effect
162 CheckAndPrepareTransition(effects, effects_);
163 CheckAndPrepareTransition(anchorEffects, effects_);
164 if (!effects_.empty()) {
165 sharedManager_->RebuildRenderContextTree();
166 }
167 }
168
CheckAndPrepareTransition(std::list<RefPtr<SharedTransitionEffect>> & effects,std::list<RefPtr<SharedTransitionEffect>> & effectiveEffects)169 void SharedOverlayManager::CheckAndPrepareTransition(
170 std::list<RefPtr<SharedTransitionEffect>>& effects, std::list<RefPtr<SharedTransitionEffect>>& effectiveEffects)
171 {
172 for (auto& effect : effects) {
173 const auto& shareId = effect->GetShareId();
174 if (!effect->Allow()) {
175 TAG_LOGI(AceLogTag::ACE_ANIMATION, "Shared transition not allowed, share id: %{public}s", shareId.c_str());
176 continue;
177 }
178 if (!PrepareEachTransition(effect)) {
179 TAG_LOGI(
180 AceLogTag::ACE_ANIMATION, "Prepare shared transition failed. share id: %{public}s", shareId.c_str());
181 continue;
182 }
183 if (!CheckIn(effect)) {
184 TAG_LOGI(
185 AceLogTag::ACE_ANIMATION, "CheckIn shared transition failed. share id: %{public}s", shareId.c_str());
186 continue;
187 }
188 effectiveEffects.emplace_back(effect);
189 }
190 }
191
PrepareEachTransition(const RefPtr<SharedTransitionEffect> & effect)192 bool SharedOverlayManager::PrepareEachTransition(const RefPtr<SharedTransitionEffect>& effect)
193 {
194 if (!effect->CreateAnimation()) {
195 return false;
196 }
197 if (!effect->ApplyAnimation()) {
198 return false;
199 }
200 return true;
201 }
202
ClearAllEffects()203 void SharedOverlayManager::ClearAllEffects()
204 {
205 while (!effects_.empty()) {
206 auto& effect = effects_.front();
207 effect->StopPlayingEffect();
208 effects_.pop_front();
209 }
210 }
211
CheckIn(const RefPtr<SharedTransitionEffect> & effect)212 bool SharedOverlayManager::CheckIn(const RefPtr<SharedTransitionEffect>& effect)
213 {
214 // Check-in
215 if (!AboardShuttle(effect)) {
216 return false;
217 }
218 const auto& controller = effect->GetController();
219 CHECK_NULL_RETURN(controller, false);
220 // Arrange Return Shuttle
221 controller->AddStopListener([weak = WeakClaim(this), effectWeak = WeakPtr<SharedTransitionEffect>(effect)]() {
222 auto shared = weak.Upgrade();
223 CHECK_NULL_VOID(shared);
224 auto effect = effectWeak.Upgrade();
225 shared->GetOffShuttle(effect);
226 });
227 return true;
228 }
229
PassengerAboard(const RefPtr<SharedTransitionEffect> & effect,const RefPtr<FrameNode> & passenger)230 void SharedOverlayManager::PassengerAboard(
231 const RefPtr<SharedTransitionEffect>& effect, const RefPtr<FrameNode>& passenger)
232 {
233 auto ticket = passenger->GetPaintRectOffsetToPage();
234 // Get offset relative to stage(or overlay), for safeArea
235 ticket += pageOffset_;
236 if (SystemProperties::GetDebugEnabled()) {
237 TAG_LOGI(AceLogTag::ACE_ANIMATION, "Transition passenger offset is %{public}s, id = %{public}s",
238 ticket.ToString().c_str(), effect->GetShareId().c_str());
239 }
240 auto initialPosition = passenger->GetRenderContext()->GetPosition();
241 // save initialFrameOffset for static type sharedTransition
242 auto initialFrameOffset = passenger->GetGeometryNode()->GetFrameOffset();
243 const auto& initialMarginPtr = passenger->GetLayoutProperty()->GetMarginProperty();
244 auto initialMargin = initialMarginPtr ? std::make_optional<MarginProperty>(*initialMarginPtr) : std::nullopt;
245 auto zIndex = passenger->GetRenderContext()->GetZIndex();
246 effect->SetPassengerInitZIndex(zIndex);
247 effect->SetPassengerInitPos(initialPosition);
248 effect->SetPassengerInitFrameOffset(initialFrameOffset);
249 effect->SetPassengerInitMargin(initialMargin);
250 bool isPassengerCurrentFocused = false;
251 auto passengerFocusHub = passenger->GetFocusHub();
252 if (passengerFocusHub) {
253 isPassengerCurrentFocused = passengerFocusHub->IsCurrentFocus();
254 }
255 effect->SetPassengerCurrentFocused(isPassengerCurrentFocused);
256 auto passengerHolder = CreateBlankFrameNode(passenger);
257 passengerHolder->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
258 ReplaceFrameNode(passenger, passengerHolder);
259 effect->SetPassengerHolder(passengerHolder);
260 sharedManager_->AddChild(passenger);
261 auto offset = OffsetT<Dimension>(Dimension(ticket.GetX()), Dimension(ticket.GetY()));
262 if (initialMargin) {
263 passenger->GetLayoutProperty()->UpdateMargin(MarginProperty());
264 passenger->GetLayoutProperty()->CleanDirty();
265 }
266 passenger->GetRenderContext()->UpdateZIndex(effect->GetZIndex());
267 passenger->GetRenderContext()->UpdatePosition(offset);
268 passenger->GetRenderContext()->OnModifyDone();
269 passenger->GetEventHub<EventHub>()->SetEnabledInternal(false);
270 }
271
AboardShuttle(const RefPtr<SharedTransitionEffect> & effect)272 bool SharedOverlayManager::AboardShuttle(const RefPtr<SharedTransitionEffect>& effect)
273 {
274 auto passenger = effect->GetPassengerNode().Upgrade();
275 if (!passenger) {
276 return false;
277 }
278 if (effect->GetType() == SharedTransitionEffectType::SHARED_EFFECT_EXCHANGE) {
279 // passenger is src
280 auto dest = effect->GetDestSharedNode().Upgrade();
281 if (!dest) {
282 return false;
283 }
284 PassengerAboard(effect, passenger);
285 auto exchangeEffect = AceType::DynamicCast<SharedTransitionExchange>(effect);
286 auto destVisible = dest->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE);
287 exchangeEffect->SetInitialDestVisible(destVisible);
288 exchangeEffect->SetVisibleToDest(VisibleType::INVISIBLE);
289 } else {
290 PassengerAboard(effect, passenger);
291 }
292 return true;
293 }
294
GetOffShuttle(const RefPtr<SharedTransitionEffect> & effect)295 void SharedOverlayManager::GetOffShuttle(const RefPtr<SharedTransitionEffect>& effect)
296 {
297 CHECK_NULL_VOID(effect);
298 TAG_LOGI(AceLogTag::ACE_ANIMATION, "Animation off shuttle, id: %{public}s", effect->GetShareId().c_str());
299 auto passenger = effect->GetPassengerNode().Upgrade();
300 CHECK_NULL_VOID(passenger);
301 sharedManager_->RemoveChild(passenger);
302 sharedManager_->RebuildRenderContextTree();
303 auto passengerHolder = effect->GetPassengerHolder().Upgrade();
304 if (passengerHolder) {
305 // restore the position and zIndex of passenger frameNode
306 passenger->GetGeometryNode()->SetFrameOffset(effect->GetPassengerInitFrameOffset());
307 if (effect->GetPassengerInitPos().has_value()) {
308 passenger->GetRenderContext()->UpdatePosition(effect->GetPassengerInitPos().value());
309 } else {
310 passenger->GetRenderContext()->ResetPositionProperty();
311 passenger->GetRenderContext()->OnPositionUpdate(OffsetT<Dimension>());
312 }
313 if (effect->GetPassengerInitZIndex().has_value()) {
314 passenger->GetRenderContext()->UpdateZIndex(effect->GetPassengerInitZIndex().value());
315 } else {
316 passenger->GetRenderContext()->ResetZIndex();
317 passenger->GetRenderContext()->OnZIndexUpdate(0);
318 }
319 if (effect->GetPassengerInitMargin().has_value()) {
320 passenger->GetLayoutProperty()->UpdateMargin(effect->GetPassengerInitMargin().value());
321 passenger->MarkDirtyNode();
322 }
323 // restore initialFrameOffset for static type sharedTransition, because it may not layout again
324 ReplaceFrameNode(passengerHolder, passenger);
325 passenger->GetEventHub<EventHub>()->RestoreEnabled();
326 auto isPassengerCurrentFocused = effect->GetPassengerCurrentFocused();
327 if (isPassengerCurrentFocused) {
328 auto passengerFocusHub = passenger->GetFocusHub();
329 if (passengerFocusHub) {
330 passengerFocusHub->RequestFocusImmediately();
331 }
332 }
333 // The callback is to restore the parameters used by passenger in the animation
334 effect->PerformFinishCallback();
335 passenger->GetRenderContext()->OnModifyDone();
336 }
337 if (effect->GetType() == SharedTransitionEffectType::SHARED_EFFECT_EXCHANGE) {
338 // restore the visibility of dest frameNode
339 auto exchangeEffect = AceType::DynamicCast<SharedTransitionExchange>(effect);
340 auto destVisible = exchangeEffect->GetInitialDestVisible();
341 exchangeEffect->SetVisibleToDest(destVisible);
342 exchangeEffect->DestRequestDefaultFocus();
343 }
344 }
345
OnBackPressed()346 bool SharedOverlayManager::OnBackPressed()
347 {
348 bool inSharedTransition = false;
349 for (const auto& effect : effects_) {
350 if (effect->GetController()->IsRunning()) {
351 inSharedTransition = true;
352 break;
353 }
354 }
355 return inSharedTransition;
356 }
357
StopSharedTransition()358 void SharedOverlayManager::StopSharedTransition()
359 {
360 for (const auto& effect : effects_) {
361 auto controller = effect->GetController();
362 if (controller->IsRunning()) {
363 // When two new pages switch, let controller finishes, so passenger can go back to the original page.
364 controller->Finish();
365 }
366 }
367 effects_.clear();
368 }
369
370 } // namespace OHOS::Ace::NG
371