1/*
2 * Copyright (c) 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 "component_manager.h"
17
18#include "accessibility_ui_test_ability.h"
19#include "multimode_manager.h"
20
21namespace OHOS {
22namespace WuKong {
23namespace {
24const std::string permissionBundleName = "com.ohos.permissionmanager";
25const int DIV = 2;
26const int DOWNTIME = 10;
27const int ONESECOND = 1000000;
28const int TWOSECONDS = 2000000;
29const int OFFSET = 10;
30}  // namespace
31
32class ComponentEventMonitor : public Accessibility::AccessibleAbilityListener {
33public:
34    virtual ~ComponentEventMonitor() = default;
35
36    void OnAbilityConnected() override;
37
38    void OnAbilityDisconnected() override;
39
40    void OnAccessibilityEvent(const Accessibility::AccessibilityEventInfo& eventInfo) override;
41
42    void SetOnAbilityConnectCallback(std::function<void()> onConnectCb);
43
44    void SetOnAbilityDisConnectCallback(std::function<void()> onDisConnectCb);
45
46    bool OnKeyPressEvent(const std::shared_ptr<MMI::KeyEvent>& keyEvent) override
47    {
48        return false;
49    }
50
51    uint64_t GetLastEventMillis();
52
53    bool WaitEventIdle(uint32_t idleThresholdMs, uint32_t timeoutMs);
54
55private:
56    std::function<void()> onConnectCallback_ = nullptr;
57    std::function<void()> onDisConnectCallback_ = nullptr;
58    std::atomic<uint64_t> lastEventMillis_ = 0;
59};
60
61void ComponentEventMonitor::SetOnAbilityConnectCallback(std::function<void()> onConnectCb)
62{
63    onConnectCallback_ = std::move(onConnectCb);
64}
65
66void ComponentEventMonitor::SetOnAbilityDisConnectCallback(std::function<void()> onDisConnectCb)
67{
68    onDisConnectCallback_ = std::move(onDisConnectCb);
69}
70
71void ComponentEventMonitor::OnAbilityConnected()
72{
73    if (onConnectCallback_ != nullptr) {
74        onConnectCallback_();
75    }
76}
77
78void ComponentEventMonitor::OnAbilityDisconnected()
79{
80    if (onDisConnectCallback_ != nullptr) {
81        onDisConnectCallback_();
82    }
83}
84
85void ComponentEventMonitor::OnAccessibilityEvent(const Accessibility::AccessibilityEventInfo& eventInfo)
86{
87    TRACK_LOG_STR("OnAccessibilityEvent Start %u", eventInfo.GetEventType());
88    TRACK_LOG_STR("current bundle: %s", eventInfo.GetBundleName().c_str());
89    if (eventInfo.GetBundleName() == permissionBundleName) {
90        auto listenerlist = ComponentManager::GetInstance()->GetListenerList();
91        for (auto it : listenerlist) {
92            it->OnPermissionScreenShown();
93        }
94    }
95}
96
97uint64_t ComponentEventMonitor::GetLastEventMillis()
98{
99    return 0;
100}
101
102bool ComponentEventMonitor::WaitEventIdle(uint32_t idleThresholdMs, uint32_t timeoutMs)
103{
104    return true;
105}
106
107ComponentManager::ComponentManager()
108{
109    componentMap_ = {
110        {Accessibility::ACCESSIBILITY_ACTION_CLICK,
111        [this] (Accessibility::AccessibilityElementInfo& elementInfo) -> ErrCode {
112            return ComponentManager::ComponentTouchInput(elementInfo);
113        }},
114        {Accessibility::ACCESSIBILITY_ACTION_SCROLL_FORWARD,
115        [this] (Accessibility::AccessibilityElementInfo& elementInfo) -> ErrCode {
116            return ComponentManager::ComponentUpSwapInput(elementInfo);
117        }},
118        {Accessibility::ACCESSIBILITY_ACTION_SCROLL_BACKWARD,
119        [this] (Accessibility::AccessibilityElementInfo& elementInfo) -> ErrCode {
120            return ComponentManager::ComponentDownSwapInput(elementInfo);
121        }},
122        {Accessibility::ACCESSIBILITY_ACTION_SET_TEXT,
123        [this] (Accessibility::AccessibilityElementInfo& elementInfo) -> ErrCode {
124            return ComponentManager::ComponentMultikeyInput(elementInfo);
125        }},
126        {COMPONENT_LEFT_SWAP,
127        [this] (Accessibility::AccessibilityElementInfo& elementInfo) -> ErrCode {
128            return ComponentManager::ComponentLeftSwapInput(elementInfo);
129        }},
130    };
131}
132ComponentManager::~ComponentManager()
133{
134}
135
136bool ComponentManager::Connect()
137{
138    if (connected_ == true) {
139        return true;
140    }
141    std::mutex mtx;
142    std::unique_lock<std::mutex> uLock(mtx);
143    std::shared_ptr<ComponentEventMonitor> g_monitorInstance_ = std::make_shared<ComponentEventMonitor>();
144    std::condition_variable condition;
145    auto onConnectCallback = [&condition]() {
146        std::cout << "Success connect to AAMS" << std::endl;
147        condition.notify_all();
148    };
149
150    g_monitorInstance_->SetOnAbilityConnectCallback(onConnectCallback);
151    auto ability = Accessibility::AccessibilityUITestAbility::GetInstance();
152    if (ability->RegisterAbilityListener(g_monitorInstance_) != Accessibility::RET_OK) {
153        std::cout << "Failed to register ComponentEventMonitor" << std::endl;
154        return false;
155    }
156    std::cout << "Start connect to AAMS" << std::endl;
157    if (ability->Connect() != Accessibility::RET_OK) {
158        std::cout << "Failed to connect to AAMS" << std::endl;
159        return false;
160    }
161    const auto timeout = std::chrono::milliseconds(1000);
162    if (condition.wait_for(uLock, timeout) == std::cv_status::timeout) {
163        std::cout << "Wait connection to AAMS timed out" << std::endl;
164        return false;
165    }
166    connected_ = true;
167    return true;
168}
169
170void ComponentManager::Disconnect()
171{
172    auto auita = Accessibility::AccessibilityUITestAbility::GetInstance();
173    if (auita != nullptr) {
174        auita->Disconnect();
175    }
176    connected_ = false;
177}
178uint32_t ComponentManager::AddRegisterListener(std::shared_ptr<ComponentManagerListener> listener)
179{
180    TRACK_LOG_STD();
181    listenerList_.push_back(listener);
182    TRACK_LOG_STR("Add linstener count (%d)", listenerList_.size());
183    return listenerList_.size() - 1;
184}
185
186void ComponentManager::DeleteRegisterListener(const uint32_t handle)
187{
188    if (listenerList_.size() > handle) {
189        listenerList_.erase(listenerList_.begin() + handle);
190    }
191}
192
193std::vector<std::shared_ptr<ComponentManagerListener>> ComponentManager::GetListenerList()
194{
195    return listenerList_;
196}
197
198ErrCode ComponentManager::GetReportInfo(std::string& info)
199{
200    ErrCode result = OHOS::ERR_OK;
201    return result;
202}
203
204ErrCode ComponentManager::PermoissionInput()
205{
206    DEBUG_LOG("handle permission window");
207    return OHOS::ERR_OK;
208}
209
210ErrCode ComponentManager::CreateEventInputMap()
211{
212    return OHOS::ERR_OK;
213}
214
215ErrCode ComponentManager::ComponentEventInput(OHOS::Accessibility::AccessibilityElementInfo& elementInfo,
216                                              const int actionType)
217{
218    CreateEventInputMap();
219    // get position of current component
220    GetComponentPosition(elementInfo);
221    auto componentRespond = componentMap_[actionType];
222    if (componentRespond == nullptr) {
223        componentRespond = componentMap_[Accessibility::ACCESSIBILITY_ACTION_CLICK];
224    }
225    ErrCode result = componentRespond(elementInfo);
226    return result;
227}
228
229ErrCode ComponentManager::ComponentTouchInput(Accessibility::AccessibilityElementInfo& elementInfo)
230{
231    ErrCode result;
232    auto touchInput = MultimodeManager::GetInstance();
233    // Calculate touch position
234    int32_t elementTouchX = startX_ + (endX_ - startX_) / DIV;
235    int32_t elementTouchY = startY_ + (endY_ - startY_) / DIV;
236    std::string type = elementInfo.GetContent();
237    INFO_LOG_STR("component Content: Touch Position: (%d, %d)", elementTouchX, elementTouchY);
238    DEBUG_LOG_STR("component Content: (%s), Touch Position: (%d, %d)", type.c_str(), elementTouchX, elementTouchY);
239    result = touchInput->PointerInput(elementTouchX, elementTouchY, MMI::PointerEvent::SOURCE_TYPE_TOUCHSCREEN,
240                                      MMI::PointerEvent::POINTER_ACTION_DOWN);
241    if (result == OHOS::ERR_OK) {
242        result = touchInput->PointerInput(elementTouchX, elementTouchY, MMI::PointerEvent::SOURCE_TYPE_TOUCHSCREEN,
243                                          MMI::PointerEvent::POINTER_ACTION_UP);
244    }
245    return result;
246}
247
248ErrCode ComponentManager::BackToPrePage()
249{
250    ErrCode result = ERR_OK;
251    int backKeyCode = OHOS::MMI::KeyEvent::KEYCODE_BACK;
252    result = MultimodeManager::GetInstance()->SingleKeyCodeInput(backKeyCode, DOWNTIME);
253    return result;
254}
255
256ErrCode ComponentManager::ComponentUpSwapInput(Accessibility::AccessibilityElementInfo& elementInfo)
257{
258    // Calculate swap position
259    int32_t componentUpSwapStartX = startX_ + (endX_ - startX_) / DIV;
260    int32_t componentUpSwapStartY = endY_ - OFFSET;
261    int32_t componentUpSwapEndX = componentUpSwapStartX;
262    int32_t componentUpSwapEndY = startY_ + OFFSET;
263    INFO_LOG_STR("Component Up Swap: (%d, %d) -> (%d, %d)", componentUpSwapStartX, componentUpSwapStartY,
264                 componentUpSwapEndX, componentUpSwapEndY);
265    ErrCode result = MultimodeManager::GetInstance()->IntervalSwap(componentUpSwapStartX, componentUpSwapStartY,
266                                                                   componentUpSwapEndX, componentUpSwapEndY);
267    usleep(TWOSECONDS);
268    return result;
269}
270
271ErrCode ComponentManager::ComponentDownSwapInput(Accessibility::AccessibilityElementInfo& elementInfo)
272{
273    // Calculate swap position
274    int32_t componentDownSwapStartX = startX_ + (endX_ - startX_) / DIV;
275    int32_t componentDownSwapStartY = startY_ + OFFSET;
276    int32_t componentDownSwapEndX = componentDownSwapStartX;
277    int32_t componentDownSwapEndY = endY_ - OFFSET;
278    INFO_LOG_STR("Component Down Swap: (%d, %d) -> (%d, %d)", componentDownSwapStartX, componentDownSwapStartY,
279                 componentDownSwapEndX, componentDownSwapEndY);
280    ErrCode result = MultimodeManager::GetInstance()->IntervalSwap(componentDownSwapStartX, componentDownSwapStartY,
281                                                                   componentDownSwapEndX, componentDownSwapEndY);
282    usleep(TWOSECONDS);
283    return result;
284}
285
286ErrCode ComponentManager::ComponentMultikeyInput(Accessibility::AccessibilityElementInfo& elementInfo)
287{
288    ErrCode result = ComponentTouchInput(elementInfo);
289    if (result != OHOS::ERR_OK) {
290        return result;
291    }
292    usleep(ONESECOND);
293    result = MultimodeManager::GetInstance()->MultiKeyCodeInput(DOWNTIME);
294    return result;
295}
296
297ErrCode ComponentManager::ComponentLeftSwapInput(Accessibility::AccessibilityElementInfo& elementInfo)
298{
299    ErrCode result;
300    // Calculate swap position
301    int32_t leftSwapStartX = startX_ + OFFSET;
302    int32_t leftSwapEndX = endX_ + OFFSET;
303    int32_t leftSwapStartY = startY_ + (endY_ - startY_) / DIV;
304    int32_t leftSwapEndY = leftSwapStartY;
305    INFO_LOG_STR("Component Left Swap: (%d, %d) -> (%d, %d)", leftSwapStartX, leftSwapStartY, leftSwapEndX,
306                 leftSwapEndY);
307    result = MultimodeManager::GetInstance()->IntervalSwap(leftSwapStartX, leftSwapStartY, leftSwapEndX, leftSwapEndY);
308    return result;
309}
310
311void ComponentManager::GetComponentPosition(Accessibility::AccessibilityElementInfo& elementInfo)
312{
313    Accessibility::Rect componentBounds = elementInfo.GetRectInScreen();
314    startX_ = componentBounds.GetLeftTopXScreenPostion();
315    startY_ = componentBounds.GetLeftTopYScreenPostion();
316    endX_ = componentBounds.GetRightBottomXScreenPostion();
317    endY_ = componentBounds.GetRightBottomYScreenPostion();
318}
319}  // namespace WuKong
320}  // namespace OHOS
321