summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/input/Android.bp2
-rw-r--r--libs/input/PointerController.cpp33
-rw-r--r--libs/input/PointerController.h2
-rw-r--r--libs/input/SpriteController.cpp77
-rw-r--r--libs/input/SpriteController.h43
-rw-r--r--libs/input/SpriteIcon.cpp65
-rw-r--r--libs/input/SpriteIcon.h67
-rw-r--r--libs/input/TEST_MAPPING7
-rw-r--r--libs/input/tests/Android.bp45
-rw-r--r--libs/input/tests/PointerController_test.cpp262
-rw-r--r--libs/input/tests/mocks/MockSprite.h41
-rw-r--r--libs/input/tests/mocks/MockSpriteController.h39
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