+++ /dev/null
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v2.widget;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.DataSetObserver;
-import android.os.Handler;
-import android.util.Config;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.Filter;
-import android.widget.FilterQueryProvider;
-import android.widget.Filterable;
-
-/**
- * Static library support version of the framework's {@link android.widget.CursorAdapter}.
- * Used to write apps that run on platforms prior to Android 3.0. When running
- * on Android 3.0 or above, this implementation is still used; it does not try
- * to switch to the framework's implementation. See the framework SDK
- * documentation for a class overview.
- */
-public abstract class CursorAdapter extends BaseAdapter implements Filterable,
- CursorFilter.CursorFilterClient {
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected boolean mDataValid;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected boolean mAutoRequery;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected Cursor mCursor;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected Context mContext;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected int mRowIDColumn;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected ChangeObserver mChangeObserver;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected DataSetObserver mDataSetObserver;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected CursorFilter mCursorFilter;
- /**
- * This field should be made private, so it is hidden from the SDK.
- * {@hide}
- */
- protected FilterQueryProvider mFilterQueryProvider;
-
- /**
- * If set the adapter will call requery() on the cursor whenever a content change
- * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
- *
- * @deprecated This option is discouraged, as it results in Cursor queries
- * being performed on the application's UI thread and thus can cause poor
- * responsiveness or even Application Not Responding errors. As an alternative,
- * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
- */
- @Deprecated
- public static final int FLAG_AUTO_REQUERY = 0x01;
-
- /**
- * If set the adapter will register a content observer on the cursor and will call
- * {@link #onContentChanged()} when a notification comes in. Be careful when
- * using this flag: you will need to unset the current Cursor from the adapter
- * to avoid leaks due to its registered observers. This flag is not needed
- * when using a CursorAdapter with a
- * {@link android.content.CursorLoader}.
- */
- public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;
-
- /**
- * Constructor that always enables auto-requery.
- *
- * @deprecated This option is discouraged, as it results in Cursor queries
- * being performed on the application's UI thread and thus can cause poor
- * responsiveness or even Application Not Responding errors. As an alternative,
- * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
- *
- * @param c The cursor from which to get the data.
- * @param context The context
- */
- @Deprecated
- public CursorAdapter(Context context, Cursor c) {
- init(context, c, FLAG_AUTO_REQUERY);
- }
-
- /**
- * Constructor that allows control over auto-requery. It is recommended
- * you not use this, but instead {@link #CursorAdapter(Context, Cursor, int)}.
- * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
- * will always be set.
- *
- * @param c The cursor from which to get the data.
- * @param context The context
- * @param autoRequery If true the adapter will call requery() on the
- * cursor whenever it changes so the most recent
- * data is always displayed. Using true here is discouraged.
- */
- public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
- init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
- }
-
- /**
- * Recommended constructor.
- *
- * @param c The cursor from which to get the data.
- * @param context The context
- * @param flags Flags used to determine the behavior of the adapter; may
- * be any combination of {@link #FLAG_AUTO_REQUERY} and
- * {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
- */
- public CursorAdapter(Context context, Cursor c, int flags) {
- init(context, c, flags);
- }
-
- /**
- * @deprecated Don't use this, use the normal constructor. This will
- * be removed in the future.
- */
- @Deprecated
- protected void init(Context context, Cursor c, boolean autoRequery) {
- init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
- }
-
- void init(Context context, Cursor c, int flags) {
- if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
- flags |= FLAG_REGISTER_CONTENT_OBSERVER;
- mAutoRequery = true;
- } else {
- mAutoRequery = false;
- }
- boolean cursorPresent = c != null;
- mCursor = c;
- mDataValid = cursorPresent;
- mContext = context;
- mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
- if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
- mChangeObserver = new ChangeObserver();
- mDataSetObserver = new MyDataSetObserver();
- } else {
- mChangeObserver = null;
- mDataSetObserver = null;
- }
-
- if (cursorPresent) {
- if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
- if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
- }
- }
-
- /**
- * Returns the cursor.
- * @return the cursor.
- */
- public Cursor getCursor() {
- return mCursor;
- }
-
- /**
- * @see android.widget.ListAdapter#getCount()
- */
- public int getCount() {
- if (mDataValid && mCursor != null) {
- return mCursor.getCount();
- } else {
- return 0;
- }
- }
-
- /**
- * @see android.widget.ListAdapter#getItem(int)
- */
- public Object getItem(int position) {
- if (mDataValid && mCursor != null) {
- mCursor.moveToPosition(position);
- return mCursor;
- } else {
- return null;
- }
- }
-
- /**
- * @see android.widget.ListAdapter#getItemId(int)
- */
- public long getItemId(int position) {
- if (mDataValid && mCursor != null) {
- if (mCursor.moveToPosition(position)) {
- return mCursor.getLong(mRowIDColumn);
- } else {
- return 0;
- }
- } else {
- return 0;
- }
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- /**
- * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
- */
- public View getView(int position, View convertView, ViewGroup parent) {
- if (!mDataValid) {
- throw new IllegalStateException("this should only be called when the cursor is valid");
- }
- if (!mCursor.moveToPosition(position)) {
- throw new IllegalStateException("couldn't move cursor to position " + position);
- }
- View v;
- if (convertView == null) {
- v = newView(mContext, mCursor, parent);
- } else {
- v = convertView;
- }
- bindView(v, mContext, mCursor);
- return v;
- }
-
- @Override
- public View getDropDownView(int position, View convertView, ViewGroup parent) {
- if (mDataValid) {
- mCursor.moveToPosition(position);
- View v;
- if (convertView == null) {
- v = newDropDownView(mContext, mCursor, parent);
- } else {
- v = convertView;
- }
- bindView(v, mContext, mCursor);
- return v;
- } else {
- return null;
- }
- }
-
- /**
- * Makes a new view to hold the data pointed to by cursor.
- * @param context Interface to application's global information
- * @param cursor The cursor from which to get the data. The cursor is already
- * moved to the correct position.
- * @param parent The parent to which the new view is attached to
- * @return the newly created view.
- */
- public abstract View newView(Context context, Cursor cursor, ViewGroup parent);
-
- /**
- * Makes a new drop down view to hold the data pointed to by cursor.
- * @param context Interface to application's global information
- * @param cursor The cursor from which to get the data. The cursor is already
- * moved to the correct position.
- * @param parent The parent to which the new view is attached to
- * @return the newly created view.
- */
- public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
- return newView(context, cursor, parent);
- }
-
- /**
- * Bind an existing view to the data pointed to by cursor
- * @param view Existing view, returned earlier by newView
- * @param context Interface to application's global information
- * @param cursor The cursor from which to get the data. The cursor is already
- * moved to the correct position.
- */
- public abstract void bindView(View view, Context context, Cursor cursor);
-
- /**
- * Change the underlying cursor to a new cursor. If there is an existing cursor it will be
- * closed.
- *
- * @param cursor The new cursor to be used
- */
- public void changeCursor(Cursor cursor) {
- Cursor old = swapCursor(cursor);
- if (old != null) {
- old.close();
- }
- }
-
- /**
- * Swap in a new Cursor, returning the old Cursor. Unlike
- * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
- * closed.
- *
- * @param newCursor The new cursor to be used.
- * @return Returns the previously set Cursor, or null if there wasa not one.
- * If the given new Cursor is the same instance is the previously set
- * Cursor, null is also returned.
- */
- public Cursor swapCursor(Cursor newCursor) {
- if (newCursor == mCursor) {
- return null;
- }
- Cursor oldCursor = mCursor;
- if (oldCursor != null) {
- if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
- if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
- }
- mCursor = newCursor;
- if (newCursor != null) {
- if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
- if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
- mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
- mDataValid = true;
- // notify the observers about the new cursor
- notifyDataSetChanged();
- } else {
- mRowIDColumn = -1;
- mDataValid = false;
- // notify the observers about the lack of a data set
- notifyDataSetInvalidated();
- }
- return oldCursor;
- }
-
- /**
- * <p>Converts the cursor into a CharSequence. Subclasses should override this
- * method to convert their results. The default implementation returns an
- * empty String for null values or the default String representation of
- * the value.</p>
- *
- * @param cursor the cursor to convert to a CharSequence
- * @return a CharSequence representing the value
- */
- public CharSequence convertToString(Cursor cursor) {
- return cursor == null ? "" : cursor.toString();
- }
-
- /**
- * Runs a query with the specified constraint. This query is requested
- * by the filter attached to this adapter.
- *
- * The query is provided by a
- * {@link android.widget.FilterQueryProvider}.
- * If no provider is specified, the current cursor is not filtered and returned.
- *
- * After this method returns the resulting cursor is passed to {@link #changeCursor(Cursor)}
- * and the previous cursor is closed.
- *
- * This method is always executed on a background thread, not on the
- * application's main thread (or UI thread.)
- *
- * Contract: when constraint is null or empty, the original results,
- * prior to any filtering, must be returned.
- *
- * @param constraint the constraint with which the query must be filtered
- *
- * @return a Cursor representing the results of the new query
- *
- * @see #getFilter()
- * @see #getFilterQueryProvider()
- * @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
- */
- public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
- if (mFilterQueryProvider != null) {
- return mFilterQueryProvider.runQuery(constraint);
- }
-
- return mCursor;
- }
-
- public Filter getFilter() {
- if (mCursorFilter == null) {
- mCursorFilter = new CursorFilter(this);
- }
- return mCursorFilter;
- }
-
- /**
- * Returns the query filter provider used for filtering. When the
- * provider is null, no filtering occurs.
- *
- * @return the current filter query provider or null if it does not exist
- *
- * @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
- * @see #runQueryOnBackgroundThread(CharSequence)
- */
- public FilterQueryProvider getFilterQueryProvider() {
- return mFilterQueryProvider;
- }
-
- /**
- * Sets the query filter provider used to filter the current Cursor.
- * The provider's
- * {@link android.widget.FilterQueryProvider#runQuery(CharSequence)}
- * method is invoked when filtering is requested by a client of
- * this adapter.
- *
- * @param filterQueryProvider the filter query provider or null to remove it
- *
- * @see #getFilterQueryProvider()
- * @see #runQueryOnBackgroundThread(CharSequence)
- */
- public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {
- mFilterQueryProvider = filterQueryProvider;
- }
-
- /**
- * Called when the {@link ContentObserver} on the cursor receives a change notification.
- * The default implementation provides the auto-requery logic, but may be overridden by
- * sub classes.
- *
- * @see ContentObserver#onChange(boolean)
- */
- protected void onContentChanged() {
- if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
- if (Config.LOGV) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
- mDataValid = mCursor.requery();
- }
- }
-
- private class ChangeObserver extends ContentObserver {
- public ChangeObserver() {
- super(new Handler());
- }
-
- @Override
- public boolean deliverSelfNotifications() {
- return true;
- }
-
- @Override
- public void onChange(boolean selfChange) {
- onContentChanged();
- }
- }
-
- private class MyDataSetObserver extends DataSetObserver {
- @Override
- public void onChanged() {
- mDataValid = true;
- notifyDataSetChanged();
- }
-
- @Override
- public void onInvalidated() {
- mDataValid = false;
- notifyDataSetInvalidated();
- }
- }
-
-}