diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:29:09 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:29:09 -0800 |
| commit | 52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98 (patch) | |
| tree | 2eb01432fa3da41550dd622d29fb672509122b51 /samples/NotePad | |
| parent | d4aee0c0caa00aa02d4c50ed28151591ac0456b5 (diff) | |
| download | android_development-52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98.tar.gz android_development-52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98.tar.bz2 android_development-52d4c30ca52320ec92d1d1ddc8db3f07f69c4f98.zip | |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'samples/NotePad')
19 files changed, 1417 insertions, 0 deletions
diff --git a/samples/NotePad/Android.mk b/samples/NotePad/Android.mk new file mode 100644 index 000000000..793921270 --- /dev/null +++ b/samples/NotePad/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := samples + +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := NotePad + +LOCAL_SDK_VERSION := current + +include $(BUILD_PACKAGE) + +# Use the following include to make our test apk. +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/samples/NotePad/AndroidManifest.xml b/samples/NotePad/AndroidManifest.xml new file mode 100644 index 000000000..5c56dafa9 --- /dev/null +++ b/samples/NotePad/AndroidManifest.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2007 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. +--> + +<!-- Declare the contents of this Android application. The namespace + attribute brings in the Android platform namespace, and the package + supplies a unique name for the application. When writing your + own application, the package name must be changed from "com.example.*" + to come from a domain that you own or have control over. --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.notepad" +> + <application android:icon="@drawable/app_notes" + android:label="@string/app_name" + > + <provider android:name="NotePadProvider" + android:authorities="com.google.provider.NotePad" + /> + + <activity android:name="NotesList" android:label="@string/title_notes_list"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + <action android:name="android.intent.action.EDIT" /> + <action android:name="android.intent.action.PICK" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> + </intent-filter> + <intent-filter> + <action android:name="android.intent.action.GET_CONTENT" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> + </intent-filter> + </activity> + + <activity android:name="NoteEditor" + android:theme="@android:style/Theme.Light" + android:label="@string/title_note" + android:screenOrientation="sensor" + android:configChanges="keyboardHidden|orientation" + > + <!-- This filter says that we can view or edit the data of + a single note --> + <intent-filter android:label="@string/resolve_edit"> + <action android:name="android.intent.action.VIEW" /> + <action android:name="android.intent.action.EDIT" /> + <action android:name="com.android.notepad.action.EDIT_NOTE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> + </intent-filter> + + <!-- This filter says that we can create a new note inside + of a directory of notes. --> + <intent-filter> + <action android:name="android.intent.action.INSERT" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" /> + </intent-filter> + + </activity> + + <activity android:name="TitleEditor" android:label="@string/title_edit_title" + android:theme="@android:style/Theme.Dialog" + android:windowSoftInputMode="stateVisible"> + <!-- This activity implements an alternative action that can be + performed on notes: editing their title. It can be used as + a default operation if the user invokes this action, and is + available as an alternative action for any note data. --> + <intent-filter android:label="@string/resolve_title"> + <!-- This is the action we perform. It is a custom action we + define for our application, not a generic VIEW or EDIT + action since we are not a general note viewer/editor. --> + <action android:name="com.android.notepad.action.EDIT_TITLE" /> + <!-- DEFAULT: execute if being directly invoked. --> + <category android:name="android.intent.category.DEFAULT" /> + <!-- ALTERNATIVE: show as an alternative action when the user is + working with this type of data. --> + <category android:name="android.intent.category.ALTERNATIVE" /> + <!-- SELECTED_ALTERNATIVE: show as an alternative action the user + can perform when selecting this type of data. --> + <category android:name="android.intent.category.SELECTED_ALTERNATIVE" /> + <!-- This is the data type we operate on. --> + <data android:mimeType="vnd.android.cursor.item/vnd.google.note" /> + </intent-filter> + </activity> + + </application> +</manifest> + diff --git a/samples/NotePad/_index.html b/samples/NotePad/_index.html new file mode 100644 index 000000000..2a1896962 --- /dev/null +++ b/samples/NotePad/_index.html @@ -0,0 +1,12 @@ +<p>A simple note pad application. +It demonstrates... +<ul> +<li>using views +<li>accessing a database +<li>using an intent to open a new window +<li>managing activity lifecycle +<li>and many other goodies... +</ul> +</p> +<img alt="Note Pad Example" class="gallery" src="sample_notepad.png" > +<img alt="Note Pad Example" class="gallery" src="sample_note.png" > diff --git a/samples/NotePad/res/drawable/app_notes.png b/samples/NotePad/res/drawable/app_notes.png Binary files differnew file mode 100644 index 000000000..0479138d1 --- /dev/null +++ b/samples/NotePad/res/drawable/app_notes.png diff --git a/samples/NotePad/res/layout/note_editor.xml b/samples/NotePad/res/layout/note_editor.xml new file mode 100644 index 000000000..c54a96308 --- /dev/null +++ b/samples/NotePad/res/layout/note_editor.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2007 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. +--> + +<view xmlns:android="http://schemas.android.com/apk/res/android" + class="com.example.android.notepad.NoteEditor$LinedEditText" + android:id="@+id/note" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@android:color/transparent" + android:padding="5dip" + android:scrollbars="vertical" + android:fadingEdge="vertical" + android:gravity="top" + android:textSize="22sp" + android:capitalize="sentences" +/> diff --git a/samples/NotePad/res/layout/noteslist_item.xml b/samples/NotePad/res/layout/noteslist_item.xml new file mode 100644 index 000000000..b16773416 --- /dev/null +++ b/samples/NotePad/res/layout/noteslist_item.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2006 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. +--> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/text1" + android:layout_width="fill_parent" + android:layout_height="?android:attr/listPreferredItemHeight" + android:textAppearance="?android:attr/textAppearanceLarge" + android:gravity="center_vertical" + android:paddingLeft="5dip" + android:singleLine="true" +/> diff --git a/samples/NotePad/res/layout/title_editor.xml b/samples/NotePad/res/layout/title_editor.xml new file mode 100644 index 000000000..3593ec6d7 --- /dev/null +++ b/samples/NotePad/res/layout/title_editor.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2007 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingLeft="6dip" + android:paddingRight="6dip" + android:paddingBottom="3dip"> + + <EditText android:id="@+id/title" + android:maxLines="1" + android:layout_marginTop="2dip" + android:layout_width="wrap_content" + android:ems="25" + android:layout_height="wrap_content" + android:autoText="true" + android:capitalize="sentences" + android:scrollHorizontally="true" /> + + <Button android:id="@+id/ok" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="right" + android:text="@string/button_ok" /> + +</LinearLayout> diff --git a/samples/NotePad/res/values/strings.xml b/samples/NotePad/res/values/strings.xml new file mode 100644 index 000000000..b4bf671e8 --- /dev/null +++ b/samples/NotePad/res/values/strings.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2007 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. +--> + +<resources> + <string name="menu_delete">Delete</string> + <string name="menu_insert">Add note</string> + <string name="menu_revert">Revert</string> + <string name="menu_discard">Discard</string> + + <string name="resolve_edit">Edit note</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="button_ok">OK</string> + + <string name="error_title">Error</string> + <string name="error_message">Error loading note</string> +</resources> diff --git a/samples/NotePad/sample_note.png b/samples/NotePad/sample_note.png Binary files differnew file mode 100644 index 000000000..cea1a0843 --- /dev/null +++ b/samples/NotePad/sample_note.png diff --git a/samples/NotePad/sample_notepad.png b/samples/NotePad/sample_notepad.png Binary files differnew file mode 100644 index 000000000..c498847d2 --- /dev/null +++ b/samples/NotePad/sample_notepad.png diff --git a/samples/NotePad/src/com/example/android/notepad/NoteEditor.java b/samples/NotePad/src/com/example/android/notepad/NoteEditor.java new file mode 100644 index 000000000..e45efd865 --- /dev/null +++ b/samples/NotePad/src/com/example/android/notepad/NoteEditor.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2007 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.Notes; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.net.Uri; +import android.os.Bundle; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.EditText; + +/** + * A generic activity for editing a note in a database. This can be used + * either to simply view a note {@link Intent#ACTION_VIEW}, view and edit a note + * {@link Intent#ACTION_EDIT}, or create a new note {@link Intent#ACTION_INSERT}. + */ +public class NoteEditor extends Activity { + private static final String TAG = "Notes"; + + /** + * Standard projection for the interesting columns of a normal note. + */ + private static final String[] PROJECTION = new String[] { + Notes._ID, // 0 + Notes.NOTE, // 1 + }; + /** The index of the note column */ + private static final int COLUMN_INDEX_NOTE = 1; + + // This is our state data that is stored when freezing. + private static final String ORIGINAL_CONTENT = "origContent"; + + // Identifiers for our menu items. + 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; + + // The different distinct states the activity can be run in. + private static final int STATE_EDIT = 0; + private static final int STATE_INSERT = 1; + + private int mState; + private boolean mNoteOnly = false; + private Uri mUri; + private Cursor mCursor; + private EditText mText; + private String mOriginalContent; + + /** + * A custom EditText that draws lines between each line of text that is displayed. + */ + public static class LinedEditText extends EditText { + private Rect mRect; + private Paint mPaint; + + // we need this constructor for LayoutInflater + public LinedEditText(Context context, AttributeSet attrs) { + super(context, attrs); + + mRect = new Rect(); + mPaint = new Paint(); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setColor(0x800000FF); + } + + @Override + protected void onDraw(Canvas canvas) { + int count = getLineCount(); + Rect r = mRect; + Paint paint = mPaint; + + for (int i = 0; i < count; i++) { + int baseline = getLineBounds(i, r); + + canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint); + } + + super.onDraw(canvas); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Intent intent = getIntent(); + + // Do some setup based on the action being performed. + + final String action = intent.getAction(); + if (Intent.ACTION_EDIT.equals(action)) { + // Requested to edit: set that state, and the data being edited. + mState = STATE_EDIT; + mUri = intent.getData(); + } else if (Intent.ACTION_INSERT.equals(action)) { + // Requested to insert: set that state, and create a new entry + // in the container. + mState = STATE_INSERT; + mUri = getContentResolver().insert(intent.getData(), null); + + // If we were unable to create a new note, then just finish + // this activity. A RESULT_CANCELED will be sent back to the + // original activity if they requested a result. + if (mUri == null) { + Log.e(TAG, "Failed to insert new note into " + getIntent().getData()); + finish(); + return; + } + + // The new entry was created, so assume all will end well and + // set the result to be returned. + setResult(RESULT_OK, (new Intent()).setAction(mUri.toString())); + + } else { + // Whoops, unknown action! Bail. + Log.e(TAG, "Unknown action, exiting"); + finish(); + return; + } + + // Set the layout for this activity. You can find it in res/layout/note_editor.xml + setContentView(R.layout.note_editor); + + // The text view for our note, identified by its ID in the XML file. + mText = (EditText) findViewById(R.id.note); + + // Get the note! + mCursor = managedQuery(mUri, PROJECTION, null, null, null); + + // If an instance of this activity had previously stopped, we can + // get the original text it started with. + if (savedInstanceState != null) { + mOriginalContent = savedInstanceState.getString(ORIGINAL_CONTENT); + } + } + + @Override + protected void onResume() { + super.onResume(); + + // If we didn't have any trouble retrieving the data, it is now + // time to get at the stuff. + if (mCursor != null) { + // Make sure we are at the one and only row in the cursor. + mCursor.moveToFirst(); + + // Modify our overall title depending on the mode we are running in. + if (mState == STATE_EDIT) { + setTitle(getText(R.string.title_edit)); + } else if (mState == STATE_INSERT) { + setTitle(getText(R.string.title_create)); + } + + // This is a little tricky: we may be resumed after previously being + // paused/stopped. We want to put the new text in the text view, + // but leave the user where they were (retain the cursor position + // etc). This version of setText does that for us. + String note = mCursor.getString(COLUMN_INDEX_NOTE); + mText.setTextKeepState(note); + + // If we hadn't previously retrieved the original text, do so + // now. This allows the user to revert their changes. + if (mOriginalContent == null) { + mOriginalContent = note; + } + + } else { + setTitle(getText(R.string.error_title)); + mText.setText(getText(R.string.error_message)); + } + } + + @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. + outState.putString(ORIGINAL_CONTENT, mOriginalContent); + } + + @Override + protected void onPause() { + super.onPause(); + + // The user is going somewhere else, so make sure their current + // changes are safely saved away in the provider. We don't need + // to do this if only editing. + if (mCursor != null) { + String text = mText.getText().toString(); + int length = text.length(); + + // If this activity is finished, and there is no text, then we + // do something a little special: simply delete the note entry. + // Note that we do this both for editing and inserting... it + // would be reasonable to only do it when inserting. + if (isFinishing() && (length == 0) && !mNoteOnly) { + setResult(RESULT_CANCELED); + deleteNote(); + + // Get out updates into the provider. + } else { + ContentValues values = new ContentValues(); + + // This stuff is only done when working with a full-fledged note. + if (!mNoteOnly) { + // Bump the modification time to now. + values.put(Notes.MODIFIED_DATE, System.currentTimeMillis()); + + // If we are creating a new note, then we want to also create + // an initial title for it. + if (mState == STATE_INSERT) { + String title = text.substring(0, Math.min(30, length)); + if (length > 30) { + int lastSpace = title.lastIndexOf(' '); + if (lastSpace > 0) { + title = title.substring(0, lastSpace); + } + } + values.put(Notes.TITLE, title); + } + } + + // Write our text back into the provider. + values.put(Notes.NOTE, text); + + // Commit all of our changes to persistent storage. When the update completes + // the content provider will notify the cursor of the change, which will + // cause the UI to be updated. + getContentResolver().update(mUri, values, null, null); + } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + // Build the menus that are shown when editing. + if (mState == STATE_EDIT) { + menu.add(0, REVERT_ID, 0, R.string.menu_revert) + .setShortcut('0', 'r') + .setIcon(android.R.drawable.ic_menu_revert); + if (!mNoteOnly) { + menu.add(0, DELETE_ID, 0, R.string.menu_delete) + .setShortcut('1', 'd') + .setIcon(android.R.drawable.ic_menu_delete); + } + + // Build the menus that are shown when inserting. + } else { + menu.add(0, DISCARD_ID, 0, R.string.menu_discard) + .setShortcut('0', 'd') + .setIcon(android.R.drawable.ic_menu_delete); + } + + // If we are working on a full note, then append to the + // menu items for any other activities that can do stuff with it + // as well. This does a query on the system for any activities that + // implement the ALTERNATIVE_ACTION for our data, adding a menu item + // for each one that is found. + if (!mNoteOnly) { + Intent intent = new Intent(null, getIntent().getData()); + intent.addCategory(Intent.CATEGORY_ALTERNATIVE); + menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0, + new ComponentName(this, NoteEditor.class), null, intent, 0, null); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle all of the possible menu actions. + switch (item.getItemId()) { + case DELETE_ID: + deleteNote(); + finish(); + break; + case DISCARD_ID: + cancelNote(); + break; + case REVERT_ID: + cancelNote(); + break; + } + return super.onOptionsItemSelected(item); + } + + /** + * Take care of canceling work on a note. Deletes the note if we + * had created it, otherwise reverts to the original text. + */ + 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(Notes.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(); + } + + /** + * 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(""); + } + } +} diff --git a/samples/NotePad/src/com/example/android/notepad/NotePad.java b/samples/NotePad/src/com/example/android/notepad/NotePad.java new file mode 100644 index 000000000..25be23e6e --- /dev/null +++ b/samples/NotePad/src/com/example/android/notepad/NotePad.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2007 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 android.net.Uri; +import android.provider.BaseColumns; + +/** + * Convenience definitions for NotePadProvider + */ +public final class NotePad { + public static final String AUTHORITY = "com.google.provider.NotePad"; + + // This class cannot be instantiated + private NotePad() {} + + /** + * Notes table + */ + public static final class Notes implements BaseColumns { + // This class cannot be instantiated + private Notes() {} + + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes"); + + /** + * The MIME type of {@link #CONTENT_URI} providing a directory of notes. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.google.note"; + + /** + * The MIME type of a {@link #CONTENT_URI} sub-directory of a single note. + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.note"; + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "modified DESC"; + + /** + * The title of the note + * <P>Type: TEXT</P> + */ + public static final String TITLE = "title"; + + /** + * The note itself + * <P>Type: TEXT</P> + */ + public static final String NOTE = "note"; + + /** + * The timestamp for when the note was created + * <P>Type: INTEGER (long from System.curentTimeMillis())</P> + */ + public static final String CREATED_DATE = "created"; + + /** + * The timestamp for when the note was last modified + * <P>Type: INTEGER (long from System.curentTimeMillis())</P> + */ + public static final String MODIFIED_DATE = "modified"; + } +} diff --git a/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java b/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java new file mode 100644 index 000000000..f1d3fdc51 --- /dev/null +++ b/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2007 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.Notes; + +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.UriMatcher; +import android.content.res.Resources; +import android.database.Cursor; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.text.TextUtils; +import android.util.Log; + +import java.util.HashMap; + +/** + * Provides access to a database of notes. Each note has a title, the note + * itself, a creation date and a modified data. + */ +public class NotePadProvider extends ContentProvider { + + private static final String TAG = "NotePadProvider"; + + private static final String DATABASE_NAME = "note_pad.db"; + private static final int DATABASE_VERSION = 2; + private static final String NOTES_TABLE_NAME = "notes"; + + private static HashMap<String, String> sNotesProjectionMap; + + private static final int NOTES = 1; + private static final int NOTE_ID = 2; + + private static final UriMatcher sUriMatcher; + + /** + * This class helps open, create, and upgrade the database file. + */ + private static class DatabaseHelper extends SQLiteOpenHelper { + + DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " (" + + Notes._ID + " INTEGER PRIMARY KEY," + + Notes.TITLE + " TEXT," + + Notes.NOTE + " TEXT," + + Notes.CREATED_DATE + " INTEGER," + + Notes.MODIFIED_DATE + " INTEGER" + + ");"); + } + + @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); + } + } + + private DatabaseHelper mOpenHelper; + + @Override + public boolean onCreate() { + mOpenHelper = new DatabaseHelper(getContext()); + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + + switch (sUriMatcher.match(uri)) { + case NOTES: + qb.setTables(NOTES_TABLE_NAME); + qb.setProjectionMap(sNotesProjectionMap); + break; + + case NOTE_ID: + qb.setTables(NOTES_TABLE_NAME); + qb.setProjectionMap(sNotesProjectionMap); + qb.appendWhere(Notes._ID + "=" + uri.getPathSegments().get(1)); + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + // If no sort order is specified use the default + String orderBy; + if (TextUtils.isEmpty(sortOrder)) { + orderBy = NotePad.Notes.DEFAULT_SORT_ORDER; + } else { + orderBy = sortOrder; + } + + // Get the database and run the query + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); + + // Tell the cursor what uri to watch, so it knows when its source data changes + c.setNotificationUri(getContext().getContentResolver(), uri); + return c; + } + + @Override + public String getType(Uri uri) { + switch (sUriMatcher.match(uri)) { + case NOTES: + return Notes.CONTENT_TYPE; + + case NOTE_ID: + return Notes.CONTENT_ITEM_TYPE; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + } + + @Override + public Uri insert(Uri uri, ContentValues initialValues) { + // Validate the requested uri + if (sUriMatcher.match(uri) != NOTES) { + throw new IllegalArgumentException("Unknown URI " + uri); + } + + ContentValues values; + if (initialValues != null) { + values = new ContentValues(initialValues); + } else { + values = new ContentValues(); + } + + Long now = Long.valueOf(System.currentTimeMillis()); + + // Make sure that the fields are all set + if (values.containsKey(NotePad.Notes.CREATED_DATE) == false) { + values.put(NotePad.Notes.CREATED_DATE, now); + } + + if (values.containsKey(NotePad.Notes.MODIFIED_DATE) == false) { + values.put(NotePad.Notes.MODIFIED_DATE, now); + } + + if (values.containsKey(NotePad.Notes.TITLE) == false) { + Resources r = Resources.getSystem(); + values.put(NotePad.Notes.TITLE, r.getString(android.R.string.untitled)); + } + + if (values.containsKey(NotePad.Notes.NOTE) == false) { + values.put(NotePad.Notes.NOTE, ""); + } + + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values); + if (rowId > 0) { + Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId); + getContext().getContentResolver().notifyChange(noteUri, null); + return noteUri; + } + + throw new SQLException("Failed to insert row into " + uri); + } + + @Override + public int delete(Uri uri, String where, String[] whereArgs) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + int count; + switch (sUriMatcher.match(uri)) { + case NOTES: + count = db.delete(NOTES_TABLE_NAME, where, whereArgs); + break; + + case NOTE_ID: + String noteId = uri.getPathSegments().get(1); + count = db.delete(NOTES_TABLE_NAME, Notes._ID + "=" + noteId + + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + getContext().getContentResolver().notifyChange(uri, null); + return count; + } + + @Override + public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + int count; + switch (sUriMatcher.match(uri)) { + case NOTES: + count = db.update(NOTES_TABLE_NAME, values, where, whereArgs); + break; + + case NOTE_ID: + String noteId = uri.getPathSegments().get(1); + count = db.update(NOTES_TABLE_NAME, values, Notes._ID + "=" + noteId + + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs); + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + } + + getContext().getContentResolver().notifyChange(uri, null); + return count; + } + + static { + sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES); + sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID); + + sNotesProjectionMap = new HashMap<String, String>(); + sNotesProjectionMap.put(Notes._ID, Notes._ID); + sNotesProjectionMap.put(Notes.TITLE, Notes.TITLE); + sNotesProjectionMap.put(Notes.NOTE, Notes.NOTE); + sNotesProjectionMap.put(Notes.CREATED_DATE, Notes.CREATED_DATE); + sNotesProjectionMap.put(Notes.MODIFIED_DATE, Notes.MODIFIED_DATE); + } +} diff --git a/samples/NotePad/src/com/example/android/notepad/NotesList.java b/samples/NotePad/src/com/example/android/notepad/NotesList.java new file mode 100644 index 000000000..ceaaa3c76 --- /dev/null +++ b/samples/NotePad/src/com/example/android/notepad/NotesList.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2007 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.Notes; + +import android.app.ListActivity; +import android.content.ComponentName; +import android.content.ContentUris; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.view.ContextMenu; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.SimpleCursorAdapter; + +/** + * Displays a list of notes. Will display notes from the {@link Uri} + * provided in the intent if there is one, otherwise defaults to displaying the + * contents of the {@link NotePadProvider} + */ +public class NotesList extends ListActivity { + private static final String TAG = "NotesList"; + + // Menu item ids + public static final int MENU_ITEM_DELETE = Menu.FIRST; + public static final int MENU_ITEM_INSERT = Menu.FIRST + 1; + + /** + * The columns we are interested in from the database + */ + private static final String[] PROJECTION = new String[] { + Notes._ID, // 0 + Notes.TITLE, // 1 + }; + + /** The index of the title column */ + private static final int COLUMN_INDEX_TITLE = 1; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT); + + // If no data was given in the intent (because we were started + // as a MAIN activity), then use our default content provider. + Intent intent = getIntent(); + if (intent.getData() == null) { + intent.setData(Notes.CONTENT_URI); + } + + // Inform the list we provide context menus for items + getListView().setOnCreateContextMenuListener(this); + + // Perform a managed query. The Activity will handle closing and requerying the cursor + // when needed. + Cursor cursor = managedQuery(getIntent().getData(), PROJECTION, null, null, + Notes.DEFAULT_SORT_ORDER); + + // Used to map notes entries from the database to views + SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.noteslist_item, cursor, + new String[] { Notes.TITLE }, new int[] { android.R.id.text1 }); + setListAdapter(adapter); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + // This is our one standard application action -- inserting a + // new note into the list. + menu.add(0, MENU_ITEM_INSERT, 0, R.string.menu_insert) + .setShortcut('3', 'a') + .setIcon(android.R.drawable.ic_menu_add); + + // Generate any additional actions that can be performed on the + // overall list. In a normal install, there are no additional + // actions found here, but this allows other applications to extend + // our menu with their own actions. + Intent intent = new Intent(null, getIntent().getData()); + intent.addCategory(Intent.CATEGORY_ALTERNATIVE); + menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0, + new ComponentName(this, NotesList.class), null, intent, 0, null); + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + final boolean haveItems = getListAdapter().getCount() > 0; + + // If there are any notes in the list (which implies that one of + // them is selected), then we need to generate the actions that + // can be performed on the current selection. This will be a combination + // of our own specific actions along with any extensions that can be + // found. + if (haveItems) { + // This is the selected item. + Uri uri = ContentUris.withAppendedId(getIntent().getData(), getSelectedItemId()); + + // Build menu... always starts with the EDIT action... + Intent[] specifics = new Intent[1]; + specifics[0] = new Intent(Intent.ACTION_EDIT, uri); + MenuItem[] items = new MenuItem[1]; + + // ... is followed by whatever other actions are available... + Intent intent = new Intent(null, uri); + intent.addCategory(Intent.CATEGORY_ALTERNATIVE); + menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0, null, specifics, intent, 0, + items); + + // Give a shortcut to the edit action. + if (items[0] != null) { + items[0].setShortcut('1', 'e'); + } + } else { + menu.removeGroup(Menu.CATEGORY_ALTERNATIVE); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_ITEM_INSERT: + // Launch activity to insert a new item + startActivity(new Intent(Intent.ACTION_INSERT, getIntent().getData())); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { + AdapterView.AdapterContextMenuInfo info; + try { + info = (AdapterView.AdapterContextMenuInfo) menuInfo; + } catch (ClassCastException e) { + Log.e(TAG, "bad menuInfo", e); + return; + } + + Cursor cursor = (Cursor) getListAdapter().getItem(info.position); + if (cursor == null) { + // For some reason the requested item isn't available, do nothing + return; + } + + // Setup the menu header + menu.setHeaderTitle(cursor.getString(COLUMN_INDEX_TITLE)); + + // Add a menu item to delete the note + menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_delete); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterView.AdapterContextMenuInfo info; + try { + info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + } catch (ClassCastException e) { + Log.e(TAG, "bad menuInfo", e); + return false; + } + + switch (item.getItemId()) { + case MENU_ITEM_DELETE: { + // Delete the note that the context menu is for + Uri noteUri = ContentUris.withAppendedId(getIntent().getData(), info.id); + getContentResolver().delete(noteUri, null, null); + return true; + } + } + return false; + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Uri uri = ContentUris.withAppendedId(getIntent().getData(), id); + + String action = getIntent().getAction(); + if (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action)) { + // The caller is waiting for us to return a note selected by + // the user. The have clicked on one, so return it now. + setResult(RESULT_OK, new Intent().setData(uri)); + } else { + // Launch activity to view/edit the currently selected item + startActivity(new Intent(Intent.ACTION_EDIT, uri)); + } + } +} diff --git a/samples/NotePad/src/com/example/android/notepad/TitleEditor.java b/samples/NotePad/src/com/example/android/notepad/TitleEditor.java new file mode 100644 index 000000000..50d38e5f6 --- /dev/null +++ b/samples/NotePad/src/com/example/android/notepad/TitleEditor.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2007 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.Notes; + +import android.app.Activity; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +/** + * An activity that will edit the title of a note. Displays a floating + * window with a text field. + */ +public class TitleEditor extends Activity implements View.OnClickListener { + + /** + * This is a special intent action that means "edit the title of a note". + */ + public static final String EDIT_TITLE_ACTION = "com.android.notepad.action.EDIT_TITLE"; + + /** + * An array of the columns we are interested in. + */ + private static final String[] PROJECTION = new String[] { + NotePad.Notes._ID, // 0 + NotePad.Notes.TITLE, // 1 + }; + /** Index of the title column */ + private static final int COLUMN_INDEX_TITLE = 1; + + /** + * Cursor which will provide access to the note whose title we are editing. + */ + private Cursor mCursor; + + /** + * The EditText field from our UI. Keep track of this so we can extract the + * text when we are finished. + */ + private EditText mText; + + /** + * The content URI to the note that's being edited. + */ + private Uri mUri; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.title_editor); + + // Get the uri of the note whose title we want to edit + mUri = getIntent().getData(); + + // Get a cursor to access the note + mCursor = managedQuery(mUri, PROJECTION, null, null, null); + + // Set up click handlers for the text field and button + mText = (EditText) this.findViewById(R.id.title); + mText.setOnClickListener(this); + + Button b = (Button) findViewById(R.id.ok); + b.setOnClickListener(this); + } + + @Override + protected void onResume() { + super.onResume(); + + // Initialize the text with the title column from the cursor + if (mCursor != null) { + mCursor.moveToFirst(); + mText.setText(mCursor.getString(COLUMN_INDEX_TITLE)); + } + } + + @Override + protected void onPause() { + super.onPause(); + + if (mCursor != null) { + // Write the title back to the note + ContentValues values = new ContentValues(); + values.put(Notes.TITLE, mText.getText().toString()); + getContentResolver().update(mUri, values, null, null); + } + } + + public void onClick(View v) { + // When the user clicks, just finish this activity. + // onPause will be called, and we save our data there. + finish(); + } +} diff --git a/samples/NotePad/src/com/google/provider/NotePad.java b/samples/NotePad/src/com/google/provider/NotePad.java new file mode 100644 index 000000000..f8de69b6b --- /dev/null +++ b/samples/NotePad/src/com/google/provider/NotePad.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2007 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.google.provider; + +import android.net.Uri; +import android.provider.BaseColumns; + +/** + * Convenience definitions for NotePadProvider + */ +public final class NotePad { + /** + * Notes table + */ + public static final class Notes implements BaseColumns { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI + = Uri.parse("content://com.google.provider.NotePad/notes"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "modified DESC"; + + /** + * The title of the note + * <P>Type: TEXT</P> + */ + public static final String TITLE = "title"; + + /** + * The note itself + * <P>Type: TEXT</P> + */ + public static final String NOTE = "note"; + + /** + * The timestamp for when the note was created + * <P>Type: INTEGER (long)</P> + */ + public static final String CREATED_DATE = "created"; + + /** + * The timestamp for when the note was last modified + * <P>Type: INTEGER (long)</P> + */ + public static final String MODIFIED_DATE = "modified"; + } +} diff --git a/samples/NotePad/tests/Android.mk b/samples/NotePad/tests/Android.mk new file mode 100644 index 000000000..43efafcf0 --- /dev/null +++ b/samples/NotePad/tests/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_JAVA_LIBRARIES := android.test.runner + +LOCAL_PACKAGE_NAME := NotePadTests + +LOCAL_MODULE_TAGS := tests + +LOCAL_INSTRUMENTATION_FOR := NotePad + +include $(BUILD_PACKAGE) diff --git a/samples/NotePad/tests/AndroidManifest.xml b/samples/NotePad/tests/AndroidManifest.xml new file mode 100644 index 000000000..afd502b5a --- /dev/null +++ b/samples/NotePad/tests/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.notepad.tests"> + + <!-- We add an application tag here just so that we can indicate that + this package needs to link against the android.test library, + which is needed when building test cases. --> + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.example.android.notepad" + android:label="NotePad sample tests"> + </instrumentation> + +</manifest> diff --git a/samples/NotePad/tests/src/com/example/android/notepad/NotePadTest.java b/samples/NotePad/tests/src/com/example/android/notepad/NotePadTest.java new file mode 100644 index 000000000..452c5991c --- /dev/null +++ b/samples/NotePad/tests/src/com/example/android/notepad/NotePadTest.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 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 android.test.ActivityInstrumentationTestCase; + +import com.example.android.notepad.NotesList; + +/** + * Make sure that the main launcher activity opens up properly, which will be + * verified by {@link ActivityTestCase#testActivityTestCaseSetUpProperly}. + */ +public class NotePadTest extends ActivityInstrumentationTestCase<NotesList> { + + public NotePadTest() { + super("com.example.android.notepad", NotesList.class); + } + +} |
