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