1 /*
2  * Copyright (c) 2024 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 
17 #include "cj_view_context_ffi.h"
18 #include <functional>
19 #include <memory>
20 #include <sstream>
21 #include <utility>
22 #include "cj_lambda.h"
23 #include "base/utils/system_properties.h"
24 #include "base/utils/utils.h"
25 #include "base/log/jank_frame_report.h"
26 #include "bridge/common/utils/engine_helper.h"
27 #include "bridge/common/utils/utils.h"
28 #include "core/common/ace_engine.h"
29 #include "core/components/common/properties/animation_option.h"
30 #include "core/components_ng/base/view_stack_model.h"
31 #include "core/components_ng/base/view_stack_processor.h"
32 #include "core/components_ng/pattern/view_context/view_context_model_ng.h"
33 
34 using namespace OHOS::Ace;
35 using namespace OHOS::FFI;
36 using namespace OHOS::Ace::Framework;
37 
38 namespace OHOS::Ace::Framework {
39 namespace {
40     constexpr uint32_t DEFAULT_DURATION = 1000; // ms
41     constexpr int64_t MICROSEC_TO_MILLISEC = 1000;
42 }
43 
GetFormAnimationTimeInterval(const RefPtr<PipelineBase> & pipelineContext)44 int64_t GetFormAnimationTimeInterval(const RefPtr<PipelineBase>& pipelineContext)
45 {
46     CHECK_NULL_RETURN(pipelineContext, 0);
47     return (GetMicroTickCount() - pipelineContext->GetFormAnimationStartTime()) / MICROSEC_TO_MILLISEC;
48 }
49 
CheckIfSetFormAnimationDuration(const RefPtr<PipelineBase> & pipelineContext,const AnimationOption & option)50 bool CheckIfSetFormAnimationDuration(const RefPtr<PipelineBase>& pipelineContext, const AnimationOption& option)
51 {
52     CHECK_NULL_RETURN(pipelineContext, false);
53     return pipelineContext->IsFormAnimationFinishCallback() && pipelineContext->IsFormRender() &&
54         option.GetDuration() > (DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContext));
55 }
56 
AnimateToForStageMode(const RefPtr<PipelineBase> & pipelineContext,AnimationOption & option,void (* callback)(),const std::function<void ()> & onFinishEvent,bool immediately)57 void AnimateToForStageMode(const RefPtr<PipelineBase>& pipelineContext, AnimationOption& option,
58     void (*callback)(), const std::function<void()>& onFinishEvent, bool immediately)
59 {
60     auto triggerId = Container::CurrentIdSafely();
61     AceEngine::Get().NotifyContainers([triggerId, option](const RefPtr<Container>& container) {
62         auto context = container->GetPipelineContext();
63         if (!context) {
64             // pa container do not have pipeline context.
65             return;
66         }
67 
68         if (!container->IsFRSCardContainer() && !container->WindowIsShow()) {
69             return;
70         }
71         ContainerScope scope(container->GetInstanceId());
72         context->FlushBuild();
73         if (context->GetInstanceId() == triggerId) {
74             return;
75         }
76         context->PrepareOpenImplicitAnimation();
77     });
78     pipelineContext->OpenImplicitAnimation(option, option.GetCurve(), onFinishEvent);
79     auto previousOption = pipelineContext->GetSyncAnimationOption();
80     pipelineContext->SetSyncAnimationOption(option);
81     // Execute the function.
82     auto ffiCallback = CJLambda::Create(callback);
83     ffiCallback();
84     AceEngine::Get().NotifyContainers([triggerId](const RefPtr<Container>& container) {
85         auto context = container->GetPipelineContext();
86         if (!context) {
87             // pa container do not have pipeline context.
88             return;
89         }
90 
91         if (!container->IsFRSCardContainer() && !container->WindowIsShow()) {
92             return;
93         }
94         ContainerScope scope(container->GetInstanceId());
95         context->FlushBuild();
96         if (context->GetInstanceId() == triggerId) {
97             return;
98         }
99         context->PrepareCloseImplicitAnimation();
100     });
101     pipelineContext->CloseImplicitAnimation();
102     pipelineContext->SetSyncAnimationOption(previousOption);
103     if (immediately) {
104         pipelineContext->FlushMessages();
105     } else {
106         pipelineContext->RequestFrame();
107     }
108 }
109 
110 extern "C" {
FfiOHOSAceFrameworkViewContextAnimation(NativeOptionAnimateParam animateOptParam)111 void FfiOHOSAceFrameworkViewContextAnimation(NativeOptionAnimateParam animateOptParam)
112 {
113     ACE_FUNCTION_TRACE();
114     if (ViewStackModel::GetInstance()->CheckTopNodeFirstBuilding()) {
115         // the node sets attribute value for the first time. No animation is generated.
116         return;
117     }
118     AnimationOption animateOpt = AnimationOption();
119     auto container = Container::CurrentSafely();
120     CHECK_NULL_VOID(container);
121     auto pipelineContextBase = container->GetPipelineContext();
122     CHECK_NULL_VOID(pipelineContextBase);
123     if (pipelineContextBase->IsFormAnimationFinishCallback() && pipelineContextBase->IsFormRender() &&
124         GetFormAnimationTimeInterval(pipelineContextBase) > DEFAULT_DURATION) {
125         return;
126     }
127 
128     NativeAnimateParam animateParam;
129     if (animateOptParam.hasValue) {
130         animateParam = animateOptParam.value;
131     } else {
132         ViewContextModel::GetInstance()->closeAnimation(animateOpt, true);
133         return;
134     }
135     ParseCjAnimation(animateParam, animateOpt);
136     if (pipelineContextBase->IsFormAnimationFinishCallback() && pipelineContextBase->IsFormRender() &&
137         animateOpt.GetDuration() > (DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase))) {
138         animateOpt.SetDuration(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase));
139         TAG_LOGW(AceLogTag::ACE_FORM, "[Form animation]  Form animation SetDuration: %{public}lld ms",
140             static_cast<long long>(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase)));
141     }
142 
143     if (SystemProperties::GetRosenBackendEnabled()) {
144         animateOpt.SetAllowRunningAsynchronously(true);
145     }
146     ViewContextModel::GetInstance()->openAnimation(animateOpt);
147     JankFrameReport::GetInstance().ReportJSAnimation();
148 }
149 
FfiOHOSAceFrameworkViewContextAnimationTo(NativeAnimateParam animateParam,void (* callback)(),bool isImmediately)150 void FfiOHOSAceFrameworkViewContextAnimationTo(NativeAnimateParam animateParam, void (*callback)(), bool isImmediately)
151 {
152     ACE_FUNCTION_TRACE();
153     AnimationOption animateOpt;
154     ParseCjAnimation(animateParam, animateOpt);
155     auto container = Container::CurrentSafely();
156     CHECK_NULL_VOID(container);
157     auto pipelineContextBase = container->GetPipelineContext();
158     CHECK_NULL_VOID(pipelineContextBase);
159     if (pipelineContextBase->IsFormAnimationFinishCallback() && pipelineContextBase->IsFormRender() &&
160         GetFormAnimationTimeInterval(pipelineContextBase) > DEFAULT_DURATION) {
161         return;
162     }
163     if (CheckIfSetFormAnimationDuration(pipelineContextBase, animateOpt)) {
164         animateOpt.SetDuration(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase));
165         TAG_LOGW(AceLogTag::ACE_FORM, "[Form animation]  Form animation SetDuration: %{public}lld ms",
166             static_cast<long long>(DEFAULT_DURATION - GetFormAnimationTimeInterval(pipelineContextBase)));
167     }
168     if (SystemProperties::GetRosenBackendEnabled()) {
169         if (pipelineContextBase->IsLayouting()) {
170             TAG_LOGW(AceLogTag::ACE_ANIMATION,
171                 "pipeline is layouting, post animateTo, duration:%{public}d, curve:%{public}s",
172                 animateOpt.GetDuration(), animateOpt.GetCurve() ? animateOpt.GetCurve()->ToString().c_str() : "");
173             pipelineContextBase->GetTaskExecutor()->PostTask(
174                 [id = Container::CurrentIdSafely(), animateOpt, callback, isImmediately]() mutable {
175                     ContainerScope scope(id);
176                     auto container = Container::CurrentSafely();
177                     CHECK_NULL_VOID(container);
178                     auto pipelineContext = container->GetPipelineContext();
179                     CHECK_NULL_VOID(pipelineContext);
180                     AnimateToForStageMode(pipelineContext, animateOpt, callback,
181                                           animateOpt.GetOnFinishEvent(), isImmediately);
182                 },
183                 TaskExecutor::TaskType::UI, "CJAnimationTo");
184             return;
185         }
186         AnimateToForStageMode(pipelineContextBase, animateOpt, callback, animateOpt.GetOnFinishEvent(), isImmediately);
187     } else {
188         pipelineContextBase->FlushBuild();
189         pipelineContextBase->SaveExplicitAnimationOption(animateOpt);
190         // Execute the function.
191         auto ffiCallback = CJLambda::Create(callback);
192         ffiCallback();
193         pipelineContextBase->FlushBuild();
194         pipelineContextBase->CreateExplicitAnimator(animateOpt.GetOnFinishEvent());
195         pipelineContextBase->ClearExplicitAnimationOption();
196     }
197 }
198 }
199 }