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