• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 
17 #define LOG_TAG "TouchSpotController"
18 
19 // Log debug messages about pointer updates
20 #define DEBUG_SPOT_UPDATES 0
21 
22 #include "TouchSpotController.h"
23 
24 #include <log/log.h>
25 
26 #include <SkBitmap.h>
27 #include <SkBlendMode.h>
28 #include <SkCanvas.h>
29 #include <SkColor.h>
30 #include <SkPaint.h>
31 
32 namespace {
33 // Time to spend fading out the spot completely.
34 const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
35 } // namespace
36 
37 namespace android {
38 
39 // --- Spot ---
40 
updateSprite(const SpriteIcon * icon,float x,float y,int32_t displayId)41 void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
42                                              int32_t displayId) {
43     sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
44     sprite->setAlpha(alpha);
45     sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
46     sprite->setPosition(x, y);
47     sprite->setDisplayId(displayId);
48     this->x = x;
49     this->y = y;
50 
51     if (icon != mLastIcon) {
52         mLastIcon = icon;
53         if (icon) {
54             sprite->setIcon(*icon);
55             sprite->setVisible(true);
56         } else {
57             sprite->setVisible(false);
58         }
59     }
60 }
61 
62 // --- TouchSpotController ---
63 
TouchSpotController(int32_t displayId,PointerControllerContext & context)64 TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context)
65       : mDisplayId(displayId), mContext(context) {
66     mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
67 }
68 
~TouchSpotController()69 TouchSpotController::~TouchSpotController() {
70     std::scoped_lock lock(mLock);
71 
72     size_t numSpots = mLocked.displaySpots.size();
73     for (size_t i = 0; i < numSpots; i++) {
74         delete mLocked.displaySpots[i];
75     }
76     mLocked.displaySpots.clear();
77 }
78 
setSpots(const PointerCoords * spotCoords,const uint32_t * spotIdToIndex,BitSet32 spotIdBits)79 void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
80                                    BitSet32 spotIdBits) {
81 #if DEBUG_SPOT_UPDATES
82     ALOGD("setSpots: idBits=%08x", spotIdBits.value);
83     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
84         uint32_t id = idBits.firstMarkedBit();
85         idBits.clearBit(id);
86         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
87         ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
88               c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y),
89               c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), displayId);
90     }
91 #endif
92 
93     std::scoped_lock lock(mLock);
94     sp<SpriteController> spriteController = mContext.getSpriteController();
95     spriteController->openTransaction();
96 
97     // Add or move spots for fingers that are down.
98     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
99         uint32_t id = idBits.clearFirstMarkedBit();
100         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
101         const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
102                 ? mResources.spotTouch
103                 : mResources.spotHover;
104         float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
105         float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
106 
107         Spot* spot = getSpot(id, mLocked.displaySpots);
108         if (!spot) {
109             spot = createAndAddSpotLocked(id, mLocked.displaySpots);
110         }
111 
112         spot->updateSprite(&icon, x, y, mDisplayId);
113     }
114 
115     for (Spot* spot : mLocked.displaySpots) {
116         if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) {
117             fadeOutAndReleaseSpotLocked(spot);
118         }
119     }
120 
121     spriteController->closeTransaction();
122 }
123 
clearSpots()124 void TouchSpotController::clearSpots() {
125 #if DEBUG_SPOT_UPDATES
126     ALOGD("clearSpots");
127 #endif
128 
129     std::scoped_lock lock(mLock);
130     fadeOutAndReleaseAllSpotsLocked();
131 }
132 
getSpot(uint32_t id,const std::vector<Spot * > & spots)133 TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id,
134                                                         const std::vector<Spot*>& spots) {
135     for (size_t i = 0; i < spots.size(); i++) {
136         Spot* spot = spots[i];
137         if (spot->id == id) {
138             return spot;
139         }
140     }
141     return nullptr;
142 }
143 
createAndAddSpotLocked(uint32_t id,std::vector<Spot * > & spots)144 TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id,
145                                                                        std::vector<Spot*>& spots)
146         REQUIRES(mLock) {
147     // Remove spots until we have fewer than MAX_SPOTS remaining.
148     while (spots.size() >= MAX_SPOTS) {
149         Spot* spot = removeFirstFadingSpotLocked(spots);
150         if (!spot) {
151             spot = spots[0];
152             spots.erase(spots.begin());
153         }
154         releaseSpotLocked(spot);
155     }
156 
157     // Obtain a sprite from the recycled pool.
158     sp<Sprite> sprite;
159     if (!mLocked.recycledSprites.empty()) {
160         sprite = mLocked.recycledSprites.back();
161         mLocked.recycledSprites.pop_back();
162     } else {
163         sprite = mContext.getSpriteController()->createSprite();
164     }
165 
166     // Return the new spot.
167     Spot* spot = new Spot(id, sprite);
168     spots.push_back(spot);
169     return spot;
170 }
171 
removeFirstFadingSpotLocked(std::vector<Spot * > & spots)172 TouchSpotController::Spot* TouchSpotController::removeFirstFadingSpotLocked(
173         std::vector<Spot*>& spots) REQUIRES(mLock) {
174     for (size_t i = 0; i < spots.size(); i++) {
175         Spot* spot = spots[i];
176         if (spot->id == Spot::INVALID_ID) {
177             spots.erase(spots.begin() + i);
178             return spot;
179         }
180     }
181     return NULL;
182 }
183 
releaseSpotLocked(Spot * spot)184 void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) {
185     spot->sprite->clearIcon();
186 
187     if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
188         mLocked.recycledSprites.push_back(spot->sprite);
189     }
190     delete spot;
191 }
192 
fadeOutAndReleaseSpotLocked(Spot * spot)193 void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) {
194     if (spot->id != Spot::INVALID_ID) {
195         spot->id = Spot::INVALID_ID;
196         startAnimationLocked();
197     }
198 }
199 
fadeOutAndReleaseAllSpotsLocked()200 void TouchSpotController::fadeOutAndReleaseAllSpotsLocked() REQUIRES(mLock) {
201     size_t numSpots = mLocked.displaySpots.size();
202     for (size_t i = 0; i < numSpots; i++) {
203         Spot* spot = mLocked.displaySpots[i];
204         fadeOutAndReleaseSpotLocked(spot);
205     }
206 }
207 
reloadSpotResources()208 void TouchSpotController::reloadSpotResources() {
209     mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
210 }
211 
doAnimations(nsecs_t timestamp)212 bool TouchSpotController::doAnimations(nsecs_t timestamp) {
213     std::scoped_lock lock(mLock);
214     bool keepAnimating = doFadingAnimationLocked(timestamp);
215     if (!keepAnimating) {
216         /*
217          * We know that this callback will be removed before another
218          * is added. mLock in PointerAnimator will not be released
219          * until after this is removed, and adding another callback
220          * requires that lock. Thus it's safe to set mLocked.animating
221          * here.
222          */
223         mLocked.animating = false;
224     }
225     return keepAnimating;
226 }
227 
doFadingAnimationLocked(nsecs_t timestamp)228 bool TouchSpotController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
229     bool keepAnimating = false;
230     nsecs_t animationTime = mContext.getAnimationTime();
231     nsecs_t frameDelay = timestamp - animationTime;
232     size_t numSpots = mLocked.displaySpots.size();
233     for (size_t i = 0; i < numSpots;) {
234         Spot* spot = mLocked.displaySpots[i];
235         if (spot->id == Spot::INVALID_ID) {
236             spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
237             if (spot->alpha <= 0) {
238                 mLocked.displaySpots.erase(mLocked.displaySpots.begin() + i);
239                 releaseSpotLocked(spot);
240                 numSpots--;
241                 continue;
242             } else {
243                 spot->sprite->setAlpha(spot->alpha);
244                 keepAnimating = true;
245             }
246         }
247         ++i;
248     }
249     return keepAnimating;
250 }
251 
startAnimationLocked()252 void TouchSpotController::startAnimationLocked() REQUIRES(mLock) {
253     using namespace std::placeholders;
254 
255     if (mLocked.animating) {
256         return;
257     }
258     mLocked.animating = true;
259 
260     std::function<bool(nsecs_t)> func = std::bind(&TouchSpotController::doAnimations, this, _1);
261     mContext.addAnimationCallback(mDisplayId, func);
262 }
263 
264 } // namespace android
265