1/* 2 * Copyright (c) 2023 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 "ui_observer.h" 17 18#include "bridge/common/utils/engine_helper.h" 19 20 21namespace OHOS::Ace::Napi { 22std::list<std::shared_ptr<UIObserverListener>> UIObserver::unspecifiedNavigationListeners_; 23std::unordered_map<std::string, std::list<std::shared_ptr<UIObserverListener>>> 24 UIObserver::specifiedCNavigationListeners_; 25 26std::list<std::shared_ptr<UIObserverListener>> UIObserver::scrollEventListeners_; 27std::unordered_map<std::string, std::list<std::shared_ptr<UIObserverListener>>> 28 UIObserver::specifiedScrollEventListeners_; 29 30std::unordered_map<napi_ref, std::list<std::shared_ptr<UIObserverListener>>> 31 UIObserver::abilityContextRouterPageListeners_; 32std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>> 33 UIObserver::specifiedRouterPageListeners_; 34std::unordered_map<napi_ref, NG::AbilityContextInfo> UIObserver::infosForRouterPage_; 35 36std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>> 37 UIObserver::specifiedDensityListeners_; 38std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>> UIObserver::specifiedDrawListeners_; 39std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>> UIObserver::specifiedLayoutListeners_; 40 41std::unordered_map<napi_ref, UIObserver::NavIdAndListenersMap> UIObserver::abilityUIContextNavDesSwitchListeners_; 42std::unordered_map<int32_t, UIObserver::NavIdAndListenersMap> UIObserver::uiContextNavDesSwitchListeners_; 43std::unordered_map<napi_ref, NG::AbilityContextInfo> UIObserver::infosForNavDesSwitch_; 44 45std::unordered_map<napi_ref, std::list<std::shared_ptr<UIObserverListener>>> 46 UIObserver::abilityContextWillClickListeners_; 47std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>> 48 UIObserver::specifiedWillClickListeners_; 49std::unordered_map<napi_ref, NG::AbilityContextInfo> UIObserver::willClickInfos_; 50 51std::unordered_map<napi_ref, std::list<std::shared_ptr<UIObserverListener>>> 52 UIObserver::abilityContextDidClickListeners_; 53std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>> 54 UIObserver::specifiedDidClickListeners_; 55std::unordered_map<napi_ref, NG::AbilityContextInfo> UIObserver::didClickInfos_; 56 57std::list<std::shared_ptr<UIObserverListener>> UIObserver::tabContentStateListeners_; 58std::unordered_map<std::string, std::list<std::shared_ptr<UIObserverListener>>> 59 UIObserver::specifiedTabContentStateListeners_; 60 61// UIObserver.on(type: "navDestinationUpdate", callback) 62// register a global listener without options 63void UIObserver::RegisterNavigationCallback(const std::shared_ptr<UIObserverListener>& listener) 64{ 65 if (std::find(unspecifiedNavigationListeners_.begin(), unspecifiedNavigationListeners_.end(), listener) != 66 unspecifiedNavigationListeners_.end()) { 67 return; 68 } 69 unspecifiedNavigationListeners_.emplace_back(listener); 70} 71 72// UIObserver.on(type: "navDestinationUpdate", options, callback) 73// register a listener on a specified Navigation 74void UIObserver::RegisterNavigationCallback( 75 std::string navigationId, const std::shared_ptr<UIObserverListener>& listener) 76{ 77 auto iter = specifiedCNavigationListeners_.find(navigationId); 78 if (iter == specifiedCNavigationListeners_.end()) { 79 specifiedCNavigationListeners_.emplace( 80 navigationId, std::list<std::shared_ptr<UIObserverListener>>({ listener })); 81 return; 82 } 83 auto& holder = iter->second; 84 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 85 return; 86 } 87 holder.emplace_back(listener); 88} 89 90// UIObserver.off(type: "navDestinationUpdate", callback) 91void UIObserver::UnRegisterNavigationCallback(napi_value cb) 92{ 93 if (cb == nullptr) { 94 unspecifiedNavigationListeners_.clear(); 95 return; 96 } 97 98 unspecifiedNavigationListeners_.erase( 99 std::remove_if( 100 unspecifiedNavigationListeners_.begin(), 101 unspecifiedNavigationListeners_.end(), 102 [cb](const std::shared_ptr<UIObserverListener>& registeredListener) { 103 return registeredListener->NapiEqual(cb); 104 } 105 ), 106 unspecifiedNavigationListeners_.end() 107 ); 108} 109 110// UIObserver.off(type: "navDestinationUpdate", options, callback) 111void UIObserver::UnRegisterNavigationCallback(std::string navigationId, napi_value cb) 112{ 113 auto iter = specifiedCNavigationListeners_.find(navigationId); 114 if (iter == specifiedCNavigationListeners_.end()) { 115 return; 116 } 117 auto& holder = iter->second; 118 if (cb == nullptr) { 119 holder.clear(); 120 return; 121 } 122 holder.erase( 123 std::remove_if( 124 holder.begin(), 125 holder.end(), 126 [cb](const std::shared_ptr<UIObserverListener>& registeredListener) { 127 return registeredListener->NapiEqual(cb); 128 } 129 ), 130 holder.end() 131 ); 132} 133 134void UIObserver::HandleNavigationStateChange(const NG::NavDestinationInfo& info) 135{ 136 auto unspecifiedHolder = unspecifiedNavigationListeners_; 137 for (const auto& listener : unspecifiedHolder) { 138 listener->OnNavigationStateChange(info); 139 } 140 auto iter = specifiedCNavigationListeners_.find(info.navigationId); 141 if (iter == specifiedCNavigationListeners_.end()) { 142 return; 143 } 144 145 auto holder = iter->second; 146 147 for (const auto& listener : holder) { 148 listener->OnNavigationStateChange(info); 149 } 150} 151 152// UIObserver.on(type: "scrollEvent", callback) 153// register a global listener without options 154void UIObserver::RegisterScrollEventCallback(const std::shared_ptr<UIObserverListener>& listener) 155{ 156 if (std::find(scrollEventListeners_.begin(), scrollEventListeners_.end(), listener) != 157 scrollEventListeners_.end()) { 158 return; 159 } 160 scrollEventListeners_.emplace_back(listener); 161} 162 163// UIObserver.on(type: "scrollEvent", options, callback) 164// register a listener on a specified scrollEvent 165void UIObserver::RegisterScrollEventCallback( 166 const std::string& id, const std::shared_ptr<UIObserverListener>& listener) 167{ 168 auto iter = specifiedScrollEventListeners_.find(id); 169 if (iter == specifiedScrollEventListeners_.end()) { 170 specifiedScrollEventListeners_.emplace(id, std::list<std::shared_ptr<UIObserverListener>>({ listener })); 171 return; 172 } 173 auto& holder = iter->second; 174 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 175 return; 176 } 177 holder.emplace_back(listener); 178} 179 180// UIObserver.off(type: "scrollEvent", callback) 181void UIObserver::UnRegisterScrollEventCallback(napi_value cb) 182{ 183 if (cb == nullptr) { 184 scrollEventListeners_.clear(); 185 return; 186 } 187 188 scrollEventListeners_.erase( 189 std::remove_if( 190 scrollEventListeners_.begin(), 191 scrollEventListeners_.end(), 192 [cb](const std::shared_ptr<UIObserverListener>& registeredListener) { 193 return registeredListener->NapiEqual(cb); 194 }), 195 scrollEventListeners_.end() 196 ); 197} 198 199// UIObserver.off(type: "scrollEvent", options, callback) 200void UIObserver::UnRegisterScrollEventCallback(const std::string& id, napi_value cb) 201{ 202 auto iter = specifiedScrollEventListeners_.find(id); 203 if (iter == specifiedScrollEventListeners_.end()) { 204 return; 205 } 206 auto& holder = iter->second; 207 if (cb == nullptr) { 208 holder.clear(); 209 return; 210 } 211 holder.erase( 212 std::remove_if( 213 holder.begin(), 214 holder.end(), 215 [cb](const std::shared_ptr<UIObserverListener>& registeredListener) { 216 return registeredListener->NapiEqual(cb); 217 }), 218 holder.end() 219 ); 220} 221 222void UIObserver::HandleScrollEventStateChange(const std::string& id, int32_t uniqueId, 223 NG::ScrollEventType eventType, float offset) 224{ 225 for (const auto& listener : scrollEventListeners_) { 226 listener->OnScrollEventStateChange(id, uniqueId, eventType, offset); 227 } 228 229 auto iter = specifiedScrollEventListeners_.find(id); 230 if (iter == specifiedScrollEventListeners_.end()) { 231 return; 232 } 233 234 auto& holder = iter->second; 235 236 for (const auto& listener : holder) { 237 listener->OnScrollEventStateChange(id, uniqueId, eventType, offset); 238 } 239} 240 241// UIObserver.on(type: "routerPageUpdate", UIAbilityContext, callback) 242// register a listener on current page 243void UIObserver::RegisterRouterPageCallback( 244 napi_env env, napi_value uiAbilityContext, const std::shared_ptr<UIObserverListener>& listener) 245{ 246 NG::AbilityContextInfo info; 247 GetAbilityInfos(env, uiAbilityContext, info); 248 for (auto listenerPair : abilityContextRouterPageListeners_) { 249 auto ref = listenerPair.first; 250 auto localInfo = infosForRouterPage_[ref]; 251 if (info.IsEqual(localInfo)) { 252 auto& holder = abilityContextRouterPageListeners_[ref]; 253 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 254 return; 255 } 256 holder.emplace_back(listener); 257 return; 258 } 259 } 260 napi_ref newRef = nullptr; 261 napi_create_reference(env, uiAbilityContext, 1, &newRef); 262 abilityContextRouterPageListeners_[newRef] = std::list<std::shared_ptr<UIObserverListener>>({ listener }); 263 infosForRouterPage_[newRef] = info; 264} 265 266// UIObserver.on(type: "routerPageUpdate", uiContext | null, callback) 267// register a listener on current page 268void UIObserver::RegisterRouterPageCallback( 269 int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener) 270{ 271 if (uiContextInstanceId == 0) { 272 uiContextInstanceId = Container::CurrentId(); 273 } 274 auto iter = specifiedRouterPageListeners_.find(uiContextInstanceId); 275 if (iter == specifiedRouterPageListeners_.end()) { 276 specifiedRouterPageListeners_.emplace( 277 uiContextInstanceId, std::list<std::shared_ptr<UIObserverListener>>({ listener })); 278 return; 279 } 280 auto& holder = iter->second; 281 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 282 return; 283 } 284 holder.emplace_back(listener); 285} 286 287// UIObserver.off(type: "routerPageUpdate", uiAbilityContext, callback) 288void UIObserver::UnRegisterRouterPageCallback(napi_env env, napi_value uiAbilityContext, napi_value callback) 289{ 290 NG::AbilityContextInfo info; 291 GetAbilityInfos(env, uiAbilityContext, info); 292 for (auto listenerPair : abilityContextRouterPageListeners_) { 293 auto ref = listenerPair.first; 294 auto localInfo = infosForRouterPage_[ref]; 295 if (info.IsEqual(localInfo)) { 296 auto& holder = abilityContextRouterPageListeners_[listenerPair.first]; 297 if (callback == nullptr) { 298 holder.clear(); 299 } else { 300 holder.erase( 301 std::remove_if( 302 holder.begin(), 303 holder.end(), 304 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 305 return registeredListener->NapiEqual(callback); 306 }), 307 holder.end()); 308 } 309 if (holder.empty()) { 310 infosForRouterPage_.erase(ref); 311 abilityContextRouterPageListeners_.erase(ref); 312 napi_delete_reference(env, ref); 313 } 314 return; 315 } 316 } 317} 318 319// UIObserver.off(type: "routerPageUpdate", uiContext | null, callback) 320void UIObserver::UnRegisterRouterPageCallback(int32_t uiContextInstanceId, napi_value callback) 321{ 322 if (uiContextInstanceId == 0) { 323 uiContextInstanceId = Container::CurrentId(); 324 } 325 auto iter = specifiedRouterPageListeners_.find(uiContextInstanceId); 326 if (iter == specifiedRouterPageListeners_.end()) { 327 return; 328 } 329 auto& holder = iter->second; 330 if (callback == nullptr) { 331 holder.clear(); 332 return; 333 } 334 holder.erase( 335 std::remove_if( 336 holder.begin(), 337 holder.end(), 338 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 339 return registeredListener->NapiEqual(callback); 340 }), 341 holder.end()); 342} 343 344// UIObserver.on(type: "willDraw", uiContext | null, callback) 345// register a listener on current page 346void UIObserver::RegisterDrawCallback(int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener) 347{ 348 if (uiContextInstanceId == 0) { 349 uiContextInstanceId = Container::CurrentId(); 350 } 351 if (specifiedDrawListeners_.find(uiContextInstanceId) == specifiedDrawListeners_.end()) { 352 specifiedDrawListeners_[uiContextInstanceId] = std::list<std::shared_ptr<UIObserverListener>>({ listener }); 353 return; 354 } 355 auto& holder = specifiedDrawListeners_[uiContextInstanceId]; 356 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 357 return; 358 } 359 holder.emplace_back(listener); 360} 361 362// UIObserver.off(type: "willDraw", uiContext | null, callback) 363void UIObserver::UnRegisterDrawCallback(int32_t uiContextInstanceId, napi_value callback) 364{ 365 if (uiContextInstanceId == 0) { 366 uiContextInstanceId = Container::CurrentId(); 367 } 368 if (specifiedDrawListeners_.find(uiContextInstanceId) == specifiedDrawListeners_.end()) { 369 return; 370 } 371 auto& holder = specifiedDrawListeners_[uiContextInstanceId]; 372 if (callback == nullptr) { 373 holder.clear(); 374 return; 375 } 376 holder.erase( 377 std::remove_if( 378 holder.begin(), 379 holder.end(), 380 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 381 return registeredListener->NapiEqual(callback); 382 }), 383 holder.end()); 384} 385 386// UIObserver.on(type: "didLayout", uiContext | null, callback) 387// register a listener on current page 388void UIObserver::RegisterLayoutCallback( 389 int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener) 390{ 391 if (uiContextInstanceId == 0) { 392 uiContextInstanceId = Container::CurrentId(); 393 } 394 if (specifiedLayoutListeners_.find(uiContextInstanceId) == specifiedLayoutListeners_.end()) { 395 specifiedLayoutListeners_[uiContextInstanceId] = std::list<std::shared_ptr<UIObserverListener>>({ listener }); 396 return; 397 } 398 auto& holder = specifiedLayoutListeners_[uiContextInstanceId]; 399 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 400 return; 401 } 402 holder.emplace_back(listener); 403} 404 405// UIObserver.off(type: "didLayout", uiContext | null, callback) 406void UIObserver::UnRegisterLayoutCallback(int32_t uiContextInstanceId, napi_value callback) 407{ 408 if (uiContextInstanceId == 0) { 409 uiContextInstanceId = Container::CurrentId(); 410 } 411 if (specifiedLayoutListeners_.find(uiContextInstanceId) == specifiedLayoutListeners_.end()) { 412 return; 413 } 414 auto& holder = specifiedLayoutListeners_[uiContextInstanceId]; 415 if (callback == nullptr) { 416 holder.clear(); 417 return; 418 } 419 holder.erase( 420 std::remove_if( 421 holder.begin(), 422 holder.end(), 423 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 424 return registeredListener->NapiEqual(callback); 425 }), 426 holder.end()); 427} 428 429void UIObserver::HandleRouterPageStateChange(NG::AbilityContextInfo& info, const NG::RouterPageInfoNG& pageInfo) 430{ 431 for (auto listenerPair : abilityContextRouterPageListeners_) { 432 auto ref = listenerPair.first; 433 auto localInfo = infosForRouterPage_[ref]; 434 if (info.IsEqual(localInfo)) { 435 auto env = GetCurrentNapiEnv(); 436 napi_value abilityContext = nullptr; 437 napi_get_reference_value(env, ref, &abilityContext); 438 439 NG::RouterPageInfoNG abilityPageInfo( 440 abilityContext, pageInfo.index, pageInfo.name, pageInfo.path, pageInfo.state, pageInfo.pageId); 441 auto holder = abilityContextRouterPageListeners_[ref]; 442 for (const auto& listener : holder) { 443 listener->OnRouterPageStateChange(abilityPageInfo); 444 } 445 break; 446 } 447 } 448 449 auto currentId = Container::CurrentId(); 450 auto iter = specifiedRouterPageListeners_.find(currentId); 451 if (iter == specifiedRouterPageListeners_.end()) { 452 return; 453 } 454 auto holder = iter->second; 455 for (const auto& listener : holder) { 456 listener->OnRouterPageStateChange(pageInfo); 457 } 458} 459 460// UIObserver.on(type: "densityUpdate", uiContext | null, callback) 461// register a listener on current page 462void UIObserver::RegisterDensityCallback( 463 int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener) 464{ 465 if (uiContextInstanceId == 0) { 466 uiContextInstanceId = Container::CurrentId(); 467 } 468 auto iter = specifiedDensityListeners_.find(uiContextInstanceId); 469 if (iter == specifiedDensityListeners_.end()) { 470 specifiedDensityListeners_.emplace( 471 uiContextInstanceId, std::list<std::shared_ptr<UIObserverListener>>({ listener })); 472 return; 473 } 474 auto& holder = iter->second; 475 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 476 return; 477 } 478 holder.emplace_back(listener); 479} 480 481// UIObserver.off(type: "densityUpdate", uiContext | null, callback) 482void UIObserver::UnRegisterDensityCallback(int32_t uiContextInstanceId, napi_value callback) 483{ 484 if (uiContextInstanceId == 0) { 485 uiContextInstanceId = Container::CurrentId(); 486 } 487 auto iter = specifiedDensityListeners_.find(uiContextInstanceId); 488 if (iter == specifiedDensityListeners_.end()) { 489 return; 490 } 491 auto& holder = iter->second; 492 if (callback == nullptr) { 493 holder.clear(); 494 return; 495 } 496 holder.erase( 497 std::remove_if( 498 holder.begin(), 499 holder.end(), 500 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 501 return registeredListener->NapiEqual(callback); 502 }), 503 holder.end()); 504} 505 506void UIObserver::HandleDensityChange(NG::AbilityContextInfo& info, double density) 507{ 508 auto currentId = Container::CurrentId(); 509 auto iter = specifiedDensityListeners_.find(currentId); 510 if (iter == specifiedDensityListeners_.end()) { 511 return; 512 } 513 auto& holder = iter->second; 514 for (const auto& listener : holder) { 515 listener->OnDensityChange(density); 516 } 517} 518 519void UIObserver::HandDrawCommandSendChange() 520{ 521 auto currentId = Container::CurrentId(); 522 if (specifiedDrawListeners_.find(currentId) == specifiedDrawListeners_.end()) { 523 return; 524 } 525 auto& holder = specifiedDrawListeners_[currentId]; 526 for (const auto& listener : holder) { 527 listener->OnDrawOrLayout(); 528 } 529} 530 531void UIObserver::HandLayoutDoneChange() 532{ 533 auto currentId = Container::CurrentId(); 534 if (specifiedLayoutListeners_.find(currentId) == specifiedLayoutListeners_.end()) { 535 return; 536 } 537 auto& holder = specifiedLayoutListeners_[currentId]; 538 for (const auto& listener : holder) { 539 listener->OnDrawOrLayout(); 540 } 541} 542 543/** 544 * observer.on('navDestinationSwitch', context: UIAbilityContext, callback) 545 * observer.on('navDestinationSwitch', context: UIAbilityContext, { navigationId: navId }, callback) 546 */ 547void UIObserver::RegisterNavDestinationSwitchCallback(napi_env env, napi_value uiAbilityContext, 548 const std::optional<std::string>& navigationId, const std::shared_ptr<UIObserverListener>& listener) 549{ 550 NG::AbilityContextInfo info; 551 GetAbilityInfos(env, uiAbilityContext, info); 552 for (auto& listenerPair : abilityUIContextNavDesSwitchListeners_) { 553 auto ref = listenerPair.first; 554 auto localInfo = infosForNavDesSwitch_[ref]; 555 if (!info.IsEqual(localInfo)) { 556 continue; 557 } 558 559 auto& listenersMap = listenerPair.second; 560 auto it = listenersMap.find(navigationId); 561 if (it == listenersMap.end()) { 562 listenersMap[navigationId] = std::list<std::shared_ptr<UIObserverListener>>({listener}); 563 return; 564 } 565 if (std::find(it->second.begin(), it->second.end(), listener) == it->second.end()) { 566 it->second.emplace_back(listener); 567 } 568 return; 569 } 570 napi_ref newRef = nullptr; 571 napi_create_reference(env, uiAbilityContext, 1, &newRef); 572 NavIdAndListenersMap listenersMap; 573 listenersMap.emplace(navigationId, std::list<std::shared_ptr<UIObserverListener>>({ listener })); 574 abilityUIContextNavDesSwitchListeners_[newRef] = listenersMap; 575 infosForNavDesSwitch_[newRef] = info; 576} 577 578/** 579 * UIObserver.on('navDestinationSwitch', { navigationId: navId }, callback) 580 * UIObserver.on('navDestinationSwitch', callback) 581 * observer.on('navDestinationSwitch', context: UIContext, { navigationId?: navId }, callback) 582 */ 583void UIObserver::RegisterNavDestinationSwitchCallback(int32_t uiContextInstanceId, 584 const std::optional<std::string>& navigationId, const std::shared_ptr<UIObserverListener>& listener) 585{ 586 if (uiContextInstanceId == 0) { 587 uiContextInstanceId = Container::CurrentId(); 588 } 589 auto listenersMapIter = uiContextNavDesSwitchListeners_.find(uiContextInstanceId); 590 if (listenersMapIter == uiContextNavDesSwitchListeners_.end()) { 591 NavIdAndListenersMap listenersMap; 592 listenersMap.emplace(navigationId, std::list<std::shared_ptr<UIObserverListener>>({ listener })); 593 uiContextNavDesSwitchListeners_[uiContextInstanceId] = listenersMap; 594 return; 595 } 596 597 auto& listenersMap = listenersMapIter->second; 598 auto it = listenersMap.find(navigationId); 599 if (it == listenersMap.end()) { 600 listenersMap[navigationId] = std::list<std::shared_ptr<UIObserverListener>>({listener}); 601 return; 602 } 603 604 if (std::find(it->second.begin(), it->second.end(), listener) == it->second.end()) { 605 it->second.emplace_back(listener); 606 } 607} 608 609/** 610 * observer.off('navDestinationSwitch', context: AbilityUIContext}) 611 * observer.off('navDestinationSwitch', context: AbilityUIContext, callback }) 612 * observer.off('navDestinationSwitch', context: AbilityUIContext, { navigationId: navId } }) 613 * observer.off('navDestinationSwitch', context: AbilityUIContext, { navigationId: navId }, callback }) 614 */ 615void UIObserver::UnRegisterNavDestinationSwitchCallback(napi_env env, napi_value uiAbilityContext, 616 const std::optional<std::string>& navigationId, napi_value callback) 617{ 618 NG::AbilityContextInfo info; 619 GetAbilityInfos(env, uiAbilityContext, info); 620 for (auto listenerPair : abilityUIContextNavDesSwitchListeners_) { 621 auto ref = listenerPair.first; 622 auto localInfo = infosForNavDesSwitch_[ref]; 623 if (!info.IsEqual(localInfo)) { 624 continue; 625 } 626 627 auto& listenersMap = listenerPair.second; 628 auto it = listenersMap.find(navigationId); 629 if (it == listenersMap.end()) { 630 return; 631 } 632 auto& listeners = it->second; 633 if (callback == nullptr) { 634 listeners.clear(); 635 } else { 636 listeners.erase(std::remove_if(listeners.begin(), listeners.end(), 637 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 638 return registeredListener->NapiEqual(callback); 639 }), listeners.end()); 640 } 641 if (listeners.empty()) { 642 listenersMap.erase(it); 643 } 644 if (listenersMap.empty()) { 645 infosForNavDesSwitch_.erase(ref); 646 abilityUIContextNavDesSwitchListeners_.erase(ref); 647 napi_delete_reference(env, ref); 648 } 649 return; 650 } 651} 652 653/** 654 * observer.off('navDestinationSwitch', context: UIContext}) 655 * observer.off('navDestinationSwitch', context: UIContext, callback }) 656 * observer.off('navDestinationSwitch', context: UIContext, { navigationId: navId }) 657 * observer.off('navDestinationSwitch', context: UIContext, { navigationId: navId }, callback ) 658 * UIObserver.off('navDestinationSwitch') 659 * UIObserver.off('navDestinationSwitch', callback) 660 * UIObserver.off('navDestinationSwitch', { navigationId: navId }) 661 * UIObserver.off('navDestinationSwitch', { navigationId: navId }, callback ) 662 */ 663void UIObserver::UnRegisterNavDestinationSwitchCallback(int32_t uiContextInstanceId, 664 const std::optional<std::string>& navigationId, napi_value callback) 665{ 666 if (uiContextInstanceId == 0) { 667 uiContextInstanceId = Container::CurrentId(); 668 } 669 auto listenersMapIter = uiContextNavDesSwitchListeners_.find(uiContextInstanceId); 670 if (listenersMapIter == uiContextNavDesSwitchListeners_.end()) { 671 return; 672 } 673 auto& listenersMap = listenersMapIter->second; 674 auto it = listenersMap.find(navigationId); 675 if (it == listenersMap.end()) { 676 return; 677 } 678 auto& listeners = it->second; 679 if (callback == nullptr) { 680 listeners.clear(); 681 } else { 682 listeners.erase(std::remove_if(listeners.begin(), listeners.end(), 683 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 684 return registeredListener->NapiEqual(callback); 685 }), listeners.end()); 686 } 687 if (listeners.empty()) { 688 listenersMap.erase(it); 689 } 690 if (listenersMap.empty()) { 691 uiContextNavDesSwitchListeners_.erase(listenersMapIter); 692 } 693} 694 695void UIObserver::HandleNavDestinationSwitch( 696 const NG::AbilityContextInfo& info, NG::NavDestinationSwitchInfo& switchInfo) 697{ 698 HandleAbilityUIContextNavDestinationSwitch(info, switchInfo); 699 HandleUIContextNavDestinationSwitch(switchInfo); 700} 701 702void UIObserver::HandleAbilityUIContextNavDestinationSwitch( 703 const NG::AbilityContextInfo& info, NG::NavDestinationSwitchInfo& switchInfo) 704{ 705 napi_value uiContextBackup = switchInfo.context; 706 for (auto listenerPair : abilityUIContextNavDesSwitchListeners_) { 707 auto ref = listenerPair.first; 708 auto localInfo = infosForNavDesSwitch_[ref]; 709 if (!info.IsEqual(localInfo)) { 710 continue; 711 } 712 713 auto env = GetCurrentNapiEnv(); 714 napi_value abilityContext = nullptr; 715 napi_get_reference_value(env, ref, &abilityContext); 716 717 switchInfo.context = abilityContext; 718 auto listenersMap = listenerPair.second; 719 HandleListenersWithEmptyNavigationId(listenersMap, switchInfo); 720 HandleListenersWithSpecifiedNavigationId(listenersMap, switchInfo); 721 break; 722 } 723 switchInfo.context = uiContextBackup; 724} 725 726void UIObserver::HandleUIContextNavDestinationSwitch(const NG::NavDestinationSwitchInfo& switchInfo) 727{ 728 auto currentId = Container::CurrentId(); 729 auto listenersMapIter = uiContextNavDesSwitchListeners_.find(currentId); 730 if (listenersMapIter == uiContextNavDesSwitchListeners_.end()) { 731 return; 732 } 733 auto listenersMap = listenersMapIter->second; 734 HandleListenersWithEmptyNavigationId(listenersMap, switchInfo); 735 HandleListenersWithSpecifiedNavigationId(listenersMap, switchInfo); 736} 737 738void UIObserver::HandleListenersWithEmptyNavigationId( 739 const NavIdAndListenersMap& listenersMap, const NG::NavDestinationSwitchInfo& switchInfo) 740{ 741 std::optional<std::string> navId; 742 auto it = listenersMap.find(navId); 743 if (it != listenersMap.end()) { 744 const auto listeners = it->second; 745 for (const auto& listener : listeners) { 746 listener->OnNavDestinationSwitch(switchInfo); 747 } 748 } 749} 750 751void UIObserver::HandleListenersWithSpecifiedNavigationId( 752 const NavIdAndListenersMap& listenersMap, const NG::NavDestinationSwitchInfo& switchInfo) 753{ 754 std::string navigationId; 755 if (switchInfo.from.has_value()) { 756 navigationId = switchInfo.from.value().navigationId; 757 } else if (switchInfo.to.has_value()) { 758 navigationId = switchInfo.to.value().navigationId; 759 } 760 if (!navigationId.empty()) { 761 std::optional<std::string> navId{navigationId}; 762 auto it = listenersMap.find(navId); 763 if (it != listenersMap.end()) { 764 const auto listeners = it->second; 765 for (const auto& listener : listeners) { 766 listener->OnNavDestinationSwitch(switchInfo); 767 } 768 } 769 } 770} 771 772void UIObserver::RegisterWillClickCallback( 773 napi_env env, napi_value uiAbilityContext, const std::shared_ptr<UIObserverListener>& listener) 774{ 775 napi_handle_scope scope = nullptr; 776 auto status = napi_open_handle_scope(env, &scope); 777 if (status != napi_ok) { 778 return; 779 } 780 NG::AbilityContextInfo info; 781 GetAbilityInfos(env, uiAbilityContext, info); 782 for (auto listenerPair : abilityContextWillClickListeners_) { 783 auto ref = listenerPair.first; 784 auto localInfo = willClickInfos_[ref]; 785 if (info.IsEqual(localInfo)) { 786 auto& holder = abilityContextWillClickListeners_[ref]; 787 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 788 napi_close_handle_scope(env, scope); 789 return; 790 } 791 holder.emplace_back(listener); 792 napi_close_handle_scope(env, scope); 793 return; 794 } 795 } 796 napi_ref newRef = nullptr; 797 napi_create_reference(env, uiAbilityContext, 1, &newRef); 798 abilityContextWillClickListeners_[newRef] = std::list<std::shared_ptr<UIObserverListener>>({ listener }); 799 willClickInfos_[newRef] = info; 800 napi_close_handle_scope(env, scope); 801} 802 803void UIObserver::RegisterWillClickCallback( 804 int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener) 805{ 806 if (uiContextInstanceId == 0) { 807 uiContextInstanceId = Container::CurrentId(); 808 } 809 auto iter = specifiedWillClickListeners_.find(uiContextInstanceId); 810 if (iter == specifiedWillClickListeners_.end()) { 811 specifiedWillClickListeners_.emplace( 812 uiContextInstanceId, std::list<std::shared_ptr<UIObserverListener>>({ listener })); 813 return; 814 } 815 auto& holder = iter->second; 816 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 817 return; 818 } 819 holder.emplace_back(listener); 820} 821 822void UIObserver::UnRegisterWillClickCallback(napi_env env, napi_value uiAbilityContext, napi_value callback) 823{ 824 napi_handle_scope scope = nullptr; 825 auto status = napi_open_handle_scope(env, &scope); 826 if (status != napi_ok) { 827 return; 828 } 829 NG::AbilityContextInfo info; 830 GetAbilityInfos(env, uiAbilityContext, info); 831 for (auto listenerPair : abilityContextWillClickListeners_) { 832 auto ref = listenerPair.first; 833 auto localInfo = willClickInfos_[ref]; 834 if (!info.IsEqual(localInfo)) { 835 continue; 836 } 837 auto& holder = abilityContextWillClickListeners_[listenerPair.first]; 838 if (callback == nullptr) { 839 holder.clear(); 840 } else { 841 holder.erase( 842 std::remove_if( 843 holder.begin(), 844 holder.end(), 845 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 846 return registeredListener->NapiEqual(callback); 847 }), 848 holder.end()); 849 } 850 if (holder.empty()) { 851 willClickInfos_.erase(ref); 852 abilityContextWillClickListeners_.erase(ref); 853 napi_delete_reference(env, ref); 854 } 855 } 856 napi_close_handle_scope(env, scope); 857} 858 859void UIObserver::UnRegisterWillClickCallback(int32_t uiContextInstanceId, napi_value callback) 860{ 861 if (uiContextInstanceId == 0) { 862 uiContextInstanceId = Container::CurrentId(); 863 } 864 auto iter = specifiedWillClickListeners_.find(uiContextInstanceId); 865 if (iter == specifiedWillClickListeners_.end()) { 866 return; 867 } 868 auto& holder = iter->second; 869 if (callback == nullptr) { 870 holder.clear(); 871 return; 872 } 873 holder.erase( 874 std::remove_if( 875 holder.begin(), 876 holder.end(), 877 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 878 return registeredListener->NapiEqual(callback); 879 }), 880 holder.end()); 881} 882 883void UIObserver::HandleWillClick(NG::AbilityContextInfo& info, const GestureEvent& gestureEventInfo, 884 const ClickInfo& clickInfo, const RefPtr<NG::FrameNode>& frameNode) 885{ 886 auto env = GetCurrentNapiEnv(); 887 napi_handle_scope scope = nullptr; 888 auto status = napi_open_handle_scope(env, &scope); 889 if (status != napi_ok) { 890 return; 891 } 892 for (auto listenerPair : abilityContextWillClickListeners_) { 893 auto ref = listenerPair.first; 894 auto localInfo = willClickInfos_[ref]; 895 if (info.IsEqual(localInfo)) { 896 napi_value abilityContext = nullptr; 897 napi_get_reference_value(env, ref, &abilityContext); 898 899 auto& holder = abilityContextWillClickListeners_[ref]; 900 for (const auto& listener : holder) { 901 listener->OnWillClick(gestureEventInfo, clickInfo, frameNode); 902 } 903 break; 904 } 905 } 906 907 auto currentId = Container::CurrentId(); 908 auto iter = specifiedWillClickListeners_.find(currentId); 909 if (iter == specifiedWillClickListeners_.end()) { 910 napi_close_handle_scope(env, scope); 911 return; 912 } 913 auto& holder = iter->second; 914 for (const auto& listener : holder) { 915 listener->OnWillClick(gestureEventInfo, clickInfo, frameNode); 916 } 917 napi_close_handle_scope(env, scope); 918} 919 920void UIObserver::RegisterDidClickCallback( 921 napi_env env, napi_value uiAbilityContext, const std::shared_ptr<UIObserverListener>& listener) 922{ 923 napi_handle_scope scope = nullptr; 924 auto status = napi_open_handle_scope(env, &scope); 925 if (status != napi_ok) { 926 return; 927 } 928 NG::AbilityContextInfo info; 929 GetAbilityInfos(env, uiAbilityContext, info); 930 for (auto listenerPair : abilityContextDidClickListeners_) { 931 auto ref = listenerPair.first; 932 auto localInfo = didClickInfos_[ref]; 933 if (info.IsEqual(localInfo)) { 934 auto& holder = abilityContextDidClickListeners_[ref]; 935 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 936 napi_close_handle_scope(env, scope); 937 return; 938 } 939 holder.emplace_back(listener); 940 napi_close_handle_scope(env, scope); 941 return; 942 } 943 } 944 napi_ref newRef = nullptr; 945 napi_create_reference(env, uiAbilityContext, 1, &newRef); 946 abilityContextDidClickListeners_[newRef] = std::list<std::shared_ptr<UIObserverListener>>({ listener }); 947 didClickInfos_[newRef] = info; 948 napi_close_handle_scope(env, scope); 949} 950 951void UIObserver::RegisterDidClickCallback( 952 int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener) 953{ 954 if (uiContextInstanceId == 0) { 955 uiContextInstanceId = Container::CurrentId(); 956 } 957 auto iter = specifiedDidClickListeners_.find(uiContextInstanceId); 958 if (iter == specifiedDidClickListeners_.end()) { 959 specifiedDidClickListeners_.emplace( 960 uiContextInstanceId, std::list<std::shared_ptr<UIObserverListener>>({ listener })); 961 return; 962 } 963 auto& holder = iter->second; 964 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 965 return; 966 } 967 holder.emplace_back(listener); 968} 969 970void UIObserver::UnRegisterDidClickCallback(napi_env env, napi_value uiAbilityContext, napi_value callback) 971{ 972 napi_handle_scope scope = nullptr; 973 auto status = napi_open_handle_scope(env, &scope); 974 if (status != napi_ok) { 975 return; 976 } 977 NG::AbilityContextInfo info; 978 GetAbilityInfos(env, uiAbilityContext, info); 979 for (auto listenerPair : abilityContextDidClickListeners_) { 980 auto ref = listenerPair.first; 981 auto localInfo = didClickInfos_[ref]; 982 if (!info.IsEqual(localInfo)) { 983 continue; 984 } 985 auto& holder = abilityContextDidClickListeners_[listenerPair.first]; 986 if (callback == nullptr) { 987 holder.clear(); 988 } else { 989 holder.erase( 990 std::remove_if( 991 holder.begin(), 992 holder.end(), 993 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 994 return registeredListener->NapiEqual(callback); 995 }), 996 holder.end()); 997 } 998 if (holder.empty()) { 999 didClickInfos_.erase(ref); 1000 abilityContextDidClickListeners_.erase(ref); 1001 napi_delete_reference(env, ref); 1002 } 1003 } 1004 napi_close_handle_scope(env, scope); 1005} 1006 1007void UIObserver::UnRegisterDidClickCallback(int32_t uiContextInstanceId, napi_value callback) 1008{ 1009 if (uiContextInstanceId == 0) { 1010 uiContextInstanceId = Container::CurrentId(); 1011 } 1012 auto iter = specifiedDidClickListeners_.find(uiContextInstanceId); 1013 if (iter == specifiedDidClickListeners_.end()) { 1014 return; 1015 } 1016 auto& holder = iter->second; 1017 if (callback == nullptr) { 1018 holder.clear(); 1019 return; 1020 } 1021 holder.erase( 1022 std::remove_if( 1023 holder.begin(), 1024 holder.end(), 1025 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) { 1026 return registeredListener->NapiEqual(callback); 1027 }), 1028 holder.end()); 1029} 1030 1031void UIObserver::HandleDidClick(NG::AbilityContextInfo& info, const GestureEvent& gestureEventInfo, 1032 const ClickInfo& clickInfo, const RefPtr<NG::FrameNode>& frameNode) 1033{ 1034 auto env = GetCurrentNapiEnv(); 1035 napi_handle_scope scope = nullptr; 1036 auto status = napi_open_handle_scope(env, &scope); 1037 if (status != napi_ok) { 1038 return; 1039 } 1040 for (auto listenerPair : abilityContextDidClickListeners_) { 1041 auto ref = listenerPair.first; 1042 auto localInfo = didClickInfos_[ref]; 1043 if (info.IsEqual(localInfo)) { 1044 napi_value abilityContext = nullptr; 1045 napi_get_reference_value(env, ref, &abilityContext); 1046 1047 auto& holder = abilityContextDidClickListeners_[ref]; 1048 for (const auto& listener : holder) { 1049 listener->OnDidClick(gestureEventInfo, clickInfo, frameNode); 1050 } 1051 break; 1052 } 1053 } 1054 1055 auto currentId = Container::CurrentId(); 1056 auto iter = specifiedDidClickListeners_.find(currentId); 1057 if (iter == specifiedDidClickListeners_.end()) { 1058 napi_close_handle_scope(env, scope); 1059 return; 1060 } 1061 auto& holder = iter->second; 1062 for (const auto& listener : holder) { 1063 listener->OnDidClick(gestureEventInfo, clickInfo, frameNode); 1064 } 1065 napi_close_handle_scope(env, scope); 1066} 1067 1068// UIObserver.on(type: "tabContentState", callback) 1069// register a global listener without options 1070void UIObserver::RegisterTabContentStateCallback(const std::shared_ptr<UIObserverListener>& listener) 1071{ 1072 if (std::find(tabContentStateListeners_.begin(), tabContentStateListeners_.end(), listener) != 1073 tabContentStateListeners_.end()) { 1074 return; 1075 } 1076 tabContentStateListeners_.emplace_back(listener); 1077} 1078 1079// UIObserver.on(type: "tabContentState", options, callback) 1080// register a listener on a specified tabContentState 1081void UIObserver::RegisterTabContentStateCallback( 1082 const std::string& id, const std::shared_ptr<UIObserverListener>& listener) 1083{ 1084 auto iter = specifiedTabContentStateListeners_.find(id); 1085 if (iter == specifiedTabContentStateListeners_.end()) { 1086 specifiedTabContentStateListeners_.emplace(id, std::list<std::shared_ptr<UIObserverListener>>({ listener })); 1087 return; 1088 } 1089 auto& holder = iter->second; 1090 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) { 1091 return; 1092 } 1093 holder.emplace_back(listener); 1094} 1095 1096// UIObserver.off(type: "tabContentState", callback) 1097void UIObserver::UnRegisterTabContentStateCallback(napi_value cb) 1098{ 1099 if (cb == nullptr) { 1100 tabContentStateListeners_.clear(); 1101 return; 1102 } 1103 1104 tabContentStateListeners_.erase( 1105 std::remove_if( 1106 tabContentStateListeners_.begin(), 1107 tabContentStateListeners_.end(), 1108 [cb](const std::shared_ptr<UIObserverListener>& registeredListener) { 1109 return registeredListener->NapiEqual(cb); 1110 }), 1111 tabContentStateListeners_.end() 1112 ); 1113} 1114 1115// UIObserver.off(type: "tabContentState", options, callback) 1116void UIObserver::UnRegisterTabContentStateCallback(const std::string& id, napi_value cb) 1117{ 1118 auto iter = specifiedTabContentStateListeners_.find(id); 1119 if (iter == specifiedTabContentStateListeners_.end()) { 1120 return; 1121 } 1122 auto& holder = iter->second; 1123 if (cb == nullptr) { 1124 holder.clear(); 1125 return; 1126 } 1127 holder.erase( 1128 std::remove_if( 1129 holder.begin(), 1130 holder.end(), 1131 [cb](const std::shared_ptr<UIObserverListener>& registeredListener) { 1132 return registeredListener->NapiEqual(cb); 1133 }), 1134 holder.end() 1135 ); 1136} 1137 1138void UIObserver::HandleTabContentStateChange(const NG::TabContentInfo& tabContentInfo) 1139{ 1140 for (const auto& listener : tabContentStateListeners_) { 1141 listener->OnTabContentStateChange(tabContentInfo); 1142 } 1143 1144 auto iter = specifiedTabContentStateListeners_.find(tabContentInfo.id); 1145 if (iter == specifiedTabContentStateListeners_.end()) { 1146 return; 1147 } 1148 1149 auto& holder = iter->second; 1150 1151 for (const auto& listener : holder) { 1152 listener->OnTabContentStateChange(tabContentInfo); 1153 } 1154} 1155 1156void UIObserver::GetAbilityInfos(napi_env env, napi_value abilityContext, NG::AbilityContextInfo& info) 1157{ 1158 if (!env || !abilityContext) { 1159 return; 1160 } 1161 napi_value napiInfo = nullptr; 1162 napi_get_named_property(env, abilityContext, "abilityInfo", &napiInfo); 1163 CHECK_NULL_VOID(napiInfo); 1164 napi_value name = nullptr; 1165 napi_get_named_property(env, napiInfo, "name", &name); 1166 ParseStringFromNapi(env, name, info.name); 1167 napi_get_named_property(env, napiInfo, "bundleName", &name); 1168 ParseStringFromNapi(env, name, info.bundleName); 1169 napi_get_named_property(env, napiInfo, "moduleName", &name); 1170 ParseStringFromNapi(env, name, info.moduleName); 1171} 1172 1173bool UIObserver::ParseStringFromNapi(napi_env env, napi_value val, std::string& str) 1174{ 1175 if (!val || !MatchValueType(env, val, napi_string)) { 1176 return false; 1177 } 1178 size_t len = 0; 1179 napi_get_value_string_utf8(env, val, nullptr, 0, &len); 1180 std::unique_ptr<char[]> result = std::make_unique<char[]>(len + 1); 1181 napi_get_value_string_utf8(env, val, result.get(), len + 1, &len); 1182 str = result.get(); 1183 return true; 1184} 1185 1186bool UIObserver::MatchValueType(napi_env env, napi_value value, napi_valuetype targetType) 1187{ 1188 napi_valuetype valueType = napi_undefined; 1189 napi_typeof(env, value, &valueType); 1190 return valueType == targetType; 1191} 1192 1193napi_env UIObserver::GetCurrentNapiEnv() 1194{ 1195 auto engine = EngineHelper::GetCurrentEngine(); 1196 CHECK_NULL_RETURN(engine, nullptr); 1197 NativeEngine* nativeEngine = engine->GetNativeEngine(); 1198 CHECK_NULL_RETURN(nativeEngine, nullptr); 1199 return reinterpret_cast<napi_env>(nativeEngine); 1200} 1201} // namespace OHOS::Ace::Napi 1202