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