blob: d9efd3c2fd83809b8299d1524f84a34eecc4a915 [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 Pradhanb5dadec2023-02-28 17:43:09 +000063 virtual void onPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) 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,
Prabir Pradhanb5dadec2023-02-28 17:43:09 +0000146 const FloatPoint& /*position*/
147) {
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000148 latestPointerDisplayId = displayId;
149}
150
Siarhei Vishniakoua4ea3002023-09-26 18:40:00 -0700151class TestPointerController : public PointerController {
152public:
153 TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
154 sp<PointerControllerPolicyInterface> policy, const sp<Looper>& looper,
155 SpriteController& spriteController)
156 : PointerController(
157 policy, looper, spriteController,
158 /*enabled=*/true,
159 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
160 // Register listener
161 registeredListener = listener;
162 },
163 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
164 // Unregister listener
165 if (registeredListener == listener) registeredListener = nullptr;
166 }) {}
167 ~TestPointerController() override {}
168};
169
Garfield Tanc15eb912019-08-05 16:47:40 -0700170class PointerControllerTest : public Test {
171protected:
172 PointerControllerTest();
173 ~PointerControllerTest();
174
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000175 void ensureDisplayViewportIsSet(int32_t displayId = ADISPLAY_ID_DEFAULT);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800176
Garfield Tanc15eb912019-08-05 16:47:40 -0700177 sp<MockSprite> mPointerSprite;
178 sp<MockPointerControllerPolicyInterface> mPolicy;
Prabir Pradhan27c6d992023-08-18 19:44:55 +0000179 std::unique_ptr<MockSpriteController> mSpriteController;
Michael Wrighta0bc6b12020-06-26 20:25:34 +0100180 std::shared_ptr<PointerController> mPointerController;
Siarhei Vishniakoua4ea3002023-09-26 18:40:00 -0700181 sp<android::gui::WindowInfosListener> mRegisteredListener;
Garfield Tanc15eb912019-08-05 16:47:40 -0700182
183private:
184 void loopThread();
185
186 std::atomic<bool> mRunning = true;
187 class MyLooper : public Looper {
188 public:
189 MyLooper() : Looper(false) {}
190 ~MyLooper() = default;
191 };
192 sp<MyLooper> mLooper;
193 std::thread mThread;
194};
195
196PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
197 mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
Prabir Pradhan27c6d992023-08-18 19:44:55 +0000198 mSpriteController.reset(new NiceMock<MockSpriteController>(mLooper));
Garfield Tanc15eb912019-08-05 16:47:40 -0700199 mPolicy = new MockPointerControllerPolicyInterface();
200
201 EXPECT_CALL(*mSpriteController, createSprite())
202 .WillOnce(Return(mPointerSprite));
203
Siarhei Vishniakoua4ea3002023-09-26 18:40:00 -0700204 mPointerController = std::make_unique<TestPointerController>(mRegisteredListener, mPolicy,
205 mLooper, *mSpriteController);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800206}
Garfield Tanc15eb912019-08-05 16:47:40 -0700207
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800208PointerControllerTest::~PointerControllerTest() {
Siarhei Vishniakoua4ea3002023-09-26 18:40:00 -0700209 mPointerController.reset();
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800210 mRunning.store(false, std::memory_order_relaxed);
211 mThread.join();
212}
213
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000214void PointerControllerTest::ensureDisplayViewportIsSet(int32_t displayId) {
Garfield Tanc15eb912019-08-05 16:47:40 -0700215 DisplayViewport viewport;
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000216 viewport.displayId = displayId;
Garfield Tanc15eb912019-08-05 16:47:40 -0700217 viewport.logicalRight = 1600;
218 viewport.logicalBottom = 1200;
219 viewport.physicalRight = 800;
220 viewport.physicalBottom = 600;
221 viewport.deviceWidth = 400;
222 viewport.deviceHeight = 300;
223 mPointerController->setDisplayViewport(viewport);
Garfield Tanc15eb912019-08-05 16:47:40 -0700224}
225
226void PointerControllerTest::loopThread() {
227 Looper::setForThread(mLooper);
228
229 while (mRunning.load(std::memory_order_relaxed)) {
230 mLooper->pollOnce(100);
231 }
232}
233
234TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800235 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100236 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700237
238 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
239 EXPECT_CALL(*mPointerSprite, setVisible(true));
240 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000241 EXPECT_CALL(*mPointerSprite,
242 setIcon(AllOf(Field(&SpriteIcon::style,
243 static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT)),
244 Field(&SpriteIcon::hotSpotX, hotspot.first),
245 Field(&SpriteIcon::hotSpotY, hotspot.second))));
Garfield Tanc15eb912019-08-05 16:47:40 -0700246 mPointerController->reloadPointerResources();
247}
248
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900249TEST_F(PointerControllerTest, useStylusTypeForStylusHover) {
250 ensureDisplayViewportIsSet();
251 mPointerController->setPresentation(PointerController::Presentation::STYLUS_HOVER);
252 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
253 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_STYLUS);
254 EXPECT_CALL(*mPointerSprite, setVisible(true));
255 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
256 EXPECT_CALL(*mPointerSprite,
257 setIcon(AllOf(Field(&SpriteIcon::style,
258 static_cast<PointerIconStyle>(CURSOR_TYPE_STYLUS)),
259 Field(&SpriteIcon::hotSpotX, hotspot.first),
260 Field(&SpriteIcon::hotSpotY, hotspot.second))));
261 mPointerController->reloadPointerResources();
262}
263
Garfield Tanc15eb912019-08-05 16:47:40 -0700264TEST_F(PointerControllerTest, updatePointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800265 ensureDisplayViewportIsSet();
Liam Harringtonc782be62020-07-17 19:48:24 +0000266 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Michael Wright6853fe62020-07-02 00:01:38 +0100267 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700268
Garfield Tane9c61512019-11-21 16:42:13 -0800269 int32_t type = CURSOR_TYPE_ADDITIONAL;
Garfield Tanc15eb912019-08-05 16:47:40 -0700270 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
271 EXPECT_CALL(*mPointerSprite, setVisible(true));
272 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000273 EXPECT_CALL(*mPointerSprite,
274 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(type)),
275 Field(&SpriteIcon::hotSpotX, hotspot.first),
276 Field(&SpriteIcon::hotSpotY, hotspot.second))));
277 mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type));
Garfield Tanc15eb912019-08-05 16:47:40 -0700278}
279
280TEST_F(PointerControllerTest, setCustomPointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800281 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100282 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700283
284 int32_t style = CURSOR_TYPE_CUSTOM;
285 float hotSpotX = 15;
286 float hotSpotY = 20;
287
288 SpriteIcon icon;
Brandon Pollack015f5d92022-06-02 06:59:33 +0000289 icon.style = static_cast<PointerIconStyle>(style);
Garfield Tanc15eb912019-08-05 16:47:40 -0700290 icon.hotSpotX = hotSpotX;
291 icon.hotSpotY = hotSpotY;
292
293 EXPECT_CALL(*mPointerSprite, setVisible(true));
294 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000295 EXPECT_CALL(*mPointerSprite,
296 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(style)),
297 Field(&SpriteIcon::hotSpotX, hotSpotX),
298 Field(&SpriteIcon::hotSpotY, hotSpotY))));
Garfield Tanc15eb912019-08-05 16:47:40 -0700299 mPointerController->setCustomPointerIcon(icon);
300}
301
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800302TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
Michael Wright6853fe62020-07-02 00:01:38 +0100303 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800304 mPointerController->setPosition(1.0f, 1.0f);
305 mPointerController->move(1.0f, 1.0f);
Michael Wright6853fe62020-07-02 00:01:38 +0100306 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
307 mPointerController->fade(PointerController::Transition::IMMEDIATE);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800308
309 EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
310
311 ensureDisplayViewportIsSet();
312}
313
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000314TEST_F(PointerControllerTest, notifiesPolicyWhenPointerDisplayChanges) {
315 EXPECT_FALSE(mPolicy->getLastReportedPointerDisplayId())
316 << "A pointer display change does not occur when PointerController is created.";
317
318 ensureDisplayViewportIsSet(ADISPLAY_ID_DEFAULT);
319
320 const auto lastReportedPointerDisplayId = mPolicy->getLastReportedPointerDisplayId();
321 ASSERT_TRUE(lastReportedPointerDisplayId)
322 << "The policy is notified of a pointer display change when the viewport is first set.";
323 EXPECT_EQ(ADISPLAY_ID_DEFAULT, *lastReportedPointerDisplayId)
324 << "Incorrect pointer display notified.";
325
326 ensureDisplayViewportIsSet(42);
327
328 EXPECT_EQ(42, *mPolicy->getLastReportedPointerDisplayId())
329 << "The policy is notified when the pointer display changes.";
330
331 // Release the PointerController.
332 mPointerController = nullptr;
333
334 EXPECT_EQ(ADISPLAY_ID_NONE, *mPolicy->getLastReportedPointerDisplayId())
335 << "The pointer display changes to invalid when PointerController is destroyed.";
336}
337
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800338class PointerControllerWindowInfoListenerTest : public Test {};
339
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800340TEST_F(PointerControllerWindowInfoListenerTest,
341 doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
Prabir Pradhan27c6d992023-08-18 19:44:55 +0000342 sp<Looper> looper = new Looper(false);
343 auto spriteController = NiceMock<MockSpriteController>(looper);
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800344 sp<android::gui::WindowInfosListener> registeredListener;
345 sp<android::gui::WindowInfosListener> localListenerCopy;
Siarhei Vishniakoua4ea3002023-09-26 18:40:00 -0700346 sp<MockPointerControllerPolicyInterface> policy = new MockPointerControllerPolicyInterface();
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800347 {
Siarhei Vishniakoua4ea3002023-09-26 18:40:00 -0700348 TestPointerController pointerController(registeredListener, policy, looper,
349 spriteController);
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800350 ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
351 localListenerCopy = registeredListener;
352 }
353 EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
Patrick Williams8e47a672023-05-01 11:30:37 -0500354 localListenerCopy->onWindowInfosChanged({{}, {}, 0, 0});
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800355}
356
Garfield Tanc15eb912019-08-05 16:47:40 -0700357} // namespace android