1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.hugebackup; 18 19 import android.app.Activity; 20 import android.app.backup.BackupManager; 21 import android.app.backup.RestoreObserver; 22 import android.os.Bundle; 23 import android.util.Log; 24 import android.view.View; 25 import android.widget.CheckBox; 26 import android.widget.CompoundButton; 27 import android.widget.RadioGroup; 28 29 import java.io.File; 30 import java.io.IOException; 31 import java.io.RandomAccessFile; 32 33 /** 34 * Deliberately back up waaaaaaay too much data. Cloned with some alterations 35 * from the Backup/Restore sample application. 36 */ 37 public class HugeBackupActivity extends Activity { 38 static final String TAG = "HugeBackupActivity"; 39 40 /** 41 * We serialize access to our persistent data through a global static 42 * object. This ensures that in the unlikely event of the our backup/restore 43 * agent running to perform a backup while our UI is updating the file, the 44 * agent will not accidentally read partially-written data. 45 * 46 * <p>Curious but true: a zero-length array is slightly lighter-weight than 47 * merely allocating an Object, and can still be synchronized on. 48 */ 49 static final Object[] sDataLock = new Object[0]; 50 51 /** Also supply a global standard file name for everyone to use */ 52 static final String DATA_FILE_NAME = "saved_data"; 53 54 /** The various bits of UI that the user can manipulate */ 55 RadioGroup mFillingGroup; 56 CheckBox mAddMayoCheckbox; 57 CheckBox mAddTomatoCheckbox; 58 59 /** Cache a reference to our persistent data file */ 60 File mDataFile; 61 62 /** Also cache a reference to the Backup Manager */ 63 BackupManager mBackupManager; 64 65 /** Set up the activity and populate its UI from the persistent data. */ 66 @Override onCreate(Bundle savedInstanceState)67 public void onCreate(Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 70 /** Establish the activity's UI */ 71 setContentView(R.layout.backup_restore); 72 73 /** Once the UI has been inflated, cache the controls for later */ 74 mFillingGroup = findViewById(R.id.filling_group); 75 mAddMayoCheckbox = findViewById(R.id.mayo); 76 mAddTomatoCheckbox = findViewById(R.id.tomato); 77 78 /** Set up our file bookkeeping */ 79 mDataFile = new File(getFilesDir(), HugeBackupActivity.DATA_FILE_NAME); 80 81 /** It is handy to keep a BackupManager cached */ 82 mBackupManager = new BackupManager(this); 83 84 /** 85 * Finally, build the UI from the persistent store 86 */ 87 populateUI(); 88 } 89 90 /** 91 * Configure the UI based on our persistent data, creating the 92 * data file and establishing defaults if necessary. 93 */ populateUI()94 void populateUI() { 95 RandomAccessFile file; 96 97 // Default values in case there's no data file yet 98 int whichFilling = R.id.pastrami; 99 boolean addMayo = false; 100 boolean addTomato = false; 101 102 /** Hold the data-access lock around access to the file */ 103 synchronized (HugeBackupActivity.sDataLock) { 104 boolean exists = mDataFile.exists(); 105 try { 106 file = new RandomAccessFile(mDataFile, "rw"); 107 if (exists) { 108 Log.v(TAG, "datafile exists"); 109 whichFilling = file.readInt(); 110 addMayo = file.readBoolean(); 111 addTomato = file.readBoolean(); 112 Log.v(TAG, " mayo=" + addMayo 113 + " tomato=" + addTomato 114 + " filling=" + whichFilling); 115 } else { 116 // The default values were configured above: write them 117 // to the newly-created file. 118 Log.v(TAG, "creating default datafile"); 119 writeDataToFileLocked(file, 120 addMayo, addTomato, whichFilling); 121 122 // We also need to perform an initial backup; ask for one 123 mBackupManager.dataChanged(); 124 } 125 } catch (IOException ioe) { 126 } 127 } 128 129 /** Now that we've processed the file, build the UI outside the lock */ 130 mFillingGroup.check(whichFilling); 131 mAddMayoCheckbox.setChecked(addMayo); 132 mAddTomatoCheckbox.setChecked(addTomato); 133 134 /** 135 * We also want to record the new state when the user makes changes, 136 * so install simple observers that do this 137 */ 138 mFillingGroup.setOnCheckedChangeListener( 139 new RadioGroup.OnCheckedChangeListener() { 140 public void onCheckedChanged(RadioGroup group, 141 int checkedId) { 142 // As with the checkbox listeners, rewrite the 143 // entire state file 144 Log.v(TAG, "New radio item selected: " + checkedId); 145 recordNewUIState(); 146 } 147 }); 148 149 CompoundButton.OnCheckedChangeListener checkListener 150 = new CompoundButton.OnCheckedChangeListener() { 151 public void onCheckedChanged(CompoundButton buttonView, 152 boolean isChecked) { 153 // Whichever one is altered, we rewrite the entire UI state 154 Log.v(TAG, "Checkbox toggled: " + buttonView); 155 recordNewUIState(); 156 } 157 }; 158 mAddMayoCheckbox.setOnCheckedChangeListener(checkListener); 159 mAddTomatoCheckbox.setOnCheckedChangeListener(checkListener); 160 } 161 162 /** 163 * Handy helper routine to write the UI data to a file. 164 */ writeDataToFileLocked(RandomAccessFile file, boolean addMayo, boolean addTomato, int whichFilling)165 void writeDataToFileLocked(RandomAccessFile file, 166 boolean addMayo, boolean addTomato, int whichFilling) 167 throws IOException { 168 file.setLength(0L); 169 file.writeInt(whichFilling); 170 file.writeBoolean(addMayo); 171 file.writeBoolean(addTomato); 172 Log.v(TAG, "NEW STATE: mayo=" + addMayo 173 + " tomato=" + addTomato 174 + " filling=" + whichFilling); 175 } 176 177 /** 178 * Another helper; this one reads the current UI state and writes that 179 * to the persistent store, then tells the backup manager that we need 180 * a backup. 181 */ recordNewUIState()182 void recordNewUIState() { 183 boolean addMayo = mAddMayoCheckbox.isChecked(); 184 boolean addTomato = mAddTomatoCheckbox.isChecked(); 185 int whichFilling = mFillingGroup.getCheckedRadioButtonId(); 186 try { 187 synchronized (HugeBackupActivity.sDataLock) { 188 RandomAccessFile file = new RandomAccessFile(mDataFile, "rw"); 189 writeDataToFileLocked(file, addMayo, addTomato, whichFilling); 190 } 191 } catch (IOException e) { 192 Log.e(TAG, "Unable to record new UI state"); 193 } 194 195 mBackupManager.dataChanged(); 196 } 197 198 /** 199 * Click handler, designated in the layout, that runs a restore of the app's 200 * most recent data when the button is pressed. 201 */ onRestoreButtonClick(View v)202 public void onRestoreButtonClick(View v) { 203 Log.v(TAG, "Requesting restore of our most recent data"); 204 mBackupManager.requestRestore( 205 new RestoreObserver() { 206 public void restoreFinished(int error) { 207 /** Done with the restore! Now draw the new state of our data */ 208 Log.v(TAG, "Restore finished, error = " + error); 209 populateUI(); 210 } 211 } 212 ); 213 } 214 } 215