• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2023 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 
16 #include "js_pip_window_manager.h"
17 
18 #include "js_pip_controller.h"
19 #include "js_pip_utils.h"
20 #include "js_runtime_utils.h"
21 #include "window_manager_hilog.h"
22 #include "window.h"
23 #include "xcomponent_controller.h"
24 
25 namespace OHOS {
26 namespace Rosen {
27 using namespace AbilityRuntime;
28 using namespace Ace;
29 namespace {
30     constexpr uint32_t MAX_CONTROL_GROUP_NUM = 3;
31     const std::set<PiPControlGroup> VIDEO_PLAY_CONTROLS {
32         PiPControlGroup::VIDEO_PREVIOUS_NEXT,
33         PiPControlGroup::FAST_FORWARD_BACKWARD,
34     };
35     const std::set<PiPControlGroup> VIDEO_CALL_CONTROLS {
36         PiPControlGroup::VIDEO_CALL_MICROPHONE_SWITCH,
37         PiPControlGroup::VIDEO_CALL_HANG_UP_BUTTON,
38         PiPControlGroup::VIDEO_CALL_CAMERA_SWITCH,
39         PiPControlGroup::VIDEO_CALL_MUTE_SWITCH,
40     };
41     const std::set<PiPControlGroup> VIDEO_MEETING_CONTROLS {
42         PiPControlGroup::VIDEO_MEETING_HANG_UP_BUTTON,
43         PiPControlGroup::VIDEO_MEETING_CAMERA_SWITCH,
44         PiPControlGroup::VIDEO_MEETING_MUTE_SWITCH,
45         PiPControlGroup::VIDEO_MEETING_MICROPHONE_SWITCH,
46     };
47     const std::set<PiPControlGroup> VIDEO_LIVE_CONTROLS {
48         PiPControlGroup::VIDEO_PLAY_PAUSE,
49         PiPControlGroup::VIDEO_LIVE_MUTE_SWITCH,
50     };
51     const std::map<PiPTemplateType, std::set<PiPControlGroup>> TEMPLATE_CONTROL_MAP {
52         {PiPTemplateType::VIDEO_PLAY, VIDEO_PLAY_CONTROLS},
53         {PiPTemplateType::VIDEO_CALL, VIDEO_CALL_CONTROLS},
54         {PiPTemplateType::VIDEO_MEETING, VIDEO_MEETING_CONTROLS},
55         {PiPTemplateType::VIDEO_LIVE, VIDEO_LIVE_CONTROLS},
56     };
57 }
58 
checkControlsRules(uint32_t pipTemplateType,std::vector<std::uint32_t> controlGroups)59 static int32_t checkControlsRules(uint32_t pipTemplateType, std::vector<std::uint32_t> controlGroups)
60 {
61     auto iter = TEMPLATE_CONTROL_MAP.find(static_cast<PiPTemplateType>(pipTemplateType));
62     auto controls = iter->second;
63     if (controlGroups.size() > MAX_CONTROL_GROUP_NUM) {
64         return -1;
65     }
66     for (auto control : controlGroups) {
67         if (controls.find(static_cast<PiPControlGroup>(control)) == controls.end()) {
68             TLOGE(WmsLogTag::WMS_PIP, "pipOption param error, controlGroup not matches, controlGroup: %{public}u",
69                 control);
70             return -1;
71         }
72     }
73     if (pipTemplateType == static_cast<uint32_t>(PiPTemplateType::VIDEO_PLAY)) {
74         auto iterFirst = std::find(controlGroups.begin(), controlGroups.end(),
75             static_cast<uint32_t>(PiPControlGroup::VIDEO_PREVIOUS_NEXT));
76         auto iterSecond = std::find(controlGroups.begin(), controlGroups.end(),
77             static_cast<uint32_t>(PiPControlGroup::FAST_FORWARD_BACKWARD));
78         if (iterFirst != controlGroups.end() && iterSecond != controlGroups.end()) {
79             TLOGE(WmsLogTag::WMS_PIP, "pipOption param error, %{public}u conflicts with %{public}u in controlGroups",
80                 static_cast<uint32_t>(PiPControlGroup::VIDEO_PREVIOUS_NEXT),
81                 static_cast<uint32_t>(PiPControlGroup::FAST_FORWARD_BACKWARD));
82             return -1;
83         }
84     }
85     return 0;
86 }
87 
checkLocalStorage(napi_env env,PipOption & option,napi_value storage)88 static void checkLocalStorage(napi_env env, PipOption& option, napi_value storage)
89 {
90     napi_valuetype valueType;
91     napi_typeof(env, storage, &valueType);
92     if (valueType != napi_object) {
93         option.SetStorageRef(nullptr);
94     } else {
95         napi_ref storageRef;
96         napi_create_reference(env, storage, 1, &storageRef);
97         option.SetStorageRef(storageRef);
98         TLOGI(WmsLogTag::WMS_PIP, "localStorage is set");
99     }
100 }
101 
checkOptionParams(PipOption & option)102 static int32_t checkOptionParams(PipOption& option)
103 {
104     if (option.GetContext() == nullptr) {
105         TLOGE(WmsLogTag::WMS_PIP, "pipOption param error, context is nullptr.");
106         return -1;
107     }
108     if (option.GetXComponentController() == nullptr) {
109         TLOGE(WmsLogTag::WMS_PIP, "pipOption param error, XComponentController is nullptr.");
110         return -1;
111     }
112     uint32_t pipTemplateType = option.GetPipTemplate();
113     if (TEMPLATE_CONTROL_MAP.find(static_cast<PiPTemplateType>(pipTemplateType)) ==
114         TEMPLATE_CONTROL_MAP.end()) {
115         TLOGE(WmsLogTag::WMS_PIP, "pipOption param error, pipTemplateType not exists.");
116         return -1;
117     }
118     uint32_t defaultWindowSizeType = option.GetDefaultWindowSizeType();
119     if (!(defaultWindowSizeType == DefaultWindowSizeType::NOT_SET ||
120             defaultWindowSizeType == DefaultWindowSizeType::SMALL ||
121             defaultWindowSizeType == DefaultWindowSizeType::LARGE)) {
122         TLOGE(WmsLogTag::WMS_PIP, "pipOption param error, invalid defaultWindowSizeType:%{public}u",
123             defaultWindowSizeType);
124         return -1;
125     }
126     return checkControlsRules(pipTemplateType, option.GetControlGroup());
127 }
128 
GetControlGroupFromJs(napi_env env,napi_value controlGroup,std::vector<std::uint32_t> & controls,uint32_t templateType)129 static bool GetControlGroupFromJs(napi_env env, napi_value controlGroup, std::vector<std::uint32_t>& controls,
130     uint32_t templateType)
131 {
132     if (controlGroup == nullptr) {
133         return false;
134     }
135     napi_valuetype type;
136     napi_typeof(env, controlGroup, &type);
137     if (type == napi_undefined && templateType == static_cast<uint32_t>(PiPTemplateType::VIDEO_LIVE)) {
138         TLOGI(WmsLogTag::WMS_PIP, "controls is undefined");
139         controls.push_back(static_cast<uint32_t>(PiPControlGroup::VIDEO_PLAY_PAUSE));
140     }
141     uint32_t size = 0;
142     napi_get_array_length(env, controlGroup, &size);
143     for (uint32_t i = 0; i < size; i++) {
144         uint32_t controlType;
145         napi_value getElementValue = nullptr;
146         napi_get_element(env, controlGroup, i, &getElementValue);
147         if (!ConvertFromJsValue(env, getElementValue, controlType)) {
148             TLOGE(WmsLogTag::WMS_PIP, "Failed to convert parameter to controlType");
149             continue;
150         }
151         auto iter = std::find(controls.begin(), controls.end(), controlType);
152         if (iter != controls.end()) {
153             TLOGI(WmsLogTag::WMS_PIP, "The controlType already exists. controlType: %{public}u", controlType);
154         } else {
155             controls.push_back(controlType);
156         }
157     }
158     return true;
159 }
160 
GetPictureInPictureOptionFromJs(napi_env env,napi_value optionObject,PipOption & option)161 static int32_t GetPictureInPictureOptionFromJs(napi_env env, napi_value optionObject, PipOption& option)
162 {
163     napi_value contextPtrValue = nullptr;
164     napi_value navigationIdValue = nullptr;
165     napi_value templateTypeValue = nullptr;
166     napi_value widthValue = nullptr;
167     napi_value heightValue = nullptr;
168     napi_value defaultWindowSizeTypeValue = nullptr;
169     napi_value xComponentControllerValue = nullptr;
170     napi_value controlGroup = nullptr;
171     napi_value nodeController = nullptr;
172     napi_value storage = nullptr;
173     napi_ref nodeControllerRef = nullptr;
174     void* contextPtr = nullptr;
175     std::string navigationId = "";
176     uint32_t templateType = static_cast<uint32_t>(PiPTemplateType::VIDEO_PLAY);
177     uint32_t width = 0;
178     uint32_t height = 0;
179     uint32_t defaultWindowSizeType = 0;
180     std::vector<std::uint32_t> controls;
181 
182     napi_get_named_property(env, optionObject, "context", &contextPtrValue);
183     napi_get_named_property(env, optionObject, "navigationId", &navigationIdValue);
184     napi_get_named_property(env, optionObject, "templateType", &templateTypeValue);
185     napi_get_named_property(env, optionObject, "contentWidth", &widthValue);
186     napi_get_named_property(env, optionObject, "contentHeight", &heightValue);
187     napi_get_named_property(env, optionObject, "defaultWindowSizeType", &defaultWindowSizeTypeValue);
188     napi_get_named_property(env, optionObject, "componentController", &xComponentControllerValue);
189     napi_get_named_property(env, optionObject, "controlGroups", &controlGroup);
190     napi_get_named_property(env, optionObject, "customUIController", &nodeController);
191     napi_get_named_property(env, optionObject, "localStorage", &storage);
192     napi_create_reference(env, nodeController, 1, &nodeControllerRef);
193     checkLocalStorage(env, option, storage);
194     napi_unwrap(env, contextPtrValue, &contextPtr);
195     ConvertFromJsValue(env, navigationIdValue, navigationId);
196     ConvertFromJsValue(env, templateTypeValue, templateType);
197     ConvertFromJsValue(env, widthValue, width);
198     ConvertFromJsValue(env, heightValue, height);
199     ConvertFromJsValue(env, defaultWindowSizeTypeValue, defaultWindowSizeType);
200     GetControlGroupFromJs(env, controlGroup, controls, templateType);
201     std::shared_ptr<XComponentController> xComponentControllerResult =
202         XComponentController::GetXComponentControllerFromNapiValue(env, xComponentControllerValue);
203     option.SetContext(contextPtr);
204     option.SetNavigationId(navigationId);
205     option.SetPipTemplate(templateType);
206     option.SetContentSize(width, height);
207     option.SetDefaultWindowSizeType(defaultWindowSizeType);
208     option.SetControlGroup(controls);
209     option.SetXComponentController(xComponentControllerResult);
210     option.SetNodeControllerRef(nodeControllerRef);
211     return checkOptionParams(option);
212 }
213 
JsPipWindowManager()214 JsPipWindowManager::JsPipWindowManager()
215 {
216 }
217 
~JsPipWindowManager()218 JsPipWindowManager::~JsPipWindowManager()
219 {
220 }
221 
Finalizer(napi_env env,void * data,void * hint)222 void JsPipWindowManager::Finalizer(napi_env env, void* data, void* hint)
223 {
224     TLOGD(WmsLogTag::WMS_PIP, "Finalizer");
225     std::unique_ptr<JsPipWindowManager>(static_cast<JsPipWindowManager*>(data));
226 }
227 
IsPipEnabled(napi_env env,napi_callback_info info)228 napi_value JsPipWindowManager::IsPipEnabled(napi_env env, napi_callback_info info)
229 {
230     JsPipWindowManager* me = CheckParamsAndGetThis<JsPipWindowManager>(env, info);
231     return (me != nullptr) ? me->OnIsPipEnabled(env, info) : nullptr;
232 }
233 
OnIsPipEnabled(napi_env env,napi_callback_info info)234 napi_value JsPipWindowManager::OnIsPipEnabled(napi_env env, napi_callback_info info)
235 {
236     TLOGD(WmsLogTag::WMS_PIP, "OnIsPipEnabled called");
237     bool isPipEnabled = PictureInPictureManager::GetPipEnabled();
238     return CreateJsValue(env, isPipEnabled);
239 }
240 
CreatePipController(napi_env env,napi_callback_info info)241 napi_value JsPipWindowManager::CreatePipController(napi_env env, napi_callback_info info)
242 {
243     JsPipWindowManager* me = CheckParamsAndGetThis<JsPipWindowManager>(env, info);
244     return (me != nullptr) ? me->OnCreatePipController(env, info) : nullptr;
245 }
246 
CreateEmptyAsyncTask(napi_env env,napi_value * result)247 std::unique_ptr<NapiAsyncTask> JsPipWindowManager::CreateEmptyAsyncTask(napi_env env, napi_value* result)
248 {
249     napi_deferred nativeDeferred = nullptr;
250     napi_create_promise(env, &nativeDeferred, result);
251     return std::make_unique<NapiAsyncTask>(nativeDeferred, std::unique_ptr<NapiAsyncTask::ExecuteCallback>(),
252         std::unique_ptr<NapiAsyncTask::CompleteCallback>());
253 }
254 
NapiSendTask(napi_env env,PipOption & pipOption)255 napi_value JsPipWindowManager::NapiSendTask(napi_env env, PipOption& pipOption)
256 {
257     napi_value result = nullptr;
258     std::shared_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, &result);
259     auto asyncTask = [this, env, task = napiAsyncTask, pipOption]() mutable {
260         if (!PictureInPictureManager::IsSupportPiP()) {
261             pipOption.ClearNapiRefs(env);
262             task->Reject(env, CreateJsError(env, static_cast<int32_t>(
263                 WMError::WM_ERROR_DEVICE_NOT_SUPPORT), "device not support pip."));
264             return;
265         }
266         sptr<PipOption> pipOptionPtr = new PipOption(pipOption);
267         auto context = static_cast<std::weak_ptr<AbilityRuntime::Context>*>(pipOptionPtr->GetContext());
268         if (context == nullptr) {
269             pipOption.ClearNapiRefs(env);
270             task->Reject(env, CreateJsError(env, static_cast<int32_t>(
271                 WMError::WM_ERROR_PIP_INTERNAL_ERROR), "Invalid context"));
272             return;
273         }
274         sptr<Window> mainWindow = Window::GetMainWindowWithContext(context->lock());
275         if (mainWindow == nullptr) {
276             pipOption.ClearNapiRefs(env);
277             task->Reject(env, CreateJsError(env, static_cast<int32_t>(
278                 WMError::WM_ERROR_PIP_INTERNAL_ERROR), "Invalid mainWindow"));
279             return;
280         }
281         sptr<PictureInPictureController> pipController =
282             new PictureInPictureController(pipOptionPtr, mainWindow, mainWindow->GetWindowId(), env);
283         task->Resolve(env, CreateJsPipControllerObject(env, pipController));
284         PiPTemplateInfo pipTemplateInfo;
285         pipOption.GetPiPTemplateInfo(pipTemplateInfo);
286         mainWindow->UpdatePiPTemplateInfo(pipTemplateInfo);
287     };
288     if (napi_send_event(env, asyncTask, napi_eprio_immediate, "NapiSendTask") != napi_status::napi_ok) {
289         pipOption.ClearNapiRefs(env);
290         napiAsyncTask->Reject(env, CreateJsError(env,
291             static_cast<int32_t>(WMError::WM_ERROR_PIP_INTERNAL_ERROR), "Send event failed"));
292     }
293     return result;
294 }
295 
OnCreatePipController(napi_env env,napi_callback_info info)296 napi_value JsPipWindowManager::OnCreatePipController(napi_env env, napi_callback_info info)
297 {
298     TLOGI(WmsLogTag::WMS_PIP, "[NAPI]");
299     size_t argc = 4;
300     napi_value argv[4] = {nullptr};
301     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
302     if (argc < 1) {
303         return NapiThrowInvalidParam(env, "Missing args when creating pipController");
304     }
305     napi_value config = argv[0];
306     if (config == nullptr) {
307         TLOGE(WmsLogTag::WMS_PIP, "config is null");
308         return NapiThrowInvalidParam(env, "Failed to convert object to pipConfiguration or pipConfiguration is null");
309     }
310     PipOption pipOption;
311     if (GetPictureInPictureOptionFromJs(env, config, pipOption) == -1) {
312         std::string errMsg = "Invalid parameters in config, please check if context/xComponentController is null,"
313             " or controlGroup mismatch the corresponding pipTemplateType, or defaultWindowSizeType is invalid";
314         TLOGE(WmsLogTag::WMS_PIP, "%{public}s", errMsg.c_str());
315         pipOption.ClearNapiRefs(env);
316         return NapiThrowInvalidParam(env, errMsg);
317     }
318     if (argc > 1) {
319         napi_value typeNode = argv[1];
320         napi_ref typeNodeRef = nullptr;
321         if (typeNode != nullptr && GetType(env, typeNode) != napi_undefined) {
322             TLOGI(WmsLogTag::WMS_PIP, "typeNode enabled");
323             pipOption.SetTypeNodeEnabled(true);
324             napi_create_reference(env, typeNode, 1, &typeNodeRef);
325             pipOption.SetTypeNodeRef(typeNodeRef);
326         } else {
327             pipOption.SetTypeNodeEnabled(false);
328             pipOption.SetTypeNodeRef(nullptr);
329         }
330     }
331     return NapiSendTask(env, pipOption);
332 }
333 
JsPipWindowManagerInit(napi_env env,napi_value exportObj)334 napi_value JsPipWindowManagerInit(napi_env env, napi_value exportObj)
335 {
336     TLOGD(WmsLogTag::WMS_PIP, "JsPipWindowManagerInit");
337     if (env == nullptr || exportObj == nullptr) {
338         TLOGE(WmsLogTag::WMS_PIP, "JsPipWindowManagerInit env or exportObj is nullptr");
339         return nullptr;
340     }
341     std::unique_ptr<JsPipWindowManager> jsPipManager = std::make_unique<JsPipWindowManager>();
342     napi_wrap(env, exportObj, jsPipManager.release(), JsPipWindowManager::Finalizer, nullptr, nullptr);
343     const char* moduleName = "JsPipWindowManager";
344     BindNativeFunction(env, exportObj, "create", moduleName, JsPipWindowManager::CreatePipController);
345     BindNativeFunction(env, exportObj, "isPiPEnabled", moduleName, JsPipWindowManager::IsPipEnabled);
346     InitEnums(env, exportObj);
347     return NapiGetUndefined(env);
348 }
349 }
350 }