1 /* 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.recyclerview.selection; 18 19 import android.graphics.Point; 20 import android.view.InputDevice; 21 import android.view.KeyEvent; 22 import android.view.MotionEvent; 23 24 import org.jspecify.annotations.NonNull; 25 26 /** 27 * Utility methods for working with {@link MotionEvent} instances. 28 */ 29 final class MotionEvents { 30 MotionEvents()31 private MotionEvents() { 32 } 33 isTouchpadEvent(@onNull MotionEvent e)34 static boolean isTouchpadEvent(@NonNull MotionEvent e) { 35 // ChromeOS ARC devices with touchpads emit their events with 36 // {@link MotionEvent#TOOL_TYPE_MOUSE}, so this is specifically capturing non-ARC devices 37 // with touchpads (e.g. attachable keyboards with touchpads on Android tablets). 38 return e.getToolType(0) == MotionEvent.TOOL_TYPE_FINGER 39 && e.getSource() == InputDevice.SOURCE_MOUSE; 40 } 41 isMouseEvent(@onNull MotionEvent e)42 static boolean isMouseEvent(@NonNull MotionEvent e) { 43 return e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE; 44 } 45 isFingerEvent(@onNull MotionEvent e)46 static boolean isFingerEvent(@NonNull MotionEvent e) { 47 return e.getToolType(0) == MotionEvent.TOOL_TYPE_FINGER; 48 } 49 isActionDown(@onNull MotionEvent e)50 static boolean isActionDown(@NonNull MotionEvent e) { 51 return e.getActionMasked() == MotionEvent.ACTION_DOWN; 52 } 53 isActionMove(@onNull MotionEvent e)54 static boolean isActionMove(@NonNull MotionEvent e) { 55 return e.getActionMasked() == MotionEvent.ACTION_MOVE; 56 } 57 isActionUp(@onNull MotionEvent e)58 static boolean isActionUp(@NonNull MotionEvent e) { 59 return e.getActionMasked() == MotionEvent.ACTION_UP; 60 } 61 isActionPointerUp(@onNull MotionEvent e)62 static boolean isActionPointerUp(@NonNull MotionEvent e) { 63 return e.getActionMasked() == MotionEvent.ACTION_POINTER_UP; 64 } 65 66 @SuppressWarnings("unused") isActionPointerDown(@onNull MotionEvent e)67 static boolean isActionPointerDown(@NonNull MotionEvent e) { 68 return e.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN; 69 } 70 isActionCancel(@onNull MotionEvent e)71 static boolean isActionCancel(@NonNull MotionEvent e) { 72 return e.getActionMasked() == MotionEvent.ACTION_CANCEL; 73 } 74 getOrigin(@onNull MotionEvent e)75 static Point getOrigin(@NonNull MotionEvent e) { 76 return new Point((int) e.getX(), (int) e.getY()); 77 } 78 isPrimaryMouseButtonPressed(@onNull MotionEvent e)79 static boolean isPrimaryMouseButtonPressed(@NonNull MotionEvent e) { 80 return isButtonPressed(e, MotionEvent.BUTTON_PRIMARY); 81 } 82 isSecondaryMouseButtonPressed(@onNull MotionEvent e)83 static boolean isSecondaryMouseButtonPressed(@NonNull MotionEvent e) { 84 return isButtonPressed(e, MotionEvent.BUTTON_SECONDARY); 85 } 86 isTertiaryMouseButtonPressed(@onNull MotionEvent e)87 static boolean isTertiaryMouseButtonPressed(@NonNull MotionEvent e) { 88 return isButtonPressed(e, MotionEvent.BUTTON_TERTIARY); 89 } 90 91 // NOTE: Can replace this with MotionEvent.isButtonPressed once targeting 21 or higher. isButtonPressed(MotionEvent e, int button)92 private static boolean isButtonPressed(MotionEvent e, int button) { 93 if (button == 0) { 94 return false; 95 } 96 return (e.getButtonState() & button) == button; 97 } 98 isShiftKeyPressed(@onNull MotionEvent e)99 static boolean isShiftKeyPressed(@NonNull MotionEvent e) { 100 return hasBit(e.getMetaState(), KeyEvent.META_SHIFT_ON); 101 } 102 isCtrlKeyPressed(@onNull MotionEvent e)103 static boolean isCtrlKeyPressed(@NonNull MotionEvent e) { 104 return hasBit(e.getMetaState(), KeyEvent.META_CTRL_ON); 105 } 106 isAltKeyPressed(@onNull MotionEvent e)107 static boolean isAltKeyPressed(@NonNull MotionEvent e) { 108 return hasBit(e.getMetaState(), KeyEvent.META_ALT_ON); 109 } 110 isTouchpadScroll(@onNull MotionEvent e)111 static boolean isTouchpadScroll(@NonNull MotionEvent e) { 112 // Touchpad inputs are treated as mouse inputs, and when scrolling, there are no buttons 113 // returned. 114 return (isTouchpadEvent(e) || isMouseEvent(e)) && isActionMove(e) 115 && e.getButtonState() == 0; 116 } 117 118 /** 119 * Returns true if the event is a drag event (which is presumbaly, but not 120 * explicitly required to be a mouse event). 121 */ isPointerDragEvent(MotionEvent e)122 static boolean isPointerDragEvent(MotionEvent e) { 123 return isPrimaryMouseButtonPressed(e) 124 && isActionMove(e); 125 } 126 hasBit(int metaState, int bit)127 private static boolean hasBit(int metaState, int bit) { 128 return (metaState & bit) != 0; 129 } 130 createCancelEvent()131 static MotionEvent createCancelEvent() { 132 return MotionEvent.obtain( 133 0, // down time 134 1, // event time 135 MotionEvent.ACTION_CANCEL, 136 0, // x 137 0, // y 138 0 // metaState 139 ); 140 } 141 } 142