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

Commit 33f98496 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Merging stylus click logic in longpress helper for better...

Merge "Merging stylus click logic in longpress helper for better state-management" into ub-launcher3-master
parents 5c359efc 17feee89
Loading
Loading
Loading
Loading
+8 −42
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@ import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.widget.TextView;

@@ -109,8 +108,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
    private final int mDisplay;

    private final CheckLongPressHelper mLongPressHelper;
    private final StylusEventHelper mStylusEventHelper;
    private final float mSlop;

    private final boolean mLayoutHorizontal;
    private final int mIconSize;
@@ -137,9 +134,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
    @ViewDebug.ExportedProperty(category = "launcher")
    private boolean mDisableRelayout = false;

    @ViewDebug.ExportedProperty(category = "launcher")
    private final boolean mIgnorePaddingTouch;

    private IconLoadRequest mIconLoadRequest;

    public BubbleTextView(Context context) {
@@ -153,7 +147,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
    public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mActivity = ActivityContext.lookupContext(context);
        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();

        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.BubbleTextView, defStyle, 0);
@@ -166,23 +159,19 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
            setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
            setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
            defaultIconSize = grid.iconSizePx;
            mIgnorePaddingTouch = true;
        } else if (mDisplay == DISPLAY_ALL_APPS) {
            DeviceProfile grid = mActivity.getDeviceProfile();
            setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
            setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
            defaultIconSize = grid.allAppsIconSizePx;
            mIgnorePaddingTouch = true;
        } else if (mDisplay == DISPLAY_FOLDER) {
            DeviceProfile grid = mActivity.getDeviceProfile();
            setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
            setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
            defaultIconSize = grid.folderChildIconSizePx;
            mIgnorePaddingTouch = true;
        } else {
            // widget_selection or shortcut_popup
            defaultIconSize = mActivity.getDeviceProfile().iconSizePx;
            mIgnorePaddingTouch = false;
        }

        mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
@@ -192,7 +181,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
        a.recycle();

        mLongPressHelper = new CheckLongPressHelper(this);
        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);

        mDotParams = new DotRenderer.DrawParams();

@@ -333,42 +321,21 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // ignore events if they happen in padding area
        if (event.getAction() == MotionEvent.ACTION_DOWN && mIgnorePaddingTouch
        if (event.getAction() == MotionEvent.ACTION_DOWN
                && (event.getY() < getPaddingTop()
                || event.getX() < getPaddingLeft()
                || event.getY() > getHeight() - getPaddingBottom()
                || event.getX() > getWidth() - getPaddingRight())) {
            return false;
        }

        // Call the superclass onTouchEvent first, because sometimes it changes the state to
        // isPressed() on an ACTION_UP
        boolean result = super.onTouchEvent(event);

        // Check for a stylus button press, if it occurs cancel any long press checks.
        if (mStylusEventHelper.onMotionEvent(event)) {
            mLongPressHelper.cancelLongPress();
            result = true;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // If we're in a stylus button press, don't check for long press.
                if (!mStylusEventHelper.inStylusButtonPressed()) {
                    mLongPressHelper.postCheckForLongPress();
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mLongPressHelper.cancelLongPress();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
                    mLongPressHelper.cancelLongPress();
                }
                break;
        if (isLongClickable()) {
            super.onTouchEvent(event);
            mLongPressHelper.onTouchEvent(event);
            // Keep receiving the rest of the events
            return true;
        } else {
            return super.onTouchEvent(event);
        }
        return result;
    }

    void setStayPressed(boolean stayPressed) {
@@ -531,7 +498,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
    @Override
    public void cancelLongPress() {
        super.cancelLongPress();

        mLongPressHelper.cancelLongPress();
    }

+90 −29
Original line number Diff line number Diff line
@@ -16,46 +16,68 @@

package com.android.launcher3;

import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

import com.android.launcher3.util.Thunk;

/**
 * Utility class to handle tripper long press on a view with custom timeout and stylus event
 */
public class CheckLongPressHelper {

    public static final float DEFAULT_LONG_PRESS_TIMEOUT_FACTOR = 0.75f;

    @Thunk View mView;
    @Thunk View.OnLongClickListener mListener;
    @Thunk boolean mHasPerformedLongPress;
    private final View mView;
    private final View.OnLongClickListener mListener;
    private final float mSlop;

    private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR;
    private CheckForLongPress mPendingCheckForLongPress;

    class CheckForLongPress implements Runnable {
        public void run() {
            if ((mView.getParent() != null) && mView.hasWindowFocus()
                    && !mHasPerformedLongPress) {
                boolean handled;
                if (mListener != null) {
                    handled = mListener.onLongClick(mView);
                } else {
                    handled = mView.performLongClick();
                }
                if (handled) {
                    mView.setPressed(false);
                    mHasPerformedLongPress = true;
                }
            }
        }
    }
    private boolean mHasPerformedLongPress;

    private Runnable mPendingCheckForLongPress;

    public CheckLongPressHelper(View v) {
        mView = v;
        this(v, null);
    }

    public CheckLongPressHelper(View v, View.OnLongClickListener listener) {
        mView = v;
        mListener = listener;
        mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
    }

    /**
     * Handles the touch event on a view
     *
     * @see View#onTouchEvent(MotionEvent)
     */
    public void onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                // Just in case the previous long press hasn't been cleared, we make sure to
                // start fresh on touch down.
                cancelLongPress();

                postCheckForLongPress();
                if (isStylusButtonPressed(ev)) {
                    triggerLongPress();
                }
                break;
            }
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                cancelLongPress();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!Utilities.pointInView(mView, ev.getX(), ev.getY(), mSlop)) {
                    cancelLongPress();
                } else if (mPendingCheckForLongPress != null && isStylusButtonPressed(ev)) {
                    // Only trigger long press if it has not been cancelled before
                    triggerLongPress();
                }
                break;
        }
    }

    /**
@@ -65,25 +87,64 @@ public class CheckLongPressHelper {
        mLongPressTimeoutFactor = longPressTimeoutFactor;
    }

    public void postCheckForLongPress() {
    private void postCheckForLongPress() {
        mHasPerformedLongPress = false;

        if (mPendingCheckForLongPress == null) {
            mPendingCheckForLongPress = new CheckForLongPress();
            mPendingCheckForLongPress = this::triggerLongPress;
        }
        mView.postDelayed(mPendingCheckForLongPress,
                (long) (ViewConfiguration.getLongPressTimeout() * mLongPressTimeoutFactor));
    }

    /**
     * Cancels any pending long press
     */
    public void cancelLongPress() {
        mHasPerformedLongPress = false;
        clearCallbacks();
    }

    /**
     * Returns true if long press has been performed in the current touch gesture
     */
    public boolean hasPerformedLongPress() {
        return mHasPerformedLongPress;
    }

    private void triggerLongPress() {
        if ((mView.getParent() != null) && mView.hasWindowFocus() && !mHasPerformedLongPress) {
            boolean handled;
            if (mListener != null) {
                handled = mListener.onLongClick(mView);
            } else {
                handled = mView.performLongClick();
            }
            if (handled) {
                mView.setPressed(false);
                mHasPerformedLongPress = true;
            }
            clearCallbacks();
        }
    }

    private void clearCallbacks() {
        if (mPendingCheckForLongPress != null) {
            mView.removeCallbacks(mPendingCheckForLongPress);
            mPendingCheckForLongPress = null;
        }
    }

    public boolean hasPerformedLongPress() {
        return mHasPerformedLongPress;

    /**
     * Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button
     * pressed.
     *
     * @param event The event to check.
     * @return Whether a stylus button press occurred.
     */
    private static boolean isStylusButtonPressed(MotionEvent event) {
        return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
                && event.isButtonPressed(MotionEvent.BUTTON_SECONDARY);
    }
}
+0 −25
Original line number Diff line number Diff line
package com.android.launcher3;

import android.view.MotionEvent;
import android.view.View;

import com.android.launcher3.StylusEventHelper.StylusButtonListener;

/**
 * Simple listener that performs a long click on the view after a stylus button press.
 */
public class SimpleOnStylusPressListener implements StylusButtonListener {
    private View mView;

    public SimpleOnStylusPressListener(View view) {
        mView = view;
    }

    public boolean onPressed(MotionEvent event) {
        return mView.isLongClickable() && mView.performLongClick();
    }

    public boolean onReleased(MotionEvent event) {
        return false;
    }
}
 No newline at end of file
+0 −109
Original line number Diff line number Diff line
package com.android.launcher3;

import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

/**
 * Helper for identifying when a stylus touches a view while the primary stylus button is pressed.
 * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}.
 */
public class StylusEventHelper {

    /**
     * Implement this interface to receive callbacks for a stylus button press and release.
     */
    public interface StylusButtonListener {
        /**
         * Called when the stylus button is pressed.
         *
         * @param event The MotionEvent that the button press occurred for.
         * @return Whether the event was handled.
         */
        public boolean onPressed(MotionEvent event);

        /**
         * Called when the stylus button is released after a button press. This is also called if
         * the event is canceled or the stylus is lifted off the screen.
         *
         * @param event The MotionEvent the button release occurred for.
         * @return Whether the event was handled.
         */
        public boolean onReleased(MotionEvent event);
    }

    private boolean mIsButtonPressed;
    private View mView;
    private StylusButtonListener mListener;
    private final float mSlop;

    /**
     * Constructs a helper for listening to stylus button presses and releases. Ensure that {
     * {@link #onMotionEvent(MotionEvent)} and {@link #onGenericMotionEvent(MotionEvent)} are called on
     * the helper to correctly identify stylus events.
     *
     * @param listener The listener to call for stylus events.
     * @param view Optional view associated with the touch events.
     */
    public StylusEventHelper(StylusButtonListener listener, View view) {
        mListener = listener;
        mView = view;
        if (mView != null) {
            mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
        } else {
            mSlop = ViewConfiguration.getTouchSlop();
        }
    }

    public boolean onMotionEvent(MotionEvent event) {
        final boolean stylusButtonPressed = isStylusButtonPressed(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mIsButtonPressed = stylusButtonPressed;
                if (mIsButtonPressed) {
                    return mListener.onPressed(event);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (!Utilities.pointInView(mView, event.getX(), event.getY(), mSlop)) {
                    return false;
                }
                if (!mIsButtonPressed && stylusButtonPressed) {
                    mIsButtonPressed = true;
                    return mListener.onPressed(event);
                } else if (mIsButtonPressed && !stylusButtonPressed) {
                    mIsButtonPressed = false;
                    return mListener.onReleased(event);
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mIsButtonPressed) {
                    mIsButtonPressed = false;
                    return mListener.onReleased(event);
                }
                break;
        }
        return false;
    }

    /**
     * Whether a stylus button press is occurring.
     */
    public boolean inStylusButtonPressed() {
        return mIsButtonPressed;
    }

    /**
     * Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button
     * pressed.
     *
     * @param event The event to check.
     * @return Whether a stylus button press occurred.
     */
    private static boolean isStylusButtonPressed(MotionEvent event) {
        return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
                && ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY)
                        == MotionEvent.BUTTON_SECONDARY);
    }
}
 No newline at end of file
+4 −31
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import android.util.Property;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -52,8 +51,6 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.R;
import com.android.launcher3.SimpleOnStylusPressListener;
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
@@ -87,7 +84,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
    private FolderInfo mInfo;

    private CheckLongPressHelper mLongPressHelper;
    private StylusEventHelper mStylusEventHelper;

    static final int DROP_IN_ANIMATION_DURATION = 400;

@@ -110,8 +106,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel

    boolean mAnimating = false;

    private float mSlop;

    private Alarm mOpenAlarm = new Alarm();

    private boolean mForceHideDot;
@@ -149,9 +143,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel

    private void init() {
        mLongPressHelper = new CheckLongPressHelper(this);
        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
        mPreviewLayoutRule = new ClippedFolderIconLayoutRule();
        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        mPreviewItemManager = new PreviewItemManager(this);
        mDotParams = new DotRenderer.DrawParams();
    }
@@ -663,31 +655,12 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
    public boolean onTouchEvent(MotionEvent event) {
        // Call the superclass onTouchEvent first, because sometimes it changes the state to
        // isPressed() on an ACTION_UP
        boolean result = super.onTouchEvent(event);

        // Check for a stylus button press, if it occurs cancel any long press checks.
        if (mStylusEventHelper.onMotionEvent(event)) {
            mLongPressHelper.cancelLongPress();
        super.onTouchEvent(event);
        mLongPressHelper.onTouchEvent(event);
        // Keep receiving the rest of the events
        return true;
    }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLongPressHelper.postCheckForLongPress();
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mLongPressHelper.cancelLongPress();
                break;
            case MotionEvent.ACTION_MOVE:
                if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
                    mLongPressHelper.cancelLongPress();
                }
                break;
        }
        return result;
    }

    @Override
    public void cancelLongPress() {
        super.cancelLongPress();
Loading