Loading core/java/android/widget/ListPopupWindow.java +77 −24 Original line number Original line Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.MotionEvent; import android.view.View; import android.view.View; import android.view.View.MeasureSpec; import android.view.View.MeasureSpec; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnTouchListener; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewGroup; Loading Loading @@ -1133,18 +1134,32 @@ public class ListPopupWindow { * * * @hide * @hide */ */ public static abstract class ForwardingListener implements View.OnTouchListener { public static abstract class ForwardingListener implements View.OnTouchListener, View.OnAttachStateChangeListener { /** Scaled touch slop, used for detecting movement outside bounds. */ /** Scaled touch slop, used for detecting movement outside bounds. */ private final float mScaledTouchSlop; private final float mScaledTouchSlop; /** Timeout before disallowing intercept on the source's parent. */ private final int mTapTimeout; /** Source view from which events are forwarded. */ private final View mSrc; /** Runnable used to prevent conflicts with scrolling parents. */ private Runnable mDisallowIntercept; /** Whether this listener is currently forwarding touch events. */ /** Whether this listener is currently forwarding touch events. */ private boolean mForwarding; private boolean mForwarding; /** The id of the first pointer down in the current event stream. */ /** The id of the first pointer down in the current event stream. */ private int mActivePointerId; private int mActivePointerId; public ForwardingListener(Context context) { public ForwardingListener(View src) { mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mSrc = src; mScaledTouchSlop = ViewConfiguration.get(src.getContext()).getScaledTouchSlop(); mTapTimeout = ViewConfiguration.getTapTimeout(); src.addOnAttachStateChangeListener(this); } } /** /** Loading @@ -1164,15 +1179,29 @@ public class ListPopupWindow { final boolean wasForwarding = mForwarding; final boolean wasForwarding = mForwarding; final boolean forwarding; final boolean forwarding; if (wasForwarding) { if (wasForwarding) { forwarding = onTouchForwarded(v, event) || !onForwardingStopped(); forwarding = onTouchForwarded(event) || !onForwardingStopped(); } else { } else { forwarding = onTouchObserved(v, event) && onForwardingStarted(); forwarding = onTouchObserved(event) && onForwardingStarted(); } } mForwarding = forwarding; mForwarding = forwarding; return forwarding || wasForwarding; return forwarding || wasForwarding; } } @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { mForwarding = false; mActivePointerId = MotionEvent.INVALID_POINTER_ID; if (mDisallowIntercept != null) { mSrc.removeCallbacks(mDisallowIntercept); } } /** /** * Called when forwarding would like to start. * Called when forwarding would like to start. * <p> * <p> Loading @@ -1182,7 +1211,7 @@ public class ListPopupWindow { * * * @return true to start forwarding, false otherwise * @return true to start forwarding, false otherwise */ */ public boolean onForwardingStarted() { protected boolean onForwardingStarted() { final ListPopupWindow popup = getPopup(); final ListPopupWindow popup = getPopup(); if (popup != null && !popup.isShowing()) { if (popup != null && !popup.isShowing()) { popup.show(); popup.show(); Loading @@ -1199,7 +1228,7 @@ public class ListPopupWindow { * * * @return true to stop forwarding, false otherwise * @return true to stop forwarding, false otherwise */ */ public boolean onForwardingStopped() { protected boolean onForwardingStopped() { final ListPopupWindow popup = getPopup(); final ListPopupWindow popup = getPopup(); if (popup != null && popup.isShowing()) { if (popup != null && popup.isShowing()) { popup.dismiss(); popup.dismiss(); Loading @@ -1210,30 +1239,46 @@ public class ListPopupWindow { /** /** * Observes motion events and determines when to start forwarding. * Observes motion events and determines when to start forwarding. * * * @param src view from which the event originated * @param srcEvent motion event in source view coordinates * @param srcEvent motion event in source view coordinates * @return true to start forwarding motion events, false otherwise * @return true to start forwarding motion events, false otherwise */ */ private boolean onTouchObserved(View src, MotionEvent srcEvent) { private boolean onTouchObserved(MotionEvent srcEvent) { final View src = mSrc; if (!src.isEnabled()) { if (!src.isEnabled()) { return false; return false; } } // The first pointer down is always the active pointer. final int actionMasked = srcEvent.getActionMasked(); final int actionMasked = srcEvent.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN) { switch (actionMasked) { case MotionEvent.ACTION_DOWN: mActivePointerId = srcEvent.getPointerId(0); mActivePointerId = srcEvent.getPointerId(0); if (mDisallowIntercept == null) { mDisallowIntercept = new DisallowIntercept(); } } src.postDelayed(mDisallowIntercept, mTapTimeout); break; case MotionEvent.ACTION_MOVE: final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId); final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId); if (activePointerIndex >= 0) { if (activePointerIndex >= 0) { final float x = srcEvent.getX(activePointerIndex); final float x = srcEvent.getX(activePointerIndex); final float y = srcEvent.getY(activePointerIndex); final float y = srcEvent.getY(activePointerIndex); if (!src.pointInView(x, y, mScaledTouchSlop)) { if (!src.pointInView(x, y, mScaledTouchSlop)) { // The pointer has moved outside of the view. // The pointer has moved outside of the view. if (mDisallowIntercept != null) { src.removeCallbacks(mDisallowIntercept); } src.getParent().requestDisallowInterceptTouchEvent(true); return true; return true; } } } } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (mDisallowIntercept != null) { src.removeCallbacks(mDisallowIntercept); } break; } return false; return false; } } Loading @@ -1242,11 +1287,11 @@ public class ListPopupWindow { * Handled forwarded motion events and determines when to stop * Handled forwarded motion events and determines when to stop * forwarding. * forwarding. * * * @param src view from which the event originated * @param srcEvent motion event in source view coordinates * @param srcEvent motion event in source view coordinates * @return true to continue forwarding motion events, false to cancel * @return true to continue forwarding motion events, false to cancel */ */ private boolean onTouchForwarded(View src, MotionEvent srcEvent) { private boolean onTouchForwarded(MotionEvent srcEvent) { final View src = mSrc; final ListPopupWindow popup = getPopup(); final ListPopupWindow popup = getPopup(); if (popup == null || !popup.isShowing()) { if (popup == null || !popup.isShowing()) { return false; return false; Loading @@ -1267,6 +1312,14 @@ public class ListPopupWindow { dstEvent.recycle(); dstEvent.recycle(); return handled; return handled; } } private class DisallowIntercept implements Runnable { @Override public void run() { final ViewParent parent = mSrc.getParent(); parent.requestDisallowInterceptTouchEvent(true); } } } } /** /** Loading core/java/android/widget/Spinner.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -198,7 +198,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { } } mPopup = popup; mPopup = popup; mForwardingListener = new ForwardingListener(context) { mForwardingListener = new ForwardingListener(this) { @Override @Override public ListPopupWindow getPopup() { public ListPopupWindow getPopup() { return popup; return popup; Loading core/java/com/android/internal/view/menu/ActionMenuPresenter.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -565,7 +565,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter setVisibility(VISIBLE); setVisibility(VISIBLE); setEnabled(true); setEnabled(true); setOnTouchListener(new ForwardingListener(context) { setOnTouchListener(new ForwardingListener(this) { @Override @Override public ListPopupWindow getPopup() { public ListPopupWindow getPopup() { if (mOverflowPopup == null) { if (mOverflowPopup == null) { Loading Loading
core/java/android/widget/ListPopupWindow.java +77 −24 Original line number Original line Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.MotionEvent; import android.view.View; import android.view.View; import android.view.View.MeasureSpec; import android.view.View.MeasureSpec; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnTouchListener; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewGroup; Loading Loading @@ -1133,18 +1134,32 @@ public class ListPopupWindow { * * * @hide * @hide */ */ public static abstract class ForwardingListener implements View.OnTouchListener { public static abstract class ForwardingListener implements View.OnTouchListener, View.OnAttachStateChangeListener { /** Scaled touch slop, used for detecting movement outside bounds. */ /** Scaled touch slop, used for detecting movement outside bounds. */ private final float mScaledTouchSlop; private final float mScaledTouchSlop; /** Timeout before disallowing intercept on the source's parent. */ private final int mTapTimeout; /** Source view from which events are forwarded. */ private final View mSrc; /** Runnable used to prevent conflicts with scrolling parents. */ private Runnable mDisallowIntercept; /** Whether this listener is currently forwarding touch events. */ /** Whether this listener is currently forwarding touch events. */ private boolean mForwarding; private boolean mForwarding; /** The id of the first pointer down in the current event stream. */ /** The id of the first pointer down in the current event stream. */ private int mActivePointerId; private int mActivePointerId; public ForwardingListener(Context context) { public ForwardingListener(View src) { mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mSrc = src; mScaledTouchSlop = ViewConfiguration.get(src.getContext()).getScaledTouchSlop(); mTapTimeout = ViewConfiguration.getTapTimeout(); src.addOnAttachStateChangeListener(this); } } /** /** Loading @@ -1164,15 +1179,29 @@ public class ListPopupWindow { final boolean wasForwarding = mForwarding; final boolean wasForwarding = mForwarding; final boolean forwarding; final boolean forwarding; if (wasForwarding) { if (wasForwarding) { forwarding = onTouchForwarded(v, event) || !onForwardingStopped(); forwarding = onTouchForwarded(event) || !onForwardingStopped(); } else { } else { forwarding = onTouchObserved(v, event) && onForwardingStarted(); forwarding = onTouchObserved(event) && onForwardingStarted(); } } mForwarding = forwarding; mForwarding = forwarding; return forwarding || wasForwarding; return forwarding || wasForwarding; } } @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { mForwarding = false; mActivePointerId = MotionEvent.INVALID_POINTER_ID; if (mDisallowIntercept != null) { mSrc.removeCallbacks(mDisallowIntercept); } } /** /** * Called when forwarding would like to start. * Called when forwarding would like to start. * <p> * <p> Loading @@ -1182,7 +1211,7 @@ public class ListPopupWindow { * * * @return true to start forwarding, false otherwise * @return true to start forwarding, false otherwise */ */ public boolean onForwardingStarted() { protected boolean onForwardingStarted() { final ListPopupWindow popup = getPopup(); final ListPopupWindow popup = getPopup(); if (popup != null && !popup.isShowing()) { if (popup != null && !popup.isShowing()) { popup.show(); popup.show(); Loading @@ -1199,7 +1228,7 @@ public class ListPopupWindow { * * * @return true to stop forwarding, false otherwise * @return true to stop forwarding, false otherwise */ */ public boolean onForwardingStopped() { protected boolean onForwardingStopped() { final ListPopupWindow popup = getPopup(); final ListPopupWindow popup = getPopup(); if (popup != null && popup.isShowing()) { if (popup != null && popup.isShowing()) { popup.dismiss(); popup.dismiss(); Loading @@ -1210,30 +1239,46 @@ public class ListPopupWindow { /** /** * Observes motion events and determines when to start forwarding. * Observes motion events and determines when to start forwarding. * * * @param src view from which the event originated * @param srcEvent motion event in source view coordinates * @param srcEvent motion event in source view coordinates * @return true to start forwarding motion events, false otherwise * @return true to start forwarding motion events, false otherwise */ */ private boolean onTouchObserved(View src, MotionEvent srcEvent) { private boolean onTouchObserved(MotionEvent srcEvent) { final View src = mSrc; if (!src.isEnabled()) { if (!src.isEnabled()) { return false; return false; } } // The first pointer down is always the active pointer. final int actionMasked = srcEvent.getActionMasked(); final int actionMasked = srcEvent.getActionMasked(); if (actionMasked == MotionEvent.ACTION_DOWN) { switch (actionMasked) { case MotionEvent.ACTION_DOWN: mActivePointerId = srcEvent.getPointerId(0); mActivePointerId = srcEvent.getPointerId(0); if (mDisallowIntercept == null) { mDisallowIntercept = new DisallowIntercept(); } } src.postDelayed(mDisallowIntercept, mTapTimeout); break; case MotionEvent.ACTION_MOVE: final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId); final int activePointerIndex = srcEvent.findPointerIndex(mActivePointerId); if (activePointerIndex >= 0) { if (activePointerIndex >= 0) { final float x = srcEvent.getX(activePointerIndex); final float x = srcEvent.getX(activePointerIndex); final float y = srcEvent.getY(activePointerIndex); final float y = srcEvent.getY(activePointerIndex); if (!src.pointInView(x, y, mScaledTouchSlop)) { if (!src.pointInView(x, y, mScaledTouchSlop)) { // The pointer has moved outside of the view. // The pointer has moved outside of the view. if (mDisallowIntercept != null) { src.removeCallbacks(mDisallowIntercept); } src.getParent().requestDisallowInterceptTouchEvent(true); return true; return true; } } } } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (mDisallowIntercept != null) { src.removeCallbacks(mDisallowIntercept); } break; } return false; return false; } } Loading @@ -1242,11 +1287,11 @@ public class ListPopupWindow { * Handled forwarded motion events and determines when to stop * Handled forwarded motion events and determines when to stop * forwarding. * forwarding. * * * @param src view from which the event originated * @param srcEvent motion event in source view coordinates * @param srcEvent motion event in source view coordinates * @return true to continue forwarding motion events, false to cancel * @return true to continue forwarding motion events, false to cancel */ */ private boolean onTouchForwarded(View src, MotionEvent srcEvent) { private boolean onTouchForwarded(MotionEvent srcEvent) { final View src = mSrc; final ListPopupWindow popup = getPopup(); final ListPopupWindow popup = getPopup(); if (popup == null || !popup.isShowing()) { if (popup == null || !popup.isShowing()) { return false; return false; Loading @@ -1267,6 +1312,14 @@ public class ListPopupWindow { dstEvent.recycle(); dstEvent.recycle(); return handled; return handled; } } private class DisallowIntercept implements Runnable { @Override public void run() { final ViewParent parent = mSrc.getParent(); parent.requestDisallowInterceptTouchEvent(true); } } } } /** /** Loading
core/java/android/widget/Spinner.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -198,7 +198,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { } } mPopup = popup; mPopup = popup; mForwardingListener = new ForwardingListener(context) { mForwardingListener = new ForwardingListener(this) { @Override @Override public ListPopupWindow getPopup() { public ListPopupWindow getPopup() { return popup; return popup; Loading
core/java/com/android/internal/view/menu/ActionMenuPresenter.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -565,7 +565,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter setVisibility(VISIBLE); setVisibility(VISIBLE); setEnabled(true); setEnabled(true); setOnTouchListener(new ForwardingListener(context) { setOnTouchListener(new ForwardingListener(this) { @Override @Override public ListPopupWindow getPopup() { public ListPopupWindow getPopup() { if (mOverflowPopup == null) { if (mOverflowPopup == null) { Loading