2020-07-17 19:48:24 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define LOG_TAG "TouchSpotController"
|
|
|
|
|
|
|
|
// Log debug messages about pointer updates
|
|
|
|
#define DEBUG_SPOT_UPDATES 0
|
|
|
|
|
|
|
|
#include "TouchSpotController.h"
|
|
|
|
|
|
|
|
#include <log/log.h>
|
|
|
|
|
|
|
|
#include <SkBitmap.h>
|
|
|
|
#include <SkBlendMode.h>
|
|
|
|
#include <SkCanvas.h>
|
|
|
|
#include <SkColor.h>
|
|
|
|
#include <SkPaint.h>
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
// Time to spend fading out the spot completely.
|
|
|
|
const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace android {
|
|
|
|
|
|
|
|
// --- Spot ---
|
|
|
|
|
|
|
|
void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
|
|
|
|
int32_t displayId) {
|
|
|
|
sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
|
|
|
|
sprite->setAlpha(alpha);
|
|
|
|
sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
|
|
|
|
sprite->setPosition(x, y);
|
|
|
|
sprite->setDisplayId(displayId);
|
|
|
|
this->x = x;
|
|
|
|
this->y = y;
|
|
|
|
|
|
|
|
if (icon != mLastIcon) {
|
|
|
|
mLastIcon = icon;
|
|
|
|
if (icon) {
|
|
|
|
sprite->setIcon(*icon);
|
|
|
|
sprite->setVisible(true);
|
|
|
|
} else {
|
|
|
|
sprite->setVisible(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --- TouchSpotController ---
|
|
|
|
|
|
|
|
TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context)
|
|
|
|
: mDisplayId(displayId), mContext(context) {
|
|
|
|
mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
|
|
|
|
}
|
|
|
|
|
|
|
|
TouchSpotController::~TouchSpotController() {
|
|
|
|
std::scoped_lock lock(mLock);
|
|
|
|
|
|
|
|
size_t numSpots = mLocked.displaySpots.size();
|
|
|
|
for (size_t i = 0; i < numSpots; i++) {
|
|
|
|
delete mLocked.displaySpots[i];
|
|
|
|
}
|
|
|
|
mLocked.displaySpots.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
|
|
|
|
BitSet32 spotIdBits) {
|
|
|
|
#if DEBUG_SPOT_UPDATES
|
|
|
|
ALOGD("setSpots: idBits=%08x", spotIdBits.value);
|
|
|
|
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
|
|
|
|
uint32_t id = idBits.firstMarkedBit();
|
|
|
|
idBits.clearBit(id);
|
|
|
|
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
|
|
|
|
ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
|
|
|
|
c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y),
|
|
|
|
c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), displayId);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
std::scoped_lock lock(mLock);
|
|
|
|
sp<SpriteController> spriteController = mContext.getSpriteController();
|
|
|
|
spriteController->openTransaction();
|
|
|
|
|
|
|
|
// Add or move spots for fingers that are down.
|
|
|
|
for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
|
|
|
|
uint32_t id = idBits.clearFirstMarkedBit();
|
|
|
|
const PointerCoords& c = spotCoords[spotIdToIndex[id]];
|
|
|
|
const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
|
|
|
|
? mResources.spotTouch
|
|
|
|
: mResources.spotHover;
|
|
|
|
float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
|
|
|
|
float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
|
|
|
|
|
|
|
|
Spot* spot = getSpot(id, mLocked.displaySpots);
|
|
|
|
if (!spot) {
|
|
|
|
spot = createAndAddSpotLocked(id, mLocked.displaySpots);
|
|
|
|
}
|
|
|
|
|
|
|
|
spot->updateSprite(&icon, x, y, mDisplayId);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Spot* spot : mLocked.displaySpots) {
|
|
|
|
if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) {
|
|
|
|
fadeOutAndReleaseSpotLocked(spot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spriteController->closeTransaction();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TouchSpotController::clearSpots() {
|
|
|
|
#if DEBUG_SPOT_UPDATES
|
|
|
|
ALOGD("clearSpots");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
std::scoped_lock lock(mLock);
|
|
|
|
fadeOutAndReleaseAllSpotsLocked();
|
|
|
|
}
|
|
|
|
|
|
|
|
TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id,
|
|
|
|
const std::vector<Spot*>& spots) {
|
|
|
|
for (size_t i = 0; i < spots.size(); i++) {
|
|
|
|
Spot* spot = spots[i];
|
|
|
|
if (spot->id == id) {
|
|
|
|
return spot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id,
|
2020-08-14 04:00:11 +00:00
|
|
|
std::vector<Spot*>& spots)
|
|
|
|
REQUIRES(mLock) {
|
2020-07-17 19:48:24 +00:00
|
|
|
// Remove spots until we have fewer than MAX_SPOTS remaining.
|
|
|
|
while (spots.size() >= MAX_SPOTS) {
|
|
|
|
Spot* spot = removeFirstFadingSpotLocked(spots);
|
|
|
|
if (!spot) {
|
|
|
|
spot = spots[0];
|
|
|
|
spots.erase(spots.begin());
|
|
|
|
}
|
|
|
|
releaseSpotLocked(spot);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain a sprite from the recycled pool.
|
|
|
|
sp<Sprite> sprite;
|
|
|
|
if (!mLocked.recycledSprites.empty()) {
|
|
|
|
sprite = mLocked.recycledSprites.back();
|
|
|
|
mLocked.recycledSprites.pop_back();
|
|
|
|
} else {
|
|
|
|
sprite = mContext.getSpriteController()->createSprite();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the new spot.
|
|
|
|
Spot* spot = new Spot(id, sprite);
|
|
|
|
spots.push_back(spot);
|
|
|
|
return spot;
|
|
|
|
}
|
|
|
|
|
|
|
|
TouchSpotController::Spot* TouchSpotController::removeFirstFadingSpotLocked(
|
|
|
|
std::vector<Spot*>& spots) REQUIRES(mLock) {
|
|
|
|
for (size_t i = 0; i < spots.size(); i++) {
|
|
|
|
Spot* spot = spots[i];
|
|
|
|
if (spot->id == Spot::INVALID_ID) {
|
|
|
|
spots.erase(spots.begin() + i);
|
|
|
|
return spot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) {
|
|
|
|
spot->sprite->clearIcon();
|
|
|
|
|
|
|
|
if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
|
|
|
|
mLocked.recycledSprites.push_back(spot->sprite);
|
|
|
|
}
|
|
|
|
delete spot;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) {
|
|
|
|
if (spot->id != Spot::INVALID_ID) {
|
|
|
|
spot->id = Spot::INVALID_ID;
|
2020-08-14 04:00:11 +00:00
|
|
|
startAnimationLocked();
|
2020-07-17 19:48:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TouchSpotController::fadeOutAndReleaseAllSpotsLocked() REQUIRES(mLock) {
|
|
|
|
size_t numSpots = mLocked.displaySpots.size();
|
|
|
|
for (size_t i = 0; i < numSpots; i++) {
|
|
|
|
Spot* spot = mLocked.displaySpots[i];
|
|
|
|
fadeOutAndReleaseSpotLocked(spot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TouchSpotController::reloadSpotResources() {
|
|
|
|
mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
|
|
|
|
}
|
|
|
|
|
2020-08-14 04:00:11 +00:00
|
|
|
bool TouchSpotController::doAnimations(nsecs_t timestamp) {
|
2020-07-17 19:48:24 +00:00
|
|
|
std::scoped_lock lock(mLock);
|
2020-08-14 04:00:11 +00:00
|
|
|
bool keepAnimating = doFadingAnimationLocked(timestamp);
|
|
|
|
if (!keepAnimating) {
|
|
|
|
/*
|
|
|
|
* We know that this callback will be removed before another
|
|
|
|
* is added. mLock in PointerAnimator will not be released
|
|
|
|
* until after this is removed, and adding another callback
|
|
|
|
* requires that lock. Thus it's safe to set mLocked.animating
|
|
|
|
* here.
|
|
|
|
*/
|
|
|
|
mLocked.animating = false;
|
|
|
|
}
|
|
|
|
return keepAnimating;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TouchSpotController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
|
|
|
|
bool keepAnimating = false;
|
2020-07-17 19:48:24 +00:00
|
|
|
nsecs_t animationTime = mContext.getAnimationTime();
|
|
|
|
nsecs_t frameDelay = timestamp - animationTime;
|
|
|
|
size_t numSpots = mLocked.displaySpots.size();
|
|
|
|
for (size_t i = 0; i < numSpots;) {
|
|
|
|
Spot* spot = mLocked.displaySpots[i];
|
|
|
|
if (spot->id == Spot::INVALID_ID) {
|
|
|
|
spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
|
|
|
|
if (spot->alpha <= 0) {
|
|
|
|
mLocked.displaySpots.erase(mLocked.displaySpots.begin() + i);
|
|
|
|
releaseSpotLocked(spot);
|
|
|
|
numSpots--;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
spot->sprite->setAlpha(spot->alpha);
|
|
|
|
keepAnimating = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
return keepAnimating;
|
|
|
|
}
|
|
|
|
|
2020-08-14 04:00:11 +00:00
|
|
|
void TouchSpotController::startAnimationLocked() REQUIRES(mLock) {
|
|
|
|
using namespace std::placeholders;
|
|
|
|
|
|
|
|
if (mLocked.animating) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mLocked.animating = true;
|
|
|
|
|
|
|
|
std::function<bool(nsecs_t)> func = std::bind(&TouchSpotController::doAnimations, this, _1);
|
|
|
|
mContext.addAnimationCallback(mDisplayId, func);
|
|
|
|
}
|
|
|
|
|
2020-07-17 19:48:24 +00:00
|
|
|
} // namespace android
|