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