| /* |
| * 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<NoisyVelocityTracker> 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<MotionEventCopy> mEventBuf = new ArrayDeque<MotionEventCopy>(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<MotionEventCopy> 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); |
| } |
| } |