/* * 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 #if !defined(PREVIEW) && defined(OHOS_PLATFORM) #include "interfaces/inner_api/ui_session/ui_session_manager.h" #endif #include "base/log/ace_scoring_log.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/refresh/refresh_theme.h" #include "core/components_ng/base/view_stack_processor.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& refreshing) { JSRef changeEventVal = refreshing->GetProperty("changeEvent"); 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("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::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) { if (!info[0]->IsObject()) { return; } RefPtr theme = GetTheme(); if (!theme) { return; } auto paramObject = JSRef::Cast(info[0]); auto refreshing = paramObject->GetProperty("refreshing"); auto jsOffset = paramObject->GetProperty("offset"); auto friction = paramObject->GetProperty("friction"); auto promptText = paramObject->GetProperty("promptText"); RefreshModel::GetInstance()->Create(); if (refreshing->IsBoolean()) { RefreshModel::GetInstance()->SetRefreshing(refreshing->ToBoolean()); } else if (refreshing->IsObject()) { JSRef refreshingObj = JSRef::Cast(refreshing); ParseRefreshingObject(info, refreshingObj); RefreshModel::GetInstance()->SetRefreshing(refreshingObj->GetProperty("value")->ToBoolean()); } else { RefreshModel::GetInstance()->SetRefreshing(false); } CalcDimension offset; if (ParseJsDimensionVp(jsOffset, offset)) { if (LessNotEqual(offset.Value(), 0.0) || offset.Unit() == DimensionUnit::PERCENT) { RefreshModel::GetInstance()->SetRefreshDistance(theme->GetRefreshDistance()); } else { RefreshModel::GetInstance()->SetIndicatorOffset(offset); } } ParsFrictionData(friction); if (!ParseRefreshingContent(paramObject)) { bool isCustomBuilderExist = ParseCustomBuilder(info); RefreshModel::GetInstance()->SetIsCustomBuilderExist(isCustomBuilderExist); } std::string loadingStr = ""; if (ParseJsString(promptText, loadingStr)) { RefreshModel::GetInstance()->SetLoadingText(loadingStr); } else { RefreshModel::GetInstance()->ResetLoadingText(); } } bool JSRefresh::ParseRefreshingContent(const JSRef& paramObject) { JSRef contentParam = paramObject->GetProperty("refreshingContent"); if (!contentParam->IsObject()) { return false; } JSRef contentObject = JSRef::Cast(contentParam); JSRef builderNodeParam = contentObject->GetProperty("builderNode_"); if (!builderNodeParam->IsObject()) { return false; } JSRef builderNodeObject = JSRef::Cast(builderNodeParam); JSRef nodeptr = builderNodeObject->GetProperty("nodePtr_"); if (nodeptr.IsEmpty()) { return false; } const auto* vm = nodeptr->GetEcmaVM(); 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("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); #if !defined(PREVIEW) && defined(OHOS_PLATFORM) UiSessionManager::GetInstance().ReportComponentChangeEvent("event", "Refresh.OnStateChange"); #endif }; 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