• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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