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