1 /* 2 * Copyright (C) 2008 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 package android.view.cts; 18 19 import android.test.AndroidTestCase; 20 import android.view.MotionEvent; 21 import android.view.VelocityTracker; 22 import android.util.Log; 23 24 /** 25 * Test {@link VelocityTracker}. 26 */ 27 public class VelocityTrackerTest extends AndroidTestCase { 28 private static final String TAG = "VelocityTrackerTest"; 29 30 private static final float TOLERANCE_EXACT = 0.01f; 31 private static final float TOLERANCE_TIGHT = 0.05f; 32 private static final float TOLERANCE_WEAK = 0.1f; 33 private static final float TOLERANCE_VERY_WEAK = 0.2f; 34 35 private VelocityTracker mVelocityTracker; 36 37 // Current position, velocity and acceleration. 38 private long mTime; 39 private long mLastTime; 40 private float mPx, mPy; 41 private float mVx, mVy; 42 private float mAx, mAy; 43 44 @Override setUp()45 protected void setUp() throws Exception { 46 super.setUp(); 47 mVelocityTracker = VelocityTracker.obtain(); 48 mTime = 1000; 49 mLastTime = 0; 50 mPx = 300; 51 mPy = 600; 52 mVx = 0; 53 mVy = 0; 54 mAx = 0; 55 mAy = 0; 56 } 57 58 @Override tearDown()59 protected void tearDown() throws Exception { 60 super.tearDown(); 61 mVelocityTracker.recycle(); 62 } 63 testNoMovement()64 public void testNoMovement() { 65 move(100, 10); 66 assertVelocity(TOLERANCE_EXACT, "Expect exact bound when no movement occurs."); 67 } 68 testLinearMovement()69 public void testLinearMovement() { 70 mVx = 2.0f; 71 mVy = -4.0f; 72 move(100, 10); 73 assertVelocity(TOLERANCE_TIGHT, "Expect tight bound for linear motion."); 74 } 75 testAcceleratingMovement()76 public void testAcceleratingMovement() { 77 // A very good velocity tracking algorithm will produce a tight bound on 78 // simple acceleration. Certain alternate algorithms will fare less well but 79 // may be more stable in the presence of bad input data. 80 mVx = 2.0f; 81 mVy = -4.0f; 82 mAx = 1.0f; 83 mAy = -0.5f; 84 move(200, 10); 85 assertVelocity(TOLERANCE_WEAK, "Expect weak bound when there is acceleration."); 86 } 87 testDeceleratingMovement()88 public void testDeceleratingMovement() { 89 // A very good velocity tracking algorithm will produce a tight bound on 90 // simple acceleration. Certain alternate algorithms will fare less well but 91 // may be more stable in the presence of bad input data. 92 mVx = 2.0f; 93 mVy = -4.0f; 94 mAx = -1.0f; 95 mAy = 0.2f; 96 move(200, 10); 97 assertVelocity(TOLERANCE_WEAK, "Expect weak bound when there is deceleration."); 98 } 99 testLinearSharpDirectionChange()100 public void testLinearSharpDirectionChange() { 101 // After a sharp change of direction we expect the velocity to eventually 102 // converge but it might take a moment to get there. 103 mVx = 2.0f; 104 mVy = -4.0f; 105 move(100, 10); 106 assertVelocity(TOLERANCE_TIGHT, "Expect tight bound for linear motion."); 107 mVx = -1.0f; 108 mVy = -3.0f; 109 move(100, 10); 110 assertVelocity(TOLERANCE_WEAK, "Expect weak bound after 100ms of new direction."); 111 move(100, 10); 112 assertVelocity(TOLERANCE_TIGHT, "Expect tight bound after 200ms of new direction."); 113 } 114 testLinearSharpDirectionChangeAfterALongPause()115 public void testLinearSharpDirectionChangeAfterALongPause() { 116 // Should be able to get a tighter bound if there is a pause before the 117 // change of direction. 118 mVx = 2.0f; 119 mVy = -4.0f; 120 move(100, 10); 121 assertVelocity(TOLERANCE_TIGHT, "Expect tight bound for linear motion."); 122 pause(100); 123 mVx = -1.0f; 124 mVy = -3.0f; 125 move(100, 10); 126 assertVelocity(TOLERANCE_TIGHT, 127 "Expect tight bound after a 100ms pause and 100ms of new direction."); 128 } 129 testChangingAcceleration()130 public void testChangingAcceleration() { 131 // In real circumstances, the acceleration changes continuously throughout a 132 // gesture. Try to model this and see how the algorithm copes. 133 mVx = 2.0f; 134 mVy = -4.0f; 135 for (float change : new float[] { 1, -2, -3, -1, 1 }) { 136 mAx += 1.0f * change; 137 mAy += -0.5f * change; 138 move(30, 10); 139 } 140 assertVelocity(TOLERANCE_VERY_WEAK, 141 "Expect weak bound when there is changing acceleration."); 142 } 143 move(long duration, long step)144 private void move(long duration, long step) { 145 addMovement(); 146 while (duration > 0) { 147 duration -= step; 148 mTime += step; 149 mPx += (mAx / 2 * step + mVx) * step; 150 mPy += (mAy / 2 * step + mVy) * step; 151 mVx += mAx * step; 152 mVy += mAy * step; 153 addMovement(); 154 } 155 } 156 pause(long duration)157 private void pause(long duration) { 158 mTime += duration; 159 } 160 addMovement()161 private void addMovement() { 162 if (mTime >= mLastTime) { 163 MotionEvent ev = MotionEvent.obtain(0L, mTime, MotionEvent.ACTION_MOVE, mPx, mPy, 0); 164 mVelocityTracker.addMovement(ev); 165 ev.recycle(); 166 mLastTime = mTime; 167 168 mVelocityTracker.computeCurrentVelocity(1); 169 final float estimatedVx = mVelocityTracker.getXVelocity(); 170 final float estimatedVy = mVelocityTracker.getYVelocity(); 171 Log.d(TAG, String.format( 172 "[%d] x=%6.1f, y=%6.1f, vx=%6.1f, vy=%6.1f, ax=%6.1f, ay=%6.1f, " 173 + "evx=%6.1f (%6.1f%%), evy=%6.1f (%6.1f%%)", 174 mTime, mPx, mPy, mVx, mVy, mAx, mAy, 175 estimatedVx, error(mVx, estimatedVx) * 100.0f, 176 estimatedVy, error(mVy, estimatedVy) * 100.0f)); 177 } 178 } 179 assertVelocity(float tolerance, String message)180 private void assertVelocity(float tolerance, String message) { 181 mVelocityTracker.computeCurrentVelocity(1); 182 final float estimatedVx = mVelocityTracker.getXVelocity(); 183 final float estimatedVy = mVelocityTracker.getYVelocity(); 184 float errorVx = error(mVx, estimatedVx); 185 float errorVy = error(mVy, estimatedVy); 186 if (errorVx > tolerance || errorVy > tolerance) { 187 fail(String.format("Velocity exceeds tolerance of %6.1f%%: " 188 + "expected vx=%6.1f, vy=%6.1f. " 189 + "actual vx=%6.1f (%6.1f%%), vy=%6.1f (%6.1f%%)", 190 tolerance * 100.0f, mVx, mVy, 191 estimatedVx, errorVx * 100.0f, estimatedVy, errorVy * 100.0f)); 192 } 193 } 194 error(float expected, float actual)195 private static float error(float expected, float actual) { 196 float absError = Math.abs(actual - expected); 197 if (absError < 0.001f) { 198 return 0; 199 } 200 if (Math.abs(expected) < 0.001f) { 201 return 1; 202 } 203 return absError / Math.abs(expected); 204 } 205 } 206