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