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 <stack>
19 #include <string>
20 
21 #include "base/log/dump_log.h"
22 #include "base/i18n/localization.h"
23 #include "base/utils/utils.h"
24 #include "core/components/common/layout/constants.h"
25 #include "core/components_ng/pattern/text/text_layout_property.h"
26 #include "core/components_ng/pattern/text/text_pattern.h"
27 #include "core/components_ng/pattern/texttimer/text_timer_layout_property.h"
28 #include "core/components_ng/property/property.h"
29 #include "core/pipeline_ng/pipeline_context.h"
30 
31 namespace OHOS::Ace::NG {
32 namespace {
33 constexpr int32_t TOTAL_MINUTE_OF_HOUR = 60;
34 constexpr int32_t TOTAL_SECONDS_OF_HOUR = 60 * 60;
35 constexpr int32_t SECONDS_OF_MILLISECOND = 1000;
36 constexpr int32_t SECONDS_OF_HUNDRED = 100;
37 constexpr int32_t SECONDS_OF_TEN = 10;
38 constexpr int32_t DEFAULT_SCALE = 1;
39 constexpr double DEFAULT_COUNT = 60000.0;
40 const std::string DEFAULT_FORMAT = "HH:mm:ss.SS";
41 } // namespace
42 
TextTimerPattern()43 TextTimerPattern::TextTimerPattern()
44 {
45     textTimerController_ = MakeRefPtr<TextTimerController>();
46 }
47 
FireChangeEvent()48 void TextTimerPattern::FireChangeEvent()
49 {
50     auto textTimerEventHub = GetEventHub<TextTimerEventHub>();
51     CHECK_NULL_VOID(textTimerEventHub);
52     auto utcTime = GetFormatDuration(GetMilliseconds());
53     auto elapsedTime = GetFormatDuration(elapsedTime_);
54     if (elapsedTime - lastElapsedTime_ >= 1) {
55         textTimerEventHub->FireChangeEvent(utcTime, elapsedTime);
56         lastElapsedTime_ = elapsedTime;
57     }
58 }
59 
InitTextTimerController()60 void TextTimerPattern::InitTextTimerController()
61 {
62     if (textTimerController_) {
63         if (textTimerController_->HasInitialized()) {
64             return;
65         }
66         auto weak = AceType::WeakClaim(this);
67         textTimerController_->OnStart([weak]() {
68             auto timerRender = weak.Upgrade();
69             if (timerRender) {
70                 timerRender->HandleStart();
71             }
72         });
73         textTimerController_->OnPause([weak]() {
74             auto timerRender = weak.Upgrade();
75             if (timerRender) {
76                 timerRender->HandlePause();
77             }
78         });
79         textTimerController_->OnReset([weak]() {
80             auto timerRender = weak.Upgrade();
81             if (timerRender) {
82                 timerRender->HandleReset();
83             }
84         });
85     }
86 }
87 
InitTimerDisplay()88 void TextTimerPattern::InitTimerDisplay()
89 {
90     auto host = GetHost();
91     CHECK_NULL_VOID(host);
92     if (!scheduler_) {
93         resetCount_ = false;
94         auto weak = AceType::WeakClaim(this);
95         auto&& callback = [weak](uint64_t duration) {
96             auto timer = weak.Upgrade();
97             if (timer) {
98                 timer->Tick(duration);
99             }
100         };
101         auto context = host->GetContextRefPtr();
102         CHECK_NULL_VOID(context);
103         scheduler_ = SchedulerBuilder::Build(callback, context);
104         auto count = isCountDown_ ? inputCount_ : 0;
105         UpdateTextTimer(static_cast<uint32_t>(count));
106         return;
107     }
108     if (resetCount_) {
109         resetCount_ = false;
110         HandleReset();
111     }
112 }
113 
Tick(uint64_t duration)114 void TextTimerPattern::Tick(uint64_t duration)
115 {
116     elapsedTime_ += duration;
117     FireChangeEvent();
118 
119     auto tmpValue = static_cast<double>(elapsedTime_);
120     if (isCountDown_) {
121         auto elapsedTime = GetMillisecondsDuration(GetFormatDuration(elapsedTime_));
122         tmpValue =
123             (inputCount_ >= static_cast<double>(elapsedTime_)) ? (inputCount_ - static_cast<double>(elapsedTime)) : 0;
124     }
125     if (isCountDown_ && tmpValue <= 0) {
126         UpdateTextTimer(0);
127         HandlePause();
128         return;
129     }
130 
131     UpdateTextTimer(static_cast<uint32_t>(tmpValue));
132 }
133 
UpdateTextLayoutProperty(RefPtr<TextTimerLayoutProperty> & layoutProperty,RefPtr<TextLayoutProperty> & textLayoutProperty)134 void TextTimerPattern::UpdateTextLayoutProperty(
135     RefPtr<TextTimerLayoutProperty>& layoutProperty, RefPtr<TextLayoutProperty>& textLayoutProperty)
136 {
137     if (layoutProperty->GetFontSize().has_value()) {
138         textLayoutProperty->UpdateFontSize(layoutProperty->GetFontSize().value());
139     }
140     if (layoutProperty->GetFontWeight().has_value()) {
141         textLayoutProperty->UpdateFontWeight(layoutProperty->GetFontWeight().value());
142     }
143     if (layoutProperty->GetTextColor().has_value()) {
144         textLayoutProperty->UpdateTextColor(layoutProperty->GetTextColor().value());
145     }
146     if (layoutProperty->GetFontFamily().has_value()) {
147         textLayoutProperty->UpdateFontFamily(layoutProperty->GetFontFamily().value());
148     }
149     if (layoutProperty->GetItalicFontStyle().has_value()) {
150         textLayoutProperty->UpdateItalicFontStyle(layoutProperty->GetItalicFontStyle().value());
151     }
152     if (layoutProperty->GetTextShadow().has_value()) {
153         textLayoutProperty->UpdateTextShadow(layoutProperty->GetTextShadow().value());
154     }
155 }
156 
OnAttachToFrameNode()157 void TextTimerPattern::OnAttachToFrameNode()
158 {
159     auto host = GetHost();
160     CHECK_NULL_VOID(host);
161     auto textTimerProperty = host->GetLayoutProperty<TextTimerLayoutProperty>();
162     CHECK_NULL_VOID(textTimerProperty);
163     textTimerProperty->UpdateAlignment(Alignment::CENTER_LEFT);
164 }
165 
OnModifyDone()166 void TextTimerPattern::OnModifyDone()
167 {
168     auto host = GetHost();
169     CHECK_NULL_VOID(host);
170 
171     if (!textNode_) {
172         textNode_ = GetTextNode();
173     }
174     CHECK_NULL_VOID(textNode_);
175     auto textLayoutProperty = textNode_->GetLayoutProperty<TextLayoutProperty>();
176     CHECK_NULL_VOID(textLayoutProperty);
177     textLayoutProperty->UpdateTextOverflow(TextOverflow::NONE);
178     if (textLayoutProperty->GetPositionProperty()) {
179         textLayoutProperty->UpdateAlignment(
180             textLayoutProperty->GetPositionProperty()->GetAlignment().value_or(Alignment::CENTER));
181     } else {
182         textLayoutProperty->UpdateAlignment(Alignment::CENTER);
183     }
184     auto textTimerProperty = host->GetLayoutProperty<TextTimerLayoutProperty>();
185     CHECK_NULL_VOID(textTimerProperty);
186     textLayoutProperty->UpdateTextOverflow(TextOverflow::NONE);
187     UpdateTextLayoutProperty(textTimerProperty, textLayoutProperty);
188     auto textContext = textNode_->GetRenderContext();
189     CHECK_NULL_VOID(textContext);
190     textContext->SetClipToFrame(false);
191     textContext->UpdateClipEdge(false);
192     isCountDown_ = GetIsCountDown();
193     inputCount_ = GetInputCount();
194 
195     InitTextTimerController();
196     InitTimerDisplay();
197     textNode_->MarkModifyDone();
198     RegisterVisibleAreaChangeCallback();
199     FireBuilder();
200 }
201 
RegisterVisibleAreaChangeCallback()202 void TextTimerPattern::RegisterVisibleAreaChangeCallback()
203 {
204     if (isRegisteredAreaCallback_) {
205         return;
206     }
207     isRegisteredAreaCallback_ = true;
208     auto host = GetHost();
209     CHECK_NULL_VOID(host);
210     auto pipeline = host->GetContext();
211     CHECK_NULL_VOID(pipeline);
212     auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
213         auto pattern = weak.Upgrade();
214         CHECK_NULL_VOID(pattern);
215         pattern->OnVisibleAreaChange(visible);
216     };
217     std::vector<double> ratioList = {0.0};
218     pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false);
219 }
220 
OnVisibleAreaChange(bool visible)221 void TextTimerPattern::OnVisibleAreaChange(bool visible)
222 {
223     auto host = GetHost();
224     CHECK_NULL_VOID(host);
225     CHECK_NULL_VOID(textNode_);
226     if (visible) {
227         auto childNode = DynamicCast<FrameNode>(host->GetFirstChild());
228         if (!childNode) {
229             host->AddChild(textNode_);
230             host->RebuildRenderContextTree();
231         }
232     } else {
233         host->RemoveChild(textNode_);
234         host->RebuildRenderContextTree();
235     }
236 }
237 
UpdateTextTimer(uint32_t elapsedTime)238 void TextTimerPattern::UpdateTextTimer(uint32_t elapsedTime)
239 {
240     if (UseContentModifier()) {
241         FireBuilder();
242         return;
243     }
244     auto host = GetHost();
245     CHECK_NULL_VOID(host);
246     CHECK_NULL_VOID(textNode_);
247     auto textLayoutProperty = textNode_->GetLayoutProperty<TextLayoutProperty>();
248     CHECK_NULL_VOID(textLayoutProperty);
249 
250     // format time text.
251     std::string timerText = Localization::GetInstance()->FormatDuration(elapsedTime, GetFormat());
252     if (timerText.empty()) {
253         timerText = Localization::GetInstance()->FormatDuration(elapsedTime, DEFAULT_FORMAT);
254     }
255     textLayoutProperty->UpdateContent(timerText); // Update time text.
256     if (CheckMeasureFlag(textLayoutProperty->GetPropertyChangeFlag()) ||
257         CheckLayoutFlag(textLayoutProperty->GetPropertyChangeFlag())) {
258         textNode_->MarkModifyDone();
259         textNode_->MarkDirtyNode();
260     }
261 }
262 
GetFormat() const263 std::string TextTimerPattern::GetFormat() const
264 {
265     auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
266     CHECK_NULL_RETURN(textTimerLayoutProperty, DEFAULT_FORMAT);
267     return textTimerLayoutProperty->GetFormat().value_or(DEFAULT_FORMAT);
268 }
269 
GetIsCountDown() const270 bool TextTimerPattern::GetIsCountDown() const
271 {
272     auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
273     CHECK_NULL_RETURN(textTimerLayoutProperty, false);
274     return textTimerLayoutProperty->GetIsCountDown().value_or(false);
275 }
276 
GetInputCount() const277 double TextTimerPattern::GetInputCount() const
278 {
279     auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
280     CHECK_NULL_RETURN(textTimerLayoutProperty, DEFAULT_COUNT);
281     return textTimerLayoutProperty->GetInputCount().value_or(DEFAULT_COUNT);
282 }
283 
HandleStart()284 void TextTimerPattern::HandleStart()
285 {
286     if (scheduler_ && !scheduler_->IsActive()) {
287         scheduler_->Start();
288     }
289 }
290 
HandlePause()291 void TextTimerPattern::HandlePause()
292 {
293     if (scheduler_ && scheduler_->IsActive()) {
294         scheduler_->Stop();
295     }
296 }
297 
HandleReset()298 void TextTimerPattern::HandleReset()
299 {
300     if (scheduler_ && scheduler_->IsActive()) {
301         scheduler_->Stop();
302     }
303     elapsedTime_ = 0;
304     lastElapsedTime_ = 0;
305     auto count = isCountDown_ ? inputCount_ : 0;
306     UpdateTextTimer(static_cast<uint32_t>(count));
307 }
308 
GetTextNode()309 RefPtr<FrameNode> TextTimerPattern::GetTextNode()
310 {
311     auto host = GetHost();
312     CHECK_NULL_RETURN(host, nullptr);
313     auto textNode = AceType::DynamicCast<FrameNode>(host->GetLastChild());
314     CHECK_NULL_RETURN(textNode, nullptr);
315     if (textNode->GetTag() != V2::TEXT_ETS_TAG) {
316         return nullptr;
317     }
318     return textNode;
319 }
320 
GetFormatDuration(uint64_t duration) const321 uint64_t TextTimerPattern::GetFormatDuration(uint64_t duration) const
322 {
323     return duration / GetMillisecondsDuration(DEFAULT_SCALE);
324 }
325 
GetMillisecondsDuration(uint64_t duration) const326 uint64_t TextTimerPattern::GetMillisecondsDuration(uint64_t duration) const
327 {
328     auto host = GetHost();
329     CHECK_NULL_RETURN(host, duration);
330     auto layoutProperty = host->GetLayoutProperty<TextTimerLayoutProperty>();
331     CHECK_NULL_RETURN(layoutProperty, duration);
332     auto format = layoutProperty->GetFormat().value_or(DEFAULT_FORMAT);
333     if (format.find("SSS") != std::string::npos) {
334         return duration;
335     } else if (format.find("SS") != std::string::npos) {
336         return duration * SECONDS_OF_TEN;
337     } else if (format.find('S') != std::string::npos) {
338         return duration * SECONDS_OF_HUNDRED;
339     } else if (format.find('s') != std::string::npos) {
340         duration = duration * SECONDS_OF_MILLISECOND;
341     } else if (format.find('m') != std::string::npos) {
342         duration = duration * (SECONDS_OF_MILLISECOND * TOTAL_MINUTE_OF_HOUR);
343     } else if (format.find('H') != std::string::npos) {
344         duration = duration * (SECONDS_OF_MILLISECOND * TOTAL_SECONDS_OF_HOUR);
345     }
346     return duration;
347 }
348 
ResetCount()349 void TextTimerPattern::ResetCount()
350 {
351     resetCount_ = true;
352 }
353 
FireBuilder()354 void TextTimerPattern::FireBuilder()
355 {
356     if (!makeFunc_.has_value()) {
357         return;
358     }
359     auto host = GetHost();
360     CHECK_NULL_VOID(host);
361     host->RemoveChildAtIndex(0);
362     contentModifierNode_ = BuildContentModifierNode();
363     CHECK_NULL_VOID(contentModifierNode_);
364     host->AddChild(contentModifierNode_, 0);
365     host->MarkNeedFrameFlushDirty(PROPERTY_UPDATE_MEASURE);
366 }
367 
BuildContentModifierNode()368 RefPtr<FrameNode> TextTimerPattern::BuildContentModifierNode()
369 {
370     if (!makeFunc_.has_value()) {
371         return nullptr;
372     }
373     auto host = GetHost();
374     CHECK_NULL_RETURN(host, nullptr);
375     auto eventHub = host->GetEventHub<TextTimerEventHub>();
376     CHECK_NULL_RETURN(eventHub, nullptr);
377     auto enabled = eventHub->IsEnabled();
378     auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
379     CHECK_NULL_RETURN(textTimerLayoutProperty, nullptr);
380     auto count = textTimerLayoutProperty->GetInputCount().value_or(DEFAULT_COUNT);
381     auto isCountDown = textTimerLayoutProperty->GetIsCountDown().value_or(false);
382     auto started = scheduler_ && scheduler_->IsActive();
383     auto elapsedTime = GetFormatDuration(elapsedTime_);
384     TextTimerConfiguration textTimerConfiguration(count, isCountDown, started, elapsedTime, enabled);
385     return (makeFunc_.value())(textTimerConfiguration);
386 }
387 
DumpInfo()388 void TextTimerPattern::DumpInfo()
389 {
390     auto textTimerLayoutProperty = GetLayoutProperty<TextTimerLayoutProperty>();
391     CHECK_NULL_VOID(textTimerLayoutProperty);
392     auto isCountDown = textTimerLayoutProperty->GetIsCountDown().value_or(false);
393     isCountDown ? DumpLog::GetInstance().AddDesc("isCountDown: true") :
394         DumpLog::GetInstance().AddDesc("isCountDown: false");
395     auto format = textTimerLayoutProperty->GetFormat().value_or(DEFAULT_FORMAT);
396     DumpLog::GetInstance().AddDesc("format: ", format);
397     auto elapsedTime = GetFormatDuration(elapsedTime_);
398     DumpLog::GetInstance().AddDesc("elapsedTime: ", elapsedTime);
399 }
400 } // namespace OHOS::Ace::NG
401