|
|
|
|
@@ -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,
|
|
|
|
|
@@ -92,6 +101,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
|
|
|
|
|
*/
|
|
|
|
|
@@ -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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|