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