• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 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 "frameworks/bridge/declarative_frontend/jsview/js_text_clock.h"
17 
18 #include <regex>
19 #include <string>
20 
21 #include "base/log/ace_scoring_log.h"
22 #include "base/utils/string_utils.h"
23 #include "bridge/declarative_frontend/jsview/js_view_abstract.h"
24 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
25 #include "bridge/declarative_frontend/jsview/js_utils.h"
26 #include "bridge/declarative_frontend/jsview/models/text_clock_model_impl.h"
27 #include "core/components/common/properties/text_style.h"
28 #include "core/components/common/properties/text_style_parser.h"
29 #include "core/components_ng/base/view_stack_processor.h"
30 #include "core/components_ng/pattern/text_clock/text_clock_model.h"
31 #include "core/components_ng/pattern/text_clock/text_clock_model_ng.h"
32 #include "frameworks/core/components/text_clock/text_clock_theme.h"
33 
34 namespace OHOS::Ace {
35 
36 std::unique_ptr<TextClockModel> TextClockModel::instance_ = nullptr;
37 std::mutex TextClockModel::mutex_;
38 const char TEXTCLOCK_DATE_TIME_OPTIONS_HOUR[] = "hour";
39 const std::string TEXTCLOCK_DATE_TIME_OPTIONS_TWO_DIGIT_VAL = "2-digit";
40 const std::string TEXTCLOCK_DATE_TIME_OPTIONS_NUMERIC_VAL = "numeric";
41 
GetInstance()42 TextClockModel* TextClockModel::GetInstance()
43 {
44     if (!instance_) {
45         std::lock_guard<std::mutex> lock(mutex_);
46         if (!instance_) {
47 #ifdef NG_BUILD
48             instance_.reset(new NG::TextClockModelNG());
49 #else
50             if (Container::IsCurrentUseNewPipeline()) {
51                 instance_.reset(new NG::TextClockModelNG());
52             } else {
53                 instance_.reset(new Framework::TextClockModelImpl());
54             }
55 #endif
56         }
57     }
58     return instance_.get();
59 }
60 } // namespace OHOS::Ace
61 
62 namespace OHOS::Ace::Framework {
63 
64 namespace {
65 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
66 const std::string DEFAULT_FORMAT_API_TEN = "hms";
67 constexpr int32_t HOURS_WEST_LOWER_LIMIT = -14;
68 constexpr int32_t HOURS_WEST_UPPER_LIMIT = 12;
69 constexpr float HOURS_WEST[] = { 9.5f, 3.5f, -3.5f, -4.5f, -5.5f, -5.75f, -6.5f, -9.5f, -10.5f, -12.75f };
70 
HoursWestIsValid(int32_t hoursWest)71 bool HoursWestIsValid(int32_t hoursWest)
72 {
73     return !(hoursWest < HOURS_WEST_LOWER_LIMIT || hoursWest > HOURS_WEST_UPPER_LIMIT);
74 }
75 
GetHoursWest(float hoursWest)76 float GetHoursWest(float hoursWest)
77 {
78     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN)) {
79         for (float i : HOURS_WEST) {
80             if (NearEqual(hoursWest, i)) {
81                 return hoursWest;
82             }
83         }
84     }
85 
86     return int32_t(hoursWest);
87 }
88 } // namespace
89 
Create(const JSCallbackInfo & info)90 void JSTextClock::Create(const JSCallbackInfo& info)
91 {
92     auto controller = TextClockModel::GetInstance()->Create();
93     if (info.Length() < 1 || !info[0]->IsObject()) {
94         return;
95     }
96     JSRef<JSObject> optionsObject = JSRef<JSObject>::Cast(info[0]);
97     JSRef<JSVal> hourWestVal = optionsObject->GetProperty("timeZoneOffset");
98     if (hourWestVal->IsNumber() && HoursWestIsValid(hourWestVal->ToNumber<int32_t>())) {
99         float hourWest = GetHoursWest(hourWestVal->ToNumber<float>());
100         TextClockModel::GetInstance()->SetHoursWest(hourWest);
101     } else {
102         TextClockModel::GetInstance()->SetHoursWest(NAN);
103     }
104     auto controllerObj = optionsObject->GetProperty("controller");
105     if (!controllerObj->IsUndefined() && !controllerObj->IsNull() && controllerObj->IsObject()) {
106         auto* jsController = JSRef<JSObject>::Cast(controllerObj)->Unwrap<JSTextClockController>();
107         if (jsController != nullptr) {
108             jsController->SetInstanceId(Container::CurrentId());
109             if (controller) {
110                 jsController->AddController(controller);
111             }
112         }
113         return;
114     }
115 }
116 
JSBind(BindingTarget globalObj)117 void JSTextClock::JSBind(BindingTarget globalObj)
118 {
119     JSClass<JSTextClock>::Declare("TextClock");
120     MethodOptions opt = MethodOptions::NONE;
121     JSClass<JSTextClock>::StaticMethod("create", &JSTextClock::Create, opt);
122     JSClass<JSTextClock>::StaticMethod("format", &JSTextClock::SetFormat, opt);
123     JSClass<JSTextClock>::StaticMethod("onDateChange", &JSTextClock::JsOnDateChange, opt);
124     JSClass<JSTextClock>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
125     JSClass<JSTextClock>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
126     JSClass<JSTextClock>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
127     JSClass<JSTextClock>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
128     JSClass<JSTextClock>::StaticMethod("onAttach", &JSInteractableView::JsOnAttach);
129     JSClass<JSTextClock>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
130     JSClass<JSTextClock>::StaticMethod("onDetach", &JSInteractableView::JsOnDetach);
131     JSClass<JSTextClock>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
132     JSClass<JSTextClock>::StaticMethod("fontColor", &JSTextClock::SetTextColor, opt);
133     JSClass<JSTextClock>::StaticMethod("fontSize", &JSTextClock::SetFontSize, opt);
134     JSClass<JSTextClock>::StaticMethod("fontWeight", &JSTextClock::SetFontWeight, opt);
135     JSClass<JSTextClock>::StaticMethod("fontStyle", &JSTextClock::SetFontStyle, opt);
136     JSClass<JSTextClock>::StaticMethod("fontFamily", &JSTextClock::SetFontFamily, opt);
137     JSClass<JSTextClock>::StaticMethod("textShadow", &JSTextClock::SetTextShadow, opt);
138     JSClass<JSTextClock>::StaticMethod("fontFeature", &JSTextClock::SetFontFeature, opt);
139     JSClass<JSTextClock>::StaticMethod("dateTimeOptions", &JSTextClock::SetDateTimeOptions, opt);
140     JSClass<JSTextClock>::InheritAndBind<JSViewAbstract>(globalObj);
141 }
142 
SetTextColor(const JSCallbackInfo & info)143 void JSTextClock::SetTextColor(const JSCallbackInfo& info)
144 {
145     if (info.Length() < 1) {
146         return;
147     }
148     Color textColor;
149     RefPtr<ResourceObject> resObj;
150     bool colorParsed = SystemProperties::ConfigChangePerform() ? ParseJsColor(info[0], textColor, resObj)
151                                                                : ParseJsColor(info[0], textColor);
152     if (SystemProperties::ConfigChangePerform()) {
153         TextClockModel::GetInstance()->CreateWithTextColorResourceObj(resObj);
154     }
155     if (!colorParsed) {
156         TextClockModel::GetInstance()->ResetTextColor();
157         return;
158     }
159     TextClockModel::GetInstance()->SetTextColor(textColor);
160 }
161 
SetFontSize(const JSCallbackInfo & info)162 void JSTextClock::SetFontSize(const JSCallbackInfo& info)
163 {
164     if (info.Length() < 1) {
165         return;
166     }
167     auto pipelineContext = PipelineContext::GetCurrentContext();
168     CHECK_NULL_VOID(pipelineContext);
169     auto theme = pipelineContext->GetTheme<TextClockTheme>();
170     CHECK_NULL_VOID(theme);
171 
172     CalcDimension fontSize;
173     RefPtr<ResourceObject> resObj;
174     bool parseSuccess = SystemProperties::ConfigChangePerform() ? ParseJsDimensionFpNG(info[0], fontSize, resObj, false)
175                                                                 : ParseJsDimensionFpNG(info[0], fontSize, false);
176     if (SystemProperties::ConfigChangePerform()) {
177         TextClockModel::GetInstance()->CreateWithFontSizeResourceObj(resObj);
178     }
179      auto getDefaultFontSize = [&]() {
180         return theme->GetTextStyleClock().GetFontSize();
181     };
182 
183     if (!parseSuccess || fontSize.IsNegative() || fontSize.Unit() == DimensionUnit::PERCENT) {
184         fontSize = getDefaultFontSize();
185     }
186 
187     TextClockModel::GetInstance()->SetFontSize(fontSize);
188 }
189 
SetFontWeight(const JSCallbackInfo & info)190 void JSTextClock::SetFontWeight(const JSCallbackInfo& info)
191 {
192     if (info.Length() < 1) {
193         return;
194     }
195     RefPtr<TextClockTheme> textTheme = GetTheme<TextClockTheme>();
196     CHECK_NULL_VOID(textTheme);
197     const auto& fontWeight = info[0];
198     RefPtr<ResourceObject> weightResObj;
199     if (fontWeight->IsUndefined()) {
200         TextClockModel::GetInstance()->SetFontWeight(textTheme->GetTextStyleClock().GetFontWeight());
201         return;
202     }
203 
204     if (!fontWeight->IsNull()) {
205         std::string weight;
206         if (fontWeight->IsNumber()) {
207             weight = std::to_string(fontWeight->ToNumber<int32_t>());
208         } else {
209             ParseJsString(fontWeight, weight, weightResObj);
210             if (SystemProperties::ConfigChangePerform()) {
211                 TextClockModel::GetInstance()->CreateWithFontWeightResourceObj(weightResObj);
212             }
213         }
214         TextClockModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(weight));
215     } else {
216         TextClockModel::GetInstance()->SetFontWeight(textTheme->GetTextStyleClock().GetFontWeight());
217     }
218 }
219 
SetFontStyle(int32_t value)220 void JSTextClock::SetFontStyle(int32_t value)
221 {
222     if (value < 0 || value >= static_cast<int32_t>(FONT_STYLES.size())) {
223         return;
224     }
225     TextClockModel::GetInstance()->SetItalicFontStyle(FONT_STYLES[value]);
226 }
227 
SetFontFamily(const JSCallbackInfo & info)228 void JSTextClock::SetFontFamily(const JSCallbackInfo& info)
229 {
230     if (info.Length() < 1) {
231         return;
232     }
233     std::vector<std::string> fontFamilies;
234     RefPtr<ResourceObject> resObj;
235 
236     bool parseSuccess = SystemProperties::ConfigChangePerform() ? ParseJsFontFamilies(info[0], fontFamilies, resObj)
237                                                                 : ParseJsFontFamilies(info[0], fontFamilies);
238     if (SystemProperties::ConfigChangePerform()) {
239         TextClockModel::GetInstance()->CreateWithFontFamilyResourceObj(resObj);
240     }
241     if (parseSuccess) {
242         TextClockModel::GetInstance()->SetFontFamily(fontFamilies);
243     }
244 }
245 
SetFormat(const JSCallbackInfo & info)246 void JSTextClock::SetFormat(const JSCallbackInfo& info)
247 {
248     if (info.Length() < 1) {
249         return;
250     }
251     if (!info[0]->IsString() && !info[0]->IsObject()) {
252         if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_ELEVEN)) {
253             TextClockModel::GetInstance()->SetFormat("");
254         } else {
255             TextClockModel::GetInstance()->SetFormat(DEFAULT_FORMAT_API_TEN);
256         }
257         if (SystemProperties::ConfigChangePerform()) {
258             TextClockModel::GetInstance()->RemoveResObjByKey("textClockFormat");
259         }
260         return;
261     }
262     std::string format;
263     RefPtr<ResourceObject> resObj;
264     static const std::string TEXT_CLOCK_FORMAT_REGEX =
265             R"(^([Yy]*[_|\W\s]*[M]*[_|\W\s]*[d]*[_|\W\s]*[D]*[_|\W\s]*[Hh]*[_|\W\s]*[m]*[_|\W\s]*[s]*[_|\W\s]*[S]*)$)";
266     if (SystemProperties::ConfigChangePerform()) {
267         ParseJsString(info[0], format, resObj);
268         TextClockModel::GetInstance()->CreateWithFormatResourceObj(resObj);
269         if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_ELEVEN)) {
270             std::regex pattern(TEXT_CLOCK_FORMAT_REGEX);
271             if (format.empty() || !StringUtils::IsAscii(format) || !std::regex_match(format, pattern)) {
272                 TextClockModel::GetInstance()->SetFormat(DEFAULT_FORMAT_API_TEN);
273                 return;
274             }
275         }
276 
277         TextClockModel::GetInstance()->SetFormat(format);
278     } else {
279         ParseJsString(info[0], format);
280         if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_ELEVEN)) {
281             std::regex pattern(TEXT_CLOCK_FORMAT_REGEX);
282             if (format.empty() || !StringUtils::IsAscii(format) || !std::regex_match(format, pattern)) {
283                 TextClockModel::GetInstance()->SetFormat(DEFAULT_FORMAT_API_TEN);
284                 return;
285             }
286         }
287 
288         TextClockModel::GetInstance()->SetFormat(format);
289     }
290 }
291 
SetTextShadow(const JSCallbackInfo & info)292 void JSTextClock::SetTextShadow(const JSCallbackInfo& info)
293 {
294     if (info.Length() < 1) {
295         return;
296     }
297     std::vector<Shadow> shadows;
298     ParseTextShadowFromShadowObject(info[0], shadows);
299     if (!shadows.empty()) {
300         TextClockModel::GetInstance()->SetTextShadow(shadows);
301     } else {
302         if (SystemProperties::ConfigChangePerform()) {
303             TextClockModel::GetInstance()->RemoveResObjByKey("textClock.shadow");
304         }
305     }
306 }
307 
SetFontFeature(const JSCallbackInfo & info)308 void JSTextClock::SetFontFeature(const JSCallbackInfo& info)
309 {
310     if (info.Length() < 1) {
311         return;
312     }
313     if (!info[0]->IsString()) {
314         return;
315     }
316 
317     std::string fontFeatureSettings = info[0]->ToString();
318     TextClockModel::GetInstance()->SetFontFeature(ParseFontFeatureSettings(fontFeatureSettings));
319 }
320 
JsOnDateChange(const JSCallbackInfo & info)321 void JSTextClock::JsOnDateChange(const JSCallbackInfo& info)
322 {
323     if (!info[0]->IsFunction()) {
324         return;
325     }
326 
327     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
328     WeakPtr<NG::FrameNode> targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
329     auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode](
330                         const std::string& value) {
331         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
332         ACE_SCORING_EVENT("TextClock.onDateChange");
333         PipelineContext::SetCallBackNode(node);
334         auto newJSVal = JSRef<JSVal>::Make(ToJSValue(value));
335         func->ExecuteJS(1, &newJSVal);
336     };
337     TextClockModel::GetInstance()->SetOnDateChange(std::move(onChange));
338 }
339 
JSBind(BindingTarget globalObj)340 void JSTextClockController::JSBind(BindingTarget globalObj)
341 {
342     JSClass<JSTextClockController>::Declare("TextClockController");
343     JSClass<JSTextClockController>::Method("start", &JSTextClockController::Start);
344     JSClass<JSTextClockController>::Method("stop", &JSTextClockController::Stop);
345     JSClass<JSTextClockController>::Bind(
346         globalObj, JSTextClockController::Constructor, JSTextClockController::Destructor);
347 }
348 
Constructor(const JSCallbackInfo & args)349 void JSTextClockController::Constructor(const JSCallbackInfo& args)
350 {
351     auto scroller = Referenced::MakeRefPtr<JSTextClockController>();
352     scroller->IncRefCount();
353     args.SetReturnValue(Referenced::RawPtr(scroller));
354 }
355 
Destructor(JSTextClockController * scroller)356 void JSTextClockController::Destructor(JSTextClockController* scroller)
357 {
358     if (scroller != nullptr) {
359         scroller->DecRefCount();
360     }
361 }
362 
Start()363 void JSTextClockController::Start()
364 {
365     ContainerScope scope(instanceId_);
366     if (!controller_.empty()) {
367         for (auto& i : controller_) {
368             i->Start();
369         }
370     }
371 }
372 
Stop()373 void JSTextClockController::Stop()
374 {
375     ContainerScope scope(instanceId_);
376     if (!controller_.empty()) {
377         for (auto& i : controller_) {
378             i->Stop();
379         }
380     }
381 }
382 
removeController(const RefPtr<TextClockController> & controller)383 void JSTextClockController::removeController(const RefPtr<TextClockController>& controller)
384 {
385     for (auto it = controller_.begin(); it != controller_.end(); it++) {
386         if (controller == *it) {
387             controller_.erase(it);
388             break;
389         }
390     }
391 }
392 
SetDateTimeOptions(const JSCallbackInfo & info)393 void JSTextClock::SetDateTimeOptions(const JSCallbackInfo& info)
394 {
395     JSRef<JSObject> paramObject;
396     ZeroPrefixType hourType = ZeroPrefixType::AUTO;
397     if (info.Length() >= 1 && info[0]->IsObject()) {
398         paramObject = JSRef<JSObject>::Cast(info[0]);
399         auto hourValue = paramObject->GetProperty(TEXTCLOCK_DATE_TIME_OPTIONS_HOUR);
400         if (hourValue->IsString()) {
401             std::string hour = hourValue->ToString();
402             if (hour == TEXTCLOCK_DATE_TIME_OPTIONS_TWO_DIGIT_VAL) {
403                 hourType = ZeroPrefixType::SHOW;
404             } else if (hour == TEXTCLOCK_DATE_TIME_OPTIONS_NUMERIC_VAL) {
405                 hourType = ZeroPrefixType::HIDE;
406             }
407         }
408     }
409     TextClockModel::GetInstance()->SetDateTimeOptions(hourType);
410 }
411 } // namespace OHOS::Ace::Framework
412