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 */ 14package android.support.v17.leanback.widget; 15 16import 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 */ 22public 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 */ 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 */ 48 public void onItemRangeChanged(int positionStart, int itemCount) { 49 onChanged(); 50 } 51 52 /** 53 * Called when a range of items is inserted into the ObjectAdapter. 54 * 55 * @param positionStart The position of the first inserted item. 56 * @param itemCount The number of items inserted. 57 */ 58 public void onItemRangeInserted(int positionStart, int itemCount) { 59 onChanged(); 60 } 61 62 /** 63 * Called when a range of items is removed from the ObjectAdapter. 64 * 65 * @param positionStart The position of the first removed item. 66 * @param itemCount The number of items removed. 67 */ 68 public void onItemRangeRemoved(int positionStart, int itemCount) { 69 onChanged(); 70 } 71 } 72 73 private static final class DataObservable extends Observable<DataObserver> { 74 75 public void notifyChanged() { 76 for (int i = mObservers.size() - 1; i >= 0; i--) { 77 mObservers.get(i).onChanged(); 78 } 79 } 80 81 public void notifyItemRangeChanged(int positionStart, int itemCount) { 82 for (int i = mObservers.size() - 1; i >= 0; i--) { 83 mObservers.get(i).onItemRangeChanged(positionStart, itemCount); 84 } 85 } 86 87 public void notifyItemRangeInserted(int positionStart, int itemCount) { 88 for (int i = mObservers.size() - 1; i >= 0; i--) { 89 mObservers.get(i).onItemRangeInserted(positionStart, itemCount); 90 } 91 } 92 93 public void notifyItemRangeRemoved(int positionStart, int itemCount) { 94 for (int i = mObservers.size() - 1; i >= 0; i--) { 95 mObservers.get(i).onItemRangeRemoved(positionStart, itemCount); 96 } 97 } 98 } 99 100 private final DataObservable mObservable = new DataObservable(); 101 private boolean mHasStableIds; 102 private PresenterSelector mPresenterSelector; 103 104 /** 105 * Constructs an adapter with the given {@link PresenterSelector}. 106 */ 107 public ObjectAdapter(PresenterSelector presenterSelector) { 108 setPresenterSelector(presenterSelector); 109 } 110 111 /** 112 * Constructs an adapter that uses the given {@link Presenter} for all items. 113 */ 114 public ObjectAdapter(Presenter presenter) { 115 setPresenterSelector(new SinglePresenterSelector(presenter)); 116 } 117 118 /** 119 * Constructs an adapter. 120 */ 121 public ObjectAdapter() { 122 } 123 124 /** 125 * Sets the presenter selector. May not be null. 126 */ 127 public final void setPresenterSelector(PresenterSelector presenterSelector) { 128 if (presenterSelector == null) { 129 throw new IllegalArgumentException("Presenter selector must not be null"); 130 } 131 final boolean update = (mPresenterSelector != null); 132 final boolean selectorChanged = update && mPresenterSelector != presenterSelector; 133 134 mPresenterSelector = presenterSelector; 135 136 if (selectorChanged) { 137 onPresenterSelectorChanged(); 138 } 139 if (update) { 140 notifyChanged(); 141 } 142 } 143 144 /** 145 * Called when {@link #setPresenterSelector(PresenterSelector)} is called 146 * and the PresenterSelector differs from the previous one. 147 */ 148 protected void onPresenterSelectorChanged() { 149 } 150 151 /** 152 * Returns the presenter selector for this ObjectAdapter. 153 */ 154 public final PresenterSelector getPresenterSelector() { 155 return mPresenterSelector; 156 } 157 158 /** 159 * Registers a DataObserver for data change notifications. 160 */ 161 public final void registerObserver(DataObserver observer) { 162 mObservable.registerObserver(observer); 163 } 164 165 /** 166 * Unregisters a DataObserver for data change notifications. 167 */ 168 public final void unregisterObserver(DataObserver observer) { 169 mObservable.unregisterObserver(observer); 170 } 171 172 /** 173 * Unregisters all DataObservers for this ObjectAdapter. 174 */ 175 public final void unregisterAllObservers() { 176 mObservable.unregisterAll(); 177 } 178 179 final protected void notifyItemRangeChanged(int positionStart, int itemCount) { 180 mObservable.notifyItemRangeChanged(positionStart, itemCount); 181 } 182 183 final protected void notifyItemRangeInserted(int positionStart, int itemCount) { 184 mObservable.notifyItemRangeInserted(positionStart, itemCount); 185 } 186 187 final protected void notifyItemRangeRemoved(int positionStart, int itemCount) { 188 mObservable.notifyItemRangeRemoved(positionStart, itemCount); 189 } 190 191 final protected void notifyChanged() { 192 mObservable.notifyChanged(); 193 } 194 195 /** 196 * Returns true if the item ids are stable across changes to the 197 * underlying data. When this is true, clients of the ObjectAdapter can use 198 * {@link #getId(int)} to correlate Objects across changes. 199 */ 200 public final boolean hasStableIds() { 201 return mHasStableIds; 202 } 203 204 /** 205 * Sets whether the item ids are stable across changes to the underlying 206 * data. 207 */ 208 public final void setHasStableIds(boolean hasStableIds) { 209 boolean changed = mHasStableIds != hasStableIds; 210 mHasStableIds = hasStableIds; 211 212 if (changed) { 213 onHasStableIdsChanged(); 214 } 215 } 216 217 /** 218 * Called when {@link #setHasStableIds(boolean)} is called and the status 219 * of stable ids has changed. 220 */ 221 protected void onHasStableIdsChanged() { 222 } 223 224 /** 225 * Returns the {@link Presenter} for the given item from the adapter. 226 */ 227 public final Presenter getPresenter(Object item) { 228 if (mPresenterSelector == null) { 229 throw new IllegalStateException("Presenter selector must not be null"); 230 } 231 return mPresenterSelector.getPresenter(item); 232 } 233 234 /** 235 * Returns the number of items in the adapter. 236 */ 237 public abstract int size(); 238 239 /** 240 * Returns the item for the given position. 241 */ 242 public abstract Object get(int position); 243 244 /** 245 * Returns the id for the given position. 246 */ 247 public long getId(int position) { 248 return NO_ID; 249 } 250} 251