/* * Copyright (c) 2021-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "frameworks/bridge/declarative_frontend/jsview/js_refresh.h" #include #include "interfaces/inner_api/ui_session/ui_session_manager.h" #include "base/log/ace_scoring_log.h" #include "bridge/declarative_frontend/engine/jsi/js_ui_index.h" #include "bridge/declarative_frontend/jsview/js_refresh.h" #include "bridge/declarative_frontend/jsview/js_view_common_def.h" #include "bridge/declarative_frontend/jsview/models/refresh_model_impl.h" #include "core/components_ng/base/view_stack_processor.h" #include "core/components_ng/pattern/refresh/refresh_model.h" #include "core/components_ng/pattern/refresh/refresh_model_ng.h" namespace OHOS::Ace { namespace { constexpr int32_t DEFAULT_FRICTION = 62; constexpr int32_t MAX_FRICTION = 100; } // namespace std::unique_ptr RefreshModel::instance_ = nullptr; std::mutex RefreshModel::mutex_; RefreshModel* RefreshModel::GetInstance() { if (!instance_) { std::lock_guard lock(mutex_); if (!instance_) { #ifdef NG_BUILD instance_.reset(new NG::RefreshModelNG()); #else if (Container::IsCurrentUseNewPipeline()) { instance_.reset(new NG::RefreshModelNG()); } else { instance_.reset(new Framework::RefreshModelImpl()); } #endif } } return instance_.get(); } } // namespace OHOS::Ace namespace OHOS::Ace::Framework { void ParseRefreshingObject(const JSCallbackInfo& info, const JSRef& changeEventVal) { CHECK_NULL_VOID(changeEventVal->IsFunction()); auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(changeEventVal)); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto changeEvent = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const std::string& param) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); if (param != "true" && param != "false") { return; } bool newValue = StringToBool(param); ACE_SCORING_EVENT("Refresh.ChangeEvent"); PipelineContext::SetCallBackNode(node); auto newJSVal = JSRef::Make(ToJSValue(newValue)); func->ExecuteJS(1, &newJSVal); }; RefreshModel::GetInstance()->SetChangeEvent(std::move(changeEvent)); } void JSRefresh::SetPullToRefresh(const JSCallbackInfo& info) { bool pullToRefresh = true; if (info[0]->IsBoolean()) { pullToRefresh = info[0]->ToBoolean(); } RefreshModel::GetInstance()->SetPullToRefresh(pullToRefresh); } void JSRefresh::JSBind(BindingTarget globalObj) { JSClass::Declare("Refresh"); MethodOptions opt = MethodOptions::NONE; JSClass::StaticMethod("create", &JSRefresh::Create, opt); JSClass::StaticMethod("refreshOffset", &JSRefresh::JsRefreshOffset); JSClass::StaticMethod("pullToRefresh", &JSRefresh::SetPullToRefresh, opt); JSClass::StaticMethod("onStateChange", &JSRefresh::OnStateChange); JSClass::StaticMethod("onRefreshing", &JSRefresh::OnRefreshing); JSClass::StaticMethod("onOffsetChange", &JSRefresh::OnOffsetChange); JSClass::StaticMethod("pullDownRatio", &JSRefresh::SetPullDownRatio); JSClass::StaticMethod("maxPullDownDistance", &JSRefresh::SetMaxPullDownDistance); JSClass::StaticMethod("onAttach", &JSInteractableView::JsOnAttach); JSClass::StaticMethod("onAppear", &JSInteractableView::JsOnAppear); JSClass::StaticMethod("onDetach", &JSInteractableView::JsOnDetach); JSClass::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear); JSClass::StaticMethod("onTouch", &JSInteractableView::JsOnTouch); JSClass::InheritAndBind(globalObj); } void JSRefresh::SetPullDownRatio(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } auto args = info[0]; std::optional pulldownRatio = std::nullopt; if (!args->IsNumber() || std::isnan(args->ToNumber())) { RefreshModel::GetInstance()->SetPullDownRatio(pulldownRatio); return; } pulldownRatio = std::clamp(args->ToNumber(), 0.f, 1.f); RefreshModel::GetInstance()->SetPullDownRatio(pulldownRatio); } void JSRefresh::SetMaxPullDownDistance(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } auto args = info[0]; if (!args->IsNumber()) { RefreshModel::GetInstance()->SetMaxPullDownDistance(std::nullopt); return; } float maxPullDownDistance = args->ToNumber(); if (std::isnan(maxPullDownDistance)) { RefreshModel::GetInstance()->SetMaxPullDownDistance(std::nullopt); return; } maxPullDownDistance = std::max(maxPullDownDistance, 0.0f); RefreshModel::GetInstance()->SetMaxPullDownDistance(maxPullDownDistance); } void JSRefresh::JsRefreshOffset(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } JsRefreshOffset(info[0]); } void JSRefresh::JsRefreshOffset(const JSRef& jsVal) { CalcDimension value(0.0f); if (!ParseJsDimensionVpNG(jsVal, value)) { value.SetValue(0.0f); } RefreshModel::GetInstance()->SetRefreshOffset(value); } void JSRefresh::Create(const JSCallbackInfo& info) { auto info0 = info[0]; if (!info0->IsObject()) { return; } auto paramObject = JSRef::Cast(info0); auto refreshing = paramObject->GetProperty(static_cast(ArkUIIndex::REFRESHING)); auto jsOffset = paramObject->GetProperty(static_cast(ArkUIIndex::OFFSET)); auto friction = paramObject->GetProperty(static_cast(ArkUIIndex::FRICTION)); auto promptText = paramObject->GetProperty(static_cast(ArkUIIndex::PROMPT_TEXT)); JSRef changeEventVal; RefreshModel::GetInstance()->Create(); if (refreshing->IsBoolean()) { RefreshModel::GetInstance()->SetRefreshing(refreshing->ToBoolean()); changeEventVal = paramObject->GetProperty(static_cast(ArkUIIndex::$REFRESHING)); ParseRefreshingObject(info, changeEventVal); } else if (refreshing->IsObject()) { JSRef refreshingObj = JSRef::Cast(refreshing); changeEventVal = refreshingObj->GetProperty(static_cast(ArkUIIndex::CHANGE_EVENT)); ParseRefreshingObject(info, changeEventVal); RefreshModel::GetInstance()->SetRefreshing( refreshingObj->GetProperty(static_cast(ArkUIIndex::VALUE))->ToBoolean()); } else { RefreshModel::GetInstance()->SetRefreshing(false); } CalcDimension offset; if (ParseJsDimensionVp(jsOffset, offset)) { if (GreatOrEqual(offset.Value(), 0.0) && offset.Unit() != DimensionUnit::PERCENT) { RefreshModel::GetInstance()->SetIndicatorOffset(offset); } } ParsFrictionData(friction); if (!ParseRefreshingContent(paramObject)) { bool isCustomBuilderExist = ParseCustomBuilder(info); RefreshModel::GetInstance()->SetIsCustomBuilderExist(isCustomBuilderExist); } std::string loadingStr = ""; if (SystemProperties::ConfigChangePerform()) { RefPtr resObj; if (ParseJsString(promptText, loadingStr, resObj)) { RefreshModel::GetInstance()->CreateWithResourceObj(resObj); RefreshModel::GetInstance()->SetLoadingText(loadingStr); } else { RefreshModel::GetInstance()->ResetLoadingText(); } } else if (ParseJsString(promptText, loadingStr)) { RefreshModel::GetInstance()->SetLoadingText(loadingStr); } else { RefreshModel::GetInstance()->ResetLoadingText(); } } bool JSRefresh::ParseRefreshingContent(const JSRef& paramObject) { JSRef contentParam = paramObject->GetProperty(static_cast(ArkUIIndex::REFRESHING_CONTENT)); if (!contentParam->IsObject()) { return false; } JSRef contentObject = JSRef::Cast(contentParam); JSRef builderNodeParam = contentObject->GetProperty(static_cast(ArkUIIndex::BUILDER_NODE)); if (!builderNodeParam->IsObject()) { return false; } JSRef builderNodeObject = JSRef::Cast(builderNodeParam); JSRef nodeptr = builderNodeObject->GetProperty(static_cast(ArkUIIndex::NODEPTR)); if (nodeptr.IsEmpty()) { return false; } const auto* vm = nodeptr->GetEcmaVM(); CHECK_NULL_RETURN(nodeptr->GetLocalHandle()->IsNativePointer(vm), false); auto* node = nodeptr->GetLocalHandle()->ToNativePointer(vm)->Value(); auto* frameNode = reinterpret_cast(node); CHECK_NULL_RETURN(frameNode, false); RefPtr refPtrFrameNode = AceType::Claim(frameNode); RefreshModel::GetInstance()->SetCustomBuilder(refPtrFrameNode); RefreshModel::GetInstance()->SetIsCustomBuilderExist(false); return true; } bool JSRefresh::ParseCustomBuilder(const JSCallbackInfo& info) { if (!info[0]->IsObject()) { return false; } auto paramObject = JSRef::Cast(info[0]); auto builder = paramObject->GetProperty(static_cast(ArkUIIndex::BUILDER)); RefPtr customNode; if (builder->IsFunction()) { { NG::ScopedViewStackProcessor builderViewStackProcessor; JsFunction Jsfunc(info.This(), JSRef::Cast(builder)); Jsfunc.Execute(); customNode = NG::ViewStackProcessor::GetInstance()->Finish(); } RefreshModel::GetInstance()->SetCustomBuilder(customNode); return true; } else { RefreshModel::GetInstance()->SetCustomBuilder(customNode); return false; } } void JSRefresh::OnStateChange(const JSCallbackInfo& args) { if (!args[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(args[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onStateChange = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const int32_t& value) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Refresh.OnStateChange"); PipelineContext::SetCallBackNode(node); auto newJSVal = JSRef::Make(ToJSValue(value)); func->ExecuteJS(1, &newJSVal); UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "Refresh.OnStateChange"); }; RefreshModel::GetInstance()->SetOnStateChange(std::move(onStateChange)); } void JSRefresh::OnRefreshing(const JSCallbackInfo& args) { if (!args[0]->IsFunction()) { return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(args[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onRefreshing = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]() { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Refresh.OnRefreshing"); PipelineContext::SetCallBackNode(node); auto newJSVal = JSRef::Make(); func->ExecuteJS(1, &newJSVal); }; RefreshModel::GetInstance()->SetOnRefreshing(std::move(onRefreshing)); } void JSRefresh::OnOffsetChange(const JSCallbackInfo& args) { if (!args[0]->IsFunction()) { RefreshModel::GetInstance()->ResetOnOffsetChange(); return; } auto jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(args[0])); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto offsetChange = [execCtx = args.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]( const float& value) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Refresh.OnOffsetChange"); PipelineContext::SetCallBackNode(node); auto newJSVal = JSRef::Make(ToJSValue(value)); func->ExecuteJS(1, &newJSVal); }; RefreshModel::GetInstance()->SetOnOffsetChange(std::move(offsetChange)); } void JSRefresh::ParsFrictionData(const JsiRef& friction) { int32_t frictionNumber = DEFAULT_FRICTION; if (friction->IsString()) { frictionNumber = StringUtils::StringToInt(friction->ToString()); if ((frictionNumber == 0 && friction->ToString() != "0") || frictionNumber < 0 || frictionNumber > MAX_FRICTION) { frictionNumber = DEFAULT_FRICTION; } } else if (friction->IsNumber()) { frictionNumber = friction->ToNumber(); if (frictionNumber < 0 || frictionNumber > MAX_FRICTION) { frictionNumber = DEFAULT_FRICTION; } } RefreshModel::GetInstance()->SetFriction(frictionNumber); } } // namespace OHOS::Ace::Framework