switch to alsa.omap3 module
[android_pandora.git] / apps / AndroidSupportV2 / src / android / support / v2 / widget / CursorAdapter.java
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package android.support.v2.widget;
18
19 import android.content.Context;
20 import android.database.ContentObserver;
21 import android.database.Cursor;
22 import android.database.DataSetObserver;
23 import android.os.Handler;
24 import android.util.Config;
25 import android.util.Log;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.widget.BaseAdapter;
29 import android.widget.Filter;
30 import android.widget.FilterQueryProvider;
31 import android.widget.Filterable;
32
33 /**
34  * Static library support version of the framework's {@link android.widget.CursorAdapter}.
35  * Used to write apps that run on platforms prior to Android 3.0.  When running
36  * on Android 3.0 or above, this implementation is still used; it does not try
37  * to switch to the framework's implementation.  See the framework SDK
38  * documentation for a class overview.
39  */
40 public abstract class CursorAdapter extends BaseAdapter implements Filterable,
41         CursorFilter.CursorFilterClient {
42     /**
43      * This field should be made private, so it is hidden from the SDK.
44      * {@hide}
45      */
46     protected boolean mDataValid;
47     /**
48      * This field should be made private, so it is hidden from the SDK.
49      * {@hide}
50      */
51     protected boolean mAutoRequery;
52     /**
53      * This field should be made private, so it is hidden from the SDK.
54      * {@hide}
55      */
56     protected Cursor mCursor;
57     /**
58      * This field should be made private, so it is hidden from the SDK.
59      * {@hide}
60      */
61     protected Context mContext;
62     /**
63      * This field should be made private, so it is hidden from the SDK.
64      * {@hide}
65      */
66     protected int mRowIDColumn;
67     /**
68      * This field should be made private, so it is hidden from the SDK.
69      * {@hide}
70      */
71     protected ChangeObserver mChangeObserver;
72     /**
73      * This field should be made private, so it is hidden from the SDK.
74      * {@hide}
75      */
76     protected DataSetObserver mDataSetObserver;
77     /**
78      * This field should be made private, so it is hidden from the SDK.
79      * {@hide}
80      */
81     protected CursorFilter mCursorFilter;
82     /**
83      * This field should be made private, so it is hidden from the SDK.
84      * {@hide}
85      */
86     protected FilterQueryProvider mFilterQueryProvider;
87
88     /**
89      * If set the adapter will call requery() on the cursor whenever a content change
90      * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
91      *
92      * @deprecated This option is discouraged, as it results in Cursor queries
93      * being performed on the application's UI thread and thus can cause poor
94      * responsiveness or even Application Not Responding errors.  As an alternative,
95      * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
96      */
97     @Deprecated
98     public static final int FLAG_AUTO_REQUERY = 0x01;
99
100     /**
101      * If set the adapter will register a content observer on the cursor and will call
102      * {@link #onContentChanged()} when a notification comes in.  Be careful when
103      * using this flag: you will need to unset the current Cursor from the adapter
104      * to avoid leaks due to its registered observers.  This flag is not needed
105      * when using a CursorAdapter with a
106      * {@link android.content.CursorLoader}.
107      */
108     public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;
109
110     /**
111      * Constructor that always enables auto-requery.
112      *
113      * @deprecated This option is discouraged, as it results in Cursor queries
114      * being performed on the application's UI thread and thus can cause poor
115      * responsiveness or even Application Not Responding errors.  As an alternative,
116      * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
117      *
118      * @param c The cursor from which to get the data.
119      * @param context The context
120      */
121     @Deprecated
122     public CursorAdapter(Context context, Cursor c) {
123         init(context, c, FLAG_AUTO_REQUERY);
124     }
125
126     /**
127      * Constructor that allows control over auto-requery.  It is recommended
128      * you not use this, but instead {@link #CursorAdapter(Context, Cursor, int)}.
129      * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
130      * will always be set.
131      *
132      * @param c The cursor from which to get the data.
133      * @param context The context
134      * @param autoRequery If true the adapter will call requery() on the
135      *                    cursor whenever it changes so the most recent
136      *                    data is always displayed.  Using true here is discouraged.
137      */
138     public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
139         init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
140     }
141
142     /**
143      * Recommended constructor.
144      *
145      * @param c The cursor from which to get the data.
146      * @param context The context
147      * @param flags Flags used to determine the behavior of the adapter; may
148      * be any combination of {@link #FLAG_AUTO_REQUERY} and
149      * {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
150      */
151     public CursorAdapter(Context context, Cursor c, int flags) {
152         init(context, c, flags);
153     }
154
155     /**
156      * @deprecated Don't use this, use the normal constructor.  This will
157      * be removed in the future.
158      */
159     @Deprecated
160     protected void init(Context context, Cursor c, boolean autoRequery) {
161         init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
162     }
163
164     void init(Context context, Cursor c, int flags) {
165         if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
166             flags |= FLAG_REGISTER_CONTENT_OBSERVER;
167             mAutoRequery = true;
168         } else {
169             mAutoRequery = false;
170         }
171         boolean cursorPresent = c != null;
172         mCursor = c;
173         mDataValid = cursorPresent;
174         mContext = context;
175         mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
176         if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
177             mChangeObserver = new ChangeObserver();
178             mDataSetObserver = new MyDataSetObserver();
179         } else {
180             mChangeObserver = null;
181             mDataSetObserver = null;
182         }
183
184         if (cursorPresent) {
185             if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
186             if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
187         }
188     }
189
190     /**
191      * Returns the cursor.
192      * @return the cursor.
193      */
194     public Cursor getCursor() {
195         return mCursor;
196     }
197
198     /**
199      * @see android.widget.ListAdapter#getCount()
200      */
201     public int getCount() {
202         if (mDataValid && mCursor != null) {
203             return mCursor.getCount();
204         } else {
205             return 0;
206         }
207     }
208     
209     /**
210      * @see android.widget.ListAdapter#getItem(int)
211      */
212     public Object getItem(int position) {
213         if (mDataValid && mCursor != null) {
214             mCursor.moveToPosition(position);
215             return mCursor;
216         } else {
217             return null;
218         }
219     }
220
221     /**
222      * @see android.widget.ListAdapter#getItemId(int)
223      */
224     public long getItemId(int position) {
225         if (mDataValid && mCursor != null) {
226             if (mCursor.moveToPosition(position)) {
227                 return mCursor.getLong(mRowIDColumn);
228             } else {
229                 return 0;
230             }
231         } else {
232             return 0;
233         }
234     }
235     
236     @Override
237     public boolean hasStableIds() {
238         return true;
239     }
240
241     /**
242      * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
243      */
244     public View getView(int position, View convertView, ViewGroup parent) {
245         if (!mDataValid) {
246             throw new IllegalStateException("this should only be called when the cursor is valid");
247         }
248         if (!mCursor.moveToPosition(position)) {
249             throw new IllegalStateException("couldn't move cursor to position " + position);
250         }
251         View v;
252         if (convertView == null) {
253             v = newView(mContext, mCursor, parent);
254         } else {
255             v = convertView;
256         }
257         bindView(v, mContext, mCursor);
258         return v;
259     }
260
261     @Override
262     public View getDropDownView(int position, View convertView, ViewGroup parent) {
263         if (mDataValid) {
264             mCursor.moveToPosition(position);
265             View v;
266             if (convertView == null) {
267                 v = newDropDownView(mContext, mCursor, parent);
268             } else {
269                 v = convertView;
270             }
271             bindView(v, mContext, mCursor);
272             return v;
273         } else {
274             return null;
275         }
276     }
277     
278     /**
279      * Makes a new view to hold the data pointed to by cursor.
280      * @param context Interface to application's global information
281      * @param cursor The cursor from which to get the data. The cursor is already
282      * moved to the correct position.
283      * @param parent The parent to which the new view is attached to
284      * @return the newly created view.
285      */
286     public abstract View newView(Context context, Cursor cursor, ViewGroup parent);
287
288     /**
289      * Makes a new drop down view to hold the data pointed to by cursor.
290      * @param context Interface to application's global information
291      * @param cursor The cursor from which to get the data. The cursor is already
292      * moved to the correct position.
293      * @param parent The parent to which the new view is attached to
294      * @return the newly created view.
295      */
296     public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
297         return newView(context, cursor, parent);
298     }
299
300     /**
301      * Bind an existing view to the data pointed to by cursor
302      * @param view Existing view, returned earlier by newView
303      * @param context Interface to application's global information
304      * @param cursor The cursor from which to get the data. The cursor is already
305      * moved to the correct position.
306      */
307     public abstract void bindView(View view, Context context, Cursor cursor);
308     
309     /**
310      * Change the underlying cursor to a new cursor. If there is an existing cursor it will be
311      * closed.
312      * 
313      * @param cursor The new cursor to be used
314      */
315     public void changeCursor(Cursor cursor) {
316         Cursor old = swapCursor(cursor);
317         if (old != null) {
318             old.close();
319         }
320     }
321
322     /**
323      * Swap in a new Cursor, returning the old Cursor.  Unlike
324      * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
325      * closed.
326      *
327      * @param newCursor The new cursor to be used.
328      * @return Returns the previously set Cursor, or null if there wasa not one.
329      * If the given new Cursor is the same instance is the previously set
330      * Cursor, null is also returned.
331      */
332     public Cursor swapCursor(Cursor newCursor) {
333         if (newCursor == mCursor) {
334             return null;
335         }
336         Cursor oldCursor = mCursor;
337         if (oldCursor != null) {
338             if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
339             if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
340         }
341         mCursor = newCursor;
342         if (newCursor != null) {
343             if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
344             if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
345             mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
346             mDataValid = true;
347             // notify the observers about the new cursor
348             notifyDataSetChanged();
349         } else {
350             mRowIDColumn = -1;
351             mDataValid = false;
352             // notify the observers about the lack of a data set
353             notifyDataSetInvalidated();
354         }
355         return oldCursor;
356     }
357
358     /**
359      * <p>Converts the cursor into a CharSequence. Subclasses should override this
360      * method to convert their results. The default implementation returns an
361      * empty String for null values or the default String representation of
362      * the value.</p>
363      *
364      * @param cursor the cursor to convert to a CharSequence
365      * @return a CharSequence representing the value
366      */
367     public CharSequence convertToString(Cursor cursor) {
368         return cursor == null ? "" : cursor.toString();
369     }
370
371     /**
372      * Runs a query with the specified constraint. This query is requested
373      * by the filter attached to this adapter.
374      *
375      * The query is provided by a
376      * {@link android.widget.FilterQueryProvider}.
377      * If no provider is specified, the current cursor is not filtered and returned.
378      *
379      * After this method returns the resulting cursor is passed to {@link #changeCursor(Cursor)}
380      * and the previous cursor is closed.
381      *
382      * This method is always executed on a background thread, not on the
383      * application's main thread (or UI thread.)
384      * 
385      * Contract: when constraint is null or empty, the original results,
386      * prior to any filtering, must be returned.
387      *
388      * @param constraint the constraint with which the query must be filtered
389      *
390      * @return a Cursor representing the results of the new query
391      *
392      * @see #getFilter()
393      * @see #getFilterQueryProvider()
394      * @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
395      */
396     public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
397         if (mFilterQueryProvider != null) {
398             return mFilterQueryProvider.runQuery(constraint);
399         }
400
401         return mCursor;
402     }
403
404     public Filter getFilter() {
405         if (mCursorFilter == null) {
406             mCursorFilter = new CursorFilter(this);
407         }
408         return mCursorFilter;
409     }
410
411     /**
412      * Returns the query filter provider used for filtering. When the
413      * provider is null, no filtering occurs.
414      *
415      * @return the current filter query provider or null if it does not exist
416      *
417      * @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
418      * @see #runQueryOnBackgroundThread(CharSequence)
419      */
420     public FilterQueryProvider getFilterQueryProvider() {
421         return mFilterQueryProvider;
422     }
423
424     /**
425      * Sets the query filter provider used to filter the current Cursor.
426      * The provider's
427      * {@link android.widget.FilterQueryProvider#runQuery(CharSequence)}
428      * method is invoked when filtering is requested by a client of
429      * this adapter.
430      *
431      * @param filterQueryProvider the filter query provider or null to remove it
432      *
433      * @see #getFilterQueryProvider()
434      * @see #runQueryOnBackgroundThread(CharSequence)
435      */
436     public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {
437         mFilterQueryProvider = filterQueryProvider;
438     }
439
440     /**
441      * Called when the {@link ContentObserver} on the cursor receives a change notification.
442      * The default implementation provides the auto-requery logic, but may be overridden by
443      * sub classes.
444      * 
445      * @see ContentObserver#onChange(boolean)
446      */
447     protected void onContentChanged() {
448         if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
449             if (Config.LOGV) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
450             mDataValid = mCursor.requery();
451         }
452     }
453
454     private class ChangeObserver extends ContentObserver {
455         public ChangeObserver() {
456             super(new Handler());
457         }
458
459         @Override
460         public boolean deliverSelfNotifications() {
461             return true;
462         }
463
464         @Override
465         public void onChange(boolean selfChange) {
466             onContentChanged();
467         }
468     }
469
470     private class MyDataSetObserver extends DataSetObserver {
471         @Override
472         public void onChanged() {
473             mDataValid = true;
474             notifyDataSetChanged();
475         }
476
477         @Override
478         public void onInvalidated() {
479             mDataValid = false;
480             notifyDataSetInvalidated();
481         }
482     }
483
484 }