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