/* * Copyright (C) 2014 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 */ package com.android.systemui.statusbar.phone; import android.util.Log; import android.util.Pools; import android.view.MotionEvent; import java.util.ArrayDeque; import java.util.Iterator; /** * A very simple low-pass velocity filter for motion events for noisy touch screens. */ public class NoisyVelocityTracker implements VelocityTrackerInterface { private static final Pools.SynchronizedPool sNoisyPool = new Pools.SynchronizedPool<>(2); private static final float DECAY = 0.75f; private static final boolean DEBUG = false; private final int MAX_EVENTS = 8; private ArrayDeque mEventBuf = new ArrayDeque(MAX_EVENTS); private float mVX, mVY = 0; private static class MotionEventCopy { public MotionEventCopy(float x2, float y2, long eventTime) { this.x = x2; this.y = y2; this.t = eventTime; } float x, y; long t; } public static NoisyVelocityTracker obtain() { NoisyVelocityTracker instance = sNoisyPool.acquire(); return (instance != null) ? instance : new NoisyVelocityTracker(); } private NoisyVelocityTracker() { } public void addMovement(MotionEvent event) { if (mEventBuf.size() == MAX_EVENTS) { mEventBuf.remove(); } mEventBuf.add(new MotionEventCopy(event.getX(), event.getY(), event.getEventTime())); } public void computeCurrentVelocity(int units) { if (NoisyVelocityTracker.DEBUG) { Log.v("FlingTracker", "computing velocities for " + mEventBuf.size() + " events"); } mVX = mVY = 0; MotionEventCopy last = null; int i = 0; float totalweight = 0f; float weight = 10f; for (final Iterator iter = mEventBuf.iterator(); iter.hasNext();) { final MotionEventCopy event = iter.next(); if (last != null) { final float dt = (float) (event.t - last.t) / units; final float dx = (event.x - last.x); final float dy = (event.y - last.y); if (NoisyVelocityTracker.DEBUG) { Log.v("FlingTracker", String.format( " [%d] (t=%d %.1f,%.1f) dx=%.1f dy=%.1f dt=%f vx=%.1f vy=%.1f", i, event.t, event.x, event.y, dx, dy, dt, (dx/dt), (dy/dt) )); } if (event.t == last.t) { // Really not sure what to do with events that happened at the same time, // so we'll skip subsequent events. continue; } mVX += weight * dx / dt; mVY += weight * dy / dt; totalweight += weight; weight *= DECAY; } last = event; i++; } if (totalweight > 0) { mVX /= totalweight; mVY /= totalweight; } else { // so as not to contaminate the velocities with NaN mVX = mVY = 0; } if (NoisyVelocityTracker.DEBUG) { Log.v("FlingTracker", "computed: vx=" + mVX + " vy=" + mVY); } } public float getXVelocity() { if (Float.isNaN(mVX) || Float.isInfinite(mVX)) { mVX = 0; } return mVX; } public float getYVelocity() { if (Float.isNaN(mVY) || Float.isInfinite(mVX)) { mVY = 0; } return mVY; } public void recycle() { mEventBuf.clear(); sNoisyPool.release(this); } }