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
@@ -812,6 +840,39 @@ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
812840 super .requestDisallowInterceptTouchEvent (disallowIntercept );
813841 }
814842
843+ final class CheckForTap implements Runnable {
844+ public void run () {
845+ if (mTouchMode == TOUCH_MODE_DOWN ) {
846+ mTouchMode = TOUCH_MODE_TAP ;
847+ final View child = getChildAt (mMotionPosition );
848+ if (child != null && !child .hasFocusable ()) {
849+ mLayoutMode = LAYOUT_NORMAL ;
850+
851+ if (!mDataChanged ) {
852+ layoutChildren ();
853+ child .setPressed (true );
854+ setPressed (true );
855+
856+ final int longPressTimeout = ViewConfiguration .getLongPressTimeout ();
857+ final boolean longClickable = isLongClickable ();
858+
859+ if (longClickable ) {
860+ if (mPendingCheckForLongPress == null ) {
861+ mPendingCheckForLongPress = new CheckForLongPress ();
862+ }
863+ mPendingCheckForLongPress .rememberWindowAttachCount ();
864+ postDelayed (mPendingCheckForLongPress , longPressTimeout );
865+ } else {
866+ mTouchMode = TOUCH_MODE_DONE_WAITING ;
867+ }
868+ } else {
869+ mTouchMode = TOUCH_MODE_DONE_WAITING ;
870+ }
871+ }
872+ }
873+ }
874+ }
875+
815876 private boolean onTouchDown (final MotionEvent event ) {
816877 final int x = (int ) event .getX ();
817878 final int y = (int ) event .getY ();
@@ -831,7 +892,10 @@ private boolean onTouchDown(final MotionEvent event) {
831892 // is it a tap or a scroll .. we don't know yet!
832893 mTouchMode = TOUCH_MODE_DOWN ;
833894
834- // TODO : add handling for a click removed from here
895+ if (mPendingCheckForTap == null ) {
896+ mPendingCheckForTap = new CheckForTap ();
897+ }
898+ postDelayed (mPendingCheckForTap , ViewConfiguration .getTapTimeout ());
835899
836900 if (event .getEdgeFlags () != 0 && motionPosition < 0 ) {
837901 // If we couldn't find a view to click on, but the down event was touching
@@ -891,6 +955,12 @@ private boolean onTouchCancel(final MotionEvent event) {
891955 mTouchMode = TOUCH_MODE_IDLE ;
892956 setPressed (false );
893957 invalidate (); // redraw selector
958+ final Handler handler = getHandler ();
959+
960+ if (handler != null ) {
961+ handler .removeCallbacks (mPendingCheckForLongPress );
962+ }
963+
894964 recycleVelocityTracker ();
895965 mActivePointerId = INVALID_POINTER ;
896966 return true ;
@@ -909,7 +979,14 @@ private boolean onTouchUp(final MotionEvent event) {
909979
910980 setPressed (false );
911981 invalidate (); // redraw selector
982+
983+ final Handler handler = getHandler ();
984+ if (handler != null ) {
985+ handler .removeCallbacks (mPendingCheckForLongPress );
986+ }
987+
912988 recycleVelocityTracker ();
989+
913990 mActivePointerId = INVALID_POINTER ;
914991 return true ;
915992 }
@@ -945,17 +1022,58 @@ private boolean onTouchUpScrolling(final MotionEvent event) {
9451022 }
9461023
9471024 private boolean onTouchUpTap (final MotionEvent event ) {
948- if (mPerformClick == null ) {
949- invalidate ();
950- mPerformClick = new PerformClick ();
951- }
9521025 final int motionPosition = mMotionPosition ;
953- if (!mDataChanged && motionPosition >= 0 && mAdapter .isEnabled (motionPosition )) {
954- final PerformClick performClick = mPerformClick ;
955- performClick .mClickMotionPosition = motionPosition ;
956- performClick .rememberWindowAttachCount ();
957- performClick .run ();
1026+ if (motionPosition >= 0 ) {
1027+ final View child = getChildAt (motionPosition );
1028+ if (child != null && !child .hasFocusable ()) {
1029+ if (mTouchMode != TOUCH_MODE_DOWN ) {
1030+ child .setPressed (false );
1031+ }
1032+
1033+ if (mPerformClick == null ) {
1034+ invalidate ();
1035+ mPerformClick = new PerformClick ();
1036+ }
1037+
1038+ final PerformClick performClick = mPerformClick ;
1039+ performClick .mClickMotionPosition = motionPosition ;
1040+ performClick .rememberWindowAttachCount ();
1041+
1042+ // mResurrectToPosition = motionPosition;
1043+
1044+ if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP ) {
1045+ final Handler handler = getHandler ();
1046+ if (handler != null ) {
1047+ handler .removeCallbacks (mTouchMode == TOUCH_MODE_DOWN ?
1048+ mPendingCheckForTap : mPendingCheckForLongPress );
1049+ }
1050+ mLayoutMode = LAYOUT_NORMAL ;
1051+ if (!mDataChanged && motionPosition >= 0 && mAdapter .isEnabled (motionPosition )) {
1052+ mTouchMode = TOUCH_MODE_TAP ;
1053+ layoutChildren ();
1054+ child .setPressed (true );
1055+ setPressed (true );
1056+ postDelayed (new Runnable () {
1057+ public void run () {
1058+ child .setPressed (false );
1059+ setPressed (false );
1060+ if (!mDataChanged ) {
1061+ post (performClick );
1062+ }
1063+ mTouchMode = TOUCH_MODE_IDLE ;
1064+ }
1065+ }, ViewConfiguration .getPressedStateDuration ());
1066+ } else {
1067+ mTouchMode = TOUCH_MODE_IDLE ;
1068+ }
1069+ return true ;
1070+ } else if (!mDataChanged && motionPosition >= 0 && mAdapter .isEnabled (motionPosition )) {
1071+ post (performClick );
1072+ }
1073+ }
9581074 }
1075+ mTouchMode = TOUCH_MODE_IDLE ;
1076+
9591077 return true ;
9601078 }
9611079
@@ -1011,7 +1129,10 @@ private boolean startScrollIfNeeded(final int y) {
10111129 mMotionCorrection = deltaY > 0 ? mTouchSlop : -mTouchSlop ;
10121130 }
10131131
1014- // TODO : LONG PRESS
1132+ final Handler handler = getHandler ();
1133+ if (handler != null ) {
1134+ handler .removeCallbacks (mPendingCheckForLongPress );
1135+ }
10151136 setPressed (false );
10161137 View motionView = getChildAt (mMotionPosition - mFirstPosition );
10171138 if (motionView != null ) {
@@ -2754,6 +2875,25 @@ public void run() {
27542875 }
27552876 }
27562877 }
2878+
2879+ private boolean performLongPress (final View child ,
2880+ final int longPressPosition , final long longPressId ) {
2881+ boolean handled = false ;
2882+
2883+ OnItemLongClickListener onItemLongClickListener = getOnItemLongClickListener ();
2884+ if (onItemLongClickListener != null ) {
2885+ handled = onItemLongClickListener .onItemLongClick (ExtendableListView .this , child ,
2886+ longPressPosition , longPressId );
2887+ }
2888+ // if (!handled) {
2889+ // mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
2890+ // handled = super.showContextMenuForChild(AbsListView.this);
2891+ // }
2892+ if (handled ) {
2893+ performHapticFeedback (HapticFeedbackConstants .LONG_PRESS );
2894+ }
2895+ return handled ;
2896+ }
27572897
27582898 /**
27592899 * A base class for Runnables that will check that their view is still attached to
0 commit comments