Subject: Android application files for the Activity Testing Tutorial
Change-ID: If1b96a211487f43df76912bdbaf3568c1878478c
diff --git a/build/sdk.atree b/build/sdk.atree
index 8976116..be70974 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -98,6 +98,8 @@
 development/samples/SoftKeyboard             samples/${PLATFORM_NAME}/SoftKeyboard
 development/samples/JetBoy                   samples/${PLATFORM_NAME}/JetBoy
 development/samples/SearchableDictionary     samples/${PLATFORM_NAME}/SearchableDictionary
+development/samples/Spinner                  samples/${PlATFORM_NAME}/Spinner
+development/samples/SpinnerTest              samples/${PLATFORM_NAME}/SpinnerTest
 development/samples/ContactManager           samples/${PLATFORM_NAME}/ContactManager
 development/samples/MultiResolution          samples/${PLATFORM_NAME}/MultiResolution
 development/samples/Wiktionary               samples/${PLATFORM_NAME}/Wiktionary
diff --git a/samples/Spinner/AndroidManifest.xml b/samples/Spinner/AndroidManifest.xml
new file mode 100644
index 0000000..f1accf8
--- /dev/null
+++ b/samples/Spinner/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!--
+    Declares the contents of this Android application. The "namespace"
+    attribute brings in the Android platform namespace, and the
+    "package" attribute provides a unique Android name for the application.
+    If you use this file as a template in your own application, you must change
+    the package name from "com.android.example" to one that you own or have
+    control over.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="com.android.example.spinner"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <!--
+        Sets the application's user-readable label
+     -->
+    <application android:label="@string/app_name">
+        <!--
+            Sets the activity's name and label
+         -->
+        <activity android:name=".SpinnerActivity"
+                  android:label="@string/app_name">
+            <!--
+                This activity responds to MAIN and LAUNCHER intents
+             -->
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+    <!--
+        Requires a minimum platform version of Android-3 (SDK 1.5) to run
+    -->
+    <uses-sdk android:minSdkVersion="3"/>
+
+</manifest>
diff --git a/samples/Spinner/_index.html b/samples/Spinner/_index.html
new file mode 100644
index 0000000..2e50d9d
--- /dev/null
+++ b/samples/Spinner/_index.html
@@ -0,0 +1,22 @@
+<p>
+This sample is the application under test for the
+<a href="../../../resources/tutorials/testing/activity_test.html">Activity
+  Testing Tutorial</a>. It contains a single Activity that displays a
+Spinner widget backed by an array of strings containing the names of the planets
+in the Solar System. When an entry is selected from the Spinner, the entry is
+displayed in a text box.
+</p>
+<p>
+An important part of this application is state management. When the application
+is first run, the spinner widget displays a default selection of
+&quot;Earth&quot;. Thereafter, the application saves a selection as soon as it
+is made. The application remembers the selection from invocation to invocation, even
+if the device reboots.
+</p>
+<p>
+For more information about this application, see the Activity Testing Tutorial.
+The test application for this application is in the <a
+  href="../SpinnerTest/index.html">SpinnerTest</a> sample application.
+</p>
+
+<img alt="The Spinner application" src="../images/testing/spinner_main_screen.png" />
diff --git a/samples/Spinner/res/layout/main.xml b/samples/Spinner/res/layout/main.xml
new file mode 100644
index 0000000..3765b3a
--- /dev/null
+++ b/samples/Spinner/res/layout/main.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!--
+    Creates a Linear Layout View to contain the spinner
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+
+    <!--
+        Creates a spinner widget called Spinner01, within the Linear Layout
+        The prompt text comes from the string "planet_prompt" in strings.xml
+    -->
+    <Spinner 
+        android:id="@+id/Spinner01"
+        android:layout_height="wrap_content"
+        android:layout_width="fill_parent"
+        android:drawSelectorOnTop = "true"
+        android:prompt = "@string/planet_prompt">
+    </Spinner>
+
+    <!--
+        Creates a TextView called SpinnerResult, below the spinner.
+    -->
+    <TextView 
+        android:id="@+id/SpinnerResult" android:text="Result"
+        android:layout_height="fill_parent" android:textSize="10pt"
+        android:textStyle="bold" android:gravity="center"
+        android:layout_width="fill_parent">
+    </TextView>
+
+</LinearLayout>
diff --git a/samples/Spinner/res/values/strings.xml b/samples/Spinner/res/values/strings.xml
new file mode 100644
index 0000000..ae445f3
--- /dev/null
+++ b/samples/Spinner/res/values/strings.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!--
+    The string named "app_name" defines the application's visible name.
+    The string array "Planets" defines an array of 9 strings. The application loads
+    this array into the spinner's array adapter.
+    The string "planet_prompt" defines the prompt for the result text box.
+ -->
+<resources>
+    <string name="app_name">Spinner</string>
+    <string-array name="Planets">
+        <item>Mercury</item>
+        <item>Venus</item>
+        <item>Earth</item>
+        <item>Mars</item>
+        <item>Jupiter</item>
+        <item>Saturn</item>
+        <item>Uranus</item>
+        <item>Neptune</item>
+        <item>Pluto</item>
+    </string-array>
+    <string name="planet_prompt">Select a planet</string>
+</resources>
\ No newline at end of file
diff --git a/samples/Spinner/src/com/android/example/spinner/SpinnerActivity.java b/samples/Spinner/src/com/android/example/spinner/SpinnerActivity.java
new file mode 100644
index 0000000..fa4967c
--- /dev/null
+++ b/samples/Spinner/src/com/android/example/spinner/SpinnerActivity.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2010 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.android.example.spinner;
+
+import com.android.example.spinner.R;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+/**
+ * Displays an Android spinner widget backed by data in an array. The
+ * array is loaded from the strings.xml resources file.
+ */
+public class SpinnerActivity extends Activity {
+
+    /**
+     * Fields to contain the current position and display contents of the spinner
+     */
+    protected int mPos;
+    protected String mSelection;
+
+    /**
+     * ArrayAdapter connects the spinner widget to array-based data.
+     */
+    protected ArrayAdapter<CharSequence> mAdapter;
+
+    /**
+     *  The initial position of the spinner when it is first installed.
+     */
+    public static final int DEFAULT_POSITION = 2;
+
+    /**
+     * The name of a properties file that stores the position and
+     * selection when the activity is not loaded.
+     */
+    public static final String PREFERENCES_FILE = "SpinnerPrefs";
+
+    /**
+     * These values are used to read and write the properties file.
+     * PROPERTY_DELIMITER delimits the key and value in a Java properties file.
+     * The "marker" strings are used to write the properties into the file
+     */
+    public static final String PROPERTY_DELIMITER = "=";
+
+    /**
+     * The key or label for "position" in the preferences file
+     */
+    public static final String POSITION_KEY = "Position";
+
+    /**
+     * The key or label for "selection" in the preferences file
+     */
+    public static final String SELECTION_KEY = "Selection";
+
+    public static final String POSITION_MARKER =
+            POSITION_KEY + PROPERTY_DELIMITER;
+
+    public static final String SELECTION_MARKER =
+            SELECTION_KEY + PROPERTY_DELIMITER;
+
+    /**
+     * Initializes the application and the activity.
+     * 1) Sets the view
+     * 2) Reads the spinner's backing data from the string resources file
+     * 3) Instantiates a callback listener for handling selection from the
+     *    spinner
+     * Notice that this method includes code that can be uncommented to force
+     * tests to fail.
+     *
+     * This method overrides the default onCreate() method for an Activity.
+     *
+     * @see android.app.Activity#onCreate(android.os.Bundle)
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+
+        /**
+         * derived classes that use onCreate() overrides must always call the super constructor
+         */
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.main);
+
+        Spinner spinner = (Spinner) findViewById(R.id.Spinner01);
+
+        /*
+         * Create a backing mLocalAdapter for the Spinner from a list of the
+         * planets. The list is defined by XML in the strings.xml file.
+         */
+
+        this.mAdapter = ArrayAdapter.createFromResource(this, R.array.Planets,
+                android.R.layout.simple_spinner_dropdown_item);
+
+        /*
+         * Attach the mLocalAdapter to the spinner.
+         */
+
+        spinner.setAdapter(this.mAdapter);
+
+        /*
+         * Create a listener that is triggered when Android detects the
+         * user has selected an item in the Spinner.
+         */
+
+        OnItemSelectedListener spinnerListener = new myOnItemSelectedListener(this,this.mAdapter);
+
+        /*
+         * Attach the listener to the Spinner.
+         */
+
+        spinner.setOnItemSelectedListener(spinnerListener);
+
+
+        /*
+         * To demonstrate a failure in the preConditions test,
+         * uncomment the following line.
+         * The test will fail because the selection listener for the
+         * Spinner is not set.
+         */
+         // spinner.setOnItemSelectedListener(null);
+
+    }
+
+
+    /**
+     *  A callback listener that implements the
+     *  {@link android.widget.AdapterView.OnItemSelectedListener} interface
+     *  For views based on adapters, this interface defines the methods available
+     *  when the user selects an item from the View.
+     *
+     */
+    public class myOnItemSelectedListener implements OnItemSelectedListener {
+
+        /*
+         * provide local instances of the mLocalAdapter and the mLocalContext
+         */
+
+        ArrayAdapter<CharSequence> mLocalAdapter;
+        Activity mLocalContext;
+
+        /**
+         *  Constructor
+         *  @param c - The activity that displays the Spinner.
+         *  @param ad - The Adapter view that
+         *    controls the Spinner.
+         *  Instantiate a new listener object.
+         */
+        public myOnItemSelectedListener(Activity c, ArrayAdapter<CharSequence> ad) {
+
+          this.mLocalContext = c;
+          this.mLocalAdapter = ad;
+
+        }
+
+        /**
+         * When the user selects an item in the spinner, this method is invoked by the callback
+         * chain. Android calls the item selected listener for the spinner, which invokes the
+         * onItemSelected method.
+         *
+         * @see android.widget.AdapterView.OnItemSelectedListener#onItemSelected(
+         *  android.widget.AdapterView, android.view.View, int, long)
+         * @param parent - the AdapterView for this listener
+         * @param v - the View for this listener
+         * @param pos - the 0-based position of the selection in the mLocalAdapter
+         * @param row - the 0-based row number of the selection in the View
+         */
+        public void onItemSelected(AdapterView<?> parent, View v, int pos, long row) {
+
+            SpinnerActivity.this.mPos = pos;
+            SpinnerActivity.this.mSelection = parent.getItemAtPosition(pos).toString();
+            /*
+             * Set the value of the text field in the UI
+             */
+            TextView resultText = (TextView)findViewById(R.id.SpinnerResult);
+            resultText.setText(SpinnerActivity.this.mSelection);
+        }
+
+        /**
+         * The definition of OnItemSelectedListener requires an override
+         * of onNothingSelected(), even though this implementation does not use it.
+         * @param parent - The View for this Listener
+         */
+        public void onNothingSelected(AdapterView<?> parent) {
+
+            // do nothing
+
+        }
+    }
+
+    /**
+     * Restores the current state of the spinner (which item is selected, and the value
+     * of that item).
+     * Since onResume() is always called when an Activity is starting, even if it is re-displaying
+     * after being hidden, it is the best place to restore state.
+     *
+     * Attempts to read the state from a preferences file. If this read fails,
+     * assume it was just installed, so do an initialization. Regardless, change the
+     * state of the spinner to be the previous position.
+     *
+     * @see android.app.Activity#onResume()
+     */
+    @Override
+    public void onResume() {
+
+        /*
+         * an override to onResume() must call the super constructor first.
+         */
+
+        super.onResume();
+
+        /*
+         * Try to read the preferences file. If not found, set the state to the desired initial
+         * values.
+         */
+
+        if (!readInstanceState(this)) setInitialState();
+
+        /*
+         * Set the spinner to the current state.
+         */
+
+        Spinner restoreSpinner = (Spinner)findViewById(R.id.Spinner01);
+        restoreSpinner.setSelection(getSpinnerPosition());
+
+    }
+
+    /**
+     * Store the current state of the spinner (which item is selected, and the value of that item).
+     * Since onPause() is always called when an Activity is about to be hidden, even if it is about
+     * to be destroyed, it is the best place to save state.
+     *
+     * Attempt to write the state to the preferences file. If this fails, notify the user.
+     *
+     * @see android.app.Activity#onPause()
+     */
+    @Override
+    public void onPause() {
+
+        /*
+         * an override to onPause() must call the super constructor first.
+         */
+
+        super.onPause();
+
+        /*
+         * Save the state to the preferences file. If it fails, display a Toast, noting the failure.
+         */
+
+        if (!writeInstanceState(this)) {
+             Toast.makeText(this,
+                     "Failed to write state!", Toast.LENGTH_LONG).show();
+          }
+    }
+
+    /**
+     * Sets the initial state of the spinner when the application is first run.
+     */
+    public void setInitialState() {
+
+        this.mPos = DEFAULT_POSITION;
+
+    }
+
+    /**
+     * Read the previous state of the spinner from the preferences file
+     * @param c - The Activity's Context
+     */
+    public boolean readInstanceState(Context c) {
+
+        /*
+         * The preferences are stored in a SharedPreferences file. The abstract implementation of
+         * SharedPreferences is a "file" containing a hashmap. All instances of an application
+         * share the same instance of this file, which means that all instances of an application
+         * share the same preference settings.
+         */
+
+        /*
+         * Get the SharedPreferences object for this application
+         */
+
+        SharedPreferences p = c.getSharedPreferences(PREFERENCES_FILE, MODE_WORLD_READABLE);
+        /*
+         * Get the position and value of the spinner from the file, or a default value if the
+         * key-value pair does not exist.
+         */
+        this.mPos = p.getInt(POSITION_KEY, SpinnerActivity.DEFAULT_POSITION);
+        this.mSelection = p.getString(SELECTION_KEY, "");
+
+        /*
+         * SharedPreferences doesn't fail if the code tries to get a non-existent key. The
+         * most straightforward way to indicate success is to return the results of a test that
+         * SharedPreferences contained the position key.
+         */
+
+          return (p.contains(POSITION_KEY));
+
+        }
+
+    /**
+     * Write the application's current state to a properties repository.
+     * @param c - The Activity's Context
+     *
+     */
+    public boolean writeInstanceState(Context c) {
+
+        /*
+         * Get the SharedPreferences object for this application
+         */
+
+        SharedPreferences p =
+                c.getSharedPreferences(SpinnerActivity.PREFERENCES_FILE, MODE_WORLD_READABLE);
+
+        /*
+         * Get the editor for this object. The editor interface abstracts the implementation of
+         * updating the SharedPreferences object.
+         */
+
+        SharedPreferences.Editor e = p.edit();
+
+        /*
+         * Write the keys and values to the Editor
+         */
+
+        e.putInt(POSITION_KEY, this.mPos);
+        e.putString(SELECTION_KEY, this.mSelection);
+
+        /*
+         * Commit the changes. Return the result of the commit. The commit fails if Android
+         * failed to commit the changes to persistent storage.
+         */
+
+        return (e.commit());
+
+    }
+
+    public int getSpinnerPosition() {
+        return this.mPos;
+    }
+
+    public void setSpinnerPosition(int pos) {
+        this.mPos = pos;
+    }
+
+    public String getSpinnerSelection() {
+        return this.mSelection;
+    }
+
+    public void setSpinnerSelection(String selection) {
+        this.mSelection = selection;
+    }
+}
diff --git a/samples/SpinnerTest/AndroidManifest.xml b/samples/SpinnerTest/AndroidManifest.xml
new file mode 100644
index 0000000..e151d6e
--- /dev/null
+++ b/samples/SpinnerTest/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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" attribute provides a unique Android name for the application.
+    If you use this file as a template in your own application, you must change
+    the package name from "com.android.example" to one that you own or have
+    control over.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.example.spinner.test"
+          android:versionCode="1"
+          android:versionName="1.0">
+    <application  android:label="@string/app_name">
+        <!--
+            this package needs to link against the android.test library,
+            which is needed when building test cases.
+        -->
+        <uses-library android:name="android.test.runner"/>
+    </application>
+    <!--
+        This declares that this application uses the instrumentation test runner targeting
+        the package of com.android.example.spinner.  To run the tests use the command:
+        "adb shell am instrument -w \
+         com.android.example.spinner.test/android.test.InstrumentationTestRunner"
+    -->
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="com.android.example.spinner"
+                     android:label="Tests for com.android.example.spinner"/>
+</manifest>
diff --git a/samples/SpinnerTest/_index.html b/samples/SpinnerTest/_index.html
new file mode 100644
index 0000000..7a176fd
--- /dev/null
+++ b/samples/SpinnerTest/_index.html
@@ -0,0 +1,9 @@
+<p>
+This sample is the test application for the 
+<a href="../../../resources/tutorials/testing/activity_test.html">Activity
+  Testing Tutorial</a>. Refer to the tutorial text for a full explanation
+of this sample code.
+</p>
+<p>
+  This application does not have an Android GUI.
+</p>
diff --git a/samples/SpinnerTest/res/values/strings.xml b/samples/SpinnerTest/res/values/strings.xml
new file mode 100644
index 0000000..9721572
--- /dev/null
+++ b/samples/SpinnerTest/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<!--
+    This defines the test application's name
+ -->
+<resources>
+    <string name="app_name">SpinnerTest</string>
+</resources>
diff --git a/samples/SpinnerTest/src/com/android/example/spinner/test/SpinnerActivityTest.java b/samples/SpinnerTest/src/com/android/example/spinner/test/SpinnerActivityTest.java
new file mode 100644
index 0000000..4c23317
--- /dev/null
+++ b/samples/SpinnerTest/src/com/android/example/spinner/test/SpinnerActivityTest.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2010 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.android.example.spinner.test;
+
+import com.android.example.spinner.SpinnerActivity;
+
+import android.app.Instrumentation;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.view.KeyEvent;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+/**
+ * Tests the example application Spinner. Uses the instrumentation test class
+ * {@link ActivityInstrumentationTestCase2} as its base class. The tests include
+ * <ol>
+ *  <li>test initial conditions</li>
+ *  <li>test the UI</li>
+ *  <li>state management - preserving state after the app is shut down and restarted, preserving
+ *    state after the app is hidden (paused) and re-displayed (resumed)</li>
+ * </ol>
+ *
+ * Demonstrates the use of JUnit setUp() and assert() methods.
+ */
+public class SpinnerActivityTest extends ActivityInstrumentationTestCase2<SpinnerActivity> {
+
+    // Number of items in the spinner's backing mLocalAdapter
+
+    public static final int ADAPTER_COUNT = 9;
+
+    // The location of Saturn in the backing mLocalAdapter array (0-based)
+
+    public static final int TEST_POSITION = 5;
+
+    // Set the initial position of the spinner to zero
+
+    public static final int INITIAL_POSITION = 0;
+
+    // The initial position corresponds to Mercury
+
+    public static final String INITIAL_SELECTION = "Mercury";
+
+    // Test values of position and selection for the testStateDestroy test
+
+    public static final int TEST_STATE_DESTROY_POSITION = 2;
+    public static final String TEST_STATE_DESTROY_SELECTION = "Earth";
+
+    // Test values of position and selection for the testStatePause test
+
+    public static final int TEST_STATE_PAUSE_POSITION = 4;
+    public static final String TEST_STATE_PAUSE_SELECTION = "Jupiter";
+
+    // The Application object for the application under test
+
+    private SpinnerActivity mActivity;
+
+    // String displayed in the spinner in the app under test
+
+    private String mSelection;
+
+    // The currently selected position in the spinner in the app under test
+
+    private int mPos;
+
+    /*
+     *  The Spinner object in the app under test. Used with instrumentation to control the
+     *  app under test.
+     */
+
+    private Spinner mSpinner;
+
+    /*
+     * The data backing the Spinner in the app under test.
+     */
+
+    private SpinnerAdapter mPlanetData;
+
+    /**
+     * Constructor for the test class. Required by Android test classes. The constructor
+     * must call the super constructor, providing the Android package name of the app under test
+     * and the Java class name of the activity in that application that handles the MAIN intent.
+     */
+    public SpinnerActivityTest() {
+
+        super("com.android.example.spinner", SpinnerActivity.class);
+    }
+
+    /**
+     * Sets up the test environment before each test.
+     * @see android.test.ActivityInstrumentationTestCase2#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+
+        /*
+         * Call the super constructor (required by JUnit)
+         */
+
+        super.setUp();
+
+        /*
+         * prepare to send key events to the app under test by turning off touch mode.
+         * Must be done before the first call to getActivity()
+         */
+
+        setActivityInitialTouchMode(false);
+
+        /*
+         * Start the app under test by starting its main activity. The test runner already knows
+         * which activity this is from the call to the super constructor, as mentioned
+         * previously. The tests can now use instrumentation to directly access the main
+         * activity through mActivity.
+         */
+        mActivity = getActivity();
+
+        /*
+         * Get references to objects in the application under test. These are
+         * tested to ensure that the app under test has initialized correctly.
+         */
+
+        mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01);
+
+        mPlanetData = mSpinner.getAdapter();
+
+    }
+
+    /**
+     * Tests the initial values of key objects in the app under test, to ensure the initial
+     * conditions make sense. If one of these is not initialized correctly, then subsequent
+     * tests are suspect and should be ignored.
+     */
+
+    public void testPreconditions() {
+
+        /*
+         *  An example of an initialization test. Assert that the item select listener in
+         *  the main Activity is not null (has been set to a valid callback)
+         */
+        assertTrue(mSpinner.getOnItemSelectedListener() != null);
+
+        /*
+         * Test that the spinner's backing mLocalAdapter was initialized correctly.
+         */
+
+        assertTrue(mPlanetData != null);
+
+        /*
+         *  Also ensure that the backing mLocalAdapter has the correct number of entries.
+         */
+
+        assertEquals(mPlanetData.getCount(), ADAPTER_COUNT);
+    }
+
+    /*
+     * Tests the UI of the main activity. Sends key events (keystrokes) to the UI, then checks
+     * if the resulting spinner state is consistent with the attempted selection.
+     */
+    public void testSpinnerUI() {
+
+        /*
+         * Request focus for the spinner widget in the application under test,
+         * and set its initial position. This code interacts with the app's View
+         *  so it has to run on the app's thread not the test's thread.
+         *
+         * To do this, pass the necessary code to the application with
+         * runOnUiThread(). The parameter is an anonymous Runnable object that
+         * contains the Java statements put in it by its run() method.
+         */
+        mActivity.runOnUiThread(
+            new Runnable() {
+                public void run() {
+                    mSpinner.requestFocus();
+                    mSpinner.setSelection(INITIAL_POSITION);
+                }
+            }
+        );
+
+        // Activate the spinner by clicking the center keypad key
+
+        this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+
+        // send 5 down arrow keys to the spinner
+
+        for (int i = 1; i <= TEST_POSITION; i++) {
+
+            this.sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        }
+
+        // select the item at the current spinner position
+
+        this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+
+        // get the position of the selected item
+
+        mPos = mSpinner.getSelectedItemPosition();
+
+        /*
+         * from the spinner's data mLocalAdapter, get the object at the selected position
+         * (this is a String value)
+         */
+
+        mSelection = (String)mSpinner.getItemAtPosition(mPos);
+
+        /*
+         * Get the TextView widget that displays the result of selecting an item from the spinner
+         */
+
+        TextView resultView =
+                (TextView) mActivity.findViewById(com.android.example.spinner.R.id.SpinnerResult);
+
+        // Get the String value in the EditText object
+
+        String resultText = (String) resultView.getText();
+
+        /*
+         * Confirm that the EditText contains the same value as the data in the mLocalAdapter
+         */
+
+        assertEquals(resultText,mSelection);
+    }
+
+    /*
+     *  Tests that the activity under test maintains the spinner state when the activity halts
+     *  and then restarts (for example, if the device reboots). Sets the spinner to a
+     *  certain state, calls finish() on the activity, restarts the activity, and then
+     *  checks that the spinner has the same state.
+     *
+     */
+
+    public void testStateDestroy() {
+
+        /*
+         * Set the position and value of the spinner in the Activity. The test runner's
+         * instrumentation enables this by running the test app and the main app in the same
+         * process.
+         */
+
+
+        mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION);
+
+        mActivity.setSpinnerSelection(TEST_STATE_DESTROY_SELECTION);
+
+        // Halt the Activity by calling Activity.finish() on it
+
+        mActivity.finish();
+
+        // Restart the activity by calling ActivityInstrumentationTestCase2.getActivity()
+
+        mActivity = this.getActivity();
+
+        /*
+         * Get the current position and selection from the activity.
+         */
+
+        int currentPosition = mActivity.getSpinnerPosition();
+        String currentSelection = mActivity.getSpinnerSelection();
+
+        // test that they are the same.
+
+        assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);
+
+        assertEquals(TEST_STATE_DESTROY_SELECTION, currentSelection);
+    }
+
+    /*
+     * Tests that the activity under test maintains the spinner's state when the activity is
+     * paused and then resumed.
+     *
+     * Calls the activity's onResume() method. Changes the spinner's state by
+     * altering the activity's View. This means the test must run
+     * on the UI Thread. All the statements in the test method may be run on
+     * that thread, so instead of using the runOnUiThread() method, the
+     * @UiThreadTest is used.
+     */
+    @UiThreadTest
+
+    public void testStatePause() {
+
+        /*
+         * Get the instrumentation object for this application. This object
+         * does all the instrumentation work for the test runner
+         */
+
+        Instrumentation instr = this.getInstrumentation();
+
+        /*
+         * Set the activity's fields for the position and value of the spinner
+         */
+
+        mActivity.setSpinnerPosition(TEST_STATE_PAUSE_POSITION);
+
+        mActivity.setSpinnerSelection(TEST_STATE_PAUSE_SELECTION);
+
+        /*
+         *  Use the instrumentation to onPause() on the currently running Activity.
+         *  This analogous to calling finish() in the testStateDestroy() method.
+         *  This way demonstrates using the test class' instrumentation.
+         */
+
+        instr.callActivityOnPause(mActivity);
+
+        /*
+         * Set the spinner to a test position
+         */
+
+        mActivity.setSpinnerPosition(0);
+
+        mActivity.setSpinnerSelection("");
+
+        /*
+         * Call the activity's onResume() method. This forces the activity
+         * to restore its state.
+         */
+
+        instr.callActivityOnResume(mActivity);
+
+        /*
+         * Get the current state of the spinner
+         */
+
+        int currentPosition = mActivity.getSpinnerPosition();
+
+        String currentSelection = mActivity.getSpinnerSelection();
+
+        assertEquals(TEST_STATE_PAUSE_POSITION,currentPosition);
+        assertEquals(TEST_STATE_PAUSE_SELECTION,currentSelection);
+  }
+
+}