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 17package androidx.recyclerview.selection; 18 19import static androidx.core.util.Preconditions.checkArgument; 20 21import android.view.GestureDetector; 22import android.view.MotionEvent; 23 24import androidx.annotation.NonNull; 25import androidx.recyclerview.widget.RecyclerView; 26import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener; 27 28/** 29 * A class responsible for routing MotionEvents to tool-type specific handlers, 30 * and if not handled by a handler, on to a {@link GestureDetector} for further 31 * processing. 32 * 33 * <p> 34 * TouchEventRouter takes its name from 35 * {@link RecyclerView#addOnItemTouchListener(OnItemTouchListener)}. Despite "Touch" 36 * being in the name, it receives MotionEvents for all types of tools. 37 */ 38final class TouchEventRouter implements OnItemTouchListener { 39 40 private static final String TAG = "TouchEventRouter"; 41 42 private final GestureDetector mDetector; 43 private final ToolHandlerRegistry<OnItemTouchListener> mDelegates; 44 45 TouchEventRouter( 46 @NonNull GestureDetector detector, @NonNull OnItemTouchListener defaultDelegate) { 47 48 checkArgument(detector != null); 49 checkArgument(defaultDelegate != null); 50 51 mDetector = detector; 52 mDelegates = new ToolHandlerRegistry<>(defaultDelegate); 53 } 54 55 TouchEventRouter(@NonNull GestureDetector detector) { 56 this( 57 detector, 58 // Supply a fallback listener does nothing...because the caller 59 // didn't supply a fallback. 60 new OnItemTouchListener() { 61 @Override 62 public boolean onInterceptTouchEvent( 63 @NonNull RecyclerView unused, @NonNull MotionEvent e) { 64 65 return false; 66 } 67 68 @Override 69 public void onTouchEvent( 70 @NonNull RecyclerView unused, @NonNull MotionEvent e) { 71 } 72 73 @Override 74 public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { 75 } 76 }); 77 } 78 79 /** 80 * @param toolType See MotionEvent for details on available types. 81 * @param delegate An {@link OnItemTouchListener} to receive events 82 * of {@code toolType}. 83 */ 84 void register(int toolType, @NonNull OnItemTouchListener delegate) { 85 checkArgument(delegate != null); 86 mDelegates.set(toolType, delegate); 87 } 88 89 @Override 90 public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { 91 boolean handled = mDelegates.get(e).onInterceptTouchEvent(rv, e); 92 93 // Forward all events to UserInputHandler. 94 // This is necessary since UserInputHandler needs to always see the first DOWN event. Or 95 // else all future UP events will be tossed. 96 handled |= mDetector.onTouchEvent(e); 97 98 return handled; 99 } 100 101 @Override 102 public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { 103 mDelegates.get(e).onTouchEvent(rv, e); 104 105 // Note: even though this event is being handled as part of gestures such as drag and band, 106 // continue forwarding to the GestureDetector. The detector needs to see the entire cluster 107 // of events in order to properly interpret other gestures, such as long press. 108 mDetector.onTouchEvent(e); 109 } 110 111 @Override 112 public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {} 113} 114