• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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 "event_listener.h"
17 
18 #include <linux/input.h>
19 #include <mutex>
20 #include <thread>
21 #include "dock/focus_manager.h"
22 #include "dock/input_device.h"
23 #include "page/page_manager.h"
24 #include "scope_guard.h"
25 #include "updater_ui_facade.h"
26 
27 namespace Updater {
28 std::mutex CallBackDecorator::mtx_;
operator ()(OHOS::UIView & view,bool isAsync) const29 void CallBackDecorator::operator()(OHOS::UIView &view, bool isAsync) const
30 {
31     auto *page = view.GetParent();
32     if (page == nullptr) {
33         LOG(ERROR) << "view hasn't a parent";
34         return;
35     }
36     if (view.GetViewId() == nullptr) {
37         LOG(ERROR) << "view is invalid, please check your json config";
38         return;
39     }
40     std::string id = view.GetViewId();
41     std::string pageId {};
42     if (page->GetViewId() != nullptr) {
43         pageId = page->GetViewId();
44     }
45     // page should be visible
46     if (!page->IsVisible()) {
47         LOG(ERROR) << pageId << " is not visible";
48         return;
49     }
50     // component should be visible
51     if (!view.IsVisible()) {
52         LOG(ERROR) << id << " is not visible";
53         return;
54     }
55     if (isAsync) {
56         LOG(INFO) << "callback by async method";
57         // then can trigger callback by async method
58         std::thread t {
59             [cb = cb_, &view] () {
60                 CallbackWithGuard(cb, view);
61             }
62         };
63         t.detach();
64     } else {
65         LOG(INFO) << "callback by sync method";
66         // then can trigger callback by sync method
67         CallbackWithGuard(cb_, view);
68     }
69 }
70 
CallbackWithGuard(Callback cb,OHOS::UIView & view)71 void CallBackDecorator::CallbackWithGuard(Callback cb, OHOS::UIView &view)
72 {
73     std::unique_lock<std::mutex> lock(mtx_, std::defer_lock);
74     if (!lock.try_lock()) {
75         LOG(ERROR) << "try lock failed, only allow running one callback at the same time";
76         return;
77     }
78     if (cb.func != nullptr) {
79         cb.func(view);
80     }
81 }
82 
OnPress(OHOS::UIView & view,const OHOS::PressEvent & event)83 bool LabelOnTouchListener::OnPress(OHOS::UIView &view, [[maybe_unused]] const OHOS::PressEvent &event)
84 {
85     KeyListener::SetButtonPressed(true);
86     return true;
87 }
88 
OnCancel(OHOS::UIView & view,const OHOS::CancelEvent & event)89 bool LabelOnTouchListener::OnCancel(OHOS::UIView &view, [[maybe_unused]] const OHOS::CancelEvent &event)
90 {
91     KeyListener::SetButtonPressed(false);
92     return true;
93 }
94 
OnRelease(OHOS::UIView & view,const OHOS::ReleaseEvent & event)95 bool LabelOnTouchListener::OnRelease(OHOS::UIView &view, [[maybe_unused]] const OHOS::ReleaseEvent &event)
96 {
97     // wrap cb_ with CallBackDecorator, then call operator()()
98     KeyListener::SetButtonPressed(false);
99     CallBackDecorator{cb_}(view, cb_.isAsync);
100     return isConsumed_;
101 }
102 
OnClick(OHOS::UIView & view,const OHOS::ClickEvent & event)103 bool BtnOnEventListener::OnClick(OHOS::UIView &view, [[maybe_unused]] const OHOS::ClickEvent &event)
104 {
105     CallBackDecorator{cb_}(view, cb_.isAsync);
106     return isConsumed_;
107 }
108 
OnPress(OHOS::UIView & view,const OHOS::PressEvent & event)109 bool BtnOnEventListener::OnPress(OHOS::UIView &view, [[maybe_unused]] const OHOS::PressEvent &event)
110 {
111     KeyListener::SetButtonPressed(true);
112     return true;
113 }
114 
OnRelease(OHOS::UIView & view,const OHOS::ReleaseEvent & event)115 bool BtnOnEventListener::OnRelease(OHOS::UIView &view, [[maybe_unused]] const OHOS::ReleaseEvent &event)
116 {
117     KeyListener::SetButtonPressed(false);
118     return true;
119 }
120 
OnCancel(OHOS::UIView & view,const OHOS::CancelEvent & event)121 bool BtnOnEventListener::OnCancel(OHOS::UIView &view, [[maybe_unused]] const OHOS::CancelEvent &event)
122 {
123     KeyListener::SetButtonPressed(false);
124     return true;
125 }
126 
OnDragStart(OHOS::UIView & view,const OHOS::DragEvent & event)127 bool BtnOnDragListener::OnDragStart(OHOS::UIView &view, [[maybe_unused]] const OHOS::DragEvent &event)
128 {
129     CallBackDecorator{cb_}(view, cb_.isAsync);
130     return isConsumed_;
131 }
132 
OnDrag(OHOS::UIView & view,const OHOS::DragEvent & event)133 bool BtnOnDragListener::OnDrag(OHOS::UIView &view, const OHOS::DragEvent &event)
134 {
135     CallBackDecorator{cb_}(view, cb_.isAsync);
136     view.SetPosition(view.GetX() + event.GetDeltaX(), view.GetY() + event.GetDeltaY());
137     if (view.GetParent() != nullptr) {
138         view.GetParent()->Invalidate();
139     }
140     return isConsumed_;
141 }
142 
OnDragEnd(OHOS::UIView & view,const OHOS::DragEvent & event)143 bool BtnOnDragListener::OnDragEnd(OHOS::UIView &view, [[maybe_unused]] const OHOS::DragEvent &event)
144 {
145     CallBackDecorator{cb_}(view, cb_.isAsync);
146     return isConsumed_;
147 }
148 
149 bool KeyListener::isButtonPressed_ {false};
150 
OnKeyAct(OHOS::UIView & view,const OHOS::KeyEvent & event)151 bool KeyListener::OnKeyAct(OHOS::UIView &view, const OHOS::KeyEvent &event)
152 {
153     bool consumed = false;
154     switch (event.GetKeyId()) {
155         case KEY_POWER:
156             consumed = ProcessPowerKey(view, event);
157             break;
158         case KEY_VOLUMEUP:
159         case KEY_VOLUMEDOWN:
160             consumed = ProcessVolumeKey(view, event);
161             break;
162         default:
163             LOG(ERROR) << "unsupported key id";
164     }
165     return consumed;
166 }
167 
ProcessPowerKey(OHOS::UIView & view,const OHOS::KeyEvent & event)168 bool KeyListener::ProcessPowerKey(OHOS::UIView &view, const OHOS::KeyEvent &event)
169 {
170 #ifndef UPDATER_UT
171     OHOS::UIView *pView = OHOS::FocusManager::GetInstance()->GetFocusedView();
172     if (pView == nullptr) {
173         LOG(ERROR) << "focused view is nullptr";
174         return false;
175     }
176     // triggering button press event by key supports labelButton and label
177     if (!((pView->GetViewType() == OHOS::UI_LABEL_BUTTON) || (pView->GetViewType() == OHOS::UI_LABEL))) {
178         LOG(ERROR) << "focused view is not label button or label";
179         return false;
180     }
181     int16_t centerX = pView->GetX() + static_cast<int16_t>(static_cast<uint16_t>(pView->GetWidth()) >> 1u);
182     int16_t centerY = pView->GetY() + static_cast<int16_t>(static_cast<uint16_t>(pView->GetHeight()) >> 1u);
183     if (event.GetState() == OHOS::InputDevice::STATE_PRESS) {
184         LOG(DEBUG) << "OnPress";
185         pView->OnClickEvent(OHOS::Point { centerX, centerY });
186     } else if (event.GetState() == OHOS::InputDevice::STATE_RELEASE) {
187         LOG(DEBUG) << "OnRelease";
188         pView->OnReleaseEvent(OHOS::Point { centerX, centerY });
189     }
190 #endif
191     return true;
192 }
193 
ProcessVolumeKey(OHOS::UIView & view,const OHOS::KeyEvent & event)194 bool KeyListener::ProcessVolumeKey(OHOS::UIView &view, const OHOS::KeyEvent &event)
195 {
196     const static std::unordered_map<uint16_t, uint8_t> dirMap {
197         {KEY_VOLUMEUP, OHOS::FOCUS_DIRECTION_UP},
198         {KEY_VOLUMEDOWN, OHOS::FOCUS_DIRECTION_DOWN},
199         {KEY_UP, OHOS::FOCUS_DIRECTION_UP},
200         {KEY_DOWN, OHOS::FOCUS_DIRECTION_DOWN},
201     };
202     if (isButtonPressed_) {
203         return true;
204     }
205     if (auto it = dirMap.find(event.GetKeyId()); it != dirMap.end() &&
206         event.GetState() == OHOS::InputDevice::STATE_RELEASE) {
207         if (OHOS::FocusManager::GetInstance()->RequestFocusByDirection(it->second)) {
208             return true;
209         }
210         LOG(WARNING) << "request focus failed";
211         OHOS::UIView *candidate = GetFirstFocusableViewByDir(it->second);
212         if (candidate != nullptr) {
213             OHOS::FocusManager::GetInstance()->RequestFocus(candidate);
214         }
215     }
216     return true;
217 }
218 
GetFirstFocusableViewByDir(uint8_t dir)219 OHOS::UIView *KeyListener::GetFirstFocusableViewByDir(uint8_t dir)
220 {
221     OHOS::UIView *cur = OHOS::FocusManager::GetInstance()->GetFocusedView();
222     if (cur == nullptr || (dir != OHOS::FOCUS_DIRECTION_UP && dir != OHOS::FOCUS_DIRECTION_DOWN)) {
223         return cur;
224     }
225     OHOS::UIView *parent = cur->GetParent();
226     if (parent == nullptr || !parent->IsViewGroup()) {
227         return cur;
228     }
229     OHOS::UIView *candidate = static_cast<OHOS::UIViewGroup *>(parent)->GetChildrenHead();
230     OHOS::UIView *topFocusableView = cur;
231     OHOS::UIView *bottomFocusableView = cur;
232     while (candidate != nullptr) {
233         if (candidate->IsFocusable() && candidate->IsVisible()) {
234             if (candidate->GetY() < topFocusableView->GetY()) {
235                 topFocusableView = candidate;
236             } else if (candidate->GetY() > bottomFocusableView->GetY()) {
237                 bottomFocusableView = candidate;
238             }
239         }
240         candidate = candidate->GetNextSibling();
241     }
242     return dir == OHOS::FOCUS_DIRECTION_UP ? bottomFocusableView : topFocusableView;
243 }
244 
SetButtonPressed(bool isPressed)245 void KeyListener::SetButtonPressed(bool isPressed)
246 {
247     isButtonPressed_ = isPressed;
248 }
249 }
250