1 /*
2  * Copyright (c) 2021 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/image/image_animator_element.h"
17 
18 namespace OHOS::Ace {
19 
~ImageAnimatorElement()20 ImageAnimatorElement::~ImageAnimatorElement()
21 {
22     auto pageElement = pageElement_.Upgrade();
23     if (pageElement && callbackId_ >= 0) {
24         pageElement->CancelHiddenCallback(callbackId_);
25     }
26 }
27 
Update()28 void Ace::ImageAnimatorElement::Update()
29 {
30     const auto imageAnimatorComponent = AceType::DynamicCast<ImageAnimatorComponent>(component_);
31     if (!imageAnimatorComponent) {
32         LOGE("ImageAnimator element update failed. imageAnimatorComponent is null.");
33         return;
34     }
35     SetElementId(imageAnimatorComponent->GetElementId());
36 
37     if (!animator_) {
38         animator_ = CREATE_ANIMATOR();
39     }
40     UpdateCallbackAndFunc(imageAnimatorComponent);
41     if (!animator_->HasScheduler()) {
42         animator_->AttachScheduler(context_);
43     }
44     animator_->SetFillMode(imageAnimatorComponent->GetFillMode());
45     animator_->SetIteration(imageAnimatorComponent->GetIteration());
46     status_ = imageAnimatorComponent->GetStatus();
47     preDecode_ = imageAnimatorComponent->GetPreDecode();
48     duration_ = imageAnimatorComponent->GetDuration();
49     isReverse_ = imageAnimatorComponent->GetIsReverse();
50     images_ = imageAnimatorComponent->GetImageProperties();
51     isFixedSize_ = imageAnimatorComponent->GetIsFixedSize();
52     border_ = imageAnimatorComponent->GetBorder();
53     fillMode_ = imageAnimatorComponent->GetFillMode();
54     iteration_ = imageAnimatorComponent->GetIteration();
55     UpdateFilterImages();
56 
57     if (!pageElement_.Invalid()) {
58         return;
59     }
60 
61     auto pageElement = GetPageElement();
62     if (!pageElement) {
63         return;
64     }
65     pageElement_ = pageElement;
66     callbackId_ = pageElement->RegisterHiddenCallback([weak = AceType::WeakClaim(this)](bool hidden) {
67         auto element = weak.Upgrade();
68         if (!element || !element->animator_) {
69             return;
70         }
71 
72         if (hidden) {
73             if (element->animator_->GetStatus() == Animator::Status::RUNNING) {
74                 element->animator_->Pause();
75                 element->isPaused_ = true;
76             }
77         } else {
78             if (element->isPaused_) {
79                 if (element->isReverse_) {
80                     element->animator_->Backward();
81                 } else {
82                     element->animator_->Forward();
83                 }
84                 element->isPaused_ = false;
85             }
86         }
87     });
88 }
89 
PerformBuild()90 void ImageAnimatorElement::PerformBuild()
91 {
92     int32_t size = static_cast<int32_t>(images_.size());
93     if (size <= 0) {
94         LOGE("image size is less than 0.");
95         return;
96     }
97     if (children_.empty()) {
98         // first time to set image child.
99         childComponent_ = BuildChild();
100         ComposedElement::PerformBuild();
101     }
102     if (preDecode_ > 1) {
103         auto boxComponent = DynamicCast<BoxComponent>(childComponent_);
104         UpdatePreLoadImages(boxComponent);
105     }
106     CreatePictureAnimation(size);
107     if (!animator_) {
108         LOGE("animator is null, need to get animator first.");
109         return;
110     }
111     // update duration after a loop of animation.
112     if (!isSetDuration_) {
113         durationTotal_ > 0 ? animator_->SetDuration(durationTotal_) : animator_->SetDuration(duration_);
114         isSetDuration_ = true;
115     }
116     animator_->ClearInterpolators();
117     animator_->AddInterpolator(pictureAnimation_);
118     animator_->RemoveRepeatListener(repeatCallbackId_);
119     repeatCallbackId_ = static_cast<int64_t>(animator_->AddRepeatListener([weak = WeakClaim(this)]() {
120         auto imageAnimator = weak.Upgrade();
121         if (!imageAnimator) {
122             return;
123         }
124         if (imageAnimator->durationTotal_ > 0) {
125             imageAnimator->animator_->SetDuration(imageAnimator->durationTotal_);
126         } else {
127             imageAnimator->animator_->SetDuration(imageAnimator->duration_);
128         }
129     }));
130     animator_->RemoveStopListener(stopCallbackId_);
131     stopCallbackId_ = static_cast<int64_t>(animator_->AddStopListener([weak = WeakClaim(this)]() {
132         auto imageAnimator = weak.Upgrade();
133         if (imageAnimator) {
134             imageAnimator->isSetDuration_ = false;
135         }
136     }));
137 
138     // for declarative frontend.
139     if (context_.Upgrade() && context_.Upgrade()->GetIsDeclarative()) {
140         if (status_ == Animator::Status::IDLE) {
141             CallAnimatorMethod(CANCEL);
142         } else if (status_ == Animator::Status::PAUSED) {
143             CallAnimatorMethod(PAUSE);
144         } else if (status_ == Animator::Status::STOPPED) {
145             CallAnimatorMethod(STOP);
146         } else {
147             CallAnimatorMethod(START);
148         }
149         return;
150     }
151     isReverse_ ? animator_->Backward() : animator_->Forward();
152 }
153 
BuildChild()154 RefPtr<Component> ImageAnimatorElement::BuildChild()
155 {
156     uint32_t size = images_.size();
157     if (size == 0) {
158         LOGE("image size is 0.");
159         return nullptr;
160     }
161     auto boxComponent = AceType::MakeRefPtr<BoxComponent>();
162     boxComponent->SetFlex(BoxFlex::FLEX_XY);
163     boxComponent->SetAlignment(Alignment::TOP_LEFT);
164     ImageProperties childImage;
165     if (durationTotal_ > 0) {
166         childImage = (isReverse_ ? filterImages_.back() : filterImages_.front());
167     } else {
168         childImage = (isReverse_ ? images_.back() : images_.front());
169     }
170     auto imageComponent = AceType::MakeRefPtr<ImageComponent>(childImage.src);
171     if (!isFixedSize_) {
172         UpdateImageSize(childImage, imageComponent);
173     }
174     if (!childImage.src.empty()) {
175         imageComponent->SetBorder(border_);
176         imageComponent->SetFitMaxSize(true);
177     }
178     boxComponent->SetChild(imageComponent);
179     return boxComponent;
180 }
181 
UpdatePreLoadImages(const RefPtr<BoxComponent> & box)182 void ImageAnimatorElement::UpdatePreLoadImages(const RefPtr<BoxComponent>& box)
183 {
184     if (!box) {
185         LOGE("boxComponent is null.");
186         return;
187     }
188     int32_t size = static_cast<int32_t>(images_.size());
189     for (int32_t idx = 0; (idx < preDecode_) && (idx < size); idx++) {
190         auto imageComponent = DynamicCast<ImageComponent>(box->GetChild());
191         if (!imageComponent) {
192             LOGE("imageComponent is null.");
193             return;
194         }
195         ImageProperties childImage = images_[idx];
196         if (!childImage.src.empty()) {
197             imageComponent->SetSrc(childImage.src);
198         }
199     }
200 }
201 
CreatePictureAnimation(int32_t size)202 void ImageAnimatorElement::CreatePictureAnimation(int32_t size)
203 {
204     if (!pictureAnimation_) {
205         pictureAnimation_ = MakeRefPtr<PictureAnimation<int32_t>>();
206     }
207 
208     pictureAnimation_->ClearListeners();
209     pictureAnimation_->ClearPictures();
210     if (durationTotal_ > 0) {
211         int32_t filterImagesSize = static_cast<int32_t>(filterImages_.size());
212         for (int32_t index = 0; index < filterImagesSize; ++index) {
213             int32_t imageDuration = filterImages_[index].duration;
214             pictureAnimation_->AddPicture((float)imageDuration / durationTotal_, index);
215         }
216     } else {
217         for (int32_t index = 0; index < size; ++index) {
218             pictureAnimation_->AddPicture(NORMALIZED_DURATION_MAX / size, index);
219         }
220     }
221     pictureAnimation_->AddListener([weak = WeakClaim(this)](const int32_t index) {
222         auto imageAnimator = weak.Upgrade();
223         if (imageAnimator) {
224             imageAnimator->PlayImageAnimator(index);
225         }
226     });
227 }
228 
UpdateFilterImages()229 void ImageAnimatorElement::UpdateFilterImages()
230 {
231     filterImages_.clear();
232     durationTotal_ = 0;
233     for (auto& childImage : images_) {
234         if (!childImage.src.empty() && childImage.duration > 0) {
235             durationTotal_ += childImage.duration;
236             filterImages_.emplace_back(childImage);
237         }
238     }
239 }
240 
PlayImageAnimator(int32_t index)241 void ImageAnimatorElement::PlayImageAnimator(int32_t index)
242 {
243     auto boxComponent = DynamicCast<BoxComponent>(childComponent_);
244     if (!boxComponent) {
245         LOGE("child boxComponent is null.");
246         return;
247     }
248     auto imageComponent = DynamicCast<ImageComponent>(boxComponent->GetChild());
249     if (!imageComponent) {
250         LOGE("imageComponent is null.");
251         return;
252     }
253     ImageProperties childImage;
254     if (durationTotal_ > 0) {
255         childImage = filterImages_[index];
256     } else {
257         childImage = images_[index];
258     }
259     if (!isFixedSize_) {
260         UpdateImageSize(childImage, imageComponent);
261         isResetBox_ = false;
262     } else {
263         if (!isResetBox_) {
264             imageComponent->SetLeft(Dimension());
265             imageComponent->SetTop(Dimension());
266             // Follows the size of the parent component
267             imageComponent->SetWidth(-1.0);
268             imageComponent->SetHeight(-1.0);
269             isResetBox_ = true;
270         }
271     }
272     if (!childImage.src.empty()) {
273         imageComponent->SetSrc(childImage.src);
274     }
275     UpdateChild(GetFirstChild(), boxComponent);
276 }
277 
UpdateImageSize(ImageProperties & imageProperties,const RefPtr<ImageComponent> & image)278 void ImageAnimatorElement::UpdateImageSize(ImageProperties& imageProperties, const RefPtr<ImageComponent>& image)
279 {
280     image->SetLeft(imageProperties.left);
281     image->SetTop(imageProperties.top);
282     if (imageProperties.width.IsValid()) {
283         image->SetWidth(imageProperties.width);
284     }
285     if (imageProperties.height.IsValid()) {
286         image->SetHeight(imageProperties.height);
287     }
288 }
289 
CallAnimatorMethod(const std::string & method)290 void ImageAnimatorElement::CallAnimatorMethod(const std::string& method)
291 {
292     if (!animator_) {
293         LOGE("CallAnimatorMethod failed, animator is null.");
294         return;
295     }
296     if (method == START) {
297         isReverse_ ? animator_->Backward() : animator_->Forward();
298     } else if (method == PAUSE) {
299         animator_->Pause();
300     } else if (method == STOP) {
301         animator_->Finish();
302     } else if (method == RESUME) {
303         animator_->Resume();
304     } else if (method == CANCEL) {
305         animator_->Cancel();
306     } else {
307         LOGE("Unsupported method name : %s", method.c_str());
308     }
309 }
310 
UpdateCallbackAndFunc(const RefPtr<ImageAnimatorComponent> & imageAnimatorComponent)311 void ImageAnimatorElement::UpdateCallbackAndFunc(const RefPtr<ImageAnimatorComponent>& imageAnimatorComponent)
312 {
313     const auto& imageAnimatorController = imageAnimatorComponent->GetImageAnimatorController();
314     if (!imageAnimatorController) {
315         LOGE("UpdateCallbackAndFunc failed, imageAnimatorController is null.");
316         return;
317     }
318 
319     // start / stop / pause / resume method.
320     imageAnimatorController->SetAnimationFunc([weak = WeakClaim(this)](const std::string& method) {
321         auto element = weak.Upgrade();
322         if (element) {
323             element->CallAnimatorMethod(method);
324         }
325     });
326 
327     // getStatus method.
328     imageAnimatorController->SetAnimatorGetStatusFunc([weak = WeakClaim(this)]() -> Animator::Status {
329         auto element = weak.Upgrade();
330         if (element) {
331             return element->GetAnimatorStatus();
332         }
333         return Animator::Status::IDLE;
334     });
335 
336     animator_->ClearAllListeners();
337     auto startEvent = imageAnimatorController->GetStartEvent();
338     if (!startEvent.IsEmpty()) {
339         animator_->AddStartListener(
340             [startEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(startEvent, weakContext)(); });
341     }
342     auto stopEvent = imageAnimatorController->GetStopEvent();
343     if (!stopEvent.IsEmpty()) {
344         animator_->AddStopListener(
345             [stopEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(stopEvent, weakContext)(); });
346     }
347     auto pauseEvent = imageAnimatorController->GetPauseEvent();
348     if (!pauseEvent.IsEmpty()) {
349         animator_->AddPauseListener(
350             [pauseEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(pauseEvent, weakContext)(); });
351     }
352     auto resumeEvent = imageAnimatorController->GetResumeEvent();
353     if (!resumeEvent.IsEmpty()) {
354         animator_->AddResumeListener(
355             [resumeEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(resumeEvent, weakContext)(); });
356     }
357     auto repeatEvent = imageAnimatorController->GetRepeatEvent();
358     if (!repeatEvent.IsEmpty()) {
359         animator_->AddRepeatListener(
360             [repeatEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(repeatEvent, weakContext)(); });
361     }
362     auto cancelEvent = imageAnimatorController->GetCancelEvent();
363     if (!cancelEvent.IsEmpty()) {
364         animator_->AddIdleListener(
365             [cancelEvent, weakContext = context_] { AceAsyncEvent<void()>::Create(cancelEvent, weakContext)(); });
366     }
367 }
368 
GetAnimatorStatus() const369 Animator::Status ImageAnimatorElement::GetAnimatorStatus() const
370 {
371     if (animator_) {
372         return animator_->GetStatus();
373     }
374     return Animator::Status::IDLE;
375 }
376 
CanUpdate(const RefPtr<Component> & newComponent)377 bool ImageAnimatorElement::CanUpdate(const RefPtr<Component>& newComponent)
378 {
379     return Element::CanUpdate(newComponent);
380 }
381 
382 } // namespace OHOS::Ace
383