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 #include "frameworks/bridge/declarative_frontend/jsview/dialog/js_alert_dialog.h"
16
17 #include <sstream>
18 #include <string>
19 #include <vector>
20
21 #include "base/log/ace_scoring_log.h"
22 #include "bridge/declarative_frontend/jsview/models/alert_dialog_model_impl.h"
23 #include "core/common/container.h"
24 #include "core/components_ng/pattern/dialog/alert_dialog_model_ng.h"
25 #include "frameworks/bridge/common/utils/engine_helper.h"
26 #include "frameworks/bridge/declarative_frontend/engine/functions/js_function.h"
27
28 namespace OHOS::Ace {
29 std::unique_ptr<AlertDialogModel> AlertDialogModel::instance_ = nullptr;
30 std::mutex AlertDialogModel::mutex_;
GetInstance()31 AlertDialogModel* AlertDialogModel::GetInstance()
32 {
33 if (!instance_) {
34 std::lock_guard<std::mutex> lock(mutex_);
35 if (!instance_) {
36 #ifdef NG_BUILD
37 instance_.reset(new NG::AlertDialogModelNG());
38 #else
39 if (Container::IsCurrentUseNewPipeline()) {
40 instance_.reset(new NG::AlertDialogModelNG());
41 } else {
42 instance_.reset(new Framework::AlertDialogModelImpl());
43 }
44 #endif
45 }
46 }
47 return instance_.get();
48 }
49
50 } // namespace OHOS::Ace
51 namespace OHOS::Ace::Framework {
52 namespace {
53 const std::vector<DialogAlignment> DIALOG_ALIGNMENT = { DialogAlignment::TOP, DialogAlignment::CENTER,
54 DialogAlignment::BOTTOM, DialogAlignment::DEFAULT, DialogAlignment::TOP_START, DialogAlignment::TOP_END,
55 DialogAlignment::CENTER_START, DialogAlignment::CENTER_END, DialogAlignment::BOTTOM_START,
56 DialogAlignment::BOTTOM_END };
57 const std::vector<DialogButtonDirection> DIALOG_BUTTONS_DIRECTION = { DialogButtonDirection::AUTO,
58 DialogButtonDirection::HORIZONTAL, DialogButtonDirection::VERTICAL };
59 } // namespace
60
SetParseStyle(ButtonInfo & buttonInfo,const int32_t styleValue)61 void SetParseStyle(ButtonInfo& buttonInfo, const int32_t styleValue)
62 {
63 if (styleValue >= static_cast<int32_t>(DialogButtonStyle::DEFAULT) &&
64 styleValue <= static_cast<int32_t>(DialogButtonStyle::HIGHTLIGHT)) {
65 buttonInfo.dlgButtonStyle = static_cast<DialogButtonStyle>(styleValue);
66 }
67 }
68
ParseButtonObj(const JSCallbackInfo & args,DialogProperties & properties,JSRef<JSVal> jsVal,const std::string & property)69 void ParseButtonObj(
70 const JSCallbackInfo& args, DialogProperties& properties, JSRef<JSVal> jsVal, const std::string& property)
71 {
72 if (!jsVal->IsObject()) {
73 return;
74 }
75 auto objInner = JSRef<JSObject>::Cast(jsVal);
76 auto value = objInner->GetProperty("value");
77 std::string buttonValue;
78 ButtonInfo buttonInfo;
79 if (JSAlertDialog::ParseJsString(value, buttonValue)) {
80 buttonInfo.text = buttonValue;
81 }
82
83 // Parse enabled
84 auto enabledValue = objInner->GetProperty("enabled");
85 if (enabledValue->IsBoolean()) {
86 buttonInfo.enabled = enabledValue->ToBoolean();
87 }
88
89 // Parse defaultFocus
90 auto defaultFocusValue = objInner->GetProperty("defaultFocus");
91 if (defaultFocusValue->IsBoolean()) {
92 buttonInfo.defaultFocus = defaultFocusValue->ToBoolean();
93 }
94
95 // Parse style
96 auto style = objInner->GetProperty("style");
97 if (style->IsNumber()) {
98 auto styleValue = style->ToNumber<int32_t>();
99 SetParseStyle(buttonInfo, styleValue);
100 }
101
102 auto fontColorValue = objInner->GetProperty("fontColor");
103 Color textColor;
104 if (JSAlertDialog::ParseJsColor(fontColorValue, textColor)) {
105 buttonInfo.textColor = textColor.ColorToString();
106 }
107
108 auto backgroundColorValue = objInner->GetProperty("backgroundColor");
109 Color backgroundColor;
110 if (JSAlertDialog::ParseJsColor(backgroundColorValue, backgroundColor)) {
111 buttonInfo.isBgColorSetted = true;
112 buttonInfo.bgColor = backgroundColor;
113 }
114
115 auto actionValue = objInner->GetProperty("action");
116 if (actionValue->IsFunction()) {
117 auto actionFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(actionValue));
118 auto eventFunc = [execCtx = args.GetExecutionContext(), func = std::move(actionFunc), property]() {
119 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
120 ACE_SCORING_EVENT("AlertDialog.[" + property + "].onAction");
121 func->Execute();
122 };
123 AlertDialogModel::GetInstance()->SetParseButtonObj(eventFunc, buttonInfo, properties, property);
124 }
125
126 if (buttonInfo.IsValid()) {
127 properties.buttons.emplace_back(buttonInfo);
128 }
129 }
130
ParseButtonArray(const JSCallbackInfo & args,DialogProperties & properties,JSRef<JSObject> obj,const std::string & property)131 void ParseButtonArray(
132 const JSCallbackInfo& args, DialogProperties& properties, JSRef<JSObject> obj, const std::string& property)
133 {
134 auto jsVal = obj->GetProperty(property.c_str());
135 if (!jsVal->IsArray()) {
136 return;
137 }
138 JSRef<JSArray> array = JSRef<JSArray>::Cast(jsVal);
139 size_t length = array->Length();
140 if (length <= 0) {
141 return;
142 }
143 for (size_t i = 0; i < length; i++) {
144 JSRef<JSVal> buttonItem = array->GetValueAt(i);
145 if (!buttonItem->IsObject()) {
146 break;
147 }
148 ParseButtonObj(args, properties, buttonItem, property + std::to_string(i));
149 }
150 }
151
Show(const JSCallbackInfo & args)152 void JSAlertDialog::Show(const JSCallbackInfo& args)
153 {
154 auto scopedDelegate = EngineHelper::GetCurrentDelegate();
155 if (!scopedDelegate) {
156 // this case usually means there is no foreground container, need to figure out the reason.
157 LOGE("scopedDelegate is null, please check");
158 return;
159 }
160
161 DialogProperties properties { .type = DialogType::ALERT_DIALOG };
162 if (args[0]->IsObject()) {
163 auto obj = JSRef<JSObject>::Cast(args[0]);
164
165 // Parse title.
166 auto titleValue = obj->GetProperty("title");
167 std::string title;
168 if (ParseJsString(titleValue, title)) {
169 properties.title = title;
170 }
171
172 // Parse subtitle.
173 auto subtitleValue = obj->GetProperty("subtitle");
174 std::string subtitle;
175 if (ParseJsString(subtitleValue, subtitle)) {
176 properties.subtitle = subtitle;
177 }
178
179 // Parses message.
180 auto messageValue = obj->GetProperty("message");
181 std::string message;
182 if (ParseJsString(messageValue, message)) {
183 properties.content = message;
184 }
185
186 // Parses gridCount.
187 auto gridCountValue = obj->GetProperty("gridCount");
188 if (gridCountValue->IsNumber()) {
189 properties.gridCount = gridCountValue->ToNumber<int32_t>();
190 }
191
192 // Parse auto autoCancel.
193 auto autoCancelValue = obj->GetProperty("autoCancel");
194 if (autoCancelValue->IsBoolean()) {
195 properties.autoCancel = autoCancelValue->ToBoolean();
196 }
197
198 // Parse cancel.
199 auto cancelValue = obj->GetProperty("cancel");
200 if (cancelValue->IsFunction()) {
201 auto cancelFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(cancelValue));
202 auto eventFunc = [execCtx = args.GetExecutionContext(), func = std::move(cancelFunc)]() {
203 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
204 ACE_SCORING_EVENT("AlertDialog.property.cancel");
205 func->Execute();
206 };
207 AlertDialogModel::GetInstance()->SetOnCancel(eventFunc, properties);
208 }
209
210 if (obj->GetProperty("confirm")->IsObject()) {
211 // Parse confirm.
212 auto objInner = obj->GetProperty("confirm");
213 ParseButtonObj(args, properties, objInner, "confirm");
214 } else if (obj->GetProperty("buttons")->IsArray()) {
215 // Parse buttons array.
216 ParseButtonArray(args, properties, obj, "buttons");
217 } else {
218 // Parse primaryButton and secondaryButton.
219 auto objInner = obj->GetProperty("primaryButton");
220 ParseButtonObj(args, properties, objInner, "primaryButton");
221 objInner = obj->GetProperty("secondaryButton");
222 ParseButtonObj(args, properties, objInner, "secondaryButton");
223 }
224
225 // Parse buttons direction.
226 auto directionValue = obj->GetProperty("buttonDirection");
227 if (directionValue->IsNumber()) {
228 auto buttonDirection = directionValue->ToNumber<int32_t>();
229 if (buttonDirection >= 0 && buttonDirection <= static_cast<int32_t>(DIALOG_BUTTONS_DIRECTION.size())) {
230 properties.buttonDirection = DIALOG_BUTTONS_DIRECTION[buttonDirection];
231 }
232 }
233
234 // Parse alignment
235 auto alignmentValue = obj->GetProperty("alignment");
236 if (alignmentValue->IsNumber()) {
237 auto alignment = alignmentValue->ToNumber<int32_t>();
238 if (alignment >= 0 && alignment <= static_cast<int32_t>(DIALOG_ALIGNMENT.size())) {
239 properties.alignment = DIALOG_ALIGNMENT[alignment];
240 }
241 }
242
243 // Parse offset
244 auto offsetValue = obj->GetProperty("offset");
245 if (offsetValue->IsObject()) {
246 auto offsetObj = JSRef<JSObject>::Cast(offsetValue);
247 CalcDimension dx;
248 auto dxValue = offsetObj->GetProperty("dx");
249 ParseJsDimensionVp(dxValue, dx);
250 CalcDimension dy;
251 auto dyValue = offsetObj->GetProperty("dy");
252 ParseJsDimensionVp(dyValue, dy);
253 properties.offset = DimensionOffset(dx, dy);
254 }
255
256 // Parse maskRect.
257 auto maskRectValue = obj->GetProperty("maskRect");
258 DimensionRect maskRect;
259 if (JSViewAbstract::ParseJsDimensionRect(maskRectValue, maskRect)) {
260 properties.maskRect = maskRect;
261 }
262 AlertDialogModel::GetInstance()->SetShowDialog(properties);
263 }
264 }
265
JSBind(BindingTarget globalObj)266 void JSAlertDialog::JSBind(BindingTarget globalObj)
267 {
268 JSClass<JSAlertDialog>::Declare("AlertDialog");
269 JSClass<JSAlertDialog>::StaticMethod("show", &JSAlertDialog::Show);
270
271 JSClass<JSAlertDialog>::InheritAndBind<JSViewAbstract>(globalObj);
272 }
273
274 } // namespace OHOS::Ace::Framework
275