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 #include <algorithm>
21
22 namespace OHOS::Ace::Napi {
23 std::list<std::shared_ptr<UIObserverListener>> UIObserver::unspecifiedNavigationListeners_;
24 std::unordered_map<std::string, std::list<std::shared_ptr<UIObserverListener>>>
25 UIObserver::specifiedCNavigationListeners_;
26
27 std::unordered_map<napi_ref, std::list<std::shared_ptr<UIObserverListener>>>
28 UIObserver::abilityContextRouterPageListeners_;
29 std::unordered_map<int32_t, std::list<std::shared_ptr<UIObserverListener>>>
30 UIObserver::specifiedRouterPageListeners_;
31 std::unordered_map<napi_ref, NG::AbilityContextInfo> UIObserver::infos_;
32
33 // UIObserver.on(type: "navDestinationUpdate", callback)
34 // register a global listener without options
RegisterNavigationCallback(const std::shared_ptr<UIObserverListener> & listener)35 void UIObserver::RegisterNavigationCallback(const std::shared_ptr<UIObserverListener>& listener)
36 {
37 if (std::find(unspecifiedNavigationListeners_.begin(), unspecifiedNavigationListeners_.end(), listener) !=
38 unspecifiedNavigationListeners_.end()) {
39 return;
40 }
41 unspecifiedNavigationListeners_.emplace_back(listener);
42 }
43
44 // UIObserver.on(type: "navDestinationUpdate", options, callback)
45 // register a listener on a specified Navigation
RegisterNavigationCallback(std::string navigationId,const std::shared_ptr<UIObserverListener> & listener)46 void UIObserver::RegisterNavigationCallback(
47 std::string navigationId, const std::shared_ptr<UIObserverListener>& listener)
48 {
49 if (specifiedCNavigationListeners_.find(navigationId) == specifiedCNavigationListeners_.end()) {
50 specifiedCNavigationListeners_[navigationId] = std::list<std::shared_ptr<UIObserverListener>>({ listener });
51 return;
52 }
53 auto& holder = specifiedCNavigationListeners_[navigationId];
54 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
55 return;
56 }
57 holder.emplace_back(listener);
58 }
59
60 // UIObserver.off(type: "navDestinationUpdate", callback)
UnRegisterNavigationCallback(napi_value cb)61 void UIObserver::UnRegisterNavigationCallback(napi_value cb)
62 {
63 if (cb == nullptr) {
64 unspecifiedNavigationListeners_.clear();
65 return;
66 }
67
68 unspecifiedNavigationListeners_.erase(
69 std::remove_if(
70 unspecifiedNavigationListeners_.begin(),
71 unspecifiedNavigationListeners_.end(),
72 [cb](const std::shared_ptr<UIObserverListener>& registeredListener) {
73 return registeredListener->NapiEqual(cb);
74 }
75 ),
76 unspecifiedNavigationListeners_.end()
77 );
78 }
79
80 // UIObserver.off(type: "navDestinationUpdate", options, callback)
UnRegisterNavigationCallback(std::string navigationId,napi_value cb)81 void UIObserver::UnRegisterNavigationCallback(std::string navigationId, napi_value cb)
82 {
83 if (specifiedCNavigationListeners_.find(navigationId) == specifiedCNavigationListeners_.end()) {
84 return;
85 }
86 auto& holder = specifiedCNavigationListeners_[navigationId];
87 if (cb == nullptr) {
88 holder.clear();
89 return;
90 }
91 holder.erase(
92 std::remove_if(
93 holder.begin(),
94 holder.end(),
95 [cb](const std::shared_ptr<UIObserverListener>& registeredListener) {
96 return registeredListener->NapiEqual(cb);
97 }
98 ),
99 holder.end()
100 );
101 }
102
HandleNavigationStateChange(const std::string & navigationId,const std::string & navDestinationName,NG::NavDestinationState state)103 void UIObserver::HandleNavigationStateChange(const std::string& navigationId, const std::string& navDestinationName,
104 NG::NavDestinationState state)
105 {
106 for (const auto& listener : unspecifiedNavigationListeners_) {
107 listener->OnNavigationStateChange(navigationId, navDestinationName, state);
108 }
109
110 if (specifiedCNavigationListeners_.find(navigationId) ==
111 specifiedCNavigationListeners_.end()) {
112 return;
113 }
114
115 auto& holder = specifiedCNavigationListeners_[navigationId];
116
117 for (const auto& listener : holder) {
118 listener->OnNavigationStateChange(navigationId, navDestinationName, state);
119 }
120 }
121
122 // UIObserver.on(type: "routerPageUpdate", UIAbilityContext, callback)
123 // register a listener on current page
RegisterRouterPageCallback(napi_env env,napi_value uiAbilityContext,const std::shared_ptr<UIObserverListener> & listener)124 void UIObserver::RegisterRouterPageCallback(
125 napi_env env, napi_value uiAbilityContext, const std::shared_ptr<UIObserverListener>& listener)
126 {
127 NG::AbilityContextInfo info;
128 GetAbilityInfos(env, uiAbilityContext, info);
129 for (auto listenerPair : abilityContextRouterPageListeners_) {
130 auto ref = listenerPair.first;
131 auto localInfo = infos_[ref];
132 if (info.IsEqual(localInfo)) {
133 auto& holder = abilityContextRouterPageListeners_[ref];
134 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
135 return;
136 }
137 holder.emplace_back(listener);
138 return;
139 }
140 }
141 napi_ref newRef = nullptr;
142 napi_create_reference(env, uiAbilityContext, 1, &newRef);
143 abilityContextRouterPageListeners_[newRef] = std::list<std::shared_ptr<UIObserverListener>>({ listener });
144 infos_[newRef] = info;
145 }
146
147 // UIObserver.on(type: "routerPageUpdate", uiContext | null, callback)
148 // register a listener on current page
RegisterRouterPageCallback(int32_t uiContextInstanceId,const std::shared_ptr<UIObserverListener> & listener)149 void UIObserver::RegisterRouterPageCallback(
150 int32_t uiContextInstanceId, const std::shared_ptr<UIObserverListener>& listener)
151 {
152 if (uiContextInstanceId == 0) {
153 uiContextInstanceId = Container::CurrentId();
154 }
155 if (specifiedRouterPageListeners_.find(uiContextInstanceId) == specifiedRouterPageListeners_.end()) {
156 specifiedRouterPageListeners_[uiContextInstanceId] =
157 std::list<std::shared_ptr<UIObserverListener>>({ listener });
158 return;
159 }
160 auto& holder = specifiedRouterPageListeners_[uiContextInstanceId];
161 if (std::find(holder.begin(), holder.end(), listener) != holder.end()) {
162 return;
163 }
164 holder.emplace_back(listener);
165 }
166
167 // UIObserver.off(type: "routerPageUpdate", uiAbilityContext, callback)
UnRegisterRouterPageCallback(napi_env env,napi_value uiAbilityContext,napi_value callback)168 void UIObserver::UnRegisterRouterPageCallback(napi_env env, napi_value uiAbilityContext, napi_value callback)
169 {
170 NG::AbilityContextInfo info;
171 GetAbilityInfos(env, uiAbilityContext, info);
172 for (auto listenerPair : abilityContextRouterPageListeners_) {
173 auto ref = listenerPair.first;
174 auto localInfo = infos_[ref];
175 if (info.IsEqual(localInfo)) {
176 auto& holder = abilityContextRouterPageListeners_[listenerPair.first];
177 if (callback == nullptr) {
178 holder.clear();
179 } else {
180 holder.erase(
181 std::remove_if(
182 holder.begin(),
183 holder.end(),
184 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
185 return registeredListener->NapiEqual(callback);
186 }),
187 holder.end());
188 }
189 if (holder.empty()) {
190 infos_.erase(ref);
191 abilityContextRouterPageListeners_.erase(ref);
192 napi_delete_reference(env, ref);
193 }
194 return;
195 }
196 }
197 }
198
199 // UIObserver.off(type: "routerPageUpdate", uiContext | null, callback)
UnRegisterRouterPageCallback(int32_t uiContextInstanceId,napi_value callback)200 void UIObserver::UnRegisterRouterPageCallback(int32_t uiContextInstanceId, napi_value callback)
201 {
202 if (uiContextInstanceId == 0) {
203 uiContextInstanceId = Container::CurrentId();
204 }
205 if (specifiedRouterPageListeners_.find(uiContextInstanceId) == specifiedRouterPageListeners_.end()) {
206 return;
207 }
208 auto& holder = specifiedRouterPageListeners_[uiContextInstanceId];
209 if (callback == nullptr) {
210 holder.clear();
211 return;
212 }
213 holder.erase(
214 std::remove_if(
215 holder.begin(),
216 holder.end(),
217 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
218 return registeredListener->NapiEqual(callback);
219 }),
220 holder.end());
221 }
222
HandleRouterPageStateChange(NG::AbilityContextInfo & info,napi_value context,int32_t index,const std::string & name,const std::string & path,NG::RouterPageState state)223 void UIObserver::HandleRouterPageStateChange(NG::AbilityContextInfo& info, napi_value context, int32_t index,
224 const std::string& name, const std::string& path, NG::RouterPageState state)
225 {
226 for (auto listenerPair : abilityContextRouterPageListeners_) {
227 auto ref = listenerPair.first;
228 auto localInfo = infos_[ref];
229 if (info.IsEqual(localInfo)) {
230 auto env = GetCurrentNapiEnv();
231 napi_value abilityContext = nullptr;
232 napi_get_reference_value(env, ref, &abilityContext);
233
234 auto& holder = abilityContextRouterPageListeners_[ref];
235 for (const auto& listener : holder) {
236 listener->OnRouterPageStateChange(abilityContext, index, name, path, state);
237 }
238 break;
239 }
240 }
241
242 auto currentId = Container::CurrentId();
243 if (specifiedRouterPageListeners_.find(currentId) == specifiedRouterPageListeners_.end()) {
244 return;
245 }
246 auto& holder = specifiedRouterPageListeners_[currentId];
247 for (const auto& listener : holder) {
248 listener->OnRouterPageStateChange(context, index, name, path, state);
249 }
250 }
251
GetAbilityInfos(napi_env env,napi_value abilityContext,NG::AbilityContextInfo & info)252 void UIObserver::GetAbilityInfos(napi_env env, napi_value abilityContext, NG::AbilityContextInfo& info)
253 {
254 if (!env || !abilityContext) {
255 return;
256 }
257 napi_value napiInfo = nullptr;
258 napi_get_named_property(env, abilityContext, "abilityInfo", &napiInfo);
259 CHECK_NULL_VOID(napiInfo);
260 napi_value name = nullptr;
261 napi_get_named_property(env, napiInfo, "name", &name);
262 ParseStringFromNapi(env, name, info.name);
263 napi_get_named_property(env, napiInfo, "bundleName", &name);
264 ParseStringFromNapi(env, name, info.bundleName);
265 napi_get_named_property(env, napiInfo, "moduleName", &name);
266 ParseStringFromNapi(env, name, info.moduleName);
267 }
268
ParseStringFromNapi(napi_env env,napi_value val,std::string & str)269 bool UIObserver::ParseStringFromNapi(napi_env env, napi_value val, std::string& str)
270 {
271 if (!val || !MatchValueType(env, val, napi_string)) {
272 return false;
273 }
274 size_t len = 0;
275 napi_get_value_string_utf8(env, val, nullptr, 0, &len);
276 std::unique_ptr<char[]> result = std::make_unique<char[]>(len + 1);
277 napi_get_value_string_utf8(env, val, result.get(), len + 1, &len);
278 str = result.get();
279 return true;
280 }
281
MatchValueType(napi_env env,napi_value value,napi_valuetype targetType)282 bool UIObserver::MatchValueType(napi_env env, napi_value value, napi_valuetype targetType)
283 {
284 napi_valuetype valueType = napi_undefined;
285 napi_typeof(env, value, &valueType);
286 return valueType == targetType;
287 }
288
GetCurrentNapiEnv()289 napi_env UIObserver::GetCurrentNapiEnv()
290 {
291 auto engine = EngineHelper::GetCurrentEngine();
292 CHECK_NULL_RETURN(engine, nullptr);
293 NativeEngine* nativeEngine = engine->GetNativeEngine();
294 CHECK_NULL_RETURN(nativeEngine, nullptr);
295 return reinterpret_cast<napi_env>(nativeEngine);
296 }
297 } // namespace OHOS::Ace::Napi
298