Merge "Change Icon for Export Function" into gingerbread
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 68c3682..ead03f2 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -30,7 +30,13 @@
     <!-- Needed by the Audio Quality Verifier to store the sound samples that will be mailed. -->
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
-    <application android:label="@string/app_name" android:icon="@drawable/icon" android:debuggable="true">
+    <application android:label="@string/app_name" 
+            android:icon="@drawable/icon"
+            android:backupAgent="VerifierBackupAgent" 
+            android:debuggable="true">
+            
+        <meta-data android:name="com.google.android.backup.api_key"
+                android:value="AEdPqrEAAAAIbK6ldcOzoeRtQ1u1dFVJ1A7KetRhit-a1Xa82Q" />
 
         <activity android:name=".CtsVerifierActivity" android:label="@string/app_name">
             <intent-filter>
@@ -73,6 +79,13 @@
             </intent-filter>
         </receiver>
 
+        <activity android:name=".backup.BackupTestActivity" android:label="@string/backup_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".bluetooth.BluetoothTestActivity"
                 android:label="@string/bluetooth_test"
                 android:configChanges="keyboardHidden|orientation">
diff --git a/apps/CtsVerifier/res/layout/bu_main.xml b/apps/CtsVerifier/res/layout/bu_main.xml
new file mode 100644
index 0000000..2289fee
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/bu_main.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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:orientation="vertical"
+         android:layout_width="match_parent"
+         android:layout_height="match_parent"
+         >
+         
+    <ListView android:id="@+id/android:list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            />
+            
+    <TextView android:id="@id/android:empty"
+           android:gravity="center"
+           android:layout_width="match_parent"
+           android:layout_height="wrap_content"
+           android:layout_weight="1"
+           android:text="@string/bu_loading"
+           />
+
+    <Button android:id="@+id/generate_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/bu_generate"
+            />
+
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/bu_preference_row.xml b/apps/CtsVerifier/res/layout/bu_preference_row.xml
new file mode 100644
index 0000000..c37eece
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/bu_preference_row.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="18sp"
+        android:padding="5dp"
+        />
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 0f0f645..628b931 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -44,6 +44,32 @@
     <string name="no_storage">Cannot save report to external storage, see log for details.</string>
     <string name="report_saved">Report saved to: %s</string>
 
+    <!-- Strings for BackupTestActivity -->
+    <string name="backup_test">Data Backup Test</string>
+    <string name="backup_info">This test checks that data backup and automatic restore works 
+        properly. The test activity lists some preferences and files that are backed up and
+        restored by the CTS Verifier. If backup and restore is working properly, these values
+        should be restored after running the backup manager, uninstalling the app, and reinstalling
+        the CTS Verifier.
+        \n\nPress the \"Generate Test Data\" to populate these values
+        and then follow the on screen instructions to finish the test.
+    </string>
+    <string name="bu_preferences">Preferences</string>
+    <string name="bu_files">Files</string>
+    <string name="bu_loading">Loading...</string>
+    <string name="bu_generate">Generate Test Data</string>
+    <string name="bu_generate_error">Error occurred while generating test data...</string>
+    <string name="bu_instructions">Random values for the preferences and files have been saved.
+        \n\nFollow the instructions below to check that the data backup and restore works:
+        \n\n1. Make sure backup and automatic restore are enabled in settings. Depending on the 
+        backup transport supported by the device you may need to do additional steps. For instance 
+        you may need to set a Google account as the backup account for the device.
+        \n\n2. Run the backup manager: adb shell bmgr run
+        \n\n3. Uninstall the program: adb uninstall com.android.cts.verifier
+        \n\n4. Reinstall the CTS Verifier and verify that the values are still the same.
+    </string>
+    <string name="bu_settings">Settings</string>
+
     <!-- Strings for Device Administration tests -->
     <string name="da_policy_serialization_test">Policy Serialization Test</string>
     <string name="da_policy_serialization_info">This test checks that a device policy is properly
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
index f6e6f1b..38b4dbc 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
@@ -350,18 +350,7 @@
 
         @Override
         protected Void doInBackground(Void... params) {
-            ContentValues values = new ContentValues(2);
-            values.put(TestResultsProvider.COLUMN_TEST_RESULT, mResult);
-            values.put(TestResultsProvider.COLUMN_TEST_NAME, mTestName);
-
-            ContentResolver resolver = mContext.getContentResolver();
-            int numUpdated = resolver.update(TestResultsProvider.RESULTS_CONTENT_URI, values,
-                    TestResultsProvider.COLUMN_TEST_NAME + " = ?",
-                    new String[] {mTestName});
-
-            if (numUpdated == 0) {
-                resolver.insert(TestResultsProvider.RESULTS_CONTENT_URI, values);
-            }
+            TestResultsProvider.setTestResult(mContext, mTestName, mResult);
             return null;
         }
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsBackupHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsBackupHelper.java
new file mode 100644
index 0000000..0966de4
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsBackupHelper.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 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.backup.BackupTestActivity;
+
+import android.app.backup.BackupDataInputStream;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupHelper;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/** {@link BackupHelper} for the test results database. */
+class TestResultsBackupHelper implements BackupHelper {
+
+    private static final String TAG = TestResultsBackupHelper.class.getSimpleName();
+
+    private static final String DB_BACKUP_KEY = "db";
+
+    private final Context mContext;
+
+    TestResultsBackupHelper(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState) {
+        ContentResolver resolver = mContext.getContentResolver();
+        Cursor cursor = null;
+        try {
+            cursor = resolver.query(TestResultsProvider.RESULTS_CONTENT_URI,
+                    null, null, null, null);
+            int nameIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_NAME);
+            int resultIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_RESULT);
+            int infoSeenIndex = cursor.getColumnIndex(TestResultsProvider.COLUMN_TEST_INFO_SEEN);
+
+            ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
+            DataOutputStream dataOutput = new DataOutputStream(byteOutput);
+
+            dataOutput.writeInt(cursor.getCount());
+            while (cursor.moveToNext()) {
+                String name = cursor.getString(nameIndex);
+                int result = cursor.getInt(resultIndex);
+                int infoSeen = cursor.getInt(infoSeenIndex);
+
+                dataOutput.writeUTF(name);
+                dataOutput.writeInt(result);
+                dataOutput.writeInt(infoSeen);
+            }
+
+            byte[] rawBytes = byteOutput.toByteArray();
+            data.writeEntityHeader(DB_BACKUP_KEY, rawBytes.length);
+            data.writeEntityData(rawBytes, rawBytes.length);
+        } catch (IOException e) {
+            Log.e(TAG, "Couldn't backup test results...", e);
+            failBackupTest();
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    @Override
+    public void restoreEntity(BackupDataInputStream data) {
+        try {
+            if (DB_BACKUP_KEY.equals(data.getKey())) {
+                byte[] rawBytes = new byte[data.size()];
+                data.read(rawBytes, 0, data.size());
+
+                ByteArrayInputStream byteInput = new ByteArrayInputStream(rawBytes);
+                DataInputStream dataInput = new DataInputStream(byteInput);
+
+                int numRows = dataInput.readInt();
+                ContentValues[] values = new ContentValues[numRows];
+                for (int i = 0; i < numRows; i++) {
+                    String name = dataInput.readUTF();
+                    int result = dataInput.readInt();
+                    int infoSeen = dataInput.readInt();
+
+                    values[i] = new ContentValues();
+                    values[i].put(TestResultsProvider.COLUMN_TEST_NAME, name);
+                    values[i].put(TestResultsProvider.COLUMN_TEST_RESULT, result);
+                    values[i].put(TestResultsProvider.COLUMN_TEST_INFO_SEEN, infoSeen);
+                }
+
+                ContentResolver resolver = mContext.getContentResolver();
+                resolver.bulkInsert(TestResultsProvider.RESULTS_CONTENT_URI, values);
+            } else {
+                failBackupTest();
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Couldn't restore test results...", e);
+            failBackupTest();
+        }
+    }
+
+    private void failBackupTest() {
+        TestResultsProvider.setTestResult(mContext, BackupTestActivity.class.getName(),
+                TestResult.TEST_RESULT_FAILED);
+    }
+
+    @Override
+    public void writeNewStateDescription(ParcelFileDescriptor newState) {
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
index a41cb43..cc9dc73 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
@@ -16,7 +16,9 @@
 
 package com.android.cts.verifier;
 
+import android.app.backup.BackupManager;
 import android.content.ContentProvider;
+import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.UriMatcher;
@@ -40,18 +42,18 @@
         return Uri.withAppendedPath(RESULTS_CONTENT_URI, testName);
     }
 
-    public static final String _ID = "_id";
+    static final String _ID = "_id";
 
     /** String name of the test like "com.android.cts.verifier.foo.FooTestActivity" */
-    public static final String COLUMN_TEST_NAME = "testname";
+    static final String COLUMN_TEST_NAME = "testname";
 
     /** Integer test result corresponding to constants in {@link TestResult}. */
-    public static final String COLUMN_TEST_RESULT = "testresult";
+    static final String COLUMN_TEST_RESULT = "testresult";
 
     /** Boolean indicating whether the test info has been seen. */
-    public static final String COLUMN_TEST_INFO_SEEN = "testinfoseen";
+    static final String COLUMN_TEST_INFO_SEEN = "testinfoseen";
 
-    public static final String[] ALL_COLUMNS = {
+    static final String[] ALL_COLUMNS = {
         _ID,
         COLUMN_TEST_NAME,
         COLUMN_TEST_RESULT,
@@ -72,9 +74,12 @@
 
     private SQLiteOpenHelper mOpenHelper;
 
+    private BackupManager mBackupManager;
+
     @Override
     public boolean onCreate() {
         mOpenHelper = new TestResultsOpenHelper(getContext());
+        mBackupManager = new BackupManager(getContext());
         return false;
     }
 
@@ -140,14 +145,12 @@
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         long id = db.insert(TABLE_NAME, null, values);
         getContext().getContentResolver().notifyChange(uri, null);
+        mBackupManager.dataChanged();
         return Uri.withAppendedPath(RESULTS_CONTENT_URI, "" + id);
-
     }
 
     @Override
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-
         int match = URI_MATCHER.match(uri);
         switch (match) {
             case RESULTS_ALL:
@@ -176,9 +179,11 @@
                 throw new IllegalArgumentException("Unknown URI: " + uri);
         }
 
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         int numUpdated = db.update(TABLE_NAME, values, selection, selectionArgs);
         if (numUpdated > 0) {
             getContext().getContentResolver().notifyChange(uri, null);
+            mBackupManager.dataChanged();
         }
         return numUpdated;
     }
@@ -189,6 +194,7 @@
         int numDeleted = db.delete(TABLE_NAME, selection, selectionArgs);
         if (numDeleted > 0) {
             getContext().getContentResolver().notifyChange(uri, null);
+            mBackupManager.dataChanged();
         }
         return numDeleted;
     }
@@ -197,4 +203,20 @@
     public String getType(Uri uri) {
         return null;
     }
+
+    static void setTestResult(Context context, String testName, int testResult) {
+        ContentValues values = new ContentValues(2);
+        values.put(TestResultsProvider.COLUMN_TEST_RESULT, testResult);
+        values.put(TestResultsProvider.COLUMN_TEST_NAME, testName);
+
+        ContentResolver resolver = context.getContentResolver();
+        int numUpdated = resolver.update(TestResultsProvider.RESULTS_CONTENT_URI, values,
+                TestResultsProvider.COLUMN_TEST_NAME + " = ?",
+                new String[] {testName});
+
+        if (numUpdated == 0) {
+            resolver.insert(TestResultsProvider.RESULTS_CONTENT_URI, values);
+        }
+    }
+
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/VerifierBackupAgent.java b/apps/CtsVerifier/src/com/android/cts/verifier/VerifierBackupAgent.java
new file mode 100644
index 0000000..3c980b9
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/VerifierBackupAgent.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 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.backup.BackupTestActivity;
+
+import android.app.backup.BackupAgentHelper;
+
+public class VerifierBackupAgent extends BackupAgentHelper {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        addHelper("test-results", new TestResultsBackupHelper(this));
+        addHelper("backup-test-prefs", BackupTestActivity.getSharedPreferencesBackupHelper(this));
+        addHelper("backup-test-files", BackupTestActivity.getFileBackupHelper(this));
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/backup/BackupTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/backup/BackupTestActivity.java
new file mode 100644
index 0000000..cccc1c2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/backup/BackupTestActivity.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2011 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.backup;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.backup.BackupManager;
+import android.app.backup.FileBackupHelper;
+import android.app.backup.SharedPreferencesBackupHelper;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.Scanner;
+
+/**
+ * Test for checking whether the BackupManager is working properly. It lists the values of
+ * several preferences and contents of files that should get backed up and restored after
+ * running the backup manager and reinstalling the CTS verifier.
+ */
+public class BackupTestActivity extends PassFailButtons.ListActivity {
+
+    private static final String TAG = BackupTestActivity.class.getSimpleName();
+
+    private static final int INSTRUCTIONS_DIALOG_ID = 1;
+
+    private static final String TEST_PREFS_1 = "test-prefs-1";
+    private static final String INT_PREF = "int-pref";
+    private static final String BOOL_PREF = "bool-pref";
+
+    private static final String TEST_PREFS_2 = "test-prefs-2";
+    private static final String FLOAT_PREF = "float-pref";
+    private static final String LONG_PREF = "long-pref";
+    private static final String STRING_PREF = "string-pref";
+
+    private static final String TEST_FILE_1 = "test-file-1";
+    private static final String TEST_FILE_2 = "test-file-2";
+
+    private BackupAdapter mAdapter;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+        setContentView(R.layout.bu_main);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.backup_test, R.string.backup_info, 0);
+
+        mAdapter = new BackupAdapter(this);
+        setListAdapter(mAdapter);
+
+        new LoadBackupItemsTask().execute();
+
+        findViewById(R.id.generate_button).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                new GenerateValuesTask().execute();
+            }
+        });
+    }
+
+    public static SharedPreferencesBackupHelper getSharedPreferencesBackupHelper(Context context) {
+        return new SharedPreferencesBackupHelper(context, TEST_PREFS_1, TEST_PREFS_2);
+    }
+
+    public static FileBackupHelper getFileBackupHelper(Context context) {
+        return new FileBackupHelper(context, TEST_FILE_1, TEST_FILE_2);
+    }
+
+    class LoadBackupItemsTask extends AsyncTask<Void, Void, List<BackupItem>> {
+
+        @Override
+        protected void onPreExecute() {
+            super.onPreExecute();
+            setProgressBarIndeterminateVisibility(true);
+        }
+
+        @Override
+        protected List<BackupItem> doInBackground(Void... params) {
+            List<BackupItem> items = new ArrayList<BackupItem>();
+
+            items.add(new CategoryBackupItem(R.string.bu_preferences));
+            loadPreferenceGroup1(items);
+            loadPreferenceGroup2(items);
+
+            items.add(new CategoryBackupItem(R.string.bu_files));
+            loadFile(TEST_FILE_1, items);
+            loadFile(TEST_FILE_2, items);
+
+            return items;
+        }
+
+        private void loadPreferenceGroup1(List<BackupItem> items) {
+            SharedPreferences prefs = getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
+
+            int intValue = prefs.getInt(INT_PREF, 0);
+            items.add(new PreferenceBackupItem(TEST_PREFS_1, INT_PREF, "" + intValue));
+
+            boolean boolValue = prefs.getBoolean(BOOL_PREF, false);
+            items.add(new PreferenceBackupItem(TEST_PREFS_1, BOOL_PREF, "" + boolValue));
+        }
+
+        private void loadPreferenceGroup2(List<BackupItem> items) {
+            SharedPreferences prefs = getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE);
+
+            float floatValue = prefs.getFloat(FLOAT_PREF, 0.0f);
+            items.add(new PreferenceBackupItem(TEST_PREFS_2, FLOAT_PREF, "" + floatValue));
+
+            long longValue = prefs.getLong(LONG_PREF, 0L);
+            items.add(new PreferenceBackupItem(TEST_PREFS_2, LONG_PREF, "" + longValue));
+
+            String stringValue = prefs.getString(STRING_PREF, null);
+            items.add(new PreferenceBackupItem(TEST_PREFS_2, STRING_PREF, stringValue));
+        }
+
+        private void loadFile(String fileName, List<BackupItem> items) {
+            StringBuilder contents = new StringBuilder();
+            Scanner scanner = null;
+            try {
+                scanner = new Scanner(new File(getFilesDir(), fileName));
+                while (scanner.hasNext()) {
+                    contents.append(scanner.nextLine());
+                }
+                scanner.close();
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "Couldn't find test file but this may be fine...", e);
+            } finally {
+                if (scanner != null) {
+                    scanner.close();
+                }
+            }
+            items.add(new FileBackupItem(fileName, contents.toString()));
+        }
+
+        @Override
+        protected void onPostExecute(List<BackupItem> result) {
+            super.onPostExecute(result);
+            setProgressBarIndeterminateVisibility(false);
+            mAdapter.clear();
+            mAdapter.addAll(result);
+        }
+    }
+
+    class GenerateValuesTask extends AsyncTask<Void, Void, Exception> {
+
+        @Override
+        protected Exception doInBackground(Void... params) {
+            Random random = new Random();
+            generatePreferenceGroup1(random);
+            generatePreferenceGroup2(random);
+            try {
+                generateTestFile(TEST_FILE_1, random);
+                generateTestFile(TEST_FILE_2, random);
+            } catch (FileNotFoundException e) {
+                return e;
+            }
+            return null;
+        }
+
+        private void generatePreferenceGroup1(Random random) {
+            SharedPreferences prefs = getSharedPreferences(TEST_PREFS_1, MODE_PRIVATE);
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putInt(INT_PREF, (random.nextInt(100) + 1));
+            editor.putBoolean(BOOL_PREF, random.nextBoolean());
+            editor.commit();
+        }
+
+        private void generatePreferenceGroup2(Random random) {
+            SharedPreferences prefs = getSharedPreferences(TEST_PREFS_2, MODE_PRIVATE);
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putFloat(FLOAT_PREF, random.nextFloat());
+            editor.putLong(LONG_PREF, random.nextLong());
+            editor.putString(STRING_PREF, "Random number: " + (random.nextInt(100) + 1));
+            editor.commit();
+        }
+
+        private void generateTestFile(String fileName, Random random)
+                throws FileNotFoundException {
+            File file = new File(getFilesDir(), fileName);
+            PrintWriter writer = new PrintWriter(file);
+            writer.write("Random number: " + (random.nextInt(100) + 1));
+            writer.close();
+        }
+
+        @Override
+        protected void onPostExecute(Exception exception) {
+            super.onPostExecute(exception);
+            if (exception != null) {
+                Log.e(TAG, "Couldn't generate test data...", exception);
+                Toast.makeText(BackupTestActivity.this, R.string.bu_generate_error,
+                        Toast.LENGTH_LONG).show();
+            } else {
+                showDialog(INSTRUCTIONS_DIALOG_ID);
+
+                BackupManager backupManager = new BackupManager(BackupTestActivity.this);
+                backupManager.dataChanged();
+
+                new LoadBackupItemsTask().execute();
+            }
+        }
+    }
+
+    @Override
+    public Dialog onCreateDialog(int id, Bundle args) {
+        switch (id) {
+            case INSTRUCTIONS_DIALOG_ID:
+                return new AlertDialog.Builder(this)
+                    .setIcon(android.R.drawable.ic_dialog_info)
+                    .setTitle(R.string.backup_test)
+                    .setMessage(R.string.bu_instructions)
+                    .setPositiveButton(android.R.string.ok, null)
+                    .setNeutralButton(R.string.bu_settings, new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            startActivity(new Intent(Settings.ACTION_PRIVACY_SETTINGS));
+                        }
+                    })
+                    .create();
+
+            default:
+                return super.onCreateDialog(id, args);
+        }
+    }
+
+    interface BackupItem {
+        int getViewType();
+        View getView(LayoutInflater inflater, int position, View convertView, ViewGroup parent);
+    }
+
+    static class CategoryBackupItem implements BackupItem {
+
+        private final int mTitleResId;
+
+        CategoryBackupItem(int titleResId) {
+            mTitleResId = titleResId;
+        }
+
+        @Override
+        public int getViewType() {
+            return 0;
+        }
+
+        @Override
+        public View getView(LayoutInflater inflater, int position, View convertView,
+                ViewGroup parent) {
+            TextView view = (TextView) convertView;
+            if (convertView == null) {
+                view = (TextView) inflater.inflate(R.layout.test_category_row, parent, false);
+            }
+            view.setText(mTitleResId);
+            return view;
+        }
+    }
+
+    static class PreferenceBackupItem implements BackupItem {
+
+        private final String mGroup;
+
+        private final String mName;
+
+        private final String mValue;
+
+        PreferenceBackupItem(String group, String name, String value) {
+            mGroup = group;
+            mName = name;
+            mValue = value;
+        }
+
+        @Override
+        public int getViewType() {
+            return 1;
+        }
+
+        @Override
+        public View getView(LayoutInflater inflater, int position, View convertView,
+                ViewGroup parent) {
+            TextView view = (TextView) convertView;
+            if (convertView == null) {
+                view = (TextView) inflater.inflate(R.layout.bu_preference_row, parent, false);
+            }
+            view.setText(mGroup + "/" + mName + " : " + mValue);
+            return view;
+        }
+    }
+
+    static class FileBackupItem implements BackupItem {
+
+        private final String mName;
+
+        private final String mContents;
+
+        FileBackupItem(String name, String contents) {
+            mName = name;
+            mContents = contents;
+        }
+
+        @Override
+        public int getViewType() {
+            return 2;
+        }
+
+        @Override
+        public View getView(LayoutInflater inflater, int position, View convertView,
+                ViewGroup parent) {
+            TextView view = (TextView) convertView;
+            if (convertView == null) {
+                view = (TextView) inflater.inflate(R.layout.bu_preference_row, parent, false);
+            }
+            view.setText(mName + " : " + mContents);
+            return view;
+        }
+    }
+
+    class BackupAdapter extends BaseAdapter {
+
+        private final LayoutInflater mLayoutInflater;
+
+        private final List<BackupItem> mItems = new ArrayList<BackupItem>();
+
+        public BackupAdapter(Context context) {
+            mLayoutInflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE);
+        }
+
+        public void clear() {
+            mItems.clear();
+        }
+
+        public void addAll(List<BackupItem> items) {
+            mItems.addAll(items);
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public int getCount() {
+            return mItems.size();
+        }
+
+        @Override
+        public BackupItem getItem(int position) {
+            return mItems.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return false;
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 3;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            return getItem(position).getViewType();
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            return getItem(position).getView(mLayoutInflater, position, convertView, parent);
+        }
+    }
+}