120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn/*
220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * Copyright (C) 2014 The Android Open Source Project
320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * in compliance with the License. You may obtain a copy of the License at
620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * http://www.apache.org/licenses/LICENSE-2.0
820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn *
920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * Unless required by applicable law or agreed to in writing, software distributed under the License
1020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
1120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * or implied. See the License for the specific language governing permissions and limitations under
1220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn * the License.
1320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn */
1420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbournpackage android.support.v17.leanback.widget;
1520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
1620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbournimport android.database.Cursor;
1720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbournimport android.support.v17.leanback.database.CursorMapper;
1820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbournimport android.util.LruCache;
1920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
2020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn/**
21a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout * An {@link ObjectAdapter} implemented with a {@link Cursor}.
2220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn */
2320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbournpublic class CursorObjectAdapter extends ObjectAdapter {
2420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    private static final int CACHE_SIZE = 100;
250f1fa0dfa946ddc8afb6af26a4dd1a4d01dca10fDake Gu    private Cursor mCursor;
260f1fa0dfa946ddc8afb6af26a4dd1a4d01dca10fDake Gu    private CursorMapper mMapper;
270f1fa0dfa946ddc8afb6af26a4dd1a4d01dca10fDake Gu    private final LruCache<Integer, Object> mItemCache = new LruCache<Integer, Object>(CACHE_SIZE);
2820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
29beeaa973d1b5bd79ee8ae798141231d9a315eba7Tim Kilbourn    /**
30a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Constructs an adapter with the given {@link PresenterSelector}.
31beeaa973d1b5bd79ee8ae798141231d9a315eba7Tim Kilbourn     */
3220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public CursorObjectAdapter(PresenterSelector presenterSelector) {
3320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        super(presenterSelector);
3420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
3520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
36beeaa973d1b5bd79ee8ae798141231d9a315eba7Tim Kilbourn    /**
37a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Constructs an adapter that uses the given {@link Presenter} for all items.
38beeaa973d1b5bd79ee8ae798141231d9a315eba7Tim Kilbourn     */
3920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public CursorObjectAdapter(Presenter presenter) {
4020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        super(presenter);
4120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
4220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
43beeaa973d1b5bd79ee8ae798141231d9a315eba7Tim Kilbourn    /**
44a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Constructs an adapter.
45beeaa973d1b5bd79ee8ae798141231d9a315eba7Tim Kilbourn     */
4620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public CursorObjectAdapter() {
4720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        super();
4820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
4920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
5020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
51a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Changes the underlying cursor to a new cursor. If there is
52beeaa973d1b5bd79ee8ae798141231d9a315eba7Tim Kilbourn     * an existing cursor it will be closed if it is different than the new
53beeaa973d1b5bd79ee8ae798141231d9a315eba7Tim Kilbourn     * cursor.
5420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     *
5520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * @param cursor The new cursor to be used.
5620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
5720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public void changeCursor(Cursor cursor) {
5820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        if (cursor == mCursor) {
5920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn            return;
6020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        }
612eaaad9c189f1ae42bf6c382a30ccab455030e54Andrew Wilson        if (mCursor != null) {
622eaaad9c189f1ae42bf6c382a30ccab455030e54Andrew Wilson            mCursor.close();
632eaaad9c189f1ae42bf6c382a30ccab455030e54Andrew Wilson        }
6420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        mCursor = cursor;
6520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        mItemCache.trimToSize(0);
6620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        onCursorChanged();
6720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
6820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
6920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
7020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * Swap in a new Cursor, returning the old Cursor. Unlike changeCursor(Cursor),
7120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * the returned old Cursor is not closed.
7220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     *
7320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * @param cursor The new cursor to be used.
7420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
7520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public Cursor swapCursor(Cursor cursor) {
7620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        if (cursor == mCursor) {
7720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn            return mCursor;
7820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        }
7920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        Cursor oldCursor = mCursor;
8020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        mCursor = cursor;
8120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        mItemCache.trimToSize(0);
8220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        onCursorChanged();
8320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        return oldCursor;
8420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
8520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
8620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
8720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * Called whenever the cursor changes.
8820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
8920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    protected void onCursorChanged() {
9020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        notifyChanged();
9120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
9220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
9320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
94a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Returns the {@link Cursor} backing the adapter.
9520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
960f1fa0dfa946ddc8afb6af26a4dd1a4d01dca10fDake Gu     public final Cursor getCursor() {
9720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        return mCursor;
9820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
9920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
10020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
10120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * Sets the {@link CursorMapper} used to convert {@link Cursor} rows into
10220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * Objects.
10320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
1040f1fa0dfa946ddc8afb6af26a4dd1a4d01dca10fDake Gu    public final void setMapper(CursorMapper mapper) {
105b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson        boolean changed = mMapper != mapper;
10620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        mMapper = mapper;
107b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson
108b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson        if (changed) {
109b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson            onMapperChanged();
110b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson        }
111b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson    }
112b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson
113b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson    /**
114b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson     * Called when {@link #setMapper(CursorMapper)} is called and a different
115b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson     * mapper is provided.
116b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson     */
117b3451baf39c3b17972e7826baee90be4b1cd2626Justin Mattson    protected void onMapperChanged() {
11820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
11920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
12020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
121a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Returns the {@link CursorMapper} used to convert {@link Cursor} rows into
12220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * Objects.
12320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
1240f1fa0dfa946ddc8afb6af26a4dd1a4d01dca10fDake Gu    public final CursorMapper getMapper() {
12520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        return mMapper;
12620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
12720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
12820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    @Override
12920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public int size() {
13020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        if (mCursor == null) {
13120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn            return 0;
13220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        }
13320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        return mCursor.getCount();
13420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
13520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
13620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    @Override
13720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public Object get(int index) {
13820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        if (mCursor == null) {
13920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn            return null;
14020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        }
14120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        if (!mCursor.moveToPosition(index)) {
14220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn            throw new ArrayIndexOutOfBoundsException();
14320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        }
14420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        Object item = mItemCache.get(index);
14520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        if (item != null) {
14620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn            return item;
14720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        }
14820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        item = mMapper.convert(mCursor);
14920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        mItemCache.put(index, item);
15020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        return item;
15120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
15220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
15320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
15420c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     * Closes this adapter, closing the backing {@link Cursor} as well.
15520c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
15620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public void close() {
15720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        if (mCursor != null) {
15820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn            mCursor.close();
15920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn            mCursor = null;
16020c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        }
16120c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
16220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn
16320c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    /**
164a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Returns true if the adapter, and hence the backing {@link Cursor}, is closed; false
165a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * otherwise.
16620c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn     */
16720c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    public boolean isClosed() {
16820c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn        return mCursor == null || mCursor.isClosed();
16920c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn    }
170c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson
171c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson    /**
172a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Removes an item from the cache. This will force the item to be re-read
173377357a8c26c8c54ba8cb876ae775265635a8448Elliot Waite     * from the data source the next time {@link #get(int)} is called.
174c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson     */
175c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson    protected final void invalidateCache(int index) {
176c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson        mItemCache.remove(index);
177c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson    }
178c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson
179c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson    /**
180a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout     * Removes {@code count} items starting at {@code index}.
181c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson     */
182c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson    protected final void invalidateCache(int index, int count) {
183c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson        for (int limit = count + index; index < limit; index++) {
184c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson            invalidateCache(index);
185c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson        }
186c251a00602f6872d07e5b464ce7c67e78696438eJustin Mattson    }
1870674181fa96779197f965dd4efc47bfd90cd085esusnata
1880674181fa96779197f965dd4efc47bfd90cd085esusnata    @Override
1890674181fa96779197f965dd4efc47bfd90cd085esusnata    public boolean isImmediateNotifySupported() {
1900674181fa96779197f965dd4efc47bfd90cd085esusnata        return true;
1910674181fa96779197f965dd4efc47bfd90cd085esusnata    }
19220c094c196271089a7119a965b6a99786ea9ed36Tim Kilbourn}
193