diff options
| -rw-r--r-- | tests/HugeBackup/Android.mk | 15 | ||||
| -rw-r--r-- | tests/HugeBackup/AndroidManifest.xml | 44 | ||||
| -rw-r--r-- | tests/HugeBackup/proguard.flags | 3 | ||||
| -rw-r--r-- | tests/HugeBackup/res/layout/backup_restore.xml | 87 | ||||
| -rw-r--r-- | tests/HugeBackup/res/values/strings.xml | 28 | ||||
| -rw-r--r-- | tests/HugeBackup/src/com/android/hugebackup/HugeAgent.java | 261 | ||||
| -rw-r--r-- | tests/HugeBackup/src/com/android/hugebackup/HugeBackupActivity.java | 214 | 
7 files changed, 652 insertions, 0 deletions
diff --git a/tests/HugeBackup/Android.mk b/tests/HugeBackup/Android.mk new file mode 100644 index 000000000000..4789bc8118f2 --- /dev/null +++ b/tests/HugeBackup/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := HugeBackup + +LOCAL_SDK_VERSION := current + +LOCAL_PROGUARD_FLAG_FILES := proguard.flags + +include $(BUILD_PACKAGE) diff --git a/tests/HugeBackup/AndroidManifest.xml b/tests/HugeBackup/AndroidManifest.xml new file mode 100644 index 000000000000..923881b9b0b0 --- /dev/null +++ b/tests/HugeBackup/AndroidManifest.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. +--> + +<!-- Declare the contents of this Android application.  The namespace +     attribute brings in the Android platform namespace, and the package +     supplies a unique name for the application.  When writing your +     own application, the package name must be changed from "com.example.*" +     to come from a domain that you own or have control over. --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" +    package="com.android.hugebackup" +    android:versionCode="1" +    android:versionName="1.0"> + +    <!-- The backup/restore mechanism was introduced in API version 8 --> +    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8" /> + +    <application android:label="Huge Backup" +        android:backupAgent="HugeAgent"> + +        <meta-data android:name="com.google.android.backup.api_key" +            android:value="AEdPqrEAAAAINyoagzQOEEpIH3yw7LYCFN7CRX4FMd6TGIGVaA" /> + +        <activity android:name="HugeBackupActivity"> +            <intent-filter> +                <action android:name="android.intent.action.MAIN" /> +                <category android:name="android.intent.category.LAUNCHER" /> +            </intent-filter> +        </activity> + +    </application> +</manifest> diff --git a/tests/HugeBackup/proguard.flags b/tests/HugeBackup/proguard.flags new file mode 100644 index 000000000000..b4d01bfbaa43 --- /dev/null +++ b/tests/HugeBackup/proguard.flags @@ -0,0 +1,3 @@ +-keepclassmembers class com.android.hugebackup.HugeBackupActivity { +    public void onRestoreButtonClick(android.view.View); +} diff --git a/tests/HugeBackup/res/layout/backup_restore.xml b/tests/HugeBackup/res/layout/backup_restore.xml new file mode 100644 index 000000000000..7f11984e86f9 --- /dev/null +++ b/tests/HugeBackup/res/layout/backup_restore.xml @@ -0,0 +1,87 @@ +<?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. +--> + +<!-- Layout description of the BackupRestore sample's main activity --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:orientation="vertical" +    android:layout_width="match_parent" +    android:layout_height="wrap_content"> + +    <ScrollView +        android:orientation="vertical" +        android:layout_width="fill_parent" +        android:layout_height="fill_parent" +        android:layout_weight="1"> + +        <LinearLayout +            android:orientation="vertical" +            android:layout_width="match_parent" +            android:layout_height="wrap_content"> + +            <TextView android:text="@string/filling_text" +                android:textSize="20dp" +                android:layout_marginTop="20dp" +                android:layout_marginBottom="10dp" +                android:layout_width="match_parent" +                android:layout_height="wrap_content"/> + +            <RadioGroup android:id="@+id/filling_group" +                android:layout_width="match_parent" +                android:layout_height="wrap_content" +                android:layout_marginLeft="20dp" +                android:orientation="vertical"> + +                <RadioButton android:id="@+id/bacon" +                    android:text="@string/bacon_label"/> +                <RadioButton android:id="@+id/pastrami" +                    android:text="@string/pastrami_label"/> +                <RadioButton android:id="@+id/hummus" +                    android:text="@string/hummus_label"/> + +            </RadioGroup> + +            <TextView android:text="@string/extras_text" +                android:textSize="20dp" +                android:layout_marginTop="20dp" +                android:layout_marginBottom="10dp" +                android:layout_width="match_parent" +                android:layout_height="wrap_content"/> + +            <CheckBox android:id="@+id/mayo" +                android:text="@string/mayo_text" +                android:layout_marginLeft="20dp" +                android:layout_width="match_parent" +                android:layout_height="wrap_content"/> + +            <CheckBox android:id="@+id/tomato" +                android:text="@string/tomato_text" +                android:layout_marginLeft="20dp" +                android:layout_width="match_parent" +                android:layout_height="wrap_content"/> + +        </LinearLayout> + +    </ScrollView> + +    <Button android:id="@+id/restore_button" +        android:text="@string/restore_text" +        android:onClick="onRestoreButtonClick" +        android:layout_height="wrap_content" +        android:layout_width="wrap_content" +        android:layout_weight="0" /> + +</LinearLayout> diff --git a/tests/HugeBackup/res/values/strings.xml b/tests/HugeBackup/res/values/strings.xml new file mode 100644 index 000000000000..c0b9226ef690 --- /dev/null +++ b/tests/HugeBackup/res/values/strings.xml @@ -0,0 +1,28 @@ +<?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. +--> + +<resources> +  <string name="filling_text">Choose a sandwich filling:</string> +  <string name="bacon_label">Bacon</string> +  <string name="pastrami_label">Pastrami</string> +  <string name="hummus_label">Hummus</string> + +  <string name="extras_text">Extras:</string> +  <string name="mayo_text">Mayonnaise\?</string> +  <string name="tomato_text">Tomato\?</string> + +  <string name="restore_text">Restore last data</string> +</resources> diff --git a/tests/HugeBackup/src/com/android/hugebackup/HugeAgent.java b/tests/HugeBackup/src/com/android/hugebackup/HugeAgent.java new file mode 100644 index 000000000000..f90bd9f8bf18 --- /dev/null +++ b/tests/HugeBackup/src/com/android/hugebackup/HugeAgent.java @@ -0,0 +1,261 @@ +/* + * 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.hugebackup; + +import android.app.backup.BackupAgent; +import android.app.backup.BackupDataInput; +import android.app.backup.BackupDataOutput; +import android.os.ParcelFileDescriptor; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * This is the backup/restore agent class for the BackupRestore sample + * application.  This particular agent illustrates using the backup and + * restore APIs directly, without taking advantage of any helper classes. + */ +public class HugeAgent extends BackupAgent { +    /** +     * We put a simple version number into the state files so that we can +     * tell properly how to read "old" versions if at some point we want +     * to change what data we back up and how we store the state blob. +     */ +    static final int AGENT_VERSION = 1; + +    /** +     * Pick an arbitrary string to use as the "key" under which the +     * data is backed up.  This key identifies different data records +     * within this one application's data set.  Since we only maintain +     * one piece of data we don't need to distinguish, so we just pick +     * some arbitrary tag to use. +     */ +    static final String APP_DATA_KEY = "alldata"; +    static final String HUGE_DATA_KEY = "colossus"; + +    /** The app's current data, read from the live disk file */ +    boolean mAddMayo; +    boolean mAddTomato; +    int mFilling; + +    /** The location of the application's persistent data file */ +    File mDataFile; + +    /** For convenience, we set up the File object for the app's data on creation */ +    @Override +    public void onCreate() { +        mDataFile = new File(getFilesDir(), HugeBackupActivity.DATA_FILE_NAME); +    } + +    /** +     * The set of data backed up by this application is very small: just +     * two booleans and an integer.  With such a simple dataset, it's +     * easiest to simply store a copy of the backed-up data as the state +     * blob describing the last dataset backed up.  The state file +     * contents can be anything; it is private to the agent class, and +     * is never stored off-device. +     * +     * <p>One thing that an application may wish to do is tag the state +     * blob contents with a version number.  This is so that if the +     * application is upgraded, the next time it attempts to do a backup, +     * it can detect that the last backup operation was performed by an +     * older version of the agent, and might therefore require different +     * handling. +     */ +    @Override +    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, +            ParcelFileDescriptor newState) throws IOException { +        // First, get the current data from the application's file.  This +        // may throw an IOException, but in that case something has gone +        // badly wrong with the app's data on disk, and we do not want +        // to back up garbage data.  If we just let the exception go, the +        // Backup Manager will handle it and simply skip the current +        // backup operation. +        synchronized (HugeBackupActivity.sDataLock) { +            RandomAccessFile file = new RandomAccessFile(mDataFile, "r"); +            mFilling = file.readInt(); +            mAddMayo = file.readBoolean(); +            mAddTomato = file.readBoolean(); +        } + +        // If the new state file descriptor is null, this is the first time +        // a backup is being performed, so we know we have to write the +        // data.  If there <em>is</em> a previous state blob, we want to +        // double check whether the current data is actually different from +        // our last backup, so that we can avoid transmitting redundant +        // data to the storage backend. +        boolean doBackup = (oldState == null); +        if (!doBackup) { +            doBackup = compareStateFile(oldState); +        } + +        // If we decided that we do in fact need to write our dataset, go +        // ahead and do that.  The way this agent backs up the data is to +        // flatten it into a single buffer, then write that to the backup +        // transport under the single key string. +        if (doBackup) { +            ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); + +            // We use a DataOutputStream to write structured data into +            // the buffering stream +            DataOutputStream outWriter = new DataOutputStream(bufStream); +            outWriter.writeInt(mFilling); +            outWriter.writeBoolean(mAddMayo); +            outWriter.writeBoolean(mAddTomato); + +            // Okay, we've flattened the data for transmission.  Pull it +            // out of the buffering stream object and send it off. +            byte[] buffer = bufStream.toByteArray(); +            int len = buffer.length; +            data.writeEntityHeader(APP_DATA_KEY, len); +            data.writeEntityData(buffer, len); + +            // ***** pathological behavior ***** +            // Now, in order to incur deliberate too-much-data failures, +            // try to back up 20 MB of data besides what we already pushed. +            final int MEGABYTE = 1024*1024; +            final int NUM_MEGS = 20; +            buffer = new byte[MEGABYTE]; +            data.writeEntityHeader(HUGE_DATA_KEY, NUM_MEGS * MEGABYTE); +            for (int i = 0; i < NUM_MEGS; i++) { +                data.writeEntityData(buffer, MEGABYTE); +            } +        } + +        // Finally, in all cases, we need to write the new state blob +        writeStateFile(newState); +    } + +    /** +     * Helper routine - read a previous state file and decide whether to +     * perform a backup based on its contents. +     * +     * @return <code>true</code> if the application's data has changed since +     *   the last backup operation; <code>false</code> otherwise. +     */ +    boolean compareStateFile(ParcelFileDescriptor oldState) { +        FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); +        DataInputStream in = new DataInputStream(instream); + +        try { +            int stateVersion = in.readInt(); +            if (stateVersion > AGENT_VERSION) { +                // Whoops; the last version of the app that backed up +                // data on this device was <em>newer</em> than the current +                // version -- the user has downgraded.  That's problematic. +                // In this implementation, we recover by simply rewriting +                // the backup. +                return true; +            } + +            // The state data we store is just a mirror of the app's data; +            // read it from the state file then return 'true' if any of +            // it differs from the current data. +            int lastFilling = in.readInt(); +            boolean lastMayo = in.readBoolean(); +            boolean lastTomato = in.readBoolean(); + +            return (lastFilling != mFilling) +                    || (lastTomato != mAddTomato) +                    || (lastMayo != mAddMayo); +        } catch (IOException e) { +            // If something went wrong reading the state file, be safe +            // and back up the data again. +            return true; +        } +    } + +    /** +     * Write out the new state file:  the version number, followed by the +     * three bits of data as we sent them off to the backup transport. +     */ +    void writeStateFile(ParcelFileDescriptor stateFile) throws IOException { +        FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); +        DataOutputStream out = new DataOutputStream(outstream); + +        out.writeInt(AGENT_VERSION); +        out.writeInt(mFilling); +        out.writeBoolean(mAddMayo); +        out.writeBoolean(mAddTomato); +    } + +    /** +     * This application does not do any "live" restores of its own data, +     * so the only time a restore will happen is when the application is +     * installed.  This means that the activity itself is not going to +     * be running while we change its data out from under it.  That, in +     * turn, means that there is no need to send out any sort of notification +     * of the new data:  we only need to read the data from the stream +     * provided here, build the application's new data file, and then +     * write our new backup state blob that will be consulted at the next +     * backup operation. +     * +     * <p>We don't bother checking the versionCode of the app who originated +     * the data because we have never revised the backup data format.  If +     * we had, the 'appVersionCode' parameter would tell us how we should +     * interpret the data we're about to read. +     */ +    @Override +    public void onRestore(BackupDataInput data, int appVersionCode, +            ParcelFileDescriptor newState) throws IOException { +        // We should only see one entity in the data stream, but the safest +        // way to consume it is using a while() loop +        while (data.readNextHeader()) { +            String key = data.getKey(); +            int dataSize = data.getDataSize(); + +            if (APP_DATA_KEY.equals(key)) { +                // It's our saved data, a flattened chunk of data all in +                // one buffer.  Use some handy structured I/O classes to +                // extract it. +                byte[] dataBuf = new byte[dataSize]; +                data.readEntityData(dataBuf, 0, dataSize); +                ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); +                DataInputStream in = new DataInputStream(baStream); + +                mFilling = in.readInt(); +                mAddMayo = in.readBoolean(); +                mAddTomato = in.readBoolean(); + +                // Now we are ready to construct the app's data file based +                // on the data we are restoring from. +                synchronized (HugeBackupActivity.sDataLock) { +                    RandomAccessFile file = new RandomAccessFile(mDataFile, "rw"); +                    file.setLength(0L); +                    file.writeInt(mFilling); +                    file.writeBoolean(mAddMayo); +                    file.writeBoolean(mAddTomato); +                } +            } else { +                // Curious!  This entity is data under a key we do not +                // understand how to process.  Just skip it. +                data.skipEntityData(); +            } +        } + +        // The last thing to do is write the state blob that describes the +        // app's data as restored from backup. +        writeStateFile(newState); +    } +} diff --git a/tests/HugeBackup/src/com/android/hugebackup/HugeBackupActivity.java b/tests/HugeBackup/src/com/android/hugebackup/HugeBackupActivity.java new file mode 100644 index 000000000000..84e31aa8417e --- /dev/null +++ b/tests/HugeBackup/src/com/android/hugebackup/HugeBackupActivity.java @@ -0,0 +1,214 @@ +/* + * 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.hugebackup; + +import android.app.Activity; +import android.app.backup.BackupManager; +import android.app.backup.RestoreObserver; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.RadioGroup; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * Deliberately back up waaaaaaay too much data.  Cloned with some alterations + * from the Backup/Restore sample application. + */ +public class HugeBackupActivity extends Activity { +    static final String TAG = "HugeBackupActivity"; + +    /** +     * We serialize access to our persistent data through a global static +     * object.  This ensures that in the unlikely event of the our backup/restore +     * agent running to perform a backup while our UI is updating the file, the +     * agent will not accidentally read partially-written data. +     * +     * <p>Curious but true: a zero-length array is slightly lighter-weight than +     * merely allocating an Object, and can still be synchronized on. +     */ +    static final Object[] sDataLock = new Object[0]; + +    /** Also supply a global standard file name for everyone to use */ +    static final String DATA_FILE_NAME = "saved_data"; + +    /** The various bits of UI that the user can manipulate */ +    RadioGroup mFillingGroup; +    CheckBox mAddMayoCheckbox; +    CheckBox mAddTomatoCheckbox; + +    /** Cache a reference to our persistent data file */ +    File mDataFile; + +    /** Also cache a reference to the Backup Manager */ +    BackupManager mBackupManager; + +    /** Set up the activity and populate its UI from the persistent data. */ +    @Override +    public void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); + +        /** Establish the activity's UI */ +        setContentView(R.layout.backup_restore); + +        /** Once the UI has been inflated, cache the controls for later */ +        mFillingGroup = (RadioGroup) findViewById(R.id.filling_group); +        mAddMayoCheckbox = (CheckBox) findViewById(R.id.mayo); +        mAddTomatoCheckbox = (CheckBox) findViewById(R.id.tomato); + +        /** Set up our file bookkeeping */ +        mDataFile = new File(getFilesDir(), HugeBackupActivity.DATA_FILE_NAME); + +        /** It is handy to keep a BackupManager cached */ +        mBackupManager = new BackupManager(this); + +        /** +         * Finally, build the UI from the persistent store +         */ +        populateUI(); +    } + +    /** +     * Configure the UI based on our persistent data, creating the +     * data file and establishing defaults if necessary. +     */ +    void populateUI() { +        RandomAccessFile file; + +        // Default values in case there's no data file yet +        int whichFilling = R.id.pastrami; +        boolean addMayo = false; +        boolean addTomato = false; + +        /** Hold the data-access lock around access to the file */ +        synchronized (HugeBackupActivity.sDataLock) { +            boolean exists = mDataFile.exists(); +            try { +                file = new RandomAccessFile(mDataFile, "rw"); +                if (exists) { +                    Log.v(TAG, "datafile exists"); +                    whichFilling = file.readInt(); +                    addMayo = file.readBoolean(); +                    addTomato = file.readBoolean(); +                    Log.v(TAG, "  mayo=" + addMayo +                            + " tomato=" + addTomato +                            + " filling=" + whichFilling); +                } else { +                    // The default values were configured above: write them +                    // to the newly-created file. +                    Log.v(TAG, "creating default datafile"); +                    writeDataToFileLocked(file, +                            addMayo, addTomato, whichFilling); + +                    // We also need to perform an initial backup; ask for one +                    mBackupManager.dataChanged(); +                } +            } catch (IOException ioe) { +            } +        } + +        /** Now that we've processed the file, build the UI outside the lock */ +        mFillingGroup.check(whichFilling); +        mAddMayoCheckbox.setChecked(addMayo); +        mAddTomatoCheckbox.setChecked(addTomato); + +        /** +         * We also want to record the new state when the user makes changes, +         * so install simple observers that do this +         */ +        mFillingGroup.setOnCheckedChangeListener( +                new RadioGroup.OnCheckedChangeListener() { +                    public void onCheckedChanged(RadioGroup group, +                            int checkedId) { +                        // As with the checkbox listeners, rewrite the +                        // entire state file +                        Log.v(TAG, "New radio item selected: " + checkedId); +                        recordNewUIState(); +                    } +                }); + +        CompoundButton.OnCheckedChangeListener checkListener +                = new CompoundButton.OnCheckedChangeListener() { +            public void onCheckedChanged(CompoundButton buttonView, +                    boolean isChecked) { +                // Whichever one is altered, we rewrite the entire UI state +                Log.v(TAG, "Checkbox toggled: " + buttonView); +                recordNewUIState(); +            } +        }; +        mAddMayoCheckbox.setOnCheckedChangeListener(checkListener); +        mAddTomatoCheckbox.setOnCheckedChangeListener(checkListener); +    } + +    /** +     * Handy helper routine to write the UI data to a file. +     */ +    void writeDataToFileLocked(RandomAccessFile file, +            boolean addMayo, boolean addTomato, int whichFilling) +        throws IOException { +            file.setLength(0L); +            file.writeInt(whichFilling); +            file.writeBoolean(addMayo); +            file.writeBoolean(addTomato); +            Log.v(TAG, "NEW STATE: mayo=" + addMayo +                    + " tomato=" + addTomato +                    + " filling=" + whichFilling); +    } + +    /** +     * Another helper; this one reads the current UI state and writes that +     * to the persistent store, then tells the backup manager that we need +     * a backup. +     */ +    void recordNewUIState() { +        boolean addMayo = mAddMayoCheckbox.isChecked(); +        boolean addTomato = mAddTomatoCheckbox.isChecked(); +        int whichFilling = mFillingGroup.getCheckedRadioButtonId(); +        try { +            synchronized (HugeBackupActivity.sDataLock) { +                RandomAccessFile file = new RandomAccessFile(mDataFile, "rw"); +                writeDataToFileLocked(file, addMayo, addTomato, whichFilling); +            } +        } catch (IOException e) { +            Log.e(TAG, "Unable to record new UI state"); +        } + +        mBackupManager.dataChanged(); +    } + +    /** +     * Click handler, designated in the layout, that runs a restore of the app's +     * most recent data when the button is pressed. +     */ +    public void onRestoreButtonClick(View v) { +        Log.v(TAG, "Requesting restore of our most recent data"); +        mBackupManager.requestRestore( +                new RestoreObserver() { +                    public void restoreFinished(int error) { +                        /** Done with the restore!  Now draw the new state of our data */ +                        Log.v(TAG, "Restore finished, error = " + error); +                        populateUI(); +                    } +                } +        ); +    } +}  |