• 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 
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