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
16import deviceInfo from '@ohos.deviceInfo';
17import CommonEvent from '@ohos.commonEvent';
18import CommonEventManager from '@ohos.commonEventManager';
19import DateAndTimeModel from '../model/dateAndTimeImpl/DateAndTimeModel';
20import LogUtil from '../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil';
21import { BaseData } from '../../../../../../common/utils/src/main/ets/default/bean/BaseData';
22import ConfigData from '../../../../../../common/utils/src/main/ets/default/baseUtil/ConfigData';
23import HeadComponent from '../../../../../../common/component/src/main/ets/default/headComponent';
24import ResourceUtil from '../../../../../../common/search/src/main/ets/default/common/ResourceUtil';
25import DateAndTime from '../../../../../../common/utils/src/main/ets/default/baseUtil/DateAndTimeUtil';
26import { DialogButtonLayout } from '../../../../../../common/component/src/main/ets/default/dialogComponent';
27import { TextComponentWithEndText } from '../../../../../../common/component/src/main/ets/default/textComponent';
28
29const MODULE_TAG = ConfigData.TAG + '.dateAndTime -> ';
30const deviceTypeInfo = deviceInfo.deviceType;
31
32class commonEvents {
33  events: string[] = []
34}
35/**
36 * date and time
37 */
38@Entry
39@Component
40struct dateAndTime {
41  @State dateAndTimeList: BaseData[] = [];
42  private date: string = '';
43  private time: Resource | string = '';
44  private image: string | Resource = '';
45  private dateMark: string = 'date';
46  private timeMark: string = 'time';
47    private subscriber: CommonEventManager.CommonEventSubscriber | null = null;
48  private headName: string = '';
49
50  private commonEventSubscribeInfo: commonEvents = {
51    events: [
52      CommonEvent.Support.COMMON_EVENT_TIME_CHANGED,
53      CommonEvent.Support.COMMON_EVENT_TIMEZONE_CHANGED,
54      CommonEvent.Support.COMMON_EVENT_TIME_TICK,
55      CommonEvent.Support.COMMON_EVENT_DATE_CHANGED,
56    ]
57  };
58
59  timeDialogController: CustomDialogController | null = new CustomDialogController({
60    builder: TimeDialog24H({ action: this.onAccept }),
61    cancel: this.existApp,
62    autoCancel: true,
63    alignment: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? DialogAlignment.Bottom : DialogAlignment.Center,
64    offset: ({ dx: 0, dy: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? '-24dp' : 0 }),
65  });
66
67  dateDialogController: CustomDialogController | null = new CustomDialogController({
68    builder: DateDialog({ action: this.onAccept }),
69    cancel: this.existApp,
70    autoCancel: true,
71    alignment: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? DialogAlignment.Bottom : DialogAlignment.Center,
72    offset: ({ dx: 0, dy: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? '-24dp' : 0 }),
73  });
74
75  build() {
76    Column() {
77      GridContainer({ gutter: ConfigData.GRID_CONTAINER_GUTTER_24, margin: ConfigData.GRID_CONTAINER_MARGIN_24 }) {
78        Column() {
79          HeadComponent({ headName: $r('app.string.dateAndTimeTab'), isActive: true });
80
81          Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
82            Text($r('app.string.timeFormat'))
83              .fontSize($r('app.float.font_16'))
84              .fontColor($r('app.color.font_color_182431'))
85              .fontWeight(FontWeight.Medium)
86              .textAlign(TextAlign.Start)
87              .margin({
88                top: $r("app.float.wh_value_4"),
89                bottom: $r("app.float.wh_value_4"),
90                left: $r("app.float.wh_value_12")
91              })
92
93            Toggle({ type: ToggleType.Switch, isOn: this.is24hTimeFormat() })
94              .margin({ right: $r('app.float.wh_value_6') })
95              .width('36vp')
96              .height('20vp')
97              .selectedColor('#007DFF')
98              .onChange((isOn: boolean) => {
99                let timeFormat :boolean = this.is24hTimeFormat();
100                LogUtil.info(MODULE_TAG + 'Toggle come onChange timeFormat ' + timeFormat);
101                if (this.is24hTimeFormat()) {
102                  this.setTimeFormatAs12H();
103                } else {
104                  this.setTimeFormatAs24H();
105                }
106                this.time = DateAndTime.getSystemTime(timeFormat);
107                this.changeValue();
108              });
109          }
110          .height($r('app.float.wh_value_56'))
111          .width(ConfigData.WH_100_100)
112          .backgroundColor($r("app.color.white_bg_color"))
113          .margin({ bottom: $r("app.float.wh_value_12"), top: $r("app.float.distance_8") })
114          .borderRadius($r('app.float.radius_24'));
115
116          List() {
117            ForEach(this.dateAndTimeList, (item: BaseData) => {
118              ListItem() {
119                TextComponentWithEndText({ title: item.settingTitle, endText: item.settingValue?.toString(), clickEvent: () => {
120                  LogUtil.info(MODULE_TAG + 'dialog come in' + JSON.stringify(item));
121                  if (item.settingAlias === 'date') {
122                    LogUtil.info(MODULE_TAG + 'date dialog come in');
123                    this.dateDialogController?.open();
124                  }
125                  if (item.settingAlias === 'time') {
126                    LogUtil.info(MODULE_TAG + 'time dialog come in');
127                    this.timeDialogController?.open();
128                  }
129                } });
130              }
131            }, (item: BaseData) => JSON.stringify(item))
132          }
133          .padding($r('app.float.wh_value_4'))
134          .divider({
135            strokeWidth: $r('app.float.divider_wh'),
136            color: $r('sys.color.ohos_id_color_list_separator'),
137            startMargin: $r('app.float.wh_value_15'),
138            endMargin: $r('app.float.wh_value_15')
139          })
140          .borderRadius($r("app.float.radius_24"))
141          .backgroundColor($r("app.color.white_bg_color"))
142          .visibility(this.dateAndTimeList.length > 0 ? Visibility.Visible : Visibility.None)
143        }
144        .useSizeType({
145          sm: { span: 4, offset: 0 },
146          md: { span: 6, offset: 1 },
147          lg: { span: 8, offset: 2 }
148        })
149      }
150      .width(ConfigData.WH_100_100)
151      .height(ConfigData.WH_100_100);
152    }
153    .backgroundColor($r("sys.color.ohos_id_color_sub_background"))
154    .width(ConfigData.WH_100_100)
155    .height(ConfigData.WH_100_100)
156  }
157
158  /**
159   * Successfully built Dialog
160   */
161  onAccept() {
162    LogUtil.info(MODULE_TAG + 'onAccept');
163  }
164
165  /**
166   * Cancel Dialog
167   */
168  existApp() {
169    LogUtil.info(MODULE_TAG + 'Cancel dialog!');
170  }
171
172  /**
173   * modify date and time
174   */
175  changeValue() {
176    this.dateAndTimeList = [
177      {
178        settingSummary: '',
179        settingTitle: $r('app.string.date'),
180        settingValue: this.date,
181        settingAlias: this.dateMark,
182        settingArrow: this.image,
183      },
184      {
185        settingSummary: '',
186        settingTitle: $r('app.string.time'),
187        settingValue: this.time,
188        settingAlias: this.timeMark,
189        settingArrow: this.image,
190      }
191    ]
192  }
193
194  aboutToAppear(): void {
195    this.image = "/res/image/ic_settings_arrow.svg";
196    this.time = DateAndTime.getSystemTime(this.is24hTimeFormat());
197    this.date = DateAndTime.getSystemDate();
198    this.changeValue();
199    this.getNowTime();
200    DateAndTimeModel.registerObserver(this.getNowTime);
201
202    CommonEvent.createSubscriber(this.commonEventSubscribeInfo)
203      .then(subscriber => {
204        this.subscriber = subscriber;
205        CommonEvent.subscribe(this.subscriber, (error, commonEventData) => {
206          this.getNowTime();
207          LogUtil.info(`${MODULE_TAG}, CommonEvent Subscribe callback in, error: ${error}, commonEventData: ${commonEventData}`);
208        });
209      });
210  }
211
212  /**
213   * get current system time
214   */
215  private getNowTime(): void {
216    LogUtil.info(MODULE_TAG + 'get time come in');
217    this.time = DateAndTime.getSystemTime(this.is24hTimeFormat());
218    this.date = DateAndTime.getSystemDate();
219    LogUtil.info(MODULE_TAG + 'get time end in date=' + this.date);
220    LogUtil.info(MODULE_TAG + 'get time end in time=' + this.time);
221    this.changeValue();
222  }
223
224  aboutToDisappear(): void {
225    CommonEvent.unsubscribe(this.subscriber, (error, commonEventData) => {
226      LogUtil.info(`${MODULE_TAG}, CommonEvent unSubscribe callback in, error: ${error}, commonEventData: ${commonEventData}`);
227      DateAndTimeModel.unregisterObserver();
228    });
229    this.timeDialogController = null;
230    this.dateDialogController = null;
231  }
232
233  private setTimeFormatAs24H() {
234    DateAndTimeModel.setTimeFormatAs24H();
235  }
236
237  private setTimeFormatAs12H() {
238    DateAndTimeModel.setTimeFormatAs12H();
239  }
240
241  private is24hTimeFormat(): boolean {
242    let result = DateAndTimeModel.getTimeFormat();
243    if (result === ConfigData.TIME_FORMAT_24) {
244      return true;
245    }
246    return false;
247  }
248}
249
250/**
251 * set time 24h format dialog
252 */
253@CustomDialog
254@Component
255struct TimeDialog24H {
256  controller?: CustomDialogController;
257  action: () => void = () => {};
258  private currentTimeString: string = '';
259  private currentTimeDate: Date = new Date();
260  private formatTime: string = '';
261
262  build() {
263    Column() {
264      Column() {
265        Text($r('app.string.settingTime'))
266          .height($r('app.float.wh_value_56'))
267          .width(ConfigData.WH_100_100)
268          .textAlign(TextAlign.Center)
269          .fontSize($r('app.float.font_20'))
270          .fontColor($r("sys.color.ohos_id_color_primary"))
271          .fontWeight(FontWeight.Medium);
272
273        TimePicker({ selected: this.currentTimeDate })
274          .useMilitaryTime(this.isNeedMilitaryTime())
275          .onChange((date: TimePickerResult) => {
276            this.currentTimeString = DateAndTime.concatTime(date.hour, date.minute);
277            this.currentTimeDate.setHours(date.hour? date.hour : 0);
278            this.currentTimeDate.setMinutes(date.minute? date.minute : 0);
279            LogUtil.info(MODULE_TAG + 'onchange currentTimeString' + this.currentTimeString);
280          })
281          .width(ConfigData.WH_100_100)
282          .height($r('app.float.wh_value_200'))
283      }
284      .padding({ left: $r('sys.float.ohos_id_max_padding_start'), right: $r('app.float.wh_value_21') })
285
286      DialogButtonLayout({
287        firstClickEvent: () => {
288          this.controller?.close();
289          this.action();
290        },
291        secondClickEvent: () => {
292          LogUtil.info(MODULE_TAG + 'button confirm');
293          let datetime = new Date();
294          let y = datetime.getFullYear();
295          let m = datetime.getMonth() + 1;
296          let d = datetime.getDate();
297          this.formatTime = y + '-' + DateAndTime.fill(m) + '-' + DateAndTime.fill(d)
298          + 'T' + this.currentTimeString + ':' + '00';
299          LogUtil.info(MODULE_TAG + 'onchange format time' + this.formatTime);
300          let s = (new Date(this.formatTime)).getTime();
301          LogUtil.info('onchange time second' + s);
302          DateAndTimeModel.setTime(s);
303          this.controller?.close();
304          this.action();
305        }
306      })
307    }
308    .width(ConfigData.WH_100_100)
309  }
310
311  private isNeedMilitaryTime(): boolean {
312    let result = DateAndTimeModel.getTimeFormat();
313    // 24h time format need using military time
314    if (result === ConfigData.TIME_FORMAT_24) {
315      return true;
316    }
317    return false;
318  }
319}
320
321/**
322 * set date dialog
323 */
324@CustomDialog
325@Component
326struct DateDialog {
327  controller?: CustomDialogController;
328  action: () => void = () => {};
329  private minDate: Date = new Date('1970-01-01');
330  private maxDate: Date = new Date('2037-12-31');
331  private selectedDate: Date = this.initValidDate();
332  @State date: string = '';
333  @State day: string = '';
334
335  build() {
336    Column() {
337      Column() {
338        Row() {
339          Text(this.date)
340            .fontSize($r('app.float.font_20'))
341            .fontWeight(500)
342            .fontColor($r("sys.color.ohos_id_color_primary"))
343
344          Text(this.day)
345            .fontSize($r('app.float.font_20'))
346            .fontWeight(500)
347            .fontColor($r("sys.color.ohos_id_color_primary"))
348        }
349        .height($r('app.float.wh_value_56'))
350
351        DatePicker({ start: this.minDate, end: this.maxDate, selected: this.selectedDate })
352          .onChange((result: DatePickerResult) => {
353            let month = result.month ? result.month : 0;
354            let fmt = result.year + '-' + DateAndTime.fill(month + 1) + '-' + DateAndTime.fill(result.day);
355            this.selectedDate = new Date(fmt);
356            LogUtil.info(MODULE_TAG + 'onchange user select date fmt is : ' + fmt);
357            LogUtil.info(MODULE_TAG + 'onchange user select date is :' + this.selectedDate);
358            this.date = DateAndTime.concatDate(result.year, month + 1, result.day);
359            let resource = DateAndTime.convert(result.year, month + 1, result.day);
360            ResourceUtil.getString(resource).then(value => {
361              this.day = value;
362              LogUtil.info(MODULE_TAG + 'onchange refresh the show date is : ' + this.date);
363              LogUtil.info(MODULE_TAG + 'onchange refresh the show day is : ' + this.day);
364            });
365          })
366          .width(ConfigData.WH_100_100)
367          .height($r('app.float.wh_value_200'))
368          .padding({ right: $r('app.float.wh_37') })
369      }
370      .padding({ left: $r('app.float.wh_padding_32'), right: $r('app.float.wh_padding_33') })
371
372      DialogButtonLayout({
373        firstClickEvent: () => {
374          this.controller?.close();
375          this.action();
376        },
377        secondClickEvent: () => {
378          LogUtil.info(MODULE_TAG + 'start set date to : ' + this.selectedDate);
379          let sysTime = new Date();
380          let fmt = this.selectedDate.getFullYear() + '-' +
381          DateAndTime.fill(this.selectedDate.getMonth() + 1) + '-' +
382          DateAndTime.fill(this.selectedDate.getDate()) + 'T' +
383          DateAndTime.fill(sysTime.getHours()) + ':' +
384          DateAndTime.fill(sysTime.getMinutes()) + ':' +
385          DateAndTime.fill(sysTime.getSeconds());
386          LogUtil.info(MODULE_TAG + 'set date fmt is : ' + fmt);
387          let s = (new Date(fmt)).getTime();
388          DateAndTimeModel.setTime(s);
389          this.controller?.close();
390          this.action();
391        }
392      })
393    }
394    .width(ConfigData.WH_100_100);
395  }
396
397  aboutToAppear() {
398    let datetime = new Date();
399    let y = datetime.getFullYear();
400    let m = datetime.getMonth() + 1;
401    let d = datetime.getDate();
402    this.date = DateAndTime.getSystemDate();
403    ResourceUtil.getString(DateAndTime.convert(y, m, d)).then(value => {
404      this.day = value;
405    });
406  }
407
408  private initValidDate() {
409    let sysDate = new Date();
410    let sysDateTime = sysDate.getTime();
411    LogUtil.info(MODULE_TAG + 'init sysDate is :' + sysDateTime);
412    if (sysDateTime < this.minDate.getTime()) {
413      return this.minDate;
414    }
415    if (sysDateTime > this.maxDate.getTime()) {
416      return this.maxDate;
417    }
418    return sysDate;
419  }
420}