1 /*
2  * Copyright (c) 2022-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 #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/log/dump_log.h"
21 #include "base/memory/ace_type.h"
22 #include "base/utils/utils.h"
23 #include "core/animation/spring_curve.h"
24 #include "core/common/container.h"
25 #include "core/components/common/properties/animation_option.h"
26 #include "core/components/refresh/refresh_theme.h"
27 #include "core/components_ng/base/frame_node.h"
28 #include "core/components_ng/event/event_hub.h"
29 #include "core/components_ng/pattern/loading_progress/loading_progress_layout_property.h"
30 #include "core/components_ng/pattern/loading_progress/loading_progress_paint_property.h"
31 #include "core/components_ng/pattern/refresh/refresh_animation_state.h"
32 #include "core/components_ng/pattern/refresh/refresh_layout_property.h"
33 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
34 #include "core/components_ng/property/property.h"
35 #include "core/components_ng/render/animation_utils.h"
36 #include "core/pipeline/base/element_register.h"
37 #include "core/pipeline_ng/pipeline_context.h"
38 #include "frameworks/base/i18n/localization.h"
39 #include "frameworks/base/utils/time_util.h"
40 #include "frameworks/base/utils/utils.h"
41 #include "frameworks/core/components/common/layout/constants.h"
42 #include "frameworks/core/components_ng/pattern/loading_progress/loading_progress_pattern.h"
43 #include "frameworks/core/components_ng/pattern/text/text_pattern.h"
44 
45 namespace OHOS::Ace::NG {
46 
47 namespace {
48 constexpr float PERCENT = 0.01f; // Percent
49 constexpr float FOLLOW_TO_RECYCLE_DURATION = 600.0f;
50 constexpr float CUSTOM_BUILDER_ANIMATION_DURATION = 100.0f;
51 constexpr float LOADING_ANIMATION_DURATION = 350.0f;
52 constexpr float MAX_OFFSET = 100000.0f;
53 constexpr float HALF = 0.5f;
54 constexpr float BASE_SCALE = 0.707f; // std::sqrt(2)/2
55 constexpr Dimension TRIGGER_LOADING_DISTANCE = 16.0_vp;
56 constexpr Dimension TRIGGER_REFRESH_WITH_TEXT_DISTANCE = 96.0_vp;
57 constexpr Dimension TRIGGER_REFRESH_DISTANCE = 64.0_vp;
58 constexpr Dimension MAX_SCROLL_DISTANCE = 128.0_vp;
59 constexpr Dimension LOADING_PROGRESS_SIZE = 32.0_vp;
60 constexpr float DEFAULT_FRICTION = 62.0f;
61 const RefPtr<Curve> DEFAULT_CURVE = AceType::MakeRefPtr<CubicCurve>(0.2f, 0.0f, 0.1f, 1.0f);
62 const std::string REFRESH_DRAG_SCENE = "refresh_drag_scene";
63 constexpr Dimension LOADING_TEXT_TOP_MARGIN = 16.0_vp;
64 constexpr Dimension LOADING_TEXT_DISPLAY_DISTANCE = 80.0_vp;
65 } // namespace
66 
67 
GetTriggerRefreshDisTance()68 Dimension RefreshPattern::GetTriggerRefreshDisTance()
69 {
70     if (hasLoadingText_) {
71         return TRIGGER_REFRESH_WITH_TEXT_DISTANCE;
72     } else {
73         return TRIGGER_REFRESH_DISTANCE;
74     }
75 }
76 
OnAttachToFrameNode()77 void RefreshPattern::OnAttachToFrameNode()
78 {
79     auto host = GetHost();
80     CHECK_NULL_VOID(host);
81     host->GetRenderContext()->SetClipToBounds(true);
82     host->GetRenderContext()->UpdateClipEdge(true);
83 }
84 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)85 bool RefreshPattern::OnDirtyLayoutWrapperSwap(
86     const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
87 {
88     if (isRemoveCustomBuilder_ || isTextNodeChanged_) {
89         UpdateFirstChildPlacement();
90         if (isRefreshing_) {
91             UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
92         }
93         isRemoveCustomBuilder_ = false;
94         isTextNodeChanged_ = false;
95     } else if (progressChild_) {
96         auto host = GetHost();
97         CHECK_NULL_RETURN(host, false);
98         auto geometryNode = host->GetGeometryNode();
99         CHECK_NULL_RETURN(geometryNode, false);
100         auto refreshHeight = geometryNode->GetFrameSize().Height();
101         auto scrollOffset = std::clamp(scrollOffset_, 0.0f, refreshHeight);
102         UpdateScrollTransition(scrollOffset);
103     }
104     return false;
105 }
106 
OnModifyDone()107 void RefreshPattern::OnModifyDone()
108 {
109     Pattern::OnModifyDone();
110     auto host = GetHost();
111     CHECK_NULL_VOID(host);
112     auto hub = host->GetEventHub<EventHub>();
113     CHECK_NULL_VOID(hub);
114     auto gestureHub = hub->GetOrCreateGestureEventHub();
115     CHECK_NULL_VOID(gestureHub);
116     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
117     CHECK_NULL_VOID(layoutProperty);
118     hasLoadingText_ = layoutProperty->HasLoadingText();
119     refreshOffset_ = layoutProperty->GetRefreshOffset().value_or(GetTriggerRefreshDisTance());
120     if (LessOrEqual(refreshOffset_.Value(), 0)) {
121         refreshOffset_ = GetTriggerRefreshDisTance();
122     }
123     pullToRefresh_ = layoutProperty->GetPullToRefresh().value_or(true);
124     InitPanEvent(gestureHub);
125     InitOnKeyEvent();
126     InitChildNode();
127     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
128         InitOffsetProperty();
129     } else {
130         triggerLoadingDistance_ = static_cast<float>(
131             std::clamp(layoutProperty->GetIndicatorOffset().value_or(TRIGGER_LOADING_DISTANCE).ConvertToPx(),
132                 -1.0f * TRIGGER_LOADING_DISTANCE.ConvertToPx(), GetTriggerRefreshDisTance().ConvertToPx()));
133         InitLowVersionOffset();
134     }
135     RefreshStatusChangeEffect();
136     SetAccessibilityAction();
137 }
138 
CreateLayoutAlgorithm()139 RefPtr<LayoutAlgorithm> RefreshPattern::CreateLayoutAlgorithm()
140 {
141     auto refreshLayoutAlgorithm = MakeRefPtr<RefreshLayoutAlgorithm>();
142     if (isCustomBuilderExist_) {
143         refreshLayoutAlgorithm->SetCustomBuilderIndex(0);
144         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
145             refreshLayoutAlgorithm->SetBuilderMeasureBaseHeight(builderMeasureBaseHeight_);
146         } else {
147             refreshLayoutAlgorithm->SetCustomBuilderOffset(customBuilderOffset_);
148             refreshLayoutAlgorithm->SetScrollOffset(scrollOffset_);
149         }
150     }
151     return refreshLayoutAlgorithm;
152 }
153 
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)154 void RefreshPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
155 {
156     if (panEvent_) {
157         return;
158     }
159     auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
160         auto pattern = weak.Upgrade();
161         CHECK_NULL_VOID(pattern);
162         auto speed = static_cast<float>(info.GetMainVelocity());
163         pattern->UpdateDragFRCSceneInfo(REFRESH_DRAG_SCENE, speed, SceneStatus::START);
164         pattern->HandleDragStart(true, speed);
165     };
166     auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
167         auto pattern = weak.Upgrade();
168         CHECK_NULL_VOID(pattern);
169         pattern->HandleDragUpdate(static_cast<float>(info.GetMainDelta()), static_cast<float>(info.GetMainVelocity()));
170     };
171     auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
172         auto pattern = weak.Upgrade();
173         CHECK_NULL_VOID(pattern);
174         auto speed = static_cast<float>(info.GetMainVelocity());
175         pattern->UpdateDragFRCSceneInfo(REFRESH_DRAG_SCENE, speed, SceneStatus::END);
176         pattern->HandleDragEnd(speed);
177     };
178     auto actionCancelTask = [weak = WeakClaim(this)]() {
179         auto pattern = weak.Upgrade();
180         CHECK_NULL_VOID(pattern);
181         pattern->HandleDragCancel();
182     };
183     PanDirection panDirection;
184     panDirection.type = PanDirection::VERTICAL;
185     if (panEvent_) {
186         gestureHub->RemovePanEvent(panEvent_);
187     }
188 
189     panEvent_ = MakeRefPtr<PanEvent>(
190         std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
191     gestureHub->AddPanEvent(panEvent_, panDirection, 1, DEFAULT_PAN_DISTANCE);
192     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_THIRTEEN)) {
193         gestureHub->SetIsAllowMouse(false);
194     }
195 }
196 
InitOnKeyEvent()197 void RefreshPattern::InitOnKeyEvent()
198 {
199     if (isKeyEventRegisted_) {
200         return;
201     }
202     auto host = GetHost();
203     CHECK_NULL_VOID(host);
204     auto focusHub = host->GetFocusHub();
205     CHECK_NULL_VOID(focusHub);
206     auto onKeyEvent = [wp = WeakClaim(this)](const KeyEvent& event) -> bool {
207         auto pattern = wp.Upgrade();
208         CHECK_NULL_RETURN(pattern, false);
209         return pattern->OnKeyEvent(event);
210     };
211     isKeyEventRegisted_ = true;
212     focusHub->SetOnKeyEventInternal(std::move(onKeyEvent));
213 }
214 
InitProgressNode()215 void RefreshPattern::InitProgressNode()
216 {
217     auto host = GetHost();
218     CHECK_NULL_VOID(host);
219     progressChild_ = FrameNode::CreateFrameNode(V2::LOADING_PROGRESS_ETS_TAG,
220         ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<LoadingProgressPattern>());
221     CHECK_NULL_VOID(progressChild_);
222     auto gestureHub = progressChild_->GetEventHub<EventHub>();
223     if (gestureHub) {
224         gestureHub->SetEnabled(false);
225     }
226     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
227     CHECK_NULL_VOID(progressLayoutProperty);
228     progressLayoutProperty->UpdateUserDefinedIdealSize(
229         CalcSize(CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx()), CalcLength(LOADING_PROGRESS_SIZE.ConvertToPx())));
230     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
231     CHECK_NULL_VOID(progressPaintProperty);
232     progressPaintProperty->UpdateLoadingProgressOwner(LoadingProgressOwner::REFRESH);
233     host->AddChild(progressChild_, 0);
234     progressChild_->MarkDirtyNode();
235     auto context = host->GetContext();
236     CHECK_NULL_VOID(context);
237     auto theme = context->GetTheme<RefreshTheme>();
238     CHECK_NULL_VOID(theme);
239     progressPaintProperty->UpdateColor(theme->GetProgressColor());
240 }
241 
UpdateLoadingTextOpacity(float opacity)242 void RefreshPattern::UpdateLoadingTextOpacity(float opacity)
243 {
244     CHECK_NULL_VOID(loadingTextNode_);
245     auto loadingTextRenderContext = loadingTextNode_->GetRenderContext();
246     CHECK_NULL_VOID(loadingTextRenderContext);
247     if (opacity > 0.0f) {
248         opacity = std::clamp(scrollOffset_ - static_cast<float>(LOADING_TEXT_DISPLAY_DISTANCE.ConvertToPx()), 0.0f,
249             static_cast<float>(TRIGGER_REFRESH_WITH_TEXT_DISTANCE.ConvertToPx() -
250                                          LOADING_TEXT_DISPLAY_DISTANCE.ConvertToPx())) /
251                   static_cast<float>(
252                       TRIGGER_REFRESH_WITH_TEXT_DISTANCE.ConvertToPx() - LOADING_TEXT_DISPLAY_DISTANCE.ConvertToPx());
253     }
254     loadingTextRenderContext->UpdateOpacity(opacity);
255 }
256 
InitProgressColumn()257 void RefreshPattern::InitProgressColumn()
258 {
259     auto host = GetHost();
260     CHECK_NULL_VOID(host);
261     columnNode_ = FrameNode::CreateFrameNode(V2::COLUMN_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
262         AceType::MakeRefPtr<LinearLayoutPattern>(true));
263     loadingTextNode_ = FrameNode::CreateFrameNode(
264         V2::TEXT_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(), AceType::MakeRefPtr<TextPattern>());
265     auto loadingTextLayoutProperty = loadingTextNode_->GetLayoutProperty<TextLayoutProperty>();
266     CHECK_NULL_VOID(loadingTextLayoutProperty);
267     auto layoutProperty = host->GetLayoutProperty<RefreshLayoutProperty>();
268     CHECK_NULL_VOID(layoutProperty);
269     loadingTextLayoutProperty->UpdateContent(layoutProperty->GetLoadingTextValue());
270     loadingTextLayoutProperty->UpdateMaxLines(1);
271     loadingTextLayoutProperty->UpdateMaxFontScale(2.0f);
272     loadingTextLayoutProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
273     auto context = PipelineContext::GetCurrentContextSafely();
274     CHECK_NULL_VOID(context);
275     auto theme = context->GetTheme<RefreshTheme>();
276     CHECK_NULL_VOID(theme);
277     loadingTextLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
278     loadingTextLayoutProperty->UpdateFontSize(theme->GetTextStyle().GetFontSize());
279 
280     PaddingProperty textpadding;
281     textpadding.top = CalcLength(TRIGGER_LOADING_DISTANCE.ConvertToPx() + LOADING_TEXT_TOP_MARGIN.ConvertToPx());
282     auto prop = columnNode_->GetLayoutProperty<LinearLayoutProperty>();
283     prop->UpdatePadding(textpadding);
284     UpdateLoadingTextOpacity(0.0f);
285 
286     columnNode_->AddChild(loadingTextNode_, -1);
287     host->AddChild(columnNode_, 0);
288 }
289 
OnColorConfigurationUpdate()290 void RefreshPattern::OnColorConfigurationUpdate()
291 {
292     if (isCustomBuilderExist_) {
293         return;
294     }
295     CHECK_NULL_VOID(progressChild_);
296     auto pipeline = PipelineContext::GetCurrentContextSafely();
297     CHECK_NULL_VOID(pipeline);
298     auto themeManager = pipeline->GetThemeManager();
299     CHECK_NULL_VOID(themeManager);
300     auto theme = themeManager->GetTheme<RefreshTheme>();
301     CHECK_NULL_VOID(theme);
302     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
303     CHECK_NULL_VOID(layoutProperty);
304     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
305     CHECK_NULL_VOID(progressPaintProperty);
306     progressPaintProperty->UpdateColor(theme->GetProgressColor());
307     if (hasLoadingText_) {
308         CHECK_NULL_VOID(loadingTextNode_);
309         auto textLayoutProperty = loadingTextNode_->GetLayoutProperty<TextLayoutProperty>();
310         CHECK_NULL_VOID(textLayoutProperty);
311         textLayoutProperty->UpdateFontSize(theme->GetTextStyle().GetFontSize());
312         textLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
313     }
314 }
315 
InitChildNode()316 void RefreshPattern::InitChildNode()
317 {
318     if (isCustomBuilderExist_) {
319         return;
320     }
321     auto host = GetHost();
322     CHECK_NULL_VOID(host);
323     auto accessibilityProperty = host->GetAccessibilityProperty<NG::AccessibilityProperty>();
324     CHECK_NULL_VOID(accessibilityProperty);
325     auto accessibilityLevel = accessibilityProperty->GetAccessibilityLevel();
326     if (!progressChild_) {
327         InitProgressNode();
328         if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
329             auto progressContext = progressChild_->GetRenderContext();
330             CHECK_NULL_VOID(progressContext);
331             progressContext->UpdateOpacity(0.0f);
332         } else {
333             UpdateLoadingProgress();
334         }
335     }
336     auto progressAccessibilityProperty = progressChild_->GetAccessibilityProperty<AccessibilityProperty>();
337     CHECK_NULL_VOID(progressAccessibilityProperty);
338     progressAccessibilityProperty->SetAccessibilityLevel(accessibilityLevel);
339 
340     if (hasLoadingText_ && !loadingTextNode_) {
341         InitProgressColumn();
342         isTextNodeChanged_ = true;
343     } else if (!hasLoadingText_ && loadingTextNode_) {
344         host->RemoveChild(columnNode_);
345         columnNode_ = nullptr;
346         loadingTextNode_ = nullptr;
347         isTextNodeChanged_ = true;
348         host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
349     }
350 
351     if (hasLoadingText_ && loadingTextNode_) {
352         auto loadingTextLayoutProperty = loadingTextNode_->GetLayoutProperty<TextLayoutProperty>();
353         CHECK_NULL_VOID(loadingTextLayoutProperty);
354         auto layoutProperty = host->GetLayoutProperty<RefreshLayoutProperty>();
355         CHECK_NULL_VOID(layoutProperty);
356         loadingTextLayoutProperty->UpdateContent(layoutProperty->GetLoadingTextValue());
357         loadingTextNode_->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
358         auto textAccessibilityProperty = loadingTextNode_->GetAccessibilityProperty<AccessibilityProperty>();
359         CHECK_NULL_VOID(textAccessibilityProperty);
360         textAccessibilityProperty->SetAccessibilityLevel(accessibilityLevel);
361     }
362 }
363 
RefreshStatusChangeEffect()364 void RefreshPattern::RefreshStatusChangeEffect()
365 {
366     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
367     CHECK_NULL_VOID(layoutProperty);
368     auto refreshingProp = layoutProperty->GetIsRefreshing().value_or(false);
369     if (isRefreshing_ != refreshingProp) {
370         if (refreshingProp) {
371             QuickStartFresh();
372         } else {
373             QuickEndFresh();
374         }
375     }
376 }
377 
QuickStartFresh()378 void RefreshPattern::QuickStartFresh()
379 {
380     UpdateRefreshStatus(RefreshStatus::REFRESH);
381     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
382         QuickFirstChildAppear();
383         return;
384     }
385 
386     if (isCustomBuilderExist_) {
387         CustomBuilderRefreshingAnimation(false);
388     } else {
389         LoadingProgressRefreshingAnimation(false);
390     }
391 }
392 
QuickEndFresh()393 void RefreshPattern::QuickEndFresh()
394 {
395     SwitchToFinish();
396     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
397         QuickFirstChildDisappear();
398         return;
399     }
400 
401     if (isCustomBuilderExist_) {
402         CustomBuilderExit();
403     } else {
404         LoadingProgressExit();
405     }
406 }
407 
OnKeyEvent(const KeyEvent & event)408 bool RefreshPattern::OnKeyEvent(const KeyEvent& event)
409 {
410     if (event.code == KeyCode::KEY_F5 || (event.IsCombinationKey() && event.IsCtrlWith(KeyCode::KEY_R))) {
411         if (!isRefreshing_) {
412             QuickStartFresh();
413         }
414         return true;
415     }
416     return false;
417 }
418 
HandleDragStart(bool isDrag,float mainSpeed)419 void RefreshPattern::HandleDragStart(bool isDrag, float mainSpeed)
420 {
421     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
422         isSourceFromAnimation_ = !isDrag;
423         ResetAnimation();
424     } else {
425         HandleDragStartLowVersion();
426     }
427     // AccessibilityEventType::SCROLL_START
428 }
429 
HandleDragUpdate(float delta,float mainSpeed)430 ScrollResult RefreshPattern::HandleDragUpdate(float delta, float mainSpeed)
431 {
432     UpdateDragFRCSceneInfo(REFRESH_DRAG_SCENE, mainSpeed, SceneStatus::RUNNING);
433     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
434         // If dragging does not expand the refresh, there is no need to continue executing the code
435         if (NearZero(scrollOffset_) && NonPositive(delta)) {
436             return { delta, true };
437         }
438         auto pullDownRatio = CalculatePullDownRatio();
439         scrollOffset_ = std::clamp(scrollOffset_ + delta * pullDownRatio, 0.0f, MAX_OFFSET);
440         UpdateFirstChildPlacement();
441         FireOnOffsetChange(scrollOffset_);
442         if (!isSourceFromAnimation_) {
443             if (isRefreshing_) {
444                 UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
445                 return { 0.f, true };
446             }
447             UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, GetFollowRatio());
448             if (LessNotEqual(scrollOffset_, static_cast<float>(refreshOffset_.ConvertToPx()))) {
449                 UpdateRefreshStatus(RefreshStatus::DRAG);
450             } else {
451                 UpdateRefreshStatus(RefreshStatus::OVER_DRAG);
452             }
453         }
454     } else {
455         HandleDragUpdateLowVersion(delta);
456     }
457     return { 0.f, true };
458 }
459 
HandleDragEnd(float speed)460 void RefreshPattern::HandleDragEnd(float speed)
461 {
462     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
463         SpeedTriggerAnimation(speed);
464     } else {
465         HandleDragEndLowVersion();
466     }
467 }
468 
HandleDragCancel()469 void RefreshPattern::HandleDragCancel()
470 {
471     HandleDragEnd(0.0f);
472 }
473 
CalculatePullDownRatio()474 float RefreshPattern::CalculatePullDownRatio()
475 {
476     auto host = GetHost();
477     CHECK_NULL_RETURN(host, 1.0f);
478     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
479     CHECK_NULL_RETURN(layoutProperty, 1.f);
480     if (layoutProperty->GetPullDownRatio().has_value()) {
481         return layoutProperty->GetPullDownRatio().value();
482     }
483     auto geometryNode = host->GetGeometryNode();
484     CHECK_NULL_RETURN(geometryNode, 1.0f);
485     auto contentHeight = geometryNode->GetPaddingSize().Height();
486     return NearZero(contentHeight) ? 1.0f : ScrollablePattern::CalculateFriction(scrollOffset_ / contentHeight);
487 }
488 
GetFollowRatio()489 float RefreshPattern::GetFollowRatio()
490 {
491     auto loadingVisibleHeight = GetLoadingVisibleHeight();
492     auto ratio = 0.0f;
493     if (!NearEqual(static_cast<float>(refreshOffset_.ConvertToPx()), loadingVisibleHeight)) {
494         ratio = static_cast<float>(
495             (scrollOffset_ - loadingVisibleHeight) / (refreshOffset_.ConvertToPx() - loadingVisibleHeight));
496     }
497     return std::clamp(ratio, 0.0f, 1.0f);
498 }
499 
FireStateChange(int32_t value)500 void RefreshPattern::FireStateChange(int32_t value)
501 {
502     auto refreshEventHub = GetEventHub<RefreshEventHub>();
503     CHECK_NULL_VOID(refreshEventHub);
504     refreshEventHub->FireOnStateChange(value);
505     if (refreshStatus_ == RefreshStatus::REFRESH && Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
506         auto host = GetHost();
507         CHECK_NULL_VOID(host);
508         auto inspectorId = host->GetInspectorId().value_or("");
509         Recorder::EventParamsBuilder builder;
510         builder.SetId(inspectorId)
511             .SetType(host->GetTag())
512             .SetEventType(Recorder::EventType::REFRESH)
513             .SetDescription(host->GetAutoEventParamValue(""));
514         Recorder::EventRecorder::Get().OnEvent(std::move(builder));
515     }
516 }
517 
FireRefreshing()518 void RefreshPattern::FireRefreshing()
519 {
520     auto refreshEventHub = GetEventHub<RefreshEventHub>();
521     CHECK_NULL_VOID(refreshEventHub);
522     refreshEventHub->FireOnRefreshing();
523 }
524 
FireChangeEvent(const std::string & value)525 void RefreshPattern::FireChangeEvent(const std::string& value)
526 {
527     auto refreshEventHub = GetEventHub<RefreshEventHub>();
528     CHECK_NULL_VOID(refreshEventHub);
529     refreshEventHub->FireChangeEvent(value);
530 }
531 
FireOnOffsetChange(float value)532 void RefreshPattern::FireOnOffsetChange(float value)
533 {
534     if (NearZero(value)) {
535         value = 0.0f;
536     }
537     if (!NearEqual(lastScrollOffset_, value)) {
538         auto refreshEventHub = GetEventHub<RefreshEventHub>();
539         CHECK_NULL_VOID(refreshEventHub);
540         refreshEventHub->FireOnOffsetChange(Dimension(value).ConvertToVp());
541         lastScrollOffset_ = value;
542     }
543 }
544 
AddCustomBuilderNode(const RefPtr<NG::UINode> & builder)545 void RefreshPattern::AddCustomBuilderNode(const RefPtr<NG::UINode>& builder)
546 {
547     auto host = GetHost();
548     CHECK_NULL_VOID(host);
549     if (!builder) {
550         if (isCustomBuilderExist_) {
551             host->RemoveChild(customBuilder_);
552             isCustomBuilderExist_ = false;
553             customBuilder_ = nullptr;
554             isRemoveCustomBuilder_ = true;
555         }
556         return;
557     }
558 
559     if (!isCustomBuilderExist_) {
560         if (progressChild_) {
561             if (columnNode_) {
562                 host->RemoveChild(columnNode_);
563                 columnNode_ = nullptr;
564                 loadingTextNode_ = nullptr;
565             }
566             host->RemoveChild(progressChild_);
567             progressChild_ = nullptr;
568         }
569         host->AddChild(builder, 0);
570         UpdateFirstChildPlacement();
571         UpdateScrollTransition(0.f);
572     } else {
573         auto customNodeChild = host->GetFirstChild();
574         CHECK_NULL_VOID(customNodeChild);
575         if (customNodeChild != builder) {
576             host->ReplaceChild(customNodeChild, builder);
577             host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
578         }
579     }
580     customBuilder_ = AceType::DynamicCast<FrameNode>(builder);
581     isCustomBuilderExist_ = true;
582 }
583 
SetAccessibilityAction()584 void RefreshPattern::SetAccessibilityAction()
585 {
586     auto host = GetHost();
587     CHECK_NULL_VOID(host);
588     auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
589     CHECK_NULL_VOID(accessibilityProperty);
590     accessibilityProperty->SetActionScrollForward([weakPtr = WeakClaim(this)]() {
591         const auto& pattern = weakPtr.Upgrade();
592         CHECK_NULL_VOID(pattern);
593         if (pattern->IsRefreshing()) {
594             return;
595         }
596         pattern->HandleDragStart(true, 0.0f);
597         for (float delta = 0.0f; LessNotEqual(delta, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
598              delta += TRIGGER_LOADING_DISTANCE.ConvertToPx()) {
599             pattern->HandleDragUpdate(delta, 0.0f);
600         }
601         pattern->HandleDragEnd(0.0f);
602     });
603 }
604 
InitCoordinationEvent(RefPtr<ScrollableCoordinationEvent> & coordinationEvent)605 void RefreshPattern::InitCoordinationEvent(RefPtr<ScrollableCoordinationEvent>& coordinationEvent)
606 {
607     auto onScrollEvent = [weak = WeakClaim(this)](float offset, float mainSpeed) -> bool {
608         auto pattern = weak.Upgrade();
609         CHECK_NULL_RETURN(pattern, false);
610         pattern->HandleDragUpdate(offset, mainSpeed);
611         return Positive(pattern->scrollOffset_) || NonNegative(offset);
612     };
613     coordinationEvent->SetOnScrollEvent(onScrollEvent);
614     auto onScrollStartEvent = [weak = WeakClaim(this)](bool isDrag, float mainSpeed) {
615         auto pattern = weak.Upgrade();
616         CHECK_NULL_VOID(pattern);
617         pattern->HandleDragStart(isDrag, mainSpeed);
618     };
619     coordinationEvent->SetOnScrollStartEvent(onScrollStartEvent);
620     auto onScrollEndEvent = [weak = WeakClaim(this)](float speed) {
621         auto pattern = weak.Upgrade();
622         CHECK_NULL_VOID(pattern);
623         pattern->HandleDragEnd(speed);
624     };
625     coordinationEvent->SetOnScrollEndEvent(onScrollEndEvent);
626 }
627 
UpdateRefreshStatus(RefreshStatus newStatus)628 void RefreshPattern::UpdateRefreshStatus(RefreshStatus newStatus)
629 {
630     if (refreshStatus_ == newStatus) {
631         return;
632     }
633     refreshStatus_ = newStatus;
634     if (refreshStatus_ == RefreshStatus::REFRESH) {
635         isRefreshing_ = true;
636         // the two-way binding of 'refreshing' variable need to be changed before 'onRefreshing' function is triggered
637         FireChangeEvent("true");
638         FireRefreshing();
639     } else {
640         isRefreshing_ = false;
641         FireChangeEvent("false");
642     }
643     FireStateChange(static_cast<int>(refreshStatus_));
644     TAG_LOGD(AceLogTag::ACE_REFRESH, "refresh status changed %{public}d", static_cast<int32_t>(refreshStatus_));
645 }
646 
SwitchToFinish()647 void RefreshPattern::SwitchToFinish()
648 {
649     if (refreshStatus_ != RefreshStatus::REFRESH && refreshStatus_ != RefreshStatus::DONE) {
650         UpdateRefreshStatus(RefreshStatus::INACTIVE);
651     } else {
652         UpdateRefreshStatus(RefreshStatus::DONE);
653     }
654 }
655 
UpdateLoadingProgressStatus(RefreshAnimationState state,float ratio)656 void RefreshPattern::UpdateLoadingProgressStatus(RefreshAnimationState state, float ratio)
657 {
658     // Need to check loadingProgress mode
659     CHECK_NULL_VOID(progressChild_);
660     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
661     CHECK_NULL_VOID(progressPaintProperty);
662     progressPaintProperty->UpdateRefreshAnimationState(state);
663     switch (state) {
664         case RefreshAnimationState::FOLLOW_HAND:
665         case RefreshAnimationState::RECYCLE:
666             progressPaintProperty->UpdateRefreshSizeScaleRatio(ratio);
667             break;
668         default:
669             break;
670     }
671     if (CheckNeedRender(progressPaintProperty->GetPropertyChangeFlag())) {
672         progressChild_->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
673     }
674 }
675 
InitOffsetProperty()676 void RefreshPattern::InitOffsetProperty()
677 {
678     if (!offsetProperty_) {
679         auto propertyCallback = [weak = AceType::WeakClaim(this)](float scrollOffset) {
680             auto pattern = weak.Upgrade();
681             CHECK_NULL_VOID(pattern);
682             pattern->scrollOffset_ = scrollOffset;
683             pattern->UpdateFirstChildPlacement();
684             pattern->FireOnOffsetChange(scrollOffset);
685         };
686         offsetProperty_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
687         auto host = GetHost();
688         CHECK_NULL_VOID(host);
689         auto renderContext = host->GetRenderContext();
690         CHECK_NULL_VOID(renderContext);
691         renderContext->AttachNodeAnimatableProperty(offsetProperty_);
692         offsetProperty_->SetPropertyUnit(PropertyUnit::PIXEL_POSITION);
693     }
694 }
695 
UpdateFirstChildPlacement()696 void RefreshPattern::UpdateFirstChildPlacement()
697 {
698     auto host = GetHost();
699     CHECK_NULL_VOID(host);
700     auto geometryNode = host->GetGeometryNode();
701     CHECK_NULL_VOID(geometryNode);
702     auto refreshHeight = geometryNode->GetFrameSize().Height();
703     auto scrollOffset = std::clamp(scrollOffset_, 0.0f, refreshHeight);
704     if (progressChild_) {
705         if (isSourceFromAnimation_) {
706             UpdateLoadingProgressTranslate(0.0f);
707             UpdateScrollTransition(scrollOffset);
708         } else {
709             UpdateLoadingProgressTranslate(scrollOffset);
710             UpdateScrollTransition(scrollOffset);
711             UpdateLoadingProgressStatus(GetLoadingProgressStatus(), GetFollowRatio());
712         }
713     } else {
714         UpdateBuilderHeight(scrollOffset);
715     }
716 }
717 
UpdateScrollTransition(float scrollOffset)718 void RefreshPattern::UpdateScrollTransition(float scrollOffset)
719 {
720     auto host = GetHost();
721     CHECK_NULL_VOID(host);
722     int32_t childCount = host->TotalChildCount();
723     // If the refresh has no children without loadingProgress and text, it does not need to update offset.
724     if (childCount < 2 || (childCount == 2 && columnNode_)) { // 2 means loadingProgress and text child components.
725         return;
726     }
727     // Need to search for frameNode and skip ComponentNode
728     auto childNode = host->GetLastChild();
729     CHECK_NULL_VOID(childNode);
730     while (!AceType::InstanceOf<FrameNode>(childNode) && !childNode->GetChildren().empty()) {
731         childNode = childNode->GetFirstChild();
732     }
733     auto scrollableNode = AceType::DynamicCast<FrameNode>(childNode);
734     CHECK_NULL_VOID(scrollableNode);
735     auto scrollableRenderContext = scrollableNode->GetRenderContext();
736     CHECK_NULL_VOID(scrollableRenderContext);
737     scrollableRenderContext->UpdateTransformTranslate({ 0.0f, scrollOffset, 0.0f });
738 }
739 
UpdateBuilderHeight(float builderHeight)740 void RefreshPattern::UpdateBuilderHeight(float builderHeight)
741 {
742     auto host = GetHost();
743     CHECK_NULL_VOID(host);
744     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
745     CHECK_NULL_VOID(layoutProperty);
746     builderMeasureBaseHeight_ = builderHeight;
747     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
748 }
749 
UpdateLoadingProgressTranslate(float scrollOffset)750 void RefreshPattern::UpdateLoadingProgressTranslate(float scrollOffset)
751 {
752     CHECK_NULL_VOID(progressChild_);
753     auto renderContext = progressChild_->GetRenderContext();
754     CHECK_NULL_VOID(renderContext);
755     auto loadingVisibleHeight = GetLoadingVisibleHeight();
756     if (GreatOrEqual(scrollOffset, loadingVisibleHeight) &&
757         !NearEqual(loadingVisibleHeight, static_cast<float>(GetTriggerRefreshDisTance().ConvertToPx()))) {
758         auto ratio = static_cast<float>(
759             (scrollOffset - loadingVisibleHeight) / (GetTriggerRefreshDisTance().ConvertToPx() - loadingVisibleHeight));
760         renderContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
761         renderContext->UpdateTransformTranslate({ 0.0f, (scrollOffset - loadingVisibleHeight) * HALF, 0.0f });
762         if (loadingTextNode_) {
763             UpdateLoadingTextOpacity(std::clamp(ratio, 0.0f, 1.0f));
764             auto loadingTextRenderContext = loadingTextNode_->GetRenderContext();
765             CHECK_NULL_VOID(loadingTextRenderContext);
766             loadingTextRenderContext->UpdateTransformTranslate({ 0.0f,
767                 scrollOffset_ - TRIGGER_LOADING_DISTANCE.ConvertToPx() - LOADING_PROGRESS_SIZE.ConvertToPx() -
768                     LOADING_TEXT_TOP_MARGIN.ConvertToPx(),
769                 0.0f });
770         }
771     } else {
772         renderContext->UpdateOpacity(0.0f);
773         UpdateLoadingTextOpacity(0.0f);
774     }
775 }
776 
GetLoadingVisibleHeight()777 float RefreshPattern::GetLoadingVisibleHeight()
778 {
779     float loadingHeight = 0.0f;
780     CHECK_NULL_RETURN(progressChild_, 0.0f);
781     auto renderContext = progressChild_->GetRenderContext();
782     CHECK_NULL_RETURN(renderContext, 0.0f);
783     auto geometryNode = progressChild_->GetGeometryNode();
784     CHECK_NULL_RETURN(geometryNode, 0.0f);
785     if (loadingTextNode_) {
786         auto loadingTextGeometryNode = loadingTextNode_->GetGeometryNode();
787         CHECK_NULL_RETURN(loadingTextGeometryNode, 0.0f);
788         loadingHeight = geometryNode->GetFrameSize().Height() + loadingTextGeometryNode->GetFrameSize().Height() +
789                         LOADING_TEXT_TOP_MARGIN.ConvertToPx();
790     } else {
791         loadingHeight = geometryNode->GetFrameSize().Height();
792     }
793     return (HALF + BASE_SCALE * HALF) * loadingHeight;
794 }
795 
SpeedTriggerAnimation(float speed)796 void RefreshPattern::SpeedTriggerAnimation(float speed)
797 {
798     auto targetOffset = (isSourceFromAnimation_ ||
799                             LessNotEqual(scrollOffset_, refreshOffset_.ConvertToPx()) || !pullToRefresh_)
800                             ? 0.0f
801                             : refreshOffset_.ConvertToPx();
802     auto dealSpeed = 0.0f;
803     if (!NearEqual(scrollOffset_, targetOffset)) {
804         dealSpeed = speed / (targetOffset - scrollOffset_);
805     }
806     bool recycle = true;
807     if (pullToRefresh_ && !isSourceFromAnimation_ && refreshStatus_ == RefreshStatus::OVER_DRAG) {
808         UpdateRefreshStatus(RefreshStatus::REFRESH);
809         UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_TO_RECYCLE, GetFollowRatio());
810     } else if (NearZero(targetOffset)) {
811         recycle = false;
812         SwitchToFinish();
813     }
814     ResetAnimation();
815     AnimationOption option;
816     auto curve = AceType::MakeRefPtr<InterpolatingSpring>(dealSpeed, 1.0f, 228.0f, 30.0f);
817     option.SetCurve(curve);
818     animation_ = AnimationUtils::StartAnimation(
819         option,
820         [&, weak = AceType::WeakClaim(this)]() {
821             auto pattern = weak.Upgrade();
822             CHECK_NULL_VOID(pattern);
823             pattern->offsetProperty_->Set(targetOffset);
824         },
825         [weak = AceType::WeakClaim(this), recycle]() {
826             auto pattern = weak.Upgrade();
827             CHECK_NULL_VOID(pattern);
828             if (recycle) {
829                 pattern->UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, pattern->GetFollowRatio());
830             }
831         });
832     auto context = PipelineContext::GetCurrentContextSafely();
833     CHECK_NULL_VOID(context);
834     context->RequestFrame();
835 }
836 
GetTargetOffset()837 float RefreshPattern::GetTargetOffset()
838 {
839     if (isSourceFromAnimation_) {
840         return 0.0f;
841     }
842     auto targetOffset = 0.0f;
843     switch (refreshStatus_) {
844         case RefreshStatus::OVER_DRAG:
845         case RefreshStatus::REFRESH:
846             targetOffset = refreshOffset_.ConvertToPx();
847             break;
848         default:
849             targetOffset = 0.0f;
850             break;
851     }
852     return targetOffset;
853 }
854 
SpeedAnimationFinish()855 void RefreshPattern::SpeedAnimationFinish()
856 {
857     if (isRefreshing_) {
858         UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
859     } else {
860         UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, GetFollowRatio());
861     }
862 }
863 
QuickFirstChildAppear()864 void RefreshPattern::QuickFirstChildAppear()
865 {
866     isSourceFromAnimation_ = false;
867     UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, GetFollowRatio());
868     ResetAnimation();
869     AnimationOption option;
870     option.SetCurve(DEFAULT_CURVE);
871     option.SetDuration(LOADING_ANIMATION_DURATION);
872     animation_ = AnimationUtils::StartAnimation(
873         option, [&]() { offsetProperty_->Set(static_cast<float>(refreshOffset_.ConvertToPx())); });
874 }
875 
QuickFirstChildDisappear()876 void RefreshPattern::QuickFirstChildDisappear()
877 {
878     ResetAnimation();
879     AnimationOption option;
880     option.SetCurve(DEFAULT_CURVE);
881     option.SetDuration(LOADING_ANIMATION_DURATION);
882     animation_ = AnimationUtils::StartAnimation(
883         option, [&]() { offsetProperty_->Set(0.0f); },
884         [weak = AceType::WeakClaim(this)]() {
885             auto pattern = weak.Upgrade();
886             CHECK_NULL_VOID(pattern);
887             pattern->SpeedAnimationFinish();
888         });
889 }
890 
GetLoadingProgressStatus()891 RefreshAnimationState RefreshPattern::GetLoadingProgressStatus()
892 {
893     auto defaultValue = RefreshAnimationState::FOLLOW_HAND;
894     CHECK_NULL_RETURN(progressChild_, defaultValue);
895     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
896     CHECK_NULL_RETURN(progressPaintProperty, defaultValue);
897     return progressPaintProperty->GetRefreshAnimationState().value_or(defaultValue);
898 }
899 
ResetAnimation()900 void RefreshPattern::ResetAnimation()
901 {
902     float currentOffset = scrollOffset_;
903     AnimationUtils::StopAnimation(animation_);
904     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
905         CHECK_NULL_VOID(offsetProperty_);
906         offsetProperty_->Set(currentOffset);
907     } else {
908         CHECK_NULL_VOID(lowVersionOffset_);
909         lowVersionOffset_->Set(currentOffset);
910     }
911 }
912 
UpdateDragFRCSceneInfo(const std::string & scene,float speed,SceneStatus sceneStatus)913 void RefreshPattern::UpdateDragFRCSceneInfo(const std::string& scene, float speed, SceneStatus sceneStatus)
914 {
915     auto host = GetHost();
916     CHECK_NULL_VOID(host);
917     host->AddFRCSceneInfo(scene, std::abs(speed), sceneStatus);
918 }
919 
InitLowVersionOffset()920 void RefreshPattern::InitLowVersionOffset()
921 {
922     if (!lowVersionOffset_) {
923         auto propertyCallback = [weak = AceType::WeakClaim(this)](float scrollOffset) {
924             auto pattern = weak.Upgrade();
925             CHECK_NULL_VOID(pattern);
926             pattern->scrollOffset_ = scrollOffset;
927             pattern->UpdateChild();
928         };
929         lowVersionOffset_ = AceType::MakeRefPtr<NodeAnimatablePropertyFloat>(0.0, std::move(propertyCallback));
930         auto host = GetHost();
931         CHECK_NULL_VOID(host);
932         auto renderContext = host->GetRenderContext();
933         CHECK_NULL_VOID(renderContext);
934         renderContext->AttachNodeAnimatableProperty(lowVersionOffset_);
935     }
936 }
937 
UpdateChild()938 void RefreshPattern::UpdateChild()
939 {
940     if (customBuilder_) {
941         UpdateCustomBuilderProperty();
942     } else {
943         UpdateLoadingProgress();
944     }
945 }
946 
HandleDragStartLowVersion()947 void RefreshPattern::HandleDragStartLowVersion()
948 {
949     if (isRefreshing_) {
950         return;
951     }
952     scrollOffset_ = 0.0f;
953     UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, 0.0f);
954 }
955 
HandleDragUpdateLowVersion(float delta)956 void RefreshPattern::HandleDragUpdateLowVersion(float delta)
957 {
958     if (isRefreshing_) {
959         return;
960     }
961     scrollOffset_ = GetScrollOffset(delta);
962     if (LessNotEqual(scrollOffset_, static_cast<float>(GetTriggerRefreshDisTance().ConvertToPx()))) {
963         UpdateRefreshStatus(RefreshStatus::DRAG);
964     } else {
965         UpdateRefreshStatus(RefreshStatus::OVER_DRAG);
966     }
967     if (customBuilder_) {
968         HandleCustomBuilderDragUpdateStage();
969         return;
970     }
971     UpdateLoadingProgress();
972     if (GreatNotEqual(scrollOffset_, triggerLoadingDistance_)) {
973         CHECK_NULL_VOID(progressChild_);
974         auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
975         CHECK_NULL_VOID(progressPaintProperty);
976         float triggerRefreshDistance = GetTriggerRefreshDisTance().ConvertToPx();
977         float ratio =
978             NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
979                 ? 1.0f
980                 : (scrollOffset_ - triggerLoadingDistance_) / (triggerRefreshDistance - triggerLoadingDistance_);
981         progressPaintProperty->UpdateRefreshSizeScaleRatio(std::clamp(ratio, 0.0f, 1.0f));
982     }
983 }
984 
HandleDragEndLowVersion()985 void RefreshPattern::HandleDragEndLowVersion()
986 {
987     if (isRefreshing_) {
988         return;
989     }
990     if (customBuilder_) {
991         HandleCustomBuilderDragEndStage();
992         return;
993     }
994     if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
995         UpdateRefreshStatus(RefreshStatus::REFRESH);
996         LoadingProgressRefreshingAnimation(true);
997     } else {
998         SwitchToFinish();
999         LoadingProgressExit();
1000     }
1001     // AccessibilityEventType::SCROLL_END
1002 }
1003 
LoadingProgressRefreshingAnimation(bool isDrag)1004 void RefreshPattern::LoadingProgressRefreshingAnimation(bool isDrag)
1005 {
1006     UpdateLoadingProgressStatus(RefreshAnimationState::RECYCLE, 1.0f);
1007     ResetAnimation();
1008     CHECK_NULL_VOID(lowVersionOffset_);
1009     AnimationOption option;
1010     if (isDrag) {
1011         option.SetCurve(AceType::MakeRefPtr<SpringCurve>(0.0f, 1.0f, 228.0f, 30.0f));
1012         option.SetDuration(FOLLOW_TO_RECYCLE_DURATION);
1013     } else {
1014         option.SetCurve(DEFAULT_CURVE);
1015         option.SetDuration(LOADING_ANIMATION_DURATION);
1016     }
1017     animation_ = AnimationUtils::StartAnimation(
1018         option, [&]() { lowVersionOffset_->Set(GetTriggerRefreshDisTance().ConvertToPx()); });
1019 }
1020 
LoadingProgressExit()1021 void RefreshPattern::LoadingProgressExit()
1022 {
1023     ResetAnimation();
1024     CHECK_NULL_VOID(lowVersionOffset_);
1025     AnimationOption option;
1026     option.SetCurve(DEFAULT_CURVE);
1027     option.SetDuration(LOADING_ANIMATION_DURATION);
1028     animation_ = AnimationUtils::StartAnimation(
1029         option, [&]() { lowVersionOffset_->Set(0.0f); },
1030         [weak = AceType::WeakClaim(this)]() {
1031             auto pattern = weak.Upgrade();
1032             CHECK_NULL_VOID(pattern);
1033             pattern->UpdateLoadingProgressStatus(RefreshAnimationState::FOLLOW_HAND, 0.0f);
1034         });
1035 }
1036 
UpdateLoadingProgress()1037 void RefreshPattern::UpdateLoadingProgress()
1038 {
1039     float loadingProgressOffset =
1040         std::clamp(scrollOffset_, triggerLoadingDistance_, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
1041     UpdateLoadingMarginTop(loadingProgressOffset);
1042     float triggerRefreshDistance = GetTriggerRefreshDisTance().ConvertToPx();
1043     float ratio = NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
1044                       ? 1.0f
1045                       : (loadingProgressOffset - triggerLoadingDistance_) /
1046                             (GetTriggerRefreshDisTance().ConvertToPx() - triggerLoadingDistance_);
1047     auto progressPaintProperty = progressChild_->GetPaintProperty<LoadingProgressPaintProperty>();
1048     CHECK_NULL_VOID(progressPaintProperty);
1049     progressPaintProperty->UpdateRefreshSizeScaleRatio(ratio);
1050     auto progressContext = progressChild_->GetRenderContext();
1051     CHECK_NULL_VOID(progressContext);
1052     progressContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
1053     UpdateLoadingTextOpacity(std::clamp(ratio, 0.0f, 1.0f));
1054     progressChild_->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1055 }
1056 
CustomBuilderRefreshingAnimation(bool isDrag)1057 void RefreshPattern::CustomBuilderRefreshingAnimation(bool isDrag)
1058 {
1059     ResetAnimation();
1060     CHECK_NULL_VOID(lowVersionOffset_);
1061     AnimationOption option;
1062     if (isDrag) {
1063         option.SetCurve(AceType::MakeRefPtr<SpringCurve>(0.0f, 1.0f, 228.0f, 30.0f));
1064         option.SetDuration(FOLLOW_TO_RECYCLE_DURATION);
1065     } else {
1066         option.SetCurve(DEFAULT_CURVE);
1067         option.SetDuration(CUSTOM_BUILDER_ANIMATION_DURATION);
1068     }
1069     animation_ = AnimationUtils::StartAnimation(
1070         option, [&]() { lowVersionOffset_->Set(GetTriggerRefreshDisTance().ConvertToPx()); });
1071 }
1072 
CustomBuilderExit()1073 void RefreshPattern::CustomBuilderExit()
1074 {
1075     ResetAnimation();
1076     CHECK_NULL_VOID(lowVersionOffset_);
1077     AnimationOption option;
1078     option.SetDuration(CUSTOM_BUILDER_ANIMATION_DURATION);
1079     option.SetCurve(DEFAULT_CURVE);
1080     animation_ = AnimationUtils::StartAnimation(option, [&]() { lowVersionOffset_->Set(0.0f); });
1081 }
1082 
UpdateCustomBuilderProperty()1083 void RefreshPattern::UpdateCustomBuilderProperty()
1084 {
1085     auto customBuilderSize = customBuilder_->GetGeometryNode()->GetFrameSize();
1086     auto maxScroll = static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx());
1087     customBuilderOffset_ = std::clamp(scrollOffset_, triggerLoadingDistance_, maxScroll - customBuilderSize.Height());
1088     float triggerRefreshDistance = GetTriggerRefreshDisTance().ConvertToPx();
1089     float ratio = NearEqual(triggerRefreshDistance, triggerLoadingDistance_)
1090                       ? 1.0f
1091                       : (customBuilderOffset_ - triggerLoadingDistance_) /
1092                             (GetTriggerRefreshDisTance().ConvertToPx() - triggerLoadingDistance_);
1093     auto customBuilderContext = customBuilder_->GetRenderContext();
1094     CHECK_NULL_VOID(customBuilderContext);
1095     customBuilderContext->UpdateOpacity(std::clamp(ratio, 0.0f, 1.0f));
1096     auto host = GetHost();
1097     CHECK_NULL_VOID(host);
1098     host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
1099 }
1100 
HandleCustomBuilderDragUpdateStage()1101 void RefreshPattern::HandleCustomBuilderDragUpdateStage()
1102 {
1103     auto customBuilderSize = customBuilder_->GetGeometryNode()->GetMarginFrameSize();
1104     auto maxScroll = MAX_SCROLL_DISTANCE.ConvertToPx();
1105     if (NearZero(static_cast<double>(customBuilder_->GetGeometryNode()->GetMarginFrameSize().Height()))) {
1106         return;
1107     }
1108     if (LessNotEqual(static_cast<double>(maxScroll - customBuilderSize.Height()),
1109             static_cast<double>(triggerLoadingDistance_))) {
1110         return;
1111     }
1112     UpdateCustomBuilderProperty();
1113 }
1114 
HandleCustomBuilderDragEndStage()1115 void RefreshPattern::HandleCustomBuilderDragEndStage()
1116 {
1117     if (refreshStatus_ == RefreshStatus::OVER_DRAG) {
1118         UpdateRefreshStatus(RefreshStatus::REFRESH);
1119         CustomBuilderRefreshingAnimation(true);
1120     } else {
1121         SwitchToFinish();
1122         CustomBuilderExit();
1123     }
1124 }
1125 
UpdateLoadingMarginTop(float top)1126 void RefreshPattern::UpdateLoadingMarginTop(float top)
1127 {
1128     CHECK_NULL_VOID(progressChild_);
1129     auto progressLayoutProperty = progressChild_->GetLayoutProperty<LoadingProgressLayoutProperty>();
1130     CHECK_NULL_VOID(progressLayoutProperty);
1131     MarginProperty marginProperty;
1132     marginProperty.left = CalcLength(0.0f);
1133     marginProperty.right = CalcLength(0.0f);
1134     marginProperty.bottom = CalcLength(0.0f);
1135     marginProperty.top = CalcLength(top);
1136     progressLayoutProperty->UpdateMargin(marginProperty);
1137 }
1138 
GetScrollOffset(float delta)1139 float RefreshPattern::GetScrollOffset(float delta)
1140 {
1141     auto layoutProperty = GetLayoutProperty<RefreshLayoutProperty>();
1142     CHECK_NULL_RETURN(layoutProperty, 0.0f);
1143     auto frictionRatio = static_cast<float>(layoutProperty->GetFriction().value_or(DEFAULT_FRICTION)) * PERCENT;
1144     auto scrollY = delta * frictionRatio;
1145     return std::clamp(scrollOffset_ + scrollY, 0.0f, static_cast<float>(MAX_SCROLL_DISTANCE.ConvertToPx()));
1146 }
1147 
HandleScroll(float offset,int32_t source,NestedState state,float velocity)1148 ScrollResult RefreshPattern::HandleScroll(float offset, int32_t source, NestedState state, float velocity)
1149 {
1150     ScrollResult result = { offset, true };
1151     auto nestedScroll = GetNestedScroll();
1152     if (NearZero(offset)) {
1153         return result;
1154     }
1155     isSourceFromAnimation_ = (source == SCROLL_FROM_ANIMATION);
1156     auto parent = GetNestedScrollParent();
1157     if (state == NestedState::CHILD_SCROLL) {
1158         if (Negative(offset) && Positive(scrollOffset_)) {
1159             if (parent && nestedScroll.forward == NestedScrollMode::PARENT_FIRST) {
1160                 result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL, velocity);
1161                 result = HandleDragUpdate(result.remain, velocity);
1162             } else if (parent && nestedScroll.forward == NestedScrollMode::SELF_FIRST) {
1163                 result = HandleDragUpdate(offset, velocity);
1164                 result = parent->HandleScroll(result.remain, source, NestedState::CHILD_SCROLL, velocity);
1165             } else {
1166                 result = HandleDragUpdate(offset, velocity);
1167             }
1168         } else {
1169             if (!parent || ((Negative(offset) && (nestedScroll.forward == NestedScrollMode::SELF_ONLY ||
1170                                                      nestedScroll.forward == NestedScrollMode::PARALLEL)) ||
1171                                (Positive(offset) && (nestedScroll.backward == NestedScrollMode::SELF_ONLY ||
1172                                                         nestedScroll.backward == NestedScrollMode::PARALLEL)))) {
1173                 return result;
1174             } else {
1175                 result = parent->HandleScroll(offset, source, NestedState::CHILD_SCROLL, velocity);
1176             }
1177         }
1178         return result;
1179     } else if (state == NestedState::CHILD_OVER_SCROLL) {
1180         if (parent && ((Negative(offset) && nestedScroll.forward == NestedScrollMode::SELF_FIRST) ||
1181                           (Positive(offset) && nestedScroll.backward == NestedScrollMode::SELF_FIRST))) {
1182             result = parent->HandleScroll(offset, source, NestedState::CHILD_OVER_SCROLL, velocity);
1183             if (!NearZero(result.remain)) {
1184                 result = HandleDragUpdate(result.remain, velocity);
1185             }
1186             return { 0.f, true };
1187         } else {
1188             result = HandleDragUpdate(offset, velocity);
1189         }
1190     }
1191     return result;
1192 }
1193 
OnScrollStartRecursive(WeakPtr<NestableScrollContainer> child,float position,float velocity)1194 void RefreshPattern::OnScrollStartRecursive(WeakPtr<NestableScrollContainer> child, float position, float velocity)
1195 {
1196     SetIsNestedInterrupt(false);
1197     if (!GetIsFixedNestedScrollMode()) {
1198         SetParentScrollable();
1199     }
1200     auto nestedScroll = GetNestedScroll();
1201     HandleDragStart(true, velocity);
1202     auto parent = GetNestedScrollParent();
1203     if (parent && nestedScroll.NeedParent() &&
1204         (nestedScroll.forward != NestedScrollMode::PARALLEL || nestedScroll.backward != NestedScrollMode::PARALLEL)) {
1205         parent->OnScrollStartRecursive(child, position, velocity);
1206     }
1207 }
1208 
HandleScrollVelocity(float velocity,const RefPtr<NestableScrollContainer> & child)1209 bool RefreshPattern::HandleScrollVelocity(float velocity, const RefPtr<NestableScrollContainer>& child)
1210 {
1211     auto parent = GetNestedScrollParent();
1212     auto nestedScroll = GetNestedScroll();
1213     bool result = false;
1214     if (parent && ((Negative(velocity) && nestedScroll.forward == NestedScrollMode::PARENT_FIRST) ||
1215                       (Positive(velocity) && nestedScroll.backward == NestedScrollMode::PARENT_FIRST))) {
1216         result = parent->HandleScrollVelocity(velocity);
1217         if (result) {
1218             return true;
1219         }
1220     }
1221     if (Positive(scrollOffset_) || Positive(velocity)) {
1222         HandleDragEnd(velocity);
1223         result = true;
1224     } else if (parent && ((Negative(velocity) && nestedScroll.forward == NestedScrollMode::SELF_FIRST) ||
1225                              (Positive(velocity) && nestedScroll.backward == NestedScrollMode::SELF_FIRST))) {
1226         result = parent->HandleScrollVelocity(velocity);
1227     }
1228     return result;
1229 }
1230 
OnScrollEndRecursive(const std::optional<float> & velocity)1231 void RefreshPattern::OnScrollEndRecursive(const std::optional<float>& velocity)
1232 {
1233     HandleDragEnd(velocity.value_or(0.f));
1234     auto parent = GetNestedScrollParent();
1235     auto nestedScroll = GetNestedScroll();
1236     if (parent && (nestedScroll.NeedParent() || GetIsNestedInterrupt())) {
1237         parent->OnScrollEndRecursive(velocity);
1238     }
1239     SetIsNestedInterrupt(false);
1240 }
1241 
DumpInfo()1242 void RefreshPattern::DumpInfo()
1243 {
1244     DumpLog::GetInstance().AddDesc(
1245         std::string("RefreshStatus: ").append(std::to_string(static_cast<int32_t>(refreshStatus_))));
1246 }
1247 } // namespace OHOS::Ace::NG
1248