merge f41c640d84 w/minor conflict

This commit is contained in:
The Android Open Source Project
2009-05-22 11:24:36 -07:00
22 changed files with 911 additions and 267 deletions

View File

@@ -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);
}
}
}
}
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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 {