1 /* 2 * Copyright (C) 2012 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 com.example.android.apis.app; 18 19 import com.example.android.apis.app.LoaderCursor.CursorLoaderListFragment.MySearchView; 20 21 import android.app.Activity; 22 import android.app.FragmentManager; 23 import android.app.ListFragment; 24 import android.app.LoaderManager; 25 import android.content.Context; 26 import android.content.CursorLoader; 27 import android.content.Loader; 28 import android.database.Cursor; 29 import android.net.Uri; 30 import android.os.Bundle; 31 import android.provider.ContactsContract.Contacts; 32 import android.text.TextUtils; 33 import android.util.Log; 34 import android.view.Menu; 35 import android.view.MenuInflater; 36 import android.view.MenuItem; 37 import android.view.View; 38 import android.widget.ListView; 39 import android.widget.SearchView; 40 import android.widget.SimpleCursorAdapter; 41 import android.widget.SearchView.OnCloseListener; 42 import android.widget.SearchView.OnQueryTextListener; 43 44 /** 45 * Demonstration of the use of a CursorLoader to load and display contacts 46 * data in a fragment. 47 */ 48 public class LoaderRetained extends Activity { 49 50 @Override onCreate(Bundle savedInstanceState)51 protected void onCreate(Bundle savedInstanceState) { 52 super.onCreate(savedInstanceState); 53 54 FragmentManager fm = getFragmentManager(); 55 56 // Create the list fragment and add it as our sole content. 57 if (fm.findFragmentById(android.R.id.content) == null) { 58 CursorLoaderListFragment list = new CursorLoaderListFragment(); 59 fm.beginTransaction().add(android.R.id.content, list).commit(); 60 } 61 } 62 63 //BEGIN_INCLUDE(fragment_cursor) 64 public static class CursorLoaderListFragment extends ListFragment 65 implements OnQueryTextListener, OnCloseListener, 66 LoaderManager.LoaderCallbacks<Cursor> { 67 68 // This is the Adapter being used to display the list's data. 69 SimpleCursorAdapter mAdapter; 70 71 // The SearchView for doing filtering. 72 SearchView mSearchView; 73 74 // If non-null, this is the current filter the user has provided. 75 String mCurFilter; 76 onActivityCreated(Bundle savedInstanceState)77 @Override public void onActivityCreated(Bundle savedInstanceState) { 78 super.onActivityCreated(savedInstanceState); 79 80 // In this sample we are going to use a retained fragment. 81 setRetainInstance(true); 82 83 // Give some text to display if there is no data. In a real 84 // application this would come from a resource. 85 setEmptyText("No phone numbers"); 86 87 // We have a menu item to show in action bar. 88 setHasOptionsMenu(true); 89 90 // Create an empty adapter we will use to display the loaded data. 91 mAdapter = new SimpleCursorAdapter(getActivity(), 92 android.R.layout.simple_list_item_2, null, 93 new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, 94 new int[] { android.R.id.text1, android.R.id.text2 }, 0); 95 setListAdapter(mAdapter); 96 97 // Start out with a progress indicator. 98 setListShown(false); 99 100 // Prepare the loader. Either re-connect with an existing one, 101 // or start a new one. 102 getLoaderManager().initLoader(0, null, this); 103 } 104 105 public static class MySearchView extends SearchView { MySearchView(Context context)106 public MySearchView(Context context) { 107 super(context); 108 } 109 110 // The normal SearchView doesn't clear its search text when 111 // collapsed, so we will do this for it. 112 @Override onActionViewCollapsed()113 public void onActionViewCollapsed() { 114 setQuery("", false); 115 super.onActionViewCollapsed(); 116 } 117 } 118 onCreateOptionsMenu(Menu menu, MenuInflater inflater)119 @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 120 // Place an action bar item for searching. 121 MenuItem item = menu.add("Search"); 122 item.setIcon(android.R.drawable.ic_menu_search); 123 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM 124 | MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW); 125 mSearchView = new MySearchView(getActivity()); 126 mSearchView.setOnQueryTextListener(this); 127 mSearchView.setOnCloseListener(this); 128 mSearchView.setIconifiedByDefault(true); 129 item.setActionView(mSearchView); 130 } 131 onQueryTextChange(String newText)132 public boolean onQueryTextChange(String newText) { 133 // Called when the action bar search text has changed. Update 134 // the search filter, and restart the loader to do a new query 135 // with this filter. 136 String newFilter = !TextUtils.isEmpty(newText) ? newText : null; 137 // Don't do anything if the filter hasn't actually changed. 138 // Prevents restarting the loader when restoring state. 139 if (mCurFilter == null && newFilter == null) { 140 return true; 141 } 142 if (mCurFilter != null && mCurFilter.equals(newFilter)) { 143 return true; 144 } 145 mCurFilter = newFilter; 146 getLoaderManager().restartLoader(0, null, this); 147 return true; 148 } 149 onQueryTextSubmit(String query)150 @Override public boolean onQueryTextSubmit(String query) { 151 // Don't care about this. 152 return true; 153 } 154 155 @Override onClose()156 public boolean onClose() { 157 if (!TextUtils.isEmpty(mSearchView.getQuery())) { 158 mSearchView.setQuery(null, true); 159 } 160 return true; 161 } 162 onListItemClick(ListView l, View v, int position, long id)163 @Override public void onListItemClick(ListView l, View v, int position, long id) { 164 // Insert desired behavior here. 165 Log.i("FragmentComplexList", "Item clicked: " + id); 166 } 167 168 // These are the Contacts rows that we will retrieve. 169 static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { 170 Contacts._ID, 171 Contacts.DISPLAY_NAME, 172 Contacts.CONTACT_STATUS, 173 Contacts.CONTACT_PRESENCE, 174 Contacts.PHOTO_ID, 175 Contacts.LOOKUP_KEY, 176 }; 177 onCreateLoader(int id, Bundle args)178 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 179 // This is called when a new Loader needs to be created. This 180 // sample only has one Loader, so we don't care about the ID. 181 // First, pick the base URI to use depending on whether we are 182 // currently filtering. 183 Uri baseUri; 184 if (mCurFilter != null) { 185 baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 186 Uri.encode(mCurFilter)); 187 } else { 188 baseUri = Contacts.CONTENT_URI; 189 } 190 191 // Now create and return a CursorLoader that will take care of 192 // creating a Cursor for the data being displayed. 193 String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" 194 + Contacts.HAS_PHONE_NUMBER + "=1) AND (" 195 + Contacts.DISPLAY_NAME + " != '' ))"; 196 return new CursorLoader(getActivity(), baseUri, 197 CONTACTS_SUMMARY_PROJECTION, select, null, 198 Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); 199 } 200 onLoadFinished(Loader<Cursor> loader, Cursor data)201 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 202 // Swap the new cursor in. (The framework will take care of closing the 203 // old cursor once we return.) 204 mAdapter.swapCursor(data); 205 206 // The list should now be shown. 207 if (isResumed()) { 208 setListShown(true); 209 } else { 210 setListShownNoAnimation(true); 211 } 212 } 213 onLoaderReset(Loader<Cursor> loader)214 public void onLoaderReset(Loader<Cursor> loader) { 215 // This is called when the last Cursor provided to onLoadFinished() 216 // above is about to be closed. We need to make sure we are no 217 // longer using it. 218 mAdapter.swapCursor(null); 219 } 220 } 221 //END_INCLUDE(fragment_cursor) 222 } 223