add OI File Manager and AndroidSupportV2 used by it
[android_pandora.git] / apps / AndroidSupportV2 / src / android / support / v2 / widget / CursorAdapter.java
diff --git a/apps/AndroidSupportV2/src/android/support/v2/widget/CursorAdapter.java b/apps/AndroidSupportV2/src/android/support/v2/widget/CursorAdapter.java
new file mode 100644 (file)
index 0000000..dd4bcfe
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * 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();
+        }
+    }
+
+}