1 /*
2 * Copyright (c) 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_ng/pattern/texttimer/text_timer_pattern.h"
17
18 #include <string>
19
20 #include "base/log/dump_log.h"
21 #include "base/i18n/localization.h"
22 #include "core/components_ng/pattern/text/text_layout_property.h"
23 #include "core/components_ng/pattern/text/text_pattern.h"
24
25 namespace OHOS::Ace::NG {
26 namespace {
27 constexpr int32_t TOTAL_MINUTE_OF_HOUR = 60;
28 constexpr int32_t TOTAL_SECONDS_OF_HOUR = 60 * 60;
29 constexpr int32_t SECONDS_OF_MILLISECOND = 1000;
30 constexpr int32_t SECONDS_OF_HUNDRED = 100;
31 constexpr int32_t SECONDS_OF_TEN = 10;
32 constexpr int32_t DEFAULT_SCALE = 1;
33 constexpr double DEFAULT_COUNT = 60000.0;
34 const std::string DEFAULT_FORMAT = "HH:mm:ss.SS";
35 } // namespace
36
TextTimerPattern()37 TextTimerPattern::TextTimerPattern()
38 {
39 textTimerController_ = MakeRefPtr<TextTimerController>();
40 }
41
FireChangeEvent()42 void TextTimerPattern::FireChangeEvent()
43 {
44 auto textTimerEventHub = GetOrCreateEventHub<TextTimerEventHub>();
45 CHECK_NULL_VOID(textTimerEventHub);
46 auto utcTime = GetFormatDuration(GetMilliseconds());
47 auto elapsedTime = GetFormatDuration(elapsedTime_);
48 if (elapsedTime - lastElapsedTime_ >= 1) {
49 textTimerEventHub->FireChangeEvent(utcTime, elapsedTime);
50 lastElapsedTime_ = elapsedTime;
51 }
52 }
53
InitTextTimerController()54 void TextTimerPattern::InitTextTimerController()
55 {
56 if (textTimerController_) {
57 if (textTimerController_->HasInitialized()) {
58 return;
59 }
60 auto weak = AceType::WeakClaim(this);
61 textTimerController_->OnStart([weak]() {
62 auto timerRender = weak.Upgrade();
63 if (timerRender) {
64 timerRender->HandleStart();
65 }
66 });
67 textTimerController_->OnPause([weak]() {
68 auto timerRender = weak.Upgrade();
69 if (timerRender) {
70 timerRender->HandlePause();
71 }
72 });
73 textTimerController_->OnReset([weak]() {
74 auto timerRender = weak.Upgrade();
75 if (timerRender) {
76 timerRender->HandleReset();
77 }
78 });
79 }
80 }
81
InitTimerDisplay()82 void TextTimerPattern::InitTimerDisplay()
83 {
84 auto host = GetHost();
85 CHECK_NULL_VOID(host);
86 if (!scheduler_) {
87 resetCount_ = false;
88 auto weak = AceType::WeakClaim(this);
89 auto&& callback = [weak](uint64_t duration) {
90 auto timer = weak.Upgrade();
91 if (timer) {
92 timer->Tick(duration);
93 }
94 };
95 auto context = host->GetContextRefPtr();
96 CHECK_NULL_VOID(context);
97 scheduler_ = SchedulerBuilder::Build(callback, context);
98 auto count = isCountDown_ ? inputCount_ : 0;
99 UpdateTextTimer(static_cast<uint32_t>(count));
100 return;
101 }
102 if (resetCount_) {
103 resetCount_ = false;
104 HandleReset();
105 }
106 }
107
Tick(uint64_t duration)108 void TextTimerPattern::Tick(uint64_t duration)
109 {
110 elapsedTime_ += duration;
111 FireChangeEvent();
112
113 auto tmpValue = static_cast<double>(elapsedTime_);
114 if (isCountDown_) {
115 auto elapsedTime = GetMillisecondsDuration(GetFormatDuration(elapsedTime_));
116 tmpValue =
117 (inputCount_ >= static_cast<double>(elapsedTime_)) ? (inputCount_ - static_cast<double>(elapsedTime)) : 0;
118 }
119 if (isCountDown_ && tmpValue <= 0) {
120 UpdateTextTimer(0);
121 HandlePause();
122 return;
123 }
124
125 UpdateTextTimer(static_cast<uint32_t>(tmpValue));
126 }
127
UpdateTextLayoutProperty(RefPtr<TextTimerLayoutProperty> & layoutProperty,RefPtr<TextLayoutProperty> & textLayoutProperty)128 void TextTimerPattern::UpdateTextLayoutProperty(
129 RefPtr<TextTimerLayoutProperty>& layoutProperty, RefPtr<TextLayoutProperty>& textLayoutProperty)
130 {
131 if (layoutProperty->GetFontSize().has_value()) {
132 textLayoutProperty->UpdateFontSize(layoutProperty->GetFontSize().value());
133 }
134 if (layoutProperty->GetFontWeight().has_value()) {
135 textLayoutProperty->UpdateFontWeight(layoutProperty->GetFontWeight().value());
136 }
137 if (layoutProperty->GetTextColor().has_value()) {
138 textLayoutProperty->UpdateTextColor(layoutProperty->GetTextColor().value());
139 }
140 if (layoutProperty->GetFontFamily().has_value()) {
141 textLayoutProperty->UpdateFontFamily(layoutProperty->GetFontFamily().value());
142 }
143 if (layoutProperty->GetItalicFontStyle().has_value()) {
144 textLayoutProperty->UpdateItalicFontStyle(layoutProperty->GetItalicFontStyle().value());
145 }
146 if (layoutProperty->GetTextShadow().has_value()) {
147 textLayoutProperty->UpdateTextShadow(layoutProperty->GetTextShadow().value());
148 }
149 }
150
OnAttachToFrameNode()151 void TextTimerPattern::OnAttachToFrameNode()
152 {
153 auto host = GetHost();
154 CHECK_NULL_VOID(host);
155 auto textTimerProperty = host->GetLayoutProperty<TextTimerLayoutProperty>();
156 CHECK_NULL_VOID(textTimerProperty);
157 textTimerProperty->UpdateAlignment(Alignment::CENTER_LEFT);
158 }
159
OnModifyDone()160 void TextTimerPattern::OnModifyDone()
161 {
162 auto host = GetHost();
163 CHECK_NULL_VOID(host);
164
165 if (host->GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
166 Pattern::OnModifyDone();
167 }
168
169 if (!textNode_) {
170 textNode_ = GetTextNode();
171 }
172 CHECK_NULL_VOID(textNode_);
173 auto textLayoutProperty = textNode_->GetLayoutProperty<TextLayoutProperty>();
174 CHECK_NULL_VOID(textLayoutProperty);
175 textLayoutProperty->UpdateTextOverflow(TextOverflow::NONE);
176 if (textLayoutProperty->GetPositionProperty()) {
177 textLayoutProperty->UpdateAlignment(
178 textLayoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER));
179 } else {
180 textLayoutProperty->UpdateAlignment(Alignment::CENTER);
181 }
182 auto textTimerProperty = host->GetLayoutProperty<TextTimerLayoutProperty>();
183 CHECK_NULL_VOID(textTimerProperty);
184 textLayoutProperty->UpdateTextOverflow(TextOverflow::NONE);
185 UpdateTextLayoutProperty(textTimerProperty, textLayoutProperty);
186 auto textContext = textNode_->GetRenderContext();
187 CHECK_NULL_VOID(textContext);
188 textContext->SetClipToFrame(false);
189 textContext->UpdateClipEdge(false);
190 isCountDown_ = GetIsCountDown();
191 inputCount_ = GetInputCount();
192
193 InitTextTimerController();
194 InitTimerDisplay();
195 textNode_->MarkModifyDone();
196 RegisterVisibleAreaChangeCallback();
197 FireBuilder();
198 }
199
RegisterVisibleAreaChangeCallback()200 void TextTimerPattern::RegisterVisibleAreaChangeCallback()
201 {
202 if (isRegisteredAreaCallback_) {
203 return;
204 }
205 isRegisteredAreaCallback_ = true;
206 auto host = GetHost();
207 CHECK_NULL_VOID(host);
208 auto pipeline = host->GetContext();
209 CHECK_NULL_VOID(pipeline);
210 auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
211 auto pattern = weak.Upgrade();
212 CHECK_NULL_VOID(pattern);
213 pattern->OnVisibleAreaChange(visible);
214 };
215 std::vector<double> ratioList = {0.0};
216 pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
217 }
218
OnVisibleAreaChange(bool visible)219 void TextTimerPattern::OnVisibleAreaChange(bool visible)
220 {
221 auto host = GetHost();
222 CHECK_NULL_VOID(host);
223 CHECK_NULL_VOID(textNode_);
224 if (visible) {
225 auto childNode = DynamicCast<FrameNode>(host->GetFirstChild());
226 if (!childNode) {
227 host->AddChild(textNode_);
228 host->RebuildRenderContextTree();
229 if (SystemProperties::ConfigChangePerform()) {
230 host->MarkModifyDone();
231 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
232 }
233 }
234 } else {
235 host->RemoveChild(textNode_);
236 host->RebuildRenderContextTree();
237 }
238 }
239
UpdateTextTimer(uint32_t elapsedTime)240 void TextTimerPattern::UpdateTextTimer(uint32_t elapsedTime)
241 {
242 if (UseContentModifier()) {
243 FireBuilder();
244 return;
245 }
246 auto host = GetHost();
247 CHECK_NULL_VOID(host);
248 CHECK_NULL_VOID(textNode_);
249 auto textLayoutProperty = textNode_->GetLayoutProperty<TextLayoutProperty>();
250 CHECK_NULL_VOID(textLayoutProperty);
251
252 // format time text.
253 std::string timerText = Localization::GetInstance()->FormatDuration(elapsedTime, GetFormat());
254 if (timerText.empty()) {
255 timerText = Localization::GetInstance()->FormatDuration(elapsedTime, DEFAULT_FORMAT);
256 }
257 textLayoutProperty->UpdateContent(timerText); // Update time text.
258 if (CheckMeasureFlag(textLayoutProperty->GetPropertyChangeFlag()) ||
259 CheckLayoutFlag(textLayoutProperty->GetPropertyChangeFlag())) {
260 textNode_->MarkModifyDone();
261 textNode_->MarkDirtyNode();
262 }
263 }
264
GetFormat() const265 std::string TextTimerPattern::GetFormat() const
266 {
267 auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
268 CHECK_NULL_RETURN(textTimerLayoutProperty, DEFAULT_FORMAT);
269 return textTimerLayoutProperty->GetFormat().value_or(DEFAULT_FORMAT);
270 }
271
GetIsCountDown() const272 bool TextTimerPattern::GetIsCountDown() const
273 {
274 auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
275 CHECK_NULL_RETURN(textTimerLayoutProperty, false);
276 return textTimerLayoutProperty->GetIsCountDown().value_or(false);
277 }
278
GetInputCount() const279 double TextTimerPattern::GetInputCount() const
280 {
281 auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
282 CHECK_NULL_RETURN(textTimerLayoutProperty, DEFAULT_COUNT);
283 return textTimerLayoutProperty->GetInputCount().value_or(DEFAULT_COUNT);
284 }
285
HandleStart()286 void TextTimerPattern::HandleStart()
287 {
288 if (scheduler_ && !scheduler_->IsActive()) {
289 scheduler_->Start();
290 }
291 }
292
HandlePause()293 void TextTimerPattern::HandlePause()
294 {
295 if (scheduler_ && scheduler_->IsActive()) {
296 scheduler_->Stop();
297 }
298 }
299
HandleReset()300 void TextTimerPattern::HandleReset()
301 {
302 if (scheduler_ && scheduler_->IsActive()) {
303 scheduler_->Stop();
304 }
305 elapsedTime_ = 0;
306 lastElapsedTime_ = 0;
307 auto count = isCountDown_ ? inputCount_ : 0;
308 UpdateTextTimer(static_cast<uint32_t>(count));
309 }
310
GetTextNode()311 RefPtr<FrameNode> TextTimerPattern::GetTextNode()
312 {
313 auto host = GetHost();
314 CHECK_NULL_RETURN(host, nullptr);
315 auto textNode = AceType::DynamicCast<FrameNode>(host->GetLastChild());
316 CHECK_NULL_RETURN(textNode, nullptr);
317 if (textNode->GetTag() != V2::TEXT_ETS_TAG) {
318 return nullptr;
319 }
320 return textNode;
321 }
322
GetFormatDuration(uint64_t duration) const323 uint64_t TextTimerPattern::GetFormatDuration(uint64_t duration) const
324 {
325 return duration / GetMillisecondsDuration(DEFAULT_SCALE);
326 }
327
GetMillisecondsDuration(uint64_t duration) const328 uint64_t TextTimerPattern::GetMillisecondsDuration(uint64_t duration) const
329 {
330 auto host = GetHost();
331 CHECK_NULL_RETURN(host, duration);
332 auto layoutProperty = host->GetLayoutProperty<TextTimerLayoutProperty>();
333 CHECK_NULL_RETURN(layoutProperty, duration);
334 auto format = layoutProperty->GetFormat().value_or(DEFAULT_FORMAT);
335 if (format.find("SSS") != std::string::npos) {
336 return duration;
337 } else if (format.find("SS") != std::string::npos) {
338 return duration * SECONDS_OF_TEN;
339 } else if (format.find('S') != std::string::npos) {
340 return duration * SECONDS_OF_HUNDRED;
341 } else if (format.find('s') != std::string::npos) {
342 duration = duration * SECONDS_OF_MILLISECOND;
343 } else if (format.find('m') != std::string::npos) {
344 duration = duration * (SECONDS_OF_MILLISECOND * TOTAL_MINUTE_OF_HOUR);
345 } else if (format.find('H') != std::string::npos) {
346 duration = duration * (SECONDS_OF_MILLISECOND * TOTAL_SECONDS_OF_HOUR);
347 }
348 return duration;
349 }
350
ResetCount()351 void TextTimerPattern::ResetCount()
352 {
353 resetCount_ = true;
354 }
355
FireBuilder()356 void TextTimerPattern::FireBuilder()
357 {
358 if (!makeFunc_.has_value()) {
359 return;
360 }
361 auto host = GetHost();
362 CHECK_NULL_VOID(host);
363 host->RemoveChildAtIndex(0);
364 contentModifierNode_ = BuildContentModifierNode();
365 CHECK_NULL_VOID(contentModifierNode_);
366 host->AddChild(contentModifierNode_, 0);
367 host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
368 }
369
BuildContentModifierNode()370 RefPtr<FrameNode> TextTimerPattern::BuildContentModifierNode()
371 {
372 if (!makeFunc_.has_value()) {
373 return nullptr;
374 }
375 auto host = GetHost();
376 CHECK_NULL_RETURN(host, nullptr);
377 auto eventHub = host->GetOrCreateEventHub<TextTimerEventHub>();
378 CHECK_NULL_RETURN(eventHub, nullptr);
379 auto enabled = eventHub->IsEnabled();
380 auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
381 CHECK_NULL_RETURN(textTimerLayoutProperty, nullptr);
382 auto count = textTimerLayoutProperty->GetInputCount().value_or(DEFAULT_COUNT);
383 auto isCountDown = textTimerLayoutProperty->GetIsCountDown().value_or(false);
384 auto started = scheduler_ && scheduler_->IsActive();
385 auto elapsedTime = GetFormatDuration(elapsedTime_);
386 TextTimerConfiguration textTimerConfiguration(count, isCountDown, started, elapsedTime, enabled);
387 return (makeFunc_.value())(textTimerConfiguration);
388 }
389
DumpInfo()390 void TextTimerPattern::DumpInfo()
391 {
392 auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
393 CHECK_NULL_VOID(textTimerLayoutProperty);
394 auto isCountDown = textTimerLayoutProperty->GetIsCountDown().value_or(false);
395 isCountDown ? DumpLog::GetInstance().AddDesc("isCountDown: true") :
396 DumpLog::GetInstance().AddDesc("isCountDown: false");
397 auto format = textTimerLayoutProperty->GetFormat().value_or(DEFAULT_FORMAT);
398 DumpLog::GetInstance().AddDesc("format: ", format);
399 auto elapsedTime = GetFormatDuration(elapsedTime_);
400 DumpLog::GetInstance().AddDesc("elapsedTime: ", elapsedTime);
401 }
402
DumpInfo(std::unique_ptr<JsonValue> & json)403 void TextTimerPattern::DumpInfo(std::unique_ptr<JsonValue>& json)
404 {
405 auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
406 CHECK_NULL_VOID(textTimerLayoutProperty);
407 json->Put("isCountDown", textTimerLayoutProperty->GetIsCountDown().value_or(false));
408 json->Put("format", textTimerLayoutProperty->GetFormat().value_or(DEFAULT_FORMAT).c_str());
409 json->Put("elapsedTime", std::to_string(GetFormatDuration(elapsedTime_)).c_str());
410 }
411
UpdateTextColor(const Color & color,bool isFirstLoad)412 void TextTimerPattern::UpdateTextColor(const Color& color, bool isFirstLoad)
413 {
414 auto host = GetHost();
415 CHECK_NULL_VOID(host);
416 auto layoutProperty = host->GetLayoutProperty<TextTimerLayoutProperty>();
417 CHECK_NULL_VOID(layoutProperty);
418 auto renderContext = host->GetRenderContext();
419 CHECK_NULL_VOID(renderContext);
420 auto pipelineContext = host->GetContext();
421 CHECK_NULL_VOID(pipelineContext);
422 if (isFirstLoad || pipelineContext->IsSystmColorChange()) {
423 layoutProperty->UpdateTextColor(color);
424 renderContext->UpdateForegroundColor(color);
425 renderContext->ResetForegroundColorStrategy();
426 renderContext->UpdateForegroundColorFlag(true);
427 }
428 }
429
UpdateFontWeight(const FontWeight & value,bool isFirstLoad)430 void TextTimerPattern::UpdateFontWeight(const FontWeight& value, bool isFirstLoad)
431 {
432 auto host = GetHost();
433 CHECK_NULL_VOID(host);
434 auto layoutProperty = host->GetLayoutProperty<TextTimerLayoutProperty>();
435 CHECK_NULL_VOID(layoutProperty);
436 auto pipelineContext = host->GetContext();
437 CHECK_NULL_VOID(pipelineContext);
438 if (isFirstLoad || pipelineContext->IsSystmColorChange()) {
439 layoutProperty->UpdateFontWeight(value);
440 }
441 }
442
UpdateFontSize(const Dimension & value,bool isFirstLoad)443 void TextTimerPattern::UpdateFontSize(const Dimension& value, bool isFirstLoad)
444 {
445 auto host = GetHost();
446 CHECK_NULL_VOID(host);
447 auto layoutProperty = host->GetLayoutProperty<TextTimerLayoutProperty>();
448 CHECK_NULL_VOID(layoutProperty);
449 auto pipelineContext = host->GetContext();
450 CHECK_NULL_VOID(pipelineContext);
451 if (isFirstLoad || pipelineContext->IsSystmColorChange()) {
452 layoutProperty->UpdateFontSize(value);
453 }
454 }
455
OnColorModeChange(uint32_t colorMode)456 void TextTimerPattern::OnColorModeChange(uint32_t colorMode)
457 {
458 Pattern::OnColorModeChange(colorMode);
459 auto host = GetHost();
460 CHECK_NULL_VOID(host);
461 auto pipelineContext = host->GetContext();
462 CHECK_NULL_VOID(pipelineContext);
463 if (host->GetRerenderable()) {
464 host->MarkModifyDone();
465 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
466 }
467 }
468
UpdateFontFamily(const std::vector<std::string> & fontFamilies,bool isFirstLoad)469 void TextTimerPattern::UpdateFontFamily(const std::vector<std::string>& fontFamilies, bool isFirstLoad)
470 {
471 auto host = GetHost();
472 CHECK_NULL_VOID(host);
473
474 auto layoutProperty = host->GetLayoutProperty<TextTimerLayoutProperty>();
475 CHECK_NULL_VOID(layoutProperty);
476 auto pipelineContext = host->GetContext();
477 CHECK_NULL_VOID(pipelineContext);
478 if (isFirstLoad || pipelineContext->IsSystmColorChange()) {
479 layoutProperty->UpdateFontFamily(fontFamilies);
480 }
481 }
482
OnColorConfigurationUpdate()483 void TextTimerPattern::OnColorConfigurationUpdate()
484 {
485 if (!SystemProperties::ConfigChangePerform()) {
486 return;
487 }
488
489 auto host = GetHost();
490 CHECK_NULL_VOID(host);
491 auto pipeline = host->GetContextWithCheck();
492 CHECK_NULL_VOID(pipeline);
493
494 auto theme = pipeline->GetTheme<TextTheme>();
495 CHECK_NULL_VOID(theme);
496
497 auto pops = host->GetLayoutProperty<TextTimerLayoutProperty>();
498 CHECK_NULL_VOID(pops);
499
500 if (!pops->HasTextColorSetByUser() || (pops->HasTextColorSetByUser() && !pops->GetTextColorSetByUserValue())) {
501 UpdateTextColor(theme->GetTextStyle().GetTextColor(), false);
502 }
503 if (!pops->GetTextFontSizeSetByUserValue(false)) {
504 UpdateFontSize(theme->GetTextStyle().GetFontSize(), false);
505 }
506 if (!pops->GetTextFontWeightSetByUserValue(false)) {
507 UpdateFontWeight(theme->GetTextStyle().GetFontWeight(), false);
508 }
509 if (!pops->GetTextFontFamilySetByUserValue(false)) {
510 UpdateFontFamily(theme->GetTextStyle().GetFontFamilies(), false);
511 }
512 }
513 } // namespace OHOS::Ace::NG
514