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