1 /* 2 * Copyright 2019 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 static androidx.recyclerview.selection.Shared.DEBUG; 20 21 import android.util.Log; 22 import android.view.MotionEvent; 23 24 import androidx.recyclerview.selection.SelectionTracker.SelectionObserver; 25 import androidx.recyclerview.widget.RecyclerView; 26 import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener; 27 28 import org.jspecify.annotations.NonNull; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** 34 * Class managing resetting of library state in response to specific 35 * events like clearing of selection and MotionEvent.ACTION_CANCEL 36 * events. 37 * 38 * @param <K> Selection key type. @see {@link StorageStrategy} for supported types. 39 */ 40 final class ResetManager<K> { 41 42 private static final String TAG = "ResetManager"; 43 44 private final List<Resettable> mResetHandlers = new ArrayList<>(); 45 46 private final OnItemTouchListener mInputListener = new OnItemTouchListener() { 47 @Override 48 public boolean onInterceptTouchEvent(@NonNull RecyclerView unused, 49 @NonNull MotionEvent e) { 50 if (MotionEvents.isActionCancel(e)) { 51 if (DEBUG) Log.d(TAG, "Received CANCEL event."); 52 callResetHandlers(); 53 } 54 return false; 55 } 56 57 @Override 58 public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { 59 } 60 61 @Override 62 public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { 63 } 64 }; 65 66 // Resettable interface has a #requiresReset method because DefaultSelectionTracker 67 // (owner of the state we observe with our SelectionObserver) is, itself, 68 // a Resettable. Such an arrangement introduces the real possibility of infinite recursion. 69 // When we call reset on DefaultSelectionTracker it'll eventually call back to 70 // notify us of the change via onSelectionCleared. We avoid recursion by 71 // checking #requiresReset before calling reset again. 72 private final SelectionObserver<K> mSelectionObserver = new SelectionObserver<K>() { 73 @Override 74 protected void onSelectionCleared() { 75 if (DEBUG) Log.d(TAG, "Received onSelectionCleared event."); 76 callResetHandlers(); 77 } 78 }; 79 getSelectionObserver()80 SelectionObserver<K> getSelectionObserver() { 81 return mSelectionObserver; 82 } 83 getInputListener()84 OnItemTouchListener getInputListener() { 85 return mInputListener; 86 } 87 88 /** 89 * Registers a new Resettable. 90 */ addResetHandler(@onNull Resettable handler)91 void addResetHandler(@NonNull Resettable handler) { 92 mResetHandlers.add(handler); 93 } 94 callResetHandlers()95 void callResetHandlers() { 96 for (Resettable handler : mResetHandlers) { 97 if (handler.isResetRequired()) { 98 handler.reset(); 99 } 100 } 101 } 102 } 103