• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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/calendar/calendar_data_adapter.h"
17 
18 #include "base/i18n/localization.h"
19 
20 namespace OHOS::Ace {
21 namespace {
22 
23 constexpr int32_t CALENDAR_DAYS_FIVE_ROW_COUNT = 35;
24 constexpr int32_t CALENDAR_DAYS_SIX_ROW_COUNT = 42;
25 constexpr int32_t CALENDAR_MONTH_COUNT = 3;
26 
ParseDayNumberProp(const std::unique_ptr<JsonValue> & item,const std::string & key,int32_t & value)27 bool ParseDayNumberProp(const std::unique_ptr<JsonValue>& item, const std::string& key, int32_t& value)
28 {
29     if (!item) {
30         LOGE("item is nullptr");
31         return false;
32     }
33     if (!item->Contains(key)) {
34         LOGE("parse day number error, not find key:%{public}s", key.c_str());
35         return false;
36     }
37 
38     auto dayValue = item->GetValue(key);
39     if (dayValue->IsNumber()) {
40         value = dayValue->GetInt();
41         return true;
42     } else {
43         LOGW("parse day number type error");
44         return false;
45     }
46 }
47 
48 } // namespace
49 
50 std::string CalendarDataAdapter::cachePath_;
51 
CalendarDataAdapter(const CalendarDataAdapterAction & dataAdapterAction,const WeakPtr<PipelineContext> & pipelineContext)52 CalendarDataAdapter::CalendarDataAdapter(
53     const CalendarDataAdapterAction& dataAdapterAction, const WeakPtr<PipelineContext>& pipelineContext)
54     : dataAdapterAction_(dataAdapterAction), pipelineContext_(pipelineContext)
55 {
56     Date currentDate = Date::Current();
57     today_.day = static_cast<int32_t>(currentDate.day);
58     today_.month.year = static_cast<int32_t>(currentDate.year);
59     today_.month.month = static_cast<int32_t>(currentDate.month) - 1;
60 }
61 
ParseData(int32_t indexOfContainer,const std::string & source,CalendarDaysOfMonth & result)62 bool CalendarDataAdapter::ParseData(int32_t indexOfContainer, const std::string& source, CalendarDaysOfMonth& result)
63 {
64     static const std::string daysKey = "days";
65     auto sourceJsonValue = JsonUtil::ParseJsonString(source);
66     calendarCache_[indexOfContainer].clear();
67     if (!sourceJsonValue || !sourceJsonValue->Contains(daysKey)) {
68         LOGE("not find days");
69         return false;
70     }
71 
72     auto daysValue = sourceJsonValue->GetValue(daysKey);
73     if (!daysValue || !daysValue->IsArray() || daysValue->GetArraySize() <= 0) {
74         LOGE("calendar days not array");
75         return false;
76     }
77 
78     int daySize = daysValue->GetArraySize();
79     if (daySize != CALENDAR_DAYS_FIVE_ROW_COUNT && daySize != CALENDAR_DAYS_SIX_ROW_COUNT) {
80         LOGE("calendar days size cannot support, size = %{public}d", daySize);
81         return false;
82     }
83 
84     for (int32_t index = 0; index < daySize; ++index) {
85         auto item = daysValue->GetArrayItem(index);
86         if (!item) {
87             continue;
88         }
89         CalendarDay dayInfo;
90         if (!ParseDayNumberProp(item, CalendarDay::INDEX, dayInfo.index)) {
91             continue;
92         }
93         if (!ParseDayNumberProp(item, CalendarDay::DAY, dayInfo.day)) {
94             continue;
95         }
96         if (!ParseDayNumberProp(item, CalendarDay::MONTH, dayInfo.month.month)) {
97             continue;
98         }
99         if (!ParseDayNumberProp(item, CalendarDay::YEAR, dayInfo.month.year)) {
100             continue;
101         }
102         if (dayInfo.day == CALENDAR_FIRST_DAY_NUM_OF_MONTH && result.firstDayIndex == CALENDAR_INVALID) {
103             result.firstDayIndex = dayInfo.index;
104         }
105 
106         if (dayInfo.month == result.month) {
107             result.lastDayIndex = dayInfo.index;
108         }
109 
110         // mark weekend
111         SetOffDays(dayInfo);
112         // Mark today.
113         dayInfo.today = dayInfo.month == today_.month && dayInfo.day == today_.day;
114         if (dayInfo.today) {
115             result.today = dayInfo.index;
116         }
117         // Get lunarDay information.
118         dayInfo.lunarMonth = item->GetString(CalendarDay::LUNAR_MONTH, "");
119         dayInfo.lunarDay = item->GetString(CalendarDay::LUNAR_DAY, "");
120         // Get mark information.
121         dayInfo.dayMark = item->GetString(CalendarDay::DAY_MARK, "");
122         dayInfo.dayMarkValue = item->GetString(CalendarDay::DAY_MARK_VALUE, "");
123         result.days.push_back(dayInfo);
124         calendarCache_[indexOfContainer].push_back(item->ToString());
125     }
126     dayOfMonthCache_[indexOfContainer] = result;
127     return true;
128 }
129 
RequestData(const CalendarDataRequest & request)130 void CalendarDataAdapter::RequestData(const CalendarDataRequest& request)
131 {
132     auto context = pipelineContext_.Upgrade();
133     auto weak = AceType::WeakClaim(this);
134     if (!context) {
135         return;
136     }
137     if (cardCalendar_ || isV2Component_) {
138         indexMap_[request.indexOfContainer] = request.month;
139         requestNextIndex_ = request.indexOfContainer;
140         auto json = JsonUtil::Create(true);
141         json->Put("month", request.month.month);
142         json->Put("year", request.month.year);
143         json->Put("currentMonth", currentMonth_.month);
144         json->Put("currentYear", currentMonth_.year);
145         if (request.month == currentMonth_) {
146             json->Put("monthState", static_cast<int32_t>(MonthState::CUR_MONTH));
147         } else if (request.month > currentMonth_) {
148             json->Put("monthState", static_cast<int32_t>(MonthState::NEXT_MONTH));
149         } else if (request.month < currentMonth_) {
150             json->Put("monthState", static_cast<int32_t>(MonthState::PRE_MONTH));
151         }
152         if (requestDataEvent_) {
153             requestDataEvent_(json->ToString());
154         }
155         auto iter = monthCache_.find(request.month);
156         if (iter != monthCache_.end()) {
157             auto monthDataJson = JsonUtil::ParseJsonString(monthCache_[iter->first]);
158             ParseMonthData(monthDataJson);
159         }
160         context->GetTaskExecutor()->PostTask(
161             [weak]() {
162               auto dataAdapter = weak.Upgrade();
163               if (!dataAdapter) {
164                   LOGW("dataAdapter is null");
165                   return;
166               }
167               dataAdapter->RequestNextData();
168             },
169             TaskExecutor::TaskType::UI, "ArkUICalendarRequestData",
170             TaskExecutor::GetPriorityTypeWithCheck(PriorityType::IMMEDIATE));
171         return;
172     }
173     if (SystemProperties::GetDeviceType() == DeviceType::TV || type_ == CalendarType::NORMAL) {
174         GetCacheData(request);
175         if (!context->GetMessageBridge()) {
176             return;
177         }
178         context->GetMessageBridge()->SendMessage(dataAdapterAction_.GetAction(request.month.year, request.month.month),
179             [weak, request](const std::string& result) {
180                 auto dataAdapter = weak.Upgrade();
181                 if (!dataAdapter) {
182                     LOGE("date adapter is nullptr");
183                     return;
184                 }
185                 dataAdapter->HandleDataRequestResult(request, result);
186                 dataAdapter->SaveCacheData(request, result);
187             });
188     } else if (SystemProperties::GetDeviceType() == DeviceType::WATCH || type_ == CalendarType::SIMPLE) {
189         indexMap_[request.indexOfContainer] = request.month;
190         RequestDataInWatch(request);
191     }
192 }
193 
GetCacheData(const CalendarDataRequest & request)194 bool CalendarDataAdapter::GetCacheData(const CalendarDataRequest& request)
195 {
196     std::string filePath = cachePath_;
197     filePath.append("/")
198         .append(std::to_string(request.month.year))
199         .append(std::to_string(request.month.month))
200         .append(Localization::GetInstance()->GetLanguageTag());
201 
202     std::ifstream file(filePath, std::ifstream::in | std::ifstream::binary);
203     bool isDataCached = false;
204     if (file.is_open()) {
205         std::string result((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
206         if (!result.empty()) {
207             CalendarDaysOfMonth daysOfMonth;
208             daysOfMonth.month = request.month;
209             if (request.indexOfContainer >= 0 && request.indexOfContainer < CALENDAR_CACHE_PAGE) {
210                 if (!ParseData(request.indexOfContainer, result, daysOfMonth)) {
211                     return false;
212                 }
213                 NotifyDataChanged(daysOfMonth, request.indexOfContainer);
214             }
215         }
216         isDataCached = true;
217     }
218     return isDataCached;
219 }
220 
SaveCacheData(const CalendarDataRequest & request,const std::string & result)221 void CalendarDataAdapter::SaveCacheData(const CalendarDataRequest& request, const std::string& result)
222 {
223     auto context = pipelineContext_.Upgrade();
224     auto bkTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::BACKGROUND);
225 
226     bkTaskExecutor.PostTask(
227         [request, result]() {
228             std::string url = cachePath_;
229             url.append("/")
230                 .append(std::to_string(request.month.year))
231                 .append(std::to_string(request.month.month))
232                 .append(Localization::GetInstance()->GetLanguageTag());
233             std::ofstream outFile(url, std::fstream::out);
234             if (!outFile) {
235                 LOGE("the file open failed");
236                 return;
237             }
238             outFile.write(reinterpret_cast<const char*>(result.c_str()), result.size());
239         },
240         "ArkUICalendarSaveCacheData", TaskExecutor::GetPriorityTypeWithCheck(PriorityType::IMMEDIATE));
241 }
242 
ParseCardCalendarData(const std::string & source)243 void CalendarDataAdapter::ParseCardCalendarData(const std::string& source)
244 {
245     if (source.empty()) {
246         return;
247     }
248     auto sourceJson = JsonUtil::ParseJsonString(source);
249     if (!sourceJson->IsValid() || !sourceJson->IsArray()) {
250         return;
251     }
252 
253     auto child = sourceJson->GetChild();
254     while (child->IsValid()) {
255         ParseMonthData(child);
256         child = child->GetNext();
257     }
258 }
259 
UpdateCardCalendarAttr(CardCalendarAttr && attr)260 void CalendarDataAdapter::UpdateCardCalendarAttr(CardCalendarAttr&& attr)
261 {
262     showLunar_ = attr.showLunar;
263     cardCalendar_ = attr.cardCalendar;
264     offDays_ = attr.offDays;
265     holidays_ = attr.holidays;
266     workDays_ = attr.workDays;
267     type_ = attr.type;
268     isV2Component_ = attr.isV2Component;
269     SetRequestDataEvent(attr.requestData);
270 
271     if ((SystemProperties::GetDeviceType() == DeviceType::WATCH || type_ == CalendarType::SIMPLE) &&
272         startDayOfWeek_ != attr.startDayOfWeek) {
273         startDayOfWeek_ = attr.startDayOfWeek;
274         for (const auto& index : indexMap_) {
275             if (index.second == currentMonth_) {
276                 CalendarDataRequest request(index.second, index.first);
277                 RequestData(request);
278             } else {
279                 AddPendingRequest(index.second, index.first);
280             }
281         }
282     }
283     startDayOfWeek_ = attr.startDayOfWeek;
284 
285     if (allListeners_.size() != CALENDAR_MONTH_COUNT) {
286         calendarAttr_ = std::move(attr);
287         calendarAttr_.listenersReady = false;
288     } else {
289         for (const auto& listen : allListeners_) {
290             listen->UpdateCardCalendarAttr(std::move(attr));
291         }
292     }
293 }
294 
ParseMonthData(const std::unique_ptr<JsonValue> & monthData)295 void CalendarDataAdapter::ParseMonthData(const std::unique_ptr<JsonValue>& monthData)
296 {
297     CalendarMonth calendarMonth;
298     calendarMonth.month = monthData->GetInt("month", -1);
299     calendarMonth.year = monthData->GetInt("year", -1);
300     monthCache_[calendarMonth] = monthData->ToString();
301 
302     int32_t indexOfContainer = -1;
303     for (const auto& index : indexMap_) {
304         if (index.second == calendarMonth) {
305             indexOfContainer = index.first;
306         }
307     }
308     static const int32_t miniIndex = 0;
309     static const int32_t maxIndex = 2;
310     if (indexOfContainer < miniIndex || indexOfContainer > maxIndex) {
311         return;
312     }
313     auto data = monthData->GetValue("data");
314     if (!data) {
315         return;
316     }
317     auto child = data->GetChild();
318     CalendarDaysOfMonth result;
319     result.month = calendarMonth;
320     bool hasLunarInfo = true;
321     while (child->IsValid()) {
322         CalendarDay dayInfo;
323         dayInfo.day = child->GetInt("day");
324         dayInfo.index = child->GetInt("index");
325         dayInfo.month.month = child->GetInt("month");
326         dayInfo.month.year = child->GetInt("year");
327         dayInfo.lunarDay = child->GetString("lunarDay", "");
328         dayInfo.dayMark = child->GetString("dayMark", "");
329         dayInfo.dayMarkValue = child->GetString("dayMarkValue", "");
330         dayInfo.isFirstOfLunar = child->GetBool("isFirstOfLunar", false);
331         dayInfo.hasSchedule = child->GetBool("hasSchedule", false);
332         dayInfo.markLunarDay = child->GetBool("markLunarDay", false);
333         SetOffDays(dayInfo);
334         hasLunarInfo = hasLunarInfo && !dayInfo.lunarDay.empty();
335         result.days.push_back(dayInfo);
336         child = child->GetNext();
337     }
338     LOGI("current month  is %{public}s, has lunar info %{public}d", calendarMonth.ToString().c_str(), hasLunarInfo);
339     dayOfMonthCache_[indexOfContainer] = result;
340     NotifyDataChanged(result, indexOfContainer);
341 }
342 
SetOffDays(CalendarDay & dayInfo)343 void CalendarDataAdapter::SetOffDays(CalendarDay& dayInfo)
344 {
345     auto weekday = Date::CalculateWeekDay(dayInfo.month.year, dayInfo.month.month + 1, dayInfo.day);
346     std::vector<std::string> days;
347     StringUtils::StringSplitter(offDays_, ',', days);
348     bool setOffDay = true;
349     for (const auto& day : days) {
350         auto num = StringUtils::StringToInt(day);
351         if (num < 0 || num > 6) {
352             setOffDay = false;
353             break;
354         }
355         if (num == weekday) {
356             dayInfo.weekend = true;
357             return;
358         }
359     }
360     if (!setOffDay) {
361         if (weekday == 5 || weekday == 6) { // set default weekend
362             dayInfo.weekend = true;
363         }
364     }
365 }
366 
RequestDataInWatch(const CalendarDataRequest & request)367 void CalendarDataAdapter::RequestDataInWatch(const CalendarDataRequest& request)
368 {
369     auto context = pipelineContext_.Upgrade();
370     auto weak = AceType::WeakClaim(this);
371     if (!context) {
372         return;
373     }
374     if (firstLoad_) {
375         context->SetBuildAfterCallback([weak, request]() {
376             auto dataAdapter = weak.Upgrade();
377             if (!dataAdapter) {
378                 LOGW("dataAdapter is null");
379                 return;
380             }
381             CalendarDaysOfMonth result;
382             dataAdapter->FillMonthData(request, result);
383             dataAdapter->NotifyDataChanged(result, request.indexOfContainer);
384             dataAdapter->firstLoad_ = false;
385             dataAdapter->RequestNextData();
386         });
387     } else {
388         context->GetTaskExecutor()->PostTask(
389             [weak, request]() {
390                 auto dataAdapter = weak.Upgrade();
391                 if (!dataAdapter) {
392                     LOGW("dataAdapter is null");
393                     return;
394                 }
395                 CalendarDaysOfMonth result;
396                 dataAdapter->FillMonthData(request, result);
397                 dataAdapter->NotifyDataChanged(result, request.indexOfContainer);
398                 dataAdapter->RequestNextData();
399             },
400             TaskExecutor::TaskType::UI, "ArkUICalendarRequestDataInWatch",
401             TaskExecutor::GetPriorityTypeWithCheck(PriorityType::IMMEDIATE));
402     }
403 }
404 
FillMonthData(const CalendarDataRequest & request,CalendarDaysOfMonth & result)405 void CalendarDataAdapter::FillMonthData(const CalendarDataRequest& request, CalendarDaysOfMonth& result)
406 {
407     auto currentMonth = request.month;
408     result.month = currentMonth;
409     int32_t index = 0;
410     // fill last month data
411     FillPreMonthData(currentMonth, request.indexOfContainer, index, result);
412     // fill current month data
413     FillCurrentMonthData(currentMonth, request.indexOfContainer, index, result);
414 
415     result.lastDayIndex = index - 1;
416     // fill next month data
417     FillNextMonthData(currentMonth, request.indexOfContainer, index, result);
418 }
419 
FillPreMonthData(const CalendarMonth & currentMonth,int32_t indexOfContainer,int32_t & index,CalendarDaysOfMonth & result)420 void CalendarDataAdapter::FillPreMonthData(
421     const CalendarMonth& currentMonth, int32_t indexOfContainer, int32_t& index, CalendarDaysOfMonth& result)
422 {
423     static const int32_t DAYS_PER_WEEK = 7;
424     auto lastMonth = CalendarMonth::GetLastMonth(currentMonth);
425     auto currentWeekDay = Date::CalculateWeekDay(currentMonth.year, currentMonth.month + 1, 1);
426     if (currentWeekDay != startDayOfWeek_) {
427         auto lastMonthDays = Date::DayOfMonth(lastMonth.year, lastMonth.month + 1);
428         auto countDays = currentWeekDay - startDayOfWeek_ >= 0 ? currentWeekDay - startDayOfWeek_
429                                                                : currentWeekDay - startDayOfWeek_ + DAYS_PER_WEEK;
430         auto startDay = lastMonthDays - countDays + 1;
431         for (; index < countDays; ++index) {
432             CalendarDay dayInfo;
433             dayInfo.day = startDay++;
434             dayInfo.index = index;
435             dayInfo.month.month = lastMonth.month;
436             dayInfo.month.year = lastMonth.year;
437             SetOffDays(dayInfo);
438             calendarCache_[indexOfContainer].push_back(dayInfo.ToString());
439             result.days.emplace_back(dayInfo);
440         }
441     }
442 }
443 
FillCurrentMonthData(const CalendarMonth & currentMonth,int32_t indexOfContainer,int32_t & index,CalendarDaysOfMonth & result)444 void CalendarDataAdapter::FillCurrentMonthData(
445     const CalendarMonth& currentMonth, int32_t indexOfContainer, int32_t& index, CalendarDaysOfMonth& result)
446 {
447     result.firstDayIndex = index;
448     auto currentMonthDays = Date::DayOfMonth(currentMonth.year, currentMonth.month + 1);
449     for (int32_t i = 0; i < currentMonthDays; i++) {
450         CalendarDay dayInfo;
451         dayInfo.day = i + 1;
452         dayInfo.index = index;
453         dayInfo.month.month = currentMonth.month;
454         dayInfo.month.year = currentMonth.year;
455         SetOffDays(dayInfo);
456         // Mark today.
457         dayInfo.today = dayInfo.month == today_.month && dayInfo.day == today_.day;
458         if (dayInfo.today) {
459             result.today = dayInfo.index;
460         }
461         calendarCache_[indexOfContainer].push_back(dayInfo.ToString());
462         result.days.emplace_back(dayInfo);
463         ++index;
464     }
465 }
466 
FillNextMonthData(const CalendarMonth & currentMonth,int32_t indexOfContainer,int32_t & index,CalendarDaysOfMonth & result)467 void CalendarDataAdapter::FillNextMonthData(
468     const CalendarMonth& currentMonth, int32_t indexOfContainer, int32_t& index, CalendarDaysOfMonth& result)
469 {
470     auto nextMonth = CalendarMonth::GetNextMonth(currentMonth);
471     // The number of days the month view needs to be displayed
472     const int32_t daysOfCalendar = result.days.size() <= 35 ? 35 : 42;
473     int32_t indexOfNextMonth = 0;
474     while ((int32_t)result.days.size() < daysOfCalendar) {
475         CalendarDay dayInfo;
476         dayInfo.day = ++indexOfNextMonth;
477         dayInfo.index = index++;
478         dayInfo.month.month = nextMonth.month;
479         dayInfo.month.year = nextMonth.year;
480         SetOffDays(dayInfo);
481         calendarCache_[indexOfContainer].push_back(dayInfo.ToString());
482         result.days.emplace_back(dayInfo);
483     }
484 }
485 
ParseCalendarData(std::queue<ObtainedMonth> && months)486 void CalendarDataAdapter::ParseCalendarData(std::queue<ObtainedMonth>&& months)
487 {
488     while (!months.empty()) {
489         auto month = months.front();
490         CalendarMonth calendarMonth;
491         calendarMonth.year = month.year;
492         calendarMonth.month = month.month;
493 
494         int32_t indexOfContainer = -1;
495         for (const auto& index : indexMap_) {
496             if (index.second == calendarMonth) {
497                 indexOfContainer = index.first;
498             }
499         }
500 
501         if (hasMoved_  && indexOfContainer != requestNextIndex_) {
502             months.pop();
503             continue;
504         }
505 
506         static const int32_t miniIndex = 0;
507         static const int32_t maxIndex = 2;
508         if (indexOfContainer < miniIndex || indexOfContainer > maxIndex) {
509             months.pop();
510             continue;
511         }
512         CalendarDaysOfMonth result;
513         result.month = calendarMonth;
514         result.days = month.days;
515         result.firstDayIndex = month.firstDayIndex;
516         dayOfMonthCache_[indexOfContainer] = result;
517         NotifyDataChanged(result, indexOfContainer);
518         months.pop();
519     }
520 
521     if (hasMoved_) {
522         for (const auto& listener : allListeners_) {
523             listener->OnSwiperMove();
524         }
525         hasMoved_ = false;
526     }
527 }
528 
NotifyDataChanged(const CalendarDaysOfMonth & data,int32_t indexOfContainer)529 void CalendarDataAdapter::NotifyDataChanged(const CalendarDaysOfMonth& data, int32_t indexOfContainer)
530 {
531     int32_t listenersSize = static_cast<int32_t>(allListeners_.size());
532     if (indexOfContainer >= 0 && indexOfContainer < listenersSize) {
533         auto& listener = allListeners_[indexOfContainer];
534         listener->OnDataChanged(data);
535     }
536 }
537 
538 } // namespace OHOS::Ace
539