• 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/pattern/refresh/refresh_pattern.h"
17 
18 #include "base/geometry/dimension.h"
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/memory/ace_type.h"
21 #include "base/utils/utils.h"
22 #include "core/animation/spring_curve.h"
23 #include "core/common/container.h"
24 #include "core/components/common/properties/animation_option.h"
25 #include "core/components/refresh/refresh_theme.h"
26 #include "core/components_ng/base/frame_node.h"
27 #include "core/components_ng/event/event_hub.h"
28 #include "core/components_ng/pattern/loading_progress/loading_progress_layout_property.h"
29 #include "core/components_ng/pattern/loading_progress/loading_progress_paint_property.h"
30 #include "core/components_ng/pattern/refresh/refresh_layout_property.h"
31 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
32 #include "core/components_ng/property/property.h"
33 #include "core/components_ng/render/animation_utils.h"
34 #include "core/pipeline/base/element_register.h"
35 #include "core/pipeline_ng/pipeline_context.h"
36 #include "frameworks/base/i18n/localization.h"
37 #include "frameworks/base/utils/time_util.h"
38 #include "frameworks/base/utils/utils.h"
39 #include "frameworks/core/components/common/layout/constants.h"
40 #include "frameworks/core/components_ng/pattern/loading_progress/loading_progress_pattern.h"
41 #include "frameworks/core/components_ng/pattern/text/text_pattern.h"
42 
43 namespace OHOS::Ace::NG {
44 
45 namespace {
46 constexpr float PERCENT = 0.01; // Percent
47 constexpr float FOLLOW_TO_RECYCLE_DURATION = 600;
48 constexpr float CUSTOM_BUILDER_RECYCLE_DURATION = 100;
49 constexpr float CUSTOM_BUILDER_EXIT_DURATION = 100;
50 constexpr float LOADING_EXIT_DURATION = 350;
51 constexpr Dimension TRIGGER_LOADING_DISTANCE = 16.0_vp;
52 constexpr Dimension TRIGGER_REFRESH_DISTANCE = 64.0_vp;
53 constexpr Dimension MAX_SCROLL_DISTANCE = 128.0_vp;
54 constexpr Dimension LOADING_PROGRESS_SIZE = 32.0_vp;
55 constexpr Dimension CUSTOM_BUILDER_HIGHT_LIGHT_SIZE = 32.0_vp;
56 constexpr float DEFAULT_FRICTION = 64.0f;
57 constexpr int32_t STATE_PROGRESS_LOADING = 1;
58 constexpr int32_t STATE_PROGRESS_RECYCLE = 2;
59 constexpr int32_t STATE_PROGRESS_DRAG = 3;
60 } // namespace
61 
OnModifyDone()62 void RefreshPattern::OnModifyDone()
63 {
64     Pattern::OnModifyDone();
65     auto host = GetHost();
66     CHECK_NULL_VOID(host);
67     auto hub = host->GetEventHub<EventHub>();
68     CHECK_NULL_VOID(hub);
69     auto gestureHub = hub->GetOrCreateGestureEventHub();
70     CHECK_NULL_VOID(gestureHub);
71     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
72     CHECK_NULL_VOID(layoutProperty);
73     triggerLoadingDistance_ = static_cast<float>(
74         std::clamp(layoutProperty->GetIndicatorOffset().value_or(TRIGGER_LOADING_DISTANCE).ConvertToPx(),
75             -1.0f * TRIGGER_LOADING_DISTANCE.ConvertToPx(), TRIGGER_REFRESH_DISTANCE.ConvertToPx()));
76     InitPanEvent(gestureHub);
77     CheckCoordinationEvent();
78     InitOnKeyEvent();
79     auto paintProperty = GetPaintProperty<RefreshRenderProperty>();
80     CHECK_NULL_VOID(paintProperty);
81     auto refreshingProp = paintProperty->GetIsRefreshing().value_or(false);
82     if (isCustomBuilderExist_ && HasCustomBuilderIndex()) {
83         if (!customBuilder_) {
84             customBuilder_ = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(customBuilderIndex_.value_or(0)));
85             UpdateCustomBuilderProperty(RefreshState::STATE_LOADING, 0.0f);
86         }
87     } else if (!progressChild_) {
88         progressChild_ = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(host->TotalChildCount() - 1));
89     }
90     if (isRefreshing_ != refreshingProp) {
91         if (refreshingProp) {
92             QuickStartFresh();
93         } else {
94             QuickEndFresh();
95         }
96     }
97     SetAccessibilityAction();
98 }
99 
CreateLayoutAlgorithm()100 RefPtr<LayoutAlgorithm> RefreshPattern::CreateLayoutAlgorithm()
101 {
102     auto refreshLayoutAlgorithm = MakeRefPtr<RefreshLayoutAlgorithm>();
103     if (HasCustomBuilderIndex()) {
104         refreshLayoutAlgorithm->SetCustomBuilderIndex(customBuilderIndex_.value_or(0));
105         refreshLayoutAlgorithm->SetCustomBuilderOffset(customBuilderOffset_);
106     }
107     refreshLayoutAlgorithm->SetScrollOffset(scrollOffset_.GetY());
108     return refreshLayoutAlgorithm;
109 }
110 
InitOnKeyEvent()111 void RefreshPattern::InitOnKeyEvent()
112 {
113     if (isKeyEventRegisted_) {
114         return;
115     }
116     auto host = GetHost();
117     CHECK_NULL_VOID(host);
118     auto focusHub = host->GetFocusHub();
119     CHECK_NULL_VOID(focusHub);
120     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
121         auto pattern = wp.Upgrade();
122         CHECK_NULL_RETURN_NOLOG(pattern, false);
123         return pattern->OnKeyEvent(event);
124     };
125     isKeyEventRegisted_ = true;
126     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
127 }
128 
QuickStartFresh()129 void RefreshPattern::QuickStartFresh()
130 {
131     if (isCustomBuilderExist_) {
132         CustomBuilderAppear();
133         if (!isRefreshing_) {
134             TriggerRefresh();
135         }
136         return;
137     }
138     ReplaceLoadingProgressNode();
139     TriggerRefresh();
140     LoadingProgressAppear();
141 }
142 
QuickEndFresh()143 void RefreshPattern::QuickEndFresh()
144 {
145     if (isCustomBuilderExist_) {
146         CustomBuilderExit();
147         return;
148     }
149     LoadingProgressExit();
150 }
151 
OnKeyEvent(const KeyEvent & event)152 bool RefreshPattern::OnKeyEvent(const KeyEvent& event)
153 {
154     if (event.code == KeyCode::KEY_F5 || (event.IsCombinationKey() && event.IsCtrlWith(KeyCode::KEY_R))) {
155         if (isRefreshing_) {
156             return true;
157         }
158         QuickStartFresh();
159         return true;
160     }
161     return false;
162 }
163 
CheckCoordinationEvent()164 void RefreshPattern::CheckCoordinationEvent()
165 {
166     auto host = GetHost();
167     CHECK_NULL_VOID(host);
168     auto scrollableNode = FindScrollableChild();
169     scrollableNode_ = WeakClaim(AceType::RawPtr(scrollableNode));
170     CHECK_NULL_VOID(scrollableNode);
171     auto scrollablePattern = scrollableNode->GetPattern<ScrollablePattern>();
172     CHECK_NULL_VOID(scrollablePattern);
173     auto coordinationEvent = AceType::MakeRefPtr<ScrollableCoordinationEvent>();
174     auto onScrollEvent = [weak = WeakClaim(this)](double offset) -> bool {
175         auto pattern = weak.Upgrade();
176         CHECK_NULL_RETURN(pattern, false);
177         pattern->HandleDragUpdate(static_cast<float>(offset));
178         return Positive(pattern->scrollOffset_.GetY()) || NonNegative(offset);
179     };
180     coordinationEvent->SetOnScrollEvent(onScrollEvent);
181     auto onScrollStartEvent = [weak = WeakClaim(this)]() {
182         auto pattern = weak.Upgrade();
183         CHECK_NULL_VOID(pattern);
184         pattern->HandleDragStart();
185     };
186     coordinationEvent->SetOnScrollStartEvent(onScrollStartEvent);
187     auto onScrollEndEvent = [weak = WeakClaim(this)]() {
188         auto pattern = weak.Upgrade();
189         CHECK_NULL_VOID(pattern);
190         pattern->HandleDragEnd();
191     };
192     coordinationEvent->SetOnScrollEndEvent(onScrollEndEvent);
193     scrollablePattern->SetCoordinationEvent(coordinationEvent);
194 }
195 
FindScrollableChild()196 RefPtr<FrameNode> RefreshPattern::FindScrollableChild()
197 {
198     auto host = GetHost();
199     CHECK_NULL_RETURN(host, nullptr);
200     std::queue<RefPtr<FrameNode>> frameNodeQueue;
201     frameNodeQueue.push(host);
202     while (!frameNodeQueue.empty()) {
203         auto size = frameNodeQueue.size();
204         while (size > 0) {
205             auto node = frameNodeQueue.front();
206             CHECK_NULL_RETURN(node, nullptr);
207             if (AceType::InstanceOf<ScrollablePattern>(node->GetPattern())) {
208                 return node;
209             }
210             frameNodeQueue.pop();
211             auto children = node->GetChildren();
212             for (auto const& child : children) {
213                 auto childNode = DynamicCast<FrameNode>(child);
214                 if (childNode) {
215                     frameNodeQueue.push(childNode);
216                 }
217             }
218             size--;
219         }
220     }
221     return nullptr;
222 }
223 
TriggerRefresh()224 void RefreshPattern::TriggerRefresh()
225 {
226     isRefreshing_ = true;
227     FireChangeEvent("true");
228     FireRefreshing();
229     TriggerStatusChange(RefreshStatus::REFRESH);
230 }
231 
LoadingProgressRecycle()232 void RefreshPattern::LoadingProgressRecycle()
233 {
234     CHECK_NULL_VOID(progressChild_);
235     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
236     CHECK_NULL_VOID(progressPaintProperty);
237     progressPaintProperty->UpdateRefreshAnimationState(static_cast<int32_t>(RefreshAnimationState::RECYCLE));
238     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
239 }
240 
ReplaceLoadingProgressNode()241 void RefreshPattern::ReplaceLoadingProgressNode()
242 {
243     auto host = GetHost();
244     CHECK_NULL_VOID(host);
245     if (progressChild_) {
246         host->RemoveChild(progressChild_);
247     }
248     auto loadingProgressChild = FrameNode::CreateFrameNode(V2::LOADING_PROGRESS_ETS_TAG,
249         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<LoadingProgressPattern>());
250     CHECK_NULL_VOID(loadingProgressChild);
251     host->AddChild(loadingProgressChild);
252     progressChild_ = loadingProgressChild;
253     LoadingProgressReset();
254     host->RebuildRenderContextTree();
255 }
256 
LoadingProgressReset()257 void RefreshPattern::LoadingProgressReset()
258 {
259     CHECK_NULL_VOID(progressChild_);
260     auto gestureHub = progressChild_->GetEventHub<EventHub>();
261     if (gestureHub) {
262         gestureHub->SetEnabled(false);
263     }
264     UpdateLoadingProgress(STATE_PROGRESS_LOADING, 0.0f);
265     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
266     CHECK_NULL_VOID(progressLayoutProperty);
267     progressLayoutProperty->UpdateUserDefinedIdealSize(
268         CalcSize(CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx()), CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx())));
269     ResetLoadingProgressColor();
270     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
271     CHECK_NULL_VOID(progressPaintProperty);
272     progressPaintProperty->UpdateLoadingProgressOwner(LoadingProgressOwner::REFRESH);
273     scrollOffset_.SetY(0.0f);
274     progressChild_->MarkDirtyNode();
275 }
276 
OnExitAnimationFinish()277 void RefreshPattern::OnExitAnimationFinish()
278 {
279     if (isCustomBuilderExist_ && customBuilder_) {
280         scrollOffset_.Reset();
281         CustomBuilderReset();
282         TriggerFinish();
283         auto host = GetHost();
284         CHECK_NULL_VOID(host);
285         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
286         return;
287     }
288     ReplaceLoadingProgressNode();
289     TriggerFinish();
290     CHECK_NULL_VOID(progressChild_);
291     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
292 }
293 
TriggerInActive()294 void RefreshPattern::TriggerInActive()
295 {
296     isRefreshing_ = false;
297     FireChangeEvent("false");
298     TriggerStatusChange(RefreshStatus::INACTIVE);
299 }
300 
TriggerDone()301 void RefreshPattern::TriggerDone()
302 {
303     isRefreshing_ = false;
304     FireChangeEvent("false");
305     TriggerStatusChange(RefreshStatus::DONE);
306 }
307 
TriggerFinish()308 void RefreshPattern::TriggerFinish()
309 {
310     if (refreshStatus_ == RefreshStatus::REFRESH) {
311         TriggerDone();
312     } else {
313         TriggerInActive();
314     }
315 }
316 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)317 void RefreshPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
318 {
319     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& /*info*/) {
320         auto pattern = weak.Upgrade();
321         CHECK_NULL_VOID(pattern);
322         pattern->HandleDragStart();
323     };
324     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
325         auto pattern = weak.Upgrade();
326         CHECK_NULL_VOID(pattern);
327         pattern->HandleDragUpdate(static_cast<float>(info.GetMainDelta()));
328     };
329     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& /*info*/) {
330         auto pattern = weak.Upgrade();
331         CHECK_NULL_VOID(pattern);
332         pattern->HandleDragEnd();
333     };
334     auto actionCancelTask = [weak = WeakClaim(this)]() {
335         auto pattern = weak.Upgrade();
336         CHECK_NULL_VOID(pattern);
337         pattern->HandleDragCancel();
338     };
339     PanDirection panDirection;
340     panDirection.type = PanDirection::VERTICAL;
341     if (panEvent_) {
342         gestureHub->RemovePanEvent(panEvent_);
343     }
344 
345     panEvent_ = MakeRefPtr<PanEvent>(
346         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
347     gestureHub->AddPanEvent(panEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
348 }
349 
HandleDragStart()350 void RefreshPattern::HandleDragStart()
351 {
352     if (isRefreshing_) {
353         return;
354     }
355     TriggerStatusChange(RefreshStatus::DRAG);
356     if (customBuilder_) {
357         scrollOffset_.Reset();
358         return;
359     }
360     CHECK_NULL_VOID(progressChild_);
361     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
362     CHECK_NULL_VOID(progressPaintProperty);
363     progressPaintProperty->UpdateRefreshAnimationState(static_cast<int32_t>(RefreshAnimationState::FOLLOW_HAND));
364     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
365     auto frameNode = GetHost();
366     CHECK_NULL_VOID(frameNode);
367     frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_START);
368 }
369 
HandleDragUpdate(float delta)370 void RefreshPattern::HandleDragUpdate(float delta)
371 {
372     if (NearZero(delta) || isRefreshing_) {
373         LOGI("Delta is near zero or isRefreshing!");
374         return;
375     }
376 
377     scrollOffset_.SetY(GetScrollOffset(delta));
378     if (customBuilder_) {
379         HandleCustomBuilderDragUpdateStage();
380         return;
381     }
382     CHECK_NULL_VOID(progressChild_);
383     if (scrollOffset_.GetY() > triggerLoadingDistance_) {
384         auto refreshFollowRadio = GetFollowRatio();
385         UpdateLoadingProgress(STATE_PROGRESS_DRAG, refreshFollowRadio);
386         UpdateLoadingMarginTop(scrollOffset_.GetY());
387         auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
388         CHECK_NULL_VOID(progressPaintProperty);
389         progressPaintProperty->UpdateRefreshFollowRatio(refreshFollowRadio);
390     }
391 
392     if (scrollOffset_.GetY() > TRIGGER_REFRESH_DISTANCE.ConvertToPx()) {
393         TriggerStatusChange(RefreshStatus::OVER_DRAG);
394     }
395     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
396 }
397 
UpdateLoadingProgress(int32_t state,float ratio)398 void RefreshPattern::UpdateLoadingProgress(int32_t state, float ratio)
399 {
400     CHECK_NULL_VOID(progressChild_);
401     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
402     CHECK_NULL_VOID(progressLayoutProperty);
403     auto scale = std::clamp(ratio, 0.0f, 1.0f);
404     switch (state) {
405         case STATE_PROGRESS_LOADING:
406             scale = 0.0f;
407             UpdateLoadingMarginTop(triggerLoadingDistance_);
408             break;
409         case STATE_PROGRESS_RECYCLE:
410             scale = 1.0f;
411             UpdateLoadingMarginTop(TRIGGER_REFRESH_DISTANCE.ConvertToPx());
412             break;
413         default:;
414     }
415     auto progressContext = progressChild_->GetRenderContext();
416     CHECK_NULL_VOID_NOLOG(progressContext);
417     progressContext->UpdateOpacity(scale);
418 }
419 
GetFollowRatio()420 float RefreshPattern::GetFollowRatio()
421 {
422     auto triggerLoading = std::clamp(triggerLoadingDistance_, 0.0f,
423         static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx()));
424     if (GreatNotEqual(TRIGGER_REFRESH_DISTANCE.ConvertToPx(), triggerLoading)) {
425         return (scrollOffset_.GetY() - triggerLoading) / (TRIGGER_REFRESH_DISTANCE.ConvertToPx() - triggerLoading);
426     }
427     return 1.0f;
428 }
429 
GetFadeAwayRatio()430 float RefreshPattern::GetFadeAwayRatio()
431 {
432     CHECK_NULL_RETURN(progressChild_, 0.0f);
433     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
434     CHECK_NULL_RETURN(progressLayoutProperty, 0.0f);
435     auto& marginProperty = progressLayoutProperty->GetMarginProperty();
436     CHECK_NULL_RETURN(marginProperty, 0.0f);
437     auto triggerLoading = std::clamp(triggerLoadingDistance_, 0.0f,
438         static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx()));
439     if (GreatNotEqual(TRIGGER_REFRESH_DISTANCE.ConvertToPx(), triggerLoading)) {
440         return (marginProperty->top->GetDimension().ConvertToPx() - triggerLoading) /
441                (TRIGGER_REFRESH_DISTANCE.ConvertToPx() - triggerLoading);
442     }
443     return 0.0f;
444 }
445 
TransitionPeriodAnimation()446 void RefreshPattern::TransitionPeriodAnimation()
447 {
448     CHECK_NULL_VOID(progressChild_);
449     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
450     CHECK_NULL_VOID(progressPaintProperty);
451 
452     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
453     CHECK_NULL_VOID(progressLayoutProperty);
454     progressPaintProperty->UpdateRefreshAnimationState(static_cast<int32_t>(RefreshAnimationState::FOLLOW_TO_RECYCLE));
455     progressPaintProperty->UpdateRefreshTransitionRatio(0.0f);
456     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
457     auto pipeline = AceType::DynamicCast<PipelineContext>(PipelineContext::GetCurrentContext());
458     CHECK_NULL_VOID(pipeline);
459     pipeline->AddAnimationClosure([weak = AceType::WeakClaim(this)]() {
460         auto pattern = weak.Upgrade();
461         CHECK_NULL_VOID(pattern);
462         auto pipeline = PipelineContext::GetCurrentContext();
463         CHECK_NULL_VOID(pipeline);
464         auto curve = AceType::MakeRefPtr<SpringCurve>(0.0f, 1.0f, 228.0f, 30.0f);
465         AnimationOption option;
466         option.SetDuration(FOLLOW_TO_RECYCLE_DURATION);
467         option.SetCurve(curve);
468         option.SetIteration(1);
469 
470         AnimationUtils::OpenImplicitAnimation(option, curve, [weak]() {
471             auto pattern = weak.Upgrade();
472             CHECK_NULL_VOID(pattern);
473             pattern->LoadingProgressRecycle();
474         });
475         auto distance = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
476         pattern->scrollOffset_.SetY(distance);
477         pattern->UpdateLoadingMarginTop(distance);
478         pattern->progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
479         pipeline->FlushUITasks();
480         AnimationUtils::CloseImplicitAnimation();
481     });
482 }
483 
LoadingProgressAppear()484 void RefreshPattern::LoadingProgressAppear()
485 {
486     CHECK_NULL_VOID(progressChild_);
487     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
488     CHECK_NULL_VOID(progressPaintProperty);
489     progressPaintProperty->UpdateRefreshAnimationState(static_cast<int32_t>(RefreshAnimationState::RECYCLE));
490     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
491     auto pipeline = AceType::DynamicCast<PipelineContext>(PipelineContext::GetCurrentContext());
492     CHECK_NULL_VOID(pipeline);
493     pipeline->AddAnimationClosure([weak = AceType::WeakClaim(this)]() {
494         auto pattern = weak.Upgrade();
495         CHECK_NULL_VOID(pattern);
496         auto pipeline = PipelineContext::GetCurrentContext();
497         CHECK_NULL_VOID(pipeline);
498         AnimationOption option;
499         option.SetDuration(LOADING_EXIT_DURATION);
500         auto curve = AceType::MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
501         AnimationUtils::OpenImplicitAnimation(option, curve, nullptr);
502         pattern->scrollOffset_.SetY(TRIGGER_REFRESH_DISTANCE.ConvertToPx());
503         pattern->UpdateLoadingProgress(STATE_PROGRESS_RECYCLE, 1.0f);
504         pattern->progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
505         pipeline->FlushUITasks();
506         AnimationUtils::CloseImplicitAnimation();
507     });
508 }
509 
LoadingProgressExit()510 void RefreshPattern::LoadingProgressExit()
511 {
512     CHECK_NULL_VOID(progressChild_);
513     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
514     CHECK_NULL_VOID(progressPaintProperty);
515     progressPaintProperty->UpdateRefreshAnimationState(static_cast<int32_t>(RefreshAnimationState::FADEAWAY));
516     progressPaintProperty->UpdateRefreshFadeAwayRatio(GetFadeAwayRatio());
517     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
518     auto pipeline = PipelineContext::GetCurrentContext();
519     CHECK_NULL_VOID(pipeline);
520     pipeline->FlushUITasks();
521 
522     AnimationOption option;
523     option.SetDuration(LOADING_EXIT_DURATION);
524     auto curve = AceType::MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
525     AnimationUtils::OpenImplicitAnimation(option, curve, [weak = AceType::WeakClaim(this)]() {
526         auto pattern = weak.Upgrade();
527         CHECK_NULL_VOID(pattern);
528         pattern->OnExitAnimationFinish();
529     });
530 
531     scrollOffset_.SetY(0.0f);
532     UpdateLoadingProgress(STATE_PROGRESS_LOADING, 0.0f);
533     progressPaintProperty->UpdateRefreshFadeAwayRatio(0.0f);
534     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
535     pipeline->FlushUITasks();
536     AnimationUtils::CloseImplicitAnimation();
537 }
538 
HandleDragEnd()539 void RefreshPattern::HandleDragEnd()
540 {
541     if (isRefreshing_) {
542         return;
543     }
544     auto triggerRefreshDistance = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
545     if (customBuilder_) {
546         HandleCustomBuilderDragEndStage();
547         return;
548     }
549     if (scrollOffset_.GetY() >= triggerRefreshDistance) {
550         TriggerRefresh();
551         TransitionPeriodAnimation();
552         auto frameNode = GetHost();
553         CHECK_NULL_VOID(frameNode);
554         frameNode->OnAccessibilityEvent(AccessibilityEventType::SCROLL_END);
555         return;
556     }
557     LoadingProgressExit();
558 }
559 
TriggerStatusChange(RefreshStatus newStatus)560 void RefreshPattern::TriggerStatusChange(RefreshStatus newStatus)
561 {
562     if (refreshStatus_ == newStatus) {
563         return;
564     }
565     refreshStatus_ = newStatus;
566     FireStateChange(static_cast<int>(refreshStatus_));
567 }
568 
HandleDragCancel()569 void RefreshPattern::HandleDragCancel()
570 {
571     if (customBuilder_) {
572         CustomBuilderExit();
573         return;
574     }
575     LoadingProgressExit();
576 }
577 
FireStateChange(int32_t value)578 void RefreshPattern::FireStateChange(int32_t value)
579 {
580     auto refreshEventHub = GetEventHub<RefreshEventHub>();
581     CHECK_NULL_VOID(refreshEventHub);
582     refreshEventHub->FireOnStateChange(value);
583 }
584 
FireRefreshing()585 void RefreshPattern::FireRefreshing()
586 {
587     auto refreshEventHub = GetEventHub<RefreshEventHub>();
588     CHECK_NULL_VOID(refreshEventHub);
589     refreshEventHub->FireOnRefreshing();
590 }
591 
FireChangeEvent(const std::string & value)592 void RefreshPattern::FireChangeEvent(const std::string& value)
593 {
594     auto refreshEventHub = GetEventHub<RefreshEventHub>();
595     CHECK_NULL_VOID(refreshEventHub);
596     refreshEventHub->FireChangeEvent(value);
597 }
598 
GetScrollOffset(float delta)599 float RefreshPattern::GetScrollOffset(float delta)
600 {
601     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
602     CHECK_NULL_RETURN(layoutProperty, 0.0f);
603     auto frictionRatio = static_cast<float>(layoutProperty->GetFriction().value_or(DEFAULT_FRICTION)) * PERCENT;
604     auto scrollY = delta * frictionRatio;
605     auto scrollOffset = std::clamp(scrollOffset_.GetY() + scrollY, static_cast<float>(0.0f),
606         static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
607     return scrollOffset;
608 }
609 
ResetLoadingProgressColor()610 void RefreshPattern::ResetLoadingProgressColor()
611 {
612     auto pipeline = PipelineContext::GetCurrentContext();
613     CHECK_NULL_VOID(pipeline);
614     auto themeManager = pipeline->GetThemeManager();
615     CHECK_NULL_VOID(themeManager);
616     auto theme = themeManager->GetTheme<RefreshTheme>();
617     CHECK_NULL_VOID(theme);
618     CHECK_NULL_VOID(progressChild_);
619     auto paintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
620     CHECK_NULL_VOID(paintProperty);
621     paintProperty->UpdateColor(theme->GetProgressColor());
622 }
623 
AddCustomBuilderNode(const RefPtr<NG::UINode> & builder)624 void RefreshPattern::AddCustomBuilderNode(const RefPtr<NG::UINode>& builder)
625 {
626     CHECK_NULL_VOID(builder);
627     auto host = GetHost();
628     CHECK_NULL_VOID(host);
629 
630     if (!HasCustomBuilderIndex()) {
631         host->AddChild(builder);
632         UpdateCustomBuilderIndex(host->TotalChildCount() - 1);
633     } else {
634         auto customNodeChild = host->GetChildAtIndex(customBuilderIndex_.value_or(0));
635         CHECK_NULL_VOID(customNodeChild);
636         if (builder->GetId() != customNodeChild->GetId()) {
637             host->ReplaceChild(customNodeChild, builder);
638         }
639     }
640     isCustomBuilderExist_ = true;
641 }
642 
CustomBuilderAppear()643 void RefreshPattern::CustomBuilderAppear()
644 {
645     auto refreshingDistance = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
646     if (GreatOrEqual(static_cast<double>(customBuilderOffset_), refreshingDistance)) {
647         return;
648     }
649     AnimationOption option;
650     auto curve = AceType::MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
651     option.SetDuration(CUSTOM_BUILDER_RECYCLE_DURATION);
652     option.SetCurve(curve);
653 
654     AnimationUtils::Animate(
655         option,
656         [weak = AceType::WeakClaim(this)]() {
657             auto pattern = weak.Upgrade();
658             CHECK_NULL_VOID(pattern);
659             pattern->UpdateCustomBuilderProperty(RefreshState::STATE_RECYCLE, 1.0f);
660         },
661         nullptr);
662 }
663 
CustomBuilderExit()664 void RefreshPattern::CustomBuilderExit()
665 {
666     if (LessNotEqual(static_cast<double>(customBuilderOffset_), static_cast<double>(triggerLoadingDistance_))) {
667         return;
668     }
669     AnimationOption option;
670     option.SetDuration(CUSTOM_BUILDER_EXIT_DURATION);
671     auto finishCallback = [weak = AceType::WeakClaim(this)]() {
672         auto pattern = weak.Upgrade();
673         CHECK_NULL_VOID(pattern);
674         pattern->OnExitAnimationFinish();
675     };
676 
677     AnimationUtils::Animate(
678         option,
679         [weak = AceType::WeakClaim(this)]() {
680             auto pattern = weak.Upgrade();
681             CHECK_NULL_VOID(pattern);
682             pattern->UpdateCustomBuilderProperty(RefreshState::STATE_LOADING, 0.0f);
683         },
684         std::move(finishCallback));
685 }
686 
HandleCustomBuilderDragUpdateStage()687 void RefreshPattern::HandleCustomBuilderDragUpdateStage()
688 {
689     CHECK_NULL_VOID(customBuilder_);
690     auto host = GetHost();
691     CHECK_NULL_VOID(host);
692     auto customBuilderSize = customBuilder_->GetGeometryNode()->GetMarginFrameSize();
693     auto maxScroll = MAX_SCROLL_DISTANCE.ConvertToPx();
694     if (NearZero(static_cast<double>(customBuilder_->GetGeometryNode()->GetMarginFrameSize().Height()))) {
695         return;
696     }
697     if (LessNotEqual(static_cast<double>(maxScroll - customBuilderSize.Height()),
698         static_cast<double>(triggerLoadingDistance_))) {
699         return;
700     }
701     if (LessOrEqual(static_cast<double>(scrollOffset_.GetY()),
702         static_cast<double>(triggerLoadingDistance_ + customBuilderSize.Height()))) {
703         UpdateCustomBuilderProperty(RefreshState::STATE_LOADING, 0.0f);
704     } else {
705         auto refreshFollowRadio = GetCustomBuilderOpacityRatio();
706         UpdateCustomBuilderProperty(RefreshState::STATE_DRAG, refreshFollowRadio);
707         if (GreatNotEqual(static_cast<double>(customBuilderOffset_),
708             TRIGGER_REFRESH_DISTANCE.ConvertToPx())) {
709             TriggerStatusChange(RefreshStatus::OVER_DRAG);
710         }
711     }
712     host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
713 }
714 
HandleCustomBuilderDragEndStage()715 void RefreshPattern::HandleCustomBuilderDragEndStage()
716 {
717     CHECK_NULL_VOID(customBuilder_);
718     auto host = GetHost();
719     CHECK_NULL_VOID(host);
720     auto customBuilderSize = customBuilder_->GetGeometryNode()->GetMarginFrameSize();
721     auto maxScroll = MAX_SCROLL_DISTANCE.ConvertToPx();
722     if (LessNotEqual(static_cast<double>(maxScroll - customBuilderSize.Height()),
723         static_cast<double>(triggerLoadingDistance_))) {
724         return;
725     }
726 
727     if (GreatNotEqual(static_cast<double>(customBuilderOffset_), TRIGGER_REFRESH_DISTANCE.ConvertToPx())) {
728         TriggerRefresh();
729         CustomBuilderRefreshingAnimation();
730         scrollOffset_.SetY(TRIGGER_REFRESH_DISTANCE.ConvertToPx() + customBuilderSize.Height());
731     } else {
732         CustomBuilderExit();
733         scrollOffset_.Reset();
734     }
735     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
736 }
737 
CustomBuilderReset()738 void RefreshPattern::CustomBuilderReset()
739 {
740     auto host = GetHost();
741     CHECK_NULL_VOID(host);
742     if (isCustomBuilderExist_ && HasCustomBuilderIndex()) {
743         customBuilder_ = AceType::DynamicCast<FrameNode>(host->GetChildAtIndex(customBuilderIndex_.value_or(0)));
744     }
745     CHECK_NULL_VOID(customBuilder_);
746     UpdateCustomBuilderProperty(RefreshState::STATE_LOADING, 0.0f);
747 }
748 
UpdateCustomBuilderProperty(RefreshState state,float ratio)749 void RefreshPattern::UpdateCustomBuilderProperty(RefreshState state, float ratio)
750 {
751     CHECK_NULL_VOID(customBuilder_);
752     ratio = std::clamp(ratio, 0.0f, 1.0f);
753     auto verticalOffset = scrollOffset_.GetY();
754     auto customBuilderSize = customBuilder_->GetGeometryNode()->GetMarginFrameSize();
755     auto maxScroll = static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx());
756     auto custombuilderOffset = verticalOffset - customBuilderSize.Height();
757     custombuilderOffset =
758         std::clamp(custombuilderOffset, triggerLoadingDistance_, maxScroll - customBuilderSize.Height());
759     switch (state) {
760         case RefreshState::STATE_LOADING:
761             customBuilderOffset_ = triggerLoadingDistance_;
762             break;
763         case RefreshState::STATE_DRAG:
764             customBuilderOffset_ = custombuilderOffset;
765             break;
766         case RefreshState::STATE_RECYCLE:
767             customBuilderOffset_ = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
768             break;
769         default:;
770     }
771     auto customBuilderContext = customBuilder_->GetRenderContext();
772     CHECK_NULL_VOID(customBuilderContext);
773     customBuilderContext->UpdateOpacity(ratio);
774 }
775 
CustomBuilderRefreshingAnimation()776 void RefreshPattern::CustomBuilderRefreshingAnimation()
777 {
778     auto refreshingDistance = TRIGGER_REFRESH_DISTANCE.ConvertToPx();
779     if (LessNotEqual(static_cast<double>(customBuilderOffset_), refreshingDistance)) {
780         return;
781     }
782     AnimationOption option;
783     auto curve = AceType::MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
784     option.SetDuration(CUSTOM_BUILDER_RECYCLE_DURATION);
785     option.SetCurve(curve);
786 
787     AnimationUtils::Animate(
788         option,
789         [weak = AceType::WeakClaim(this)]() {
790             auto pattern = weak.Upgrade();
791             CHECK_NULL_VOID(pattern);
792             pattern->UpdateCustomBuilderProperty(RefreshState::STATE_RECYCLE, 1.0f);
793         },
794         nullptr);
795 }
796 
GetCustomBuilderOpacityRatio()797 float RefreshPattern::GetCustomBuilderOpacityRatio()
798 {
799     auto verticalOffset = scrollOffset_.GetY();
800     auto customBuilderSize = customBuilder_->GetGeometryNode()->GetMarginFrameSize();
801     auto adjustOffset = verticalOffset - customBuilderSize.Height();
802     float opacityRatio = 0.0f;
803     if (GreatOrEqual(
804         static_cast<double>(customBuilderSize.Height() + std::clamp(static_cast<double>(triggerLoadingDistance_),
805         0.0, TRIGGER_REFRESH_DISTANCE.ConvertToPx())),
806         TRIGGER_REFRESH_DISTANCE.ConvertToPx() + CUSTOM_BUILDER_HIGHT_LIGHT_SIZE.ConvertToPx())) {
807         opacityRatio = 1.0f;
808     } else {
809         opacityRatio = (adjustOffset - std::clamp(triggerLoadingDistance_, 0.0f,
810             static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx()))) /
811             (TRIGGER_REFRESH_DISTANCE.ConvertToPx() - TRIGGER_LOADING_DISTANCE.ConvertToPx());
812     }
813     return std::clamp(static_cast<float>(opacityRatio), 0.0f, 1.0f);
814 }
815 
UpdateLoadingMarginTop(float top)816 void RefreshPattern::UpdateLoadingMarginTop(float top)
817 {
818     if (LessNotEqual(top, 0.0)) {
819         return;
820     }
821     CHECK_NULL_VOID(progressChild_);
822     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
823     CHECK_NULL_VOID(progressLayoutProperty);
824     MarginProperty marginProperty;
825     if (progressLayoutProperty->GetMarginProperty()) {
826         const auto& originMargin = (*progressLayoutProperty->GetMarginProperty());
827         marginProperty.left = originMargin.left;
828         marginProperty.right = originMargin.right;
829         marginProperty.bottom = originMargin.bottom;
830     }
831     marginProperty.top = CalcLength(top);
832     progressLayoutProperty->UpdateMargin(marginProperty);
833 }
834 
SetAccessibilityAction()835 void RefreshPattern::SetAccessibilityAction()
836 {
837     auto host = GetHost();
838     CHECK_NULL_VOID(host);
839     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
840     CHECK_NULL_VOID(accessibilityProperty);
841     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
842         const auto& pattern = weakPtr.Upgrade();
843         CHECK_NULL_VOID(pattern);
844         if (pattern->IsRefreshing()) {
845             return;
846         }
847         pattern->HandleDragStart();
848         for (float delta = 0.0f; delta < MAX_SCROLL_DISTANCE.ConvertToPx();
849                 delta += TRIGGER_LOADING_DISTANCE.ConvertToPx()) {
850                 pattern->HandleDragUpdate(delta);
851         }
852         pattern->HandleDragEnd();
853     });
854 }
855 
UpdateCustomBuilderIndex(int32_t index)856 void RefreshPattern::UpdateCustomBuilderIndex(int32_t index)
857 {
858     if (!HasCustomBuilderIndex() || customBuilderIndex_.value() != index) {
859         customBuilderIndex_ = index;
860     }
861 }
862 } // namespace OHOS::Ace::NG
863