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 "bridge/declarative_frontend/jsview/js_datepicker.h"
17
18 #include <utility>
19
20 #include "base/log/ace_scoring_log.h"
21 #include "base/utils/utils.h"
22 #include "bridge/common/utils/engine_helper.h"
23 #include "bridge/declarative_frontend/engine/functions/js_function.h"
24 #include "bridge/declarative_frontend/jsview/js_interactable_view.h"
25 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
26 #include "bridge/declarative_frontend/jsview/models/picker_model_impl.h"
27 #include "bridge/declarative_frontend/jsview/models/timepicker_model_impl.h"
28 #include "bridge/declarative_frontend/view_stack_processor.h"
29 #include "core/components/picker/picker_data.h"
30 #include "core/components/picker/picker_date_component.h"
31 #include "core/components/picker/picker_theme.h"
32 #include "core/components/picker/picker_time_component.h"
33 #include "core/components_ng/base/view_stack_processor.h"
34 #include "core/components_ng/pattern/picker/datepicker_model_ng.h"
35 #include "core/components_ng/pattern/time_picker/timepicker_model.h"
36 #include "core/components_ng/pattern/time_picker/timepicker_model_ng.h"
37 #include "core/components_v2/inspector/inspector_constants.h"
38 #include "core/event/ace_event_helper.h"
39 #include "core/pipeline_ng/pipeline_context.h"
40
41 namespace OHOS::Ace {
42 std::unique_ptr<DatePickerModel> DatePickerModel::datePickerInstance_ = nullptr;
43 std::unique_ptr<TimePickerModel> TimePickerModel::timePickerInstance_ = nullptr;
GetInstance()44 DatePickerModel* DatePickerModel::GetInstance()
45 {
46 if (!datePickerInstance_) {
47 #ifdef NG_BUILD
48 datePickerInstance_.reset(new NG::DatePickerModelNG());
49 #else
50 if (Container::IsCurrentUseNewPipeline()) {
51 datePickerInstance_.reset(new NG::DatePickerModelNG());
52 } else {
53 datePickerInstance_.reset(new Framework::DatePickerModelImpl());
54 }
55 #endif
56 }
57 return datePickerInstance_.get();
58 }
59
GetInstance()60 TimePickerModel* TimePickerModel::GetInstance()
61 {
62 if (!timePickerInstance_) {
63 #ifdef NG_BUILD
64 timePickerInstance_.reset(new NG::TimePickerModelNG());
65 #else
66 if (Container::IsCurrentUseNewPipeline()) {
67 timePickerInstance_.reset(new NG::TimePickerModelNG());
68 } else {
69 timePickerInstance_.reset(new Framework::TimePickerModelImpl());
70 }
71 #endif
72 }
73 return timePickerInstance_.get();
74 }
75
76 } // namespace OHOS::Ace
77
78 namespace OHOS::Ace::Framework {
79 namespace {
AddEvent(RefPtr<PickerBaseComponent> & picker,const JSCallbackInfo & info,DatePickerType pickerType)80 void AddEvent(RefPtr<PickerBaseComponent>& picker, const JSCallbackInfo& info, DatePickerType pickerType)
81 {
82 if (info.Length() < 1 || !info[0]->IsObject()) {
83 LOGE("DatePicker AddEvent error, info is non-valid");
84 return;
85 }
86 auto paramObject = JSRef<JSObject>::Cast(info[0]);
87 auto onAccept = paramObject->GetProperty("onAccept");
88 if (!onAccept->IsUndefined() && onAccept->IsFunction()) {
89 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onAccept));
90 auto acceptId =
91 EventMarker([execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
92 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
93 std::vector<std::string> keys = { "year", "month", "day", "hour", "minute", "second" };
94 ACE_SCORING_EVENT("DatePickerDialog.onAccept");
95 func->Execute(keys, info);
96 });
97 picker->SetDialogAcceptEvent(acceptId);
98 }
99 auto onCancel = paramObject->GetProperty("onCancel");
100 if (!onCancel->IsUndefined() && onCancel->IsFunction()) {
101 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCancel));
102 auto cancelId = EventMarker([execCtx = info.GetExecutionContext(), func = std::move(jsFunc)]() {
103 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
104 ACE_SCORING_EVENT("DatePickerDialog.onCancel");
105 func->Execute();
106 });
107 picker->SetDialogCancelEvent(cancelId);
108 }
109 auto onChange = paramObject->GetProperty("onChange");
110 if (!onChange->IsUndefined() && onChange->IsFunction()) {
111 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onChange));
112 auto changeId = EventMarker([execCtx = info.GetExecutionContext(), type = pickerType, func = std::move(jsFunc)](
113 const std::string& info) {
114 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
115 std::vector<std::string> keys;
116 if (type == DatePickerType::DATE) {
117 keys = { "year", "month", "day" };
118 } else {
119 keys = { "hour", "minute" };
120 }
121 ACE_SCORING_EVENT("DatePickerDialog.onChange");
122 func->Execute(keys, info);
123 });
124 picker->SetDialogChangeEvent(changeId);
125 }
126 }
127
ChangeDialogEvent(const JSCallbackInfo & info,DatePickerType pickerType)128 std::map<std::string, NG::DialogEvent> ChangeDialogEvent(const JSCallbackInfo& info, DatePickerType pickerType)
129 {
130 std::map<std::string, NG::DialogEvent> dialogEvent;
131 if (info.Length() < 1 || !info[0]->IsObject()) {
132 LOGE("DatePicker AddEvent error, info is non-valid");
133 return dialogEvent;
134 }
135 auto paramObject = JSRef<JSObject>::Cast(info[0]);
136 auto onChange = paramObject->GetProperty("onChange");
137 if (!onChange->IsUndefined() && onChange->IsFunction()) {
138 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onChange));
139 auto changeId = [execCtx = info.GetExecutionContext(), type = pickerType, func = std::move(jsFunc)](
140 const std::string& info) {
141 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
142 std::vector<std::string> keys;
143 if (type == DatePickerType::DATE) {
144 keys = { "year", "month", "day" };
145 } else {
146 keys = { "hour", "minute" };
147 }
148 ACE_SCORING_EVENT("DatePickerDialog.onChange");
149 func->Execute(keys, info);
150 };
151 dialogEvent["changeId"] = changeId;
152 }
153 auto onAccept = paramObject->GetProperty("onAccept");
154 if (!onAccept->IsUndefined() && onAccept->IsFunction()) {
155 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onAccept));
156 auto acceptId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const std::string& info) {
157 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
158 std::vector<std::string> keys = { "year", "month", "day", "hour", "minute", "second" };
159 ACE_SCORING_EVENT("DatePickerDialog.onAccept");
160 func->Execute(keys, info);
161 };
162 dialogEvent["acceptId"] = acceptId;
163 }
164 return dialogEvent;
165 }
166
DialogCancelEvent(const JSCallbackInfo & info)167 std::map<std::string, NG::DialogGestureEvent> DialogCancelEvent(const JSCallbackInfo& info)
168 {
169 std::map<std::string, NG::DialogGestureEvent> dialogCancelEvent;
170 if (info.Length() < 1 || !info[0]->IsObject()) {
171 LOGE("DatePicker AddEvent error, info is non-valid");
172 return dialogCancelEvent;
173 }
174 auto paramObject = JSRef<JSObject>::Cast(info[0]);
175 auto onCancel = paramObject->GetProperty("onCancel");
176 if (!onCancel->IsUndefined() && onCancel->IsFunction()) {
177 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onCancel));
178 auto cancelId = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const GestureEvent& /*info*/) {
179 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
180 ACE_SCORING_EVENT("DatePickerDialog.onCancel");
181 func->Execute();
182 };
183 dialogCancelEvent["cancelId"] = cancelId;
184 }
185 return dialogCancelEvent;
186 }
187
DatePickerChangeEventToJSValue(const DatePickerChangeEvent & eventInfo)188 JSRef<JSVal> DatePickerChangeEventToJSValue(const DatePickerChangeEvent& eventInfo)
189 {
190 JSRef<JSObject> obj = JSRef<JSObject>::New();
191 std::unique_ptr<JsonValue> argsPtr = JsonUtil::ParseJsonString(eventInfo.GetSelectedStr());
192 if (!argsPtr) {
193 LOGW("selectedStr is not exist.");
194 return JSRef<JSVal>::Cast(obj);
195 }
196 std::vector<std::string> keys = { "year", "month", "day", "hour", "minute", "second" };
197 for (auto iter = keys.begin(); iter != keys.end(); iter++) {
198 const std::string key = *iter;
199 const auto value = argsPtr->GetValue(key);
200 if (!value || value->ToString().empty()) {
201 LOGI("key[%{public}s] is not exist.", key.c_str());
202 continue;
203 }
204 obj->SetProperty<int32_t>(key.c_str(), value->GetInt());
205 }
206 return JSRef<JSVal>::Cast(obj);
207 }
208 } // namespace
209
JSBind(BindingTarget globalObj)210 void JSDatePicker::JSBind(BindingTarget globalObj)
211 {
212 JSClass<JSDatePicker>::Declare("DatePicker");
213 MethodOptions opt = MethodOptions::NONE;
214 JSClass<JSDatePicker>::StaticMethod("create", &JSDatePicker::Create, opt);
215 JSClass<JSDatePicker>::StaticMethod("lunar", &JSDatePicker::SetLunar);
216 JSClass<JSDatePicker>::StaticMethod("onChange", &JSDatePicker::OnChange);
217 JSClass<JSDatePicker>::StaticMethod("backgroundColor", &JSDatePicker::PickerBackgroundColor);
218 // keep compatible, need remove after
219 JSClass<JSDatePicker>::StaticMethod("useMilitaryTime", &JSDatePicker::UseMilitaryTime);
220 JSClass<JSDatePicker>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
221 JSClass<JSDatePicker>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
222 JSClass<JSDatePicker>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
223 JSClass<JSDatePicker>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
224 JSClass<JSDatePicker>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
225 JSClass<JSDatePicker>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
226 JSClass<JSDatePicker>::Inherit<JSViewAbstract>();
227 JSClass<JSDatePicker>::Bind(globalObj);
228 }
229
Create(const JSCallbackInfo & info)230 void JSDatePicker::Create(const JSCallbackInfo& info)
231 {
232 DatePickerType pickerType = DatePickerType::DATE;
233 JSRef<JSObject> paramObject;
234 if (info.Length() >= 1 && info[0]->IsObject()) {
235 paramObject = JSRef<JSObject>::Cast(info[0]);
236 auto type = paramObject->GetProperty("type");
237 if (type->IsNumber()) {
238 pickerType = static_cast<DatePickerType>(type->ToNumber<int32_t>());
239 }
240 }
241 switch (pickerType) {
242 case DatePickerType::TIME: {
243 CreateTimePicker(paramObject);
244 break;
245 }
246 case DatePickerType::DATE: {
247 CreateDatePicker(paramObject);
248 break;
249 }
250 default: {
251 LOGE("Undefined date picker type.");
252 break;
253 }
254 }
255 }
256
SetLunar(bool isLunar)257 void JSDatePicker::SetLunar(bool isLunar)
258 {
259 DatePickerModel::GetInstance()->SetShowLunar(isLunar);
260 }
261
UseMilitaryTime(bool isUseMilitaryTime)262 void JSDatePicker::UseMilitaryTime(bool isUseMilitaryTime)
263 {
264 DatePickerModel::GetInstance()->SetHour24(isUseMilitaryTime);
265 }
266
OnChange(const JSCallbackInfo & info)267 void JSDatePicker::OnChange(const JSCallbackInfo& info)
268 {
269 if (info.Length() < 1 || !info[0]->IsFunction()) {
270 LOGI("info not function");
271 return;
272 }
273
274 auto jsFunc = AceType::MakeRefPtr<JsEventFunction<DatePickerChangeEvent, 1>>(
275 JSRef<JSFunc>::Cast(info[0]), DatePickerChangeEventToJSValue);
276 auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const BaseEventInfo* info) {
277 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
278 ACE_SCORING_EVENT("datePicker.onChange");
279 const auto* eventInfo = TypeInfoHelper::DynamicCast<DatePickerChangeEvent>(info);
280 func->Execute(*eventInfo);
281 };
282 DatePickerModel::GetInstance()->SetOnChange(std::move(onChange));
283 }
284
OnChange(const JSCallbackInfo & info)285 void JSTimePicker::OnChange(const JSCallbackInfo& info)
286 {
287 if (info.Length() < 1 || !info[0]->IsFunction()) {
288 LOGI("info not function");
289 return;
290 }
291
292 auto jsFunc = AceType::MakeRefPtr<JsEventFunction<DatePickerChangeEvent, 1>>(
293 JSRef<JSFunc>::Cast(info[0]), DatePickerChangeEventToJSValue);
294 auto onChange = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc)](const BaseEventInfo* index) {
295 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
296 ACE_SCORING_EVENT("datePicker.onChange");
297 const auto* eventInfo = TypeInfoHelper::DynamicCast<DatePickerChangeEvent>(index);
298 func->Execute(*eventInfo);
299 };
300 TimePickerModel::GetInstance()->SetOnChange(std::move(onChange));
301 }
302
PickerBackgroundColor(const JSCallbackInfo & info)303 void JSDatePicker::PickerBackgroundColor(const JSCallbackInfo& info)
304 {
305 JSViewAbstract::JsBackgroundColor(info);
306
307 auto pickerBase = AceType::DynamicCast<PickerBaseComponent>(ViewStackProcessor::GetInstance()->GetMainComponent());
308 if (!pickerBase) {
309 LOGE("PickerBaseComponent is null");
310 return;
311 }
312
313 pickerBase->SetHasBackgroundColor(true);
314 }
315
ParseDate(const JSRef<JSVal> & dateVal)316 PickerDate JSDatePicker::ParseDate(const JSRef<JSVal>& dateVal)
317 {
318 auto pickerDate = PickerDate();
319 if (!dateVal->IsObject()) {
320 return pickerDate;
321 }
322 auto dateObj = JSRef<JSObject>::Cast(dateVal);
323 auto yearFunc = JSRef<JSFunc>::Cast(dateObj->GetProperty("getFullYear"));
324 auto monthFunc = JSRef<JSFunc>::Cast(dateObj->GetProperty("getMonth"));
325 auto dateFunc = JSRef<JSFunc>::Cast(dateObj->GetProperty("getDate"));
326 JSRef<JSVal> year = yearFunc->Call(dateObj);
327 JSRef<JSVal> month = monthFunc->Call(dateObj);
328 JSRef<JSVal> date = dateFunc->Call(dateObj);
329
330 if (year->IsNumber() && month->IsNumber() && date->IsNumber()) {
331 pickerDate.SetYear(year->ToNumber<int32_t>());
332 pickerDate.SetMonth(month->ToNumber<int32_t>() + 1); // 0-11 means 1 to 12 months
333 pickerDate.SetDay(date->ToNumber<int32_t>());
334 }
335 return pickerDate;
336 }
337
ParseTime(const JSRef<JSVal> & timeVal)338 PickerTime JSDatePicker::ParseTime(const JSRef<JSVal>& timeVal)
339 {
340 auto pickerTime = PickerTime();
341 if (!timeVal->IsObject()) {
342 return pickerTime;
343 }
344 auto timeObj = JSRef<JSObject>::Cast(timeVal);
345 auto hourFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getHours"));
346 auto minuteFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getMinutes"));
347 auto secondFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getSeconds"));
348 JSRef<JSVal> hour = hourFunc->Call(timeObj);
349 JSRef<JSVal> minute = minuteFunc->Call(timeObj);
350 JSRef<JSVal> second = secondFunc->Call(timeObj);
351
352 if (hour->IsNumber() && minute->IsNumber() && second->IsNumber()) {
353 pickerTime.SetHour(hour->ToNumber<int32_t>());
354 pickerTime.SetMinute(minute->ToNumber<int32_t>());
355 pickerTime.SetSecond(second->ToNumber<int32_t>());
356 }
357 return pickerTime;
358 }
359
CreateDatePicker(const JSRef<JSObject> & paramObj)360 void JSDatePicker::CreateDatePicker(const JSRef<JSObject>& paramObj)
361 {
362 JSRef<JSVal> startDate;
363 JSRef<JSVal> endDate;
364 JSRef<JSVal> selectedDate;
365 if (!paramObj->IsUndefined()) {
366 startDate = paramObj->GetProperty("start");
367 endDate = paramObj->GetProperty("end");
368 selectedDate = paramObj->GetProperty("selected");
369 }
370 auto parseStartDate = ParseDate(startDate);
371 auto parseEndDate = ParseDate(endDate);
372 auto parseSelectedDate = ParseDate(selectedDate);
373 auto startDays = parseStartDate.ToDays();
374 auto endDays = parseEndDate.ToDays();
375 auto selectedDays = parseSelectedDate.ToDays();
376 if (startDays > endDays || selectedDays < startDays || selectedDays > endDays) {
377 LOGE("date error");
378 }
379 auto theme = GetTheme<PickerTheme>();
380 if (!theme) {
381 LOGE("datePicker Theme is null");
382 return;
383 }
384 DatePickerModel::GetInstance()->CreateDatePicker(theme);
385 if (startDate->IsObject()) {
386 DatePickerModel::GetInstance()->SetStartDate(parseStartDate);
387 }
388 if (endDate->IsObject()) {
389 DatePickerModel::GetInstance()->SetEndDate(parseEndDate);
390 }
391 if (selectedDate->IsObject()) {
392 DatePickerModel::GetInstance()->SetSelectedDate(parseSelectedDate);
393 }
394 }
395
CreateTimePicker(const JSRef<JSObject> & paramObj)396 void JSDatePicker::CreateTimePicker(const JSRef<JSObject>& paramObj)
397 {
398 auto theme = GetTheme<PickerTheme>();
399 if (!theme) {
400 LOGE("timePicker Theme is null");
401 return;
402 }
403 DatePickerModel::GetInstance()->CreateTimePicker(theme);
404 auto selectedTime = paramObj->GetProperty("selected");
405 if (selectedTime->IsObject()) {
406 DatePickerModel::GetInstance()->SetSelectedTime(ParseTime(selectedTime));
407 }
408 }
409
JSBind(BindingTarget globalObj)410 void JSDatePickerDialog::JSBind(BindingTarget globalObj)
411 {
412 JSClass<JSDatePickerDialog>::Declare("DatePickerDialog");
413 JSClass<JSDatePickerDialog>::StaticMethod("show", &JSDatePickerDialog::Show);
414
415 JSClass<JSDatePickerDialog>::Bind<>(globalObj);
416 }
417
Show(const JSCallbackInfo & info)418 void JSDatePickerDialog::Show(const JSCallbackInfo& info)
419 {
420 auto scopedDelegate = EngineHelper::GetCurrentDelegate();
421 if (!scopedDelegate) {
422 // this case usually means there is no foreground container, need to figure out the reason.
423 LOGE("scopedDelegate is null, please check");
424 return;
425 }
426 if (info.Length() < 1 || !info[0]->IsObject()) {
427 LOGE("DatePicker Show dialog error, info is non-valid");
428 return;
429 }
430
431 auto paramObject = JSRef<JSObject>::Cast(info[0]);
432 DatePickerType pickerType = DatePickerType::DATE;
433 auto type = paramObject->GetProperty("type");
434 if (type->IsNumber()) {
435 pickerType = static_cast<DatePickerType>(type->ToNumber<int32_t>());
436 }
437 if (Container::IsCurrentUseNewPipeline()) {
438 auto dialogEvent = ChangeDialogEvent(info, DatePickerType::DATE);
439 auto dialogCancelEvent = DialogCancelEvent(info);
440 DatePickerDialogShow(paramObject, dialogEvent, dialogCancelEvent);
441 return;
442 }
443
444 std::string name;
445 RefPtr<Component> component;
446 switch (pickerType) {
447 case DatePickerType::TIME: {
448 CreateTimePicker(component, paramObject);
449 name = "TimePickerDialog";
450 break;
451 }
452 case DatePickerType::DATE: {
453 CreateDatePicker(component, paramObject);
454 name = "DatePickerDialog";
455 break;
456 }
457 default: {
458 LOGE("Undefined date picker type.");
459 return;
460 }
461 }
462
463 auto datePicker = AceType::DynamicCast<PickerBaseComponent>(component);
464 DialogProperties properties {};
465 properties.alignment = DialogAlignment::CENTER;
466 properties.customComponent = datePicker;
467 properties.customStyle = true;
468 if (pickerType == DatePickerType::DATE) {
469 AddEvent(datePicker, info, DatePickerType::DATE);
470 } else {
471 AddEvent(datePicker, info, DatePickerType::TIME);
472 }
473 datePicker->SetDialogName(name);
474 datePicker->OpenDialog(properties);
475 }
476
DatePickerDialogShow(const JSRef<JSObject> & paramObj,const std::map<std::string,NG::DialogEvent> & dialogEvent,const std::map<std::string,NG::DialogGestureEvent> & dialogCancelEvent)477 void JSDatePickerDialog::DatePickerDialogShow(const JSRef<JSObject>& paramObj,
478 const std::map<std::string, NG::DialogEvent>& dialogEvent,
479 const std::map<std::string, NG::DialogGestureEvent>& dialogCancelEvent)
480 {
481 auto container = Container::Current();
482 if (!container) {
483 return;
484 }
485 auto pipelineContext = AccessibilityManager::DynamicCast<NG::PipelineContext>(container->GetPipelineContext());
486 if (!pipelineContext) {
487 return;
488 }
489
490 auto executor = pipelineContext->GetTaskExecutor();
491 if (!executor) {
492 return;
493 }
494
495 auto startDate = paramObj->GetProperty("start");
496 auto endDate = paramObj->GetProperty("end");
497 auto selectedDate = paramObj->GetProperty("selected");
498 auto lunar = paramObj->GetProperty("lunar");
499 bool isLunar = lunar->ToBoolean();
500 auto parseStartDate = ParseDate(startDate);
501 auto parseEndDate = ParseDate(endDate);
502 auto parseSelectedDate = ParseDate(selectedDate);
503 auto startDays = parseStartDate.ToDays();
504 auto endDays = parseEndDate.ToDays();
505 auto selectedDays = parseSelectedDate.ToDays();
506 if (startDays > endDays || selectedDays < startDays || selectedDays > endDays) {
507 LOGE("date error");
508 }
509
510 auto theme = GetTheme<DialogTheme>();
511 if (!theme) {
512 LOGE("DialogTheme is null");
513 return;
514 }
515
516 ButtonInfo buttonInfo;
517 DialogProperties properties;
518 if (SystemProperties::GetDeviceType() == DeviceType::PHONE) {
519 properties.alignment = DialogAlignment::BOTTOM;
520 } else {
521 properties.alignment = DialogAlignment::CENTER;
522 }
523 properties.customStyle = false;
524 properties.offset = DimensionOffset(Offset(0, -theme->GetMarginBottom().ConvertToPx()));
525
526 std::map<std::string, PickerDate> datePickerProperty;
527 if (startDate->IsObject()) {
528 datePickerProperty["start"] = parseStartDate;
529 }
530 if (endDate->IsObject()) {
531 datePickerProperty["end"] = parseEndDate;
532 }
533 if (selectedDate->IsObject()) {
534 datePickerProperty["selected"] = parseSelectedDate;
535 }
536
537 auto context = AccessibilityManager::DynamicCast<NG::PipelineContext>(pipelineContext);
538 auto overlayManager = context ? context->GetOverlayManager() : nullptr;
539 executor->PostTask(
540 [properties, datePickerProperty, isLunar, dialogEvent, dialogCancelEvent,
541 weak = WeakPtr<NG::OverlayManager>(overlayManager)] {
542 auto overlayManager = weak.Upgrade();
543 CHECK_NULL_VOID(overlayManager);
544 overlayManager->ShowDateDialog(properties, datePickerProperty, isLunar, dialogEvent, dialogCancelEvent);
545 },
546 TaskExecutor::TaskType::UI);
547 }
548
CreateDatePicker(RefPtr<Component> & component,const JSRef<JSObject> & paramObj)549 void JSDatePickerDialog::CreateDatePicker(RefPtr<Component>& component, const JSRef<JSObject>& paramObj)
550 {
551 auto datePicker = AceType::MakeRefPtr<PickerDateComponent>();
552 auto startDate = paramObj->GetProperty("start");
553 auto endDate = paramObj->GetProperty("end");
554 auto selectedDate = paramObj->GetProperty("selected");
555 auto lunar = paramObj->GetProperty("lunar");
556 bool isLunar = lunar->ToBoolean();
557 auto parseStartDate = ParseDate(startDate);
558 auto parseEndDate = ParseDate(endDate);
559 auto parseSelectedDate = ParseDate(selectedDate);
560 auto startDays = parseStartDate.ToDays();
561 auto endDays = parseEndDate.ToDays();
562 auto selectedDays = parseSelectedDate.ToDays();
563 if (startDays > endDays || selectedDays < startDays || selectedDays > endDays) {
564 LOGE("date error");
565 }
566 if (startDate->IsObject()) {
567 datePicker->SetStartDate(parseStartDate);
568 }
569 if (endDate->IsObject()) {
570 datePicker->SetEndDate(parseEndDate);
571 }
572 if (selectedDate->IsObject()) {
573 datePicker->SetSelectedDate(parseSelectedDate);
574 }
575 datePicker->SetIsDialog(true);
576 datePicker->SetIsCreateDialogComponent(true);
577 datePicker->SetShowLunar(isLunar);
578
579 component = datePicker;
580 }
581
CreateTimePicker(RefPtr<Component> & component,const JSRef<JSObject> & paramObj)582 void JSDatePickerDialog::CreateTimePicker(RefPtr<Component>& component, const JSRef<JSObject>& paramObj)
583 {
584 auto timePicker = AceType::MakeRefPtr<PickerTimeComponent>();
585 auto selectedTime = paramObj->GetProperty("selected");
586 auto useMilitaryTime = paramObj->GetProperty("useMilitaryTime");
587 bool isUseMilitaryTime = useMilitaryTime->ToBoolean();
588 if (selectedTime->IsObject()) {
589 timePicker->SetSelectedTime(ParseTime(selectedTime));
590 }
591 timePicker->SetIsDialog(true);
592 timePicker->SetIsCreateDialogComponent(true);
593 timePicker->SetHour24(isUseMilitaryTime);
594 component = timePicker;
595 }
596
ParseDate(const JSRef<JSVal> & dateVal)597 PickerDate JSDatePickerDialog::ParseDate(const JSRef<JSVal>& dateVal)
598 {
599 auto pickerDate = PickerDate();
600 if (!dateVal->IsObject()) {
601 return pickerDate;
602 }
603 auto dateObj = JSRef<JSObject>::Cast(dateVal);
604 auto yearFunc = JSRef<JSFunc>::Cast(dateObj->GetProperty("getFullYear"));
605 auto monthFunc = JSRef<JSFunc>::Cast(dateObj->GetProperty("getMonth"));
606 auto dateFunc = JSRef<JSFunc>::Cast(dateObj->GetProperty("getDate"));
607 JSRef<JSVal> year = yearFunc->Call(dateObj);
608 JSRef<JSVal> month = monthFunc->Call(dateObj);
609 JSRef<JSVal> date = dateFunc->Call(dateObj);
610
611 if (year->IsNumber() && month->IsNumber() && date->IsNumber()) {
612 pickerDate.SetYear(year->ToNumber<int32_t>());
613 pickerDate.SetMonth(month->ToNumber<int32_t>() + 1); // 0-11 means 1 to 12 months
614 pickerDate.SetDay(date->ToNumber<int32_t>());
615 }
616 return pickerDate;
617 }
618
ParseTime(const JSRef<JSVal> & timeVal)619 PickerTime JSDatePickerDialog::ParseTime(const JSRef<JSVal>& timeVal)
620 {
621 auto pickerTime = PickerTime();
622 if (!timeVal->IsObject()) {
623 return pickerTime;
624 }
625 auto timeObj = JSRef<JSObject>::Cast(timeVal);
626 auto hourFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getHours"));
627 auto minuteFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getMinutes"));
628 auto secondFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getSeconds"));
629 JSRef<JSVal> hour = hourFunc->Call(timeObj);
630 JSRef<JSVal> minute = minuteFunc->Call(timeObj);
631 JSRef<JSVal> second = secondFunc->Call(timeObj);
632
633 if (hour->IsNumber() && minute->IsNumber() && second->IsNumber()) {
634 pickerTime.SetHour(hour->ToNumber<int32_t>());
635 pickerTime.SetMinute(minute->ToNumber<int32_t>());
636 pickerTime.SetSecond(second->ToNumber<int32_t>());
637 }
638 return pickerTime;
639 }
640
JSBind(BindingTarget globalObj)641 void JSTimePicker::JSBind(BindingTarget globalObj)
642 {
643 JSClass<JSTimePicker>::Declare("TimePicker");
644 MethodOptions opt = MethodOptions::NONE;
645 JSClass<JSTimePicker>::StaticMethod("create", &JSTimePicker::Create, opt);
646 JSClass<JSTimePicker>::StaticMethod("onChange", &JSTimePicker::OnChange);
647 JSClass<JSTimePicker>::StaticMethod("backgroundColor", &JSDatePicker::PickerBackgroundColor);
648 JSClass<JSTimePicker>::StaticMethod("useMilitaryTime", &JSTimePicker::UseMilitaryTime);
649 JSClass<JSTimePicker>::StaticMethod("onClick", &JSInteractableView::JsOnClick);
650 JSClass<JSTimePicker>::StaticMethod("onTouch", &JSInteractableView::JsOnTouch);
651 JSClass<JSTimePicker>::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey);
652 JSClass<JSTimePicker>::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete);
653 JSClass<JSTimePicker>::StaticMethod("onAppear", &JSInteractableView::JsOnAppear);
654 JSClass<JSTimePicker>::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear);
655 JSClass<JSTimePicker>::Inherit<JSViewAbstract>();
656 JSClass<JSTimePicker>::Bind(globalObj);
657 }
658
Create(const JSCallbackInfo & info)659 void JSTimePicker::Create(const JSCallbackInfo& info)
660 {
661 JSRef<JSObject> paramObject = JSRef<JSObject>::New();
662 if (info.Length() >= 1 && info[0]->IsObject()) {
663 paramObject = JSRef<JSObject>::Cast(info[0]);
664 }
665 CreateTimePicker(paramObject);
666 }
667
UseMilitaryTime(bool isUseMilitaryTime)668 void JSTimePicker::UseMilitaryTime(bool isUseMilitaryTime)
669 {
670 TimePickerModel::GetInstance()->SetHour24(isUseMilitaryTime);
671 }
672
CreateTimePicker(const JSRef<JSObject> & paramObj)673 void JSTimePicker::CreateTimePicker(const JSRef<JSObject>& paramObj)
674 {
675 auto selectedTime = paramObj->GetProperty("selected");
676 auto theme = GetTheme<PickerTheme>();
677 if (!theme) {
678 LOGE("timePicker Theme is null");
679 return;
680 }
681 TimePickerModel::GetInstance()->CreateTimePicker(theme);
682 if (selectedTime->IsObject()) {
683 TimePickerModel::GetInstance()->SetSelectedTime(ParseTime(selectedTime));
684 }
685 }
686
ParseTime(const JSRef<JSVal> & timeVal)687 PickerTime JSTimePicker::ParseTime(const JSRef<JSVal>& timeVal)
688 {
689 auto pickerTime = PickerTime();
690 if (!timeVal->IsObject()) {
691 return pickerTime;
692 }
693 auto timeObj = JSRef<JSObject>::Cast(timeVal);
694 auto hourFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getHours"));
695 auto minuteFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getMinutes"));
696 auto secondFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getSeconds"));
697 JSRef<JSVal> hour = hourFunc->Call(timeObj);
698 JSRef<JSVal> minute = minuteFunc->Call(timeObj);
699 JSRef<JSVal> second = secondFunc->Call(timeObj);
700
701 if (hour->IsNumber() && minute->IsNumber() && second->IsNumber()) {
702 pickerTime.SetHour(hour->ToNumber<int32_t>());
703 pickerTime.SetMinute(minute->ToNumber<int32_t>());
704 pickerTime.SetSecond(second->ToNumber<int32_t>());
705 }
706 return pickerTime;
707 }
708
JSBind(BindingTarget globalObj)709 void JSTimePickerDialog::JSBind(BindingTarget globalObj)
710 {
711 JSClass<JSTimePickerDialog>::Declare("TimePickerDialog");
712 JSClass<JSTimePickerDialog>::StaticMethod("show", &JSTimePickerDialog::Show);
713
714 JSClass<JSTimePickerDialog>::Bind<>(globalObj);
715 }
716
Show(const JSCallbackInfo & info)717 void JSTimePickerDialog::Show(const JSCallbackInfo& info)
718 {
719 auto scopedDelegate = EngineHelper::GetCurrentDelegate();
720 if (!scopedDelegate) {
721 // this case usually means there is no foreground container, need to figure out the reason.
722 LOGE("scopedDelegate is null, please check");
723 return;
724 }
725 if (info.Length() < 1 || !info[0]->IsObject()) {
726 LOGE("DatePicker Show dialog error, info is non-valid");
727 return;
728 }
729
730 auto paramObject = JSRef<JSObject>::Cast(info[0]);
731
732 if (Container::IsCurrentUseNewPipeline()) {
733 auto dialogEvent = ChangeDialogEvent(info, DatePickerType::TIME);
734 auto dialogCancelEvent = DialogCancelEvent(info);
735 TimePickerDialogShow(paramObject, dialogEvent, dialogCancelEvent);
736 return;
737 }
738
739 RefPtr<Component> component;
740 CreateTimePicker(component, paramObject);
741
742 auto datePicker = AceType::DynamicCast<PickerBaseComponent>(component);
743 DialogProperties properties {};
744 properties.alignment = DialogAlignment::CENTER;
745 properties.customComponent = datePicker;
746 properties.customStyle = true;
747 AddEvent(datePicker, info, DatePickerType::TIME);
748 datePicker->SetDialogName("TimePickerDialog");
749 datePicker->OpenDialog(properties);
750 }
751
TimePickerDialogShow(const JSRef<JSObject> & paramObj,const std::map<std::string,NG::DialogEvent> & dialogEvent,const std::map<std::string,NG::DialogGestureEvent> & dialogCancelEvent)752 void JSTimePickerDialog::TimePickerDialogShow(const JSRef<JSObject>& paramObj,
753 const std::map<std::string, NG::DialogEvent>& dialogEvent,
754 const std::map<std::string, NG::DialogGestureEvent>& dialogCancelEvent)
755 {
756 auto container = Container::Current();
757 if (!container) {
758 return;
759 }
760 auto pipelineContext = AccessibilityManager::DynamicCast<NG::PipelineContext>(container->GetPipelineContext());
761 if (!pipelineContext) {
762 return;
763 }
764
765 auto executor = pipelineContext->GetTaskExecutor();
766 if (!executor) {
767 return;
768 }
769
770 auto selectedTime = paramObj->GetProperty("selected");
771 auto useMilitaryTime = paramObj->GetProperty("useMilitaryTime");
772 bool isUseMilitaryTime = useMilitaryTime->ToBoolean();
773 PickerDate dialogTitleDate = PickerDate::Current();
774
775 auto theme = JSAlertDialog::GetTheme<DialogTheme>();
776 if (!theme) {
777 LOGE("DialogTheme is null");
778 return;
779 }
780
781 ButtonInfo buttonInfo;
782 DialogProperties properties;
783 if (SystemProperties::GetDeviceType() == DeviceType::PHONE) {
784 properties.alignment = DialogAlignment::BOTTOM;
785 } else {
786 properties.alignment = DialogAlignment::CENTER;
787 }
788 properties.customStyle = false;
789 properties.offset = DimensionOffset(Offset(0, -theme->GetMarginBottom().ConvertToPx()));
790
791 std::map<std::string, PickerTime> timePickerProperty;
792 if (selectedTime->IsObject()) {
793 dialogTitleDate = ParseDate(selectedTime);
794 timePickerProperty["selected"] = ParseTime(selectedTime);
795 }
796 auto context = AccessibilityManager::DynamicCast<NG::PipelineContext>(pipelineContext);
797 auto overlayManager = context ? context->GetOverlayManager() : nullptr;
798 executor->PostTask(
799 [properties, timePickerProperty, isUseMilitaryTime, dialogTitleDate, dialogEvent, dialogCancelEvent,
800 weak = WeakPtr<NG::OverlayManager>(overlayManager)] {
801 auto overlayManager = weak.Upgrade();
802 CHECK_NULL_VOID(overlayManager);
803 overlayManager->ShowTimeDialog(
804 properties, timePickerProperty, isUseMilitaryTime, dialogTitleDate, dialogEvent, dialogCancelEvent);
805 },
806 TaskExecutor::TaskType::UI);
807 }
808
ParseDate(const JSRef<JSVal> & dateVal)809 PickerDate JSTimePickerDialog::ParseDate(const JSRef<JSVal>& dateVal)
810 {
811 auto pickerDate = PickerDate();
812 if (!dateVal->IsObject()) {
813 return pickerDate;
814 }
815 auto dateObj = JSRef<JSObject>::Cast(dateVal);
816 auto yearFunc = JSRef<JSFunc>::Cast(dateObj->GetProperty("getFullYear"));
817 auto monthFunc = JSRef<JSFunc>::Cast(dateObj->GetProperty("getMonth"));
818 auto dateFunc = JSRef<JSFunc>::Cast(dateObj->GetProperty("getDate"));
819 JSRef<JSVal> year = yearFunc->Call(dateObj);
820 JSRef<JSVal> month = monthFunc->Call(dateObj);
821 JSRef<JSVal> date = dateFunc->Call(dateObj);
822
823 if (year->IsNumber() && month->IsNumber() && date->IsNumber()) {
824 pickerDate.SetYear(year->ToNumber<int32_t>());
825 pickerDate.SetMonth(month->ToNumber<int32_t>() + 1); // 0-11 means 1 to 12 months
826 pickerDate.SetDay(date->ToNumber<int32_t>());
827 }
828 return pickerDate;
829 }
830
CreateTimePicker(RefPtr<Component> & component,const JSRef<JSObject> & paramObj)831 void JSTimePickerDialog::CreateTimePicker(RefPtr<Component>& component, const JSRef<JSObject>& paramObj)
832 {
833 auto timePicker = AceType::MakeRefPtr<PickerTimeComponent>();
834 auto selectedTime = paramObj->GetProperty("selected");
835 auto useMilitaryTime = paramObj->GetProperty("useMilitaryTime");
836 bool isUseMilitaryTime = useMilitaryTime->ToBoolean();
837 if (selectedTime->IsObject()) {
838 timePicker->SetSelectedTime(ParseTime(selectedTime));
839 }
840 timePicker->SetIsDialog(true);
841 timePicker->SetIsCreateDialogComponent(true);
842 timePicker->SetHour24(isUseMilitaryTime);
843 component = timePicker;
844 }
845
ParseTime(const JSRef<JSVal> & timeVal)846 PickerTime JSTimePickerDialog::ParseTime(const JSRef<JSVal>& timeVal)
847 {
848 auto pickerTime = PickerTime();
849 if (!timeVal->IsObject()) {
850 return pickerTime;
851 }
852 auto timeObj = JSRef<JSObject>::Cast(timeVal);
853 auto hourFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getHours"));
854 auto minuteFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getMinutes"));
855 auto secondFunc = JSRef<JSFunc>::Cast(timeObj->GetProperty("getSeconds"));
856 JSRef<JSVal> hour = hourFunc->Call(timeObj);
857 JSRef<JSVal> minute = minuteFunc->Call(timeObj);
858 JSRef<JSVal> second = secondFunc->Call(timeObj);
859
860 if (hour->IsNumber() && minute->IsNumber() && second->IsNumber()) {
861 pickerTime.SetHour(hour->ToNumber<int32_t>());
862 pickerTime.SetMinute(minute->ToNumber<int32_t>());
863 pickerTime.SetSecond(second->ToNumber<int32_t>());
864 }
865 return pickerTime;
866 }
867 } // namespace OHOS::Ace::Framework
868