1 package com.android.launcher3; 2 3 import android.view.MotionEvent; 4 import android.view.View; 5 import android.view.ViewConfiguration; 6 7 /** 8 * Helper for identifying when a stylus touches a view while the primary stylus button is pressed. 9 * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}. 10 */ 11 public class StylusEventHelper { 12 13 /** 14 * Implement this interface to receive callbacks for a stylus button press and release. 15 */ 16 public interface StylusButtonListener { 17 /** 18 * Called when the stylus button is pressed. 19 * 20 * @param event The MotionEvent that the button press occurred for. 21 * @return Whether the event was handled. 22 */ onPressed(MotionEvent event)23 public boolean onPressed(MotionEvent event); 24 25 /** 26 * Called when the stylus button is released after a button press. This is also called if 27 * the event is canceled or the stylus is lifted off the screen. 28 * 29 * @param event The MotionEvent the button release occurred for. 30 * @return Whether the event was handled. 31 */ onReleased(MotionEvent event)32 public boolean onReleased(MotionEvent event); 33 } 34 35 private boolean mIsButtonPressed; 36 private View mView; 37 private StylusButtonListener mListener; 38 private final float mSlop; 39 40 /** 41 * Constructs a helper for listening to stylus button presses and releases. Ensure that { 42 * {@link #onMotionEvent(MotionEvent)} and {@link #onGenericMotionEvent(MotionEvent)} are called on 43 * the helper to correctly identify stylus events. 44 * 45 * @param listener The listener to call for stylus events. 46 * @param view Optional view associated with the touch events. 47 */ StylusEventHelper(StylusButtonListener listener, View view)48 public StylusEventHelper(StylusButtonListener listener, View view) { 49 mListener = listener; 50 mView = view; 51 if (mView != null) { 52 mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop(); 53 } else { 54 mSlop = ViewConfiguration.getTouchSlop(); 55 } 56 } 57 onMotionEvent(MotionEvent event)58 public boolean onMotionEvent(MotionEvent event) { 59 final boolean stylusButtonPressed = isStylusButtonPressed(event); 60 switch (event.getAction()) { 61 case MotionEvent.ACTION_DOWN: 62 mIsButtonPressed = stylusButtonPressed; 63 if (mIsButtonPressed) { 64 return mListener.onPressed(event); 65 } 66 break; 67 case MotionEvent.ACTION_MOVE: 68 if (!Utilities.pointInView(mView, event.getX(), event.getY(), mSlop)) { 69 return false; 70 } 71 if (!mIsButtonPressed && stylusButtonPressed) { 72 mIsButtonPressed = true; 73 return mListener.onPressed(event); 74 } else if (mIsButtonPressed && !stylusButtonPressed) { 75 mIsButtonPressed = false; 76 return mListener.onReleased(event); 77 } 78 break; 79 case MotionEvent.ACTION_UP: 80 case MotionEvent.ACTION_CANCEL: 81 if (mIsButtonPressed) { 82 mIsButtonPressed = false; 83 return mListener.onReleased(event); 84 } 85 break; 86 } 87 return false; 88 } 89 90 /** 91 * Whether a stylus button press is occurring. 92 */ inStylusButtonPressed()93 public boolean inStylusButtonPressed() { 94 return mIsButtonPressed; 95 } 96 97 /** 98 * Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button 99 * pressed. 100 * 101 * @param event The event to check. 102 * @return Whether a stylus button press occurred. 103 */ isStylusButtonPressed(MotionEvent event)104 private static boolean isStylusButtonPressed(MotionEvent event) { 105 return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS 106 && ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) 107 == MotionEvent.BUTTON_SECONDARY); 108 } 109 }