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 static androidx.annotation.RestrictTo.Scope.LIBRARY; 20 import static androidx.core.util.Preconditions.checkArgument; 21 import static androidx.recyclerview.selection.Shared.VERBOSE; 22 23 import android.util.Log; 24 25 import androidx.annotation.RestrictTo; 26 import androidx.core.util.Consumer; 27 import androidx.recyclerview.widget.RecyclerView; 28 29 import org.jspecify.annotations.NonNull; 30 31 /** 32 * Provides the necessary glue to notify RecyclerView when selection data changes, 33 * and to notify SelectionTracker when the underlying RecyclerView.Adapter data changes. 34 * 35 * This strict decoupling is necessary to permit a single SelectionTracker to work 36 * with multiple RecyclerView instances. This may be necessary when multiple 37 * different views of data are presented to the user. 38 * 39 */ 40 @RestrictTo(LIBRARY) 41 public class EventBridge { 42 43 private static final String TAG = "EventsRelays"; 44 45 /** 46 * Installs the event bridge for on the supplied adapter/helper. 47 * 48 * @param adapter 49 * @param selectionTracker 50 * @param keyProvider 51 * @param runner Callback allowing operation to be run at next opportune time. 52 * Implementation could be {@link RecyclerView#postOnAnimation(Runnable)}. 53 * 54 * @param <K> Selection key type. @see {@link StorageStrategy} for supported types. 55 */ install( RecyclerView.@onNull Adapter<?> adapter, @NonNull SelectionTracker<K> selectionTracker, @NonNull ItemKeyProvider<K> keyProvider, @NonNull Consumer<Runnable> runner)56 public static <K> void install( 57 RecyclerView.@NonNull Adapter<?> adapter, 58 @NonNull SelectionTracker<K> selectionTracker, 59 @NonNull ItemKeyProvider<K> keyProvider, 60 @NonNull Consumer<Runnable> runner) { 61 62 // setup bridges to relay selection and adapter events 63 new TrackerToAdapterBridge<>(selectionTracker, keyProvider, adapter, runner); 64 adapter.registerAdapterDataObserver(selectionTracker.getAdapterDataObserver()); 65 } 66 67 private static final class TrackerToAdapterBridge<K> 68 extends SelectionTracker.SelectionObserver<K> { 69 70 // Non-private as necessary to avoid synthetic accessors for inner classes. 71 final RecyclerView.Adapter<?> mAdapter; 72 private final ItemKeyProvider<K> mKeyProvider; 73 private final Consumer<Runnable> mRunner; 74 TrackerToAdapterBridge( @onNull SelectionTracker<K> selectionTracker, @NonNull ItemKeyProvider<K> keyProvider, RecyclerView.@NonNull Adapter<?> adapter, Consumer<Runnable> runner)75 TrackerToAdapterBridge( 76 @NonNull SelectionTracker<K> selectionTracker, 77 @NonNull ItemKeyProvider<K> keyProvider, 78 RecyclerView.@NonNull Adapter<?> adapter, 79 Consumer<Runnable> runner) { 80 81 selectionTracker.addObserver(this); 82 83 checkArgument(keyProvider != null); 84 checkArgument(adapter != null); 85 checkArgument(runner != null); 86 87 mKeyProvider = keyProvider; 88 mAdapter = adapter; 89 mRunner = runner; 90 } 91 92 /** 93 * Called when state of an item has been changed. 94 */ 95 @Override onItemStateChanged(@onNull K key, boolean selected)96 public void onItemStateChanged(@NonNull K key, boolean selected) { 97 int position = mKeyProvider.getPosition(key); 98 if (VERBOSE) Log.v(TAG, "ITEM " + key + " CHANGED at pos: " + position); 99 100 if (position < 0) { 101 Log.w(TAG, "Item change notification received for unknown item: " + key); 102 return; 103 } 104 105 mRunner.accept(new Runnable() { 106 @Override 107 public void run() { 108 mAdapter.notifyItemChanged(position, SelectionTracker.SELECTION_CHANGED_MARKER); 109 } 110 }); 111 } 112 } 113 EventBridge()114 private EventBridge() { 115 } 116 } 117