1/* 2 * Copyright (C) 2014 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 17package com.android.systemui.statusbar.phone; 18 19import android.util.Log; 20import android.util.Pools; 21import android.view.MotionEvent; 22 23import java.util.ArrayDeque; 24import java.util.Iterator; 25 26/** 27 * A very simple low-pass velocity filter for motion events for noisy touch screens. 28 */ 29public class NoisyVelocityTracker implements VelocityTrackerInterface { 30 31 private static final Pools.SynchronizedPool<NoisyVelocityTracker> sNoisyPool = 32 new Pools.SynchronizedPool<>(2); 33 34 private static final float DECAY = 0.75f; 35 private static final boolean DEBUG = false; 36 37 private final int MAX_EVENTS = 8; 38 private ArrayDeque<MotionEventCopy> mEventBuf = new ArrayDeque<MotionEventCopy>(MAX_EVENTS); 39 private float mVX, mVY = 0; 40 41 private static class MotionEventCopy { 42 public MotionEventCopy(float x2, float y2, long eventTime) { 43 this.x = x2; 44 this.y = y2; 45 this.t = eventTime; 46 } 47 float x, y; 48 long t; 49 } 50 51 public static NoisyVelocityTracker obtain() { 52 NoisyVelocityTracker instance = sNoisyPool.acquire(); 53 return (instance != null) ? instance : new NoisyVelocityTracker(); 54 } 55 56 private NoisyVelocityTracker() { 57 } 58 59 public void addMovement(MotionEvent event) { 60 if (mEventBuf.size() == MAX_EVENTS) { 61 mEventBuf.remove(); 62 } 63 mEventBuf.add(new MotionEventCopy(event.getX(), event.getY(), event.getEventTime())); 64 } 65 66 public void computeCurrentVelocity(int units) { 67 if (NoisyVelocityTracker.DEBUG) { 68 Log.v("FlingTracker", "computing velocities for " + mEventBuf.size() + " events"); 69 } 70 mVX = mVY = 0; 71 MotionEventCopy last = null; 72 int i = 0; 73 float totalweight = 0f; 74 float weight = 10f; 75 for (final Iterator<MotionEventCopy> iter = mEventBuf.iterator(); 76 iter.hasNext();) { 77 final MotionEventCopy event = iter.next(); 78 if (last != null) { 79 final float dt = (float) (event.t - last.t) / units; 80 final float dx = (event.x - last.x); 81 final float dy = (event.y - last.y); 82 if (NoisyVelocityTracker.DEBUG) { 83 Log.v("FlingTracker", String.format( 84 " [%d] (t=%d %.1f,%.1f) dx=%.1f dy=%.1f dt=%f vx=%.1f vy=%.1f", 85 i, event.t, event.x, event.y, 86 dx, dy, dt, 87 (dx/dt), 88 (dy/dt) 89 )); 90 } 91 if (event.t == last.t) { 92 // Really not sure what to do with events that happened at the same time, 93 // so we'll skip subsequent events. 94 continue; 95 } 96 mVX += weight * dx / dt; 97 mVY += weight * dy / dt; 98 totalweight += weight; 99 weight *= DECAY; 100 } 101 last = event; 102 i++; 103 } 104 if (totalweight > 0) { 105 mVX /= totalweight; 106 mVY /= totalweight; 107 } else { 108 // so as not to contaminate the velocities with NaN 109 mVX = mVY = 0; 110 } 111 112 if (NoisyVelocityTracker.DEBUG) { 113 Log.v("FlingTracker", "computed: vx=" + mVX + " vy=" + mVY); 114 } 115 } 116 117 public float getXVelocity() { 118 if (Float.isNaN(mVX) || Float.isInfinite(mVX)) { 119 mVX = 0; 120 } 121 return mVX; 122 } 123 124 public float getYVelocity() { 125 if (Float.isNaN(mVY) || Float.isInfinite(mVX)) { 126 mVY = 0; 127 } 128 return mVY; 129 } 130 131 public void recycle() { 132 mEventBuf.clear(); 133 sNoisyPool.release(this); 134 } 135} 136