blob: 9a1726a9a7072305b411c75d5fed58f60ba4e3df [file] [log] [blame]
/*
* Copyright 2013 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 com.example.android.content.loadercursor;
import android.app.ListFragment;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.SearchView.OnCloseListener;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;
/**
* A {@link ListFragment} that shows the use of a {@link LoaderManager} to
* display a list of contacts accessed through a {@link Cursor}.
*/
public class CursorLoaderListFragment extends ListFragment implements
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;
@Override
public void onActivityCreated(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. The
* simple_list_item_2 layout contains two rows on top of each other
* (text1 and text2) that will show the contact's name and status.
*/
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_2, null,
new String[] {
Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS
},
new int[] {
android.R.id.text1, android.R.id.text2
}, 0);
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
// BEGIN_INCLUDE(getloader)
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
// END_INCLUDE(getloader)
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.main, menu);
// Get the search item and update its action view
MenuItem item = menu.findItem(R.id.action_search);
mSearchView = (SearchView) item.getActionView();
mSearchView.setOnQueryTextListener(queryListener);
mSearchView.setOnCloseListener(closeListener);
mSearchView.setIconifiedByDefault(true);
}
/**
* The {@link OnCloseListener} called when the SearchView is closed. Resets
* the query field.
*/
private SearchView.OnCloseListener closeListener = new SearchView.OnCloseListener() {
@Override
public boolean onClose() {
// Restore the SearchView if a query was entered
if (!TextUtils.isEmpty(mSearchView.getQuery())) {
mSearchView.setQuery(null, true);
}
return true;
}
};
/**
* The {@link OnQueryTextListener} that is called when text in the
* {@link SearchView} is changed. Updates the query filter and triggers a
* reload of the {@link LoaderManager} to update the displayed list.
*/
private OnQueryTextListener queryListener = new OnQueryTextListener() {
/**
* 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.
*
* @param newText the new content of the query text field
* @return true, the action has been handled.
*/
public boolean onQueryTextChange(String newText) {
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) {
return true;
}
if (mCurFilter != null && mCurFilter.equals(newFilter)) {
return true;
}
// Restart the Loader.
// #onCreateLoader uses the value of mCurFilter as a filter when
// creating the query for the Loader.
mCurFilter = newFilter;
getLoaderManager().restartLoader(0, null, CursorLoaderListFragment.this);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
// Don't care about this.
return true;
}
};
/**
* An item has been clicked in the {@link ListView}. Display a toast with
* the tapped item's id.
*/
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Toast.makeText(getActivity(), "Item clicked: " + id, Toast.LENGTH_LONG).show();
}
// These are the Contacts rows that we will retrieve.
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS,
Contacts.LOOKUP_KEY,
};
// BEGIN_INCLUDE(oncreateloader)
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 + " != '' ))";
String order = Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null, order);
}
// END_INCLUDE(oncreateloader)
// BEGIN_INCLUDE(onloadfinished)
public void onLoadFinished(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);
}
}
// END_INCLUDE(onloadfinished)
// BEGIN_INCLUDE(onloaderreset)
public void onLoaderReset(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);
}
// END_INCLUDE(onloaderreset)
}