blob: 3e2e43fdfdcadf62ef35cf6136b35758ed4caef5 [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
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;
Prabir Pradhan27c6d992023-08-18 19:44:55 +0000160 std::unique_ptr<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) {
Prabir Pradhan27c6d992023-08-18 19:44:55 +0000178 mSpriteController.reset(new NiceMock<MockSpriteController>(mLooper));
Garfield Tanc15eb912019-08-05 16:47:40 -0700179 mPolicy = new MockPointerControllerPolicyInterface();
180
181 EXPECT_CALL(*mSpriteController, createSprite())
182 .WillOnce(Return(mPointerSprite));
183
Prabir Pradhan27c6d992023-08-18 19:44:55 +0000184 mPointerController = PointerController::create(mPolicy, mLooper, *mSpriteController);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800185}
Garfield Tanc15eb912019-08-05 16:47:40 -0700186
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800187PointerControllerTest::~PointerControllerTest() {
188 mRunning.store(false, std::memory_order_relaxed);
189 mThread.join();
190}
191
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000192void PointerControllerTest::ensureDisplayViewportIsSet(int32_t displayId) {
Garfield Tanc15eb912019-08-05 16:47:40 -0700193 DisplayViewport viewport;
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000194 viewport.displayId = displayId;
Garfield Tanc15eb912019-08-05 16:47:40 -0700195 viewport.logicalRight = 1600;
196 viewport.logicalBottom = 1200;
197 viewport.physicalRight = 800;
198 viewport.physicalBottom = 600;
199 viewport.deviceWidth = 400;
200 viewport.deviceHeight = 300;
201 mPointerController->setDisplayViewport(viewport);
Garfield Tanc15eb912019-08-05 16:47:40 -0700202}
203
204void PointerControllerTest::loopThread() {
205 Looper::setForThread(mLooper);
206
207 while (mRunning.load(std::memory_order_relaxed)) {
208 mLooper->pollOnce(100);
209 }
210}
211
212TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800213 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100214 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700215
216 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
217 EXPECT_CALL(*mPointerSprite, setVisible(true));
218 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000219 EXPECT_CALL(*mPointerSprite,
220 setIcon(AllOf(Field(&SpriteIcon::style,
221 static_cast<PointerIconStyle>(CURSOR_TYPE_DEFAULT)),
222 Field(&SpriteIcon::hotSpotX, hotspot.first),
223 Field(&SpriteIcon::hotSpotY, hotspot.second))));
Garfield Tanc15eb912019-08-05 16:47:40 -0700224 mPointerController->reloadPointerResources();
225}
226
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900227TEST_F(PointerControllerTest, useStylusTypeForStylusHover) {
228 ensureDisplayViewportIsSet();
229 mPointerController->setPresentation(PointerController::Presentation::STYLUS_HOVER);
230 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
231 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_STYLUS);
232 EXPECT_CALL(*mPointerSprite, setVisible(true));
233 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
234 EXPECT_CALL(*mPointerSprite,
235 setIcon(AllOf(Field(&SpriteIcon::style,
236 static_cast<PointerIconStyle>(CURSOR_TYPE_STYLUS)),
237 Field(&SpriteIcon::hotSpotX, hotspot.first),
238 Field(&SpriteIcon::hotSpotY, hotspot.second))));
239 mPointerController->reloadPointerResources();
240}
241
Garfield Tanc15eb912019-08-05 16:47:40 -0700242TEST_F(PointerControllerTest, updatePointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800243 ensureDisplayViewportIsSet();
Liam Harringtonc782be62020-07-17 19:48:24 +0000244 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Michael Wright6853fe62020-07-02 00:01:38 +0100245 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700246
Garfield Tane9c61512019-11-21 16:42:13 -0800247 int32_t type = CURSOR_TYPE_ADDITIONAL;
Garfield Tanc15eb912019-08-05 16:47:40 -0700248 std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
249 EXPECT_CALL(*mPointerSprite, setVisible(true));
250 EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
Brandon Pollack015f5d92022-06-02 06:59:33 +0000251 EXPECT_CALL(*mPointerSprite,
252 setIcon(AllOf(Field(&SpriteIcon::style, static_cast<PointerIconStyle>(type)),
253 Field(&SpriteIcon::hotSpotX, hotspot.first),
254 Field(&SpriteIcon::hotSpotY, hotspot.second))));
255 mPointerController->updatePointerIcon(static_cast<PointerIconStyle>(type));
Garfield Tanc15eb912019-08-05 16:47:40 -0700256}
257
258TEST_F(PointerControllerTest, setCustomPointerIcon) {
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800259 ensureDisplayViewportIsSet();
Michael Wright6853fe62020-07-02 00:01:38 +0100260 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
Garfield Tanc15eb912019-08-05 16:47:40 -0700261
262 int32_t style = CURSOR_TYPE_CUSTOM;
263 float hotSpotX = 15;
264 float hotSpotY = 20;
265
266 SpriteIcon icon;
Brandon Pollack015f5d92022-06-02 06:59:33 +0000267 icon.style = static_cast<PointerIconStyle>(style);
Garfield Tanc15eb912019-08-05 16:47:40 -0700268 icon.hotSpotX = hotSpotX;
269 icon.hotSpotY = hotSpotY;
270
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>(style)),
275 Field(&SpriteIcon::hotSpotX, hotSpotX),
276 Field(&SpriteIcon::hotSpotY, hotSpotY))));
Garfield Tanc15eb912019-08-05 16:47:40 -0700277 mPointerController->setCustomPointerIcon(icon);
278}
279
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800280TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
Michael Wright6853fe62020-07-02 00:01:38 +0100281 mPointerController->setPresentation(PointerController::Presentation::POINTER);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800282 mPointerController->setPosition(1.0f, 1.0f);
283 mPointerController->move(1.0f, 1.0f);
Michael Wright6853fe62020-07-02 00:01:38 +0100284 mPointerController->unfade(PointerController::Transition::IMMEDIATE);
285 mPointerController->fade(PointerController::Transition::IMMEDIATE);
Prabir Pradhanca7d7232020-01-31 17:42:34 -0800286
287 EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
288
289 ensureDisplayViewportIsSet();
290}
291
Prabir Pradhan0e3d6652022-03-10 14:39:46 +0000292TEST_F(PointerControllerTest, notifiesPolicyWhenPointerDisplayChanges) {
293 EXPECT_FALSE(mPolicy->getLastReportedPointerDisplayId())
294 << "A pointer display change does not occur when PointerController is created.";
295
296 ensureDisplayViewportIsSet(ADISPLAY_ID_DEFAULT);
297
298 const auto lastReportedPointerDisplayId = mPolicy->getLastReportedPointerDisplayId();
299 ASSERT_TRUE(lastReportedPointerDisplayId)
300 << "The policy is notified of a pointer display change when the viewport is first set.";
301 EXPECT_EQ(ADISPLAY_ID_DEFAULT, *lastReportedPointerDisplayId)
302 << "Incorrect pointer display notified.";
303
304 ensureDisplayViewportIsSet(42);
305
306 EXPECT_EQ(42, *mPolicy->getLastReportedPointerDisplayId())
307 << "The policy is notified when the pointer display changes.";
308
309 // Release the PointerController.
310 mPointerController = nullptr;
311
312 EXPECT_EQ(ADISPLAY_ID_NONE, *mPolicy->getLastReportedPointerDisplayId())
313 << "The pointer display changes to invalid when PointerController is destroyed.";
314}
315
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800316class PointerControllerWindowInfoListenerTest : public Test {};
317
318class TestPointerController : public PointerController {
319public:
320 TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
Prabir Pradhan27c6d992023-08-18 19:44:55 +0000321 const sp<Looper>& looper, SpriteController& spriteController)
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800322 : PointerController(
Prabir Pradhan27c6d992023-08-18 19:44:55 +0000323 new MockPointerControllerPolicyInterface(), looper, spriteController,
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800324 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
325 // Register listener
326 registeredListener = listener;
327 },
328 [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
329 // Unregister listener
330 if (registeredListener == listener) registeredListener = nullptr;
331 }) {}
332};
333
334TEST_F(PointerControllerWindowInfoListenerTest,
335 doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
Prabir Pradhan27c6d992023-08-18 19:44:55 +0000336 sp<Looper> looper = new Looper(false);
337 auto spriteController = NiceMock<MockSpriteController>(looper);
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800338 sp<android::gui::WindowInfosListener> registeredListener;
339 sp<android::gui::WindowInfosListener> localListenerCopy;
340 {
Prabir Pradhan27c6d992023-08-18 19:44:55 +0000341 TestPointerController pointerController(registeredListener, looper, spriteController);
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800342 ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
343 localListenerCopy = registeredListener;
344 }
345 EXPECT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
Patrick Williams8e47a672023-05-01 11:30:37 -0500346 localListenerCopy->onWindowInfosChanged({{}, {}, 0, 0});
Prabir Pradhan5693cee2021-12-31 06:51:15 -0800347}
348
Garfield Tanc15eb912019-08-05 16:47:40 -0700349} // namespace android