Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15d7d6f5 authored by Alan Viverette's avatar Alan Viverette Committed by Android Git Automerger
Browse files

am 6cfce6a1: am 28dd8eb6: Merge "Forward events to ListPopupWindow, highlight...

am 6cfce6a1: am 28dd8eb6: Merge "Forward events to ListPopupWindow, highlight touched items" into klp-dev

* commit '6cfce6a1':
  Forward events to ListPopupWindow, highlight touched items
parents ec592d17 6cfce6a1
Loading
Loading
Loading
Loading
+172 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package android.widget;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
@@ -23,6 +26,7 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.IntProperty;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -31,6 +35,7 @@ import android.view.View.MeasureSpec;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.AccelerateDecelerateInterpolator;

import java.util.Locale;

@@ -955,6 +960,33 @@ public class ListPopupWindow {
        return false;
    }

    /**
     * Receives motion events forwarded from a source view. This is used
     * internally to implement support for drag-to-open.
     *
     * @param src view from which the event was forwarded
     * @param srcEvent forwarded motion event in source-local coordinates
     * @param activePointerId id of the pointer that activated forwarding
     * @return whether the event was handled
     * @hide
     */
    public boolean onForwardedEvent(View src, MotionEvent srcEvent, int activePointerId) {
        final DropDownListView dst = mDropDownList;
        if (dst == null || !dst.isShown()) {
            return false;
        }

        // Convert event to local coordinates.
        final MotionEvent dstEvent = MotionEvent.obtainNoHistory(srcEvent);
        src.toGlobalMotionEvent(dstEvent);
        dst.toLocalMotionEvent(dstEvent);

        // Forward converted event, then recycle it.
        final boolean handled = dst.onForwardedEvent(dstEvent, activePointerId);
        dstEvent.recycle();
        return handled;
    }

    /**
     * <p>Builds the popup window's content and returns the height the popup
     * should have. Returns -1 when the content already exists.</p>
@@ -1130,6 +1162,27 @@ public class ListPopupWindow {
     */
    private static class DropDownListView extends ListView {
        private static final String TAG = ListPopupWindow.TAG + ".DropDownListView";

        /** Duration in milliseconds of the drag-to-open click animation. */
        private static final long CLICK_ANIM_DURATION = 150;

        /** Target alpha value for drag-to-open click animation. */
        private static final int CLICK_ANIM_ALPHA = 0x80;

        /** Wrapper around Drawable's <code>alpha</code> property. */
        private static final IntProperty<Drawable> DRAWABLE_ALPHA =
                new IntProperty<Drawable>("alpha") {
                    @Override
                    public void setValue(Drawable object, int value) {
                        object.setAlpha(value);
                    }

                    @Override
                    public Integer get(Drawable object) {
                        return object.getAlpha();
                    }
                };

        /*
         * WARNING: This is a workaround for a touch mode issue.
         *
@@ -1165,6 +1218,12 @@ public class ListPopupWindow {
         */
        private boolean mHijackFocus;

        /** Whether to force drawing of the pressed state selector. */
        private boolean mDrawsInPressedState;

        /** Current drag-to-open click animation, if any. */
        private Animator mClickAnimation;

        /**
         * <p>Creates a new list view wrapper.</p>
         *
@@ -1177,6 +1236,119 @@ public class ListPopupWindow {
            setCacheColorHint(0); // Transparent, since the background drawable could be anything.
        }

        /**
         * Handles forwarded events.
         *
         * @param activePointerId id of the pointer that activated forwarding
         * @return whether the event was handled
         */
        public boolean onForwardedEvent(MotionEvent event, int activePointerId) {
            boolean handledEvent = true;
            boolean clearPressedItem = false;

            final int actionMasked = event.getActionMasked();
            switch (actionMasked) {
                case MotionEvent.ACTION_CANCEL:
                    handledEvent = false;
                    break;
                case MotionEvent.ACTION_UP:
                    handledEvent = false;
                    // $FALL-THROUGH$
                case MotionEvent.ACTION_MOVE:
                    final int activeIndex = event.findPointerIndex(activePointerId);
                    if (activeIndex < 0) {
                        handledEvent = false;
                        break;
                    }

                    final int x = (int) event.getX(activeIndex);
                    final int y = (int) event.getY(activeIndex);
                    final int position = pointToPosition(x, y);
                    if (position == INVALID_POSITION) {
                        clearPressedItem = true;
                        break;
                    }

                    final View child = getChildAt(position - getFirstVisiblePosition());
                    setPressedItem(child, position);
                    handledEvent = true;

                    if (actionMasked == MotionEvent.ACTION_UP) {
                        clickPressedItem(child, position);
                    }
                    break;
            }

            // Failure to handle the event cancels forwarding.
            if (!handledEvent || clearPressedItem) {
                clearPressedItem();
            }

            return handledEvent;
        }

        /**
         * Starts an alpha animation on the selector. When the animation ends,
         * the list performs a click on the item.
         */
        private void clickPressedItem(final View child, final int position) {
            final long id = getItemIdAtPosition(position);
            final Animator anim = ObjectAnimator.ofInt(
                    mSelector, DRAWABLE_ALPHA, 0xFF, CLICK_ANIM_ALPHA, 0xFF);
            anim.setDuration(CLICK_ANIM_DURATION);
            anim.setInterpolator(new AccelerateDecelerateInterpolator());
            anim.addListener(new AnimatorListenerAdapter() {
                    @Override
                public void onAnimationEnd(Animator animation) {
                    performItemClick(child, position, id);
                }
            });
            anim.start();

            if (mClickAnimation != null) {
                mClickAnimation.cancel();
            }
            mClickAnimation = anim;
        }

        private void clearPressedItem() {
            mDrawsInPressedState = false;
            setPressed(false);
            updateSelectorState();

            if (mClickAnimation != null) {
                mClickAnimation.cancel();
                mClickAnimation = null;
            }
        }

        private void setPressedItem(View child, int position) {
            mDrawsInPressedState = true;

            // Ordering is essential. First update the pressed state and layout
            // the children. This will ensure the selector actually gets drawn.
            setPressed(true);
            layoutChildren();

            // Ensure that keyboard focus starts from the last touched position.
            setSelectedPositionInt(position);
            positionSelector(position, child);

            // Refresh the drawable state to reflect the new pressed state,
            // which will also update the selector state.
            refreshDrawableState();

            if (mClickAnimation != null) {
                mClickAnimation.cancel();
                mClickAnimation = null;
            }
        }

        @Override
        boolean touchModeDrawsInPressedState() {
            return mDrawsInPressedState || super.touchModeDrawsInPressedState();
        }

        /**
         * <p>Avoids jarring scrolling effect by ensuring that list elements
         * made of a text view fit on a single line.</p>
+27 −18
Original line number Diff line number Diff line
@@ -30,9 +30,7 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ImageButton;
import android.widget.ListPopupWindow;

import com.android.internal.view.ActionBarPolicy;
import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView;
@@ -694,32 +692,43 @@ public class ActionMenuPresenter extends BaseMenuPresenter
        }

        @Override
        public boolean onTouchObserved(View v, MotionEvent ev) {
            if (ev.getActionMasked() == MotionEvent.ACTION_MOVE && v.isEnabled()
                    && !v.pointInView(ev.getX(), ev.getY(), mScaledTouchSlop)) {
                mActivePointerId = ev.getPointerId(0);
                v.performClick();
                return true;
        public boolean onTouchObserved(View src, MotionEvent srcEvent) {
            if (!src.isEnabled()) {
                return false;
            }

            return false;
            // Always start forwarding events when the source view is touched.
            mActivePointerId = srcEvent.getPointerId(0);
            src.performClick();
            return true;
        }

        @Override
        public boolean onTouchForwarded(View v, MotionEvent ev) {
            if (!v.isEnabled() || mOverflowPopup == null || !mOverflowPopup.isShowing()) {
                return false;
        public boolean onTouchForwarded(View src, MotionEvent srcEvent) {
            final OverflowPopup popup = mOverflowPopup;
            if (popup != null && popup.isShowing()) {
                final int activePointerId = mActivePointerId;
                if (activePointerId != MotionEvent.INVALID_POINTER_ID && src.isEnabled()
                        && popup.forwardMotionEvent(src, srcEvent, activePointerId)) {
                    // Handled the motion event, continue forwarding.
                    return true;
                }

            if (mActivePointerId != MotionEvent.INVALID_POINTER_ID) {
                if (mOverflowPopup.forwardMotionEvent(v, ev, mActivePointerId)) {
                    return true;
                final int activePointerIndex = srcEvent.findPointerIndex(activePointerId);
                if (activePointerIndex >= 0) {
                    final float x = srcEvent.getX(activePointerIndex);
                    final float y = srcEvent.getY(activePointerIndex);
                    if (src.pointInView(x, y, mScaledTouchSlop)) {
                        // The user is touching the source view. Cancel
                        // forwarding, but don't dismiss the popup.
                        return false;
                    }
                }

                mActivePointerId = MotionEvent.INVALID_POINTER_ID;
                popup.dismiss();
            }

            mOverflowPopup.dismiss();
            // Cancel forwarding.
            return false;
        }
    }
+10 −60
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
@@ -48,8 +47,6 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On

    static final int ITEM_LAYOUT = com.android.internal.R.layout.popup_menu_item_layout;

    private final int[] mTempLocation = new int[2];

    private final Context mContext;
    private final LayoutInflater mInflater;
    private final MenuBuilder mMenu;
@@ -162,67 +159,20 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
        return mPopup != null && mPopup.isShowing();
    }

    public boolean forwardMotionEvent(View v, MotionEvent ev, int activePointerId) {
    /**
     * Forwards motion events from a source view to the popup window.
     *
     * @param src view from which the event was forwarded
     * @param event forwarded motion event in source-local coordinates
     * @param activePointerId id of the pointer that activated forwarding
     * @return whether the event was handled
     */
    public boolean forwardMotionEvent(View src, MotionEvent event, int activePointerId) {
        if (mPopup == null || !mPopup.isShowing()) {
            return false;
        }

        final AbsListView dstView = mPopup.getListView();
        if (dstView == null || !dstView.isShown()) {
            return false;
        }

        boolean cancelForwarding = false;
        final int actionMasked = ev.getActionMasked();
        switch (actionMasked) {
            case MotionEvent.ACTION_CANCEL:
                cancelForwarding = true;
                break;
            case MotionEvent.ACTION_UP:
                cancelForwarding = true;
                // $FALL-THROUGH$
            case MotionEvent.ACTION_MOVE:
                final int activeIndex = ev.findPointerIndex(activePointerId);
                if (activeIndex < 0) {
                    return false;
                }

                final int[] location = mTempLocation;
                int x = (int) ev.getX(activeIndex);
                int y = (int) ev.getY(activeIndex);

                // Convert to global coordinates.
                v.getLocationOnScreen(location);
                x += location[0];
                y += location[1];

                // Convert to local coordinates.
                dstView.getLocationOnScreen(location);
                x -= location[0];
                y -= location[1];

                final int position = dstView.pointToPosition(x, y);
                if (position >= 0) {
                    final int childCount = dstView.getChildCount();
                    final int firstVisiblePosition = dstView.getFirstVisiblePosition();
                    final int index = position - firstVisiblePosition;
                    if (index < childCount) {
                        final View child = dstView.getChildAt(index);
                        if (actionMasked == MotionEvent.ACTION_UP) {
                            // Touch ended, click highlighted item.
                            final long id = dstView.getItemIdAtPosition(position);
                            dstView.performItemClick(child, position, id);
                        } else if (actionMasked == MotionEvent.ACTION_MOVE) {
                            // TODO: Highlight touched item, activate after
                            // long-hover. Consider forwarding events as HOVER and
                            // letting ListView handle this.
                        }
                    }
                }
                break;
        }

        return true;
        return mPopup.onForwardedEvent(src, event, activePointerId);
    }

    @Override