This is a manual merge of Ib7ea41079f3b268f3be1f86febdb1caed98bdd8e

Which includes the following:
        - inflating menus from XML and supporting API Level 3 without the need
        for version qualifiers on resource directories.
        - to NOT include items in the options menu based on the 'selected' item
        (that's what a context menu is for).
        - include all drawables in the app, instead of using system resources.

        Add features:
        - the ability to edit the note title through the context menu
        - disable menu items in the editor based on menu groups
        - add a "save" button to the editor instead of assuming BACK functionality
        - and show the title of the current note in the activity title.

Plus, this change adds Holo themes and items to the action bar.
And fix a bug in NoteEditor: the constants used for the column indexes caused the title
and note text to be saved incorrectly---removed them and instead perform column index
lookup on the notes cursor.

Change-Id: I6e7a693a35f5dc2da423982869dd871c4c171e5b
This commit is contained in:
Scott Main
2010-11-29 11:16:13 -08:00
parent 7fd3f41504
commit a00a0e7f7d
30 changed files with 262 additions and 368 deletions

View File

@@ -22,8 +22,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.notepad" > package="com.example.android.notepad" >
<uses-sdk android:targetSdkVersion="4" android:minSdkVersion="3"/> <uses-sdk android:minSdkVersion="Froyo" />
<application android:icon="@drawable/app_notes" <application android:icon="@drawable/app_notes"
android:label="@string/app_name" android:label="@string/app_name"
> >
@@ -53,8 +52,7 @@
</activity> </activity>
<activity android:name="NoteEditor" <activity android:name="NoteEditor"
android:theme="@android:style/Theme.Light" android:theme="@android:style/Theme.Light.Holo"
android:label="@string/title_note"
android:screenOrientation="sensor" android:screenOrientation="sensor"
android:configChanges="keyboardHidden|orientation" android:configChanges="keyboardHidden|orientation"
> >
@@ -83,7 +81,8 @@
<activity android:name="TitleEditor" <activity android:name="TitleEditor"
android:label="@string/title_edit_title" android:label="@string/title_edit_title"
android:theme="@android:style/Theme.Dialog" android:icon="@drawable/ic_menu_edit"
android:theme="@android:style/Theme.Holo.Dialog"
android:windowSoftInputMode="stateVisible"> android:windowSoftInputMode="stateVisible">
<!-- This activity implements an alternative action that can be <!-- This activity implements an alternative action that can be
performed on notes: editing their title. It can be used as performed on notes: editing their title. It can be used as

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

BIN
samples/NotePad/res/drawable-hdpi/app_notes.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project <!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -13,14 +13,13 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<view xmlns:android="http://schemas.android.com/apk/res/android" <view xmlns:android="http://schemas.android.com/apk/res/android"
class="com.example.android.notepad.NoteEditor$LinedEditText" class="com.example.android.notepad.NoteEditor$LinedEditText"
android:id="@+id/note" android:id="@+id/note"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:padding="5dip" android:padding="5dp"
android:scrollbars="vertical" android:scrollbars="vertical"
android:fadingEdge="vertical" android:fadingEdge="vertical"
android:gravity="top" android:gravity="top"

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project <!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project <!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -24,7 +24,8 @@
<EditText android:id="@+id/title" <EditText android:id="@+id/title"
android:maxLines="1" android:maxLines="1"
android:layout_marginTop="2dip" android:layout_marginTop="2dp"
android:layout_marginBottom="15dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:ems="25" android:ems="25"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -36,6 +37,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right" android:layout_gravity="right"
android:text="@string/button_ok" /> android:text="@string/button_ok"
android:onClick="onClickOk" />
</LinearLayout> </LinearLayout>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_save"
android:icon="@drawable/ic_menu_save"
android:alphabeticShortcut='s'
android:title="@string/menu_save"
android:showAsAction="always" />
<item android:id="@+id/menu_revert"
android:icon="@drawable/ic_menu_revert"
android:title="@string/menu_revert" />
<item android:id="@+id/menu_delete"
android:icon="@drawable/ic_menu_delete"
android:title="@string/menu_delete"
android:showAsAction="always" />
</menu>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/context_open"
android:title="@string/menu_open" />
<item android:id="@+id/context_copy"
android:title="@string/menu_copy" />
<item android:id="@+id/context_delete"
android:title="@string/menu_delete" />
</menu>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- This is our one standard application action (creating a new note). -->
<item android:id="@+id/menu_add"
android:icon="@drawable/ic_menu_compose"
android:title="@string/menu_add"
android:alphabeticShortcut='a'
android:showAsAction="always" />
<!-- If there is currently data in the clipboard, this adds a PASTE menu item to the menu
so that the user can paste in the data.. -->
<item android:id="@+id/menu_paste"
android:icon="@drawable/ic_menu_compose"
android:title="@string/menu_paste"
android:alphabeticShortcut='p' />
</menu>

View File

@@ -15,27 +15,29 @@
--> -->
<resources> <resources>
<string name="menu_copy">Copy</string> <string name="app_name">NotePad</string>
<string name="live_folder_name">Notes</string>
<string name="title_edit_title">Note title:</string>
<string name="title_create">New note</string>
<string name="title_edit">Edit: %1$s</string>
<string name="title_notes_list">Notes</string>
<string name="menu_add">New note</string>
<string name="menu_save">Save</string>
<string name="menu_delete">Delete</string> <string name="menu_delete">Delete</string>
<string name="menu_insert">Add note</string> <string name="menu_open">Open</string>
<string name="menu_revert">Revert changes</string>
<string name="menu_copy">Copy</string>
<string name="menu_paste">Paste</string> <string name="menu_paste">Paste</string>
<string name="menu_revert">Revert</string>
<string name="menu_discard">Discard</string> <string name="button_ok">OK</string>
<string name="text_title">Title:</string>
<string name="resolve_edit">Edit note</string> <string name="resolve_edit">Edit note</string>
<string name="resolve_title">Edit title</string> <string name="resolve_title">Edit title</string>
<string name="title_create">Create note</string>
<string name="title_edit">Edit note</string>
<string name="title_notes_list">Note pad</string>
<string name="title_note">Note</string>
<string name="title_edit_title">Note title:</string>
<string name="app_name">Note Pad</string>
<string name="live_folder_name">Notes</string>
<string name="button_ok">OK</string>
<string name="error_title">Error</string> <string name="error_title">Error</string>
<string name="error_message">Error loading note</string> <string name="error_message">Error loading note</string>
<string name="nothing_to_save">There is nothing to save</string>
</resources> </resources>

View File

@@ -16,16 +16,15 @@
package com.example.android.notepad; package com.example.android.notepad;
import com.example.android.notepad.NotePad;
import android.app.Activity; import android.app.Activity;
import android.content.ClipboardManager;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
@@ -35,6 +34,7 @@ import android.os.Bundle;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.EditText; import android.widget.EditText;
@@ -51,7 +51,7 @@ import android.widget.EditText;
*/ */
public class NoteEditor extends Activity { public class NoteEditor extends Activity {
// For logging and debugging purposes // For logging and debugging purposes
private static final String TAG = "Notes"; private static final String TAG = "NoteEditor";
/* /*
* Creates a projection that returns the note ID and the note contents. * Creates a projection that returns the note ID and the note contents.
@@ -62,29 +62,17 @@ public class NoteEditor extends Activity {
NotePad.Notes.COLUMN_NAME_TITLE, NotePad.Notes.COLUMN_NAME_TITLE,
NotePad.Notes.COLUMN_NAME_NOTE NotePad.Notes.COLUMN_NAME_NOTE
}; };
// The index of the note column
private static final int COLUMN_INDEX_NOTE = 1;
// The index of the title column
private static final int COLUMN_INDEX_TITLE = 2;
// A label for the saved state of the activity // A label for the saved state of the activity
private static final String ORIGINAL_CONTENT = "origContent"; private static final String ORIGINAL_CONTENT = "origContent";
// Menu item identifiers
private static final int REVERT_ID = Menu.FIRST;
private static final int DISCARD_ID = Menu.FIRST + 1;
private static final int DELETE_ID = Menu.FIRST + 2;
// This Activity can be started by more than one action. Each action is represented // This Activity can be started by more than one action. Each action is represented
// as a "state" constant // as a "state" constant
private static final int STATE_EDIT = 0; private static final int STATE_EDIT = 0;
private static final int STATE_INSERT = 1; private static final int STATE_INSERT = 1;
private static final int STATE_PASTE = 2;
// Global mutable variables // Global mutable variables
private int mState; private int mState;
private boolean mNoteOnly = false;
private Uri mUri; private Uri mUri;
private Cursor mCursor; private Cursor mCursor;
private EditText mText; private EditText mText;
@@ -199,15 +187,6 @@ public class NoteEditor extends Activity {
// set the result to be returned. // set the result to be returned.
setResult(RESULT_OK, (new Intent()).setAction(mUri.toString())); setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));
// For a paste, initializes the data from clipboard.
if (Intent.ACTION_PASTE.equals(action)) {
// Does the paste
performPaste();
// Switches the state to PASTE. The title can not be modified.
mState = STATE_PASTE;
}
// If the action was other than EDIT or INSERT: // If the action was other than EDIT or INSERT:
} else { } else {
@@ -218,12 +197,6 @@ public class NoteEditor extends Activity {
return; return;
} }
// Sets the layout for this Activity. See res/layout/note_editor.xml
setContentView(R.layout.note_editor);
// Gets a handle to the EditText in the the layout.
mText = (EditText) findViewById(R.id.note);
/* /*
* Using the URI passed in with the triggering Intent, gets the note or notes in * Using the URI passed in with the triggering Intent, gets the note or notes in
* the provider. * the provider.
@@ -240,6 +213,21 @@ public class NoteEditor extends Activity {
null // Use the default sort order (modification date, descending) null // Use the default sort order (modification date, descending)
); );
// 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();
// Switches the state to EDIT so the title can be modified.
mState = STATE_EDIT;
}
// Sets the layout for this Activity. See res/layout/note_editor.xml
setContentView(R.layout.note_editor);
// 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 * If this Activity had stopped previously, its state was written the ORIGINAL_CONTENT
* location in the saved Instance state. This gets the state. * location in the saved Instance state. This gets the state.
@@ -266,6 +254,8 @@ public class NoteEditor extends Activity {
* process. This tests that it's not null, since it should always contain data. * process. This tests that it's not null, since it should always contain data.
*/ */
if (mCursor != null) { 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 /* 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 * a Cursor for the first time. The semantics of using a Cursor are that when it is
@@ -276,12 +266,14 @@ public class NoteEditor extends Activity {
// Modifies the window title for the Activity according to the current Activity state. // Modifies the window title for the Activity according to the current Activity state.
if (mState == STATE_EDIT) { if (mState == STATE_EDIT) {
// Set the title of the Activity to include the note title
// Sets the title to Edit for edits int colTitleIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE);
setTitle(getText(R.string.title_edit)); String title = mCursor.getString(colTitleIndex);
Resources res = getResources();
// Sets the title to "create" for inserts and pastes String text = String.format(res.getString(R.string.title_edit), title);
} else if (mState == STATE_INSERT || mState == STATE_PASTE) { setTitle(text);
// Sets the title to "create" for inserts
} else if (mState == STATE_INSERT) {
setTitle(getText(R.string.title_create)); setTitle(getText(R.string.title_create));
} }
@@ -294,7 +286,8 @@ public class NoteEditor extends Activity {
// Gets the note text from the Cursor and puts it in the TextView, but doesn't change // Gets the note text from the Cursor and puts it in the TextView, but doesn't change
// the text cursor's position. // the text cursor's position.
String note = mCursor.getString(COLUMN_INDEX_NOTE); int colNoteIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE);
String note = mCursor.getString(colNoteIndex);
mText.setTextKeepState(note); mText.setTextKeepState(note);
// Stores the original note text, to allow the user to revert changes. // Stores the original note text, to allow the user to revert changes.
@@ -361,7 +354,7 @@ public class NoteEditor extends Activity {
* even if the note was being edited, the assumption being that the user wanted to * even if the note was being edited, the assumption being that the user wanted to
* "clear out" (delete) the note. * "clear out" (delete) the note.
*/ */
if (isFinishing() && (length == 0) && !mNoteOnly) { if (isFinishing() && (length == 0)) {
setResult(RESULT_CANCELED); setResult(RESULT_CANCELED);
deleteNote(); deleteNote();
@@ -371,10 +364,12 @@ public class NoteEditor extends Activity {
* onCreate() inserted a new empty note into the provider, and it is this new note * onCreate() inserted a new empty note into the provider, and it is this new note
* that is being edited. * that is being edited.
*/ */
} else { } else if (mState == STATE_EDIT) {
// Creates a map to contain the new values for the columns // Creates a map to contain the new values for the columns
updateNote(text, null, !mNoteOnly); updateNote(text, null);
} else if (mState == STATE_INSERT) {
updateNote(text, text);
mState = STATE_EDIT;
} }
} }
} }
@@ -391,72 +386,38 @@ public class NoteEditor extends Activity {
*/ */
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu); // Inflate menu from XML resource
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.editor_options_menu, menu);
// Builds the menus that are shown when editing. These are 'revert' to undo changes, and // Only add extra menu items for a saved note
// 'delete' to delete the note.
if (mState == STATE_EDIT) { if (mState == STATE_EDIT) {
// Append to the
// Adds the 'revert' menu item, and sets its shortcut to numeric 0, letter 'r' and its // menu items for any other activities that can do stuff with it
// icon to the Android standard revert icon. // as well. This does a query on the system for any activities that
menu.add(0, REVERT_ID, 0, R.string.menu_revert) // implement the ALTERNATIVE_ACTION for our data, adding a menu item
.setShortcut('0', 'r') // for each one that is found.
.setIcon(android.R.drawable.ic_menu_revert); Intent intent = new Intent(null, mUri);
if (!mNoteOnly) {
// Adds the 'delete' menu item, and sets its shortcut to numeric 1, letter 'd'
// and its icon to the Android standard delete icon
menu.add(0, DELETE_ID, 0, R.string.menu_delete)
.setShortcut('1', 'd')
.setIcon(android.R.drawable.ic_menu_delete);
}
// Builds the menus that are shown when inserting. The only option is 'Discard' to throw
// away the new note.
} else {
menu.add(0, DISCARD_ID, 0, R.string.menu_discard)
.setShortcut('0', 'd')
.setIcon(android.R.drawable.ic_menu_delete);
}
/*
* Appends menu items for any Activity declarations that implement an alternative action
* for this Activity's MIME type, one menu item for each Activity.
*/
if (!mNoteOnly) {
// Makes a new Intent with the URI data passed to this Activity
Intent intent = new Intent(null, getIntent().getData());
// Adds the ALTERNATIVE category to the Intent.
intent.addCategory(Intent.CATEGORY_ALTERNATIVE); intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
/* new ComponentName(this, NoteEditor.class), null, intent, 0, null);
* Constructs a new ComponentName object that represents the current Activity.
*/
ComponentName component = new ComponentName(
this,
NoteEditor.class);
/*
* In the ALTERNATIVE menu group, adds an option for each Activity that is registered to
* handle this Activity's MIME type. The Intent describes what type of items should be
* added to the menu; in this case, Activity declarations with category ALTERNATIVE.
*/
menu.addIntentOptions(
Menu.CATEGORY_ALTERNATIVE, // The menu group to add the items to.
Menu.NONE, // No unique ID is needed.
Menu.NONE, // No ordering is needed.
component, // The current Activity object's component name
null, // No specific items need to be placed first.
intent, // The intent containing the type of items to add.
Menu.NONE, // No flags are necessary.
null // No need to generate an array of menu items.
);
} }
// The method returns TRUE, so that further menu processing is not done. return super.onCreateOptionsMenu(menu);
return true; }
@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);
String currentNote = mText.getText().toString();
if (savedNote.equals(currentNote)) {
menu.findItem(R.id.menu_revert).setVisible(false);
} else {
menu.findItem(R.id.menu_revert).setVisible(true);
}
return super.onPrepareOptionsMenu(menu);
} }
/** /**
@@ -470,29 +431,21 @@ public class NoteEditor extends Activity {
*/ */
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
// Handle all of the possible menu actions.
// Chooses the action to perform
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_save:
// Deletes the note and close the Activity. String text = mText.getText().toString();
case DELETE_ID: updateNote(text, null);
finish();
break;
case R.id.menu_delete:
deleteNote(); deleteNote();
finish(); finish();
break; break;
case R.id.menu_revert:
// Discards the new note.
case DISCARD_ID:
cancelNote();
break;
// Discards any changes to an edited note.
case REVERT_ID:
cancelNote(); cancelNote();
break; break;
} }
// Continues with processing the menu item. In effect, if the item was an alternative
// action, this invokes the Activity for that action.
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@@ -540,8 +493,10 @@ public class NoteEditor extends Activity {
// (moveToFirst() returns true), then this gets the note data from it. // (moveToFirst() returns true), then this gets the note data from it.
if (orig != null) { if (orig != null) {
if (orig.moveToFirst()) { if (orig.moveToFirst()) {
text = orig.getString(COLUMN_INDEX_NOTE); int colNoteIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE);
title = orig.getString(COLUMN_INDEX_TITLE); int colTitleIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE);
text = orig.getString(colNoteIndex);
title = orig.getString(colTitleIndex);
} }
// Closes the cursor. // Closes the cursor.
@@ -556,7 +511,7 @@ public class NoteEditor extends Activity {
} }
// Updates the current note with the retrieved title and text. // Updates the current note with the retrieved title and text.
updateNote(text, title, true); updateNote(text, title);
} }
} }
//END_INCLUDE(paste) //END_INCLUDE(paste)
@@ -565,16 +520,11 @@ public class NoteEditor extends Activity {
* Replaces the current note contents with the text and title provided as arguments. * Replaces the current note contents with the text and title provided as arguments.
* @param text The new note contents to use. * @param text The new note contents to use.
* @param title The new note title to use * @param title The new note title to use
* @param updateTitle <em>true</em> if the title should be updated. This also updates the
* modification timestamp to the current time.
*/ */
private final void updateNote(String text, String title, boolean updateTitle) { private final void updateNote(String text, String title) {
// Sets up a map to contain values to be updated in the provider. // Sets up a map to contain values to be updated in the provider.
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
// If updateTitle is true, sets the modification date/time stamp to now.
if (updateTitle) {
values.put(NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE, System.currentTimeMillis()); values.put(NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE, System.currentTimeMillis());
// If the action is to insert a new note, this creates an initial title for it. // If the action is to insert a new note, this creates an initial title for it.
@@ -599,10 +549,11 @@ public class NoteEditor extends Activity {
} }
} }
} }
// In the values map, sets the value of the title // In the values map, sets the value of the title
values.put(NotePad.Notes.COLUMN_NAME_TITLE, title); values.put(NotePad.Notes.COLUMN_NAME_TITLE, title);
} } else if (title != null) {
// In the values map, sets the value of the title
values.put(NotePad.Notes.COLUMN_NAME_TITLE, title);
} }
// This puts the desired notes text into the map. // This puts the desired notes text into the map.
@@ -634,8 +585,6 @@ public class NoteEditor extends Activity {
* newly created, or reverts to the original text of the note i * newly created, or reverts to the original text of the note i
*/ */
private final void cancelNote() { private final void cancelNote() {
// If
if (mCursor != null) { if (mCursor != null) {
if (mState == STATE_EDIT) { if (mState == STATE_EDIT) {
// Put the original note text back into the database // Put the original note text back into the database

View File

@@ -31,6 +31,7 @@ import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
@@ -53,12 +54,6 @@ public class NotesList extends ListActivity {
// For logging and debugging // For logging and debugging
private static final String TAG = "NotesList"; private static final String TAG = "NotesList";
// Menu item ids
public static final int MENU_ITEM_DELETE = Menu.FIRST;
public static final int MENU_ITEM_COPY = Menu.FIRST + 1;
public static final int MENU_ITEM_INSERT = Menu.FIRST + 2;
public static final int MENU_ITEM_PASTE = Menu.FIRST + 3;
/** /**
* The columns needed by the cursor adapter * The columns needed by the cursor adapter
*/ */
@@ -70,8 +65,6 @@ public class NotesList extends ListActivity {
/** The index of the title column */ /** The index of the title column */
private static final int COLUMN_INDEX_TITLE = 1; private static final int COLUMN_INDEX_TITLE = 1;
private MenuItem mPasteItem;
/** /**
* onCreate is called when Android starts this Activity from scratch. * onCreate is called when Android starts this Activity from scratch.
*/ */
@@ -159,70 +152,20 @@ public class NotesList extends ListActivity {
*/ */
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
// Inflate menu from XML resource
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.list_options_menu, menu);
// The parent method creates the default method with the standard system items. // Generate any additional actions that can be performed on the
super.onCreateOptionsMenu(menu);
// Adds an Insert menu item
MenuItem menuItem = menu.add(
Menu.NONE, // No menu group.
MENU_ITEM_INSERT, // Unique ID for this item.
Menu.NONE, // No order within the group.
R.string.menu_insert // Displayed text for the menu item.
);
// Sets keyboard shortcuts for the menu item, either "3" or "a";
menuItem.setShortcut('3', 'a');
// Sets the icon for the menu item
menuItem.setIcon(android.R.drawable.ic_menu_add);
// If there is currently data in the clipboard, this adds a PASTE menu item to the menu
// so that the user can paste in the data.
mPasteItem = menu.add(
Menu.NONE, // No menu group.
MENU_ITEM_PASTE, // A unique ID for this item
Menu.NONE, // No menu order
R.string.menu_paste // The displayed text
).setShortcut('4', 'p'); // Set the keyboard shortcuts for this new item.
// Generates any additional actions that can be performed on the
// overall list. In a normal install, there are no additional // overall list. In a normal install, there are no additional
// actions found here, but this allows other applications to extend // actions found here, but this allows other applications to extend
// our menu with their own actions. // our menu with their own actions.
/* Creates a new Intent with the same incoming data and no defined action.
* It also sets its category to ALTERNATIVE. This prepares the Intent as a place
* to group alternative options in the menu.
*/
Intent intent = new Intent(null, getIntent().getData()); Intent intent = new Intent(null, getIntent().getData());
intent.addCategory(Intent.CATEGORY_ALTERNATIVE); intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
new ComponentName(this, NotesList.class), null, intent, 0, null);
/* Creates a ComponentName from the current Context and this Activity object's return super.onCreateOptionsMenu(menu);
* class object.
*/
ComponentName component = new ComponentName(this, NotesList.class);
/*
* Adds any other activities that want to be alternatives for this view. In effect,
* any application can add itself as an alternative on the options menu.
*/
menu.addIntentOptions(
Menu.CATEGORY_ALTERNATIVE, // Add the options to the Alternatives group
Menu.NONE, // Do not use a unique ID
Menu.NONE, // No need to order the options
component, // The ComponentName of the Activity making the request.
// This Activity is excluded from the list of alternatives.
null, // No specific items are listed first.
intent, // The Intent to resolve to, in effect, an Intent listing
// the alternatives
Menu.NONE, // no flags are needed
null // Since no specifics were used, so a menu item array is
// not needed.
);
// Returns true so that the menu is displayed.
return true;
} }
@Override @Override
@@ -233,11 +176,13 @@ public class NotesList extends ListActivity {
ClipboardManager clipboard = (ClipboardManager) ClipboardManager clipboard = (ClipboardManager)
getSystemService(Context.CLIPBOARD_SERVICE); getSystemService(Context.CLIPBOARD_SERVICE);
MenuItem mPasteItem = menu.findItem(R.id.menu_paste);
// If the clipboard contains an item, enables the Paste option on the menu. // If the clipboard contains an item, enables the Paste option on the menu.
if (clipboard.hasPrimaryClip()) { if (clipboard.hasPrimaryClip()) {
mPasteItem.setEnabled(true); mPasteItem.setEnabled(true);
} else { } else {
// If the clipboard is empty, disables the menu's Paste option. // If the clipboard is empty, disables the menu's Paste option.
mPasteItem.setEnabled(false); mPasteItem.setEnabled(false);
} }
@@ -274,7 +219,6 @@ public class NotesList extends ListActivity {
*/ */
intent.addCategory(Intent.CATEGORY_ALTERNATIVE); intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
/* /*
* Add alternatives to the menu * Add alternatives to the menu
*/ */
@@ -318,13 +262,8 @@ public class NotesList extends ListActivity {
*/ */
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
// Gets the ID of the selected item
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_add:
// If the menu item is Insert
case MENU_ITEM_INSERT:
/* /*
* Launches a new Activity using an Intent. The intent filter for the Activity * Launches a new Activity using an Intent. The intent filter for the Activity
* has to have action ACTION_INSERT. No category is set, so DEFAULT is assumed. * has to have action ACTION_INSERT. No category is set, so DEFAULT is assumed.
@@ -332,10 +271,7 @@ public class NotesList extends ListActivity {
*/ */
startActivity(new Intent(Intent.ACTION_INSERT, getIntent().getData())); startActivity(new Intent(Intent.ACTION_INSERT, getIntent().getData()));
return true; return true;
case R.id.menu_paste:
// If the menu item is Paste
case MENU_ITEM_PASTE:
/* /*
* Launches a new Activity using an Intent. The intent filter for the Activity * Launches a new Activity using an Intent. The intent filter for the Activity
* has to have action ACTION_PASTE. No category is set, so DEFAULT is assumed. * has to have action ACTION_PASTE. No category is set, so DEFAULT is assumed.
@@ -343,12 +279,10 @@ public class NotesList extends ListActivity {
*/ */
startActivity(new Intent(Intent.ACTION_PASTE, getIntent().getData())); startActivity(new Intent(Intent.ACTION_PASTE, getIntent().getData()));
return true; return true;
} default:
// If the menu item selected is not Insert or Paste, then this calls the regular
// processing to handle the item.
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
}
/** /**
* This method is called when the user context-clicks a note in the list. NotesList registers * This method is called when the user context-clicks a note in the list. NotesList registers
@@ -371,11 +305,9 @@ public class NotesList extends ListActivity {
// Tries to get the position of the item in the ListView that was long-pressed. // Tries to get the position of the item in the ListView that was long-pressed.
try { try {
// Casts the incoming data object into the type for AdapterView objects. // Casts the incoming data object into the type for AdapterView objects.
info = (AdapterView.AdapterContextMenuInfo) menuInfo; info = (AdapterView.AdapterContextMenuInfo) menuInfo;
} catch (ClassCastException e) { } catch (ClassCastException e) {
// If the menu object can't be cast, logs an error. // If the menu object can't be cast, logs an error.
Log.e(TAG, "bad menuInfo", e); Log.e(TAG, "bad menuInfo", e);
return; return;
@@ -392,29 +324,27 @@ public class NotesList extends ListActivity {
// If the cursor is empty, then for some reason the adapter can't get the data from the // If the cursor is empty, then for some reason the adapter can't get the data from the
// provider, so returns null to the caller. // provider, so returns null to the caller.
if (cursor == null) { if (cursor == null) {
// For some reason the requested item isn't available, do nothing // For some reason the requested item isn't available, do nothing
return; return;
} }
// Inflate menu from XML resource
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.list_context_menu, menu);
// Sets the menu header to be the title of the selected note. // Sets the menu header to be the title of the selected note.
menu.setHeaderTitle(cursor.getString(COLUMN_INDEX_TITLE)); menu.setHeaderTitle(cursor.getString(COLUMN_INDEX_TITLE));
// Adds a menu item to copy the note to the context menu // Append to the
menu.add( // menu items for any other activities that can do stuff with it
Menu.NONE, // No grouping is needed for this menu // as well. This does a query on the system for any activities that
MENU_ITEM_COPY, // A unique ID for this menu item. // implement the ALTERNATIVE_ACTION for our data, adding a menu item
Menu.NONE, // No ordering is necessary in this menu // for each one that is found.
R.string.menu_copy // The resource ID for the string to display for this item. Intent intent = new Intent(null, Uri.withAppendedPath(getIntent().getData(),
); Integer.toString((int) info.id) ));
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
// Adds a menu item to delete the note to the context menu menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
menu.add( new ComponentName(this, NotesList.class), null, intent, 0, null);
Menu.NONE, // No grouping is needed for this menu
MENU_ITEM_DELETE, // A unique ID for this menu item.
Menu.NONE, // No ordering is necessary in this menu
R.string.menu_delete // The resource ID for the string to display for this item.
);
} }
/** /**
@@ -429,7 +359,6 @@ public class NotesList extends ListActivity {
*/ */
@Override @Override
public boolean onContextItemSelected(MenuItem item) { public boolean onContextItemSelected(MenuItem item) {
// The data from the menu item. // The data from the menu item.
AdapterView.AdapterContextMenuInfo info; AdapterView.AdapterContextMenuInfo info;
@@ -446,7 +375,6 @@ public class NotesList extends ListActivity {
* note ID, to onContextItemSelected() via the item parameter. * note ID, to onContextItemSelected() via the item parameter.
*/ */
try { try {
// Casts the data object in the item into the type for AdapterView objects. // Casts the data object in the item into the type for AdapterView objects.
info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
} catch (ClassCastException e) { } catch (ClassCastException e) {
@@ -457,19 +385,36 @@ public class NotesList extends ListActivity {
// Triggers default processing of the menu item. // Triggers default processing of the menu item.
return false; return false;
} }
/*
* Gets the menu item's ID and compares it to known actions. The only actions that are
* implemented are COPY and DELETE (set in onCreateContextMenu()).
*/
switch (item.getItemId()) {
// Deletes the selected note
case MENU_ITEM_DELETE: {
// Appends the selected note's ID to the URI sent with the incoming Intent. // Appends the selected note's ID to the URI sent with the incoming Intent.
Uri noteUri = ContentUris.withAppendedId(getIntent().getData(), info.id); Uri noteUri = ContentUris.withAppendedId(getIntent().getData(), info.id);
/*
* Gets the menu item's ID and compares it to known actions.
*/
switch (item.getItemId()) {
case R.id.context_open:
// Launch activity to view/edit the currently selected item
startActivity(new Intent(Intent.ACTION_EDIT, noteUri));
return true;
//BEGIN_INCLUDE(copy)
case R.id.context_copy:
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager)
getSystemService(Context.CLIPBOARD_SERVICE);
// Copies the notes URI to the clipboard. In effect, this copies the note itself
clipboard.setPrimaryClip(ClipData.newUri( // new clipboard item holding a URI
getContentResolver(), // resolver to retrieve URI info
"Note", // label for the clip
null, // icon for the clip
noteUri) // the URI
);
// Returns to the caller and skips further processing.
return true;
//END_INCLUDE(copy)
case R.id.context_delete:
// Deletes the note from the provider by passing in a URI in note ID format. // Deletes the note from the provider by passing in a URI in note ID format.
// Please see the introductory note about performing provider operations on the // Please see the introductory note about performing provider operations on the
// UI thread. // UI thread.
@@ -482,33 +427,9 @@ public class NotesList extends ListActivity {
// Returns to the caller and skips further processing. // Returns to the caller and skips further processing.
return true; return true;
default:
return super.onContextItemSelected(item);
} }
//BEGIN_INCLUDE(copy)
// Copies the selected note to the clipboard
case MENU_ITEM_COPY: {
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager)
getSystemService(Context.CLIPBOARD_SERVICE);
// Appends the selected note's ID to the URI sent with the incoming Intent.
Uri noteUri = ContentUris.withAppendedId(getIntent().getData(), info.id);
// Copies the notes URI to the clipboard. In effect, this copies the note itself
clipboard.setPrimaryClip(ClipData.newUri( // new clipboard item holding a URI
getContentResolver(), // resolver to retrieve URI info
"Note", // label for the clip
null, // icon for the clip
noteUri) // the URI
);
// Returns to the caller and skips further processing.
return true;
}
//END_INCLUDE(copy)
}
return false;
} }
/** /**

View File

@@ -16,15 +16,12 @@
package com.example.android.notepad; package com.example.android.notepad;
import com.example.android.notepad.NotePad;
import android.app.Activity; import android.app.Activity;
import android.content.ContentValues; import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
/** /**
@@ -36,7 +33,7 @@ import android.widget.EditText;
* application should use the {@link android.content.AsyncQueryHandler} * application should use the {@link android.content.AsyncQueryHandler}
* or {@link android.os.AsyncTask} object to perform operations asynchronously on a separate thread. * or {@link android.os.AsyncTask} object to perform operations asynchronously on a separate thread.
*/ */
public class TitleEditor extends Activity implements View.OnClickListener { public class TitleEditor extends Activity {
/** /**
* This is a special intent action that means "edit the title of a note". * This is a special intent action that means "edit the title of a note".
@@ -95,11 +92,6 @@ public class TitleEditor extends Activity implements View.OnClickListener {
// Gets the View ID for the EditText box // Gets the View ID for the EditText box
mText = (EditText) this.findViewById(R.id.title); mText = (EditText) this.findViewById(R.id.title);
// Sets up a listener for the OK button. Gets the Button by its ID, then sets its
// onClickListener to this Activity.
Button b = (Button) findViewById(R.id.ok);
b.setOnClickListener(this);
} }
/** /**
@@ -169,16 +161,7 @@ public class TitleEditor extends Activity implements View.OnClickListener {
} }
} }
/** public void onClickOk(View v) {
* This method is called when the user clicks anywhere in the title text box.
*
* It calls finish(), which immediately triggers the onPause() method in this Activity. In
* turn, onPause() saves the text currently in the title text box to the note.
*/
public void onClick(View v) {
// Calls finish to force the Activity to shut down. In the lifecycle, this forces a call to
// onPause(), which saves the work the user has done.
finish(); finish();
} }
} }