• 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 android.util.Log;
17 
18 import androidx.annotation.Nullable;
19 import androidx.recyclerview.widget.DiffUtil;
20 import androidx.recyclerview.widget.ListUpdateCallback;
21 
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.List;
26 
27 /**
28  * An {@link ObjectAdapter} implemented with an {@link ArrayList}.
29  */
30 public class ArrayObjectAdapter extends ObjectAdapter {
31 
32     private static final Boolean DEBUG = false;
33     private static final String TAG = "ArrayObjectAdapter";
34 
35     private final List mItems = new ArrayList<Object>();
36 
37     // To compute the payload correctly, we should use a temporary list to hold all the old items.
38     private final List mOldItems = new ArrayList<Object>();
39 
40     // Un modifiable version of mItems;
41     private List mUnmodifiableItems;
42 
43     /**
44      * Constructs an adapter with the given {@link PresenterSelector}.
45      */
ArrayObjectAdapter(PresenterSelector presenterSelector)46     public ArrayObjectAdapter(PresenterSelector presenterSelector) {
47         super(presenterSelector);
48     }
49 
50     /**
51      * Constructs an adapter that uses the given {@link Presenter} for all items.
52      */
ArrayObjectAdapter(Presenter presenter)53     public ArrayObjectAdapter(Presenter presenter) {
54         super(presenter);
55     }
56 
57     /**
58      * Constructs an adapter.
59      */
ArrayObjectAdapter()60     public ArrayObjectAdapter() {
61         super();
62     }
63 
64     @Override
size()65     public int size() {
66         return mItems.size();
67     }
68 
69     @Override
get(int index)70     public Object get(int index) {
71         return mItems.get(index);
72     }
73 
74     /**
75      * Returns the index for the first occurrence of item in the adapter, or -1 if
76      * not found.
77      *
78      * @param item The item to find in the list.
79      * @return Index of the first occurrence of the item in the adapter, or -1
80      * if not found.
81      */
indexOf(Object item)82     public int indexOf(Object item) {
83         return mItems.indexOf(item);
84     }
85 
86     /**
87      * Notify that the content of a range of items changed. Note that this is
88      * not same as items being added or removed.
89      *
90      * @param positionStart The position of first item that has changed.
91      * @param itemCount     The count of how many items have changed.
92      */
notifyArrayItemRangeChanged(int positionStart, int itemCount)93     public void notifyArrayItemRangeChanged(int positionStart, int itemCount) {
94         notifyItemRangeChanged(positionStart, itemCount);
95     }
96 
97     /**
98      * Adds an item to the end of the adapter.
99      *
100      * @param item The item to add to the end of the adapter.
101      */
add(Object item)102     public void add(Object item) {
103         add(mItems.size(), item);
104     }
105 
106     /**
107      * Inserts an item into this adapter at the specified index.
108      * If the index is > {@link #size} an exception will be thrown.
109      *
110      * @param index The index at which the item should be inserted.
111      * @param item  The item to insert into the adapter.
112      */
add(int index, Object item)113     public void add(int index, Object item) {
114         mItems.add(index, item);
115         notifyItemRangeInserted(index, 1);
116     }
117 
118     /**
119      * Adds the objects in the given collection to the adapter, starting at the
120      * given index.  If the index is >= {@link #size} an exception will be thrown.
121      *
122      * @param index The index at which the items should be inserted.
123      * @param items A {@link Collection} of items to insert.
124      */
addAll(int index, Collection items)125     public void addAll(int index, Collection items) {
126         int itemsCount = items.size();
127         if (itemsCount == 0) {
128             return;
129         }
130         mItems.addAll(index, items);
131         notifyItemRangeInserted(index, itemsCount);
132     }
133 
134     /**
135      * Removes the first occurrence of the given item from the adapter.
136      *
137      * @param item The item to remove from the adapter.
138      * @return True if the item was found and thus removed from the adapter.
139      */
remove(Object item)140     public boolean remove(Object item) {
141         int index = mItems.indexOf(item);
142         if (index >= 0) {
143             mItems.remove(index);
144             notifyItemRangeRemoved(index, 1);
145         }
146         return index >= 0;
147     }
148 
149     /**
150      * Moved the item at fromPosition to toPosition.
151      *
152      * @param fromPosition Previous position of the item.
153      * @param toPosition   New position of the item.
154      */
move(int fromPosition, int toPosition)155     public void move(int fromPosition, int toPosition) {
156         if (fromPosition == toPosition) {
157             // no-op
158             return;
159         }
160         Object item = mItems.remove(fromPosition);
161         mItems.add(toPosition, item);
162         notifyItemMoved(fromPosition, toPosition);
163     }
164 
165     /**
166      * Replaces item at position with a new item and calls notifyItemRangeChanged()
167      * at the given position.  Note that this method does not compare new item to
168      * existing item.
169      *
170      * @param position The index of item to replace.
171      * @param item     The new item to be placed at given position.
172      */
replace(int position, Object item)173     public void replace(int position, Object item) {
174         mItems.set(position, item);
175         notifyItemRangeChanged(position, 1);
176     }
177 
178     /**
179      * Removes a range of items from the adapter. The range is specified by giving
180      * the starting position and the number of elements to remove.
181      *
182      * @param position The index of the first item to remove.
183      * @param count    The number of items to remove.
184      * @return The number of items removed.
185      */
removeItems(int position, int count)186     public int removeItems(int position, int count) {
187         int itemsToRemove = Math.min(count, mItems.size() - position);
188         if (itemsToRemove <= 0) {
189             return 0;
190         }
191 
192         for (int i = 0; i < itemsToRemove; i++) {
193             mItems.remove(position);
194         }
195         notifyItemRangeRemoved(position, itemsToRemove);
196         return itemsToRemove;
197     }
198 
199     /**
200      * Removes all items from this adapter, leaving it empty.
201      */
clear()202     public void clear() {
203         int itemCount = mItems.size();
204         if (itemCount == 0) {
205             return;
206         }
207         mItems.clear();
208         notifyItemRangeRemoved(0, itemCount);
209     }
210 
211     /**
212      * Gets a read-only view of the list of object of this ArrayObjectAdapter.
213      */
unmodifiableList()214     public <E> List<E> unmodifiableList() {
215 
216         // The mUnmodifiableItems will only be created once as long as the content of mItems has not
217         // been changed.
218         if (mUnmodifiableItems == null) {
219             mUnmodifiableItems = Collections.unmodifiableList(mItems);
220         }
221         return mUnmodifiableItems;
222     }
223 
224     @Override
isImmediateNotifySupported()225     public boolean isImmediateNotifySupported() {
226         return true;
227     }
228 
229     ListUpdateCallback mListUpdateCallback;
230 
231     /**
232      * Set a new item list to adapter. The DiffUtil will compute the difference and dispatch it to
233      * specified position.
234      *
235      * @param itemList List of new Items
236      * @param callback Optional DiffCallback Object to compute the difference between the old data
237      *                 set and new data set. When null, {@link #notifyChanged()} will be fired.
238      */
setItems(final List itemList, final DiffCallback callback)239     public void setItems(final List itemList, final DiffCallback callback) {
240         if (callback == null) {
241             // shortcut when DiffCallback is not provided
242             mItems.clear();
243             mItems.addAll(itemList);
244             notifyChanged();
245             return;
246         }
247         mOldItems.clear();
248         mOldItems.addAll(mItems);
249 
250         DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
251             @Override
252             public int getOldListSize() {
253                 return mOldItems.size();
254             }
255 
256             @Override
257             public int getNewListSize() {
258                 return itemList.size();
259             }
260 
261             @Override
262             public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
263                 return callback.areItemsTheSame(mOldItems.get(oldItemPosition),
264                         itemList.get(newItemPosition));
265             }
266 
267             @Override
268             public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
269                 return callback.areContentsTheSame(mOldItems.get(oldItemPosition),
270                         itemList.get(newItemPosition));
271             }
272 
273             @Nullable
274             @Override
275             public Object getChangePayload(int oldItemPosition, int newItemPosition) {
276                 return callback.getChangePayload(mOldItems.get(oldItemPosition),
277                         itemList.get(newItemPosition));
278             }
279         });
280 
281         // update items.
282         mItems.clear();
283         mItems.addAll(itemList);
284 
285         // dispatch diff result
286         if (mListUpdateCallback == null) {
287             mListUpdateCallback = new ListUpdateCallback() {
288 
289                 @Override
290                 public void onInserted(int position, int count) {
291                     if (DEBUG) {
292                         Log.d(TAG, "onInserted");
293                     }
294                     notifyItemRangeInserted(position, count);
295                 }
296 
297                 @Override
298                 public void onRemoved(int position, int count) {
299                     if (DEBUG) {
300                         Log.d(TAG, "onRemoved");
301                     }
302                     notifyItemRangeRemoved(position, count);
303                 }
304 
305                 @Override
306                 public void onMoved(int fromPosition, int toPosition) {
307                     if (DEBUG) {
308                         Log.d(TAG, "onMoved");
309                     }
310                     notifyItemMoved(fromPosition, toPosition);
311                 }
312 
313                 @Override
314                 public void onChanged(int position, int count, Object payload) {
315                     if (DEBUG) {
316                         Log.d(TAG, "onChanged");
317                     }
318                     notifyItemRangeChanged(position, count, payload);
319                 }
320             };
321         }
322         diffResult.dispatchUpdatesTo(mListUpdateCallback);
323         mOldItems.clear();
324     }
325 }
326