1 /*
2 * Copyright (c) 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 "bridge/declarative_frontend/jsview/models/custom_dialog_controller_model_impl.h"
17
18 #include "base/subwindow/subwindow_manager.h"
19 #include "core/components/dialog/dialog_component.h"
20 #include "core/pipeline_ng/pipeline_context.h"
21 #include "frameworks/bridge/common/utils/engine_helper.h"
22 #include "frameworks/bridge/declarative_frontend/view_stack_processor.h"
23
24 namespace OHOS::Ace::Framework {
25 namespace {
26 constexpr uint32_t DELAY_TIME_FOR_STACK = 100;
27 } // namespace
28
NotifyDialogOperation(DialogOperation operation,DialogProperties & dialogProperties,bool & pending,bool & isShown,std::function<void ()> && cancelTask,RefPtr<AceType> & dialogComponent,RefPtr<AceType> & customDialog,std::list<DialogOperation> & dialogOperation)29 void CustomDialogControllerModelImpl::NotifyDialogOperation(DialogOperation operation,
30 DialogProperties& dialogProperties, bool& pending, bool& isShown, std::function<void()>&& cancelTask,
31 RefPtr<AceType>& dialogComponent, RefPtr<AceType>& customDialog, std::list<DialogOperation>& dialogOperation)
32 {
33 LOGI("JSCustomDialogController(NotifyDialogOperation) operation: %{public}d", operation);
34 if (operation == DialogOperation::DIALOG_OPEN) {
35 isShown = true;
36 pending = false;
37 for (auto iter = dialogOperation.begin(); iter != dialogOperation.end();) {
38 if (*iter == DialogOperation::DIALOG_OPEN) {
39 dialogOperation.erase(iter++);
40 continue;
41 }
42
43 if (*iter == DialogOperation::DIALOG_CLOSE) {
44 dialogOperation.erase(iter);
45 CloseDialog(dialogProperties, pending, isShown, std::move(cancelTask), dialogComponent, customDialog,
46 dialogOperation);
47 break;
48 }
49 }
50 } else if (operation == DialogOperation::DIALOG_CLOSE) {
51 isShown = false;
52 pending = false;
53 for (auto iter = dialogOperation.begin(); iter != dialogOperation.end();) {
54 if (*iter == DialogOperation::DIALOG_CLOSE) {
55 dialogOperation.erase(iter++);
56 continue;
57 }
58
59 if (*iter == DialogOperation::DIALOG_OPEN) {
60 dialogOperation.erase(iter);
61 ShowDialog(dialogProperties, pending, isShown, std::move(cancelTask), dialogComponent, customDialog,
62 dialogOperation);
63 break;
64 }
65 }
66 }
67 }
68
ShowDialog(DialogProperties & dialogProperties,bool & pending,bool & isShown,std::function<void ()> && cancelTask,RefPtr<AceType> & dialogComponent,RefPtr<AceType> & customDialog,std::list<DialogOperation> & dialogOperation)69 void CustomDialogControllerModelImpl::ShowDialog(DialogProperties& dialogProperties, bool& pending, bool& isShown,
70 std::function<void()>&& cancelTask, RefPtr<AceType>& dialogComponent, RefPtr<AceType>& customDialog,
71 std::list<DialogOperation>& dialogOperation)
72 {
73 RefPtr<Container> container;
74 auto current = Container::Current();
75 if (!current) {
76 LOGE("Container is null.");
77 return;
78 }
79 if (current->IsSubContainer()) {
80 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(Container::CurrentId());
81 container = AceEngine::Get().GetContainer(parentContainerId);
82 } else {
83 container = std::move(current);
84 }
85 if (!container) {
86 LOGE("Container is null.");
87 return;
88 }
89 auto context = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
90 if (!context) {
91 LOGE("JSCustomDialogController No Context");
92 return;
93 }
94 dialogProperties.customComponent = customDialog;
95 dialogProperties.callbacks.try_emplace("cancel", EventMarker(std::move(cancelTask)));
96 dialogProperties.onStatusChanged = [&isShown](bool isShownStatus) {
97 if (!isShownStatus) {
98 isShown = isShownStatus;
99 }
100 };
101
102 auto executor = context->GetTaskExecutor();
103 if (!executor) {
104 LOGE("JSCustomDialogController(ShowDialog) No Executor. Cannot post task.");
105 return;
106 }
107
108 if (pending) {
109 LOGI("JSCustomDialogController(ShowDialog) current state is pending.");
110 dialogOperation.emplace_back(DialogOperation::DIALOG_OPEN);
111 return;
112 }
113
114 if (isShown) {
115 LOGI("JSCustomDialogController(ShowDialog) CustomDialog has already shown.");
116 return;
117 }
118
119 pending = true;
120 auto task = [context, showDialogProperties = dialogProperties, &dialogProperties, &pending, &isShown, &cancelTask,
121 &dialogComponent, &customDialog, &dialogOperation, this]() mutable {
122 if (context) {
123 dialogComponent = context->ShowDialog(showDialogProperties, false, "CustomDialog");
124 } else {
125 LOGE("JSCustomDialogController(ShowDialog) context is null.");
126 }
127 this->NotifyDialogOperation(DialogOperation::DIALOG_OPEN, dialogProperties, pending, isShown,
128 std::move(cancelTask), dialogComponent, customDialog, dialogOperation);
129 };
130 auto stack = context->GetLastStack();
131 auto result = false;
132 if (stack) {
133 result = executor->PostTask(task, TaskExecutor::TaskType::UI, "ArkUICustomDialogNotifyOpenOperation");
134 } else {
135 LOGE("JSCustomDialogController(ShowDialog) stack is null, post delay task.");
136 result = executor->PostDelayedTask(
137 task, TaskExecutor::TaskType::UI, DELAY_TIME_FOR_STACK, "ArkUICustomDialogNotifyOpenOperationDelay");
138 }
139 if (!result) {
140 LOGW("JSCustomDialogController(ShowDialog) fail to post task, reset pending status");
141 pending = false;
142 }
143 }
144
CloseDialog(DialogProperties & dialogProperties,bool & pending,bool & isShown,std::function<void ()> && cancelTask,RefPtr<AceType> & dialogComponent,RefPtr<AceType> & customDialog,std::list<DialogOperation> & dialogOperation)145 void CustomDialogControllerModelImpl::CloseDialog(DialogProperties& dialogProperties, bool& pending, bool& isShown,
146 std::function<void()>&& cancelTask, RefPtr<AceType>& dialogComponent, RefPtr<AceType>& customDialog,
147 std::list<DialogOperation>& dialogOperation)
148 {
149 LOGI("JSCustomDialogController(CloseDialog)");
150 RefPtr<Container> container;
151 auto current = Container::Current();
152 if (!current) {
153 LOGE("Container is null.");
154 return;
155 }
156 if (current->IsSubContainer()) {
157 auto parentContainerId = SubwindowManager::GetInstance()->GetParentContainerId(Container::CurrentId());
158 container = AceEngine::Get().GetContainer(parentContainerId);
159 } else {
160 container = std::move(current);
161 }
162 if (!container) {
163 LOGE("Container is null.");
164 return;
165 }
166 auto context = AceType::DynamicCast<PipelineContext>(container->GetPipelineContext());
167 if (!context) {
168 LOGE("JSCustomDialogController No Context");
169 return;
170 }
171 const auto& lastStack = context->GetLastStack();
172 if (!lastStack) {
173 LOGE("JSCustomDialogController No Stack!");
174 return;
175 }
176 auto executor = context->GetTaskExecutor();
177 if (!executor) {
178 LOGE("JSCustomDialogController(CloseDialog) No Executor. Cannot post task.");
179 return;
180 }
181
182 if (pending) {
183 LOGI("JSCustomDialogController(CloseDialog) current state is pending.");
184 dialogOperation.emplace_back(DialogOperation::DIALOG_CLOSE);
185 return;
186 }
187
188 pending = true;
189 auto task = [lastStack, showDialogComponent = dialogComponent, &dialogProperties, &pending, &isShown, &cancelTask,
190 &dialogComponent, &customDialog, &dialogOperation, this]() {
191 if (!lastStack || !showDialogComponent) {
192 LOGI("JSCustomDialogController(CloseDialog) stack or dialog is null.");
193 this->NotifyDialogOperation(DialogOperation::DIALOG_CLOSE, dialogProperties, pending, isShown,
194 std::move(cancelTask), dialogComponent, customDialog, dialogOperation);
195 return;
196 }
197 auto animator = AceType::DynamicCast<DialogComponent>(showDialogComponent)->GetAnimator();
198 auto dialogId = AceType::DynamicCast<DialogComponent>(showDialogComponent)->GetDialogId();
199 if (animator) {
200 if (!AceType::DynamicCast<DialogComponent>(showDialogComponent)->HasStopListenerAdded()) {
201 animator->AddStopListener([lastStack, dialogId] {
202 if (lastStack) {
203 lastStack->PopDialog(dialogId);
204 }
205 });
206 AceType::DynamicCast<DialogComponent>(showDialogComponent)->SetHasStopListenerAdded(true);
207 }
208 animator->Play();
209 } else {
210 lastStack->PopDialog(dialogId);
211 }
212 this->NotifyDialogOperation(DialogOperation::DIALOG_CLOSE, dialogProperties, pending, isShown,
213 std::move(cancelTask), dialogComponent, customDialog, dialogOperation);
214 };
215 auto result = executor->PostTask(task, TaskExecutor::TaskType::UI, "ArkUICustomDialogNotifyCloseOperation");
216 if (!result) {
217 LOGW("JSCustomDialogController(CloseDialog) fail to post task, reset pending status");
218 pending = false;
219 }
220
221 dialogComponent = nullptr;
222 }
223
SetOpenDialog(DialogProperties & dialogProperties,const WeakPtr<AceType> & controller,std::vector<WeakPtr<AceType>> & dialogs,bool & pending,bool & isShown,std::function<void ()> && cancelTask,std::function<void ()> && buildFunc,RefPtr<AceType> & dialogComponent,RefPtr<AceType> & customDialog,std::list<DialogOperation> & dialogOperation)224 void CustomDialogControllerModelImpl::SetOpenDialog(DialogProperties& dialogProperties,
225 const WeakPtr<AceType>& controller, std::vector<WeakPtr<AceType>>& dialogs,
226 bool& pending, bool& isShown, std::function<void()>&& cancelTask, std::function<void()>&& buildFunc,
227 RefPtr<AceType>& dialogComponent, RefPtr<AceType>& customDialog, std::list<DialogOperation>& dialogOperation)
228 {
229 // Cannot reuse component because might depend on state
230 if (customDialog) {
231 customDialog = nullptr;
232 }
233 buildFunc();
234 customDialog = ViewStackProcessor::GetInstance()->Finish();
235 if (!customDialog) {
236 LOGE("Builder does not generate view.");
237 return;
238 }
239
240 ShowDialog(
241 dialogProperties, pending, isShown, std::move(cancelTask), dialogComponent, customDialog, dialogOperation);
242 }
243
SetCloseDialog(DialogProperties & dialogProperties,const WeakPtr<AceType> & controller,std::vector<WeakPtr<AceType>> & dialogs,bool & pending,bool & isShown,std::function<void ()> && cancelTask,RefPtr<AceType> & dialogComponent,RefPtr<AceType> & customDialog,std::list<DialogOperation> & dialogOperation)244 void CustomDialogControllerModelImpl::SetCloseDialog(DialogProperties& dialogProperties,
245 const WeakPtr<AceType>& controller, std::vector<WeakPtr<AceType>>& dialogs,
246 bool& pending, bool& isShown, std::function<void()>&& cancelTask, RefPtr<AceType>& dialogComponent,
247 RefPtr<AceType>& customDialog, std::list<DialogOperation>& dialogOperation)
248 {
249 CloseDialog(
250 dialogProperties, pending, isShown, std::move(cancelTask), dialogComponent, customDialog, dialogOperation);
251 }
252 } // namespace OHOS::Ace::Framework
253