Update the Notepad sample app to:
1. Show that any application state should be saved in onStop() versus
   onPause().
2. Remove the deprecated methods and start using CursorLoader instead
   of any managedQuery.
3. Remove deprecated LiveFolders usage.

Change-Id: I65be6b91ee1d4d0980494fb8455ce0906691dc21
diff --git a/samples/NotePad/AndroidManifest.xml b/samples/NotePad/AndroidManifest.xml
index ead7829..51e848d 100644
--- a/samples/NotePad/AndroidManifest.xml
+++ b/samples/NotePad/AndroidManifest.xml
@@ -107,14 +107,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="NotesLiveFolder" android:label="@string/live_folder_name"
-            android:icon="@drawable/live_folder_notes">
-            <intent-filter>
-                <action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
     </application>
 
 </manifest>
diff --git a/samples/NotePad/res/values/strings.xml b/samples/NotePad/res/values/strings.xml
index 26d23d0..508fa43 100644
--- a/samples/NotePad/res/values/strings.xml
+++ b/samples/NotePad/res/values/strings.xml
@@ -40,4 +40,5 @@
     <string name="error_title">Error</string>
     <string name="error_message">Error loading note</string>
     <string name="nothing_to_save">There is nothing to save</string>
+    <string name="title_blank">Blank title not saved</string>
 </resources>
\ No newline at end of file
diff --git a/samples/NotePad/src/com/example/android/notepad/NoteEditor.java b/samples/NotePad/src/com/example/android/notepad/NoteEditor.java
index 59d6f12..b8b070f 100644
--- a/samples/NotePad/src/com/example/android/notepad/NoteEditor.java
+++ b/samples/NotePad/src/com/example/android/notepad/NoteEditor.java
@@ -17,13 +17,16 @@
 package com.example.android.notepad;
 
 import android.app.Activity;
+import android.app.LoaderManager;
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.CursorLoader;
 import android.content.Intent;
+import android.content.Loader;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Canvas;
@@ -37,19 +40,15 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.widget.EditText;
+import com.example.android.notepad.NotePad.Notes;
 
 /**
  * This Activity handles "editing" a note, where editing is responding to
  * {@link Intent#ACTION_VIEW} (request to view data), edit a note
  * {@link Intent#ACTION_EDIT}, create a note {@link Intent#ACTION_INSERT}, or
  * create a new note from the current contents of the clipboard {@link Intent#ACTION_PASTE}.
- *
- * NOTE: Notice that the provider operations in this Activity are taking place on the UI thread.
- * This is not a good practice. It is only done here to make the code more readable. A real
- * application should use the {@link android.content.AsyncQueryHandler}
- * or {@link android.os.AsyncTask} object to perform operations asynchronously on a separate thread.
  */
-public class NoteEditor extends Activity {
+public class NoteEditor extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {
     // For logging and debugging purposes
     private static final String TAG = "NoteEditor";
 
@@ -71,10 +70,11 @@
     private static final int STATE_EDIT = 0;
     private static final int STATE_INSERT = 1;
 
+    private static final int LOADER_ID = 1;
+
     // Global mutable variables
     private int mState;
     private Uri mUri;
-    private Cursor mCursor;
     private EditText mText;
     private String mOriginalContent;
 
@@ -139,6 +139,11 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        // Recovering the instance state from a previously destroyed Activity instance
+        if (savedInstanceState != null) {
+            mOriginalContent = savedInstanceState.getString(ORIGINAL_CONTENT);
+        }
+
         /*
          * Creates an Intent to use when the Activity object's result is sent back to the
          * caller.
@@ -166,6 +171,8 @@
             // Sets the Activity state to INSERT, gets the general note URI, and inserts an
             // empty record in the provider
             mState = STATE_INSERT;
+            setTitle(getText(R.string.title_create));
+
             mUri = getContentResolver().insert(intent.getData(), null);
 
             /*
@@ -197,24 +204,10 @@
             return;
         }
 
-        /*
-         * Using the URI passed in with the triggering Intent, gets the note or notes in
-         * the provider.
-         * Note: This is being done on the UI thread. It will block the thread until the query
-         * completes. In a sample app, going against a simple provider based on a local database,
-         * the block will be momentary, but in a real app you should use
-         * android.content.AsyncQueryHandler or android.os.AsyncTask.
-         */
-        mCursor = managedQuery(
-            mUri,         // The URI that gets multiple notes from the provider.
-            PROJECTION,   // A projection that returns the note ID and note content for each note.
-            null,         // No "where" clause selection criteria.
-            null,         // No "where" clause selection values.
-            null          // Use the default sort order (modification date, descending)
-        );
+        // Initialize the LoaderManager and start the query
+        getLoaderManager().initLoader(LOADER_ID, null, this);
 
         // For a paste, initializes the data from clipboard.
-        // (Must be done after mCursor is initialized.)
         if (Intent.ACTION_PASTE.equals(action)) {
             // Does the paste
             performPaste();
@@ -227,87 +220,12 @@
 
         // Gets a handle to the EditText in the the layout.
         mText = (EditText) findViewById(R.id.note);
-
-        /*
-         * If this Activity had stopped previously, its state was written the ORIGINAL_CONTENT
-         * location in the saved Instance state. This gets the state.
-         */
-        if (savedInstanceState != null) {
-            mOriginalContent = savedInstanceState.getString(ORIGINAL_CONTENT);
-        }
     }
 
-    /**
-     * This method is called when the Activity is about to come to the foreground. This happens
-     * when the Activity comes to the top of the task stack, OR when it is first starting.
-     *
-     * Moves to the first note in the list, sets an appropriate title for the action chosen by
-     * the user, puts the note contents into the TextView, and saves the original text as a
-     * backup.
-     */
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        /*
-         * mCursor is initialized, since onCreate() always precedes onResume for any running
-         * process. This tests that it's not null, since it should always contain data.
-         */
-        if (mCursor != null) {
-            // Requery in case something changed while paused (such as the title)
-            mCursor.requery();
-
-            /* Moves to the first record. Always call moveToFirst() before accessing data in
-             * a Cursor for the first time. The semantics of using a Cursor are that when it is
-             * created, its internal index is pointing to a "place" immediately before the first
-             * record.
-             */
-            mCursor.moveToFirst();
-
-            // Modifies the window title for the Activity according to the current Activity state.
-            if (mState == STATE_EDIT) {
-                // Set the title of the Activity to include the note title
-                int colTitleIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE);
-                String title = mCursor.getString(colTitleIndex);
-                Resources res = getResources();
-                String text = String.format(res.getString(R.string.title_edit), title);
-                setTitle(text);
-            // Sets the title to "create" for inserts
-            } else if (mState == STATE_INSERT) {
-                setTitle(getText(R.string.title_create));
-            }
-
-            /*
-             * onResume() may have been called after the Activity lost focus (was paused).
-             * The user was either editing or creating a note when the Activity paused.
-             * The Activity should re-display the text that had been retrieved previously, but
-             * it should not move the cursor. This helps the user to continue editing or entering.
-             */
-
-            // Gets the note text from the Cursor and puts it in the TextView, but doesn't change
-            // the text cursor's position.
-            int colNoteIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE);
-            String note = mCursor.getString(colNoteIndex);
-            mText.setTextKeepState(note);
-
-            // Stores the original note text, to allow the user to revert changes.
-            if (mOriginalContent == null) {
-                mOriginalContent = note;
-            }
-
-        /*
-         * Something is wrong. The Cursor should always contain data. Report an error in the
-         * note.
-         */
-        } else {
-            setTitle(getText(R.string.error_title));
-            mText.setText(getText(R.string.error_message));
-        }
-    }
 
     /**
-     * This method is called when an Activity loses focus during its normal operation, and is then
-     * later on killed. The Activity has a chance to save its state so that the system can restore
+     * This method is called when an Activity loses focus during its normal operation.
+     * The Activity has a chance to save its state so that the system can restore
      * it.
      *
      * Notice that this method isn't a normal part of the Activity lifecycle. It won't be called
@@ -316,37 +234,52 @@
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         // Save away the original text, so we still have it if the activity
-        // needs to be killed while paused.
+        // needs to be re-created.
         outState.putString(ORIGINAL_CONTENT, mOriginalContent);
+        // Call the superclass to save the any view hierarchy state
+        super.onSaveInstanceState(outState);
     }
 
     /**
      * This method is called when the Activity loses focus.
      *
-     * For Activity objects that edit information, onPause() may be the one place where changes are
-     * saved. The Android application model is predicated on the idea that "save" and "exit" aren't
-     * required actions. When users navigate away from an Activity, they shouldn't have to go back
-     * to it to complete their work. The act of going away should save everything and leave the
-     * Activity in a state where Android can destroy it if necessary.
+     * While there is no need to override this method in this app, it is shown here to highlight
+     * that we are not saving any state in onPause, but have moved app state saving to onStop
+     * callback.
+     * In earlier versions of this app and popular literature it had been shown that onPause is good
+     * place to persist any unsaved work, however, this is not really a good practice because of how
+     * application and process lifecycle behave.
+     * As a general guideline apps should have a way of saving their business logic that does not
+     * solely rely on Activity (or other component) lifecyle state transitions.
+     * As a backstop you should save any app state, not saved during lifetime of the Activity, in
+     * onStop().
+     * For a more detailed explanation of this recommendation please read
+     * <a href = "https://developer.android.com/guide/topics/processes/process-lifecycle.html">
+     * Processes and Application Life Cycle </a>.
+     * <a href="https://developer.android.com/training/basics/activity-lifecycle/pausing.html">
+     * Pausing and Resuming an Activity </a>.
+     */
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    /**
+     * This method is called when the Activity becomes invisible.
+     *
+     * For Activity objects that edit information, onStop() may be the one place where changes maybe
+     * saved.
      *
      * If the user hasn't done anything, then this deletes or clears out the note, otherwise it
      * writes the user's work to the provider.
      */
     @Override
-    protected void onPause() {
-        super.onPause();
+    protected void onStop() {
+        super.onStop();
 
-        /*
-         * Tests to see that the query operation didn't fail (see onCreate()). The Cursor object
-         * will exist, even if no records were returned, unless the query failed because of some
-         * exception or error.
-         *
-         */
-        if (mCursor != null) {
-
-            // Get the current note text.
-            String text = mText.getText().toString();
-            int length = text.length();
+        // Get the current note text.
+        String text = mText.getText().toString();
+        int length = text.length();
 
             /*
              * If the Activity is in the midst of finishing and there is no text in the current
@@ -354,23 +287,22 @@
              * even if the note was being edited, the assumption being that the user wanted to
              * "clear out" (delete) the note.
              */
-            if (isFinishing() && (length == 0)) {
-                setResult(RESULT_CANCELED);
-                deleteNote();
+        if (isFinishing() && (length == 0)) {
+            setResult(RESULT_CANCELED);
+            deleteNote();
 
                 /*
-                 * Writes the edits to the provider. The note has been edited if an existing note was
-                 * retrieved into the editor *or* if a new note was inserted. In the latter case,
-                 * onCreate() inserted a new empty note into the provider, and it is this new note
-                 * that is being edited.
+                 * Writes the edits to the provider. The note has been edited if an existing note
+                 * was retrieved into the editor *or* if a new note was inserted.
+                 * In the latter case, onCreate() inserted a new empty note into the provider,
+                 * and it is this new note that is being edited.
                  */
-            } else if (mState == STATE_EDIT) {
-                // Creates a map to contain the new values for the columns
-                updateNote(text, null);
-            } else if (mState == STATE_INSERT) {
-                updateNote(text, text);
-                mState = STATE_EDIT;
-          }
+        } else if (mState == STATE_EDIT) {
+            // Creates a map to contain the new values for the columns
+            updateNote(text, null);
+        } else if (mState == STATE_INSERT) {
+            updateNote(text, text);
+            mState = STATE_EDIT;
         }
     }
 
@@ -409,8 +341,16 @@
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         // Check if note has changed and enable/disable the revert option
-        int colNoteIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE);
-        String savedNote = mCursor.getString(colNoteIndex);
+        Cursor cursor = getContentResolver().query(
+            mUri,        // The URI for the note that is to be retrieved.
+            PROJECTION,  // The columns to retrieve
+            null,        // No selection criteria are used, so no where columns are needed.
+            null,        // No where columns are used, so no where values are needed.
+            null         // No sort order is needed.
+        );
+        cursor.moveToFirst();
+        int colNoteIndex = cursor.getColumnIndex(Notes.COLUMN_NAME_NOTE);
+        String savedNote = cursor.getString(colNoteIndex);
         String currentNote = mText.getText().toString();
         if (savedNote.equals(currentNote)) {
             menu.findItem(R.id.menu_revert).setVisible(false);
@@ -493,8 +433,8 @@
                 // (moveToFirst() returns true), then this gets the note data from it.
                 if (orig != null) {
                     if (orig.moveToFirst()) {
-                        int colNoteIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE);
-                        int colTitleIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE);
+                        int colNoteIndex = orig.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE);
+                        int colTitleIndex = orig.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE);
                         text = orig.getString(colNoteIndex);
                         title = orig.getString(colTitleIndex);
                     }
@@ -571,13 +511,11 @@
          * android.content.AsyncQueryHandler or android.os.AsyncTask.
          */
         getContentResolver().update(
-                mUri,    // The URI for the record to update.
-                values,  // The map of column names and new values to apply to them.
-                null,    // No selection criteria are used, so no where columns are necessary.
-                null     // No where columns are used, so no where arguments are necessary.
-            );
-
-
+            mUri,    // The URI for the record to update.
+            values,  // The map of column names and new values to apply to them.
+            null,    // No selection criteria are used, so no where columns are necessary.
+            null     // No where columns are used, so no where arguments are necessary.
+        );
     }
 
     /**
@@ -585,19 +523,17 @@
      * newly created, or reverts to the original text of the note i
      */
     private final void cancelNote() {
-        if (mCursor != null) {
-            if (mState == STATE_EDIT) {
-                // Put the original note text back into the database
-                mCursor.close();
-                mCursor = null;
-                ContentValues values = new ContentValues();
-                values.put(NotePad.Notes.COLUMN_NAME_NOTE, mOriginalContent);
-                getContentResolver().update(mUri, values, null, null);
-            } else if (mState == STATE_INSERT) {
-                // We inserted an empty note, make sure to delete it
-                deleteNote();
-            }
+
+        if (mState == STATE_EDIT) {
+            // Put the original note text back into the database
+            ContentValues values = new ContentValues();
+            values.put(NotePad.Notes.COLUMN_NAME_NOTE, mOriginalContent);
+            getContentResolver().update(mUri, values, null, null);
+        } else if (mState == STATE_INSERT) {
+            // We inserted an empty note, make sure to delete it
+            deleteNote();
         }
+
         setResult(RESULT_CANCELED);
         finish();
     }
@@ -606,11 +542,50 @@
      * Take care of deleting a note.  Simply deletes the entry.
      */
     private final void deleteNote() {
-        if (mCursor != null) {
-            mCursor.close();
-            mCursor = null;
-            getContentResolver().delete(mUri, null, null);
-            mText.setText("");
+        getContentResolver().delete(mUri, null, null);
+        mText.setText("");
+    }
+
+    // LoaderManager callbacks
+    @Override
+    public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
+        return new CursorLoader(
+            this,
+            mUri,        // The URI for the note that is to be retrieved.
+            PROJECTION,  // The columns to retrieve
+            null,        // No selection criteria are used, so no where columns are needed.
+            null,        // No where columns are used, so no where values are needed.
+            null         // No sort order is needed.
+        );
+    }
+
+    @Override
+    public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
+
+        // Modifies the window title for the Activity according to the current Activity state.
+        if (cursor != null && cursor.moveToFirst() && mState == STATE_EDIT) {
+            // Set the title of the Activity to include the note title
+            int colTitleIndex = cursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE);
+            int colNoteIndex = cursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE);
+
+            // Gets the title and sets it
+            String title = cursor.getString(colTitleIndex);
+            Resources res = getResources();
+            String text = String.format(res.getString(R.string.title_edit), title);
+            setTitle(text);
+
+            // Gets the note text from the Cursor and puts it in the TextView, but doesn't change
+            // the text cursor's position.
+
+            String note = cursor.getString(colNoteIndex);
+            mText.setTextKeepState(note);
+            // Stores the original note text, to allow the user to revert changes.
+            if (mOriginalContent == null) {
+                mOriginalContent = note;
+            }
         }
     }
+
+    @Override
+    public void onLoaderReset(Loader<Cursor> cursorLoader) {}
 }
diff --git a/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java b/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
index 1839645..f81e22d 100644
--- a/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
+++ b/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
@@ -71,11 +71,6 @@
     private static HashMap<String, String> sNotesProjectionMap;
 
     /**
-     * A projection map used to select columns from the database
-     */
-    private static HashMap<String, String> sLiveFolderProjectionMap;
-
-    /**
      * Standard projection for the interesting columns of a normal note.
      */
     private static final String[] READ_NOTE_PROJECTION = new String[] {
@@ -96,9 +91,6 @@
     // The incoming URI matches the Note ID URI pattern
     private static final int NOTE_ID = 2;
 
-    // The incoming URI matches the Live Folder URI pattern
-    private static final int LIVE_FOLDER_NOTES = 3;
-
     /**
      * A UriMatcher instance
      */
@@ -126,10 +118,6 @@
         // to a note ID operation
         sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID);
 
-        // Add a pattern that routes URIs terminated with live_folders/notes to a
-        // live folder operation
-        sUriMatcher.addURI(NotePad.AUTHORITY, "live_folders/notes", LIVE_FOLDER_NOTES);
-
         /*
          * Creates and initializes a projection map that returns all columns
          */
@@ -155,20 +143,6 @@
         sNotesProjectionMap.put(
                 NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE,
                 NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE);
-
-        /*
-         * Creates an initializes a projection map for handling Live Folders
-         */
-
-        // Creates a new projection map instance
-        sLiveFolderProjectionMap = new HashMap<String, String>();
-
-        // Maps "_ID" to "_ID AS _ID" for a live folder
-        sLiveFolderProjectionMap.put(LiveFolders._ID, NotePad.Notes._ID + " AS " + LiveFolders._ID);
-
-        // Maps "NAME" to "title AS NAME"
-        sLiveFolderProjectionMap.put(LiveFolders.NAME, NotePad.Notes.COLUMN_NAME_TITLE + " AS " +
-            LiveFolders.NAME);
     }
 
     /**
@@ -278,11 +252,6 @@
                    uri.getPathSegments().get(NotePad.Notes.NOTE_ID_PATH_POSITION));
                break;
 
-           case LIVE_FOLDER_NOTES:
-               // If the incoming URI is from a live folder, chooses the live folder projection.
-               qb.setProjectionMap(sLiveFolderProjectionMap);
-               break;
-
            default:
                // If the URI doesn't match any of the known patterns, throw an exception.
                throw new IllegalArgumentException("Unknown URI " + uri);
@@ -339,7 +308,6 @@
 
            // If the pattern is for notes or live folders, returns the general content type.
            case NOTES:
-           case LIVE_FOLDER_NOTES:
                return NotePad.Notes.CONTENT_TYPE;
 
            // If the pattern is for note IDs, returns the note ID content type.
@@ -380,7 +348,6 @@
             // If the pattern is for notes or live folders, return null. Data streams are not
             // supported for this type of URI.
             case NOTES:
-            case LIVE_FOLDER_NOTES:
                 return null;
 
             // If the pattern is for note IDs and the MIME filter is text/plain, then return
@@ -635,7 +602,7 @@
                 throw new IllegalArgumentException("Unknown URI " + uri);
         }
 
-        /*Gets a handle to the content resolver object for the current context, and notifies it
+        /* Gets a handle to the content resolver object for the current context, and notifies it
          * that the incoming URI changed. The object passes this along to the resolver framework,
          * and observers that have registered themselves for the provider are notified.
          */
@@ -728,7 +695,7 @@
                 throw new IllegalArgumentException("Unknown URI " + uri);
         }
 
-        /*Gets a handle to the content resolver object for the current context, and notifies it
+        /* Gets a handle to the content resolver object for the current context, and notifies it
          * that the incoming URI changed. The object passes this along to the resolver framework,
          * and observers that have registered themselves for the provider are notified.
          */
diff --git a/samples/NotePad/src/com/example/android/notepad/NotesList.java b/samples/NotePad/src/com/example/android/notepad/NotesList.java
index 7e91f64..bc21a70 100644
--- a/samples/NotePad/src/com/example/android/notepad/NotesList.java
+++ b/samples/NotePad/src/com/example/android/notepad/NotesList.java
@@ -16,15 +16,16 @@
 
 package com.example.android.notepad;
 
-import com.example.android.notepad.NotePad;
-
 import android.app.ListActivity;
+import android.app.LoaderManager;
 import android.content.ClipboardManager;
 import android.content.ClipData;
 import android.content.ComponentName;
 import android.content.ContentUris;
 import android.content.Context;
+import android.content.CursorLoader;
 import android.content.Intent;
+import android.content.Loader;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
@@ -36,6 +37,7 @@
 import android.view.View;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.widget.AdapterView;
+import android.widget.CursorAdapter;
 import android.widget.ListView;
 import android.widget.SimpleCursorAdapter;
 
@@ -43,17 +45,14 @@
  * Displays a list of notes. Will display notes from the {@link Uri}
  * provided in the incoming Intent if there is one, otherwise it defaults to displaying the
  * contents of the {@link NotePadProvider}.
- *
- * NOTE: Notice that the provider operations in this Activity are taking place on the UI thread.
- * This is not a good practice. It is only done here to make the code more readable. A real
- * application should use the {@link android.content.AsyncQueryHandler} or
- * {@link android.os.AsyncTask} object to perform operations asynchronously on a separate thread.
  */
-public class NotesList extends ListActivity {
+public class NotesList extends ListActivity implements LoaderManager.LoaderCallbacks<Cursor> {
 
     // For logging and debugging
     private static final String TAG = "NotesList";
 
+    private static final int LOADER_ID = 0;
+
     /**
      * The columns needed by the cursor adapter
      */
@@ -65,6 +64,8 @@
     /** The index of the title column */
     private static final int COLUMN_INDEX_TITLE = 1;
 
+    private SimpleCursorAdapter mAdapter;
+
     /**
      * onCreate is called when Android starts this Activity from scratch.
      */
@@ -95,18 +96,6 @@
          */
         getListView().setOnCreateContextMenuListener(this);
 
-        /* Performs a managed query. The Activity handles closing and requerying the cursor
-         * when needed.
-         *
-         * Please see the introductory note about performing provider operations on the UI thread.
-         */
-        Cursor cursor = managedQuery(
-            getIntent().getData(),            // Use the default content URI for the provider.
-            PROJECTION,                       // Return the note ID and title for each note.
-            null,                             // No where clause, return all records.
-            null,                             // No where clause, therefore no where column values.
-            NotePad.Notes.DEFAULT_SORT_ORDER  // Use the default sort order.
-        );
 
         /*
          * The following two arrays create a "map" between columns in the cursor and view IDs
@@ -124,17 +113,19 @@
         int[] viewIDs = { android.R.id.text1 };
 
         // Creates the backing adapter for the ListView.
-        SimpleCursorAdapter adapter
-            = new SimpleCursorAdapter(
-                      this,                             // The Context for the ListView
-                      R.layout.noteslist_item,          // Points to the XML for a list item
-                      cursor,                           // The cursor to get items from
-                      dataColumns,
-                      viewIDs
-              );
+        mAdapter = new SimpleCursorAdapter(
+            this,                             // The Context for the ListView
+            R.layout.noteslist_item,          // Points to the XML for a list item
+            null,                             // The cursor is set by CursorLoader when loaded
+            dataColumns,
+            viewIDs,
+            CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER
+        );
 
         // Sets the ListView's adapter to be the cursor adapter that was just created.
-        setListAdapter(adapter);
+        setListAdapter(mAdapter);
+        // Initialize the LoaderManager and start the query
+        getLoaderManager().initLoader(LOADER_ID, null, this);
     }
 
     /**
@@ -464,4 +455,28 @@
             startActivity(new Intent(Intent.ACTION_EDIT, uri));
         }
     }
+
+    // LoaderManager callbacks
+    @Override
+    public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
+        return new CursorLoader(
+            this,
+            getIntent().getData(),            // Use the default content URI for the provider.
+            PROJECTION,                       // Return the note ID and title for each note.
+            null,                             // No where clause, return all records.
+            null,                             // No where clause, therefore no where column values.
+            NotePad.Notes.DEFAULT_SORT_ORDER  // Use the default sort order.
+        );
+    }
+
+    @Override
+    public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
+        mAdapter.changeCursor(cursor);
+    }
+
+    @Override
+    public void onLoaderReset(Loader<Cursor> cursorLoader) {
+        // Since the Loader is reset, this removes the cursor reference from the adapter.
+        mAdapter.changeCursor(null);
+    }
 }
diff --git a/samples/NotePad/src/com/example/android/notepad/NotesLiveFolder.java b/samples/NotePad/src/com/example/android/notepad/NotesLiveFolder.java
deleted file mode 100644
index 24afaa0..0000000
--- a/samples/NotePad/src/com/example/android/notepad/NotesLiveFolder.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2009 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.notepad;
-
-import com.example.android.notepad.NotePad;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.Intent.ShortcutIconResource;
-import android.os.Bundle;
-import android.provider.LiveFolders;
-
-/**
- * This Activity creates a live folder Intent and
- * sends it back to HOME. From the data in the Intent, HOME creates a live folder and displays
- * its icon in the Home view.
- * When the user clicks the icon, Home uses the data it got from the Intent to retrieve information
- * from a content provider and display it in a View.
- *
- * The intent filter for this Activity is set to ACTION_CREATE_LIVE_FOLDER, which
- * HOME sends in response to a long press and selection of Live Folder.
- */
-public class NotesLiveFolder extends Activity {
-
-    /**
-     * All of the work is done in onCreate(). The Activity doesn't actually display a UI.
-     * Instead, it sets up an Intent and returns it to its caller (the HOME activity).
-     */
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        /*
-         * Gets the incoming Intent and its action. If the incoming Intent was
-         * ACTION_CREATE_LIVE_FOLDER, then create an outgoing Intent with the
-         * necessary data and send back OK. Otherwise, send back CANCEL.
-         */
-        final Intent intent = getIntent();
-        final String action = intent.getAction();
-
-        if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
-
-            // Creates a new Intent.
-            final Intent liveFolderIntent = new Intent();
-
-            /*
-             * The following statements put data into the outgoing Intent. Please see
-             * {@link android.provider.LiveFolders for a detailed description of these
-             * data values. From this data, HOME sets up a live folder.
-             */
-            // Sets the URI pattern for the content provider backing the folder.
-            liveFolderIntent.setData(NotePad.Notes.LIVE_FOLDER_URI);
-
-            // Adds the display name of the live folder as an Extra string.
-            String foldername = getString(R.string.live_folder_name);
-            liveFolderIntent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, foldername);
-
-            // Adds the display icon of the live folder as an Extra resource.
-            ShortcutIconResource foldericon =
-                Intent.ShortcutIconResource.fromContext(this, R.drawable.live_folder_notes);
-            liveFolderIntent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON, foldericon);
-
-            // Add the display mode of the live folder as an integer. The specified
-            // mode causes the live folder to display as a list.
-            liveFolderIntent.putExtra(
-                    LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE,
-                    LiveFolders.DISPLAY_MODE_LIST);
-
-            /*
-             * Adds a base action for items in the live folder list, as an Intent. When the
-             * user clicks an individual note in the list, the live folder fires this Intent.
-             *
-             * Its action is ACTION_EDIT, so it triggers the Note Editor activity. Its
-             * data is the URI pattern for a single note identified by its ID. The live folder
-             * automatically adds the ID value of the selected item to the URI pattern.
-             *
-             * As a result, Note Editor is triggered and gets a single note to retrieve by ID.
-             */
-            Intent returnIntent
-                    = new Intent(Intent.ACTION_EDIT, NotePad.Notes.CONTENT_ID_URI_PATTERN);
-            liveFolderIntent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT, returnIntent);
-
-            /* Creates an ActivityResult object to propagate back to HOME. Set its result indicator
-             * to OK, and sets the returned Intent to the live folder Intent that was just
-             * constructed.
-             */
-            setResult(RESULT_OK, liveFolderIntent);
-
-        } else {
-
-            // If the original action was not ACTION_CREATE_LIVE_FOLDER, creates an
-            // ActivityResult with the indicator set to CANCELED, but do not return an Intent
-            setResult(RESULT_CANCELED);
-        }
-
-        // Closes the Activity. The ActivityObject is propagated back to the caller.
-        finish();
-    }
-}
diff --git a/samples/NotePad/src/com/example/android/notepad/TitleEditor.java b/samples/NotePad/src/com/example/android/notepad/TitleEditor.java
index 5abe97b..e6f029b 100644
--- a/samples/NotePad/src/com/example/android/notepad/TitleEditor.java
+++ b/samples/NotePad/src/com/example/android/notepad/TitleEditor.java
@@ -21,8 +21,10 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.view.View;
 import android.widget.EditText;
+import android.widget.Toast;
 
 /**
  * This Activity allows the user to edit a note's title. It displays a floating window
@@ -49,15 +51,15 @@
     // The position of the title column in a Cursor returned by the provider.
     private static final int COLUMN_INDEX_TITLE = 1;
 
-    // A Cursor object that will contain the results of querying the provider for a note.
-    private Cursor mCursor;
-
     // An EditText object for preserving the edited title.
     private EditText mText;
 
     // A URI object for the note whose title is being edited.
     private Uri mUri;
 
+    // The title that was last saved.
+    private String mSavedTitle;
+
     /**
      * This method is called by Android when the Activity is first started. From the incoming
      * Intent, it determines what kind of editing is desired, and then does it.
@@ -69,6 +71,9 @@
         // Set the View for this Activity object's UI.
         setContentView(R.layout.title_editor);
 
+        // Gets the View ID for the EditText box
+        mText = (EditText) this.findViewById(R.id.title);
+
         // Get the Intent that activated this Activity, and from it get the URI of the note whose
         // title we need to edit.
         mUri = getIntent().getData();
@@ -82,7 +87,7 @@
          * android.content.AsyncQueryHandler or android.os.AsyncTask.
          */
 
-        mCursor = managedQuery(
+        Cursor cursor = getContentResolver().query(
             mUri,        // The URI for the note that is to be retrieved.
             PROJECTION,  // The columns to retrieve
             null,        // No selection criteria are used, so no where columns are needed.
@@ -90,8 +95,15 @@
             null         // No sort order is needed.
         );
 
-        // Gets the View ID for the EditText box
-        mText = (EditText) this.findViewById(R.id.title);
+        if (cursor != null) {
+
+            // The Cursor was just retrieved, so its index is set to one record *before* the first
+            // record retrieved. This moves it to the first record.
+            cursor.moveToFirst();
+
+            // Displays the current title text in the EditText object.
+            mText.setText(cursor.getString(COLUMN_INDEX_TITLE));
+        }
     }
 
     /**
@@ -103,65 +115,83 @@
     @Override
     protected void onResume() {
         super.onResume();
-
-        // Verifies that the query made in onCreate() actually worked. If it worked, then the
-        // Cursor object is not null. If it is *empty*, then mCursor.getCount() == 0.
-        if (mCursor != null) {
-
-            // The Cursor was just retrieved, so its index is set to one record *before* the first
-            // record retrieved. This moves it to the first record.
-            mCursor.moveToFirst();
-
-            // Displays the current title text in the EditText object.
-            mText.setText(mCursor.getString(COLUMN_INDEX_TITLE));
-        }
     }
 
     /**
      * This method is called when the Activity loses focus.
      *
-     * For Activity objects that edit information, onPause() may be the one place where changes are
-     * saved. The Android application model is predicated on the idea that "save" and "exit" aren't
-     * required actions. When users navigate away from an Activity, they shouldn't have to go back
-     * to it to complete their work. The act of going away should save everything and leave the
-     * Activity in a state where Android can destroy it if necessary.
-     *
-     * Updates the note with the text currently in the text box.
+     * While there is no need to override this method in this app, it is shown here to highlight
+     * that we are not saving any state in onPause, but have moved app state saving to onStop
+     * callback.
+     * In earlier versions of this app and popular literature it had been shown that onPause is good
+     * place to persist any unsaved work, however, this is not really a good practice because of how
+     * application and process lifecycle behave.
+     * As a general guideline apps should have a way of saving their business logic that does not
+     * solely rely on Activity (or other component) lifecyle state transitions.
+     * As a backstop you should save any app state, not saved during lifetime of the Activity, in
+     * onStop().
+     * For a more detailed explanation of this recommendation please read
+     * <a href = "https://developer.android.com/guide/topics/processes/process-lifecycle.html">
+     * Processes and Application Life Cycle </a>.
+     * <a href="https://developer.android.com/training/basics/activity-lifecycle/pausing.html">
+     * Pausing and Resuming an Activity </a>.
      */
     @Override
     protected void onPause() {
         super.onPause();
+    }
 
-        // Verifies that the query made in onCreate() actually worked. If it worked, then the
-        // Cursor object is not null. If it is *empty*, then mCursor.getCount() == 0.
-
-        if (mCursor != null) {
-
-            // Creates a values map for updating the provider.
-            ContentValues values = new ContentValues();
-
-            // In the values map, sets the title to the current contents of the edit box.
-            values.put(NotePad.Notes.COLUMN_NAME_TITLE, mText.getText().toString());
-
-            /*
-             * Updates the provider with the note's new title.
-             *
-             * Note: This is being done on the UI thread. It will block the thread until the
-             * update completes. In a sample app, going against a simple provider based on a
-             * local database, the block will be momentary, but in a real app you should use
-             * android.content.AsyncQueryHandler or android.os.AsyncTask.
-             */
-            getContentResolver().update(
-                mUri,    // The URI for the note to update.
-                values,  // The values map containing the columns to update and the values to use.
-                null,    // No selection criteria is used, so no "where" columns are needed.
-                null     // No "where" columns are used, so no "where" values are needed.
-            );
-
-        }
+    /**
+     * This method is called when the Activity becomes invisible.
+     *
+     * For Activity objects that edit information, onStop() may be the one place where changes are
+     * saved.
+     * Updates the note with the text currently in the text box.
+     */
+    @Override
+    protected void onStop() {
+        super.onStop();
+        saveTitle();
     }
 
     public void onClickOk(View v) {
+        saveTitle();
         finish();
     }
+
+    // Saves the title if required
+    private void saveTitle() {
+
+        if (!TextUtils.isEmpty(mText.getText())) {
+
+            String newTitle = mText.getText().toString();
+
+            if (!newTitle.equals(mSavedTitle)) {
+                // Creates a values map for updating the provider.
+                ContentValues values = new ContentValues();
+
+                // In the values map, sets the title to the current contents of the edit box.
+                values.put(NotePad.Notes.COLUMN_NAME_TITLE, newTitle);
+
+                /*
+                 * Updates the provider with the note's new title.
+                 *
+                 * Note: This is being done on the UI thread. It will block the thread until the
+                 * update completes. In a sample app, going against a simple provider based on a
+                 * local database, the block will be momentary, but in a real app you should use
+                 * android.content.AsyncQueryHandler or android.os.AsyncTask.
+                 */
+                getContentResolver().update(
+                    mUri,    // The URI for the note to update.
+                    values,
+                    // The values map containing the columns to update and the values to use.
+                    null,    // No selection criteria is used, so no "where" columns are needed.
+                    null     // No "where" columns are used, so no "where" values are needed.
+                );
+                mSavedTitle = newTitle;
+            }
+        } else {
+            Toast.makeText(this, R.string.title_blank, Toast.LENGTH_SHORT).show();
+        }
+    }
 }