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