1 /*
2  * Copyright (c) 2021 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 "acelite_config.h"
17 #if (FEATURE_MODULE_DIALOG == 1)
18 #include "ace_log.h"
19 #include "js_async_work.h"
20 #include "js_dialog.h"
21 
22 namespace OHOS {
23 namespace ACELite {
JSDialog()24 JSDialog::JSDialog()
25     : dialog_(nullptr),
26       dialogListener_(nullptr),
27       dismissListener_(nullptr),
28       title_(nullptr),
29       message_(nullptr) {}
30 
~JSDialog()31 JSDialog::~JSDialog()
32 {
33     ReleaseResource();
34 }
35 
ReleaseResource()36 void JSDialog::ReleaseResource()
37 {
38     ACE_FREE(title_);
39     ACE_FREE(message_);
40     ACE_DELETE(dialog_);
41     ACE_DELETE(dismissListener_);
42     ACE_DELETE(dialogListener_);
43 }
44 
SetTitle(char * title)45 void JSDialog::SetTitle(char *title)
46 {
47     title_ = title;
48 }
SetMessage(char * message)49 void JSDialog::SetMessage(char *message)
50 {
51     message_ = message;
52 }
53 
54 /**
55  * This the async callback used to release the JSDialog instance.
56  */
DialogRecycleExecutor(void * jsDialog)57 static void DialogRecycleExecutor(void *jsDialog)
58 {
59     if (jsDialog == nullptr) {
60         return;
61     }
62     // release resource
63     delete reinterpret_cast<JSDialog *>(jsDialog);
64 }
65 
ShowDialog(JSIValue thisVal,JSIValue buttons,JSIValue successFunc,JSIValue cancelFunc,JSIValue completeFunc)66 bool JSDialog::ShowDialog(JSIValue thisVal,
67                           JSIValue buttons,
68                           JSIValue successFunc,
69                           JSIValue cancelFunc,
70                           JSIValue completeFunc)
71 {
72     ACE_DELETE(dialog_);
73     dialog_ = new UIDialog();
74     if (dialog_ == nullptr) {
75         HILOG_ERROR(HILOG_MODULE_ACE, "create ui dialog failed, return ");
76         return false;
77     }
78 
79     dialog_->SetTitle(title_);
80     dialog_->SetText(message_);
81 
82     // set dialog dismiss listener
83     dismissListener_ = new DismissListener(this, cancelFunc, completeFunc, thisVal);
84     if (dismissListener_ == nullptr) {
85         HILOG_ERROR(HILOG_MODULE_ACE, "create dialog dismissListener_ is nullptr, return nullptr.");
86         return false;
87     }
88     dialog_->SetOnCancelListener(dismissListener_);
89 
90     // parser dialog button and set button listenr
91     if (!ParseButton(this, buttons, successFunc, completeFunc, thisVal)) {
92         HILOG_ERROR(HILOG_MODULE_ACE, "parse dialog button error");
93         return false;
94     }
95 
96     dialog_->EnableAutoCancel(true);
97     // show dialog
98     dialog_->Show();
99     return true;
100 }
101 
102 
ParseButton(JSDialog * jsDialog,JSIValue buttonArrayObject,JSIValue successFuncObject,JSIValue completeFuncObject,JSIValue context)103 bool JSDialog::ParseButton(JSDialog *jsDialog,
104                            JSIValue buttonArrayObject,
105                            JSIValue successFuncObject,
106                            JSIValue completeFuncObject,
107                            JSIValue context)
108 {
109     if (jsDialog == nullptr) {
110         return false;
111     }
112     UIDialog *dialog = const_cast<UIDialog *>(jsDialog->GetUIDialog());
113     if (dialog == nullptr) {
114         return false;
115     }
116     /* parse dialog buttons */
117     uint32_t len = JSI::GetArrayLength(buttonArrayObject);
118     const uint8_t maxButtonNum = 3;
119     const char * const buttonTextKey = "text";
120     const char * const buttonColorKey = "color";
121     // support up to 3 buttons
122     for (uint32_t index = 0; index < len; index++) {
123         if (index >= maxButtonNum) {
124             HILOG_WARN(HILOG_MODULE_ACE, "dialog support up to 3 buttons, please check dialog button num");
125             break;
126         }
127         // parse button text
128         JSIValue buttonObject = JSI::GetPropertyByIndex(buttonArrayObject, index);
129         char *buttonText = JSI::GetStringProperty(buttonObject, buttonTextKey);
130 
131         // parse button text color
132         char *buttonColorText = JSI::GetStringProperty(buttonObject, buttonColorKey);
133         ColorType color = ParseButtonColor(buttonColorText);
134         dialog->SetButtonColor(static_cast<UIDialog::DialogButtonType>(index), color);
135         // set button click listener
136         dialogListener_ = new DialogListener(jsDialog, index, successFuncObject, completeFuncObject, context);
137         if (dialogListener_ == nullptr) {
138             JSI::ReleaseString(buttonText);
139             JSI::ReleaseString(buttonColorText);
140             JSI::ReleaseValue(buttonObject);
141             return false;
142         }
143         dialog->SetButton(static_cast<UIDialog::DialogButtonType>(index), buttonText, dialogListener_);
144         // release button JSI value
145         JSI::ReleaseString(buttonText);
146         JSI::ReleaseString(buttonColorText);
147         JSI::ReleaseValue(buttonObject);
148     }
149     return true;
150 }
151 
ParseButtonColor(const char * const buttonColorText)152 ColorType JSDialog::ParseButtonColor(const char * const buttonColorText)
153 {
154     uint32_t color = 0;  // default color
155     uint8_t alpha = 0;
156     if (!ParseColor(buttonColorText, color, alpha)) {
157         HILOG_ERROR(HILOG_MODULE_ACE, "input dialog button color error, please check. default color instead");
158         return Color::GetColorFromRGB(0, 0, 0);
159     }
160     HILOG_INFO(HILOG_MODULE_ACE, "dialog buttonColorText = %{public}s, colorVal = %{public}d", buttonColorText, color);
161     uint8_t red8 = uint8_t((color & TEXT_RED_COLOR_MASK) >> RED_COLOR_START_BIT);
162     uint8_t green8 = uint8_t((color & TEXT_GREEN_COLOR_MASK) >> GREEN_COLOR_START_BIT);
163     uint8_t blue8 = uint8_t((color & TEXT_BLUE_COLOR_MASK));
164     return Color::GetColorFromRGB(red8, green8, blue8);
165 }
166 
167 /**
168  * Call this function if the dialog is dismissed in any scenario.
169  */
DispatchReleaseRequest(const JSDialog * jsDialog)170 void JSDialog::DispatchReleaseRequest(const JSDialog *jsDialog)
171 {
172     if (!JsAsyncWork::DispatchAsyncWork(DialogRecycleExecutor, const_cast<JSDialog *>(jsDialog))) {
173         // dispatch the release request failed, JSDialog resource leaked
174         HILOG_ERROR(HILOG_MODULE_ACE, "dispatch the release request failed, resource leaked [%{private}p]", jsDialog);
175     }
176 }
177 
DialogListener(JSDialog * jsDialog,uint8_t index,JSIValue successFunc,JSIValue completeFunc,JSIValue context)178 DialogListener::DialogListener(JSDialog *jsDialog,
179                                uint8_t index,
180                                JSIValue successFunc,
181                                JSIValue completeFunc,
182                                JSIValue context)
183     : jsDialog_(jsDialog),
184       buttonIndex_(index),
185       jsSuccessFunc_(JSI::AcquireValue(successFunc)),
186       jsCompleteFunc_(JSI::AcquireValue(completeFunc)),
187       jsContext_(JSI::AcquireValue(context)) {}
188 
~DialogListener()189 DialogListener::~DialogListener() {}
190 
OnClick(UIView & view,const ClickEvent & event)191 bool DialogListener::OnClick(UIView &view, const ClickEvent &event)
192 {
193     HILOG_INFO(HILOG_MODULE_ACE, "dialog button on click");
194     JSIValue args[1];
195     args[0] = JSI::CreateObject();
196     const char * const indexStr = "index";
197 
198     JSI::SetNumberProperty(args[0], indexStr, buttonIndex_);
199     if (JSI::ValueIsFunction(jsSuccessFunc_)) {
200         JSI::CallFunction(jsSuccessFunc_, jsContext_, args, 1);
201     }
202     if (JSI::ValueIsFunction(jsCompleteFunc_)) {
203         JSI::CallFunction(jsCompleteFunc_, jsContext_, nullptr, 0);
204     }
205     JSI::ReleaseValueList(args[0], jsSuccessFunc_, jsCompleteFunc_, jsContext_);
206     // the dialog is going to be dismissed, request to do the recycling
207     JSDialog::DispatchReleaseRequest(jsDialog_);
208     return true;
209 }
210 
DismissListener(JSDialog * jsDialog,JSIValue cancelFunc,JSIValue completeFunc,JSIValue context)211 DismissListener::DismissListener(JSDialog *jsDialog, JSIValue cancelFunc, JSIValue completeFunc, JSIValue context)
212     : jsDialog_(jsDialog),
213       jsCancelFunc_(JSI::AcquireValue(cancelFunc)),
214       jsCompleteFunc_(JSI::AcquireValue(completeFunc)),
215       jsContext_(JSI::AcquireValue(context)) {}
216 
~DismissListener()217 DismissListener::~DismissListener() {}
OnClick(UIView & view,const ClickEvent & event)218 bool DismissListener::OnClick(UIView &view, const ClickEvent &event)
219 {
220     HILOG_INFO(HILOG_MODULE_ACE, "dialog dismiss button on click");
221     if (JSI::ValueIsFunction(jsCancelFunc_)) {
222         JSI::CallFunction(jsCancelFunc_, jsContext_, nullptr, 0);
223     }
224     if (JSI::ValueIsFunction(jsCompleteFunc_)) {
225         JSI::CallFunction(jsCompleteFunc_, jsContext_, nullptr, 0);
226     }
227     JSI::ReleaseValueList(jsCancelFunc_, jsCompleteFunc_, jsContext_);
228     // the dialog is going to be dismissed, request to do the recycling
229     JSDialog::DispatchReleaseRequest(jsDialog_);
230     return true;
231 }
232 } // namespace ACELite
233 } // namespace OHOS
234 #endif // FEATURE_MODULE_DIALOG
235