Updating the feature summary test to include a Legend in the info dialog. This
required a minor change to the base classes to support adding a View to the
info Dialog instead of just text. Also added info strings for accelerometer
and magnetometer tests.

Change-Id: I9acd7c893aa85ddb73b3a383b29e7849086e9240
diff --git a/apps/CtsVerifier/res/layout/fs_info.xml b/apps/CtsVerifier/res/layout/fs_info.xml
new file mode 100644
index 0000000..3fe6815
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/fs_info.xml
@@ -0,0 +1,74 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent" android:layout_height="wrap_content">
+
+    <ImageView android:id="@+id/fs_legend_good_image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/fs_good"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentLeft="true" />
+    <TextView android:id="@+id/fs_legend_good_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/fs_legend_good"
+        android:layout_toRightOf="@id/fs_legend_good_image"
+        android:layout_alignTop="@id/fs_legend_good_image"
+        android:layout_marginLeft="3dip" />
+
+    <ImageView android:id="@+id/fs_legend_indeterminate_image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/fs_indeterminate"
+        android:layout_alignParentLeft="true"
+        android:layout_below="@id/fs_legend_good_image" />
+    <TextView android:id="@+id/fs_legend_indeterminate_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/fs_legend_indeterminate"
+        android:layout_toRightOf="@id/fs_legend_indeterminate_image"
+        android:layout_alignTop="@id/fs_legend_indeterminate_image"
+        android:layout_marginLeft="3dip" />
+
+    <ImageView android:id="@+id/fs_legend_warning_image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/fs_warning"
+        android:layout_alignParentLeft="true"
+        android:layout_below="@id/fs_legend_indeterminate_image" />
+    <TextView android:id="@+id/fs_legend_warning_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/fs_legend_warning"
+        android:layout_toRightOf="@id/fs_legend_warning_image"
+        android:layout_alignTop="@id/fs_legend_warning_image"
+        android:layout_marginLeft="3dip" />
+
+    <ImageView android:id="@+id/fs_legend_error_image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/fs_error"
+        android:layout_alignParentLeft="true"
+        android:layout_below="@id/fs_legend_warning_image" />
+    <TextView android:id="@+id/fs_legend_error_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/fs_legend_error"
+        android:layout_toRightOf="@id/fs_legend_error_image"
+        android:layout_alignTop="@id/fs_legend_error_image"
+        android:layout_marginLeft="3dip" />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/fs_main.xml b/apps/CtsVerifier/res/layout/fs_main.xml
index a94f326..7473f0f 100644
--- a/apps/CtsVerifier/res/layout/fs_main.xml
+++ b/apps/CtsVerifier/res/layout/fs_main.xml
@@ -22,7 +22,7 @@
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/empty"/>
-                           
+
      <ListView android:id="@id/android:list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index ba82f6e..c386a1b 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -46,13 +46,18 @@
     <string name="fs_disallowed">WARNING: device reports a disallowed feature name</string>
     <string name="fs_missing_wifi_telephony">WARNING: device reports neither WiFi nor telephony</string>
     <string name="fs_no_data">No data.</string>
+    <string name="fs_legend_good">standard feature reported by device</string>
+    <string name="fs_legend_indeterminate">optional feature not reported by device</string>
+    <string name="fs_legend_warning">non-standard feature reported by device</string>
+    <string name="fs_legend_error">required feature not reported, or forbidden feature reported</string>
+
     <string name="empty"></string>
 
     <!-- Strings for AccelerometerTestActivity and MagnetometerTestActivity -->
     <string name="snsr_accel_test">Accelerometer Test</string>
-    <string name="snsr_accel_test_info">This is a test for...</string>
+    <string name="snsr_accel_test_info">This test verifies that the accelerometer is working properly. As you move the device around through space, the triangle should always point down (i.e. in the direction of gravity.) If it does not, the accelerometer is improperly configured.</string>
     <string name="snsr_mag_test">Magnetometer Test</string>
-    <string name="snsr_mag_test_info">This is a test for...</string>
+    <string name="snsr_mag_test_info">This test verifies that the magnetometer (compass) is working properly. As you move the device around through space, the triangle should always point toward the north pole (which may point through the ground.) If it does not, the magnetometer is improperly configured. Be sure not to run this test with the device near any strong magnetic field generators.</string>
 
     <!-- Strings for SuidFilesActivity -->
     <string name="suid_files">SUID File Scanner</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
index 6b55c72..1c407b8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
@@ -19,9 +19,11 @@
 import android.app.AlertDialog;
 import android.content.ContentResolver;
 import android.content.ContentValues;
+import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnCancelListener;
 import android.database.Cursor;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 
@@ -53,7 +55,7 @@
          * @param titleId for the text shown in the dialog title area
          * @param messageId for the text shown in the dialog's body area
          */
-        void setInfoTextResources(int titleId, int messageId);
+        void setInfoResources(int titleId, int messageId, int viewId);
 
         /**
          * Click handler for the pass and fail buttons. No need to call this ever as the XML
@@ -64,8 +66,8 @@
 
     public static class Activity extends android.app.Activity implements PassFailActivity {
 
-        public void setInfoTextResources(int titleId, int messageId) {
-            setInfoText(this, titleId, messageId);
+        public void setInfoResources(int titleId, int messageId, int viewId) {
+            setInfo(this, titleId, messageId, viewId);
         }
 
         public void passFailButtonsClickHandler(View target) {
@@ -75,8 +77,8 @@
 
     public static class ListActivity extends android.app.ListActivity implements PassFailActivity {
 
-        public void setInfoTextResources(int titleId, int messageId) {
-            setInfoText(this, titleId, messageId);
+        public void setInfoResources(int titleId, int messageId, int viewId) {
+            setInfo(this, titleId, messageId, viewId);
         }
 
         public void passFailButtonsClickHandler(View target) {
@@ -84,20 +86,20 @@
         }
     }
 
-    private static void setInfoText(final android.app.Activity activity, final int titleId,
-            final int messageId) {
+    private static void setInfo(final android.app.Activity activity, final int titleId,
+            final int messageId, final int viewId) {
         // Show the middle "info" button and make it show the info dialog when clicked.
         View infoButton = activity.findViewById(R.id.info_button);
         infoButton.setVisibility(View.VISIBLE);
         infoButton.setOnClickListener(new OnClickListener() {
             public void onClick(View view) {
-                showInfoDialog(activity, titleId, messageId);
+                showInfoDialog(activity, titleId, messageId, viewId);
             }
         });
 
         // Show the info dialog if the user has never seen it before.
         if (!hasSeenInfoDialog(activity)) {
-            showInfoDialog(activity, titleId, messageId);
+            showInfoDialog(activity, titleId, messageId, viewId);
         }
     }
 
@@ -117,22 +119,26 @@
     }
 
     private static void showInfoDialog(final android.app.Activity activity, int titleId,
-            int messageId) {
-        new AlertDialog.Builder(activity)
-                .setIcon(android.R.drawable.ic_dialog_info)
-                .setTitle(titleId)
-                .setMessage(messageId)
-                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                    public void onClick(DialogInterface dialog, int which) {
-                        markSeenInfoDialog(activity);
-                    }
-                })
-                .setOnCancelListener(new OnCancelListener() {
-                    public void onCancel(DialogInterface dialog) {
-                        markSeenInfoDialog(activity);
-                    }
-                })
-                .show();
+            int messageId, int viewId) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(activity).setIcon(
+                android.R.drawable.ic_dialog_info).setTitle(titleId);
+        if (viewId > 0) {
+            LayoutInflater inflater = (LayoutInflater) activity
+                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            builder.setView(inflater.inflate(viewId, null));
+        } else {
+            builder.setMessage(messageId);
+        }
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int which) {
+                markSeenInfoDialog(activity);
+            }
+        }).setOnCancelListener(new OnCancelListener() {
+            public void onCancel(DialogInterface dialog) {
+                markSeenInfoDialog(activity);
+            }
+        })
+    	.show();
     }
 
     private static void markSeenInfoDialog(android.app.Activity activity) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index 2277876..4f6e0a3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -21,6 +21,7 @@
  * http://www.fatcow.com/free-icons/
  * http://creativecommons.org/licenses/by/3.0/us/
  */
+
 package com.android.cts.verifier.features;
 
 import com.android.cts.verifier.PassFailButtons;
@@ -28,6 +29,7 @@
 
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.Bundle;
 import android.view.View;
 import android.widget.ImageView;
@@ -63,7 +65,7 @@
         /**
          * Constructor does not include 'present' because that's a detected
          * value, and not set during creation.
-         *
+         * 
          * @param name value for this.name
          * @param required value for this.required
          */
@@ -75,47 +77,57 @@
     }
 
     /**
-     * A list of all known features. If a constant is added to PackageManager,
-     * this list needs to be updated. We could detect these fields via
-     * Reflection, but we can't determine whether the features are required or
-     * not that way, so we need this block anyway.
+     * A list of all features added in Eclair (API=7).
      */
-    public static final Feature[] ALL_FEATURES = {
-            new Feature(PackageManager.FEATURE_BLUETOOTH, true),
+    public static final Feature[] ALL_ECLAIR_FEATURES = {
             new Feature(PackageManager.FEATURE_CAMERA, true),
             new Feature(PackageManager.FEATURE_CAMERA_AUTOFOCUS, false),
             new Feature(PackageManager.FEATURE_CAMERA_FLASH, false),
             new Feature(PackageManager.FEATURE_LIVE_WALLPAPER, false),
-            new Feature(PackageManager.FEATURE_LOCATION, true),
-            new Feature(PackageManager.FEATURE_LOCATION_GPS, true),
-            new Feature(PackageManager.FEATURE_LOCATION_NETWORK, true),
-            new Feature(PackageManager.FEATURE_MICROPHONE, true),
-            new Feature(PackageManager.FEATURE_SENSOR_ACCELEROMETER, true),
-            new Feature(PackageManager.FEATURE_SENSOR_COMPASS, true),
             new Feature(PackageManager.FEATURE_SENSOR_LIGHT, false),
             new Feature(PackageManager.FEATURE_SENSOR_PROXIMITY, false),
             new Feature(PackageManager.FEATURE_TELEPHONY, false),
             new Feature(PackageManager.FEATURE_TELEPHONY_CDMA, false),
             new Feature(PackageManager.FEATURE_TELEPHONY_GSM, false),
-            new Feature(PackageManager.FEATURE_TOUCHSCREEN, true),
-            new Feature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH, false),
-            new Feature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT, false),
-            new Feature(PackageManager.FEATURE_WIFI, false),
+    };
+
+    /**
+     * A list of all features added in FroYo (API=8). Because we want to run on
+     * Eclair devices, we can't use static references to constants added later
+     * than Eclair. We could use Reflection, but we'd still need a list of
+     * string literals (for constant names) anyway, and there's little point in
+     * using Reflection to to look up a constant String value for a constant
+     * String name.
+     */
+    public static final Feature[] ALL_FROYO_FEATURES = {
+            new Feature("android.hardware.bluetooth", true),
+            new Feature("android.hardware.location", true),
+            new Feature("android.hardware.location.gps", true),
+            new Feature("android.hardware.location.network", true),
+            new Feature("android.hardware.microphone", true),
+            new Feature("android.hardware.sensor.accelerometer", true),
+            new Feature("android.hardware.sensor.compass", true),
+            new Feature("android.hardware.touchscreen", true),
+            new Feature("android.hardware.touchscreen.multitouch", false),
+            new Feature("android.hardware.touchscreen.multitouch.distinct", false),
+            new Feature("android.hardware.wifi", false),
     };
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.fs_main);
-        setInfoTextResources(R.string.feature_summary, R.string.feature_summary_info);
+        setInfoResources(R.string.feature_summary, R.string.feature_summary_info, R.layout.fs_info);
         setResult(RESULT_CANCELED);
 
-        // some values used to detect warn-able conditions involving multiple features
+        // some values used to detect warn-able conditions involving multiple
+        // features
         boolean hasWifi = false;
         boolean hasTelephony = false;
         boolean hasIllegalFeature = false;
 
-        // get list of all features device thinks it has, & store in a HashMap for fast lookups
+        // get list of all features device thinks it has, & store in a HashMap
+        // for fast lookups
         HashMap<String, String> actualFeatures = new HashMap<String, String>();
         for (FeatureInfo fi : getPackageManager().getSystemAvailableFeatures()) {
             actualFeatures.put(fi.name, fi.name);
@@ -127,7 +139,15 @@
         // roll over all known features & check whether device reports them
         boolean present = false;
         int statusIcon;
-        for (Feature f : ALL_FEATURES) {
+        ArrayList<Feature> features = new ArrayList<Feature>();
+        int apiVersion = Build.VERSION.SDK_INT;
+        if (apiVersion >= Build.VERSION_CODES.ECLAIR_MR1) {
+            Collections.addAll(features, ALL_ECLAIR_FEATURES);
+        }
+        if (apiVersion >= Build.VERSION_CODES.FROYO) {
+            Collections.addAll(features, ALL_FROYO_FEATURES);
+        }
+        for (Feature f : features) {
             HashMap<String, Object> row = new HashMap<String, Object>();
             listViewData.add(row);
             present = actualFeatures.containsKey(f.name);
@@ -138,10 +158,12 @@
                 statusIcon = R.drawable.fs_good;
                 actualFeatures.remove(f.name);
             } else if (!present && f.required) {
-                // it's required, but device doesn't report it. Boo, set the bogus icon
+                // it's required, but device doesn't report it. Boo, set the
+                // bogus icon
                 statusIcon = R.drawable.fs_error;
             } else {
-                // device doesn't report it, but it's not req'd, so can't tell if there's a problem
+                // device doesn't report it, but it's not req'd, so can't tell
+                // if there's a problem
                 statusIcon = R.drawable.fs_indeterminate;
             }
             row.put("feature", f.name);
@@ -156,7 +178,8 @@
             listViewData.add(row);
             row.put("feature", feature);
             if (feature.startsWith("android")) { // intentionally not "android."
-                // sorry, you're not allowed to squat in the official namespace; set bogus icon
+                // sorry, you're not allowed to squat in the official namespace;
+                // set bogus icon
                 row.put("icon", R.drawable.fs_error);
                 hasIllegalFeature = true;
             } else {
@@ -165,7 +188,8 @@
             }
         }
 
-        // sort the ListView's data to group by icon type, for easier reading by humans
+        // sort the ListView's data to group by icon type, for easier reading by
+        // humans
         final HashMap<Integer, Integer> idMap = new HashMap<Integer, Integer>();
         idMap.put(R.drawable.fs_error, 0);
         idMap.put(R.drawable.fs_warning, 1);
@@ -186,8 +210,11 @@
 
         // Set up the SimpleAdapter used to populate the ListView
         SimpleAdapter adapter = new SimpleAdapter(this, listViewData, R.layout.fs_row,
-            new String[] { "feature", "icon" },
-            new int[] { R.id.fs_feature, R.id.fs_icon });
+                new String[] {
+                        "feature", "icon"
+                }, new int[] {
+                        R.id.fs_feature, R.id.fs_icon
+                });
         adapter.setViewBinder(new SimpleAdapter.ViewBinder() {
             public boolean setViewValue(View view, Object data, String repr) {
                 try {
@@ -206,7 +233,8 @@
         });
         setListAdapter(adapter);
 
-        // finally, check for our second-order error cases and set warning text if necessary
+        // finally, check for our second-order error cases and set warning text
+        // if necessary
         StringBuffer sb = new StringBuffer();
         if (hasIllegalFeature) {
             sb.append(getResources().getString(R.string.fs_disallowed)).append("\n");
@@ -214,6 +242,11 @@
         if (!hasWifi && !hasTelephony) {
             sb.append(getResources().getString(R.string.fs_missing_wifi_telephony)).append("\n");
         }
-        ((TextView) (findViewById(R.id.fs_warnings))).setText(sb.toString());
+        String warnings = sb.toString().trim();
+        if (warnings == null || "".equals(warnings)) {
+            ((TextView) (findViewById(R.id.fs_warnings))).setVisibility(View.GONE);
+        } else {
+            ((TextView) (findViewById(R.id.fs_warnings))).setText(warnings);
+        }
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerTestActivity.java
index 2c07882..2100c0f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerTestActivity.java
@@ -49,7 +49,7 @@
         mListener = renderer;
 
         setContentView(R.layout.pass_fail_gl);
-        setInfoTextResources(R.string.snsr_accel_test, R.string.snsr_accel_test_info);
+        setInfoResources(R.string.snsr_accel_test, R.string.snsr_accel_test_info, -1);
         mGLSurfaceView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
         mGLSurfaceView.setRenderer(renderer);
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java
index f5ad701..479a2fb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagnetometerTestActivity.java
@@ -48,7 +48,7 @@
         mListener = renderer;
 
         setContentView(R.layout.pass_fail_gl);
-        setInfoTextResources(R.string.snsr_mag_test, R.string.snsr_mag_test_info);
+        setInfoResources(R.string.snsr_mag_test, R.string.snsr_mag_test_info, -1);
         mGLSurfaceView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
         mGLSurfaceView.setRenderer(renderer);
     }
diff --git a/apps/CtsVerifier/tests/src/com/android/cts/verifier/features/FeatureSummaryActivityTest.java b/apps/CtsVerifier/tests/src/com/android/cts/verifier/features/FeatureSummaryActivityTest.java
index 43f5da4..ca7ced4 100644
--- a/apps/CtsVerifier/tests/src/com/android/cts/verifier/features/FeatureSummaryActivityTest.java
+++ b/apps/CtsVerifier/tests/src/com/android/cts/verifier/features/FeatureSummaryActivityTest.java
@@ -19,6 +19,7 @@
 import com.android.cts.verifier.features.FeatureSummaryActivity.Feature;
 
 import android.content.pm.PackageManager;
+import android.os.Build;
 
 import java.lang.reflect.Field;
 import java.util.HashSet;
@@ -29,12 +30,19 @@
 public class FeatureSummaryActivityTest extends TestCase {
 
     public void testAllFeatures() throws Exception {
+        int version = Build.VERSION.SDK_INT;
+
         Set<String> expectedFeatures = getFeatureConstants();
 
         Set<String> actualFeatures = new HashSet<String>();
-        for (Feature feature : FeatureSummaryActivity.ALL_FEATURES) {
+        for (Feature feature : FeatureSummaryActivity.ALL_ECLAIR_FEATURES) {
             actualFeatures.add(feature.name);
         }
+        if (version >= Build.VERSION_CODES.FROYO) {
+            for (Feature feature : FeatureSummaryActivity.ALL_FROYO_FEATURES) {
+                actualFeatures.add(feature.name);
+            }
+        }
 
         assertEquals("Feature list needs to be updated.",
                 expectedFeatures.size(), actualFeatures.size());