1 /*
2  * Copyright (c) 2021-2023 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 #ifndef FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_CALENDAR_CALENDAR_DATA_ADAPTER_H
17 #define FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_CALENDAR_CALENDAR_DATA_ADAPTER_H
18 
19 #include <array>
20 #include <functional>
21 #include <map>
22 #include <queue>
23 #include <vector>
24 
25 #include "base/json/json_util.h"
26 #include "base/log/log.h"
27 #include "base/memory/ace_type.h"
28 #include "base/utils/string_utils.h"
29 #include "bridge/codec/codec_data.h"
30 #include "core/components/calendar/calendar_theme.h"
31 #include "core/event/ace_event_helper.h"
32 #include "core/pipeline/pipeline_context.h"
33 
34 namespace OHOS::Ace {
35 
36 constexpr int32_t CALENDAR_MIN_DATE_YEAR = 1970;
37 constexpr int32_t CALENDAR_MIN_DATE_MONTH = 1;
38 constexpr int32_t CALENDAR_INVALID = -1;
39 constexpr int32_t CALENDAR_FIRST_DAY_NUM_OF_MONTH = 1;
40 constexpr int32_t CALENDAR_TOTAL_MONTH_OF_YEAR = 12;
41 constexpr int32_t CALENDAR_CACHE_PAGE = 3;
42 
43 struct CalendarMonth {
44     int32_t year = CALENDAR_MIN_DATE_YEAR;
45     // calendar month value is from 0 to 11
46     int32_t month = CALENDAR_MIN_DATE_MONTH;
47 
48     bool operator==(const CalendarMonth& calendarMonth) const
49     {
50         return calendarMonth.month == month && calendarMonth.year == year;
51     }
52     bool operator!=(const CalendarMonth& calendarMonth) const
53     {
54         return calendarMonth.month != month || calendarMonth.year != year;
55     }
56     bool operator>(const CalendarMonth& calendarMonth) const
57     {
58         return year > calendarMonth.year || (year == calendarMonth.year && month > calendarMonth.month);
59     }
60     bool operator<(const CalendarMonth& calendarMonth) const
61     {
62         return year < calendarMonth.year || (year == calendarMonth.year && month < calendarMonth.month);
63     }
64 
GetNextMonthCalendarMonth65     static CalendarMonth GetNextMonth(const CalendarMonth& calendarMonth)
66     {
67         CalendarMonth nextMonth = calendarMonth;
68         if (calendarMonth.month + 1 >= CALENDAR_TOTAL_MONTH_OF_YEAR) {
69             nextMonth.month = 0;
70             ++nextMonth.year;
71         } else {
72             ++nextMonth.month;
73         }
74         return nextMonth;
75     }
76 
GetLastMonthCalendarMonth77     static CalendarMonth GetLastMonth(const CalendarMonth& calendarMonth)
78     {
79         CalendarMonth lastMonth = calendarMonth;
80         if (calendarMonth.month - 1 < 0) {
81             lastMonth.month = CALENDAR_TOTAL_MONTH_OF_YEAR - 1;
82             --lastMonth.year;
83         } else {
84             --lastMonth.month;
85         }
86         return lastMonth;
87     }
88 
ToStringCalendarMonth89     std::string ToString() const
90     {
91         auto json = JsonUtil::Create(true);
92         json->Put("year", year);
93         json->Put("month", month);
94         return json->ToString();
95     }
96 };
97 
98 struct HashFunc {
operatorHashFunc99     std::size_t operator()(const CalendarMonth& key) const
100     {
101         return ((std::hash<uint32_t>()(key.year) ^ (std::hash<uint32_t>()(key.month) << 1)) >> 1);
102     }
103 };
104 
105 enum class MonthState {
106     CUR_MONTH,
107     PRE_MONTH,
108     NEXT_MONTH
109 };
110 
111 struct CalendarDay {
112     int32_t index = 0;
113     int32_t day = 0;
114     bool weekend = false;
115     bool today = false;
116     bool focused = false;
117     bool touched = false;
118     bool isFirstOfLunar = false;
119     bool hasSchedule = false;
120     bool markLunarDay = false;
121 
122     // For calendar selector
123     bool isHovering = false;
124     bool isPressing = false;
125     bool isSelected = false;
126     bool isKeyFocused = false;
127 
128     std::string lunarMonth;
129     std::string lunarDay;
130     std::string dayMark;
131     std::string dayMarkValue;
132     CalendarMonth month;
133 
134     static constexpr char INDEX[] { "index" };
135     static constexpr char DAY[] { "day" };
136     static constexpr char MONTH[] { "month" };
137     static constexpr char YEAR[] { "year" };
138     static constexpr char LUNAR_MONTH[] { "lunarMonth" };
139     static constexpr char LUNAR_DAY[] { "lunarDay" };
140     static constexpr char DAY_MARK[] { "dayMark" };
141     static constexpr char DAY_MARK_VALUE[] { "dayMarkValue" };
142 
ToStringCalendarDay143     std::string ToString() const
144     {
145         auto json = JsonUtil::Create(true);
146         json->Put(DAY, day);
147         json->Put(INDEX, index);
148         json->Put(MONTH, month.month);
149         json->Put(YEAR, month.year);
150         json->Put(LUNAR_MONTH, lunarMonth.c_str());
151         json->Put(LUNAR_DAY, lunarDay.c_str());
152         return json->ToString();
153     }
154 };
155 
156 struct CalendarDaysOfMonth {
157     CalendarMonth month;
158     int32_t firstDayIndex = CALENDAR_INVALID;
159     int32_t lastDayIndex = CALENDAR_INVALID;
160     int32_t today = CALENDAR_INVALID;
161     std::vector<CalendarDay> days;
162 };
163 
164 struct CalendarDataRequest {
CalendarDataRequestCalendarDataRequest165     CalendarDataRequest(const CalendarMonth& month, int32_t indexOfContainer)
166         : month(month), indexOfContainer(indexOfContainer)
167     {}
168     ~CalendarDataRequest() = default;
169 
170     int32_t selected = 0;
171     CalendarMonth month;
172     MonthState state = MonthState::CUR_MONTH;
173     int32_t indexOfContainer = 0;
174 };
175 
176 struct CardCalendarAttr {
177     int32_t startDayOfWeek = 0;
178     bool isV2Component = false;
179     bool showLunar = true;
180     bool showHoliday = true;
181     bool needSlide = false;
182     bool cardCalendar = false;
183     bool listenersReady = true;
184     Axis axis = Axis::HORIZONTAL;
185     std::string offDays;
186     std::string holidays;
187     std::string workDays;
188     EventMarker requestData;
189     RefPtr<CalendarTheme> calendarTheme;
190     TextDirection textDirection = TextDirection::LTR;
191     CalendarType type { CalendarType::NORMAL };
192 };
193 
194 struct ObtainedMonth {
195     int32_t year = 0;
196     int32_t month = 0;
197     int32_t firstDayIndex = CALENDAR_INVALID;
198     std::vector<CalendarDay> days;
199 };
200 
201 using PendingRequestQueue = std::queue<CalendarDataRequest>;
202 using RequestDataCompleteCallback = std::function<void()>;
203 
204 class CalendarDataChangeListener : public AceType {
205     DECLARE_ACE_TYPE(CalendarDataChangeListener, AceType);
206 
207 public:
208     virtual void OnDataChanged(const CalendarDaysOfMonth& daysOfMonth) = 0;
209     virtual void OnSelectedDay(int32_t selected) = 0;
210     virtual void OnFocusChanged(bool focusStatus) = 0;
211     virtual void UpdateCardCalendarAttr(CardCalendarAttr&& attr) = 0;
212     virtual void OnSwiperMove() = 0;
213 };
214 
215 struct DataAdapterAction {
216     std::string bundleName;
217     std::string abilityName;
218     int32_t messageCode = 0;
219     std::string data;
220 
GetActionDataAdapterAction221     std::string GetAction()
222     {
223         return "{\"element\":{\"bundleName\":\"" + bundleName +
224                 "\",\"name\":\"" + abilityName + "\",\"type\":1},\"code\":" +
225                 std::to_string(messageCode) + "}";
226     }
227 };
228 
229 struct CalendarDataAdapterAction : public DataAdapterAction {
GetDataCalendarDataAdapterAction230     std::string GetData(int32_t year, int32_t month)
231     {
232         return "{\"year\":" + std::to_string(year) + ",\"month\":" + std::to_string(month) + "}";
233     }
234 
GetActionCalendarDataAdapterAction235     std::vector<Framework::CodecData> GetAction(int32_t year, int32_t month)
236     {
237         std::vector<Framework::CodecData> args;
238         Framework::CodecData header(DataAdapterAction::GetAction());
239         args.push_back(header);
240         Framework::CodecData params(GetData(year, month));
241         args.push_back(params);
242 
243         return args;
244     }
245 };
246 
247 class CalendarDataAdapter : public AceType {
248     DECLARE_ACE_TYPE(CalendarDataAdapter, AceType);
249 
250 public:
251     explicit CalendarDataAdapter(
252         const CalendarDataAdapterAction& dataAdapterAction, const WeakPtr<PipelineContext>& pipelineContext);
253     ~CalendarDataAdapter() override = default;
254 
RegisterDataListener(const RefPtr<CalendarDataChangeListener> & listener)255     void RegisterDataListener(const RefPtr<CalendarDataChangeListener>& listener)
256     {
257         allListeners_.push_back(listener);
258         if (allListeners_.size() == CALENDAR_CACHE_PAGE && !calendarAttr_.listenersReady) {
259             calendarAttr_.listenersReady = true;
260             for (const auto& listen : allListeners_) {
261                 listen->UpdateCardCalendarAttr(std::move(calendarAttr_));
262             }
263         }
264     }
265 
AddPendingRequest(const CalendarMonth & month,const int32_t indexOfContainer)266     void AddPendingRequest(const CalendarMonth& month, const int32_t indexOfContainer)
267     {
268         requestQueue_.emplace(month, indexOfContainer);
269         indexMap_[indexOfContainer] = month;
270     }
271 
AddPendingRequest(const CalendarDataRequest & request)272     void AddPendingRequest(const CalendarDataRequest& request)
273     {
274         requestQueue_.emplace(request);
275         indexMap_[request.indexOfContainer] = request.month;
276     }
277 
278     void RequestData(const CalendarDataRequest& request);
279 
NotifySelectedChanged()280     void NotifySelectedChanged() const
281     {
282         int32_t listenersSize = static_cast<int32_t>(allListeners_.size());
283         if (indexOfContainer_ >= 0 && indexOfContainer_ < listenersSize) {
284             auto& listener = allListeners_[indexOfContainer_];
285             listener->OnSelectedDay(selectedDay_);
286         }
287     }
288 
GetToday()289     const CalendarDay& GetToday() const
290     {
291         return today_;
292     }
293 
SetToday(const CalendarDay & today)294     void SetToday(const CalendarDay& today)
295     {
296         today_.day = today.day;
297         today_.month.month = today.month.month;
298         today_.month.year = today.month.year;
299     }
300 
GetCalendarCache()301     const std::array<std::vector<std::string>, 3>& GetCalendarCache()
302     {
303         return calendarCache_;
304     }
305 
SetSelectedChanged(int32_t selected,int32_t indexOfContainer)306     void SetSelectedChanged(int32_t selected, int32_t indexOfContainer)
307     {
308         selectedDay_ = selected;
309         indexOfContainer_ = indexOfContainer;
310     }
311 
NotifyFocusChanged(bool focusStatus)312     void NotifyFocusChanged(bool focusStatus) const
313     {
314         for (const auto& listener : allListeners_) {
315             listener->OnFocusChanged(focusStatus);
316         }
317     }
318 
SetRequestDataEvent(const EventMarker & requestData)319     void SetRequestDataEvent(const EventMarker& requestData)
320     {
321         if (requestData_ != requestData.GetData().eventId) {
322             requestData_ = requestData;
323             requestDataEvent_ =
324                 AceAsyncEvent<void(const std::string&)>::Create(requestData, pipelineContext_);
325         }
326     }
327 
SetCardCalendar(bool cardCalendar)328     void SetCardCalendar(bool cardCalendar)
329     {
330         cardCalendar_ = cardCalendar;
331     }
332 
IsCardCalendar()333     bool IsCardCalendar() const
334     {
335         return cardCalendar_;
336     }
337 
SetStartDayOfWeek(int32_t indexOfWeek)338     void SetStartDayOfWeek(int32_t indexOfWeek)
339     {
340         startDayOfWeek_ = indexOfWeek;
341     }
342 
GetStartDayOfWeek()343     int32_t GetStartDayOfWeek() const
344     {
345         return startDayOfWeek_;
346     }
347 
SetCachePath(const std::string & cachePath)348     static void SetCachePath(const std::string& cachePath)
349     {
350         cachePath_ = cachePath;
351     }
352 
GetDayOfMonthCache()353     const std::array<CalendarDaysOfMonth, CALENDAR_CACHE_PAGE>& GetDayOfMonthCache()
354     {
355         return dayOfMonthCache_;
356     }
357 
ShowLunar()358     bool ShowLunar() const
359     {
360         return showLunar_;
361     }
362 
SetCurrentMonth(const CalendarMonth & currentMonth)363     void SetCurrentMonth(const CalendarMonth& currentMonth)
364     {
365         currentMonth_ = currentMonth;
366     }
367 
SetOffDays(const std::string & offDays)368     void SetOffDays(const std::string& offDays)
369     {
370         offDays_ = offDays;
371     }
372 
GetWorkDays()373     const std::string& GetWorkDays() const
374     {
375         return workDays_;
376     }
377 
GetHolidays()378     const std::string& GetHolidays() const
379     {
380         return holidays_;
381     }
382 
HasMoved()383     bool HasMoved() const
384     {
385         return hasMoved_;
386     }
387 
SetHasMoved(bool hasMoved)388     void SetHasMoved(bool hasMoved)
389     {
390         hasMoved_ = hasMoved;
391     }
392 
SetCrossMonth(bool isCrossMonth)393     void SetCrossMonth(bool isCrossMonth)
394     {
395         isCrossMonth_ = isCrossMonth;
396     }
397 
IsCrossMonth()398     bool IsCrossMonth()
399     {
400         return isCrossMonth_;
401     }
402 
403     void ParseCardCalendarData(const std::string& source);
404     void ParseCalendarData(std::queue<ObtainedMonth>&& months);
405     void UpdateCardCalendarAttr(CardCalendarAttr&& attr);
406     void SetOffDays(CalendarDay& dayInfo);
407 
408 private:
409     bool ParseData(int32_t index, const std::string& source, CalendarDaysOfMonth& result);
410 
411     void SaveCacheData(const CalendarDataRequest& request, const std::string& result);
412 
413     bool GetCacheData(const CalendarDataRequest& request);
414 
415     void ParseMonthData(const std::unique_ptr<JsonValue>& monthData);
416 
417     void RequestDataInWatch(const CalendarDataRequest& request);
418 
419     void NotifyDataChanged(const CalendarDaysOfMonth& data, int32_t indexOfContainer);
420 
421     void FillMonthData(const CalendarDataRequest& request, CalendarDaysOfMonth& result);
422     void FillPreMonthData(
423         const CalendarMonth& currentMonth, int32_t indexOfContainer, int32_t& index, CalendarDaysOfMonth& result);
424     void FillCurrentMonthData(
425         const CalendarMonth& currentMonth, int32_t indexOfContainer, int32_t& index, CalendarDaysOfMonth& result);
426     void FillNextMonthData(
427         const CalendarMonth& currentMonth, int32_t indexOfContainer, int32_t& index, CalendarDaysOfMonth& result);
428 
RequestNextData()429     void RequestNextData()
430     {
431         if (!requestQueue_.empty()) {
432             CalendarDataRequest request = requestQueue_.front();
433             RequestData(request);
434             requestQueue_.pop();
435         } else {
436             NotifySelectedChanged();
437         }
438     }
439 
HandleDataRequestResult(const CalendarDataRequest & request,const std::string & result)440     void HandleDataRequestResult(const CalendarDataRequest& request, const std::string& result)
441     {
442         CalendarDaysOfMonth daysOfMonth;
443         daysOfMonth.month = request.month;
444         if (request.indexOfContainer >= 0 && request.indexOfContainer < CALENDAR_CACHE_PAGE) {
445             if (!ParseData(request.indexOfContainer, result, daysOfMonth)) {
446                 return;
447             }
448             NotifyDataChanged(daysOfMonth, request.indexOfContainer);
449             RequestNextData();
450         }
451     }
452 
453     bool cardCalendar_ = false;
454     bool showLunar_ = true;
455     bool firstLoad_ = true;
456     bool hasMoved_ = false;
457     bool isV2Component_ = false;
458     bool isCrossMonth_ = false;
459     int32_t selectedDay_ = 0;
460     int32_t indexOfContainer_ = 0;
461     int32_t startDayOfWeek_ = 0;
462     int32_t requestNextIndex_ = -1;
463     std::string offDays_;
464     std::string workDays_;
465     std::string holidays_;
466     CalendarDay today_;
467     EventMarker requestData_;
468     CalendarMonth currentMonth_;
469     PendingRequestQueue requestQueue_;
470     CalendarType type_ { CalendarType::NORMAL };
471     CardCalendarAttr calendarAttr_;
472     CalendarDataAdapterAction dataAdapterAction_;
473     WeakPtr<PipelineContext> pipelineContext_;
474     std::array<std::vector<std::string>, CALENDAR_CACHE_PAGE> calendarCache_;
475     std::array<CalendarDaysOfMonth, CALENDAR_CACHE_PAGE> dayOfMonthCache_;
476     std::vector<RefPtr<CalendarDataChangeListener>> allListeners_;
477     std::function<void(const std::string&)> requestDataEvent_;
478     std::unordered_map<int32_t, CalendarMonth> indexMap_;
479     std::unordered_map<CalendarMonth, std::string, HashFunc> monthCache_;
480 
481     static std::string cachePath_;
482 };
483 
484 namespace StringUtils {
485 
StringToCalendarDay(const std::string & str,CalendarDay & day)486 inline bool StringToCalendarDay(const std::string& str, CalendarDay& day)
487 {
488     std::vector<int> date;
489     StringSplitter(str, '-', date);
490     if (date.size() == 3) {
491         day.month.year = date[0];
492         day.month.month = date[1] - 1;
493         day.day = date[2];
494         return true;
495     }
496     return false;
497 }
498 
499 } // namespace StringUtils
500 } // namespace OHOS::Ace
501 
502 #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_CALENDAR_CALENDAR_DATA_ADAPTER_H
503