Home | History | Annotate | Download | only in widget
      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          */
     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