CtsVerifier Share Test Results

Add a menu item in the test list activity that allows the user to
share the CtsVerifier's current test results. It uses an ACTION_SEND
intent with createChooser so you can share the results via SMS,
Gmail, and other activities.

Change-Id: I6f2017e961f7f7ff2b0af7a4f806d56710828327
diff --git a/apps/CtsVerifier/res/layout/pass_fail_buttons.xml b/apps/CtsVerifier/res/layout/pass_fail_buttons.xml
index 62867b2..cbd1fa4 100644
--- a/apps/CtsVerifier/res/layout/pass_fail_buttons.xml
+++ b/apps/CtsVerifier/res/layout/pass_fail_buttons.xml
@@ -24,7 +24,7 @@
             android:layout_weight="1"            
             android:drawableTop="@drawable/fs_good"
             android:onClick="passFailButtonsClickHandler"
-            android:text="@string/pass"/>
+            android:text="@string/pass_button_text"/>
             
     <Button android:id="@+id/fail_button"
             android:layout_width="wrap_content"
@@ -32,6 +32,6 @@
             android:layout_weight="1"            
             android:drawableTop="@drawable/fs_error"
             android:onClick="passFailButtonsClickHandler"
-            android:text="@string/fail"/>
+            android:text="@string/fail_button_text"/>
             
 </LinearLayout>
diff --git a/apps/CtsVerifier/res/menu/test_list_menu.xml b/apps/CtsVerifier/res/menu/test_list_menu.xml
index 3f97a16..713455c 100644
--- a/apps/CtsVerifier/res/menu/test_list_menu.xml
+++ b/apps/CtsVerifier/res/menu/test_list_menu.xml
@@ -3,4 +3,7 @@
     <item android:id="@+id/clear"
           android:icon="@android:drawable/ic_menu_delete" 
           android:title="@string/clear" />
+    <item android:id="@+id/share"
+          android:icon="@android:drawable/ic_menu_share"
+          android:title="@string/share" />
 </menu>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 27fcc5d..5a97b7b 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -18,8 +18,15 @@
     <string name="welcome_text">Welcome to the CTS Verifier!</string>
     <string name="continue_button_text">Continue</string>
 
-    <string name="pass">Pass</string>
-    <string name="fail">Fail</string>
+    <string name="pass_button_text">Pass</string>
+    <string name="fail_button_text">Fail</string>
+
+    <!-- Strings for TestResultsReport -->
+    <string name="subject_header">[CtsVerifier %1$s]</string>
+    <string name="body_header">Cts Verifier %1$s Test Results</string>
+    <string name="pass_result">PASS</string>
+    <string name="fail_result">FAIL</string>
+    <string name="not_executed_result">NOT_EXECUTED</string>
 
     <!-- Strings for TestListActivity -->
     <string name="test_list_title">Manual Test List</string>
@@ -29,6 +36,8 @@
     <string name="test_category_other">Other</string>
     <string name="clear">Clear</string>
     <string name="test_results_cleared">Test results cleared.</string>
+    <string name="share">Share</string>
+    <string name="share_test_results">Share Test Results</string>
 
     <!-- Strings for FeatureSummaryActivity -->
     <string name="feature_summary">Hardware/Software Feature Summary</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
index b7801a6..8bc92f0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
@@ -112,6 +112,11 @@
             case R.id.clear:
                 handleClearItemSelected();
                 return true;
+
+            case R.id.share:
+                handleShareItemSelected();
+                return true;
+
             default:
                 return super.onOptionsItemSelected(item);
         }
@@ -123,6 +128,16 @@
         Toast.makeText(this, R.string.test_results_cleared, Toast.LENGTH_SHORT).show();
     }
 
+    private void handleShareItemSelected() {
+        Intent target = new Intent(Intent.ACTION_SEND);
+        target.setType("text/plain");
+
+        TestResultsReport report = new TestResultsReport(this, getListAdapter());
+        target.putExtra(Intent.EXTRA_SUBJECT, report.getSubject());
+        target.putExtra(Intent.EXTRA_TEXT, report.getBody());
+        startActivity(Intent.createChooser(target, getString(R.string.share_test_results)));
+    }
+
     /**
      * {@link ContentResolver} that refreshes the {@link TestListAdapter} and thus
      * the {@link ListView} when the test results change.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
index ddf4fe8..a7284f0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
@@ -273,6 +273,13 @@
         return position;
     }
 
+    public int getTestResult(int position) {
+        TestListItem item = getItem(position);
+        return mTestResults.containsKey(item.className)
+                ? mTestResults.get(item.className)
+                : TestResult.TEST_RESULT_NOT_EXECUTED;
+    }
+
     public View getView(int position, View convertView, ViewGroup parent) {
         TextView textView;
         if (convertView == null) {
@@ -288,11 +295,7 @@
         textView.setCompoundDrawablePadding(PADDING);
 
         if (item.isTest()) {
-            int testResult = mTestResults.containsKey(item.className)
-                    ? mTestResults.get(item.className)
-                    : TestResult.TEST_RESULT_NOT_EXECUTED;
-
-
+            int testResult = getTestResult(position);
             int backgroundResource = 0;
             int iconResource = 0;
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
new file mode 100644
index 0000000..18a08fe
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -0,0 +1,107 @@
+/*
+ * 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.cts.verifier;
+
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+
+/** Plain text report of the current test results. */
+class TestResultsReport {
+
+    private final Context mContext;
+
+    private final TestListAdapter mAdapter;
+
+    private final String mVersionName;
+
+    TestResultsReport(Context context, TestListAdapter adapter) {
+        this.mContext = context;
+        this.mAdapter = adapter;
+        this.mVersionName = getVersionName(context);
+    }
+
+    private static String getVersionName(Context context) {
+        try {
+            PackageManager packageManager = context.getPackageManager();
+            PackageInfo info = packageManager.getPackageInfo(context.getPackageName(), 0);
+            return info.versionName;
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException("Could not get find package information for "
+                    + context.getPackageName());
+        }
+    }
+
+    String getSubject() {
+        return new StringBuilder()
+                .append(mContext.getString(R.string.subject_header, mVersionName))
+                .append(' ')
+                .append(Build.FINGERPRINT)
+                .toString();
+    }
+
+    String getBody() {
+        StringBuilder builder = new StringBuilder()
+                .append(mContext.getString(R.string.body_header, mVersionName))
+                .append("\n\n")
+                .append(Build.FINGERPRINT)
+                .append("\n\n");
+
+        int count = mAdapter.getCount();
+        for (int i = 0; i < count; i++) {
+            TestListItem item = mAdapter.getItem(i);
+            if (!item.isTest()) {
+                builder.append(item.title).append('\n');
+            } else {
+                builder.append(item.title)
+                        .append(".....")
+                        .append(getTestResultString(mAdapter.getTestResult(i)))
+                        .append('\n');
+            }
+
+            if (i + 1 < count && !mAdapter.getItem(i + 1).isTest()) {
+                builder.append('\n');
+            }
+        }
+        return builder.toString();
+    }
+
+    private String getTestResultString(int testResult) {
+        int resId = 0;
+        switch (testResult) {
+            case TestResult.TEST_RESULT_PASSED:
+                resId = R.string.pass_result;
+                break;
+
+            case TestResult.TEST_RESULT_FAILED:
+                resId = R.string.fail_result;
+                break;
+
+            case TestResult.TEST_RESULT_NOT_EXECUTED:
+                resId = R.string.not_executed_result;
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unknown test result: " + testResult);
+        }
+        return mContext.getString(resId);
+    }
+}