publicclassLoaderCursorextendsActivity {@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
// Create the list fragment and add it as our sole content.if (fm.findFragmentById(android.R.id.content) == null) {
CursorLoaderListFragment list = new CursorLoaderListFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
publicstaticclassCursorLoaderListFragmentextendsListFragmentimplementsOnQueryTextListener, OnCloseListener,
LoaderManager.LoaderCallbacks<Cursor> {// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
// The SearchView for doing filtering.
SearchView mSearchView;
// If non-null, this is the current filter the user has provided.
String mCurFilter;
@OverridepublicvoidonActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Give some text to display if there is no data. In a real// application this would come from a resource.
setEmptyText("No phone numbers");
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_2, null,
new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
newint[] { android.R.id.text1, android.R.id.text2 }, 0);
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
// Prepare the loader. Either re-connect with an existing one,// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
publicstaticclassMySearchViewextendsSearchView {publicMySearchView(Context context) {
super(context);
}
// The normal SearchView doesn't clear its search text when// collapsed, so we will do this for it.@OverridepublicvoidonActionViewCollapsed() {
setQuery("", false);
super.onActionViewCollapsed();
}
}
@OverridepublicvoidonCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
mSearchView = new MySearchView(getActivity());
mSearchView.setOnQueryTextListener(this);
mSearchView.setOnCloseListener(this);
mSearchView.setIconifiedByDefault(true);
item.setActionView(mSearchView);
}
publicbooleanonQueryTextChange(String newText) {
// Called when the action bar search text has changed. Update// the search filter, and restart the loader to do a new query// with this filter.
String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
// Don't do anything if the filter hasn't actually changed.// Prevents restarting the loader when restoring state.if (mCurFilter == null && newFilter == null) {
returntrue;
}
if (mCurFilter != null && mCurFilter.equals(newFilter)) {
returntrue;
}
mCurFilter = newFilter;
getLoaderManager().restartLoader(0, null, this);
returntrue;
}
@OverridepublicbooleanonQueryTextSubmit(String query) {
// Don't care about this.returntrue;
}
@OverridepublicbooleanonClose() {
if (!TextUtils.isEmpty(mSearchView.getQuery())) {
mSearchView.setQuery(null, true);
}
returntrue;
}
@OverridepublicvoidonListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("FragmentComplexList", "Item clicked: " + id);
}
// These are the Contacts rows that we will retrieve.staticfinal String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS,
Contacts.CONTACT_PRESENCE,
Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY,
};
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This// sample only has one Loader, so we don't care about the ID.// First, pick the base URI to use depending on whether we are// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
// Now create and return a CursorLoader that will take care of// creating a Cursor for the data being displayed.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
returnnew CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
publicvoidonLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the// old cursor once we return.)
mAdapter.swapCursor(data);
// The list should now be shown.if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
publicvoidonLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()// above is about to be closed. We need to make sure we are no// longer using it.
mAdapter.swapCursor(null);
}
}
}
LoaderThrottle
publicclassLoaderThrottleextendsActivity {// Debugging.staticfinal String TAG = "LoaderThrottle";
/**
* The authority we use to get to our sample provider.
*/publicstaticfinal String AUTHORITY = "com.example.android.apis.app.LoaderThrottle";
/**
* Definition of the contract for the main table of our provider.
*/publicstaticfinalclassMainTableimplementsBaseColumns {// This class cannot be instantiatedprivateMainTable() {}
/**
* The table name offered by this provider
*/publicstaticfinal String TABLE_NAME = "main";
/**
* The content:// style URL for this table
*/publicstaticfinal Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/main");
/**
* The content URI base for a single row of data. Callers must
* append a numeric row id to this Uri to retrieve a row
*/publicstaticfinal Uri CONTENT_ID_URI_BASE
= Uri.parse("content://" + AUTHORITY + "/main/");
/**
* The MIME type of {@link #CONTENT_URI}.
*/publicstaticfinal String CONTENT_TYPE
= "vnd.android.cursor.dir/vnd.example.api-demos-throttle";
/**
* The MIME type of a {@link #CONTENT_URI} sub-directory of a single row.
*/publicstaticfinal String CONTENT_ITEM_TYPE
= "vnd.android.cursor.item/vnd.example.api-demos-throttle";
/**
* The default sort order for this table
*/publicstaticfinal String DEFAULT_SORT_ORDER = "data COLLATE LOCALIZED ASC";
/**
* Column name for the single column holding our data.
* <P>Type: TEXT</P>
*/publicstaticfinal String COLUMN_NAME_DATA = "data";
}
/**
* This class helps open, create, and upgrade the database file.
*/static class DatabaseHelper extends SQLiteOpenHelper {
privatestaticfinal String DATABASE_NAME = "loader_throttle.db";
privatestaticfinalint DATABASE_VERSION = 2;
DatabaseHelper(Context context) {
// calls the super constructor, requesting the default cursor factory.super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
*
* Creates the underlying database with table name and column names taken from the
* NotePad class.
*/@OverridepublicvoidonCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + MainTable.TABLE_NAME + " ("
+ MainTable._ID + " INTEGER PRIMARY KEY,"
+ MainTable.COLUMN_NAME_DATA + " TEXT"
+ ");");
}
/**
*
* Demonstrates that the provider must consider what happens when the
* underlying datastore is changed. In this sample, the database is upgraded the database
* by destroying the existing data.
* A real application should upgrade the database in place.
*/@OverridepublicvoidonUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Logs that the database is being upgraded
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
// Kills the table and existing data
db.execSQL("DROP TABLE IF EXISTS notes");
// Recreates the database with a new version
onCreate(db);
}
}
/**
* A very simple implementation of a content provider.
*/publicstaticclassSimpleProviderextendsContentProvider {// A projection map used to select columns from the databaseprivatefinal HashMap<String, String> mNotesProjectionMap;
// Uri matcher to decode incoming URIs.privatefinal UriMatcher mUriMatcher;
// The incoming URI matches the main table URI patternprivatestaticfinalint MAIN = 1;
// The incoming URI matches the main table row ID URI patternprivatestaticfinalint MAIN_ID = 2;
// Handle to a new DatabaseHelper.private DatabaseHelper mOpenHelper;
/**
* Global provider initialization.
*/publicSimpleProvider() {
// Create and initialize URI matcher.
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(AUTHORITY, MainTable.TABLE_NAME, MAIN);
mUriMatcher.addURI(AUTHORITY, MainTable.TABLE_NAME + "/#", MAIN_ID);
// Create and initialize projection map for all columns. This is// simply an identity mapping.
mNotesProjectionMap = new HashMap<String, String>();
mNotesProjectionMap.put(MainTable._ID, MainTable._ID);
mNotesProjectionMap.put(MainTable.COLUMN_NAME_DATA, MainTable.COLUMN_NAME_DATA);
}
/**
* Perform provider creation.
*/@OverridepublicbooleanonCreate() {
mOpenHelper = new DatabaseHelper(getContext());
// Assumes that any failures will be reported by a thrown exception.returntrue;
}
/**
* Handle incoming queries.
*/@Overridepublic Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// Constructs a new query builder and sets its table name
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(MainTable.TABLE_NAME);
switch (mUriMatcher.match(uri)) {
case MAIN:
// If the incoming URI is for main table.
qb.setProjectionMap(mNotesProjectionMap);
break;
case MAIN_ID:
// The incoming URI is for a single row.
qb.setProjectionMap(mNotesProjectionMap);
qb.appendWhere(MainTable._ID + "=?");
selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,
new String[] { uri.getLastPathSegment() });
break;
default:
thrownew IllegalArgumentException("Unknown URI " + uri);
}
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = MainTable.DEFAULT_SORT_ORDER;
}
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs,
null/* no group */, null/* no filter */, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
/**
* Return the MIME type for an known URI in the provider.
*/@Overridepublic String getType(Uri uri) {
switch (mUriMatcher.match(uri)) {
case MAIN:
return MainTable.CONTENT_TYPE;
case MAIN_ID:
return MainTable.CONTENT_ITEM_TYPE;
default:
thrownew IllegalArgumentException("Unknown URI " + uri);
}
}
/**
* Handler inserting new data.
*/@Overridepublic Uri insert(Uri uri, ContentValues initialValues) {
if (mUriMatcher.match(uri) != MAIN) {
// Can only insert into to main URI.thrownew IllegalArgumentException("Unknown URI " + uri);
}
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
if (values.containsKey(MainTable.COLUMN_NAME_DATA) == false) {
values.put(MainTable.COLUMN_NAME_DATA, "");
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(MainTable.TABLE_NAME, null, values);
// If the insert succeeded, the row ID exists.if (rowId > 0) {
Uri noteUri = ContentUris.withAppendedId(MainTable.CONTENT_ID_URI_BASE, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}
thrownew SQLException("Failed to insert row into " + uri);
}
/**
* Handle deleting data.
*/@Overridepublicintdelete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String finalWhere;
int count;
switch (mUriMatcher.match(uri)) {
case MAIN:
// If URI is main table, delete uses incoming where clause and args.
count = db.delete(MainTable.TABLE_NAME, where, whereArgs);
break;
// If the incoming URI matches a single note ID, does the delete based on the// incoming data, but modifies the where clause to restrict it to the// particular note ID.case MAIN_ID:
// If URI is for a particular row ID, delete is based on incoming// data but modified to restrict to the given ID.
finalWhere = DatabaseUtils.concatenateWhere(
MainTable._ID + " = " + ContentUris.parseId(uri), where);
count = db.delete(MainTable.TABLE_NAME, finalWhere, whereArgs);
break;
default:
thrownew IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
/**
* Handle updating data.
*/@Overridepublicintupdate(Uri uri, ContentValues values, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
String finalWhere;
switch (mUriMatcher.match(uri)) {
case MAIN:
// If URI is main table, update uses incoming where clause and args.
count = db.update(MainTable.TABLE_NAME, values, where, whereArgs);
break;
case MAIN_ID:
// If URI is for a particular row ID, update is based on incoming// data but modified to restrict to the given ID.
finalWhere = DatabaseUtils.concatenateWhere(
MainTable._ID + " = " + ContentUris.parseId(uri), where);
count = db.update(MainTable.TABLE_NAME, values, finalWhere, whereArgs);
break;
default:
thrownew IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
}
@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
// Create the list fragment and add it as our sole content.if (fm.findFragmentById(android.R.id.content) == null) {
ThrottledLoaderListFragment list = new ThrottledLoaderListFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
publicstaticclassThrottledLoaderListFragmentextendsListFragmentimplementsLoaderManager.LoaderCallbacks<Cursor> {// Menu identifiersstaticfinalint POPULATE_ID = Menu.FIRST;
staticfinalint CLEAR_ID = Menu.FIRST+1;
// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter;
// If non-null, this is the current filter the user has provided.
String mCurFilter;
// Task we have running to populate the database.
AsyncTask<Void, Void, Void> mPopulatingTask;
@OverridepublicvoidonActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText("No data. Select 'Populate' to fill with data from Z to A at a rate of 4 per second.");
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_1, null,
new String[] { MainTable.COLUMN_NAME_DATA },
newint[] { android.R.id.text1 }, 0);
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
// Prepare the loader. Either re-connect with an existing one,// or start a new one.
getLoaderManager().initLoader(0, null, this);
}
@OverridepublicvoidonCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(Menu.NONE, POPULATE_ID, 0, "Populate")
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(Menu.NONE, CLEAR_ID, 0, "Clear")
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
@OverridepublicbooleanonOptionsItemSelected(MenuItem item) {
final ContentResolver cr = getActivity().getContentResolver();
switch (item.getItemId()) {
case POPULATE_ID:
if (mPopulatingTask != null) {
mPopulatingTask.cancel(false);
}
mPopulatingTask = new AsyncTask<Void, Void, Void>() {
@Overrideprotected Void doInBackground(Void... params) {
for (char c='Z'; c>='A'; c--) {
if (isCancelled()) {
break;
}
StringBuilder builder = new StringBuilder("Data ");
builder.append(c);
ContentValues values = new ContentValues();
values.put(MainTable.COLUMN_NAME_DATA, builder.toString());
cr.insert(MainTable.CONTENT_URI, values);
// Wait a bit between each insert.try {
Thread.sleep(250);
} catch (InterruptedException e) {
}
}
returnnull;
}
};
mPopulatingTask.executeOnExecutor(
AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
returntrue;
case CLEAR_ID:
if (mPopulatingTask != null) {
mPopulatingTask.cancel(false);
mPopulatingTask = null;
}
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Overrideprotected Void doInBackground(Void... params) {
cr.delete(MainTable.CONTENT_URI, null, null);
returnnull;
}
};
task.execute((Void[])null);
returntrue;
default:
returnsuper.onOptionsItemSelected(item);
}
}
@OverridepublicvoidonListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i(TAG, "Item clicked: " + id);
}
// These are the rows that we will retrieve.staticfinal String[] PROJECTION = new String[] {
MainTable._ID,
MainTable.COLUMN_NAME_DATA,
};
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader cl = new CursorLoader(getActivity(), MainTable.CONTENT_URI,
PROJECTION, null, null, null);
cl.setUpdateThrottle(2000); // update at most every 2 seconds.return cl;
}
publicvoidonLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
// The list should now be shown.if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
publicvoidonLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
}