• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 "bridge/declarative_frontend/jsview/dialog/js_custom_dialog_controller.h"
17 
18 #include "base/subwindow/subwindow_manager.h"
19 #include "base/utils/system_properties.h"
20 #include "base/utils/utils.h"
21 #include "bridge/declarative_frontend/engine/jsi/jsi_types.h"
22 #include "bridge/declarative_frontend/jsview/models/custom_dialog_controller_model_impl.h"
23 #include "core/common/ace_engine.h"
24 #include "core/common/container.h"
25 #include "core/components_ng/base/view_stack_processor.h"
26 #include "core/components_ng/pattern/dialog/custom_dialog_controller_model_ng.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 #include "frameworks/bridge/common/utils/engine_helper.h"
29 #include "frameworks/bridge/declarative_frontend/jsview/js_view_abstract.h"
30 #include "frameworks/bridge/declarative_frontend/engine/jsi/js_ui_index.h"
31 
32 namespace OHOS::Ace {
33 std::unique_ptr<CustomDialogControllerModel> CustomDialogControllerModel::instance_ = nullptr;
34 std::mutex CustomDialogControllerModel::mutex_;
GetInstance()35 CustomDialogControllerModel* CustomDialogControllerModel::GetInstance()
36 {
37     if (!instance_) {
38         std::lock_guard<std::mutex> lock(mutex_);
39         if (!instance_) {
40 #ifdef NG_BUILD
41             instance_.reset(new NG::CustomDialogControllerModelNG());
42 #else
43             if (Container::IsCurrentUseNewPipeline()) {
44                 instance_.reset(new NG::CustomDialogControllerModelNG());
45             } else {
46                 instance_.reset(new Framework::CustomDialogControllerModelImpl());
47             }
48 #endif
49         }
50     }
51     return instance_.get();
52 }
53 } // namespace OHOS::Ace
54 
55 namespace OHOS::Ace::Framework {
56 namespace {
57 const std::vector<DialogAlignment> DIALOG_ALIGNMENT = { DialogAlignment::TOP, DialogAlignment::CENTER,
58     DialogAlignment::BOTTOM, DialogAlignment::DEFAULT, DialogAlignment::TOP_START, DialogAlignment::TOP_END,
59     DialogAlignment::CENTER_START, DialogAlignment::CENTER_END, DialogAlignment::BOTTOM_START,
60     DialogAlignment::BOTTOM_END };
61 const std::vector<KeyboardAvoidMode> KEYBOARD_AVOID_MODE = { KeyboardAvoidMode::DEFAULT, KeyboardAvoidMode::NONE };
62 const std::vector<LevelMode> DIALOG_LEVEL_MODE = { LevelMode::OVERLAY, LevelMode::EMBEDDED };
63 const std::vector<ImmersiveMode> DIALOG_IMMERSIVE_MODE = { ImmersiveMode::DEFAULT, ImmersiveMode::EXTEND};
64 constexpr int32_t DEFAULT_ANIMATION_DURATION = 200;
65 constexpr float DEFAULT_AVOID_DISTANCE = 16.0f;
66 
67 } // namespace
68 
ConstructorCallback(const JSCallbackInfo & info)69 void JSCustomDialogController::ConstructorCallback(const JSCallbackInfo& info)
70 {
71     int argc = info.Length();
72     if (argc > 1 && !info[0]->IsUndefined() && info[0]->IsObject() && !info[1]->IsUndefined() && info[1]->IsObject()) {
73         JSRef<JSObject> constructorArg = JSRef<JSObject>::Cast(info[0]);
74         JSRef<JSObject> ownerObj = JSRef<JSObject>::Cast(info[1]);
75 
76         // check if owner object is set
77         JSView* ownerView = ownerObj->Unwrap<JSView>();
78         auto instance = AceType::MakeRefPtr<JSCustomDialogController>(ownerView);
79         if (ownerView == nullptr) {
80             instance->IncRefCount();
81             info.SetReturnValue(AceType::RawPtr(instance));
82             instance = nullptr;
83             return;
84         }
85 
86         // Process builder function.
87         JSRef<JSVal> builderCallback = constructorArg->GetProperty("builder");
88         if (!builderCallback->IsUndefined() && builderCallback->IsFunction()) {
89             instance->jsBuilderFunction_ =
90                 AceType::MakeRefPtr<JsWeakFunction>(ownerObj, JSRef<JSFunc>::Cast(builderCallback));
91         } else {
92             instance->jsBuilderFunction_ = nullptr;
93             instance->IncRefCount();
94             info.SetReturnValue(AceType::RawPtr(instance));
95             instance = nullptr;
96             return;
97         }
98 
99         // Process cancel function.
100         JSRef<JSVal> cancelCallback = constructorArg->GetProperty("cancel");
101         if (!cancelCallback->IsUndefined() && cancelCallback->IsFunction()) {
102             auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
103             auto jsCancelFunction = AceType::MakeRefPtr<JsWeakFunction>(ownerObj, JSRef<JSFunc>::Cast(cancelCallback));
104             instance->jsCancelFunction_ = jsCancelFunction;
105 
106             auto onCancel = [execCtx = info.GetExecutionContext(), func = std::move(jsCancelFunction),
107                                 node = frameNode]() {
108                 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
109                 ACE_SCORING_EVENT("onCancel");
110                 auto pipelineContext = PipelineContext::GetCurrentContext();
111                 CHECK_NULL_VOID(pipelineContext);
112                 pipelineContext->UpdateCurrentActiveNode(node);
113                 func->Execute();
114             };
115             instance->dialogProperties_.onCancel = onCancel;
116         }
117 
118         std::function<void(const int32_t& info)> onWillDismissFunc = nullptr;
119         JSViewAbstract::ParseDialogCallback(constructorArg, onWillDismissFunc);
120         instance->dialogProperties_.onWillDismiss = onWillDismissFunc;
121 
122         // Parses autoCancel.
123         JSRef<JSVal> autoCancelValue = constructorArg->GetProperty("autoCancel");
124         if (autoCancelValue->IsBoolean()) {
125             instance->dialogProperties_.autoCancel = autoCancelValue->ToBoolean();
126         }
127 
128         // Parses customStyle.
129         JSRef<JSVal> customStyleValue = constructorArg->GetProperty("customStyle");
130         if (customStyleValue->IsBoolean()) {
131             instance->dialogProperties_.customStyle = customStyleValue->ToBoolean();
132         }
133 
134         // Parse alignment
135         auto alignmentValue = constructorArg->GetProperty("alignment");
136         if (alignmentValue->IsNumber()) {
137             auto alignment = alignmentValue->ToNumber<int32_t>();
138             if (alignment >= 0 && alignment <= static_cast<int32_t>(DIALOG_ALIGNMENT.size())) {
139                 instance->dialogProperties_.alignment = DIALOG_ALIGNMENT[alignment];
140             }
141         }
142 
143         // Parse keyboardAvoidMode
144         auto avoidModeValue = constructorArg->GetProperty("keyboardAvoidMode");
145         if (avoidModeValue->IsNumber()) {
146             auto avoidMode = avoidModeValue->ToNumber<int32_t>();
147             if (avoidMode >= 0 && avoidMode < static_cast<int32_t>(KEYBOARD_AVOID_MODE.size())) {
148                 instance->dialogProperties_.keyboardAvoidMode = KEYBOARD_AVOID_MODE[avoidMode];
149             }
150         }
151 
152         // Parse keyboardAvoidDistance
153         auto avoidDistance = constructorArg->GetProperty("keyboardAvoidDistance");
154         if (avoidDistance->IsObject()) {
155             JSRef<JSObject> avoidDistanceobj = JSRef<JSObject>::Cast(avoidDistance);
156             auto avoidDisValue = avoidDistanceobj->GetProperty(static_cast<int32_t>(ArkUIIndex::VALUE));
157             auto jsAvoidDisUnit = avoidDistanceobj->GetProperty(static_cast<int32_t>(ArkUIIndex::UNIT));
158             DimensionUnit avoidDisUnit = OHOS::Ace::DimensionUnit::VP;
159             if (jsAvoidDisUnit->IsNumber()) {
160                 avoidDisUnit = static_cast<DimensionUnit>(jsAvoidDisUnit->ToNumber<int32_t>());
161             }
162             if (avoidDisValue->IsNumber() && avoidDisValue->ToNumber<double>() >= 0 &&
163                 avoidDisUnit >= OHOS::Ace::DimensionUnit::PX && avoidDisUnit <= OHOS::Ace::DimensionUnit::CALC &&
164                 avoidDisUnit != OHOS::Ace::DimensionUnit::PERCENT) {
165                 Dimension avoidDistanceDimension(avoidDisValue->ToNumber<double>(), avoidDisUnit);
166                 instance->dialogProperties_.keyboardAvoidDistance = avoidDistanceDimension;
167             } else {
168                 Dimension avoidDistanceDimension(DEFAULT_AVOID_DISTANCE, OHOS::Ace::DimensionUnit::VP);
169                 instance->dialogProperties_.keyboardAvoidDistance = avoidDistanceDimension;
170             }
171         }
172 
173         // Parse offset
174         auto offsetValue = constructorArg->GetProperty("offset");
175         if (offsetValue->IsObject()) {
176             auto offsetObj = JSRef<JSObject>::Cast(offsetValue);
177             CalcDimension dx;
178             auto dxValue = offsetObj->GetProperty("dx");
179             JSViewAbstract::ParseJsDimensionVp(dxValue, dx);
180             CalcDimension dy;
181             auto dyValue = offsetObj->GetProperty("dy");
182             JSViewAbstract::ParseJsDimensionVp(dyValue, dy);
183             dx.ResetInvalidValue();
184             dy.ResetInvalidValue();
185             instance->dialogProperties_.offset = DimensionOffset(dx, dy);
186         }
187 
188         // Parses gridCount.
189         auto gridCountValue = constructorArg->GetProperty("gridCount");
190         if (gridCountValue->IsNumber()) {
191             instance->dialogProperties_.gridCount = gridCountValue->ToNumber<int32_t>();
192         }
193 
194         // Parse maskColor.
195         auto maskColorValue = constructorArg->GetProperty("maskColor");
196         Color maskColor;
197         if (JSViewAbstract::ParseJsColor(maskColorValue, maskColor)) {
198             instance->dialogProperties_.maskColor = maskColor;
199         }
200 
201         // Parse maskRect.
202         auto maskRectValue = constructorArg->GetProperty("maskRect");
203         DimensionRect maskRect;
204         if (JSViewAbstract::ParseJsDimensionRect(maskRectValue, maskRect)) {
205             instance->dialogProperties_.maskRect = maskRect;
206         }
207 
208         // Parse backgroundColor.
209         auto backgroundColorValue = constructorArg->GetProperty("backgroundColor");
210         Color backgroundColor;
211         if (JSViewAbstract::ParseJsColor(backgroundColorValue, backgroundColor)) {
212             instance->dialogProperties_.backgroundColor = backgroundColor;
213         }
214 
215         // Parse backgroundBlurStyle.
216         auto backgroundBlurStyle = constructorArg->GetProperty("backgroundBlurStyle");
217         if (backgroundBlurStyle->IsNumber()) {
218             auto blurStyle = backgroundBlurStyle->ToNumber<int32_t>();
219             if (blurStyle >= static_cast<int>(BlurStyle::NO_MATERIAL) &&
220                 blurStyle <= static_cast<int>(BlurStyle::COMPONENT_ULTRA_THICK)) {
221                 instance->dialogProperties_.backgroundBlurStyle = blurStyle;
222             }
223         }
224 
225         auto execContext = info.GetExecutionContext();
226         // Parse openAnimation.
227         auto openAnimationValue = constructorArg->GetProperty("openAnimation");
228         AnimationOption openAnimation;
229         if (ParseAnimation(execContext, openAnimationValue, openAnimation)) {
230             instance->dialogProperties_.openAnimation = openAnimation;
231         }
232 
233         // Parse closeAnimation.
234         auto closeAnimationValue = constructorArg->GetProperty("closeAnimation");
235         AnimationOption closeAnimation;
236         if (ParseAnimation(execContext, closeAnimationValue, closeAnimation)) {
237             instance->dialogProperties_.closeAnimation = closeAnimation;
238         }
239 
240         // Parse showInSubWindowValue.
241         auto showInSubWindowValue = constructorArg->GetProperty("showInSubWindow");
242         if (showInSubWindowValue->IsBoolean()) {
243 #if defined(PREVIEW)
244             LOGW("[Engine Log] Unable to use the SubWindow in the Previewer. Perform this operation on the "
245                  "emulator or a real device instead.");
246 #else
247             instance->dialogProperties_.isShowInSubWindow = showInSubWindowValue->ToBoolean();
248 #endif
249         }
250 
251         auto dialogLevelMode = constructorArg->GetProperty("levelMode");
252         if (dialogLevelMode->IsNumber() && !instance->dialogProperties_.isShowInSubWindow) {
253             auto levelMode = dialogLevelMode->ToNumber<int32_t>();
254             if (levelMode >= 0 && levelMode < static_cast<int32_t>(DIALOG_LEVEL_MODE.size())) {
255                 instance->dialogProperties_.dialogLevelMode = DIALOG_LEVEL_MODE[levelMode];
256             }
257         }
258 
259         auto dialogLevelUniqueId = constructorArg->GetProperty("levelUniqueId");
260         if (dialogLevelUniqueId->IsNumber()) {
261             instance->dialogProperties_.dialogLevelUniqueId = dialogLevelUniqueId->ToNumber<int32_t>();
262         }
263 
264         auto immersiveMode = constructorArg->GetProperty("immersiveMode");
265         if (immersiveMode->IsNumber()) {
266             auto immersiveVal = immersiveMode->ToNumber<int32_t>();
267             if (immersiveVal >= 0 && immersiveVal < static_cast<int32_t>(DIALOG_IMMERSIVE_MODE.size())) {
268                 instance->dialogProperties_.dialogImmersiveMode = DIALOG_IMMERSIVE_MODE[immersiveVal];
269             }
270         }
271 
272         // Parse isModal.
273         auto isModalValue = constructorArg->GetProperty("isModal");
274         if (isModalValue->IsBoolean()) {
275             instance->dialogProperties_.isModal = isModalValue->ToBoolean();
276         }
277 
278         JSViewAbstract::SetDialogProperties(constructorArg, instance->dialogProperties_);
279         JSViewAbstract::SetDialogHoverModeProperties(constructorArg, instance->dialogProperties_);
280         instance->IncRefCount();
281         info.SetReturnValue(AceType::RawPtr(instance));
282     }
283 }
284 
DestructorCallback(JSCustomDialogController * controller)285 void JSCustomDialogController::DestructorCallback(JSCustomDialogController* controller)
286 {
287     if (controller != nullptr) {
288         controller->ownerView_ = nullptr;
289         controller->DecRefCount();
290     }
291 }
292 
JsOpenDialog(const JSCallbackInfo & info)293 void JSCustomDialogController::JsOpenDialog(const JSCallbackInfo& info)
294 {
295     if (!jsBuilderFunction_) {
296         return;
297     }
298 
299     if (this->ownerView_ == nullptr) {
300         return;
301     }
302     auto containerId = this->ownerView_->GetInstanceId();
303     ContainerScope containerScope(containerId);
304     WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
305     auto pipelineContext = PipelineContext::GetCurrentContext();
306     CHECK_NULL_VOID(pipelineContext);
307 
308     auto scopedDelegate = EngineHelper::GetCurrentDelegate();
309     if (!scopedDelegate) {
310         // this case usually means there is no foreground container, need to figure out the reason.
311         return;
312     }
313 
314     auto buildFunc = [buildfunc = jsBuilderFunction_, node = frameNode, context = pipelineContext]() {
315         {
316             ACE_SCORING_EVENT("CustomDialog.builder");
317             context->UpdateCurrentActiveNode(node);
318             buildfunc->Execute();
319         }
320     };
321 
322     auto cancelTask = ([cancelCallback = jsCancelFunction_, node = frameNode, context = pipelineContext]() {
323         if (cancelCallback) {
324             ACE_SCORING_EVENT("CustomDialog.cancel");
325             context->UpdateCurrentActiveNode(node);
326             cancelCallback->Execute();
327         }
328     });
329 
330     auto container = Container::Current();
331     if (container && container->IsScenceBoardWindow() && !dialogProperties_.windowScene.Upgrade()) {
332         dialogProperties_.isScenceBoardDialog = true;
333         auto viewNode = this->ownerView_->GetViewNode();
334         CHECK_NULL_VOID(viewNode);
335         auto parentCustom = AceType::DynamicCast<NG::CustomNode>(viewNode);
336         CHECK_NULL_VOID(parentCustom);
337         auto parent = parentCustom->GetParent();
338         while (parent && parent->GetTag() != V2::WINDOW_SCENE_ETS_TAG) {
339             parent = parent->GetParent();
340         }
341         if (parent) {
342             dialogProperties_.windowScene = parent;
343         }
344     }
345     dialogProperties_.isSysBlurStyle =
346         Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_TWELVE) ? true : false;
347     CustomDialogControllerModel::GetInstance()->SetOpenDialog(dialogProperties_, WeakClaim(this), dialogs_, pending_,
348         isShown_, std::move(cancelTask), std::move(buildFunc), dialogComponent_, customDialog_, dialogOperation_);
349 }
350 
JsCloseDialog(const JSCallbackInfo & info)351 void JSCustomDialogController::JsCloseDialog(const JSCallbackInfo& info)
352 {
353 
354     if (this->ownerView_ == nullptr) {
355         return;
356     }
357     auto containerId = this->ownerView_->GetInstanceId();
358     ContainerScope containerScope(containerId);
359 
360     auto scopedDelegate = EngineHelper::GetCurrentDelegate();
361     if (!scopedDelegate) {
362         // this case usually means there is no foreground container, need to figure out the reason.
363         return;
364     }
365 
366     WeakPtr<NG::FrameNode> frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
367     auto cancelTask = ([cancelCallback = jsCancelFunction_, node = frameNode]() {
368         if (cancelCallback) {
369             ACE_SCORING_EVENT("CustomDialog.cancel");
370             auto pipelineContext = PipelineContext::GetCurrentContext();
371             CHECK_NULL_VOID(pipelineContext);
372             pipelineContext->UpdateCurrentActiveNode(node);
373             cancelCallback->Execute();
374         }
375     });
376 
377     CustomDialogControllerModel::GetInstance()->SetCloseDialog(dialogProperties_, WeakClaim(this), dialogs_, pending_,
378         isShown_, std::move(cancelTask), dialogComponent_, customDialog_, dialogOperation_);
379 }
380 
ParseAnimation(const JsiExecutionContext & execContext,const JsiRef<JsiValue> & animationValue,AnimationOption & result)381 bool JSCustomDialogController::ParseAnimation(
382     const JsiExecutionContext& execContext, const JsiRef<JsiValue>& animationValue, AnimationOption& result)
383 {
384     if (animationValue->IsNull() || !animationValue->IsObject()) {
385         return false;
386     }
387 
388     JSRef<JSObject> obj = JSRef<JSObject>::Cast(animationValue);
389     // If the attribute does not exist, the default value is used.
390     int32_t duration = obj->GetPropertyValue<int32_t>("duration", DEFAULT_ANIMATION_DURATION);
391     int32_t delay = obj->GetPropertyValue<int32_t>("delay", 0);
392     int32_t iterations = obj->GetPropertyValue<int32_t>("iterations", 1);
393     float tempo = obj->GetPropertyValue<float>("tempo", 1.0);
394     auto finishCallbackType = static_cast<FinishCallbackType>(obj->GetPropertyValue<int32_t>("finishCallbackType", 0));
395     if (NonPositive(tempo)) {
396         tempo = 1.0f;
397     }
398     auto direction = StringToAnimationDirection(obj->GetPropertyValue<std::string>("playMode", "normal"));
399     RefPtr<Curve> curve;
400     JSRef<JSVal> curveArgs = obj->GetProperty("curve");
401     if (curveArgs->IsString()) {
402         curve = CreateCurve(obj->GetPropertyValue<std::string>("curve", "linear"));
403     } else if (curveArgs->IsObject()) {
404         JSRef<JSObject> curveObj = JSRef<JSObject>::Cast(curveArgs);
405         JSRef<JSVal> curveString = curveObj->GetProperty("__curveString");
406         if (!curveString->IsString()) {
407             // Default AnimationOption which is invalid.
408             return false;
409         }
410         curve = CreateCurve(curveString->ToString());
411     } else {
412         curve = Curves::EASE_IN_OUT;
413     }
414     result.SetDuration(duration);
415     result.SetDelay(delay);
416     result.SetIteration(iterations);
417     result.SetTempo(tempo);
418     result.SetAnimationDirection(direction);
419     result.SetCurve(curve);
420     result.SetFinishCallbackType(finishCallbackType);
421 
422     JSRef<JSVal> onFinish = obj->GetProperty("onFinish");
423     std::function<void()> onFinishEvent;
424     if (onFinish->IsFunction()) {
425         auto frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode());
426         auto jsFunc = AceType::MakeRefPtr<JsWeakFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(onFinish));
427         onFinishEvent = [execCtx = execContext, func = std::move(jsFunc), node = frameNode]() {
428             JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
429             ACE_SCORING_EVENT("CustomDialog.onFinish");
430             auto pipelineContext = PipelineContext::GetCurrentContext();
431             CHECK_NULL_VOID(pipelineContext);
432             pipelineContext->UpdateCurrentActiveNode(node);
433             func->Execute();
434         };
435         result.SetOnFinishEvent(onFinishEvent);
436     }
437     return true;
438 }
439 
JSBind(BindingTarget object)440 void JSCustomDialogController::JSBind(BindingTarget object)
441 {
442     JSClass<JSCustomDialogController>::Declare("NativeCustomDialogController");
443     JSClass<JSCustomDialogController>::CustomMethod("open", &JSCustomDialogController::JsOpenDialog);
444     JSClass<JSCustomDialogController>::CustomMethod("close", &JSCustomDialogController::JsCloseDialog);
445     JSClass<JSCustomDialogController>::Bind(
446         object, &JSCustomDialogController::ConstructorCallback, &JSCustomDialogController::DestructorCallback);
447 }
448 } // namespace OHOS::Ace::Framework
449