2020import android .content .Context ;
2121import android .database .DataSetObserver ;
2222import android .graphics .Rect ;
23+ import android .os .Handler ;
2324import android .os .Parcel ;
2425import android .os .Parcelable ;
2526import android .support .v4 .util .SparseArrayCompat ;
@@ -138,6 +139,33 @@ public abstract class ExtendableListView extends AbsListView {
138139
139140 protected boolean mClipToPadding ;
140141 private PerformClick mPerformClick ;
142+
143+ private Runnable mPendingCheckForTap ;
144+ private CheckForLongPress mPendingCheckForLongPress ;
145+
146+ private class CheckForLongPress extends WindowRunnnable implements Runnable {
147+ public void run () {
148+ final int motionPosition = mMotionPosition ;
149+ final View child = getChildAt (motionPosition );
150+ if (child != null ) {
151+ final int longPressPosition = mMotionPosition ;
152+ final long longPressId = mAdapter .getItemId (mMotionPosition + mFirstPosition );
153+
154+ boolean handled = false ;
155+ if (sameWindow () && !mDataChanged ) {
156+ handled = performLongPress (child , longPressPosition + mFirstPosition , longPressId );
157+ }
158+ if (handled ) {
159+ mTouchMode = TOUCH_MODE_IDLE ;
160+ setPressed (false );
161+ child .setPressed (false );
162+ } else {
163+ mTouchMode = TOUCH_MODE_DONE_WAITING ;
164+ }
165+
166+ }
167+ }
168+ }
141169
142170 /**
143171 * A class that represents a fixed view in a list, for example a header at the top
@@ -806,6 +834,39 @@ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
806834 super .requestDisallowInterceptTouchEvent (disallowIntercept );
807835 }
808836
837+ final class CheckForTap implements Runnable {
838+ public void run () {
839+ if (mTouchMode == TOUCH_MODE_DOWN ) {
840+ mTouchMode = TOUCH_MODE_TAP ;
841+ final View child = getChildAt (mMotionPosition );
842+ if (child != null && !child .hasFocusable ()) {
843+ mLayoutMode = LAYOUT_NORMAL ;
844+
845+ if (!mDataChanged ) {
846+ layoutChildren ();
847+ child .setPressed (true );
848+ setPressed (true );
849+
850+ final int longPressTimeout = ViewConfiguration .getLongPressTimeout ();
851+ final boolean longClickable = isLongClickable ();
852+
853+ if (longClickable ) {
854+ if (mPendingCheckForLongPress == null ) {
855+ mPendingCheckForLongPress = new CheckForLongPress ();
856+ }
857+ mPendingCheckForLongPress .rememberWindowAttachCount ();
858+ postDelayed (mPendingCheckForLongPress , longPressTimeout );
859+ } else {
860+ mTouchMode = TOUCH_MODE_DONE_WAITING ;
861+ }
862+ } else {
863+ mTouchMode = TOUCH_MODE_DONE_WAITING ;
864+ }
865+ }
866+ }
867+ }
868+ }
869+
809870 private boolean onTouchDown (final MotionEvent event ) {
810871 final int x = (int ) event .getX ();
811872 final int y = (int ) event .getY ();
@@ -825,7 +886,10 @@ private boolean onTouchDown(final MotionEvent event) {
825886 // is it a tap or a scroll .. we don't know yet!
826887 mTouchMode = TOUCH_MODE_DOWN ;
827888
828- // TODO : add handling for a click removed from here
889+ if (mPendingCheckForTap == null ) {
890+ mPendingCheckForTap = new CheckForTap ();
891+ }
892+ postDelayed (mPendingCheckForTap , ViewConfiguration .getTapTimeout ());
829893
830894 if (event .getEdgeFlags () != 0 && motionPosition < 0 ) {
831895 // If we couldn't find a view to click on, but the down event was touching
@@ -885,6 +949,12 @@ private boolean onTouchCancel(final MotionEvent event) {
885949 mTouchMode = TOUCH_MODE_IDLE ;
886950 setPressed (false );
887951 invalidate (); // redraw selector
952+ final Handler handler = getHandler ();
953+
954+ if (handler != null ) {
955+ handler .removeCallbacks (mPendingCheckForLongPress );
956+ }
957+
888958 recycleVelocityTracker ();
889959 mActivePointerId = INVALID_POINTER ;
890960 return true ;
@@ -903,7 +973,14 @@ private boolean onTouchUp(final MotionEvent event) {
903973
904974 setPressed (false );
905975 invalidate (); // redraw selector
976+
977+ final Handler handler = getHandler ();
978+ if (handler != null ) {
979+ handler .removeCallbacks (mPendingCheckForLongPress );
980+ }
981+
906982 recycleVelocityTracker ();
983+
907984 mActivePointerId = INVALID_POINTER ;
908985 return true ;
909986 }
@@ -939,17 +1016,56 @@ private boolean onTouchUpScrolling(final MotionEvent event) {
9391016 }
9401017
9411018 private boolean onTouchUpTap (final MotionEvent event ) {
942- if (mPerformClick == null ) {
943- invalidate ();
944- mPerformClick = new PerformClick ();
945- }
9461019 final int motionPosition = mMotionPosition ;
947- if (!mDataChanged && motionPosition >= 0 && mAdapter .isEnabled (motionPosition )) {
1020+ final View child = getChildAt (motionPosition );
1021+ if (child != null && !child .hasFocusable ()) {
1022+ if (mTouchMode != TOUCH_MODE_DOWN ) {
1023+ child .setPressed (false );
1024+ }
1025+
1026+ if (mPerformClick == null ) {
1027+ invalidate ();
1028+ mPerformClick = new PerformClick ();
1029+ }
1030+
9481031 final PerformClick performClick = mPerformClick ;
9491032 performClick .mClickMotionPosition = motionPosition ;
9501033 performClick .rememberWindowAttachCount ();
951- performClick .run ();
1034+
1035+ // mResurrectToPosition = motionPosition;
1036+
1037+ if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP ) {
1038+ final Handler handler = getHandler ();
1039+ if (handler != null ) {
1040+ handler .removeCallbacks (mTouchMode == TOUCH_MODE_DOWN ?
1041+ mPendingCheckForTap : mPendingCheckForLongPress );
1042+ }
1043+ mLayoutMode = LAYOUT_NORMAL ;
1044+ if (!mDataChanged && mAdapter .isEnabled (motionPosition )) {
1045+ mTouchMode = TOUCH_MODE_TAP ;
1046+ layoutChildren ();
1047+ child .setPressed (true );
1048+ setPressed (true );
1049+ postDelayed (new Runnable () {
1050+ public void run () {
1051+ child .setPressed (false );
1052+ setPressed (false );
1053+ if (!mDataChanged ) {
1054+ post (performClick );
1055+ }
1056+ mTouchMode = TOUCH_MODE_IDLE ;
1057+ }
1058+ }, ViewConfiguration .getPressedStateDuration ());
1059+ } else {
1060+ mTouchMode = TOUCH_MODE_IDLE ;
1061+ }
1062+ return true ;
1063+ } else if (!mDataChanged && mAdapter .isEnabled (motionPosition )) {
1064+ post (performClick );
1065+ }
9521066 }
1067+ mTouchMode = TOUCH_MODE_IDLE ;
1068+
9531069 return true ;
9541070 }
9551071
@@ -1005,7 +1121,10 @@ private boolean startScrollIfNeeded(final int y) {
10051121 mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop ;
10061122 }
10071123
1008- // TODO : LONG PRESS
1124+ final Handler handler = getHandler ();
1125+ if (handler != null ) {
1126+ handler .removeCallbacks (mPendingCheckForLongPress );
1127+ }
10091128 setPressed (false );
10101129 View motionView = getChildAt (mMotionPosition - mFirstPosition );
10111130 if (motionView != null ) {
@@ -2741,11 +2860,30 @@ public void run() {
27412860 final View view = getChildAt (motionPosition ); // a fix by @pboos
27422861
27432862 if (view != null ) {
2744- performItemClick (view , motionPosition + mFirstPosition , adapter .getItemId (motionPosition ));
2863+ performItemClick (view , motionPosition + mFirstPosition , adapter .getItemId (motionPosition + mFirstPosition ));
27452864 }
27462865 }
27472866 }
27482867 }
2868+
2869+ private boolean performLongPress (final View child ,
2870+ final int longPressPosition , final long longPressId ) {
2871+ boolean handled = false ;
2872+
2873+ OnItemLongClickListener onItemLongClickListener = getOnItemLongClickListener ();
2874+ if (onItemLongClickListener != null ) {
2875+ handled = onItemLongClickListener .onItemLongClick (ExtendableListView .this , child ,
2876+ longPressPosition , longPressId );
2877+ }
2878+ // if (!handled) {
2879+ // mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
2880+ // handled = super.showContextMenuForChild(AbsListView.this);
2881+ // }
2882+ if (handled ) {
2883+ performHapticFeedback (HapticFeedbackConstants .LONG_PRESS );
2884+ }
2885+ return handled ;
2886+ }
27492887
27502888 /**
27512889 * A base class for Runnables that will check that their view is still attached to
0 commit comments