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 "core/components/picker/picker_time_component.h"
17 
18 namespace OHOS::Ace {
19 
PickerTimeComponent()20 PickerTimeComponent::PickerTimeComponent()
21 {
22     vecAmPm_ = Localization::GetInstance()->GetAmPmStrings();
23 }
24 
OnTitleBuilding()25 void PickerTimeComponent::OnTitleBuilding()
26 {
27     PickerBaseComponent::OnTitleBuilding();
28 
29     auto titleComponent = GetTitle();
30     if (!titleComponent) {
31         LOGE("title component is null.");
32         return;
33     }
34 
35     auto date = PickerDate::Current();
36     titleComponent->SetData(date.ToString(false));
37 }
38 
OnColumnsBuilding()39 void PickerTimeComponent::OnColumnsBuilding()
40 {
41     HandleHourColumnBuilding();
42 
43     auto minuteColumn = GetColumn(PICKER_MINUTE_COLUMN);
44     auto secondColumn = GetColumn(PICKER_SECOND_COLUMN);
45     if (!minuteColumn) {
46         LOGE("minute column is null.");
47         return;
48     }
49 
50     minuteColumn->ClearOption();
51     for (uint32_t minute = 0; minute <= 59; ++minute) { // time's minute from 0 to 59
52         if (minute == selectedTime_.GetMinute()) {
53             minuteColumn->SetCurrentIndex(minuteColumn->GetOptionCount());
54         }
55         minuteColumn->AppendOption(GetMinuteFormatString(minute));
56     }
57 
58     if (hasSecond_ && secondColumn) {
59         secondColumn->ClearOption();
60         for (uint32_t second = 0; second <= 59; ++second) { // time's second from 0 to 59
61             if (second == selectedTime_.GetSecond()) {
62                 secondColumn->SetCurrentIndex(secondColumn->GetOptionCount());
63             }
64             secondColumn->AppendOption(GetSecondFormatString(second));
65         }
66     }
67 }
68 
HandleHourColumnBuilding()69 void PickerTimeComponent::HandleHourColumnBuilding()
70 {
71     auto hourColumn = GetColumn(PICKER_HOUR_COLUMN);
72     auto amPmColumn = GetColumn(PICKER_AMPM_COLUMN);
73     if (!hourColumn) {
74         LOGE("hour column is null.");
75         return;
76     }
77 
78     hourColumn->ClearOption();
79     if (hour24_) {
80         for (uint32_t hour = 0; hour <= 23; ++hour) { // time's hour from 0 to 23.
81             if (hour == selectedTime_.GetHour()) {
82                 hourColumn->SetCurrentIndex(hourColumn->GetOptionCount());
83             }
84             hourColumn->AppendOption(GetHourFormatString(hour));
85         }
86     } else if (amPmColumn) {
87         amPmColumn->ClearOption();
88         amPmColumn->AppendOption(GetAmFormatString());
89         amPmColumn->AppendOption(GetPmFormatString());
90         if (IsAmHour(selectedTime_.GetHour())) {
91             amPmColumn->SetCurrentIndex(0); // AM's index
92         } else {
93             amPmColumn->SetCurrentIndex(1); // PM's index
94         }
95         auto selectedHour = GetAmPmHour(selectedTime_.GetHour());
96         for (uint32_t hour = 1; hour <= 12; ++hour) { // hour start from 1 to 12
97             if (hour == selectedHour) {
98                 hourColumn->SetCurrentIndex(hourColumn->GetOptionCount());
99             }
100             hourColumn->AppendOption(GetHourFormatString(hour));
101         }
102     } else {
103         LOGE("AM PM column is null.");
104     }
105 }
106 
GetSelectedObject(bool isColumnChange,const std::string & changeColumnTag,int32_t status) const107 std::string PickerTimeComponent::GetSelectedObject(bool isColumnChange,
108     const std::string& changeColumnTag, int32_t status) const
109 {
110     auto time = selectedTime_;
111     if (isColumnChange) {
112         time = GetCurrentTime();
113     }
114     return time.ToString(true, hasSecond_, status);
115 }
116 
OnDataLinking(const std::string & tag,bool isAdd,uint32_t index,std::vector<std::string> & resultTags)117 void PickerTimeComponent::OnDataLinking(const std::string& tag, bool isAdd, uint32_t index,
118     std::vector<std::string>& resultTags)
119 {
120     if (tag != PICKER_HOUR_COLUMN) {
121         return;
122     }
123 
124     if (hour24_) {
125         HandleHour24Change(isAdd, index, resultTags);
126     } else {
127         HandleHour12Change(isAdd, index, resultTags);
128     }
129 }
130 
HandleHour24Change(bool isAdd,uint32_t index,std::vector<std::string> & resultTags)131 void PickerTimeComponent::HandleHour24Change(bool isAdd, uint32_t index, std::vector<std::string>& resultTags)
132 {
133     if (isAdd && index == 0) {
134         resultTags.emplace_back(PICKER_MONTHDAY_COLUMN);
135         return;
136     }
137     if (!isAdd && index == 23) { // hour end with 23 which is the last hour
138         resultTags.emplace_back(PICKER_MONTHDAY_COLUMN);
139         return;
140     }
141 }
142 
HandleHour12Change(bool isAdd,uint32_t index,std::vector<std::string> & resultTags)143 void PickerTimeComponent::HandleHour12Change(bool isAdd, uint32_t index, std::vector<std::string>& resultTags)
144 {
145     auto amPm = GetColumn(PICKER_AMPM_COLUMN);
146     if (!amPm) {
147         LOGE("AM PM column is null.");
148         return;
149     }
150 
151     if (amPm->GetCurrentIndex() == 0 && isAdd && index == 11) { // hour index start from 0 to 11
152         amPm->SetCurrentIndex(1); // add to PM's index
153         resultTags.emplace_back(PICKER_AMPM_COLUMN);
154         return;
155     }
156 
157     if (amPm->GetCurrentIndex() == 1 && !isAdd && index == 10) { // reduce to 11 hour (index is 10)
158         amPm->SetCurrentIndex(0); // change to AM whose index is 0
159         resultTags.emplace_back(PICKER_AMPM_COLUMN);
160         return;
161     }
162 
163     if (amPm->GetCurrentIndex() == 1 && isAdd && index == 11) { // is PM (index is 1) and last hour (index is 11)
164         amPm->SetCurrentIndex(0); // change to PM (index is 0)
165         resultTags.emplace_back(PICKER_AMPM_COLUMN);
166         resultTags.emplace_back(PICKER_MONTHDAY_COLUMN);
167         return;
168     }
169 
170     if (amPm->GetCurrentIndex() == 0 && !isAdd && index == 10) { // reduce to 11 hour(index is 10)
171         amPm->SetCurrentIndex(1); // change to PM
172         resultTags.emplace_back(PICKER_AMPM_COLUMN);
173         resultTags.emplace_back(PICKER_MONTHDAY_COLUMN);
174         return;
175     }
176 }
177 
OnSelectedSaving()178 void PickerTimeComponent::OnSelectedSaving()
179 {
180     selectedTime_ = GetCurrentTime();
181 }
182 
GetCurrentTime() const183 PickerTime PickerTimeComponent::GetCurrentTime() const
184 {
185     PickerTime time;
186     auto amPmColumn = GetColumn(PICKER_AMPM_COLUMN);
187     auto hourColumn = GetColumn(PICKER_HOUR_COLUMN);
188     auto minuteColumn = GetColumn(PICKER_MINUTE_COLUMN);
189     auto secondColumn = GetColumn(PICKER_SECOND_COLUMN);
190     if (!hourColumn || !minuteColumn) {
191         LOGE("hour or minute column is null.");
192         return time;
193     }
194 
195     if (hour24_) {
196         time.SetHour(hourColumn->GetCurrentIndex()); // hour from 0 to 23, index from 0 to 23
197     } else if (amPmColumn) {
198         time.SetHour(GetHourFromAmPm(amPmColumn->GetCurrentIndex() == 0, hourColumn->GetCurrentIndex() + 1));
199     } else {
200         LOGE("AM PM column is null.");
201     }
202 
203     time.SetMinute(minuteColumn->GetCurrentIndex()); // minute from 0 to 59, index from 0 to 59
204     if (hasSecond_ && secondColumn) {
205         time.SetSecond(secondColumn->GetCurrentIndex()); // second from 0 to 59, index from 0 to 59
206     }
207     return time;
208 }
209 
OnColumnsCreating()210 void PickerTimeComponent::OnColumnsCreating()
211 {
212     RemoveColumn(PICKER_AMPM_COLUMN);
213     RemoveColumn(PICKER_HOUR_COLUMN);
214     RemoveColumn(PICKER_MINUTE_COLUMN);
215     RemoveColumn(PICKER_SECOND_COLUMN);
216 
217     if (!hour24_) {
218         auto amPmColumn = AceType::MakeRefPtr<PickerColumnComponent>();
219         amPmColumn->SetColumnTag(PICKER_AMPM_COLUMN);
220         amPmColumn->SetWidthRatio(2); // date:amPm:hour:minute:second = 4:2:2:2:2
221         AppendColumn(amPmColumn);
222     }
223 
224     auto hourColumn = AceType::MakeRefPtr<PickerColumnComponent>();
225     hourColumn->SetColumnTag(PICKER_HOUR_COLUMN);
226     hourColumn->SetWidthRatio(2); // date:hour:minute:second = 3:2:2:2
227     AppendColumn(hourColumn);
228 
229     auto minuteColumn = AceType::MakeRefPtr<PickerColumnComponent>();
230     minuteColumn->SetColumnTag(PICKER_MINUTE_COLUMN);
231     minuteColumn->SetWidthRatio(2); // date:hour:minute:second = 3:2:2:2
232     AppendColumn(minuteColumn);
233 
234     if (hasSecond_) {
235         auto secondColumn = AceType::MakeRefPtr<PickerColumnComponent>();
236         secondColumn->SetColumnTag(PICKER_SECOND_COLUMN);
237         secondColumn->SetWidthRatio(2); // date:hour:minute:second = 3:2:2:2
238         AppendColumn(secondColumn);
239         auto theme = GetTheme();
240         if (!theme) {
241             return;
242         }
243         hourColumn->SetHasSplitter(theme->HasTimeSplitter());
244         minuteColumn->SetHasSplitter(theme->HasTimeSplitter());
245     }
246 }
247 
GetAmFormatString() const248 std::string PickerTimeComponent::GetAmFormatString() const
249 {
250     if (vecAmPm_.empty()) {
251         return "AM";
252     }
253     return vecAmPm_[0]; // first index is AM
254 }
255 
GetPmFormatString() const256 std::string PickerTimeComponent::GetPmFormatString() const
257 {
258     if (vecAmPm_.size() < 2) { // size need to be 2 for AM and PM
259         return "PM";
260     }
261     return vecAmPm_[1]; // second index is PM
262 }
263 
IsAmHour(uint32_t hourOf24) const264 bool PickerTimeComponent::IsAmHour(uint32_t hourOf24) const
265 {
266     return (0 <= hourOf24 && hourOf24 <= 11); // 00:00 to 11:00 is AM hour
267 }
268 
GetAmPmHour(uint32_t hourOf24) const269 uint32_t PickerTimeComponent::GetAmPmHour(uint32_t hourOf24) const
270 {
271     if (hourOf24 == 0) {
272         return 12; // AM 12:00 means 00:00 in 24 hour style
273     } else if (1 <= hourOf24 && hourOf24 <= 11) { // 00:00 to 11:00 is the same for any hour style
274         return hourOf24;
275     } else if (hourOf24 == 12) { // 12:00 means PM start hour
276         return 12; // 12 PM
277     } else { // hour from 13 to 23
278         return hourOf24 - 12; // need reduce 12 to 12 hours style
279     }
280 }
281 
GetHourFromAmPm(bool isAm,uint32_t amPmhour) const282 uint32_t PickerTimeComponent::GetHourFromAmPm(bool isAm, uint32_t amPmhour) const
283 {
284     if (isAm) {
285         if (amPmhour == 12) { // AM 12:00 means 00:00
286             return 0;
287         }
288         return amPmhour;
289     }
290 
291     if (amPmhour == 12) { // PM 12 means 12:00
292         return 12;
293     }
294 
295     return amPmhour + 12; // need add 12 hour to 24 hours style
296 }
297 
GetHourFormatString(uint32_t hour) const298 std::string PickerTimeComponent::GetHourFormatString(uint32_t hour) const
299 {
300     DateTime time;
301     time.minute = hour; // minute range [0, 59], hour range [0, 23]; hour range is in minute range.
302     if (Localization::GetInstance()->HasZeroHour()) {
303         return AddZeroPrefix(Localization::GetInstance()->FormatDateTime(time, "m"));
304     }
305 
306     return Localization::GetInstance()->FormatDateTime(time, "m");
307 }
308 
GetMinuteFormatString(uint32_t minute) const309 std::string PickerTimeComponent::GetMinuteFormatString(uint32_t minute) const
310 {
311     DateTime time;
312     time.minute = minute;
313     return AddZeroPrefix(Localization::GetInstance()->FormatDateTime(time, "m"));
314 }
315 
GetSecondFormatString(uint32_t second) const316 std::string PickerTimeComponent::GetSecondFormatString(uint32_t second) const
317 {
318     DateTime time;
319     time.second = second;
320     return AddZeroPrefix(Localization::GetInstance()->FormatDateTime(time, "s"));
321 }
322 
AddZeroPrefix(const std::string & value) const323 std::string PickerTimeComponent::AddZeroPrefix(const std::string& value) const
324 {
325     if (value.size() == 1 && '0' <= value[0] && value[0] <= '9') { // value is number in range [0, 9]
326         return std::string("0") + value; // add prefix '0'
327     }
328     return value;
329 }
330 
331 } // namespace OHOS::Ace
332