• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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