summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain Guy <romainguy@android.com>2009-05-21 23:10:10 -0700
committerRomain Guy <romainguy@android.com>2009-05-22 01:59:59 -0700
commitd6a463a9f23b3901bf729f2f27a6bb8f78b95248 (patch)
tree1371cafd6a1c0fe8d3cd4580e7878a9adb86b183
parentcfcc0df2658d0ce7dc753511bb44ab8ae7a636f7 (diff)
downloadframeworks_base-d6a463a9f23b3901bf729f2f27a6bb8f78b95248.zip
frameworks_base-d6a463a9f23b3901bf729f2f27a6bb8f78b95248.tar.gz
frameworks_base-d6a463a9f23b3901bf729f2f27a6bb8f78b95248.tar.bz2
Add a new API to ListView: setGestures(int). This allows developers to enable gestures to jump inside the list or filter it. This change also introduces a new XML attribute to control this API. It also adds the ability to theme the GestureOverlayView from the gestures library. Finally, this adds a new VERSION header to the binary format used to store the letters for the recognizer.
-rw-r--r--api/current.xml252
-rwxr-xr-xcore/java/android/gesture/GestureOverlayView.java245
-rw-r--r--core/java/android/gesture/GestureStroke.java8
-rw-r--r--core/java/android/gesture/LetterRecognizer.java69
-rw-r--r--core/java/android/gesture/TouchThroughGestureListener.java11
-rw-r--r--core/java/android/view/View.java31
-rw-r--r--core/java/android/view/ViewGroup.java9
-rw-r--r--core/java/android/widget/AbsListView.java389
-rw-r--r--core/java/android/widget/FastScroller.java19
-rw-r--r--core/java/android/widget/PopupWindow.java14
-rw-r--r--core/java/android/widget/TextView.java11
-rw-r--r--core/res/res/layout/list_gestures_overlay.xml19
-rw-r--r--core/res/res/raw/latin_lowercasebin28494 -> 28496 bytes
-rw-r--r--core/res/res/values/attrs.xml34
-rw-r--r--core/res/res/values/public.xml8
-rw-r--r--core/res/res/values/styles.xml13
-rw-r--r--core/res/res/values/themes.xml4
-rwxr-xr-xtests/sketch/res/layout/demo.xml2
-rwxr-xr-xtests/sketch/res/layout/gestureviewer.xml2
-rw-r--r--tests/sketch/res/layout/overlaydemo.xml28
-rw-r--r--tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java9
-rw-r--r--tests/sketch/tools/Converter.java1
22 files changed, 911 insertions, 267 deletions
diff --git a/api/current.xml b/api/current.xml
index 085d8b1..339db77 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3584,17 +3584,6 @@
visibility="public"
>
</field>
-<field name="donut_resource_pad39"
- type="int"
- transient="false"
- volatile="false"
- value="16843385"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="donut_resource_pad4"
type="int"
transient="false"
@@ -3606,61 +3595,6 @@
visibility="public"
>
</field>
-<field name="donut_resource_pad40"
- type="int"
- transient="false"
- volatile="false"
- value="16843384"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad41"
- type="int"
- transient="false"
- volatile="false"
- value="16843383"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad42"
- type="int"
- transient="false"
- volatile="false"
- value="16843382"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad43"
- type="int"
- transient="false"
- volatile="false"
- value="16843381"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad44"
- type="int"
- transient="false"
- volatile="false"
- value="16843380"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="donut_resource_pad5"
type="int"
transient="false"
@@ -4134,6 +4068,28 @@
visibility="public"
>
</field>
+<field name="fadeDuration"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843384"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fadeOffset"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843383"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="fadingEdge"
type="int"
transient="false"
@@ -4453,6 +4409,39 @@
visibility="public"
>
</field>
+<field name="gestureColor"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843381"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="gestureStrokeWidth"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843380"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="gestures"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843385"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="gradientRadius"
type="int"
transient="false"
@@ -8853,6 +8842,17 @@
visibility="public"
>
</field>
+<field name="uncertainGestureColor"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843382"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="unselectedAlpha"
type="int"
transient="false"
@@ -46572,6 +46572,20 @@
<parameter name="attrs" type="android.util.AttributeSet">
</parameter>
</constructor>
+<constructor name="GestureOverlayView"
+ type="android.gesture.GestureOverlayView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+<parameter name="defStyle" type="int">
+</parameter>
+</constructor>
<method name="addOnGestureListener"
return="void"
abstract="false"
@@ -46653,6 +46667,19 @@
visibility="public"
>
</method>
+<method name="processEvent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.MotionEvent">
+</parameter>
+</method>
<method name="removeOnGestureListener"
return="void"
abstract="false"
@@ -46718,27 +46745,6 @@
<parameter name="color" type="int">
</parameter>
</method>
-<field name="DEFAULT_GESTURE_COLOR"
- type="int"
- transient="false"
- volatile="false"
- value="-256"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="DEFAULT_UNCERTAIN_GESTURE_COLOR"
- type="int"
- transient="false"
- volatile="false"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
</class>
<interface name="GestureOverlayView.OnGestureListener"
abstract="true"
@@ -159466,6 +159472,17 @@
visibility="public"
>
</method>
+<method name="getGestures"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getListPaddingBottom"
return="int"
abstract="false"
@@ -159839,6 +159856,19 @@
<parameter name="filterText" type="java.lang.String">
</parameter>
</method>
+<method name="setGestures"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gestures" type="int">
+</parameter>
+</method>
<method name="setOnScrollListener"
return="void"
abstract="false"
@@ -159984,6 +160014,39 @@
<parameter name="dr" type="android.graphics.drawable.Drawable">
</parameter>
</method>
+<field name="GESTURES_FILTER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="GESTURES_JUMP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="GESTURES_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TRANSCRIPT_MODE_ALWAYS_SCROLL"
type="int"
transient="false"
@@ -168608,6 +168671,21 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+</method>
+<method name="update"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
<parameter name="x" type="int">
</parameter>
<parameter name="y" type="int">
diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java
index bffd12e..64ddbbd 100755
--- a/core/java/android/gesture/GestureOverlayView.java
+++ b/core/java/android/gesture/GestureOverlayView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009 The Android Open Source Project
+ * Copyright (C) 2009 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.
@@ -17,62 +17,54 @@
package android.gesture;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
-import android.graphics.Color;
-import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import com.android.internal.R;
import java.util.ArrayList;
/**
* A (transparent) overlay for gesture input that can be placed on top of other
- * widgets. The view can also be opaque.
+ * widgets.
+ *
+ * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeWidth
+ * @attr ref android.R.styleable#GestureOverlayView_gestureColor
+ * @attr ref android.R.styleable#GestureOverlayView_uncertainGestureColor
+ * @attr ref android.R.styleable#GestureOverlayView_fadeDuration
+ * @attr ref android.R.styleable#GestureOverlayView_fadeOffset
*/
-
public class GestureOverlayView extends View {
- static final float TOUCH_TOLERANCE = 3;
-
- // TODO: Move all these values into XML attributes
private static final int TRANSPARENT_BACKGROUND = 0x00000000;
-
- // TODO: SHOULD BE A TOTAL DURATION
- private static final float FADING_ALPHA_CHANGE = 0.15f;
- private static final long FADING_OFFSET = 300;
- private static final long FADING_REFRESHING_RATE = 16;
-
- private static final int GESTURE_STROKE_WIDTH = 12;
private static final boolean GESTURE_RENDERING_ANTIALIAS = true;
-
private static final boolean DITHER_FLAG = true;
- public static final int DEFAULT_GESTURE_COLOR = 0xFFFFFF00;
- public static final int DEFAULT_UNCERTAIN_GESTURE_COLOR = Color.argb(60, 255, 255, 0);
-
- private static final int REFRESH_RANGE = 10;
-
- private static final BlurMaskFilter BLUR_MASK_FILTER =
- new BlurMaskFilter(1, BlurMaskFilter.Blur.NORMAL);
-
private Paint mGesturePaint;
private final Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);
- private Bitmap mBitmap; // with transparent background
+ private Bitmap mBitmap;
private Canvas mBitmapCanvas;
- private int mCertainGestureColor = DEFAULT_GESTURE_COLOR;
- private int mUncertainGestureColor = DEFAULT_UNCERTAIN_GESTURE_COLOR;
+ private long mFadeDuration = 300;
+ private long mFadeOffset = 300;
+ private long mFadingStart;
- // for rendering immediate ink feedback
- private Rect mInvalidRect = new Rect();
+ private float mGestureStroke = 12.0f;
+ private int mCertainGestureColor = 0xFFFFFF00;
+ private int mUncertainGestureColor = 0x3CFFFF00;
+ private int mInvalidateExtraBorder = 10;
- private Path mPath;
+ // for rendering immediate ink feedback
+ private final Rect mInvalidRect = new Rect();
+ private final Path mPath = new Path();
private float mX;
private float mY;
@@ -84,26 +76,32 @@ public class GestureOverlayView extends View {
private Gesture mCurrentGesture = null;
// TODO: Make this a list of WeakReferences
- private final ArrayList<OnGestureListener> mOnGestureListeners = new ArrayList<OnGestureListener>();
- private ArrayList<GesturePoint> mPointBuffer = null;
+ private final ArrayList<OnGestureListener> mOnGestureListeners =
+ new ArrayList<OnGestureListener>();
+ private final ArrayList<GesturePoint> mPointBuffer = new ArrayList<GesturePoint>(100);
// fading out effect
private boolean mIsFadingOut = false;
private float mFadingAlpha = 1;
-
- private Handler mHandler = new Handler();
+ private final AccelerateDecelerateInterpolator mInterpolator =
+ new AccelerateDecelerateInterpolator();
private final Runnable mFadingOut = new Runnable() {
public void run() {
if (mIsFadingOut) {
- mFadingAlpha -= FADING_ALPHA_CHANGE;
- if (mFadingAlpha <= 0) {
+ final long now = AnimationUtils.currentAnimationTimeMillis();
+ final long duration = now - mFadingStart;
+
+ if (duration > mFadeDuration) {
mIsFadingOut = false;
- mPath = null;
+ mPath.rewind();
mCurrentGesture = null;
mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
} else {
- mHandler.postDelayed(this, FADING_REFRESHING_RATE);
+ float interpolatedTime = Math.max(0.0f,
+ Math.min(1.0f, duration / (float) mFadeDuration));
+ mFadingAlpha = 1.0f - mInterpolator.getInterpolation(interpolatedTime);
+ postDelayed(this, 16);
}
invalidate();
}
@@ -116,7 +114,27 @@ public class GestureOverlayView extends View {
}
public GestureOverlayView(Context context, AttributeSet attrs) {
- super(context, attrs);
+ this(context, attrs, com.android.internal.R.attr.gestureOverlayViewStyle);
+ }
+
+ public GestureOverlayView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.GestureOverlayView, defStyle, 0);
+
+ mGestureStroke = a.getFloat(R.styleable.GestureOverlayView_gestureStrokeWidth,
+ mGestureStroke);
+ mInvalidateExtraBorder = Math.max(1, ((int) mGestureStroke) - 1);
+ mCertainGestureColor = a.getColor(R.styleable.GestureOverlayView_gestureColor,
+ mCertainGestureColor);
+ mUncertainGestureColor = a.getColor(R.styleable.GestureOverlayView_uncertainGestureColor,
+ mUncertainGestureColor);
+ mFadeDuration = a.getInt(R.styleable.GestureOverlayView_fadeDuration, (int) mFadeDuration);
+ mFadeOffset = a.getInt(R.styleable.GestureOverlayView_fadeOffset, (int) mFadeOffset);
+
+ a.recycle();
+
init();
}
@@ -139,6 +157,7 @@ public class GestureOverlayView extends View {
mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
mCurrentGesture.draw(mBitmapCanvas, mGesturePaint);
}
+ invalidate();
}
public void setGestureColor(int color) {
@@ -157,6 +176,16 @@ public class GestureOverlayView extends View {
return mCertainGestureColor;
}
+ public float getGestureStroke() {
+ return mGestureStroke;
+ }
+
+ public void setGestureStroke(float gestureStroke) {
+ mGestureStroke = gestureStroke;
+ mInvalidateExtraBorder = Math.max(1, ((int) mGestureStroke) - 1);
+ mGesturePaint.setStrokeWidth(mGestureStroke);
+ }
+
/**
* Set the gesture to be shown in the view
*
@@ -182,14 +211,12 @@ public class GestureOverlayView extends View {
final Paint gesturePaint = mGesturePaint;
gesturePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS);
- gesturePaint.setColor(DEFAULT_GESTURE_COLOR);
+ gesturePaint.setColor(mCertainGestureColor);
gesturePaint.setStyle(Paint.Style.STROKE);
gesturePaint.setStrokeJoin(Paint.Join.ROUND);
gesturePaint.setStrokeCap(Paint.Cap.ROUND);
- gesturePaint.setStrokeWidth(GESTURE_STROKE_WIDTH);
+ gesturePaint.setStrokeWidth(mGestureStroke);
gesturePaint.setDither(DITHER_FLAG);
-
- mPath = null;
}
@Override
@@ -226,6 +253,10 @@ public class GestureOverlayView extends View {
mOnGestureListeners.remove(listener);
}
+ public void removeAllOnGestureListeners() {
+ mOnGestureListeners.clear();
+ }
+
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -240,25 +271,24 @@ public class GestureOverlayView extends View {
}
// draw the current stroke
- if (mPath != null) {
- canvas.drawPath(mPath, mGesturePaint);
- }
+ canvas.drawPath(mPath, mGesturePaint);
}
/**
* Clear up the overlay
*
* @param fadeOut whether the gesture on the overlay should fade out
- * gradually or disappear immediately
+ * gradually or disappear immediately
*/
public void clear(boolean fadeOut) {
if (fadeOut) {
- mFadingAlpha = 1;
+ mFadingAlpha = 1.0f;
mIsFadingOut = true;
- mHandler.removeCallbacks(mFadingOut);
- mHandler.postDelayed(mFadingOut, FADING_OFFSET);
+ removeCallbacks(mFadingOut);
+ mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset;
+ postDelayed(mFadingOut, mFadeOffset);
} else {
- mPath = null;
+ mPath.rewind();
mCurrentGesture = null;
if (mBitmap != null) {
mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
@@ -269,7 +299,7 @@ public class GestureOverlayView extends View {
public void cancelFadingOut() {
mIsFadingOut = false;
- mHandler.removeCallbacks(mFadingOut);
+ removeCallbacks(mFadingOut);
}
@Override
@@ -278,6 +308,12 @@ public class GestureOverlayView extends View {
return true;
}
+ processEvent(event);
+
+ return true;
+ }
+
+ public void processEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Rect rect = touchStart(event);
@@ -290,12 +326,14 @@ public class GestureOverlayView extends View {
}
break;
case MotionEvent.ACTION_UP:
- touchUp(event);
+ touchUp(event, false);
+ invalidate();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ touchUp(event, true);
invalidate();
break;
}
-
- return true;
}
private Rect touchStart(MotionEvent event) {
@@ -310,7 +348,7 @@ public class GestureOverlayView extends View {
// if there is fading out going on, stop it.
if (mIsFadingOut) {
mIsFadingOut = false;
- mHandler.removeCallbacks(mFadingOut);
+ removeCallbacks(mFadingOut);
mBitmap.eraseColor(TRANSPARENT_BACKGROUND);
mCurrentGesture = null;
}
@@ -325,14 +363,13 @@ public class GestureOverlayView extends View {
mCurrentGesture = new Gesture();
}
- mPointBuffer = new ArrayList<GesturePoint>();
mPointBuffer.add(new GesturePoint(x, y, event.getEventTime()));
- mPath = new Path();
+ mPath.rewind();
mPath.moveTo(x, y);
- mInvalidRect.set((int) x - REFRESH_RANGE, (int) y - REFRESH_RANGE,
- (int) x + REFRESH_RANGE, (int) y + REFRESH_RANGE);
+ mInvalidRect.set((int) x - mInvalidateExtraBorder, (int) y - mInvalidateExtraBorder,
+ (int) x + mInvalidateExtraBorder, (int) y + mInvalidateExtraBorder);
mCurveEndX = x;
mCurveEndY = y;
@@ -343,36 +380,47 @@ public class GestureOverlayView extends View {
private Rect touchMove(MotionEvent event) {
Rect areaToRefresh = null;
- float x = event.getX();
- float y = event.getY();
+ final float x = event.getX();
+ final float y = event.getY();
+
+ final float previousX = mX;
+ final float previousY = mY;
- float dx = Math.abs(x - mX);
- float dy = Math.abs(y - mY);
+ final float dx = Math.abs(x - previousX);
+ final float dy = Math.abs(y - previousY);
- if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
-
+ if (dx >= GestureStroke.TOUCH_TOLERANCE || dy >= GestureStroke.TOUCH_TOLERANCE) {
+ areaToRefresh = mInvalidRect;
+
// start with the curve end
- mInvalidRect.set((int) mCurveEndX - REFRESH_RANGE, (int) mCurveEndY - REFRESH_RANGE,
- (int) mCurveEndX + REFRESH_RANGE, (int) mCurveEndY + REFRESH_RANGE);
+ areaToRefresh.set(
+ (int) mCurveEndX - mInvalidateExtraBorder,
+ (int) mCurveEndY - mInvalidateExtraBorder,
+ (int) mCurveEndX + mInvalidateExtraBorder,
+ (int) mCurveEndY + mInvalidateExtraBorder);
- mCurveEndX = (x + mX) / 2;
- mCurveEndY = (y + mY) / 2;
- mPath.quadTo(mX, mY, mCurveEndX, mCurveEndY);
+ mCurveEndX = (x + previousX) / 2;
+ mCurveEndY = (y + previousY) / 2;
+
+ mPath.quadTo(previousX, previousY, mCurveEndX, mCurveEndY);
// union with the control point of the new curve
- mInvalidRect.union((int) mX - REFRESH_RANGE, (int) mY - REFRESH_RANGE,
- (int) mX + REFRESH_RANGE, (int) mY + REFRESH_RANGE);
+ areaToRefresh.union(
+ (int) previousX - mInvalidateExtraBorder,
+ (int) previousY - mInvalidateExtraBorder,
+ (int) previousX + mInvalidateExtraBorder,
+ (int) previousY + mInvalidateExtraBorder);
// union with the end point of the new curve
- mInvalidRect.union((int) mCurveEndX - REFRESH_RANGE, (int) mCurveEndY - REFRESH_RANGE,
- (int) mCurveEndX + REFRESH_RANGE, (int) mCurveEndY + REFRESH_RANGE);
+ areaToRefresh.union(
+ (int) mCurveEndX - mInvalidateExtraBorder,
+ (int) mCurveEndY - mInvalidateExtraBorder,
+ (int) mCurveEndX + mInvalidateExtraBorder,
+ (int) mCurveEndY + mInvalidateExtraBorder);
- areaToRefresh = mInvalidRect;
-
mX = x;
mY = y;
}
-
mPointBuffer.add(new GesturePoint(x, y, event.getEventTime()));
@@ -386,34 +434,43 @@ public class GestureOverlayView extends View {
return areaToRefresh;
}
- private void touchUp(MotionEvent event) {
+ private void touchUp(MotionEvent event, boolean cancel) {
// add the stroke to the current gesture
mCurrentGesture.addStroke(new GestureStroke(mPointBuffer));
// add the stroke to the double buffer
- mGesturePaint.setMaskFilter(BLUR_MASK_FILTER);
mBitmapCanvas.drawPath(mPath, mGesturePaint);
- mGesturePaint.setMaskFilter(null);
-
- // pass the event to handlers
- final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
- final int count = listeners.size();
- for (int i = 0; i < count; i++) {
- listeners.get(i).onGestureEnded(this, event);
+
+ if (!cancel) {
+ // pass the event to handlers
+ final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
+ final int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ listeners.get(i).onGestureEnded(this, event);
+ }
+ } else {
+ // pass the event to handlers
+ final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
+ final int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ listeners.get(i).onGestureCancelled(this, event);
+ }
}
-
- mPath = null;
- mPointBuffer = null;
+
+ mPath.rewind();
+ mPointBuffer.clear();
}
/**
* An interface for processing gesture events
*/
public static interface OnGestureListener {
- public void onGestureStarted(GestureOverlayView overlay, MotionEvent event);
+ void onGestureStarted(GestureOverlayView overlay, MotionEvent event);
+
+ void onGesture(GestureOverlayView overlay, MotionEvent event);
- public void onGesture(GestureOverlayView overlay, MotionEvent event);
+ void onGestureEnded(GestureOverlayView overlay, MotionEvent event);
- public void onGestureEnded(GestureOverlayView overlay, MotionEvent event);
+ void onGestureCancelled(GestureOverlayView overlay, MotionEvent event);
}
}
diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java
index 5160a76..6d022b4 100644
--- a/core/java/android/gesture/GestureStroke.java
+++ b/core/java/android/gesture/GestureStroke.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009 The Android Open Source Project
+ * Copyright (C) 2009 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.
@@ -31,6 +31,8 @@ import java.util.ArrayList;
* A gesture stroke started on a touch down and ended on a touch up.
*/
public class GestureStroke {
+ static final float TOUCH_TOLERANCE = 3;
+
public final RectF boundingBox;
public final float length;
@@ -156,8 +158,8 @@ public class GestureStroke {
} else {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
- if (dx >= GestureOverlayView.TOUCH_TOLERANCE ||
- dy >= GestureOverlayView.TOUCH_TOLERANCE) {
+ if (dx >= TOUCH_TOLERANCE ||
+ dy >= TOUCH_TOLERANCE) {
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
diff --git a/core/java/android/gesture/LetterRecognizer.java b/core/java/android/gesture/LetterRecognizer.java
index 4476746..b26b3f2 100644
--- a/core/java/android/gesture/LetterRecognizer.java
+++ b/core/java/android/gesture/LetterRecognizer.java
@@ -45,32 +45,27 @@ public class LetterRecognizer {
private GestureLibrary mGestureLibrary;
+ private final Comparator<Prediction> mComparator = new PredictionComparator();
+
private static class SigmoidUnit {
final float[] mWeights;
- private boolean mComputed;
- private float mResult;
-
SigmoidUnit(float[] weights) {
mWeights = weights;
}
private float compute(float[] inputs) {
- if (!mComputed) {
- float sum = 0;
+ float sum = 0;
- final int count = inputs.length;
- final float[] weights = mWeights;
+ final int count = inputs.length;
+ final float[] weights = mWeights;
- for (int i = 0; i < count; i++) {
- sum += inputs[i] * weights[i];
- }
- sum += weights[weights.length - 1];
-
- mResult = 1.0f / (float) (1 + Math.exp(-sum));
- mComputed = true;
+ for (int i = 0; i < count; i++) {
+ sum += inputs[i] * weights[i];
}
- return mResult;
+ sum += weights[weights.length - 1];
+
+ return 1.0f / (float) (1 + Math.exp(-sum));
}
}
@@ -91,16 +86,25 @@ public class LetterRecognizer {
}
public ArrayList<Prediction> recognize(Gesture gesture) {
+ return recognize(gesture, null);
+ }
+
+ public ArrayList<Prediction> recognize(Gesture gesture, ArrayList<Prediction> predictions) {
float[] query = GestureUtilities.spatialSampling(gesture, mPatchSize);
- ArrayList<Prediction> predictions = classify(query);
+ predictions = classify(query, predictions);
adjustPrediction(gesture, predictions);
return predictions;
}
- private ArrayList<Prediction> classify(float[] vector) {
+ private ArrayList<Prediction> classify(float[] vector, ArrayList<Prediction> predictions) {
+ if (predictions == null) {
+ predictions = new ArrayList<Prediction>();
+ } else {
+ predictions.clear();
+ }
+
final float[] intermediateOutput = compute(mHiddenLayer, vector);
final float[] output = compute(mOutputLayer, intermediateOutput);
- final ArrayList<Prediction> predictions = new ArrayList<Prediction>();
double sum = 0;
@@ -117,19 +121,8 @@ public class LetterRecognizer {
predictions.get(i).score /= sum;
}
- Collections.sort(predictions, new Comparator<Prediction>() {
- public int compare(Prediction object1, Prediction object2) {
- double score1 = object1.score;
- double score2 = object2.score;
- if (score1 > score2) {
- return -1;
- } else if (score1 < score2) {
- return 1;
- } else {
- return 0;
- }
- }
- });
+ Collections.sort(predictions, mComparator);
+
return predictions;
}
@@ -270,4 +263,18 @@ public class LetterRecognizer {
}
}
}
+
+ private static class PredictionComparator implements Comparator<Prediction> {
+ public int compare(Prediction object1, Prediction object2) {
+ double score1 = object1.score;
+ double score2 = object2.score;
+ if (score1 > score2) {
+ return -1;
+ } else if (score1 < score2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
}
diff --git a/core/java/android/gesture/TouchThroughGestureListener.java b/core/java/android/gesture/TouchThroughGestureListener.java
index 7621ddf..09a528d 100644
--- a/core/java/android/gesture/TouchThroughGestureListener.java
+++ b/core/java/android/gesture/TouchThroughGestureListener.java
@@ -55,7 +55,7 @@ public class TouchThroughGestureListener implements GestureOverlayView.OnGesture
private boolean mStealEvents = false;
public TouchThroughGestureListener(View model) {
- this(model, false);
+ this(model, true);
}
public TouchThroughGestureListener(View model, boolean stealEvents) {
@@ -125,7 +125,7 @@ public class TouchThroughGestureListener implements GestureOverlayView.OnGesture
overlay.setGestureDrawingColor(overlay.getGestureColor());
if (mStealEvents) {
event = MotionEvent.obtain(event.getDownTime(), System.currentTimeMillis(),
- MotionEvent.ACTION_UP, x, y, event.getPressure(), event.getSize(),
+ MotionEvent.ACTION_CANCEL, x, y, event.getPressure(), event.getSize(),
event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
event.getDeviceId(), event.getEdgeFlags());
}
@@ -153,6 +153,13 @@ public class TouchThroughGestureListener implements GestureOverlayView.OnGesture
}
}
+ public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) {
+ overlay.clear(mIsGesturing);
+ if (!mIsGesturing) {
+ dispatchEventToModel(event);
+ }
+ }
+
public void addOnGestureActionListener(OnGesturePerformedListener listener) {
mPerformedListeners.add(listener);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1f72eea..bcb97ed 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -538,21 +538,48 @@ import java.util.WeakHashMap;
* take care of redrawing the appropriate views until the animation completes.
* </p>
*
+ * @attr ref android.R.styleable#View_background
+ * @attr ref android.R.styleable#View_clickable
+ * @attr ref android.R.styleable#View_contentDescription
+ * @attr ref android.R.styleable#View_drawingCacheQuality
+ * @attr ref android.R.styleable#View_duplicateParentState
+ * @attr ref android.R.styleable#View_id
+ * @attr ref android.R.styleable#View_fadingEdge
+ * @attr ref android.R.styleable#View_fadingEdgeLength
* @attr ref android.R.styleable#View_fitsSystemWindows
+ * @attr ref android.R.styleable#View_isScrollContainer
+ * @attr ref android.R.styleable#View_focusable
+ * @attr ref android.R.styleable#View_focusableInTouchMode
+ * @attr ref android.R.styleable#View_hapticFeedbackEnabled
+ * @attr ref android.R.styleable#View_keepScreenOn
+ * @attr ref android.R.styleable#View_longClickable
+ * @attr ref android.R.styleable#View_minHeight
+ * @attr ref android.R.styleable#View_minWidth
* @attr ref android.R.styleable#View_nextFocusDown
* @attr ref android.R.styleable#View_nextFocusLeft
* @attr ref android.R.styleable#View_nextFocusRight
* @attr ref android.R.styleable#View_nextFocusUp
+ * @attr ref android.R.styleable#View_onClick
+ * @attr ref android.R.styleable#View_padding
+ * @attr ref android.R.styleable#View_paddingBottom
+ * @attr ref android.R.styleable#View_paddingLeft
+ * @attr ref android.R.styleable#View_paddingRight
+ * @attr ref android.R.styleable#View_paddingTop
+ * @attr ref android.R.styleable#View_saveEnabled
* @attr ref android.R.styleable#View_scrollX
* @attr ref android.R.styleable#View_scrollY
- * @attr ref android.R.styleable#View_scrollbarTrackHorizontal
- * @attr ref android.R.styleable#View_scrollbarThumbHorizontal
* @attr ref android.R.styleable#View_scrollbarSize
+ * @attr ref android.R.styleable#View_scrollbarStyle
* @attr ref android.R.styleable#View_scrollbars
+ * @attr ref android.R.styleable#View_scrollbarTrackHorizontal
+ * @attr ref android.R.styleable#View_scrollbarThumbHorizontal
* @attr ref android.R.styleable#View_scrollbarThumbVertical
* @attr ref android.R.styleable#View_scrollbarTrackVertical
* @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack
* @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack
+ * @attr ref android.R.styleable#View_soundEffectsEnabled
+ * @attr ref android.R.styleable#View_tag
+ * @attr ref android.R.styleable#View_visibility
*
* @see android.view.ViewGroup
*/
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index bf04dcd..c6f36a0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -53,6 +53,15 @@ import java.util.ArrayList;
* <p>
* Also see {@link LayoutParams} for layout attributes.
* </p>
+ *
+ * @attr ref android.R.styleable#ViewGroup_clipChildren
+ * @attr ref android.R.styleable#ViewGroup_clipToPadding
+ * @attr ref android.R.styleable#ViewGroup_layoutAnimation
+ * @attr ref android.R.styleable#ViewGroup_animationCache
+ * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
+ * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
+ * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
+ * @attr ref android.R.styleable#ViewGroup_descendantFocusability
*/
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
private static final boolean DBG = false;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1ca59b2..8a538d7 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -41,12 +41,18 @@ import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.KeyCharacterMap;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputMethodManager;
import android.view.ContextMenu.ContextMenuInfo;
+import android.gesture.GestureOverlayView;
+import android.gesture.TouchThroughGestureListener;
+import android.gesture.Gesture;
+import android.gesture.LetterRecognizer;
+import android.gesture.Prediction;
import com.android.internal.R;
@@ -54,7 +60,9 @@ import java.util.ArrayList;
import java.util.List;
/**
- * Common code shared between ListView and GridView
+ * Base class that can be used to implement virtualized lists of items. A list does
+ * not have a spatial definition here. For instance, subclases of this class can
+ * display the content of the list in a grid, in a carousel, as stack, etc.
*
* @attr ref android.R.styleable#AbsListView_listSelector
* @attr ref android.R.styleable#AbsListView_drawSelectorOnTop
@@ -65,6 +73,7 @@ import java.util.List;
* @attr ref android.R.styleable#AbsListView_cacheColorHint
* @attr ref android.R.styleable#AbsListView_fastScrollEnabled
* @attr ref android.R.styleable#AbsListView_smoothScrollbar
+ * @attr ref android.R.styleable#AbsListView_gestures
*/
public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,
ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,
@@ -93,6 +102,31 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
public static final int TRANSCRIPT_MODE_ALWAYS_SCROLL = 2;
/**
+ * Disables gestures.
+ *
+ * @see #setGestures(int)
+ * @see #GESTURES_JUMP
+ * @see #GESTURES_FILTER
+ */
+ public static final int GESTURES_NONE = 0;
+ /**
+ * When a letter gesture is recognized the list jumps to a matching position.
+ *
+ * @see #setGestures(int)
+ * @see #GESTURES_NONE
+ * @see #GESTURES_FILTER
+ */
+ public static final int GESTURES_JUMP = 1;
+ /**
+ * When a letter gesture is recognized the letter is added to the filter.
+ *
+ * @see #setGestures(int)
+ * @see #GESTURES_NONE
+ * @see #GESTURES_JUMP
+ */
+ public static final int GESTURES_FILTER = 2;
+
+ /**
* Indicates that we are not in the middle of a touch gesture
*/
static final int TOUCH_MODE_REST = -1;
@@ -427,8 +461,23 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
*/
private FastScroller mFastScroller;
- private int mTouchSlop;
+ /**
+ * Indicates the type of gestures to use: GESTURES_NONE, GESTURES_FILTER or GESTURES_NONE
+ */
+ private int mGestures;
+
+ // Used to implement the gestures overlay
+ private GestureOverlayView mGesturesOverlay;
+ private PopupWindow mGesturesPopup;
+ private ViewTreeObserver.OnGlobalLayoutListener mGesturesLayoutListener;
+ private boolean mGlobalLayoutListenerAddedGestures;
+ private boolean mInstallGesturesOverlay;
+ private TouchThroughGestureListener mGesturesListener;
+ private boolean mPreviousGesturing;
+
+ private boolean mGlobalLayoutListenerAddedFilter;
+ private int mTouchSlop;
private float mDensityScale;
private InputConnection mDefInputConnection;
@@ -535,10 +584,197 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
boolean smoothScrollbar = a.getBoolean(R.styleable.AbsListView_smoothScrollbar, true);
setSmoothScrollbarEnabled(smoothScrollbar);
-
+
+ int defaultGestures = GESTURES_NONE;
+ if (useTextFilter) {
+ defaultGestures = GESTURES_FILTER;
+ } else if (enableFastScroll) {
+ defaultGestures = GESTURES_JUMP;
+ }
+ int gestures = a.getInt(R.styleable.AbsListView_gestures, defaultGestures);
+ setGestures(gestures);
+
a.recycle();
}
+ private void initAbsListView() {
+ // Setting focusable in touch mode will set the focusable property to true
+ setFocusableInTouchMode(true);
+ setWillNotDraw(false);
+ setAlwaysDrawnWithCacheEnabled(false);
+ setScrollingCacheEnabled(true);
+
+ mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ mDensityScale = getContext().getResources().getDisplayMetrics().density;
+ }
+
+ /**
+ * <p>Sets the type of gestures to use with this list. When gestures are enabled,
+ * that is if the <code>gestures</code> parameter is not {@link #GESTURES_NONE},
+ * the user can draw characters on top of this view. When a character is
+ * recognized and matches a known character, the list will either:</p>
+ * <ul>
+ * <li>Jump to the appropriate position ({@link #GESTURES_JUMP})</li>
+ * <li>Add the character to the current filter ({@link #GESTURES_FILTER})</li>
+ * </ul>
+ * <p>Using {@link #GESTURES_JUMP} requires {@link #isFastScrollEnabled()} to
+ * be true. Using {@link #GESTURES_FILTER} requires {@link #isTextFilterEnabled()}
+ * to be true.</p>
+ *
+ * @param gestures The type of gestures to enable for this list:
+ * {@link #GESTURES_NONE}, {@link #GESTURES_JUMP} or {@link #GESTURES_FILTER}
+ *
+ * @see #GESTURES_NONE
+ * @see #GESTURES_JUMP
+ * @see #GESTURES_FILTER
+ * @see #getGestures()
+ */
+ public void setGestures(int gestures) {
+ switch (gestures) {
+ case GESTURES_JUMP:
+ if (!mFastScrollEnabled) {
+ throw new IllegalStateException("Jump gestures can only be used with "
+ + "fast scroll enabled");
+ }
+ break;
+ case GESTURES_FILTER:
+ if (!mTextFilterEnabled) {
+ throw new IllegalStateException("Filter gestures can only be used with "
+ + "text filtering enabled");
+ }
+ break;
+ }
+
+ final int oldGestures = mGestures;
+ mGestures = gestures;
+
+ // Install overlay later
+ if (oldGestures == GESTURES_NONE && gestures != GESTURES_NONE) {
+ mInstallGesturesOverlay = true;
+ // Uninstall overlay
+ } else if (oldGestures != GESTURES_NONE && gestures == GESTURES_NONE) {
+ uninstallGesturesOverlay();
+ }
+ }
+
+ /**
+ * Indicates what gestures are enabled on this view.
+ *
+ * @return {@link #GESTURES_NONE}, {@link #GESTURES_JUMP} or {@link #GESTURES_FILTER}
+ *
+ * @see #GESTURES_NONE
+ * @see #GESTURES_JUMP
+ * @see #GESTURES_FILTER
+ * @see #setGestures(int)
+ */
+ @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.IntToString(from = GESTURES_NONE, to = "NONE"),
+ @ViewDebug.IntToString(from = GESTURES_JUMP, to = "JUMP"),
+ @ViewDebug.IntToString(from = GESTURES_FILTER, to = "FILTER")
+ })
+ public int getGestures() {
+ return mGestures;
+ }
+
+ private void dismissGesturesPopup() {
+ if (mGesturesPopup != null) {
+ mGesturesPopup.dismiss();
+ }
+ }
+
+ private void showGesturesPopup() {
+ // Make sure we have a window before showing the popup
+ if (getWindowVisibility() == View.VISIBLE) {
+ installGesturesOverlay();
+ positionGesturesPopup();
+ }
+ }
+
+ private void positionGesturesPopup() {
+ final int[] xy = new int[2];
+ getLocationOnScreen(xy);
+ if (!mGesturesPopup.isShowing()) {
+ mGesturesPopup.showAtLocation(this, Gravity.LEFT | Gravity.TOP, xy[0], xy[1]);
+ } else {
+ mGesturesPopup.update(xy[0], xy[1], -1, -1);
+ }
+ }
+
+ private void installGesturesOverlay() {
+ mInstallGesturesOverlay = false;
+
+ if (mGesturesPopup == null) {
+ final Context c = getContext();
+ final LayoutInflater layoutInflater = (LayoutInflater)
+ c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mGesturesOverlay = (GestureOverlayView)
+ layoutInflater.inflate(R.layout.list_gestures_overlay, null);
+
+ final PopupWindow p = new PopupWindow(c);
+ p.setFocusable(false);
+ p.setTouchable(false);
+ p.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+ p.setContentView(mGesturesOverlay);
+ p.setWidth(getWidth());
+ p.setHeight(getHeight());
+ p.setBackgroundDrawable(null);
+
+ if (mGesturesLayoutListener == null) {
+ mGesturesLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
+ public void onGlobalLayout() {
+ if (isShown()) {
+ showGesturesPopup();
+ } else if (mGesturesPopup.isShowing()) {
+ dismissGesturesPopup();
+ }
+ }
+ };
+ }
+ getViewTreeObserver().addOnGlobalLayoutListener(mGesturesLayoutListener);
+ mGlobalLayoutListenerAddedGestures = true;
+
+ mGesturesPopup = p;
+
+ mGesturesOverlay.removeAllOnGestureListeners();
+ mGesturesListener = new TouchThroughGestureListener(null);
+ mGesturesListener.setGestureType(TouchThroughGestureListener.MULTIPLE_STROKE);
+ mGesturesListener.addOnGestureActionListener(new GesturesProcessor());
+ mGesturesOverlay.addOnGestureListener(mGesturesListener);
+
+ mPreviousGesturing = false;
+ }
+ }
+
+ private void uninstallGesturesOverlay() {
+ dismissGesturesPopup();
+ mGesturesPopup = null;
+ if (mGesturesLayoutListener != null) {
+ getViewTreeObserver().removeGlobalOnLayoutListener(mGesturesLayoutListener);
+ }
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (mGestures != GESTURES_NONE) {
+ mGesturesOverlay.processEvent(ev);
+
+ final boolean isGesturing = mGesturesListener.isGesturing();
+
+ if (!isGesturing) {
+ mPreviousGesturing = isGesturing;
+ return super.dispatchTouchEvent(ev);
+ } else if (!mPreviousGesturing){
+ mPreviousGesturing = isGesturing;
+ final MotionEvent event = MotionEvent.obtain(ev);
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ super.dispatchTouchEvent(event);
+ return true;
+ }
+ }
+
+ return super.dispatchTouchEvent(ev);
+ }
+
/**
* Enables fast scrolling by letting the user quickly scroll through lists by
* dragging the fast scroll thumb. The adapter attached to the list may want
@@ -712,17 +948,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
- private void initAbsListView() {
- // Setting focusable in touch mode will set the focusable property to true
- setFocusableInTouchMode(true);
- setWillNotDraw(false);
- setAlwaysDrawnWithCacheEnabled(false);
- setScrollingCacheEnabled(true);
-
- mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- mDensityScale = getContext().getResources().getDisplayMetrics().density;
- }
-
private void useDefaultSelector() {
setSelector(getResources().getDrawable(
com.android.internal.R.drawable.list_selector_background));
@@ -908,11 +1133,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
private boolean acceptFilter() {
- if (!mTextFilterEnabled || !(getAdapter() instanceof Filterable) ||
- ((Filterable) getAdapter()).getFilter() == null) {
- return false;
- }
- return true;
+ return mTextFilterEnabled && getAdapter() instanceof Filterable &&
+ ((Filterable) getAdapter()).getFilter() != null;
}
/**
@@ -1096,6 +1318,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
listPadding.bottom = mSelectionBottomPadding + mPaddingBottom;
}
+ /**
+ * Subclasses should NOT override this method but
+ * {@link #layoutChildren()} instead.
+ */
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
@@ -1111,17 +1337,27 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
protected boolean setFrame(int left, int top, int right, int bottom) {
final boolean changed = super.setFrame(left, top, right, bottom);
- // Reposition the popup when the frame has changed. This includes
- // translating the widget, not just changing its dimension. The
- // filter popup needs to follow the widget.
- if (mFiltered && changed && getWindowVisibility() == View.VISIBLE && mPopup != null &&
- mPopup.isShowing()) {
- positionPopup();
+ if (changed) {
+ // Reposition the popup when the frame has changed. This includes
+ // translating the widget, not just changing its dimension. The
+ // filter popup needs to follow the widget.
+ final boolean visible = getWindowVisibility() == View.VISIBLE;
+ if (mFiltered && visible && mPopup != null && mPopup.isShowing()) {
+ positionPopup();
+ }
+
+ if (mGestures != GESTURES_NONE && visible && mGesturesPopup != null &&
+ mGesturesPopup.isShowing()) {
+ positionGesturesPopup();
+ }
}
return changed;
}
+ /**
+ * Subclasses must override this method to layout their children.
+ */
protected void layoutChildren() {
}
@@ -1324,9 +1560,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mDataChanged = true;
rememberSyncState();
}
+
if (mFastScroller != null) {
mFastScroller.onSizeChanged(w, h, oldw, oldh);
}
+
+ if (mInstallGesturesOverlay) {
+ installGesturesOverlay();
+ positionGesturesPopup();
+ } else if (mGesturesPopup != null) {
+ mGesturesPopup.update(w, h);
+ }
}
/**
@@ -1510,6 +1754,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final ViewTreeObserver treeObserver = getViewTreeObserver();
if (treeObserver != null) {
treeObserver.addOnTouchModeChangeListener(this);
+ if (mTextFilterEnabled && mPopup != null && !mGlobalLayoutListenerAddedFilter) {
+ treeObserver.addOnGlobalLayoutListener(this);
+ }
+ if (mGestures != GESTURES_NONE && mGesturesPopup != null &&
+ !mGlobalLayoutListenerAddedGestures) {
+ treeObserver.addOnGlobalLayoutListener(mGesturesLayoutListener);
+ }
}
}
@@ -1520,6 +1771,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final ViewTreeObserver treeObserver = getViewTreeObserver();
if (treeObserver != null) {
treeObserver.removeOnTouchModeChangeListener(this);
+ if (mTextFilterEnabled && mPopup != null) {
+ treeObserver.removeGlobalOnLayoutListener(this);
+ mGlobalLayoutListenerAddedFilter = false;
+ }
+ if (mGesturesLayoutListener != null && mGesturesPopup != null) {
+ mGlobalLayoutListenerAddedGestures = false;
+ treeObserver.removeGlobalOnLayoutListener(mGesturesLayoutListener);
+ }
}
}
@@ -1534,6 +1793,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
removeCallbacks(mFlingRunnable);
// Always hide the type filter
dismissPopup();
+ dismissGesturesPopup();
if (touchMode == TOUCH_MODE_OFF) {
// Remember the last selected element
@@ -1544,6 +1804,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// Show the type filter only if a filter is in effect
showPopup();
}
+ if (mGestures != GESTURES_NONE) {
+ showGesturesPopup();
+ }
// If we changed touch mode since the last time we had focus
if (touchMode != mLastTouchMode && mLastTouchMode != TOUCH_MODE_UNKNOWN) {
@@ -2775,7 +3038,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
/**
* Removes the filter window
*/
- void dismissPopup() {
+ private void dismissPopup() {
if (mPopup != null) {
mPopup.dismiss();
}
@@ -3017,6 +3280,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
p.setBackgroundDrawable(null);
mPopup = p;
getViewTreeObserver().addOnGlobalLayoutListener(this);
+ mGlobalLayoutListenerAddedFilter = true;
}
if (animateEntrance) {
mPopup.setAnimationStyle(com.android.internal.R.style.Animation_TypingFilter);
@@ -3583,4 +3847,77 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
}
+
+ private class GesturesProcessor implements
+ TouchThroughGestureListener.OnGesturePerformedListener {
+
+ private static final double SCORE_THRESHOLD = 0.1;
+
+ private LetterRecognizer mRecognizer;
+ private ArrayList<Prediction> mPredictions;
+ private final KeyCharacterMap mKeyMap;
+ private final char[] mHolder;
+
+ GesturesProcessor() {
+ mRecognizer = LetterRecognizer.getLetterRecognizer(getContext(),
+ LetterRecognizer.RECOGNIZER_LATIN_LOWERCASE);
+ if (mGestures == GESTURES_FILTER) {
+ mKeyMap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+ mHolder = new char[1];
+ } else {
+ mKeyMap = null;
+ mHolder = null;
+ }
+ }
+
+ public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
+ mPredictions = mRecognizer.recognize(gesture, mPredictions);
+ if (!mPredictions.isEmpty()) {
+ final Prediction prediction = mPredictions.get(0);
+ if (prediction.score > SCORE_THRESHOLD) {
+ switch (mGestures) {
+ case GESTURES_JUMP:
+ processJump(prediction);
+ break;
+ case GESTURES_FILTER:
+ processFilter(prediction);
+ break;
+ }
+ }
+ }
+ }
+
+ private void processJump(Prediction prediction) {
+ final Object[] sections = mFastScroller.getSections();
+ if (sections != null) {
+ final String name = prediction.name;
+ final int count = sections.length;
+
+ int index = -1;
+ for (int i = 0; i < count; i++) {
+ if (name.equalsIgnoreCase((String) sections[i])) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index != -1) {
+ final SectionIndexer indexer = mFastScroller.getSectionIndexer();
+ final int position = indexer.getPositionForSection(index);
+ setSelection(position);
+ }
+ }
+ }
+
+ private void processFilter(Prediction prediction) {
+ mHolder[0] = prediction.name.charAt(0);
+ final KeyEvent[] events = mKeyMap.getEvents(mHolder);
+ if (events != null) {
+ for (KeyEvent event : events) {
+ sendToTextFilter(event.getKeyCode(), event.getRepeatCount(),
+ event);
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 3368477..b9fd5a6 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -134,7 +134,7 @@ class FastScroller {
mScrollCompleted = true;
- getSections();
+ getSectionsFromIndexer();
mOverlayPos = new RectF();
mScrollFade = new ScrollFade();
@@ -250,7 +250,18 @@ class FastScroller {
}
}
- private void getSections() {
+ SectionIndexer getSectionIndexer() {
+ return mSectionIndexer;
+ }
+
+ Object[] getSections() {
+ if (mListAdapter == null && mList != null) {
+ getSectionsFromIndexer();
+ }
+ return mSections;
+ }
+
+ private void getSectionsFromIndexer() {
Adapter adapter = mList.getAdapter();
mSectionIndexer = null;
if (adapter instanceof HeaderViewListAdapter) {
@@ -411,7 +422,7 @@ class FastScroller {
setState(STATE_DRAGGING);
if (mListAdapter == null && mList != null) {
- getSections();
+ getSectionsFromIndexer();
}
cancelFling();
@@ -448,7 +459,7 @@ class FastScroller {
}
return false;
}
-
+
public class ScrollFade implements Runnable {
long mStartTime;
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 975277b..68764a5 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1072,6 +1072,20 @@ public class PopupWindow {
mWindowManager.updateViewLayout(mPopupView, p);
}
}
+
+ /**
+ * <p>Updates the dimension of the popup window. Calling this function
+ * also updates the window with the current popup state as described
+ * for {@link #update()}.</p>
+ *
+ * @param width the new width
+ * @param height the new height
+ */
+ public void update(int width, int height) {
+ WindowManager.LayoutParams p = (WindowManager.LayoutParams)
+ mPopupView.getLayoutParams();
+ update(p.x, p.y, width, height, false);
+ }
/**
* <p>Updates the position and the dimension of the popup window. Width and
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 40a72a4..5c75af2c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -127,6 +127,8 @@ import java.util.ArrayList;
* @attr ref android.R.styleable#TextView_textColor
* @attr ref android.R.styleable#TextView_textColorHighlight
* @attr ref android.R.styleable#TextView_textColorHint
+ * @attr ref android.R.styleable#TextView_textAppearance
+ * @attr ref android.R.styleable#TextView_textColorLink
* @attr ref android.R.styleable#TextView_textSize
* @attr ref android.R.styleable#TextView_textScaleX
* @attr ref android.R.styleable#TextView_typeface
@@ -164,13 +166,22 @@ import java.util.ArrayList;
* @attr ref android.R.styleable#TextView_capitalize
* @attr ref android.R.styleable#TextView_autoText
* @attr ref android.R.styleable#TextView_editable
+ * @attr ref android.R.styleable#TextView_freezesText
+ * @attr ref android.R.styleable#TextView_ellipsize
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableBottom
* @attr ref android.R.styleable#TextView_drawableRight
* @attr ref android.R.styleable#TextView_drawableLeft
+ * @attr ref android.R.styleable#TextView_drawablePadding
* @attr ref android.R.styleable#TextView_lineSpacingExtra
* @attr ref android.R.styleable#TextView_lineSpacingMultiplier
* @attr ref android.R.styleable#TextView_marqueeRepeatLimit
+ * @attr ref android.R.styleable#TextView_inputType
+ * @attr ref android.R.styleable#TextView_imeOptions
+ * @attr ref android.R.styleable#TextView_privateImeOptions
+ * @attr ref android.R.styleable#TextView_imeActionLabel
+ * @attr ref android.R.styleable#TextView_imeActionId
+ * @attr ref android.R.styleable#TextView_editorExtras
*/
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
diff --git a/core/res/res/layout/list_gestures_overlay.xml b/core/res/res/layout/list_gestures_overlay.xml
new file mode 100644
index 0000000..54d72c8
--- /dev/null
+++ b/core/res/res/layout/list_gestures_overlay.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<android.gesture.GestureOverlayView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" />
diff --git a/core/res/res/raw/latin_lowercase b/core/res/res/raw/latin_lowercase
index 5c38b91..fd67333 100644
--- a/core/res/res/raw/latin_lowercase
+++ b/core/res/res/raw/latin_lowercase
Binary files differ
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 052ab35..c4c2446 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -324,6 +324,8 @@
<attr name="expandableListViewStyle" format="reference" />
<!-- Default Gallery style. -->
<attr name="galleryStyle" format="reference" />
+ <!-- Default GestureOverlayView style. -->
+ <attr name="gestureOverlayViewStyle" format="reference" />
<!-- Default GridView style. -->
<attr name="gridViewStyle" format="reference" />
<!-- The style resource to use for an ImageButton -->
@@ -1379,6 +1381,19 @@
will use only the number of items in the adapter and the number of items visible
on screen to determine the scrollbar's properties. -->
<attr name="smoothScrollbar" format="boolean" />
+ <!-- Defines the type of gesture to enable for the widget. -->
+ <attr name="gestures">
+ <!-- No gesture -->
+ <enum name="none" value="0" />
+ <!-- Gestures jump to a specific position in the content. This requires
+ fast scroll to be enabled. If fast scroll is enabled from XML,
+ jump gestures will be enabled automatically. -->
+ <enum name="jump" value="1" />
+ <!-- Gestures filter the content. This requires text filtering to be enabled.
+ If text filtering is enabled from XML, filter gestures will be enabled
+ automatically. -->
+ <enum name="filter" value="2" />
+ </attr>
</declare-styleable>
<declare-styleable name="AbsSpinner">
<!-- Reference to an array resource that will populate the Spinner. For static content,
@@ -1581,7 +1596,7 @@
<!-- Height of the divider. Will use the intrinsic height of the divider if this
is not specified. -->
<attr name="dividerHeight" format="dimension" />
- <!-- Defines the choice behavior for the List. By default, Lists do not have
+ <!-- Defines the choice behavior for the ListView. By default, lists do not have
any choice behavior. By setting the choiceMode to singleChoice, the List
allows up to one item to be in a chosen state. By setting the choiceMode to
multipleChoice, the list allows any number of items to be chosen. -->
@@ -2061,6 +2076,23 @@
<attr name="animateOnClick" format="boolean" />
</declare-styleable>
+ <!-- GestureOverlayView specific attributes. These attributes are used to configure
+ a GestureOverlayView from XML. -->
+ <declare-styleable name="GestureOverlayView">
+ <!-- Width of the stroke used to draw the gesture. -->
+ <attr name="gestureStrokeWidth" format="float" />
+ <!-- Color used to draw a gesture. -->
+ <attr name="gestureColor" format="color" />
+ <!-- Color used to draw the user's strokes until we are sure it's a gesture. -->
+ <attr name="uncertainGestureColor" format="color" />
+ <!-- Time, in milliseconds, to wait before the gesture fades out after the user
+ is done drawing it. -->
+ <attr name="fadeOffset" format="integer" />
+ <!-- Duration, in milliseconds, of the fade out effect after the user is done
+ drawing a gesture. -->
+ <attr name="fadeDuration" format="integer" />
+ </declare-styleable>
+
<!-- ======================================= -->
<!-- Widget package parent layout attributes -->
<!-- ======================================= -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 07bb759..55aecc4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1101,7 +1101,13 @@
<public type="attr" name="maxSdkVersion" />
<public type="attr" name="testOnly" />
<public type="attr" name="contentDescription" />
-
+ <public type="attr" name="gestureStrokeWidth" />
+ <public type="attr" name="gestureColor" />
+ <public type="attr" name="uncertainGestureColor" />
+ <public type="attr" name="fadeOffset" />
+ <public type="attr" name="fadeDuration" />
+ <public type="attr" name="gestures" />
+
<public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />
<public-padding type="id" name="donut_resource_pad" end="0x01020040" />
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 8160c9c..09e299a 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -171,6 +171,19 @@
<item name="android:fadingEdge">vertical</item>
</style>
+ <style name="Widget.GestureOverlayView">
+ <item name="android:gestureStrokeWidth">12.0</item>
+ <item name="android:gestureColor">#ffffff00</item>
+ <item name="android:uncertainGestureColor">#3cffff00</item>
+ <item name="android:fadeOffset">300</item>
+ <item name="android:fadeDuration">300</item>
+ </style>
+
+ <style name="Widget.GestureOverlayView.White">
+ <item name="android:gestureColor">#ff00ff00</item>
+ <item name="android:uncertainGestureColor">#3c00ff00</item>
+ </style>
+
<style name="Widget.Button">
<item name="android:background">@android:drawable/btn_default</item>
<item name="android:focusable">true</item>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index b168fb8..00dc6fa 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -142,7 +142,8 @@
<item name="editTextStyle">@android:style/Widget.EditText</item>
<item name="expandableListViewStyle">@android:style/Widget.ExpandableListView</item>
<item name="galleryStyle">@android:style/Widget.Gallery</item>
- <item name="gridViewStyle">@android:style/Widget.GridView</item>
+ <item name="gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView</item>
+ <item name="gridViewStyle">@android:style/Widget.GridView</item>
<item name="imageButtonStyle">@android:style/Widget.ImageButton</item>
<item name="imageWellStyle">@android:style/Widget.ImageWell</item>
<item name="listViewStyle">@android:style/Widget.ListView</item>
@@ -225,6 +226,7 @@
<item name="textCheckMark">@android:drawable/indicator_check_mark_light</item>
<item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_dark</item>
+ <item name="gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView.White</item>
<item name="listViewStyle">@android:style/Widget.ListView.White</item>
<item name="listDivider">@drawable/divider_horizontal_bright</item>
<item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item>
diff --git a/tests/sketch/res/layout/demo.xml b/tests/sketch/res/layout/demo.xml
index 8c9161a..2ef291a 100755
--- a/tests/sketch/res/layout/demo.xml
+++ b/tests/sketch/res/layout/demo.xml
@@ -25,7 +25,7 @@
android:drawSelectorOnTop="true"
android:prompt="@string/recognition_result"/>
- <com.android.gesture.GestureOverlay
+ <android.gesture.GestureOverlayView
android:id="@+id/drawingpad"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
diff --git a/tests/sketch/res/layout/gestureviewer.xml b/tests/sketch/res/layout/gestureviewer.xml
index 73d6a35..1503736 100755
--- a/tests/sketch/res/layout/gestureviewer.xml
+++ b/tests/sketch/res/layout/gestureviewer.xml
@@ -26,7 +26,7 @@
android:drawSelectorOnTop="true"
android:prompt="@string/recognition_result"/>
- <com.android.gesture.GestureOverlay
+ <android.gesture.GestureOverlayView
android:id="@+id/drawingpad"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
diff --git a/tests/sketch/res/layout/overlaydemo.xml b/tests/sketch/res/layout/overlaydemo.xml
index b6bbab3..75c86ed 100644
--- a/tests/sketch/res/layout/overlaydemo.xml
+++ b/tests/sketch/res/layout/overlaydemo.xml
@@ -1,12 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
+<!-- Copyright (C) 2009 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.
+-->
+
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list"
android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <ListView
- android:id="@+id/list"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1"/>
-</LinearLayout>
+ android:layout_height="fill_parent" />
diff --git a/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java b/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java
index eda4224..f3081b7 100644
--- a/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java
+++ b/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java
@@ -101,9 +101,12 @@ public class ContactListGestureOverlay extends Activity {
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
ArrayList<Prediction> predictions = mRecognizer.recognize(gesture);
if (!predictions.isEmpty()) {
- Log.v(LOGTAG, "1st Prediction : " + predictions.get(0).name);
- Log.v(LOGTAG, "2nd Prediction : " + predictions.get(1).name);
- Log.v(LOGTAG, "3rd Prediction : " + predictions.get(2).name);
+ Log.v(LOGTAG, "1st Prediction : " + predictions.get(0).name +
+ " @" + predictions.get(0).score);
+ Log.v(LOGTAG, "2nd Prediction : " + predictions.get(1).name +
+ " @" + predictions.get(1).score);
+ Log.v(LOGTAG, "3rd Prediction : " + predictions.get(2).name +
+ " @" + predictions.get(2).score);
int index = mContactAdapter.search(predictions.get(0).name);
if (index != -1) {
mContactList.setSelection(index);
diff --git a/tests/sketch/tools/Converter.java b/tests/sketch/tools/Converter.java
index c0391d8..5db2769 100644
--- a/tests/sketch/tools/Converter.java
+++ b/tests/sketch/tools/Converter.java
@@ -146,6 +146,7 @@ public class Converter {
long start = System.nanoTime();
+ in.readShort();
iCount = in.readInt();
hCount = in.readInt();
oCount = in.readInt();