• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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/models/text_clock_model_impl.h"
26 #include "core/components/common/properties/text_style.h"
27 #include "core/components_ng/pattern/text_clock/text_clock_model.h"
28 #include "core/components_ng/pattern/text_clock/text_clock_model_ng.h"
29 
30 namespace OHOS::Ace {
31 
32 std::unique_ptr<TextClockModel> TextClockModel::instance_ = nullptr;
33 std::mutex TextClockModel::mutex_;
34 
GetInstance()35 TextClockModel* TextClockModel::GetInstance()
36 {
37     if (!instance_) {
38         std::lock_guard<std::mutex> lock(mutex_);
39         if (!instance_) {
40 #ifdef NG_BUILD
41             instance_.reset(new NG::TextClockModelNG());
42 #else
43             if (Container::IsCurrentUseNewPipeline()) {
44                 instance_.reset(new NG::TextClockModelNG());
45             } else {
46                 instance_.reset(new Framework::TextClockModelImpl());
47             }
48 #endif
49         }
50     }
51     return instance_.get();
52 }
53 } // namespace OHOS::Ace
54 
55 namespace OHOS::Ace::Framework {
56 
57 namespace {
58 const std::vector<FontStyle> FONT_STYLES = { FontStyle::NORMAL, FontStyle::ITALIC };
59 const int32_t TWENTY_FOUR_HOUR_BASE = 24;
60 const std::string DEFAULT_FORMAT = "hms";
61 constexpr int32_t HOURS_WEST_LOWER_LIMIT = -14;
62 constexpr int32_t HOURS_WEST_UPPER_LIMIT = 12;
63 constexpr int32_t HOURS_WEST_GEOGRAPHICAL_LOWER_LIMIT = -12;
64 constexpr int32_t PLATFORM_VERSION_ELEVEN = 11;
65 
HoursWestIsValid(int32_t hoursWest)66 bool HoursWestIsValid(int32_t hoursWest)
67 {
68     if (hoursWest < HOURS_WEST_LOWER_LIMIT || hoursWest > HOURS_WEST_UPPER_LIMIT) {
69         return false;
70     }
71     if (hoursWest < HOURS_WEST_GEOGRAPHICAL_LOWER_LIMIT) {
72         hoursWest += TWENTY_FOUR_HOUR_BASE;
73     }
74     return true;
75 }
76 } // namespace
77 
Create(const JSCallbackInfo & info)78 void JSTextClock::Create(const JSCallbackInfo& info)
79 {
80     auto controller = TextClockModel::GetInstance()->Create();
81     if (info.Length() < 1 || !info[0]->IsObject()) {
82         SetFontDefault();
83         LOGD("TextClock Info is non-valid");
84         return;
85     }
86     JSRef<JSObject> optionsObject = JSRef<JSObject>::Cast(info[0]);
87     JSRef<JSVal> hourWestVal = optionsObject->GetProperty("timeZoneOffset");
88     if (hourWestVal->IsNumber() && HoursWestIsValid(hourWestVal->ToNumber<int32_t>())) {
89         TextClockModel::GetInstance()->SetHoursWest(hourWestVal->ToNumber<int32_t>());
90     } else {
91         TextClockModel::GetInstance()->SetHoursWest(INT_MAX);
92         LOGE("hourWest args is invalid");
93     }
94     auto controllerObj = optionsObject->GetProperty("controller");
95     if (!controllerObj->IsUndefined() && !controllerObj->IsNull() && controllerObj->IsObject()) {
96         auto* jsController = JSRef<JSObject>::Cast(controllerObj)->Unwrap<JSTextClockController>();
97         if (jsController != nullptr) {
98             if (controller) {
99                 jsController->AddController(controller);
100             } else {
101                 LOGE("TextClockController is nullptr");
102             }
103         }
104         return;
105     }
106     LOGE("controllerObj is nullptr or undefined or invalid");
107 }
108 
JSBind(BindingTarget globalObj)109 void JSTextClock::JSBind(BindingTarget globalObj)
110 {
111     JSClass<JSTextClock>::Declare("TextClock");
112     MethodOptions opt = MethodOptions::NONE;
113     JSClass<JSTextClock>::StaticMethod("create", &JSTextClock::Create, opt);
114     JSClass<JSTextClock>::StaticMethod("format", &JSTextClock::SetFormat, opt);
115     JSClass<JSTextClock>::StaticMethod("onDateChange", &JSTextClock::JsOnDateChange, opt);
116     JSClass<JSTextClock>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
117     JSClass<JSTextClock>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
118     JSClass<JSTextClock>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
119     JSClass<JSTextClock>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
120     JSClass<JSTextClock>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
121     JSClass<JSTextClock>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
122     JSClass<JSTextClock>::StaticMethod("fontColor", &JSTextClock::SetTextColor, opt);
123     JSClass<JSTextClock>::StaticMethod("fontSize", &JSTextClock::SetFontSize, opt);
124     JSClass<JSTextClock>::StaticMethod("fontWeight", &JSTextClock::SetFontWeight, opt);
125     JSClass<JSTextClock>::StaticMethod("fontStyle", &JSTextClock::SetFontStyle, opt);
126     JSClass<JSTextClock>::StaticMethod("fontFamily", &JSTextClock::SetFontFamily, opt);
127     JSClass<JSTextClock>::InheritAndBind<JSViewAbstract>(globalObj);
128 }
129 
SetFontDefault()130 void JSTextClock::SetFontDefault()
131 {
132     RefPtr<TextTheme> textTheme = GetTheme<TextTheme>();
133     TextClockModel::GetInstance()->SetFontSize(textTheme->GetTextStyle().GetFontSize());
134     TextClockModel::GetInstance()->SetTextColor(textTheme->GetTextStyle().GetTextColor());
135     TextClockModel::GetInstance()->SetFontFamily(textTheme->GetTextStyle().GetFontFamilies());
136     TextClockModel::GetInstance()->SetFontWeight(textTheme->GetTextStyle().GetFontWeight());
137     TextClockModel::GetInstance()->SetItalicFontStyle(textTheme->GetTextStyle().GetFontStyle());
138 }
139 
SetTextColor(const JSCallbackInfo & info)140 void JSTextClock::SetTextColor(const JSCallbackInfo& info)
141 {
142     if (info.Length() < 1) {
143         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
144         return;
145     }
146     Color textColor;
147     if (!ParseJsColor(info[0], textColor)) {
148         auto pipelineContext = PipelineContext::GetCurrentContext();
149         CHECK_NULL_VOID_NOLOG(pipelineContext);
150         auto theme = pipelineContext->GetTheme<TextTheme>();
151         textColor = theme->GetTextStyle().GetTextColor();
152     }
153 
154     TextClockModel::GetInstance()->SetTextColor(textColor);
155 }
156 
SetFontSize(const JSCallbackInfo & info)157 void JSTextClock::SetFontSize(const JSCallbackInfo& info)
158 {
159     if (info.Length() < 1) {
160         LOGE("JSTextInput::SetFontSize The argv is wrong, it is supposed to have at least 1 argument");
161         return;
162     }
163     auto pipelineContext = PipelineContext::GetCurrentContext();
164     CHECK_NULL_VOID_NOLOG(pipelineContext);
165     auto theme = pipelineContext->GetTheme<TextTheme>();
166     CHECK_NULL_VOID_NOLOG(theme);
167 
168     CalcDimension fontSize;
169     if (!ParseJsDimensionFp(info[0], fontSize)) {
170         fontSize = theme->GetTextStyle().GetFontSize();
171     }
172 
173     if (fontSize.IsNegative() || fontSize.Unit() == DimensionUnit::PERCENT) {
174         auto pipelineContext = PipelineContext::GetCurrentContext();
175         CHECK_NULL_VOID_NOLOG(pipelineContext);
176         auto theme = pipelineContext->GetTheme<TextTheme>();
177         CHECK_NULL_VOID_NOLOG(theme);
178         fontSize = theme->GetTextStyle().GetFontSize();
179     }
180 
181     TextClockModel::GetInstance()->SetFontSize(fontSize);
182 }
183 
SetFontWeight(const JSCallbackInfo & info)184 void JSTextClock::SetFontWeight(const JSCallbackInfo& info)
185 {
186     if (info.Length() < 1) {
187         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
188         return;
189     }
190     RefPtr<TextTheme> textTheme = GetTheme<TextTheme>();
191     CHECK_NULL_VOID(textTheme);
192     const auto& fontWeight = info[0];
193     if (fontWeight->IsUndefined()) {
194         TextClockModel::GetInstance()->SetFontWeight(textTheme->GetTextStyle().GetFontWeight());
195         return;
196     }
197 
198     if (!fontWeight->IsNull()) {
199         std::string weight;
200         if (fontWeight->IsNumber()) {
201             weight = std::to_string(fontWeight->ToNumber<int32_t>());
202         } else {
203             ParseJsString(fontWeight, weight);
204         }
205         TextClockModel::GetInstance()->SetFontWeight(ConvertStrToFontWeight(weight));
206     } else {
207         TextClockModel::GetInstance()->SetFontWeight(textTheme->GetTextStyle().GetFontWeight());
208     }
209 }
210 
SetFontStyle(int32_t value)211 void JSTextClock::SetFontStyle(int32_t value)
212 {
213     if (value < 0 || value >= static_cast<int32_t>(FONT_STYLES.size())) {
214         LOGE("TextTimer fontStyle(%{public}d) illegal value", value);
215         return;
216     }
217     TextClockModel::GetInstance()->SetItalicFontStyle(FONT_STYLES[value]);
218 }
219 
SetFontFamily(const JSCallbackInfo & info)220 void JSTextClock::SetFontFamily(const JSCallbackInfo& info)
221 {
222     if (info.Length() < 1) {
223         LOGE("The argv is wrong, it is supposed to have at least 1 argument");
224         return;
225     }
226     std::vector<std::string> fontFamilies;
227     if (!ParseJsFontFamilies(info[0], fontFamilies)) {
228         LOGE("Parse FontFamilies failed");
229         return;
230     }
231     TextClockModel::GetInstance()->SetFontFamily(fontFamilies);
232 }
233 
SetFormat(const JSCallbackInfo & info)234 void JSTextClock::SetFormat(const JSCallbackInfo& info)
235 {
236     if (info.Length() < 1) {
237         LOGE("The arg is wrong, it is supposed to have atleast 1 argument.");
238         return;
239     }
240     if (!info[0]->IsString()) {
241         LOGE("The arg is not string,it is supposed to be a string.");
242         TextClockModel::GetInstance()->SetFormat(DEFAULT_FORMAT);
243         return;
244     }
245 
246     std::string value;
247     auto format = info[0]->ToString();
248     auto pipeline = PipelineContext::GetCurrentContext();
249     CHECK_NULL_VOID(pipeline);
250     auto currentPlatformVersion = pipeline->GetMinPlatformVersion();
251     if ((currentPlatformVersion >= PLATFORM_VERSION_ELEVEN) ? (format.length() == 0)
252                                                             : (format.length() == 0 || !StringUtils::IsAscii(format))) {
253         format = DEFAULT_FORMAT;
254         TextClockModel::GetInstance()->SetFormat(format);
255         return;
256     }
257 
258     if (!ParseJsString(info[0], value)) {
259         return;
260     }
261     if (currentPlatformVersion < PLATFORM_VERSION_ELEVEN) {
262         std::regex pattern(
263             R"(^([Yy]*[_|\W\s]*[M]*[_|\W\s]*[d]*[_|\W\s]*[D]*[_|\W\s]*[Hh]*[_|\W\s]*[m]*[_|\W\s]*[s]*[_|\W\s]*[S]*)$)");
264         if (!std::regex_match(value, pattern)) {
265             LOGW("The arg is wrong, because of format matching error.");
266             TextClockModel::GetInstance()->SetFormat("hms");
267             return;
268         }
269     }
270 
271     TextClockModel::GetInstance()->SetFormat(value);
272 }
273 
JsOnDateChange(const JSCallbackInfo & info)274 void JSTextClock::JsOnDateChange(const JSCallbackInfo& info)
275 {
276     if (!info[0]->IsFunction()) {
277         return;
278     }
279 
280     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[0]));
281     auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& value) {
282         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
283         ACE_SCORING_EVENT("TextClock.onDateChange");
284         auto newJSVal = JSRef<JSVal>::Make(ToJSValue(value));
285         func->ExecuteJS(1, &newJSVal);
286     };
287     TextClockModel::GetInstance()->SetOnDateChange(std::move(onChange));
288 }
289 
JSBind(BindingTarget globalObj)290 void JSTextClockController::JSBind(BindingTarget globalObj)
291 {
292     JSClass<JSTextClockController>::Declare("TextClockController");
293     JSClass<JSTextClockController>::Method("start", &JSTextClockController::Start);
294     JSClass<JSTextClockController>::Method("stop", &JSTextClockController::Stop);
295     JSClass<JSTextClockController>::Bind(
296         globalObj, JSTextClockController::Constructor, JSTextClockController::Destructor);
297 }
298 
Constructor(const JSCallbackInfo & args)299 void JSTextClockController::Constructor(const JSCallbackInfo& args)
300 {
301     auto scroller = Referenced::MakeRefPtr<JSTextClockController>();
302     scroller->IncRefCount();
303     args.SetReturnValue(Referenced::RawPtr(scroller));
304 }
305 
Destructor(JSTextClockController * scroller)306 void JSTextClockController::Destructor(JSTextClockController* scroller)
307 {
308     if (scroller != nullptr) {
309         scroller->DecRefCount();
310     }
311 }
312 
Start()313 void JSTextClockController::Start()
314 {
315     if (!controller_.empty()) {
316         for (auto& i : controller_) {
317             i->Start();
318         }
319     }
320 }
321 
Stop()322 void JSTextClockController::Stop()
323 {
324     if (!controller_.empty()) {
325         for (auto& i : controller_) {
326             i->Stop();
327         }
328     }
329 }
330 } // namespace OHOS::Ace::Framework
331