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