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