• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 #define ABILITY_WANT_PARAMS_UIEXTENSIONTARGETTYPE "ability.want.params.uiExtensionTargetType"
16 #include "picker_n_exporter.h"
17 #include "modal_ui_callback.h"
18 #include "modal_ui_extension_config.h"
19 #include "ability_context.h"
20 #include "context.h"
21 #include "ability.h"
22 #ifdef HAS_ACE_ENGINE_PART
23 #include "ui_content.h"
24 #endif
25 #include "ui_extension_context.h"
26 #include "want.h"
27 #ifdef UDMF_ENABLED
28 #include "unified_data.h"
29 #include "udmf_client.h"
30 #endif
31 
32 namespace OHOS {
33 namespace Picker {
34 using namespace std;
35 using namespace FileManagement;
36 using namespace FileManagement::LibN;
37 using namespace AppExecFwk;
38 #define WAIT_TIME_MS 100
39 
40 static const string PHOTO_URIS_KEY = "photoUris";
41 
Export()42 bool PickerNExporter::Export()
43 {
44     return exports_.AddProp({
45         NVal::DeclareNapiFunction("startModalPicker", StartModalPicker)
46     });
47 }
48 
GetClassName()49 string PickerNExporter::GetClassName()
50 {
51     return PickerNExporter::className_;
52 }
53 
StartModalPickerExecute(napi_env env,void * data)54 static void StartModalPickerExecute(napi_env env, void *data)
55 {
56     HILOG_INFO("[picker]: StartModalPickerExecute begin");
57     auto *context = static_cast<PickerAsyncContext*>(data);
58     if (!context) {
59         HILOG_ERROR("[picker]:context is nullptr");
60         return;
61     }
62     if (!context->pickerCallBack) {
63         HILOG_ERROR("[picker]:!context->pickerCallBack is nullptr");
64         return;
65     }
66     while (!context->pickerCallBack->ready) {
67         std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_MS));
68     }
69     HILOG_INFO("[picker]: StartModalPickerExecute is ready.");
70 }
71 
MakeResultWithArr(napi_env env,std::string key,napi_value & result,std::shared_ptr<PickerCallBack> pickerCallBack)72 static void MakeResultWithArr(napi_env env, std::string key, napi_value &result,
73     std::shared_ptr<PickerCallBack> pickerCallBack)
74 {
75     if (pickerCallBack == nullptr) {
76         HILOG_ERROR("[picker]:pickerCallBack is nullptr");
77         return;
78     }
79     napi_value array;
80     napi_create_array(env, &array);
81     napi_status status = napi_generic_failure;
82     if (pickerCallBack->want.GetParams().HasParam(key.c_str())) {
83         std::vector<std::string> list = pickerCallBack->want.GetStringArrayParam(key.c_str());
84         HILOG_INFO("[picker]: %{public}s size. %{public}zu ", key.c_str(), list.size());
85         for (size_t i = 0; i < list.size(); i++) {
86             napi_value uri = nullptr;
87             napi_create_string_utf8(env, list[i].c_str(), NAPI_AUTO_LENGTH, &uri);
88             napi_set_element(env, array, i, uri);
89         }
90         if (key == "ability.params.stream") {
91             key = "ability_params_stream";
92         }
93         if (key == "select-item-list") {
94             key = PHOTO_URIS_KEY;
95         }
96         status = napi_set_named_property(env, result, key.c_str(), array);
97         if (status != napi_ok) {
98             HILOG_ERROR("[picker]: napi_set_named_property %{public}s failed", key.c_str());
99         }
100     }
101 }
102 
MakeResultWithInt(napi_env env,std::string key,napi_value & result,std::shared_ptr<PickerCallBack> pickerCallBack)103 static void MakeResultWithInt(napi_env env, std::string key, napi_value &result,
104     std::shared_ptr<PickerCallBack> pickerCallBack)
105 {
106     if (pickerCallBack == nullptr) {
107         HILOG_ERROR("[picker]: pickerCallBack is nullptr");
108         return;
109     }
110     napi_status status = napi_generic_failure;
111     if (pickerCallBack->want.GetParams().HasParam(key.c_str())) {
112         const int32_t suffixindex = pickerCallBack->want.GetIntParam(key.c_str(), -1);
113         HILOG_INFO("[picker]: %{public}s is %{public}d ", key.c_str(), suffixindex);
114         napi_value suffix = nullptr;
115         napi_create_int32(env, suffixindex, &suffix);
116         status = napi_set_named_property(env, result, key.c_str(), suffix);
117         if (status != napi_ok) {
118             HILOG_ERROR("[picker]: napi_set_named_property %{public}s failed", key.c_str());
119         }
120     }
121 }
122 
MakeResultWithBool(napi_env env,std::string key,napi_value & result,std::shared_ptr<PickerCallBack> pickerCallBack)123 static void MakeResultWithBool(napi_env env, std::string key, napi_value &result,
124     std::shared_ptr<PickerCallBack> pickerCallBack)
125 {
126     if (pickerCallBack == nullptr) {
127         HILOG_ERROR("[picker]: pickerCallBack is nullptr");
128         return;
129     }
130     napi_status status = napi_generic_failure;
131     if (pickerCallBack->want.GetParams().HasParam(key.c_str())) {
132         const bool boolVal = pickerCallBack->want.GetBoolParam(key.c_str(), false);
133         HILOG_INFO("[picker]: %{public}s is %{public}d ", key.c_str(), boolVal);
134         napi_value nBoolVal = nullptr;
135         napi_get_boolean(env, boolVal, &nBoolVal);
136         status = napi_set_named_property(env, result, key.c_str(), nBoolVal);
137         if (status != napi_ok) {
138             HILOG_ERROR("[picker]: napi_set_named_property %{public}s failed", key.c_str());
139         }
140     }
141 }
142 
GetUriArray(napi_env env,std::vector<UDMF::UnifiedData> unifiedDataSet,napi_value & array)143 void GetUriArray(napi_env env, std::vector<UDMF::UnifiedData> unifiedDataSet, napi_value &array)
144 {
145     size_t len = unifiedDataSet[0].GetRecords().size();
146     size_t position = 0;
147     for (size_t i = 0; i < len; ++i) {
148         auto readRecord = unifiedDataSet[0].GetRecordAt(i);
149         if (readRecord == nullptr) {
150             HILOG_ERROR("[picker]: readRecord is nullptr");
151             continue;
152         }
153         auto entry = readRecord->GetEntry("general.file-uri");
154         if (!std::holds_alternative<std::shared_ptr<UDMF::Object>>(entry)) {
155             HILOG_ERROR("[picker]: entry is not Object");
156             continue;
157         }
158         auto obj = std::get<std::shared_ptr<UDMF::Object>>(entry);
159         std::string uri;
160         obj->GetValue("oriUri", uri);
161         if (uri.empty()) {
162             HILOG_ERROR("[picker]: uri is empty");
163             continue;
164         }
165         napi_value uriVal = nullptr;
166         napi_create_string_utf8(env, uri.c_str(), NAPI_AUTO_LENGTH, &uriVal);
167         if (uriVal == nullptr) {
168             HILOG_ERROR("[picker]: create uri js value fail.");
169             continue;
170         }
171         auto status = napi_set_element(env, array, position, uriVal);
172         position++;
173         if (status != napi_ok) {
174             HILOG_ERROR("[picker]: napi_set_element failed, error: %{public}d", status);
175         }
176     }
177 }
178 
MakeResultWithUdkey(napi_env env,const std::string key,napi_value & result,std::shared_ptr<PickerCallBack> pickerCallBack)179 static void MakeResultWithUdkey(napi_env env, const std::string key, napi_value &result,
180     std::shared_ptr<PickerCallBack> pickerCallBack)
181 {
182     if (pickerCallBack == nullptr) {
183         HILOG_ERROR("[picker]: pickerCallBack is nullptr");
184         return;
185     }
186     napi_value array;
187     napi_create_array(env, &array);
188     napi_status status = napi_generic_failure;
189     if (!pickerCallBack->want.GetParams().HasParam(key.c_str())) {
190         return;
191     }
192     const std::string udkey = pickerCallBack->want.GetStringParam(key.c_str());
193     UDMF::QueryOption query = {.key = udkey};
194     std::vector<UDMF::UnifiedData> unifiedDataSet;
195     auto stat = UDMF::UdmfClient::GetInstance().GetBatchData(query, unifiedDataSet);
196     if (stat != UDMF::Status::E_OK || unifiedDataSet.empty()) {
197         HILOG_ERROR("[picker]: unifiedDataSet isEmpty or GetBatchData failed, stat=%{public}d", stat);
198         return;
199     }
200     GetUriArray(env, unifiedDataSet, array);
201     status = napi_set_named_property(env, result, "ability_params_udkey", array);
202     if (status != napi_ok) {
203         HILOG_ERROR("[picker]: napi_set_named_property failed");
204     }
205 }
206 
MakeResultWithPickerCallBack(napi_env env,std::shared_ptr<PickerCallBack> pickerCallBack)207 static napi_value MakeResultWithPickerCallBack(napi_env env, std::shared_ptr<PickerCallBack> pickerCallBack)
208 {
209     if (pickerCallBack == nullptr) {
210         HILOG_ERROR("[picker]: pickerCallBack is null");
211         return nullptr;
212     }
213     napi_value result = nullptr;
214     napi_status status = napi_generic_failure;
215     napi_create_object(env, &result);
216 
217     const int32_t resCode = pickerCallBack->resultCode;
218     HILOG_INFO("[picker]: resCode is %{public}d.", resCode);
219     napi_value resultCode = nullptr;
220     napi_create_int32(env, resCode, &resultCode);
221     status = napi_set_named_property(env, result, "resultCode", resultCode);
222     if (status != napi_ok) {
223         HILOG_ERROR("[picker]: napi_set_named_property resultCode failed");
224     }
225     MakeResultWithArr(env, "ability.params.stream", result, pickerCallBack);
226 #ifdef UDMF_ENABLED
227     MakeResultWithUdkey(env, "ability.params.udkey", result, pickerCallBack);
228 #endif
229     MakeResultWithArr(env, "uriArr", result, pickerCallBack);
230     MakeResultWithArr(env, "select-item-list", result, pickerCallBack);
231     MakeResultWithBool(env, "isOriginal", result, pickerCallBack);
232     MakeResultWithInt(env, "userSuffixIndex", result, pickerCallBack);
233     return result;
234 }
235 
StartModalPickerAsyncCallbackComplete(napi_env env,napi_status status,void * data)236 static void StartModalPickerAsyncCallbackComplete(napi_env env, napi_status status, void *data)
237 {
238     HILOG_INFO("[picker]: StartModalPickerAsyncCallbackComplete begin.");
239     auto *context = static_cast<PickerAsyncContext*>(data);
240     if (context == nullptr) {
241         HILOG_ERROR("Async context is null");
242         return;
243     }
244     auto jsContext = make_unique<JSAsyncContextOutput>();
245     jsContext->status = false;
246     status = napi_get_undefined(env, &jsContext->data);
247     if (status != napi_ok) {
248         HILOG_ERROR("[picker]: napi_get_undefined jsContext->data failed");
249     }
250     status = napi_get_undefined(env, &jsContext->error);
251     if (status != napi_ok) {
252         HILOG_ERROR("[picker]: napi_get_undefined jsContext->error failed");
253     }
254     napi_value result = MakeResultWithPickerCallBack(env, context->pickerCallBack);
255     if (result != nullptr) {
256         jsContext->data = result;
257         jsContext->status = true;
258     } else {
259         PickerNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_MEM_ALLOCATION,
260             "failed to create js object");
261     }
262     if (context->work != nullptr) {
263         PickerNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
264             context->work, *jsContext);
265     }
266     delete context;
267     if (window_) {
268         window_ = nullptr;
269     }
270 }
271 
IsTypeRight(napi_env env,napi_value val,napi_valuetype type)272 static bool IsTypeRight(napi_env env, napi_value val, napi_valuetype type)
273 {
274     napi_valuetype valueType;
275     napi_status status = napi_typeof(env, val, &valueType);
276     if (status != napi_ok || valueType != type) {
277         HILOG_ERROR("[picker] Type is not right, type: %{public}d", valueType);
278         return false;
279     }
280     return true;
281 }
282 
GetWindowName(napi_env env,napi_value properties,sptr<Rosen::Window> & window)283 static ErrCode GetWindowName(napi_env env, napi_value properties, sptr<Rosen::Window> &window)
284 {
285     HILOG_INFO("[picker] Begin GetWindowName.");
286     napi_value name;
287     napi_status status = napi_get_named_property(env, properties, "name", &name);
288     if (status != napi_ok) {
289         HILOG_ERROR("Get name from properties fail.");
290         return ERR_INV;
291     }
292     size_t nameLen;
293     status = napi_get_value_string_utf8(env, name, NULL, 0, &nameLen);
294     if (status != napi_ok) {
295         HILOG_ERROR("[picker] Get window name length fail.");
296         return ERR_INV;
297     }
298     char *nameBuf = new char[nameLen + 1];
299     status = napi_get_value_string_utf8(env, name, nameBuf, nameLen + 1, &nameLen);
300     if (status != napi_ok) {
301         HILOG_ERROR("[picker] Get value string UTF8 fail.");
302         delete[] nameBuf;
303         return ERR_INV;
304     }
305     HILOG_INFO("[picker] Get window name: %{public}s", nameBuf);
306     auto customWindow = Rosen::Window::Find(nameBuf);
307     if (!customWindow) {
308         HILOG_ERROR("[picker] Window find fail.");
309         delete[] nameBuf;
310         return ERR_INV;
311     }
312     window = customWindow;
313     HILOG_INFO("[picker] Window found: %{public}s", nameBuf);
314     delete[] nameBuf;
315     return ERR_OK;
316 }
317 
318 template <class AsyncContext>
GetCustomShowingWindow(napi_env env,AsyncContext & asyncContext,const napi_callback_info info,sptr<Rosen::Window> & window)319 static ErrCode GetCustomShowingWindow(napi_env env, AsyncContext &asyncContext,
320     const napi_callback_info info, sptr<Rosen::Window> &window)
321 {
322     HILOG_INFO("[picker] GetCustomShowingWindow enter.");
323     if (!asyncContext) {
324         HILOG_ERROR("[picker] asyncContext is nullptr.");
325         return ERR_INV;
326     }
327     napi_status status;
328     if (!IsTypeRight(env, asyncContext->argv[ARGS_TWO], napi_object)) {
329         HILOG_ERROR("[picker] The type of the parameter transferred to the window is not object.");
330         return ERR_INV;
331     }
332     auto windowObj = asyncContext->argv[ARGS_TWO];
333     napi_value getPropertiesFunc;
334     status = napi_get_named_property(env, windowObj, "getWindowProperties", &getPropertiesFunc);
335     if (status != napi_ok || !getPropertiesFunc) {
336         HILOG_ERROR("[picker] getWindowProperties fail.");
337         return ERR_INV;
338     }
339     if (!IsTypeRight(env, getPropertiesFunc, napi_function)) {
340         HILOG_ERROR("[picker] The type of the parameter transferred to the getPropertiesFunc is not function.");
341         return ERR_INV;
342     }
343     napi_value properties;
344     status = napi_call_function(env, windowObj, getPropertiesFunc, 0, nullptr, &properties);
345     if (status != napi_ok || !properties) {
346         HILOG_INFO("[picker] Call getPropertiesFunc fail.");
347         return ERR_INV;
348     }
349     return GetWindowName(env, properties, window);
350 }
351 
GetUIContent(napi_env env,napi_callback_info info,unique_ptr<PickerAsyncContext> & AsyncContext)352 Ace::UIContent *GetUIContent(napi_env env, napi_callback_info info,
353     unique_ptr<PickerAsyncContext> &AsyncContext)
354 {
355     if (!AsyncContext) {
356         HILOG_ERROR("[picker] asyncContext is nullptr.");
357         return nullptr;
358     }
359     bool isStageMode = false;
360     napi_status status = AbilityRuntime::IsStageContext(env, AsyncContext->argv[ARGS_ZERO], isStageMode);
361     if (status != napi_ok || !isStageMode) {
362         HILOG_ERROR("[picker]: is not StageMode context");
363         return nullptr;
364     }
365     auto context = AbilityRuntime::GetStageModeContext(env, AsyncContext->argv[ARGS_ZERO]);
366     if (context == nullptr) {
367         HILOG_ERROR("[picker]: Failed to get native stage context instance");
368         return nullptr;
369     }
370     auto abilityContext = AbilityRuntime::Context::ConvertTo<AbilityRuntime::AbilityContext>(context);
371     if (abilityContext == nullptr) {
372         auto uiExtensionContext = AbilityRuntime::Context::ConvertTo<AbilityRuntime::UIExtensionContext>(context);
373         if (uiExtensionContext == nullptr) {
374             HILOG_ERROR("[picker]: Fail to convert to abilityContext or uiExtensionContext");
375             return nullptr;
376         }
377         return uiExtensionContext->GetUIContent();
378     }
379     return abilityContext->GetUIContent();
380 }
381 
StartPickerExtension(napi_env env,napi_callback_info info,unique_ptr<PickerAsyncContext> & asyncContext)382 static napi_value StartPickerExtension(napi_env env, napi_callback_info info,
383     unique_ptr<PickerAsyncContext> &asyncContext)
384 {
385     HILOG_INFO("[picker]: StartPickerExtension begin.");
386     Ace::UIContent *uiContent;
387     if (asyncContext->argc == ARGS_THREE && window_) {
388         HILOG_INFO("[picker] Will get uiContent by window.");
389         uiContent = window_->GetUIContent();
390     } else {
391         HILOG_INFO("[picker] Will get uiContent by context.");
392         uiContent= GetUIContent(env, info, asyncContext);
393     }
394 
395     if (uiContent == nullptr) {
396         HILOG_ERROR("[picker]: get uiContent failed");
397         return nullptr;
398     }
399     AAFwk::Want request;
400     AppExecFwk::UnwrapWant(env, asyncContext->argv[ARGS_ONE], request);
401 
402     std::string targetType = request.GetStringParam("extType");
403     std::string pickerType;
404     if (request.GetParams().HasParam("pickerType")) {
405         pickerType = request.GetStringParam("pickerType");
406     }
407     request.SetParam(ABILITY_WANT_PARAMS_UIEXTENSIONTARGETTYPE, targetType);
408     asyncContext->pickerCallBack = make_shared<PickerCallBack>();
409     auto callback = std::make_shared<ModalUICallback>(uiContent, asyncContext->pickerCallBack);
410     Ace::ModalUIExtensionCallbacks extensionCallback = {
411         .onRelease = std::bind(&ModalUICallback::OnRelease, callback, std::placeholders::_1),
412         .onResult = std::bind(&ModalUICallback::OnResultForModal, callback, std::placeholders::_1,
413             std::placeholders::_2),
414         .onReceive = std::bind(&ModalUICallback::OnReceive, callback, std::placeholders::_1),
415         .onError = std::bind(&ModalUICallback::OnError, callback, std::placeholders::_1, std::placeholders::_2,
416             std::placeholders::_3),
417         .onDestroy = std::bind(&ModalUICallback::OnDestroy, callback),
418     };
419     Ace::ModalUIExtensionConfig config;
420     HILOG_INFO("[picker]: will CreateModalUIExtension by extType: %{public}s, pickerType: %{public}s",
421         targetType.c_str(), pickerType.c_str());
422     int sessionId = uiContent->CreateModalUIExtension(request, extensionCallback, config);
423     if (sessionId == 0) {
424         HILOG_ERROR("[picker]: create modalUIExtension failed");
425         return nullptr;
426     }
427     callback->SetSessionId(sessionId);
428     napi_value result = nullptr;
429     napi_get_boolean(env, true, &result);
430     return result;
431 }
432 
433 template <class AsyncContext>
AsyncContextSetStaticObjectInfo(napi_env env,napi_callback_info info,AsyncContext & asyncContext,const size_t minArgs,const size_t maxArgs)434 static napi_status AsyncContextSetStaticObjectInfo(napi_env env, napi_callback_info info,
435     AsyncContext &asyncContext, const size_t minArgs, const size_t maxArgs)
436 {
437     HILOG_INFO("[picker]: AsyncContextSetStaticObjectInfo begin.");
438     if (!asyncContext) {
439         HILOG_ERROR("[picker] asyncContext is nullptr.");
440         return napi_invalid_arg;
441     }
442     napi_value thisVar = nullptr;
443     asyncContext->argc = maxArgs;
444     napi_status ret = napi_get_cb_info(env, info, &asyncContext->argc, &(asyncContext->argv[ARGS_ZERO]),
445         &thisVar, nullptr);
446     if (ret != napi_ok) {
447         HILOG_ERROR("[picker]: Failed to get cb info");
448         return ret;
449     }
450     if (asyncContext->argc == ARGS_THREE) {
451         int res = GetCustomShowingWindow(env, asyncContext, info, window_);
452         if (res != ERR_OK) {
453             HILOG_ERROR("[picker] Failed to get cb window_ info.");
454             return napi_invalid_arg;
455         }
456     }
457     if (!((asyncContext->argc >= minArgs) && (asyncContext->argc <= maxArgs))) {
458         HILOG_ERROR("[picker]: Number of args is invalid");
459         return napi_invalid_arg;
460     }
461     if (minArgs > 0) {
462         if (asyncContext->argv[ARGS_ZERO] == nullptr) {
463             HILOG_ERROR("[picker]: Argument list is empty");
464             return napi_invalid_arg;
465         }
466     }
467     return napi_ok;
468 }
469 
ParseArgsStartModalPicker(napi_env env,napi_callback_info info,unique_ptr<PickerAsyncContext> & context)470 static napi_value ParseArgsStartModalPicker(napi_env env, napi_callback_info info,
471     unique_ptr<PickerAsyncContext> &context)
472 {
473     HILOG_INFO("[picker]: ParseArgsStartModalPicker begin.");
474     constexpr size_t minArgs = ARGS_TWO;
475     constexpr size_t maxArgs = ARGS_THREE;
476     napi_status status = AsyncContextSetStaticObjectInfo(env, info, context, minArgs, maxArgs);
477     if (status != napi_ok) {
478         HILOG_ERROR("[picker]: AsyncContextSetStaticObjectInfo faild");
479         return nullptr;
480     }
481     napi_value result = nullptr;
482     napi_value ret = StartPickerExtension(env, info, context);
483     if (ret == nullptr) {
484         return nullptr;
485     }
486     napi_get_boolean(env, true, &result);
487     return result;
488 }
489 
StartModalPicker(napi_env env,napi_callback_info info)490 napi_value PickerNExporter::StartModalPicker(napi_env env, napi_callback_info info)
491 {
492     HILOG_INFO("[picker]: StartModalPicker begin.");
493     unique_ptr<PickerAsyncContext> asyncContext = make_unique<PickerAsyncContext>();
494     napi_value ret = ParseArgsStartModalPicker(env, info, asyncContext);
495     if (ret == nullptr) {
496         return nullptr;
497     }
498     return PickerNapiUtils::NapiCreateAsyncWork(env, asyncContext, "StrartModalPicker",
499         StartModalPickerExecute, StartModalPickerAsyncCallbackComplete);
500 }
501 
PickerNExporter(napi_env env,napi_value exports)502 PickerNExporter::PickerNExporter(napi_env env, napi_value exports) : NExporter(env, exports)
503 {
504     HILOG_INFO("PickerNExporter is called.");
505 }
506 
~PickerNExporter()507 PickerNExporter::~PickerNExporter() {}
508 } // namespace Picker
509 } // namespace OHOS
510