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