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_LOGD(AceLogTag::ACE_ANIMATION, "sharedTransition starts, 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     if (effects.size()) {
157         TAG_LOGD(AceLogTag::ACE_ANIMATION,
158             "prepare sharedTransition, effectSize:%{public}zu, anchorEffectSize:%{public}zu, srcMap "
159             "size:%{public}zu, destMap size:%{public}zu",
160             effects.size(), anchorEffects.size(), srcMap.size(), destMap.size());
161     }
162     // prepare each sharedTransition effect
163     CheckAndPrepareTransition(effects, effects_);
164     CheckAndPrepareTransition(anchorEffects, effects_);
165     if (!effects_.empty()) {
166         sharedManager_->RebuildRenderContextTree();
167     }
168 }
169 
CheckAndPrepareTransition(std::list<RefPtr<SharedTransitionEffect>> & effects,std::list<RefPtr<SharedTransitionEffect>> & effectiveEffects)170 void SharedOverlayManager::CheckAndPrepareTransition(
171     std::list<RefPtr<SharedTransitionEffect>>& effects, std::list<RefPtr<SharedTransitionEffect>>& effectiveEffects)
172 {
173     for (auto& effect : effects) {
174         const auto& shareId = effect->GetShareId();
175         if (!effect->Allow()) {
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             continue;
185         }
186         effectiveEffects.emplace_back(effect);
187     }
188 }
189 
PrepareEachTransition(const RefPtr<SharedTransitionEffect> & effect)190 bool SharedOverlayManager::PrepareEachTransition(const RefPtr<SharedTransitionEffect>& effect)
191 {
192     if (!effect->CreateAnimation()) {
193         return false;
194     }
195     if (!effect->ApplyAnimation()) {
196         return false;
197     }
198     return true;
199 }
200 
ClearAllEffects()201 void SharedOverlayManager::ClearAllEffects()
202 {
203     while (!effects_.empty()) {
204         auto& effect = effects_.front();
205         effect->StopPlayingEffect();
206         effects_.pop_front();
207     }
208 }
209 
CheckIn(const RefPtr<SharedTransitionEffect> & effect)210 bool SharedOverlayManager::CheckIn(const RefPtr<SharedTransitionEffect>& effect)
211 {
212     // Check-in
213     if (!AboardShuttle(effect)) {
214         return false;
215     }
216     const auto& controller = effect->GetController();
217     CHECK_NULL_RETURN(controller, false);
218     // Arrange Return Shuttle
219     controller->AddStopListener([weak = WeakClaim(this), effectWeak = WeakPtr<SharedTransitionEffect>(effect)]() {
220         auto shared = weak.Upgrade();
221         CHECK_NULL_VOID(shared);
222         auto effect = effectWeak.Upgrade();
223         shared->GetOffShuttle(effect);
224     });
225     return true;
226 }
227 
PassengerAboard(const RefPtr<SharedTransitionEffect> & effect,const RefPtr<FrameNode> & passenger)228 void SharedOverlayManager::PassengerAboard(
229     const RefPtr<SharedTransitionEffect>& effect, const RefPtr<FrameNode>& passenger)
230 {
231     auto ticket = passenger->GetPaintRectOffsetToPage();
232     // Get offset relative to stage(or overlay), for safeArea
233     ticket += pageOffset_;
234     TAG_LOGD(AceLogTag::ACE_ANIMATION, "Transition passenger offset is %{public}s, id = %{public}s",
235         ticket.ToString().c_str(), effect->GetShareId().c_str());
236     auto initialPosition = passenger->GetRenderContext()->GetPosition();
237     // save initialFrameOffset for static type sharedTransition
238     auto initialFrameOffset = passenger->GetGeometryNode()->GetFrameOffset();
239     const auto& initialMarginPtr = passenger->GetLayoutProperty()->GetMarginProperty();
240     auto initialMargin = initialMarginPtr ? std::make_optional<MarginProperty>(*initialMarginPtr) : std::nullopt;
241     auto zIndex = passenger->GetRenderContext()->GetZIndex();
242     effect->SetPassengerInitZIndex(zIndex);
243     effect->SetPassengerInitPos(initialPosition);
244     effect->SetPassengerInitFrameOffset(initialFrameOffset);
245     effect->SetPassengerInitMargin(initialMargin);
246     bool isPassengerCurrentFocused = false;
247     auto passengerFocusHub = passenger->GetFocusHub();
248     if (passengerFocusHub) {
249         isPassengerCurrentFocused = passengerFocusHub->IsCurrentFocus();
250     }
251     effect->SetPassengerCurrentFocused(isPassengerCurrentFocused);
252     auto passengerHolder = CreateBlankFrameNode(passenger);
253     passengerHolder->GetLayoutProperty()->UpdateVisibility(VisibleType::INVISIBLE);
254     ReplaceFrameNode(passenger, passengerHolder);
255     effect->SetPassengerHolder(passengerHolder);
256     sharedManager_->AddChild(passenger);
257     auto offset = OffsetT<Dimension>(Dimension(ticket.GetX()), Dimension(ticket.GetY()));
258     if (initialMargin) {
259         passenger->GetLayoutProperty()->UpdateMargin(MarginProperty());
260         passenger->GetLayoutProperty()->CleanDirty();
261     }
262     passenger->GetRenderContext()->UpdateZIndex(effect->GetZIndex());
263     passenger->GetRenderContext()->UpdatePosition(offset);
264     passenger->GetRenderContext()->OnModifyDone();
265     passenger->GetEventHub<EventHub>()->SetEnabledInternal(false);
266 }
267 
AboardShuttle(const RefPtr<SharedTransitionEffect> & effect)268 bool SharedOverlayManager::AboardShuttle(const RefPtr<SharedTransitionEffect>& effect)
269 {
270     auto passenger = effect->GetPassengerNode().Upgrade();
271     if (!passenger) {
272         return false;
273     }
274     if (effect->GetType() == SharedTransitionEffectType::SHARED_EFFECT_EXCHANGE) {
275         // passenger is src
276         auto dest = effect->GetDestSharedNode().Upgrade();
277         if (!dest) {
278             return false;
279         }
280         PassengerAboard(effect, passenger);
281         auto exchangeEffect = AceType::DynamicCast<SharedTransitionExchange>(effect);
282         auto destVisible = dest->GetLayoutProperty()->GetVisibilityValue(VisibleType::VISIBLE);
283         exchangeEffect->SetInitialDestVisible(destVisible);
284         exchangeEffect->SetVisibleToDest(VisibleType::INVISIBLE);
285     } else {
286         PassengerAboard(effect, passenger);
287     }
288     return true;
289 }
290 
GetOffShuttle(const RefPtr<SharedTransitionEffect> & effect)291 void SharedOverlayManager::GetOffShuttle(const RefPtr<SharedTransitionEffect>& effect)
292 {
293     CHECK_NULL_VOID(effect);
294     TAG_LOGD(AceLogTag::ACE_ANIMATION, "Animation off shuttle, id: %{public}s", effect->GetShareId().c_str());
295     auto passenger = effect->GetPassengerNode().Upgrade();
296     CHECK_NULL_VOID(passenger);
297     sharedManager_->RemoveChild(passenger);
298     sharedManager_->RebuildRenderContextTree();
299     auto passengerHolder = effect->GetPassengerHolder().Upgrade();
300     if (passengerHolder) {
301         // restore the position and zIndex of passenger frameNode
302         passenger->GetGeometryNode()->SetFrameOffset(effect->GetPassengerInitFrameOffset());
303         if (effect->GetPassengerInitPos().has_value()) {
304             passenger->GetRenderContext()->UpdatePosition(effect->GetPassengerInitPos().value());
305         } else {
306             passenger->GetRenderContext()->ResetPositionProperty();
307             passenger->GetRenderContext()->OnPositionUpdate(OffsetT<Dimension>());
308         }
309         if (effect->GetPassengerInitZIndex().has_value()) {
310             passenger->GetRenderContext()->UpdateZIndex(effect->GetPassengerInitZIndex().value());
311         } else {
312             passenger->GetRenderContext()->ResetZIndex();
313             passenger->GetRenderContext()->OnZIndexUpdate(0);
314         }
315         if (effect->GetPassengerInitMargin().has_value()) {
316             passenger->GetLayoutProperty()->UpdateMargin(effect->GetPassengerInitMargin().value());
317             passenger->MarkDirtyNode();
318         }
319         // restore initialFrameOffset for static type sharedTransition, because it may not layout again
320         ReplaceFrameNode(passengerHolder, passenger);
321         passenger->GetEventHub<EventHub>()->RestoreEnabled();
322         auto isPassengerCurrentFocused = effect->GetPassengerCurrentFocused();
323         if (isPassengerCurrentFocused) {
324             auto passengerFocusHub = passenger->GetFocusHub();
325             if (passengerFocusHub) {
326                 passengerFocusHub->RequestFocusImmediately();
327             }
328         }
329         // The callback is to restore the parameters used by passenger in the animation
330         effect->PerformFinishCallback();
331         passenger->GetRenderContext()->OnModifyDone();
332     }
333     if (effect->GetType() == SharedTransitionEffectType::SHARED_EFFECT_EXCHANGE) {
334         // restore the visibility of dest frameNode
335         auto exchangeEffect = AceType::DynamicCast<SharedTransitionExchange>(effect);
336         auto destVisible = exchangeEffect->GetInitialDestVisible();
337         exchangeEffect->SetVisibleToDest(destVisible);
338         exchangeEffect->DestRequestDefaultFocus();
339     }
340 }
341 
OnBackPressed()342 bool SharedOverlayManager::OnBackPressed()
343 {
344     bool inSharedTransition = false;
345     for (const auto& effect : effects_) {
346         if (effect->GetController()->IsRunning()) {
347             inSharedTransition = true;
348             break;
349         }
350     }
351     return inSharedTransition;
352 }
353 
StopSharedTransition()354 void SharedOverlayManager::StopSharedTransition()
355 {
356     for (const auto& effect : effects_) {
357         auto controller = effect->GetController();
358         if (controller->IsRunning()) {
359             // When two new pages switch, let controller finishes, so passenger can go back to the original page.
360             controller->Finish();
361         }
362     }
363     effects_.clear();
364 }
365 
366 } // namespace OHOS::Ace::NG
367