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