blob: c820d0007a4bb7d6b98640bd9417e6536339848f [file] [log] [blame]
Garfield Tanc15eb912019-08-05 16:47:40 -07001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Brandon Pollack015f5d92022-06-02 06:59:33 +000017#include <gmock/gmock.h>
18#include <gtest/gtest.h>
Garfield Tanc15eb912019-08-05 16:47:40 -070019#include <input/PointerController.h>
20#include <input/SpriteController.h>
21
22#include <atomic>
Garfield Tanc15eb912019-08-05 16:47:40 -070023#include <thread>
24
Brandon Pollack015f5d92022-06-02 06:59:33 +000025#include "input/Input.h"
26#include "mocks/MockSprite.h"
27#include "mocks/MockSpriteController.h"
28
Garfield Tanc15eb912019-08-05 16:47:40 -070029namespace android {
30
31enum TestCursorType {
32 CURSOR_TYPE_DEFAULT = 0,
33 CURSOR_TYPE_HOVER,
34 CURSOR_TYPE_TOUCH,
35 CURSOR_TYPE_ANCHOR,
Garfield Tane9c61512019-11-21 16:42:13 -080036 CURSOR_TYPE_ADDITIONAL,
37 CURSOR_TYPE_ADDITIONAL_ANIM,
Seunghwan Choi670b33d2023-01-13 21:12:59 +090038 CURSOR_TYPE_STYLUS,
Garfield Tanc15eb912019-08-05 16:47:40 -070039 CURSOR_TYPE_CUSTOM = -1,
40};
41
42using ::testing::AllOf;
43using ::testing::Field;
Prabir Pradhanca7d7232020-01-31 17:42:34 -080044using ::testing::NiceMock;
Garfield Tanc15eb912019-08-05 16:47:40 -070045using ::testing::Return;
46using ::testing::Test;
47
48std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
49 return std::make_pair(type * 10, type * 10 + 5);
50}
51
52class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
53public:
54 virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
55 virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
Brandon Pollack015f5d92022-06-02 06:59:33 +000056 virtual void loadAdditionalMouseResources(
57 std::map<PointerIconStyle, SpriteIcon>* outResources,
58 std::map<PointerIconStyle, PointerAnimation>* outAnimationResources,
59 int32_t displayId) override;
60 virtual PointerIconStyle getDefaultPointerIconId() override;
Seunghwan Choi670b33d2023-01-13 21:12:59 +090061 virtual PointerIconStyle getDefaultStylusIconId() override;
Brandon Pollack015f5d92022-06-02 06:59:33 +000062 virtual PointerIconStyle getCustomPointerIconId() override;
Prabir Pradhan0e3d6652022-03-10 14:39:46 +000063 virtual void onPointerDisplayIdChanged(int32_t displayId, float xPos, float yPos) override;
Garfield Tanc15eb912019-08-05 16:47:40 -070064
Prabir Pradhanca7d7232020-01-31 17:42:34 -080065 bool allResourcesAreLoaded();
66 bool noResourcesAreLoaded();
Prabir Pradhan0e3d6652022-03-10 14:39:46 +000067 std::optional<int32_t> getLastReportedPointerDisplayId() { return latestPointerDisplayId; }
Prabir Pradhanca7d7232020-01-31 17:42:34 -080068
Garfield Tanc15eb912019-08-05 16:47:40 -070069private:
70 void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080071
72 bool pointerIconLoaded{false};
73 bool pointerResourcesLoaded{false};
74 bool additionalMouseResourcesLoaded{false};
Prabir Pradhan0e3d6652022-03-10 14:39:46 +000075 std::optional<int32_t /*displayId*/> latestPointerDisplayId;
Garfield Tanc15eb912019-08-05 16:47:40 -070076};
77
78void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
79 loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080080 pointerIconLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -070081}
82
83void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
84 int32_t) {
85 loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
86 loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
87 loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
Prabir Pradhanca7d7232020-01-31 17:42:34 -080088 pointerResourcesLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -070089}
90
91void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
Brandon Pollack015f5d92022-06-02 06:59:33 +000092 std::map<PointerIconStyle, SpriteIcon>* outResources,
93 std::map<PointerIconStyle, PointerAnimation>* outAnimationResources, int32_t) {
Garfield Tanc15eb912019-08-05 16:47:40 -070094 SpriteIcon icon;
95 PointerAnimation anim;
96
Garfield Tane9c61512019-11-21 16:42:13 -080097 // CURSOR_TYPE_ADDITIONAL doesn't have animation resource.
98 int32_t cursorType = CURSOR_TYPE_ADDITIONAL;
99 loadPointerIconForType(&icon, cursorType);
Brandon Pollack015f5d92022-06-02 06:59:33 +0000100 (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
Garfield Tane9c61512019-11-21 16:42:13 -0800101
102 // CURSOR_TYPE_ADDITIONAL_ANIM has animation resource.
103 cursorType = CURSOR_TYPE_ADDITIONAL_ANIM;
104 loadPointerIconForType(&icon, cursorType);
105 anim.animationFrames.push_back(icon);
106 anim.durationPerFrame = 10;
Brandon Pollack015f5d92022-06-02 06:59:33 +0000107 (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
108 (*outAnimationResources)[static_cast<PointerIconStyle>(cursorType)] = anim;
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800109
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900110 // CURSOR_TYPE_STYLUS doesn't have animation resource.
111 cursorType = CURSOR_TYPE_STYLUS;
112 loadPointerIconForType(&icon, cursorType);
113 (*outResources)[static_cast<PointerIconStyle>(cursorType)] = icon;
114
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800115 additionalMouseResourcesLoaded = true;
Garfield Tanc15eb912019-08-05 16:47:40 -0700116}
117
Brandon Pollack015f5d92022-06-02 06:59:33 +0000118PointerIconStyle MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
119 return static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT);
Garfield Tanc15eb912019-08-05 16:47:40 -0700120}
121
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900122PointerIconStyle MockPointerControllerPolicyInterface::getDefaultStylusIconId() {
123 return static_cast<PointerIconStyle>(CURSOR_TYPE_STYLUS);
124}
125
Brandon Pollack015f5d92022-06-02 06:59:33 +0000126PointerIconStyle MockPointerControllerPolicyInterface::getCustomPointerIconId() {
127 return static_cast<PointerIconStyle>(CURSOR_TYPE_CUSTOM);
Garfield Tanc15eb912019-08-05 16:47:40 -0700128}
129
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800130bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
131 return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
132}
133
134bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
135 return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
136}
137
Garfield Tanc15eb912019-08-05 16:47:40 -0700138void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000139 icon->style = static_cast<PointerIconStyle>(type);
Garfield Tanc15eb912019-08-05 16:47:40 -0700140 std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
141 icon->hotSpotX = hotSpot.first;
142 icon->hotSpotY = hotSpot.second;
143}
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000144
145void MockPointerControllerPolicyInterface::onPointerDisplayIdChanged(int32_t displayId,
146 float /*xPos*/,
147 float /*yPos*/) {
148 latestPointerDisplayId = displayId;
149}
150
Garfield Tanc15eb912019-08-05 16:47:40 -0700151class PointerControllerTest : public Test {
152protected:
153 PointerControllerTest();
154 ~PointerControllerTest();
155
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000156 void ensureDisplayViewportIsSet(int32_t displayId = ADISPLAY_ID_DEFAULT);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800157
Garfield Tanc15eb912019-08-05 16:47:40 -0700158 sp<MockSprite> mPointerSprite;
159 sp<MockPointerControllerPolicyInterface> mPolicy;
160 sp<MockSpriteController> mSpriteController;
Michael Wrighta0bc6b12020-06-26 20:25:34 +0100161 std::shared_ptr<PointerController> mPointerController;
Garfield Tanc15eb912019-08-05 16:47:40 -0700162
163private:
164 void loopThread();
165
166 std::atomic<bool> mRunning = true;
167 class MyLooper : public Looper {
168 public:
169 MyLooper() : Looper(false) {}
170 ~MyLooper() = default;
171 };
172 sp<MyLooper> mLooper;
173 std::thread mThread;
174};
175
176PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
177 mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
178
179 mSpriteController = new NiceMock<MockSpriteController>(mLooper);
180 mPolicy = new MockPointerControllerPolicyInterface();
181
182 EXPECT_CALL(*mSpriteController, createSprite())
183 .WillOnce(Return(mPointerSprite));
184
Michael Wrighta0bc6b12020-06-26 20:25:34 +0100185 mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800186}
Garfield Tanc15eb912019-08-05 16:47:40 -0700187
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800188PointerControllerTest::~PointerControllerTest() {
189 mRunning.store(false, std::memory_order_relaxed);
190 mThread.join();
191}
192
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000193void PointerControllerTest::ensureDisplayViewportIsSet(int32_t displayId) {
Garfield Tanc15eb912019-08-05 16:47:40 -0700194 DisplayViewport viewport;
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000195 viewport.displayId = displayId;
Garfield Tanc15eb912019-08-05 16:47:40 -0700196 viewport.logicalRight = 1600;
197 viewport.logicalBottom = 1200;
198 viewport.physicalRight = 800;
199 viewport.physicalBottom = 600;
200 viewport.deviceWidth = 400;
201 viewport.deviceHeight = 300;
202 mPointerController->setDisplayViewport(viewport);
Garfield Tanc15eb912019-08-05 16:47:40 -0700203}
204
205void PointerControllerTest::loopThread() {
206 Looper::setForThread(mLooper);
207
208 while (mRunning.load(std::memory_order_relaxed)) {
209 mLooper->pollOnce(100);
210 }
211}
212
213TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800214 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100215 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700216
217 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
218 EXPECT_CALL(*mPointerSprite, setVisible(true));
219 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000220 EXPECT_CALL(*mPointerSprite,
221 setIcon(AllOf(Field(&SpriteIcon::style,
222 static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT)),
223 Field(&SpriteIcon::hotSpotX, hotspot.first),
224 Field(&SpriteIcon::hotSpotY, hotspot.second))));
Garfield Tanc15eb912019-08-05 16:47:40 -0700225 mPointerController->reloadPointerResources();
226}
227
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900228TEST_F(PointerControllerTest, useStylusTypeForStylusHover) {
229 ensureDisplayViewportIsSet();
230 mPointerController->setPresentation(PointerController::Presentation::STYLUS_HOVER);
231 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
232 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_STYLUS);
233 EXPECT_CALL(*mPointerSprite, setVisible(true));
234 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
235 EXPECT_CALL(*mPointerSprite,
236 setIcon(AllOf(Field(&SpriteIcon::style,
237 static_cast<PointerIconStyle>(CURSOR_TYPE_STYLUS)),
238 Field(&SpriteIcon::hotSpotX, hotspot.first),
239 Field(&SpriteIcon::hotSpotY, hotspot.second))));
240 mPointerController->reloadPointerResources();
241}
242
Garfield Tanc15eb912019-08-05 16:47:40 -0700243TEST_F(PointerControllerTest, updatePointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800244 ensureDisplayViewportIsSet();
Liam Harringtonc782be62020-07-17 19:48:24 +0000245 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Michael Wright6853fe62020-07-02 00:01:38 +0100246 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700247
Garfield Tane9c61512019-11-21 16:42:13 -0800248 int32_t type = CURSOR_TYPE_ADDITIONAL;
Garfield Tanc15eb912019-08-05 16:47:40 -0700249 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
250 EXPECT_CALL(*mPointerSprite, setVisible(true));
251 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000252 EXPECT_CALL(*mPointerSprite,
253 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(type)),
254 Field(&SpriteIcon::hotSpotX, hotspot.first),
255 Field(&SpriteIcon::hotSpotY, hotspot.second))));
256 mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type));
Garfield Tanc15eb912019-08-05 16:47:40 -0700257}
258
259TEST_F(PointerControllerTest, setCustomPointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800260 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100261 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700262
263 int32_t style = CURSOR_TYPE_CUSTOM;
264 float hotSpotX = 15;
265 float hotSpotY = 20;
266
267 SpriteIcon icon;
Brandon Pollack015f5d92022-06-02 06:59:33 +0000268 icon.style = static_cast<PointerIconStyle>(style);
Garfield Tanc15eb912019-08-05 16:47:40 -0700269 icon.hotSpotX = hotSpotX;
270 icon.hotSpotY = hotSpotY;
271
272 EXPECT_CALL(*mPointerSprite, setVisible(true));
273 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000274 EXPECT_CALL(*mPointerSprite,
275 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(style)),
276 Field(&SpriteIcon::hotSpotX, hotSpotX),
277 Field(&SpriteIcon::hotSpotY, hotSpotY))));
Garfield Tanc15eb912019-08-05 16:47:40 -0700278 mPointerController->setCustomPointerIcon(icon);
279}
280
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800281TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
Michael Wright6853fe62020-07-02 00:01:38 +0100282 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800283 mPointerController->setPosition(1.0f, 1.0f);
284 mPointerController->move(1.0f, 1.0f);
Michael Wright6853fe62020-07-02 00:01:38 +0100285 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
286 mPointerController->fade(PointerController::Transition::IMMEDIATE);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800287
288 EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
289
290 ensureDisplayViewportIsSet();
291}
292
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000293TEST_F(PointerControllerTest, notifiesPolicyWhenPointerDisplayChanges) {
294 EXPECT_FALSE(mPolicy->getLastReportedPointerDisplayId())
295 << "A pointer display change does not occur when PointerController is created.";
296
297 ensureDisplayViewportIsSet(ADISPLAY_ID_DEFAULT);
298
299 const auto lastReportedPointerDisplayId = mPolicy->getLastReportedPointerDisplayId();
300 ASSERT_TRUE(lastReportedPointerDisplayId)
301 << "The policy is notified of a pointer display change when the viewport is first set.";
302 EXPECT_EQ(ADISPLAY_ID_DEFAULT, *lastReportedPointerDisplayId)
303 << "Incorrect pointer display notified.";
304
305 ensureDisplayViewportIsSet(42);
306
307 EXPECT_EQ(42, *mPolicy->getLastReportedPointerDisplayId())
308 << "The policy is notified when the pointer display changes.";
309
310 // Release the PointerController.
311 mPointerController = nullptr;
312
313 EXPECT_EQ(ADISPLAY_ID_NONE, *mPolicy->getLastReportedPointerDisplayId())
314 << "The pointer display changes to invalid when PointerController is destroyed.";
315}
316
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800317class PointerControllerWindowInfoListenerTest : public Test {};
318
319class TestPointerController : public PointerController {
320public:
321 TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
322 const sp<Looper>& looper)
323 : PointerController(
324 new MockPointerControllerPolicyInterface(), looper,
325 new NiceMock<MockSpriteController>(looper),
326 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
327 // Register listener
328 registeredListener = listener;
329 },
330 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
331 // Unregister listener
332 if (registeredListener == listener) registeredListener = nullptr;
333 }) {}
334};
335
336TEST_F(PointerControllerWindowInfoListenerTest,
337 doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
338 sp<android::gui::WindowInfosListener> registeredListener;
339 sp<android::gui::WindowInfosListener> localListenerCopy;
340 {
341 TestPointerController pointerController(registeredListener, new Looper(false));
342 ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
343 localListenerCopy = registeredListener;
344 }
345 EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
346 localListenerCopy->onWindowInfosChanged({}, {});
347}
348
Garfield Tanc15eb912019-08-05 16:47:40 -0700349} // namespace android