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/video/resource/player.h"
17
18 #include <iomanip>
19 #include <sstream>
20
21 #include "base/log/log.h"
22
23 namespace OHOS::Ace {
24
25 const char PLAYER_PARAM_WIDTH[] = "width";
26 const char PLAYER_PARAM_HEIGHT[] = "height";
27 const char PLAYER_PARAM_DURATION[] = "duration";
28 const char PLAYER_PARAM_CURRENTPOS[] = "currentpos";
29 const char PLAYER_PARAM_ISPLAYING[] = "isplaying";
30 const char PLAYER_PARAM_SRC[] = "src";
31 const char PLAYER_PARAM_ISAUTOPLAY[] = "autoplay";
32 const char PLAYER_PARAM_ISMUTE[] = "mute";
33 const char PLAYER_PARAM_TEXTURE[] = "texture";
34 const char PLAYER_PARAM_NEEDFRESHFORCE[] = "needRefreshForce";
35 const char PLAYER_PARAM_LOOP[] = "loop";
36 const char PLAYER_PARAM_SEEKMODE[] = "seekMode";
37
38 const char PLAYER_METHOD_INIT[] = "init";
39 const char PLAYER_METHOD_GETPOSITION[] = "getposition";
40 const char PLAYER_METHOD_START[] = "start";
41 const char PLAYER_METHOD_PAUSE[] = "pause";
42 const char PLAYER_METHOD_SEEKTO[] = "seekto";
43 const char PLAYER_METHOD_STOP[] = "stop";
44 const char PLAYER_METHOD_SETVOLUME[] = "setvolume";
45 const char PLAYER_METHOD_FULLSCREEN[] = "fullscreen";
46 const char PLAYER_METHOD_ENABLE_LOOPING[] = "enablelooping";
47 const char PLAYER_METHOD_SETSPEED[] = "setspeed";
48 const char PLAYER_METHOD_SETDIRECTION[] = "setdirection";
49 const char PLAYER_METHOD_SETLANDSCAPE[] = "setlandscape";
50 const char PLAYER_METHOD_SETSURFACE[] = "setsurface";
51 const char PLAYER_METHOD_UPDATERESOURCE[] = "updateresource";
52
53 const char PLAYER_EVENT_PREPARED[] = "prepared";
54 const char PLAYER_EVENT_COMPLETION[] = "completion";
55 const char PLAYER_EVENT_SEEKCOMPLETE[] = "seekcomplete";
56 const char PLAYER_EVENT_ONPLAYSTATUS[] = "onplaystatus";
57 const char PLAYER_EVENT_ONGETCURRENTTIME[] = "ongetcurrenttime";
58
59 const char PLAYER_ERROR_CODE_CREATEFAIL[] = "error_video_000001";
60 const char PLAYER_ERROR_MSG_CREATEFAIL[] = "Create player failed.";
61 const char PLAYER_ERROR_CODE_FILEINVALID[] = "error_video_000002";
62 const char PLAYER_ERROR_MSG_FILEINVALID[] = "File invalid.";
63
Create(const std::function<void (int64_t)> & onCreate)64 void Player::Create(const std::function<void(int64_t)>& onCreate)
65 {
66 scheduler_ = SchedulerBuilder::Build(
67 [weak = WeakClaim(this)](uint64_t timestamp) {
68 auto player = weak.Upgrade();
69 if (player) {
70 player->OnTick(timestamp);
71 }
72 },
73 context_);
74 auto context = context_.Upgrade();
75 if (!context) {
76 LOGE("fail to get context to create player");
77 return;
78 }
79
80 auto platformTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::PLATFORM);
81
82 platformTaskExecutor.PostTask([weak = WeakClaim(this), onCreate] {
83 auto player = weak.Upgrade();
84 if (player) {
85 player->CreatePlayer(onCreate);
86 }
87 });
88 }
89
CreatePlayer(const std::function<void (int64_t)> & onCreate)90 void Player::CreatePlayer(const std::function<void(int64_t)>& onCreate)
91 {
92 auto context = context_.Upgrade();
93 if (!context) {
94 return;
95 }
96 auto resRegister = context->GetPlatformResRegister();
97 if (resRegister == nullptr) {
98 return;
99 }
100 std::stringstream paramStream;
101 paramStream << PLAYER_PARAM_TEXTURE << PARAM_EQUALS << textureId_;
102 std::string param = paramStream.str();
103 id_ = resRegister->CreateResource(type_, param);
104 if (id_ == INVALID_ID) {
105 if (onError_) {
106 onError_(PLAYER_ERROR_CODE_CREATEFAIL, PLAYER_ERROR_MSG_CREATEFAIL);
107 }
108 return;
109 }
110 hash_ = MakeResourceHash();
111 resRegister->RegisterEvent(
112 MakeEventHash(PLAYER_EVENT_PREPARED), [weak = WeakClaim(this)](const std::string& param) {
113 auto player = weak.Upgrade();
114 if (player) {
115 player->OnPrepared(param);
116 }
117 });
118 resRegister->RegisterEvent(
119 MakeEventHash(PLAYER_EVENT_COMPLETION), [weak = WeakClaim(this)](const std::string& param) {
120 auto player = weak.Upgrade();
121 if (player) {
122 player->OnCompletion(param);
123 }
124 });
125 resRegister->RegisterEvent(
126 MakeEventHash(PLAYER_EVENT_SEEKCOMPLETE), [weak = WeakClaim(this)](const std::string& param) {
127 auto player = weak.Upgrade();
128 if (player) {
129 player->OnSeekComplete(param);
130 }
131 });
132 resRegister->RegisterEvent(
133 MakeEventHash(PLAYER_EVENT_ONPLAYSTATUS), [weak = WeakClaim(this)](const std::string& param) {
134 auto player = weak.Upgrade();
135 if (player) {
136 player->OnPlayStatus(param);
137 }
138 });
139 resRegister->RegisterEvent(
140 MakeEventHash(PLAYER_EVENT_ONGETCURRENTTIME), [weak = WeakClaim(this)](const std::string& param) {
141 auto player = weak.Upgrade();
142 if (player) {
143 player->OnTimeGetted(param);
144 }
145 });
146 if (onCreate) {
147 onCreate(id_);
148 }
149 InitPlay();
150 }
151
InitPlay()152 void Player::InitPlay()
153 {
154 std::stringstream paramStream;
155 paramStream << PLAYER_PARAM_SRC << PARAM_EQUALS << src_ << PARAM_AND << PLAYER_PARAM_ISMUTE << PARAM_EQUALS
156 << (isMute_ ? 1 : 0) << PARAM_AND << PLAYER_PARAM_ISAUTOPLAY << PARAM_EQUALS << (isAutoPlay_ ? 1 : 0);
157
158 std::string param = paramStream.str();
159 CallResRegisterMethod(MakeMethodHash(PLAYER_METHOD_INIT), param, [weak = WeakClaim(this)](std::string& result) {
160 auto player = weak.Upgrade();
161 if (player) {
162 if (!player->IsResultSuccess(result)) {
163 player->OnError(PLAYER_ERROR_CODE_FILEINVALID, PLAYER_ERROR_MSG_FILEINVALID);
164 }
165 }
166 });
167
168 isInit_ = true;
169 }
170
SetSource(const std::string & src)171 void Player::SetSource(const std::string& src)
172 {
173 src_ = src;
174 if (isInit_) {
175 std::stringstream paramStream;
176 paramStream << PLAYER_PARAM_SRC << PARAM_EQUALS << src_;
177 std::string param = paramStream.str();
178 CallResRegisterMethod(MakeMethodHash(PLAYER_METHOD_UPDATERESOURCE), param,
179 [weak = WeakClaim(this)](std::string& result) {
180 auto player = weak.Upgrade();
181 if (player) {
182 if (!player->IsResultSuccess(result)) {
183 player->OnError(PLAYER_ERROR_CODE_FILEINVALID, PLAYER_ERROR_MSG_FILEINVALID);
184 }
185 }
186 });
187 }
188 }
189
SetSurfaceId(int64_t id)190 void Player::SetSurfaceId(int64_t id)
191 {
192 textureId_ = id;
193 std::stringstream paramStream;
194 paramStream << PARAM_VALUE << PARAM_EQUALS << textureId_;
195 std::string param = paramStream.str();
196 CallResRegisterMethod(MakeMethodHash(PLAYER_METHOD_SETSURFACE), param);
197 }
198
OnPrepared(const std::string & param)199 void Player::OnPrepared(const std::string& param)
200 {
201 currentPos_ = 0;
202 width_ = GetIntParam(param, PLAYER_PARAM_WIDTH);
203 height_ = GetIntParam(param, PLAYER_PARAM_HEIGHT);
204 duration_ = GetIntParam(param, PLAYER_PARAM_DURATION) / 1000;
205 isPlaying_ = GetIntParam(param, PLAYER_PARAM_ISPLAYING) == 1;
206 isNeedFreshForce_ = GetIntParam(param, PLAYER_PARAM_NEEDFRESHFORCE) == 1;
207 isPrepared_ = true;
208 if (width_ == 0 || height_ == 0 || duration_ == 0) {
209 if (onError_) {
210 onError_(PLAYER_ERROR_CODE_FILEINVALID, PLAYER_ERROR_MSG_FILEINVALID);
211 }
212 return;
213 }
214 getCurrentPosMethod_ = MakeMethodHash(PLAYER_METHOD_GETPOSITION);
215 playMethod_ = MakeMethodHash(PLAYER_METHOD_START);
216 pauseMethod_ = MakeMethodHash(PLAYER_METHOD_PAUSE);
217 seekMethod_ = MakeMethodHash(PLAYER_METHOD_SEEKTO);
218 stopMethod_ = MakeMethodHash(PLAYER_METHOD_STOP);
219 setVolumeMethod_ = MakeMethodHash(PLAYER_METHOD_SETVOLUME);
220 fullscreenMethod_ = MakeMethodHash(PLAYER_METHOD_FULLSCREEN);
221 enableloopingMethod_ = MakeMethodHash(PLAYER_METHOD_ENABLE_LOOPING);
222 setSpeedMethod_ = MakeMethodHash(PLAYER_METHOD_SETSPEED);
223 setDirectionMethod_ = MakeMethodHash(PLAYER_METHOD_SETDIRECTION);
224 setLandsacpeMethod_ = MakeMethodHash(PLAYER_METHOD_SETLANDSCAPE);
225
226 if (isPlaying_) {
227 SetTickActive(true);
228 }
229
230 for (const auto& listener : onPreparedListener_) {
231 listener(width_, height_, isPlaying_, duration_, currentPos_, true);
232 }
233 }
234
OnCompletion(const std::string & param)235 void Player::OnCompletion(const std::string& param)
236 {
237 isPlaying_ = false;
238 currentPos_ = duration_;
239 SetTickActive(isPlaying_);
240 for (const auto& listener : onCompletionListener_) {
241 listener();
242 }
243 }
244
OnSeekComplete(const std::string & param)245 void Player::OnSeekComplete(const std::string& param)
246 {
247 currentPos_ = GetIntParam(param, PLAYER_PARAM_CURRENTPOS);
248 if (!onCurrentPosListener_.empty()) {
249 onCurrentPosListener_.back()(currentPos_);
250 }
251 }
252
OnPlayStatus(const std::string & param)253 void Player::OnPlayStatus(const std::string& param)
254 {
255 isPlaying_ = GetIntParam(param, PLAYER_PARAM_ISPLAYING) == 1;
256 SetTickActive(isPlaying_);
257 for (const auto& listener : onPlayStatusListener_) {
258 listener(isPlaying_);
259 }
260 }
261
SetTickActive(bool isActive)262 void Player::SetTickActive(bool isActive)
263 {
264 auto context = context_.Upgrade();
265 if (!context) {
266 LOGE("fail to get context to set tick active");
267 return;
268 }
269
270 auto uiTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
271 uiTaskExecutor.PostSyncTask([this, isActive] {
272 if (scheduler_) {
273 if (isActive) {
274 scheduler_->Start();
275 } else {
276 scheduler_->Stop();
277 }
278 }
279 });
280 }
281
OnTick(uint64_t timeStamp)282 void Player::OnTick(uint64_t timeStamp)
283 {
284 timeStamp_ += timeStamp;
285 if (isNeedFreshForce_) {
286 if (!onRefreshRenderListener_.empty()) {
287 onRefreshRenderListener_.back()();
288 }
289 }
290 if (timeStamp_ > FREAUENCY_GET_CURRENT_TIME) {
291 GetCurrentTime();
292 timeStamp_ -= FREAUENCY_GET_CURRENT_TIME;
293 }
294 }
295
GetCurrentTime()296 void Player::GetCurrentTime()
297 {
298 CallResRegisterMethod(getCurrentPosMethod_, PARAM_NONE);
299 }
300
OnTimeGetted(const std::string & result)301 void Player::OnTimeGetted(const std::string& result)
302 {
303 currentPos_ = GetIntParam(result, PLAYER_PARAM_CURRENTPOS);
304 if (!onCurrentPosListener_.empty()) {
305 onCurrentPosListener_.back()(currentPos_);
306 }
307 }
308
Start()309 void Player::Start()
310 {
311 CallResRegisterMethod(playMethod_, PARAM_NONE, [weak = WeakClaim(this)](std::string& result) {
312 auto player = weak.Upgrade();
313 if (player) {
314 player->OnStarted();
315 }
316 });
317 }
318
OnStarted()319 void Player::OnStarted()
320 {
321 isPlaying_ = true;
322 SetTickActive(isPlaying_);
323
324 for (const auto& listener : onPlayStatusListener_) {
325 listener(isPlaying_);
326 }
327 }
328
Pause()329 void Player::Pause()
330 {
331 CallResRegisterMethod(pauseMethod_, PARAM_NONE, [weak = WeakClaim(this)](std::string& result) {
332 auto player = weak.Upgrade();
333 if (player) {
334 player->OnPaused();
335 }
336 });
337 }
338
OnPaused()339 void Player::OnPaused()
340 {
341 isPlaying_ = false;
342 SetTickActive(isPlaying_);
343
344 for (const auto& listener : onPlayStatusListener_) {
345 listener(isPlaying_);
346 }
347 }
348
Stop()349 void Player::Stop()
350 {
351 if (scheduler_) {
352 scheduler_->Stop();
353 }
354
355 CallResRegisterMethod(stopMethod_, PARAM_NONE, [weak = WeakClaim(this)](std::string& result) {
356 LOGI("callback play OnStop.");
357 auto player = weak.Upgrade();
358 if (player) {
359 player->OnStop();
360 }
361 });
362 }
363
OnStop()364 void Player::OnStop()
365 {
366 isPlaying_ = false;
367 currentPos_ = 0;
368 SetTickActive(isPlaying_);
369
370 for (const auto& listener : onPlayStatusListener_) {
371 listener(isPlaying_);
372 }
373 }
374
UnregisterEvent()375 void Player::UnregisterEvent()
376 {
377 auto context = context_.Upgrade();
378 if (!context) {
379 LOGE("fail to get context");
380 return;
381 }
382 auto resRegister = context->GetPlatformResRegister();
383 if (resRegister == nullptr) {
384 return;
385 }
386 resRegister->UnregisterEvent(MakeEventHash(PLAYER_EVENT_PREPARED));
387 resRegister->UnregisterEvent(MakeEventHash(PLAYER_EVENT_COMPLETION));
388 resRegister->UnregisterEvent(MakeEventHash(PLAYER_EVENT_SEEKCOMPLETE));
389 resRegister->UnregisterEvent(MakeEventHash(PLAYER_EVENT_ONPLAYSTATUS));
390 }
391
EnterFullScreen()392 void Player::EnterFullScreen()
393 {
394 CallResRegisterMethod(fullscreenMethod_, PARAM_NONE);
395 }
396
SeekTo(uint32_t pos)397 void Player::SeekTo(uint32_t pos)
398 {
399 std::stringstream paramStream;
400 paramStream << PARAM_VALUE << PARAM_EQUALS << pos;
401
402 std::string param = paramStream.str();
403 CallResRegisterMethod(seekMethod_, param);
404 }
405
SeekTo(uint32_t pos,uint32_t mode)406 void Player::SeekTo(uint32_t pos, uint32_t mode)
407 {
408 std::stringstream paramStream;
409 paramStream << PARAM_VALUE << PARAM_EQUALS << pos << PARAM_AND <<
410 PLAYER_PARAM_SEEKMODE << PARAM_EQUALS << mode;
411
412 std::string param = paramStream.str();
413 CallResRegisterMethod(seekMethod_, param);
414 }
415
SetVolume(float volume)416 void Player::SetVolume(float volume)
417 {
418 std::stringstream paramStream;
419 paramStream << PARAM_VALUE << PARAM_EQUALS << volume;
420
421 std::string param = paramStream.str();
422 CallResRegisterMethod(setVolumeMethod_, param);
423 }
424
AddPreparedListener(PreparedListener && listener)425 void Player::AddPreparedListener(PreparedListener&& listener)
426 {
427 auto context = context_.Upgrade();
428 if (!context) {
429 LOGE("fail to get context");
430 return;
431 }
432
433 auto platformTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::PLATFORM);
434 platformTaskExecutor.PostTask([weak = WeakClaim(this), listener = std::move(listener)]() mutable {
435 auto player = weak.Upgrade();
436 if (player) {
437 player->OnAddPreparedListener(std::move(listener));
438 }
439 });
440 }
441
OnAddPreparedListener(PreparedListener && listener)442 void Player::OnAddPreparedListener(PreparedListener&& listener)
443 {
444 onPreparedListener_.push_back(std::move(listener));
445 if (isPrepared_) {
446 onPreparedListener_.back()(width_, height_, isPlaying_, duration_, currentPos_, false);
447 }
448 }
449
AddPlayStatusListener(PlayStatusListener && listener)450 void Player::AddPlayStatusListener(PlayStatusListener&& listener)
451 {
452 auto context = context_.Upgrade();
453 if (!context) {
454 LOGE("fail to get context");
455 return;
456 }
457
458 auto platformTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::PLATFORM);
459 platformTaskExecutor.PostTask([weak = WeakClaim(this), listener = std::move(listener)]() mutable {
460 auto player = weak.Upgrade();
461 if (player) {
462 player->OnAddPlayStatusListener(std::move(listener));
463 }
464 });
465 }
466
OnAddPlayStatusListener(PlayStatusListener && listener)467 void Player::OnAddPlayStatusListener(PlayStatusListener&& listener)
468 {
469 onPlayStatusListener_.push_back(std::move(listener));
470 }
471
AddCurrentPosListener(CurrentPosListener && listener)472 void Player::AddCurrentPosListener(CurrentPosListener&& listener)
473 {
474 auto context = context_.Upgrade();
475 if (!context) {
476 LOGE("fail to get context");
477 return;
478 }
479
480 auto platformTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::PLATFORM);
481 platformTaskExecutor.PostTask([weak = WeakClaim(this), listener = std::move(listener)]() mutable {
482 auto player = weak.Upgrade();
483 if (player) {
484 player->OnAddCurrentPosListener(std::move(listener));
485 }
486 });
487 }
488
OnAddCurrentPosListener(CurrentPosListener && listener)489 void Player::OnAddCurrentPosListener(CurrentPosListener&& listener)
490 {
491 onCurrentPosListener_.push_back(std::move(listener));
492 }
493
AddCompletionListener(CompletionListener && listener)494 void Player::AddCompletionListener(CompletionListener&& listener)
495 {
496 auto context = context_.Upgrade();
497 if (!context) {
498 LOGE("fail to get context");
499 return;
500 }
501
502 auto platformTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::PLATFORM);
503 platformTaskExecutor.PostTask([weak = WeakClaim(this), listener = std::move(listener)]() mutable {
504 auto player = weak.Upgrade();
505 if (player) {
506 player->OnAddCompletionListener(std::move(listener));
507 }
508 });
509 }
510
OnAddCompletionListener(CompletionListener && listener)511 void Player::OnAddCompletionListener(CompletionListener&& listener)
512 {
513 onCompletionListener_.push_back(std::move(listener));
514 }
515
PopListener()516 void Player::PopListener()
517 {
518 onRefreshRenderListener_.pop_back();
519 auto context = context_.Upgrade();
520 if (!context) {
521 LOGE("fail to get context");
522 return;
523 }
524
525 auto platformTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::PLATFORM);
526 platformTaskExecutor.PostTask([weak = WeakClaim(this)] {
527 auto player = weak.Upgrade();
528 if (player) {
529 player->OnPopListener();
530 }
531 });
532 }
533
AddRefreshRenderListener(RefreshRenderListener && listener)534 void Player::AddRefreshRenderListener(RefreshRenderListener&& listener)
535 {
536 onRefreshRenderListener_.push_back(std::move(listener));
537 }
538
EnableLooping(bool loop)539 void Player::EnableLooping(bool loop)
540 {
541 std::stringstream paramStream;
542 paramStream << PLAYER_PARAM_LOOP << PARAM_EQUALS << (loop ? "1" : "0");
543
544 std::string param = paramStream.str();
545 CallResRegisterMethod(enableloopingMethod_, param);
546 }
547
SetSpeed(float speed)548 void Player::SetSpeed(float speed)
549 {
550 std::stringstream paramStream;
551 paramStream << PARAM_VALUE << PARAM_EQUALS << speed;
552
553 std::string param = paramStream.str();
554 CallResRegisterMethod(setSpeedMethod_, param);
555 }
556
SetDirection(std::string & direction)557 void Player::SetDirection(std::string& direction)
558 {
559 std::stringstream paramStream;
560 paramStream << PARAM_VALUE << PARAM_EQUALS << direction;
561
562 std::string param = paramStream.str();
563 CallResRegisterMethod(setDirectionMethod_, param);
564 }
565
SetLandscape()566 void Player::SetLandscape()
567 {
568 CallResRegisterMethod(setLandsacpeMethod_, PARAM_NONE);
569 }
570
OnPopListener()571 void Player::OnPopListener()
572 {
573 onPreparedListener_.pop_back();
574 onPlayStatusListener_.pop_back();
575 onCurrentPosListener_.pop_back();
576 onCompletionListener_.pop_back();
577 if (!onCurrentPosListener_.empty()) {
578 onCurrentPosListener_.back()(currentPos_);
579 }
580 }
581
SetFullScreenChange(bool isFullScreen)582 void Player::SetFullScreenChange(bool isFullScreen)
583 {
584 std::stringstream paramStream;
585 paramStream << PARAM_VALUE << PARAM_EQUALS << (isFullScreen ? "1" : "0");
586
587 std::string param = paramStream.str();
588 CallResRegisterMethod(fullscreenMethod_, param);
589 }
590
Release(const std::function<void (bool)> & onRelease)591 void Player::Release(const std::function<void(bool)>& onRelease)
592 {
593 // The destructor will be executed at platform thread, so destroy scheduler at here.
594 if (scheduler_) {
595 scheduler_.Reset();
596 }
597
598 auto context = context_.Upgrade();
599 if (!context) {
600 LOGE("fail to get context");
601 return;
602 }
603 auto platformTaskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::PLATFORM);
604 if (platformTaskExecutor.IsRunOnCurrentThread()) {
605 UnregisterEvent();
606 } else {
607 platformTaskExecutor.PostTask([weak = WeakClaim(this)] {
608 auto player = weak.Upgrade();
609 if (player) {
610 player->UnregisterEvent();
611 }
612 });
613 }
614
615 Resource::Release(onRelease);
616
617 isInit_ = false;
618 }
619 } // namespace OHOS::Ace