diff --git a/samples/NotePad/AndroidManifest.xml b/samples/NotePad/AndroidManifest.xml
index ead782925..51e848d8e 100644
--- a/samples/NotePad/AndroidManifest.xml
+++ b/samples/NotePad/AndroidManifest.xml
@@ -107,14 +107,6 @@
-
-
-
-
-
-
-
diff --git a/samples/NotePad/res/values/strings.xml b/samples/NotePad/res/values/strings.xml
index 26d23d072..508fa439f 100644
--- a/samples/NotePad/res/values/strings.xml
+++ b/samples/NotePad/res/values/strings.xml
@@ -40,4 +40,5 @@
Error
Error loading note
There is nothing to save
+ Blank title not saved
\ 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 59d6f1290..b8b070f20 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.Menu;
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 {
// For logging and debugging purposes
private static final String TAG = "NoteEditor";
@@ -71,10 +70,11 @@ public class NoteEditor extends Activity {
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 @@ public class NoteEditor extends Activity {
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 @@ public class NoteEditor extends Activity {
// 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 @@ public class NoteEditor extends Activity {
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 @@ public class NoteEditor extends Activity {
// 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 @@ public class NoteEditor extends Activity {
@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
+ *
+ * Processes and Application Life Cycle .
+ *
+ * Pausing and Resuming an Activity .
+ */
+ @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 @@ public class NoteEditor extends Activity {
* 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 @@ public class NoteEditor extends Activity {
@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 @@ public class NoteEditor extends Activity {
// (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 @@ public class NoteEditor extends Activity {
* 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 @@ public class NoteEditor extends Activity {
* 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 @@ public class NoteEditor extends Activity {
* 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 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 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 cursorLoader) {}
}
diff --git a/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java b/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
index 183964563..f81e22d1f 100644
--- a/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
+++ b/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
@@ -70,11 +70,6 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter sNotesProjectionMap;
- /**
- * A projection map used to select columns from the database
- */
- private static HashMap sLiveFolderProjectionMap;
-
/**
* Standard projection for the interesting columns of a normal note.
*/
@@ -96,9 +91,6 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter();
-
- // 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 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter {
// 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 @@ public class NotesList extends ListActivity {
/** 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 @@ public class NotesList extends ListActivity {
*/
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 @@ public class NotesList extends ListActivity {
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 @@ public class NotesList extends ListActivity {
startActivity(new Intent(Intent.ACTION_EDIT, uri));
}
}
+
+ // LoaderManager callbacks
+ @Override
+ public Loader 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 cursorLoader, Cursor cursor) {
+ mAdapter.changeCursor(cursor);
+ }
+
+ @Override
+ public void onLoaderReset(Loader 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 24afaa0d4..000000000
--- 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 5abe97b39..e6f029b98 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.content.ContentValues;
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 @@ public class TitleEditor extends Activity {
// 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 @@ public class TitleEditor extends Activity {
// 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 @@ public class TitleEditor extends Activity {
* 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 @@ public class TitleEditor extends Activity {
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 @@ public class TitleEditor extends Activity {
@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
+ *
+ * Processes and Application Life Cycle .
+ *
+ * Pausing and Resuming an Activity .
*/
@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();
+ }
+ }
}