• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package androidx.leanback.widget;
15 
16 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
17 
18 import android.database.Observable;
19 
20 import androidx.annotation.RestrictTo;
21 
22 /**
23  * Base class adapter to be used in leanback activities.  Provides access to a data model and is
24  * decoupled from the presentation of the items via {@link PresenterSelector}.
25  */
26 public abstract class ObjectAdapter {
27 
28     /** Indicates that an id has not been set. */
29     public static final int NO_ID = -1;
30 
31     /**
32      * A DataObserver can be notified when an ObjectAdapter's underlying data
33      * changes. Separate methods provide notifications about different types of
34      * changes.
35      */
36     public static abstract class DataObserver {
37         /**
38          * Called whenever the ObjectAdapter's data has changed in some manner
39          * outside of the set of changes covered by the other range-based change
40          * notification methods.
41          */
onChanged()42         public void onChanged() {
43         }
44 
45         /**
46          * Called when a range of items in the ObjectAdapter has changed. The
47          * basic ordering and structure of the ObjectAdapter has not changed.
48          *
49          * @param positionStart The position of the first item that changed.
50          * @param itemCount     The number of items changed.
51          */
onItemRangeChanged(int positionStart, int itemCount)52         public void onItemRangeChanged(int positionStart, int itemCount) {
53             onChanged();
54         }
55 
56         /**
57          * Called when a range of items in the ObjectAdapter has changed. The
58          * basic ordering and structure of the ObjectAdapter has not changed.
59          *
60          * @param positionStart The position of the first item that changed.
61          * @param itemCount     The number of items changed.
62          * @param payload       Optional parameter, use null to identify a "full" update.
63          */
onItemRangeChanged(int positionStart, int itemCount, Object payload)64         public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
65             onChanged();
66         }
67 
68         /**
69          * Called when a range of items is inserted into the ObjectAdapter.
70          *
71          * @param positionStart The position of the first inserted item.
72          * @param itemCount     The number of items inserted.
73          */
onItemRangeInserted(int positionStart, int itemCount)74         public void onItemRangeInserted(int positionStart, int itemCount) {
75             onChanged();
76         }
77 
78         /**
79          * Called when an item is moved from one position to another position
80          *
81          * @param fromPosition Previous position of the item.
82          * @param toPosition   New position of the item.
83          */
onItemMoved(int fromPosition, int toPosition)84         public void onItemMoved(int fromPosition, int toPosition) {
85             onChanged();
86         }
87 
88         /**
89          * Called when a range of items is removed from the ObjectAdapter.
90          *
91          * @param positionStart The position of the first removed item.
92          * @param itemCount     The number of items removed.
93          */
onItemRangeRemoved(int positionStart, int itemCount)94         public void onItemRangeRemoved(int positionStart, int itemCount) {
95             onChanged();
96         }
97     }
98 
99     private static final class DataObservable extends Observable<DataObserver> {
100 
DataObservable()101         DataObservable() {
102         }
103 
notifyChanged()104         public void notifyChanged() {
105             for (int i = mObservers.size() - 1; i >= 0; i--) {
106                 mObservers.get(i).onChanged();
107             }
108         }
109 
notifyItemRangeChanged(int positionStart, int itemCount)110         public void notifyItemRangeChanged(int positionStart, int itemCount) {
111             for (int i = mObservers.size() - 1; i >= 0; i--) {
112                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount);
113             }
114         }
115 
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)116         public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
117             for (int i = mObservers.size() - 1; i >= 0; i--) {
118                 mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
119             }
120         }
121 
notifyItemRangeInserted(int positionStart, int itemCount)122         public void notifyItemRangeInserted(int positionStart, int itemCount) {
123             for (int i = mObservers.size() - 1; i >= 0; i--) {
124                 mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
125             }
126         }
127 
notifyItemRangeRemoved(int positionStart, int itemCount)128         public void notifyItemRangeRemoved(int positionStart, int itemCount) {
129             for (int i = mObservers.size() - 1; i >= 0; i--) {
130                 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
131             }
132         }
133 
notifyItemMoved(int positionStart, int toPosition)134         public void notifyItemMoved(int positionStart, int toPosition) {
135             for (int i = mObservers.size() - 1; i >= 0; i--) {
136                 mObservers.get(i).onItemMoved(positionStart, toPosition);
137             }
138         }
139 
hasObserver()140         boolean hasObserver() {
141             return mObservers.size() > 0;
142         }
143     }
144 
145     private final DataObservable mObservable = new DataObservable();
146     private boolean mHasStableIds;
147     private PresenterSelector mPresenterSelector;
148 
149     /**
150      * Constructs an adapter with the given {@link PresenterSelector}.
151      */
ObjectAdapter(PresenterSelector presenterSelector)152     public ObjectAdapter(PresenterSelector presenterSelector) {
153         setPresenterSelector(presenterSelector);
154     }
155 
156     /**
157      * Constructs an adapter that uses the given {@link Presenter} for all items.
158      */
ObjectAdapter(Presenter presenter)159     public ObjectAdapter(Presenter presenter) {
160         setPresenterSelector(new SinglePresenterSelector(presenter));
161     }
162 
163     /**
164      * Constructs an adapter.
165      */
ObjectAdapter()166     public ObjectAdapter() {
167     }
168 
169     /**
170      * Sets the presenter selector.  May not be null.
171      */
setPresenterSelector(PresenterSelector presenterSelector)172     public final void setPresenterSelector(PresenterSelector presenterSelector) {
173         if (presenterSelector == null) {
174             throw new IllegalArgumentException("Presenter selector must not be null");
175         }
176         final boolean update = (mPresenterSelector != null);
177         final boolean selectorChanged = update && mPresenterSelector != presenterSelector;
178 
179         mPresenterSelector = presenterSelector;
180 
181         if (selectorChanged) {
182             onPresenterSelectorChanged();
183         }
184         if (update) {
185             notifyChanged();
186         }
187     }
188 
189     /**
190      * Called when {@link #setPresenterSelector(PresenterSelector)} is called
191      * and the PresenterSelector differs from the previous one.
192      */
onPresenterSelectorChanged()193     protected void onPresenterSelectorChanged() {
194     }
195 
196     /**
197      * Returns the presenter selector for this ObjectAdapter.
198      */
getPresenterSelector()199     public final PresenterSelector getPresenterSelector() {
200         return mPresenterSelector;
201     }
202 
203     /**
204      * Registers a DataObserver for data change notifications.
205      */
registerObserver(DataObserver observer)206     public final void registerObserver(DataObserver observer) {
207         mObservable.registerObserver(observer);
208     }
209 
210     /**
211      * Unregisters a DataObserver for data change notifications.
212      */
unregisterObserver(DataObserver observer)213     public final void unregisterObserver(DataObserver observer) {
214         mObservable.unregisterObserver(observer);
215     }
216 
217     /**
218      * @hide
219      */
220     @RestrictTo(LIBRARY_GROUP)
hasObserver()221     public final boolean hasObserver() {
222         return mObservable.hasObserver();
223     }
224 
225     /**
226      * Unregisters all DataObservers for this ObjectAdapter.
227      */
unregisterAllObservers()228     public final void unregisterAllObservers() {
229         mObservable.unregisterAll();
230     }
231 
232     /**
233      * Notifies UI that some items has changed.
234      *
235      * @param positionStart Starting position of the changed items.
236      * @param itemCount     Total number of items that changed.
237      */
notifyItemRangeChanged(int positionStart, int itemCount)238     public final void notifyItemRangeChanged(int positionStart, int itemCount) {
239         mObservable.notifyItemRangeChanged(positionStart, itemCount);
240     }
241 
242     /**
243      * Notifies UI that some items has changed.
244      *
245      * @param positionStart Starting position of the changed items.
246      * @param itemCount     Total number of items that changed.
247      * @param payload       Optional parameter, use null to identify a "full" update.
248      */
notifyItemRangeChanged(int positionStart, int itemCount, Object payload)249     public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
250         mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
251     }
252 
253     /**
254      * Notifies UI that new items has been inserted.
255      *
256      * @param positionStart Position where new items has been inserted.
257      * @param itemCount     Count of the new items has been inserted.
258      */
notifyItemRangeInserted(int positionStart, int itemCount)259     final protected void notifyItemRangeInserted(int positionStart, int itemCount) {
260         mObservable.notifyItemRangeInserted(positionStart, itemCount);
261     }
262 
263     /**
264      * Notifies UI that some items that has been removed.
265      *
266      * @param positionStart Starting position of the removed items.
267      * @param itemCount     Total number of items that has been removed.
268      */
notifyItemRangeRemoved(int positionStart, int itemCount)269     final protected void notifyItemRangeRemoved(int positionStart, int itemCount) {
270         mObservable.notifyItemRangeRemoved(positionStart, itemCount);
271     }
272 
273     /**
274      * Notifies UI that item at fromPosition has been moved to toPosition.
275      *
276      * @param fromPosition Previous position of the item.
277      * @param toPosition   New position of the item.
278      */
notifyItemMoved(int fromPosition, int toPosition)279     protected final void notifyItemMoved(int fromPosition, int toPosition) {
280         mObservable.notifyItemMoved(fromPosition, toPosition);
281     }
282 
283     /**
284      * Notifies UI that the underlying data has changed.
285      */
notifyChanged()286     final protected void notifyChanged() {
287         mObservable.notifyChanged();
288     }
289 
290     /**
291      * Returns true if the item ids are stable across changes to the
292      * underlying data.  When this is true, clients of the ObjectAdapter can use
293      * {@link #getId(int)} to correlate Objects across changes.
294      */
hasStableIds()295     public final boolean hasStableIds() {
296         return mHasStableIds;
297     }
298 
299     /**
300      * Sets whether the item ids are stable across changes to the underlying
301      * data.
302      */
setHasStableIds(boolean hasStableIds)303     public final void setHasStableIds(boolean hasStableIds) {
304         boolean changed = mHasStableIds != hasStableIds;
305         mHasStableIds = hasStableIds;
306 
307         if (changed) {
308             onHasStableIdsChanged();
309         }
310     }
311 
312     /**
313      * Called when {@link #setHasStableIds(boolean)} is called and the status
314      * of stable ids has changed.
315      */
onHasStableIdsChanged()316     protected void onHasStableIdsChanged() {
317     }
318 
319     /**
320      * Returns the {@link Presenter} for the given item from the adapter.
321      */
getPresenter(Object item)322     public final Presenter getPresenter(Object item) {
323         if (mPresenterSelector == null) {
324             throw new IllegalStateException("Presenter selector must not be null");
325         }
326         return mPresenterSelector.getPresenter(item);
327     }
328 
329     /**
330      * Returns the number of items in the adapter.
331      */
size()332     public abstract int size();
333 
334     /**
335      * Returns the item for the given position.
336      */
get(int position)337     public abstract Object get(int position);
338 
339     /**
340      * Returns the id for the given position.
341      */
getId(int position)342     public long getId(int position) {
343         return NO_ID;
344     }
345 
346     /**
347      * Returns true if the adapter pairs each underlying data change with a call to notify and
348      * false otherwise.
349      */
isImmediateNotifySupported()350     public boolean isImmediateNotifySupported() {
351         return false;
352     }
353 }
354