From 94b6d06899ddb3f97cd0171bce5de1cb60602d01 Mon Sep 17 00:00:00 2001 From: Tom O'Neill Date: Wed, 9 Dec 2009 15:41:05 -0800 Subject: Extract and check in NotepadCodeLab.zip for change tracking The 3-part Notepad tutorial is currently only available in the zip archive frameworks/base/docs/html/guide/tutorials/notepad/codelab/NotepadCodeLab.zip. This CL adds the uncompressed code to the repository so that there is a paper trail of future changes to the code. --- .../src/com/android/demo/notepad3/NoteEdit.java | 109 ++++++++++++ .../src/com/android/demo/notepad3/Notepadv3.java | 124 ++++++++++++++ .../com/android/demo/notepad3/NotesDbAdapter.java | 188 +++++++++++++++++++++ 3 files changed, 421 insertions(+) create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/Notepadv3.java create mode 100755 tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NotesDbAdapter.java (limited to 'tutorials/NotepadCodeLab/Notepadv3Solution/src') diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java new file mode 100755 index 000000000..710ea339d --- /dev/null +++ b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * 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.android.demo.notepad3; + +import android.app.Activity; +import android.database.Cursor; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +public class NoteEdit extends Activity { + + private EditText mTitleText; + private EditText mBodyText; + private Long mRowId; + private NotesDbAdapter mDbHelper; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mDbHelper = new NotesDbAdapter(this); + mDbHelper.open(); + setContentView(R.layout.note_edit); + + + mTitleText = (EditText) findViewById(R.id.title); + mBodyText = (EditText) findViewById(R.id.body); + + Button confirmButton = (Button) findViewById(R.id.confirm); + + mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID) + : null; + if (mRowId == null) { + Bundle extras = getIntent().getExtras(); + mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID) + : null; + } + + populateFields(); + + confirmButton.setOnClickListener(new View.OnClickListener() { + + public void onClick(View view) { + setResult(RESULT_OK); + finish(); + } + + }); + } + + private void populateFields() { + if (mRowId != null) { + Cursor note = mDbHelper.fetchNote(mRowId); + startManagingCursor(note); + mTitleText.setText(note.getString( + note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE))); + mBodyText.setText(note.getString( + note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY))); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putLong(NotesDbAdapter.KEY_ROWID, mRowId); + } + + @Override + protected void onPause() { + super.onPause(); + saveState(); + } + + @Override + protected void onResume() { + super.onResume(); + populateFields(); + } + + private void saveState() { + String title = mTitleText.getText().toString(); + String body = mBodyText.getText().toString(); + + if (mRowId == null) { + long id = mDbHelper.createNote(title, body); + if (id > 0) { + mRowId = id; + } + } else { + mDbHelper.updateNote(mRowId, title, body); + } + } + +} diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/Notepadv3.java b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/Notepadv3.java new file mode 100755 index 000000000..7f9903750 --- /dev/null +++ b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/Notepadv3.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License")savedInstanceState; + * 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.android.demo.notepad3; + +import android.app.ListActivity; +import android.content.Intent; +import android.database.Cursor; +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import android.widget.ListView; +import android.widget.SimpleCursorAdapter; +import android.widget.AdapterView.AdapterContextMenuInfo; + +public class Notepadv3 extends ListActivity { + private static final int ACTIVITY_CREATE=0; + private static final int ACTIVITY_EDIT=1; + + private static final int INSERT_ID = Menu.FIRST; + private static final int DELETE_ID = Menu.FIRST + 1; + + private NotesDbAdapter mDbHelper; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.notes_list); + mDbHelper = new NotesDbAdapter(this); + mDbHelper.open(); + fillData(); + registerForContextMenu(getListView()); + } + + private void fillData() { + Cursor notesCursor = mDbHelper.fetchAllNotes(); + startManagingCursor(notesCursor); + + // Create an array to specify the fields we want to display in the list (only TITLE) + String[] from = new String[]{NotesDbAdapter.KEY_TITLE}; + + // and an array of the fields we want to bind those fields to (in this case just text1) + int[] to = new int[]{R.id.text1}; + + // Now create a simple cursor adapter and set it to display + SimpleCursorAdapter notes = + new SimpleCursorAdapter(this, R.layout.notes_row, notesCursor, from, to); + setListAdapter(notes); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + menu.add(0, INSERT_ID, 0, R.string.menu_insert); + return true; + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + switch(item.getItemId()) { + case INSERT_ID: + createNote(); + return true; + } + + return super.onMenuItemSelected(featureId, item); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + menu.add(0, DELETE_ID, 0, R.string.menu_delete); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + switch(item.getItemId()) { + case DELETE_ID: + AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); + mDbHelper.deleteNote(info.id); + fillData(); + return true; + } + return super.onContextItemSelected(item); + } + + private void createNote() { + Intent i = new Intent(this, NoteEdit.class); + startActivityForResult(i, ACTIVITY_CREATE); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + super.onListItemClick(l, v, position, id); + Intent i = new Intent(this, NoteEdit.class); + i.putExtra(NotesDbAdapter.KEY_ROWID, id); + startActivityForResult(i, ACTIVITY_EDIT); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, + Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + fillData(); + } +} diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NotesDbAdapter.java b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NotesDbAdapter.java new file mode 100755 index 000000000..61ad04623 --- /dev/null +++ b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NotesDbAdapter.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * 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.android.demo.notepad3; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +/** + * Simple notes database access helper class. Defines the basic CRUD operations + * for the notepad example, and gives the ability to list all notes as well as + * retrieve or modify a specific note. + * + * This has been improved from the first version of this tutorial through the + * addition of better error handling and also using returning a Cursor instead + * of using a collection of inner classes (which is less scalable and not + * recommended). + */ +public class NotesDbAdapter { + + public static final String KEY_TITLE = "title"; + public static final String KEY_BODY = "body"; + public static final String KEY_ROWID = "_id"; + + private static final String TAG = "NotesDbAdapter"; + private DatabaseHelper mDbHelper; + private SQLiteDatabase mDb; + + /** + * Database creation sql statement + */ + private static final String DATABASE_CREATE = + "create table notes (_id integer primary key autoincrement, " + + "title text not null, body text not null);"; + + private static final String DATABASE_NAME = "data"; + private static final String DATABASE_TABLE = "notes"; + private static final int DATABASE_VERSION = 2; + + private final Context mCtx; + + private static class DatabaseHelper extends SQLiteOpenHelper { + + DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + + db.execSQL(DATABASE_CREATE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + + newVersion + ", which will destroy all old data"); + db.execSQL("DROP TABLE IF EXISTS notes"); + onCreate(db); + } + } + + /** + * Constructor - takes the context to allow the database to be + * opened/created + * + * @param ctx the Context within which to work + */ + public NotesDbAdapter(Context ctx) { + this.mCtx = ctx; + } + + /** + * Open the notes database. If it cannot be opened, try to create a new + * instance of the database. If it cannot be created, throw an exception to + * signal the failure + * + * @return this (self reference, allowing this to be chained in an + * initialization call) + * @throws SQLException if the database could be neither opened or created + */ + public NotesDbAdapter open() throws SQLException { + mDbHelper = new DatabaseHelper(mCtx); + mDb = mDbHelper.getWritableDatabase(); + return this; + } + + public void close() { + mDbHelper.close(); + } + + + /** + * Create a new note using the title and body provided. If the note is + * successfully created return the new rowId for that note, otherwise return + * a -1 to indicate failure. + * + * @param title the title of the note + * @param body the body of the note + * @return rowId or -1 if failed + */ + public long createNote(String title, String body) { + ContentValues initialValues = new ContentValues(); + initialValues.put(KEY_TITLE, title); + initialValues.put(KEY_BODY, body); + + return mDb.insert(DATABASE_TABLE, null, initialValues); + } + + /** + * Delete the note with the given rowId + * + * @param rowId id of note to delete + * @return true if deleted, false otherwise + */ + public boolean deleteNote(long rowId) { + + return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0; + } + + /** + * Return a Cursor over the list of all notes in the database + * + * @return Cursor over all notes + */ + public Cursor fetchAllNotes() { + + return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE, + KEY_BODY}, null, null, null, null, null); + } + + /** + * Return a Cursor positioned at the note that matches the given rowId + * + * @param rowId id of note to retrieve + * @return Cursor positioned to matching note, if found + * @throws SQLException if note could not be found/retrieved + */ + public Cursor fetchNote(long rowId) throws SQLException { + + Cursor mCursor = + + mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID, + KEY_TITLE, KEY_BODY}, KEY_ROWID + "=" + rowId, null, + null, null, null, null); + if (mCursor != null) { + mCursor.moveToFirst(); + } + return mCursor; + + } + + /** + * Update the note using the details provided. The note to be updated is + * specified using the rowId, and it is altered to use the title and body + * values passed in + * + * @param rowId id of note to update + * @param title value to set note title to + * @param body value to set note body to + * @return true if the note was successfully updated, false otherwise + */ + public boolean updateNote(long rowId, String title, String body) { + ContentValues args = new ContentValues(); + args.put(KEY_TITLE, title); + args.put(KEY_BODY, body); + + return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0; + } +} -- cgit v1.2.3 From 7c21d1cd4051a7aaab5c15d63649315708c6aaa4 Mon Sep 17 00:00:00 2001 From: Tom O'Neill Date: Fri, 11 Dec 2009 16:01:04 -0800 Subject: Fix Notepadv3Solution orientation changes during NoteEdit The current NoteEdit.java in Notepadv3Solution crashes on NPE when the screen orientation changes. This happens because a null Long is auto-unboxed to a long when used as an input to Bundle.putLong(String, long). The easiest solution is to use Bundle.putSerializable() instead. In addition duplicate notepad entries could result because mRowId was not necessarily defined when onSaveInstanceState(Bundle) was called. The solution to that is to call saveState() in that method. Fixes these buganizer bugs: Change-Id: Ice325f3b089867e4716deb48aefe8ec03f30ad55 http://b/issue?id=2266994 http://b/issue?id=2266962 --- .../Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'tutorials/NotepadCodeLab/Notepadv3Solution/src') diff --git a/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java index 710ea339d..f5eb6c433 100755 --- a/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java +++ b/tutorials/NotepadCodeLab/Notepadv3Solution/src/com/android/demo/notepad3/NoteEdit.java @@ -43,8 +43,8 @@ public class NoteEdit extends Activity { Button confirmButton = (Button) findViewById(R.id.confirm); - mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID) - : null; + mRowId = (savedInstanceState == null) ? null : + (Long) savedInstanceState.getSerializable(NotesDbAdapter.KEY_ROWID); if (mRowId == null) { Bundle extras = getIntent().getExtras(); mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID) @@ -77,7 +77,8 @@ public class NoteEdit extends Activity { @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putLong(NotesDbAdapter.KEY_ROWID, mRowId); + saveState(); + outState.putSerializable(NotesDbAdapter.KEY_ROWID, mRowId); } @Override -- cgit v1.2.3