diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/input/Android.bp | 2 | ||||
| -rw-r--r-- | libs/input/PointerController.cpp | 33 | ||||
| -rw-r--r-- | libs/input/PointerController.h | 2 | ||||
| -rw-r--r-- | libs/input/SpriteController.cpp | 77 | ||||
| -rw-r--r-- | libs/input/SpriteController.h | 43 | ||||
| -rw-r--r-- | libs/input/SpriteIcon.cpp | 65 | ||||
| -rw-r--r-- | libs/input/SpriteIcon.h | 67 | ||||
| -rw-r--r-- | libs/input/TEST_MAPPING | 7 | ||||
| -rw-r--r-- | libs/input/tests/Android.bp | 45 | ||||
| -rw-r--r-- | libs/input/tests/PointerController_test.cpp | 262 | ||||
| -rw-r--r-- | libs/input/tests/mocks/MockSprite.h | 41 | ||||
| -rw-r--r-- | libs/input/tests/mocks/MockSpriteController.h | 39 |
12 files changed, 589 insertions, 94 deletions
diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 89d3cc4f5083..fd4371cac795 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -17,9 +17,11 @@ cc_library_shared { srcs: [ "PointerController.cpp", "SpriteController.cpp", + "SpriteIcon.cpp", ], shared_libs: [ + "libbinder", "libcutils", "liblog", "libutils", diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index abf083789c23..5c2ef15cbd1e 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -257,19 +257,24 @@ void PointerController::unfade(Transition transition) { void PointerController::setPresentation(Presentation presentation) { AutoMutex _l(mLock); - if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) { - mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources, mLocked.viewport.displayId); + if (mLocked.presentation == presentation) { + return; } - if (mLocked.presentation != presentation) { - mLocked.presentation = presentation; - mLocked.presentationChanged = true; + mLocked.presentation = presentation; + mLocked.presentationChanged = true; - if (presentation != PRESENTATION_SPOT) { - fadeOutAndReleaseAllSpotsLocked(); - } + if (!mLocked.viewport.isValid()) { + return; + } + if (presentation == PRESENTATION_POINTER) { + if (mLocked.additionalMouseResources.empty()) { + mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, + &mLocked.animationResources, + mLocked.viewport.displayId); + } + fadeOutAndReleaseAllSpotsLocked(); updatePointerLocked(); } } @@ -291,6 +296,9 @@ void PointerController::setSpots(const PointerCoords* spotCoords, #endif AutoMutex _l(mLock); + if (!mLocked.viewport.isValid()) { + return; + } std::vector<Spot*> newSpots; std::map<int32_t, std::vector<Spot*>>::const_iterator iter = @@ -337,6 +345,9 @@ void PointerController::clearSpots() { #endif AutoMutex _l(mLock); + if (!mLocked.viewport.isValid()) { + return; + } fadeOutAndReleaseAllSpotsLocked(); } @@ -758,6 +769,10 @@ void PointerController::fadeOutAndReleaseAllSpotsLocked() { } void PointerController::loadResourcesLocked() REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return; + } + mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId); mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 52305b8244a6..ebc622bae302 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -100,6 +100,7 @@ public: virtual int32_t getDisplayId() const; virtual void fade(Transition transition); virtual void unfade(Transition transition); + virtual void setDisplayViewport(const DisplayViewport& viewport); virtual void setPresentation(Presentation presentation); virtual void setSpots(const PointerCoords* spotCoords, @@ -108,7 +109,6 @@ public: void updatePointerIcon(int32_t iconId); void setCustomPointerIcon(const SpriteIcon& icon); - void setDisplayViewport(const DisplayViewport& viewport); void setInactivityTimeout(InactivityTimeout inactivityTimeout); void reloadPointerResources(); diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index c1868d3a94d6..5f481ca67ba6 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -23,13 +23,6 @@ #include <utils/String8.h> #include <gui/Surface.h> -#include <SkBitmap.h> -#include <SkCanvas.h> -#include <SkColor.h> -#include <SkPaint.h> - -#include <android/native_window.h> - namespace android { // --- SpriteController --- @@ -132,8 +125,8 @@ void SpriteController::doUpdateSprites() { SpriteUpdate& update = updates.editItemAt(i); if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) { - update.state.surfaceWidth = update.state.icon.bitmap.width(); - update.state.surfaceHeight = update.state.icon.bitmap.height(); + update.state.surfaceWidth = update.state.icon.width(); + update.state.surfaceHeight = update.state.icon.height(); update.state.surfaceDrawn = false; update.state.surfaceVisible = false; update.state.surfaceControl = obtainSurface( @@ -154,8 +147,8 @@ void SpriteController::doUpdateSprites() { } if (update.state.wantSurfaceVisible()) { - int32_t desiredWidth = update.state.icon.bitmap.width(); - int32_t desiredHeight = update.state.icon.bitmap.height(); + int32_t desiredWidth = update.state.icon.width(); + int32_t desiredHeight = update.state.icon.height(); if (update.state.surfaceWidth < desiredWidth || update.state.surfaceHeight < desiredHeight) { needApplyTransaction = true; @@ -196,40 +189,9 @@ void SpriteController::doUpdateSprites() { if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn && update.state.wantSurfaceVisible()) { sp<Surface> surface = update.state.surfaceControl->getSurface(); - ANativeWindow_Buffer outBuffer; - status_t status = surface->lock(&outBuffer, NULL); - if (status) { - ALOGE("Error %d locking sprite surface before drawing.", status); - } else { - SkBitmap surfaceBitmap; - ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); - surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height), - outBuffer.bits, bpr); - - SkCanvas surfaceCanvas(surfaceBitmap); - - SkPaint paint; - paint.setBlendMode(SkBlendMode::kSrc); - surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint); - - if (outBuffer.width > update.state.icon.bitmap.width()) { - paint.setColor(0); // transparent fill color - surfaceCanvas.drawRect(SkRect::MakeLTRB(update.state.icon.bitmap.width(), 0, - outBuffer.width, update.state.icon.bitmap.height()), paint); - } - if (outBuffer.height > update.state.icon.bitmap.height()) { - paint.setColor(0); // transparent fill color - surfaceCanvas.drawRect(SkRect::MakeLTRB(0, update.state.icon.bitmap.height(), - outBuffer.width, outBuffer.height), paint); - } - - status = surface->unlockAndPost(); - if (status) { - ALOGE("Error %d unlocking and posting sprite surface after drawing.", status); - } else { - update.state.surfaceDrawn = true; - update.surfaceChanged = surfaceChanged = true; - } + if (update.state.icon.draw(surface)) { + update.state.surfaceDrawn = true; + update.surfaceChanged = surfaceChanged = true; } } } @@ -245,7 +207,8 @@ void SpriteController::doUpdateSprites() { if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER - | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) { + | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID + | DIRTY_ICON_STYLE))))) { needApplyTransaction = true; if (wantSurfaceVisibleAndDrawn @@ -274,6 +237,21 @@ void SpriteController::doUpdateSprites() { update.state.transformationMatrix.dtdy); } + if (wantSurfaceVisibleAndDrawn + && (becomingVisible + || (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) { + Parcel p; + p.writeInt32(update.state.icon.style); + p.writeFloat(update.state.icon.hotSpotX); + p.writeFloat(update.state.icon.hotSpotY); + + // Pass cursor metadata in the sprite surface so that when Android is running as a + // client OS (e.g. ARC++) the host OS can get the requested cursor metadata and + // update mouse cursor in the host OS. + t.setMetadata( + update.state.surfaceControl, METADATA_MOUSE_CURSOR, p); + } + int32_t surfaceLayer = mOverlayLayer + update.state.layer; if (wantSurfaceVisibleAndDrawn && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) { @@ -397,9 +375,14 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) { } else { dirty = DIRTY_BITMAP; } + + if (mLocked.state.icon.style != icon.style) { + mLocked.state.icon.style = icon.style; + dirty |= DIRTY_ICON_STYLE; + } } else if (mLocked.state.icon.isValid()) { mLocked.state.icon.bitmap.reset(); - dirty = DIRTY_BITMAP | DIRTY_HOTSPOT; + dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE; } else { return; // setting to invalid icon and already invalid so nothing to do } diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h index 5b216f50d113..137b5646feae 100644 --- a/libs/input/SpriteController.h +++ b/libs/input/SpriteController.h @@ -22,7 +22,7 @@ #include <gui/SurfaceComposerClient.h> -#include <SkBitmap.h> +#include "SpriteIcon.h" namespace android { @@ -52,38 +52,6 @@ struct SpriteTransformationMatrix { }; /* - * Icon that a sprite displays, including its hotspot. - */ -struct SpriteIcon { - inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { } - inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) : - bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { } - - SkBitmap bitmap; - float hotSpotX; - float hotSpotY; - - inline SpriteIcon copy() const { - SkBitmap bitmapCopy; - if (bitmapCopy.tryAllocPixels(bitmap.info().makeColorType(kN32_SkColorType))) { - bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(), - 0, 0); - } - return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY); - } - - inline void reset() { - bitmap.reset(); - hotSpotX = 0; - hotSpotY = 0; - } - - inline bool isValid() const { - return !bitmap.isNull() && !bitmap.empty(); - } -}; - -/* * A sprite is a simple graphical object that is displayed on-screen above other layers. * The basic sprite class is an interface. * The implementation is provided by the sprite controller. @@ -149,15 +117,15 @@ public: SpriteController(const sp<Looper>& looper, int32_t overlayLayer); /* Creates a new sprite, initially invisible. */ - sp<Sprite> createSprite(); + virtual sp<Sprite> createSprite(); /* Opens or closes a transaction to perform a batch of sprite updates as part of * a single operation such as setPosition and setAlpha. It is not necessary to * open a transaction when updating a single property. * Calls to openTransaction() nest and must be matched by an equal number * of calls to closeTransaction(). */ - void openTransaction(); - void closeTransaction(); + virtual void openTransaction(); + virtual void closeTransaction(); private: enum { @@ -174,13 +142,14 @@ private: DIRTY_VISIBILITY = 1 << 5, DIRTY_HOTSPOT = 1 << 6, DIRTY_DISPLAY_ID = 1 << 7, + DIRTY_ICON_STYLE = 1 << 8, }; /* Describes the state of a sprite. * This structure is designed so that it can be copied during updates so that * surfaces can be resized and redrawn without blocking the client by holding a lock * on the sprites for a long time. - * Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */ + * Note that the SpriteIcon holds a reference to a shared (and immutable) bitmap. */ struct SpriteState { inline SpriteState() : dirty(0), visible(false), diff --git a/libs/input/SpriteIcon.cpp b/libs/input/SpriteIcon.cpp new file mode 100644 index 000000000000..10cc458b9289 --- /dev/null +++ b/libs/input/SpriteIcon.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SpriteIcon.h" + +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> + +#include <android/native_window.h> +#include <log/log.h> + +namespace android { + +bool SpriteIcon::draw(sp<Surface> surface) const { + ANativeWindow_Buffer outBuffer; + status_t status = surface->lock(&outBuffer, NULL); + if (status) { + ALOGE("Error %d locking sprite surface before drawing.", status); + return false; + } + + SkBitmap surfaceBitmap; + ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); + surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height), + outBuffer.bits, bpr); + + SkCanvas surfaceCanvas(surfaceBitmap); + + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + surfaceCanvas.drawBitmap(bitmap, 0, 0, &paint); + + if (outBuffer.width > width()) { + paint.setColor(0); // transparent fill color + surfaceCanvas.drawRect(SkRect::MakeLTRB(width(), 0, outBuffer.width, height()), paint); + } + if (outBuffer.height > height()) { + paint.setColor(0); // transparent fill color + surfaceCanvas.drawRect(SkRect::MakeLTRB(0, height(), outBuffer.width, outBuffer.height), + paint); + } + + status = surface->unlockAndPost(); + if (status) { + ALOGE("Error %d unlocking and posting sprite surface after drawing.", status); + } + return !status; +} + +} // namespace android diff --git a/libs/input/SpriteIcon.h b/libs/input/SpriteIcon.h new file mode 100644 index 000000000000..c535faf624f7 --- /dev/null +++ b/libs/input/SpriteIcon.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_SPRITE_ICON_H +#define _UI_SPRITE_ICON_H + +#include <SkBitmap.h> +#include <gui/Surface.h> + +namespace android { + +/* + * Icon that a sprite displays, including its hotspot. + */ +struct SpriteIcon { + inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { } + inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) : + bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { } + + SkBitmap bitmap; + int32_t style; + float hotSpotX; + float hotSpotY; + + inline SpriteIcon copy() const { + SkBitmap bitmapCopy; + if (bitmapCopy.tryAllocPixels(bitmap.info().makeColorType(kN32_SkColorType))) { + bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(), + 0, 0); + } + return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY); + } + + inline void reset() { + bitmap.reset(); + style = 0; + hotSpotX = 0; + hotSpotY = 0; + } + + inline bool isValid() const { return !bitmap.isNull() && !bitmap.empty(); } + + inline int32_t width() const { return bitmap.width(); } + inline int32_t height() const { return bitmap.height(); } + + // Draw the bitmap onto the given surface. Returns true if it's successful, or false otherwise. + // Note it doesn't set any metadata to the surface. + bool draw(const sp<Surface> surface) const; +}; + + +} // namespace android + +#endif // _UI_SPRITE_ICON_H diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING new file mode 100644 index 000000000000..fe74c62d4ec1 --- /dev/null +++ b/libs/input/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libinputservice_test" + } + ] +} diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp new file mode 100644 index 000000000000..e83b2a78d180 --- /dev/null +++ b/libs/input/tests/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "libinputservice_test", + srcs: [ + "PointerController_test.cpp", + ], + shared_libs: [ + "libinputservice", + "libgui", + "libhwui", + "libutils", + ], + static_libs: [ + "libgmock", + "libgtest", + ], + header_libs: [ + "libbase_headers", + "libinputflinger_headers", + ], + include_dirs: [ + "frameworks/base/libs", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + test_suites: [ + "general-tests", + ], +} diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp new file mode 100644 index 000000000000..a15742671dc7 --- /dev/null +++ b/libs/input/tests/PointerController_test.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mocks/MockSprite.h" +#include "mocks/MockSpriteController.h" + +#include <input/PointerController.h> +#include <input/SpriteController.h> + +#include <atomic> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <thread> + +namespace android { + +enum TestCursorType { + CURSOR_TYPE_DEFAULT = 0, + CURSOR_TYPE_HOVER, + CURSOR_TYPE_TOUCH, + CURSOR_TYPE_ANCHOR, + CURSOR_TYPE_ADDITIONAL, + CURSOR_TYPE_ADDITIONAL_ANIM, + CURSOR_TYPE_CUSTOM = -1, +}; + +using ::testing::AllOf; +using ::testing::Field; +using ::testing::Mock; +using ::testing::NiceMock; +using ::testing::Return; +using ::testing::Test; + +std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) { + return std::make_pair(type * 10, type * 10 + 5); +} + +class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface { +public: + virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override; + virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override; + virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources, + std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override; + virtual int32_t getDefaultPointerIconId() override; + virtual int32_t getCustomPointerIconId() override; + + bool allResourcesAreLoaded(); + bool noResourcesAreLoaded(); + +private: + void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType); + + bool pointerIconLoaded{false}; + bool pointerResourcesLoaded{false}; + bool additionalMouseResourcesLoaded{false}; +}; + +void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) { + loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT); + pointerIconLoaded = true; +} + +void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources, + int32_t) { + loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER); + loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH); + loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR); + pointerResourcesLoaded = true; +} + +void MockPointerControllerPolicyInterface::loadAdditionalMouseResources( + std::map<int32_t, SpriteIcon>* outResources, + std::map<int32_t, PointerAnimation>* outAnimationResources, + int32_t) { + SpriteIcon icon; + PointerAnimation anim; + + // CURSOR_TYPE_ADDITIONAL doesn't have animation resource. + int32_t cursorType = CURSOR_TYPE_ADDITIONAL; + loadPointerIconForType(&icon, cursorType); + (*outResources)[cursorType] = icon; + + // CURSOR_TYPE_ADDITIONAL_ANIM has animation resource. + cursorType = CURSOR_TYPE_ADDITIONAL_ANIM; + loadPointerIconForType(&icon, cursorType); + anim.animationFrames.push_back(icon); + anim.durationPerFrame = 10; + (*outResources)[cursorType] = icon; + (*outAnimationResources)[cursorType] = anim; + + additionalMouseResourcesLoaded = true; +} + +int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() { + return CURSOR_TYPE_DEFAULT; +} + +int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() { + return CURSOR_TYPE_CUSTOM; +} + +bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() { + return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded; +} + +bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() { + return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded); +} + +void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) { + icon->style = type; + std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type); + icon->hotSpotX = hotSpot.first; + icon->hotSpotY = hotSpot.second; +} +class PointerControllerTest : public Test { +protected: + PointerControllerTest(); + ~PointerControllerTest(); + + void ensureDisplayViewportIsSet(); + + sp<MockSprite> mPointerSprite; + sp<MockPointerControllerPolicyInterface> mPolicy; + sp<MockSpriteController> mSpriteController; + sp<PointerController> mPointerController; + +private: + void loopThread(); + + std::atomic<bool> mRunning = true; + class MyLooper : public Looper { + public: + MyLooper() : Looper(false) {} + ~MyLooper() = default; + }; + sp<MyLooper> mLooper; + std::thread mThread; +}; + +PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>), + mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) { + + mSpriteController = new NiceMock<MockSpriteController>(mLooper); + mPolicy = new MockPointerControllerPolicyInterface(); + + EXPECT_CALL(*mSpriteController, createSprite()) + .WillOnce(Return(mPointerSprite)); + + mPointerController = new PointerController(mPolicy, mLooper, mSpriteController); +} + +PointerControllerTest::~PointerControllerTest() { + mRunning.store(false, std::memory_order_relaxed); + mThread.join(); +} + +void PointerControllerTest::ensureDisplayViewportIsSet() { + DisplayViewport viewport; + viewport.displayId = ADISPLAY_ID_DEFAULT; + viewport.logicalRight = 1600; + viewport.logicalBottom = 1200; + viewport.physicalRight = 800; + viewport.physicalBottom = 600; + viewport.deviceWidth = 400; + viewport.deviceHeight = 300; + mPointerController->setDisplayViewport(viewport); + + // The first call to setDisplayViewport should trigger the loading of the necessary resources. + EXPECT_TRUE(mPolicy->allResourcesAreLoaded()); +} + +void PointerControllerTest::loopThread() { + Looper::setForThread(mLooper); + + while (mRunning.load(std::memory_order_relaxed)) { + mLooper->pollOnce(100); + } +} + +TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { + ensureDisplayViewportIsSet(); + mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + + std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT); + EXPECT_CALL(*mPointerSprite, setVisible(true)); + EXPECT_CALL(*mPointerSprite, setAlpha(1.0f)); + EXPECT_CALL(*mPointerSprite, setIcon( + AllOf( + Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT), + Field(&SpriteIcon::hotSpotX, hotspot.first), + Field(&SpriteIcon::hotSpotY, hotspot.second)))); + mPointerController->reloadPointerResources(); +} + +TEST_F(PointerControllerTest, updatePointerIcon) { + ensureDisplayViewportIsSet(); + mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + + int32_t type = CURSOR_TYPE_ADDITIONAL; + std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type); + EXPECT_CALL(*mPointerSprite, setVisible(true)); + EXPECT_CALL(*mPointerSprite, setAlpha(1.0f)); + EXPECT_CALL(*mPointerSprite, setIcon( + AllOf( + Field(&SpriteIcon::style, type), + Field(&SpriteIcon::hotSpotX, hotspot.first), + Field(&SpriteIcon::hotSpotY, hotspot.second)))); + mPointerController->updatePointerIcon(type); +} + +TEST_F(PointerControllerTest, setCustomPointerIcon) { + ensureDisplayViewportIsSet(); + mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + + int32_t style = CURSOR_TYPE_CUSTOM; + float hotSpotX = 15; + float hotSpotY = 20; + + SpriteIcon icon; + icon.style = style; + icon.hotSpotX = hotSpotX; + icon.hotSpotY = hotSpotY; + + EXPECT_CALL(*mPointerSprite, setVisible(true)); + EXPECT_CALL(*mPointerSprite, setAlpha(1.0f)); + EXPECT_CALL(*mPointerSprite, setIcon( + AllOf( + Field(&SpriteIcon::style, style), + Field(&SpriteIcon::hotSpotX, hotSpotX), + Field(&SpriteIcon::hotSpotY, hotSpotY)))); + mPointerController->setCustomPointerIcon(icon); +} + +TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) { + mPointerController->setPresentation(PointerController::PRESENTATION_POINTER); + mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1); + mPointerController->clearSpots(); + mPointerController->setPosition(1.0f, 1.0f); + mPointerController->move(1.0f, 1.0f); + mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE); + mPointerController->fade(PointerController::TRANSITION_IMMEDIATE); + + EXPECT_TRUE(mPolicy->noResourcesAreLoaded()); + + ensureDisplayViewportIsSet(); +} + +} // namespace android diff --git a/libs/input/tests/mocks/MockSprite.h b/libs/input/tests/mocks/MockSprite.h new file mode 100644 index 000000000000..013b79c3a3bf --- /dev/null +++ b/libs/input/tests/mocks/MockSprite.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _MOCK_SPRITE_H +#define _MOCK_SPRITE_H + +#include <input/SpriteController.h> + +#include <gmock/gmock.h> + +namespace android { + +class MockSprite : public Sprite { +public: + virtual ~MockSprite() = default; + + MOCK_METHOD(void, setIcon, (const SpriteIcon& icon), (override)); + MOCK_METHOD(void, setVisible, (bool), (override)); + MOCK_METHOD(void, setPosition, (float, float), (override)); + MOCK_METHOD(void, setLayer, (int32_t), (override)); + MOCK_METHOD(void, setAlpha, (float), (override)); + MOCK_METHOD(void, setTransformationMatrix, (const SpriteTransformationMatrix&), (override)); + MOCK_METHOD(void, setDisplayId, (int32_t), (override)); +}; + +} // namespace android + +#endif // _MOCK_SPRITE_H diff --git a/libs/input/tests/mocks/MockSpriteController.h b/libs/input/tests/mocks/MockSpriteController.h new file mode 100644 index 000000000000..a034f66c9abf --- /dev/null +++ b/libs/input/tests/mocks/MockSpriteController.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _MOCK_SPRITE_CONTROLLER_H +#define _MOCK_SPRITE_CONTROLLER_H + +#include "MockSprite.h" + +#include <input/SpriteController.h> + +namespace android { + +class MockSpriteController : public SpriteController { + +public: + MockSpriteController(sp<Looper> looper) : SpriteController(looper, 0) {} + ~MockSpriteController() {} + + MOCK_METHOD(sp<Sprite>, createSprite, (), (override)); + MOCK_METHOD(void, openTransaction, (), (override)); + MOCK_METHOD(void, closeTransaction, (), (override)); +}; + +} // namespace android + +#endif // _MOCK_SPRITE_CONTROLLER_H |