Skip to content

Commit 5b5c414

Browse files
committed
Further refine touchMajor processing in ScaleGestureDetector
On some devices the information coming from the touchscreen is very noisy or otherwise unreliable. Perform some processing on the data we have to try to provide a smoother experience. Bug 7267507 Change-Id: I863125f58577f522de05a1361b81c2e42975fd89
1 parent 57e6203 commit 5b5c414

File tree

1 file changed

+49
-108
lines changed

1 file changed

+49
-108
lines changed

core/java/android/view/ScaleGestureDetector.java

Lines changed: 49 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@
1919
import android.content.Context;
2020
import android.os.SystemClock;
2121
import android.util.FloatMath;
22-
import android.util.Log;
23-
24-
import java.util.Arrays;
2522

2623
/**
2724
* Detects scaling transformation gestures using the supplied {@link MotionEvent}s.
@@ -143,11 +140,16 @@ public void onScaleEnd(ScaleGestureDetector detector) {
143140
private int mSpanSlop;
144141
private int mMinSpan;
145142

146-
private float[] mTouchHistoryLastAccepted;
147-
private int[] mTouchHistoryDirection;
148-
private long[] mTouchHistoryLastAcceptedTime;
143+
// Bounds for recently seen values
144+
private float mTouchUpper;
145+
private float mTouchLower;
146+
private float mTouchHistoryLastAccepted;
147+
private int mTouchHistoryDirection;
148+
private long mTouchHistoryLastAcceptedTime;
149+
private int mTouchMinMajor;
149150

150151
private static final long TOUCH_STABILIZE_TIME = 128; // ms
152+
private static final int TOUCH_MIN_MAJOR = 48; // dp
151153

152154
/**
153155
* Consistency verifier for debugging purposes.
@@ -160,6 +162,8 @@ public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
160162
mContext = context;
161163
mListener = listener;
162164
mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
165+
mTouchMinMajor =
166+
(int) (context.getResources().getDisplayMetrics().density * TOUCH_MIN_MAJOR + 0.5f);
163167
mMinSpan = context.getResources().getDimensionPixelSize(
164168
com.android.internal.R.dimen.config_minScalingSpan);
165169
}
@@ -172,123 +176,67 @@ public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
172176
private void addTouchHistory(MotionEvent ev) {
173177
final long currentTime = SystemClock.uptimeMillis();
174178
final int count = ev.getPointerCount();
179+
boolean accept = currentTime - mTouchHistoryLastAcceptedTime >= TOUCH_STABILIZE_TIME;
180+
float total = 0;
181+
int sampleCount = 0;
175182
for (int i = 0; i < count; i++) {
176-
final int id = ev.getPointerId(i);
177-
ensureTouchHistorySize(id);
178-
179-
final boolean hasLastAccepted = !Float.isNaN(mTouchHistoryLastAccepted[id]);
180-
boolean accept = true;
183+
final boolean hasLastAccepted = !Float.isNaN(mTouchHistoryLastAccepted);
181184
final int historySize = ev.getHistorySize();
182-
for (int h = 0; h < historySize + 1; h++) {
183-
final float major;
184-
final float minor;
185+
final int pointerSampleCount = historySize + 1;
186+
for (int h = 0; h < pointerSampleCount; h++) {
187+
float major;
185188
if (h < historySize) {
186189
major = ev.getHistoricalTouchMajor(i, h);
187-
minor = ev.getHistoricalTouchMinor(i, h);
188190
} else {
189191
major = ev.getTouchMajor(i);
190-
minor = ev.getTouchMinor(i);
191192
}
192-
final float avg = (major + minor) / 2;
193+
if (major < mTouchMinMajor) major = mTouchMinMajor;
194+
total += major;
195+
196+
if (Float.isNaN(mTouchUpper) || major > mTouchUpper) {
197+
mTouchUpper = major;
198+
}
199+
if (Float.isNaN(mTouchLower) || major < mTouchLower) {
200+
mTouchLower = major;
201+
}
193202

194203
if (hasLastAccepted) {
195-
final int directionSig = (int) Math.signum(avg - mTouchHistoryLastAccepted[id]);
196-
if (directionSig != mTouchHistoryDirection[id] ||
197-
(directionSig == 0 && mTouchHistoryDirection[id] == 0)) {
198-
mTouchHistoryDirection[id] = directionSig;
204+
final int directionSig = (int) Math.signum(major - mTouchHistoryLastAccepted);
205+
if (directionSig != mTouchHistoryDirection ||
206+
(directionSig == 0 && mTouchHistoryDirection == 0)) {
207+
mTouchHistoryDirection = directionSig;
199208
final long time = h < historySize ? ev.getHistoricalEventTime(h)
200209
: ev.getEventTime();
201-
mTouchHistoryLastAcceptedTime[id] = time;
202-
accept = false;
203-
}
204-
if (currentTime - mTouchHistoryLastAcceptedTime[id] < TOUCH_STABILIZE_TIME) {
210+
mTouchHistoryLastAcceptedTime = time;
205211
accept = false;
206212
}
207213
}
208214
}
209-
210-
if (accept) {
211-
float newAccepted = (ev.getTouchMajor(i) + ev.getTouchMinor(i)) / 2;
212-
if (hasLastAccepted) {
213-
newAccepted = (mTouchHistoryLastAccepted[id] + newAccepted) / 2;
214-
}
215-
mTouchHistoryLastAccepted[id] = newAccepted;
216-
mTouchHistoryDirection[id] = 0;
217-
mTouchHistoryLastAcceptedTime[id] = ev.getEventTime();
218-
}
215+
sampleCount += pointerSampleCount;
219216
}
220-
}
221217

222-
/**
223-
* Clear out the touch history for a given pointer id.
224-
* @param id pointer id to clear
225-
* @see #addTouchHistory(MotionEvent)
226-
*/
227-
private boolean removeTouchHistoryForId(int id) {
228-
if (id >= mTouchHistoryLastAccepted.length) {
229-
return false;
230-
}
231-
mTouchHistoryLastAccepted[id] = Float.NaN;
232-
mTouchHistoryDirection[id] = 0;
233-
mTouchHistoryLastAcceptedTime[id] = 0;
234-
return true;
235-
}
218+
final float avg = total / sampleCount;
236219

237-
/**
238-
* Get the adjusted combined touchMajor/touchMinor value for a given pointer id
239-
* @param id the pointer id of the data to obtain
240-
* @return the adjusted major/minor value for the point at id
241-
* @see #addTouchHistory(MotionEvent)
242-
*/
243-
private float getAdjustedTouchHistory(int id) {
244-
if (id >= mTouchHistoryLastAccepted.length) {
245-
Log.e(TAG, "Error retrieving adjusted touch history for id=" + id +
246-
" - incomplete event stream?");
247-
return 0;
220+
if (accept) {
221+
float newAccepted = (mTouchUpper + mTouchLower + avg) / 3;
222+
mTouchUpper = (mTouchUpper + newAccepted) / 2;
223+
mTouchLower = (mTouchLower + newAccepted) / 2;
224+
mTouchHistoryLastAccepted = newAccepted;
225+
mTouchHistoryDirection = 0;
226+
mTouchHistoryLastAcceptedTime = ev.getEventTime();
248227
}
249-
return mTouchHistoryLastAccepted[id];
250228
}
251229

252230
/**
253231
* Clear all touch history tracking. Useful in ACTION_CANCEL or ACTION_UP.
254232
* @see #addTouchHistory(MotionEvent)
255233
*/
256234
private void clearTouchHistory() {
257-
if (mTouchHistoryLastAccepted == null) {
258-
// All three arrays will be null if this is the case; nothing to do.
259-
return;
260-
}
261-
Arrays.fill(mTouchHistoryLastAccepted, Float.NaN);
262-
Arrays.fill(mTouchHistoryDirection, 0);
263-
Arrays.fill(mTouchHistoryLastAcceptedTime, 0);
264-
}
265-
266-
private void ensureTouchHistorySize(int id) {
267-
final int requiredSize = id + 1;
268-
if (mTouchHistoryLastAccepted == null || mTouchHistoryLastAccepted.length < requiredSize) {
269-
final float[] newLastAccepted = new float[requiredSize];
270-
final int[] newDirection = new int[requiredSize];
271-
final long[] newLastAcceptedTime = new long[requiredSize];
272-
273-
int oldLength = 0;
274-
if (mTouchHistoryLastAccepted != null) {
275-
System.arraycopy(mTouchHistoryLastAccepted, 0, newLastAccepted, 0,
276-
mTouchHistoryLastAccepted.length);
277-
System.arraycopy(mTouchHistoryDirection, 0, newDirection, 0,
278-
mTouchHistoryDirection.length);
279-
System.arraycopy(mTouchHistoryLastAcceptedTime, 0, newLastAcceptedTime, 0,
280-
mTouchHistoryLastAcceptedTime.length);
281-
oldLength = mTouchHistoryLastAccepted.length;
282-
}
283-
284-
Arrays.fill(newLastAccepted, oldLength, newLastAccepted.length, Float.NaN);
285-
Arrays.fill(newDirection, oldLength, newDirection.length, 0);
286-
Arrays.fill(newLastAcceptedTime, oldLength, newLastAcceptedTime.length, 0);
287-
288-
mTouchHistoryLastAccepted = newLastAccepted;
289-
mTouchHistoryDirection = newDirection;
290-
mTouchHistoryLastAcceptedTime = newLastAcceptedTime;
291-
}
235+
mTouchUpper = Float.NaN;
236+
mTouchLower = Float.NaN;
237+
mTouchHistoryLastAccepted = Float.NaN;
238+
mTouchHistoryDirection = 0;
239+
mTouchHistoryLastAcceptedTime = 0;
292240
}
293241

294242
/**
@@ -346,23 +294,16 @@ public boolean onTouchEvent(MotionEvent event) {
346294
final float focusX = sumX / div;
347295
final float focusY = sumY / div;
348296

349-
if (pointerUp) {
350-
final int id = event.getPointerId(event.getActionIndex());
351-
if (!removeTouchHistoryForId(id)) {
352-
Log.e(TAG, "Got ACTION_POINTER_UP for previously unknown id=" + id +
353-
" - incomplete event stream?");
354-
}
355-
} else {
356-
addTouchHistory(event);
357-
}
297+
298+
addTouchHistory(event);
358299

359300
// Determine average deviation from focal point
360301
float devSumX = 0, devSumY = 0;
361302
for (int i = 0; i < count; i++) {
362303
if (skipIndex == i) continue;
363304

364-
// Average touch major and touch minor and convert the resulting diameter into a radius.
365-
final float touchSize = getAdjustedTouchHistory(event.getPointerId(i)) / 2;
305+
// Convert the resulting diameter into a radius.
306+
final float touchSize = mTouchHistoryLastAccepted / 2;
366307
devSumX += Math.abs(event.getX(i) - focusX) + touchSize;
367308
devSumY += Math.abs(event.getY(i) - focusY) + touchSize;
368309
}

0 commit comments

Comments
 (0)