138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate/* 238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Copyright (C) 2011 The Android Open Source Project 338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * 438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Licensed under the Apache License, Version 2.0 (the "License"); 538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * you may not use this file except in compliance with the License. 638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * You may obtain a copy of the License at 738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * 838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * http://www.apache.org/licenses/LICENSE-2.0 938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * 1038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Unless required by applicable law or agreed to in writing, software 1138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * distributed under the License is distributed on an "AS IS" BASIS, 1238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * See the License for the specific language governing permissions and 1438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * limitations under the License. 1538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */ 1638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 1738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tatepackage com.android.hugebackup; 1838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 1938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.app.Activity; 2038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.app.backup.BackupManager; 2138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.app.backup.RestoreObserver; 2238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.os.Bundle; 2338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.util.Log; 2438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.view.View; 2538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.widget.CheckBox; 2638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.widget.CompoundButton; 2738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.widget.RadioGroup; 2838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 2938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.File; 3038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.IOException; 3138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.RandomAccessFile; 3238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 3338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate/** 3438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Deliberately back up waaaaaaay too much data. Cloned with some alterations 3538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * from the Backup/Restore sample application. 3638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */ 3738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tatepublic class HugeBackupActivity extends Activity { 3838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate static final String TAG = "HugeBackupActivity"; 3938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 4038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** 4138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * We serialize access to our persistent data through a global static 4238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * object. This ensures that in the unlikely event of the our backup/restore 4338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * agent running to perform a backup while our UI is updating the file, the 4438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * agent will not accidentally read partially-written data. 4538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * 4638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * <p>Curious but true: a zero-length array is slightly lighter-weight than 4738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * merely allocating an Object, and can still be synchronized on. 4838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */ 4938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate static final Object[] sDataLock = new Object[0]; 5038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 5138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** Also supply a global standard file name for everyone to use */ 5238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate static final String DATA_FILE_NAME = "saved_data"; 5338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 5438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** The various bits of UI that the user can manipulate */ 5538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate RadioGroup mFillingGroup; 5638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate CheckBox mAddMayoCheckbox; 5738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate CheckBox mAddTomatoCheckbox; 5838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 5938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** Cache a reference to our persistent data file */ 6038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate File mDataFile; 6138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 6238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** Also cache a reference to the Backup Manager */ 6338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate BackupManager mBackupManager; 6438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 6538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** Set up the activity and populate its UI from the persistent data. */ 6638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate @Override 6738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate public void onCreate(Bundle savedInstanceState) { 6838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate super.onCreate(savedInstanceState); 6938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 7038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** Establish the activity's UI */ 7138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate setContentView(R.layout.backup_restore); 7238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 7338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** Once the UI has been inflated, cache the controls for later */ 7438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mFillingGroup = (RadioGroup) findViewById(R.id.filling_group); 7538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mAddMayoCheckbox = (CheckBox) findViewById(R.id.mayo); 7638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mAddTomatoCheckbox = (CheckBox) findViewById(R.id.tomato); 7738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 7838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** Set up our file bookkeeping */ 7938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mDataFile = new File(getFilesDir(), HugeBackupActivity.DATA_FILE_NAME); 8038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 8138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** It is handy to keep a BackupManager cached */ 8238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mBackupManager = new BackupManager(this); 8338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 8438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** 8538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Finally, build the UI from the persistent store 8638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */ 8738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate populateUI(); 8838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 8938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 9038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** 9138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Configure the UI based on our persistent data, creating the 9238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * data file and establishing defaults if necessary. 9338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */ 9438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate void populateUI() { 9538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate RandomAccessFile file; 9638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 9738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate // Default values in case there's no data file yet 9838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate int whichFilling = R.id.pastrami; 9938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate boolean addMayo = false; 10038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate boolean addTomato = false; 10138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 10238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** Hold the data-access lock around access to the file */ 10338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate synchronized (HugeBackupActivity.sDataLock) { 10438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate boolean exists = mDataFile.exists(); 10538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate try { 10638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate file = new RandomAccessFile(mDataFile, "rw"); 10738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate if (exists) { 10838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate Log.v(TAG, "datafile exists"); 10938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate whichFilling = file.readInt(); 11038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate addMayo = file.readBoolean(); 11138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate addTomato = file.readBoolean(); 11238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate Log.v(TAG, " mayo=" + addMayo 11338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate + " tomato=" + addTomato 11438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate + " filling=" + whichFilling); 11538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } else { 11638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate // The default values were configured above: write them 11738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate // to the newly-created file. 11838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate Log.v(TAG, "creating default datafile"); 11938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate writeDataToFileLocked(file, 12038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate addMayo, addTomato, whichFilling); 12138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 12238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate // We also need to perform an initial backup; ask for one 12338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mBackupManager.dataChanged(); 12438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 12538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } catch (IOException ioe) { 12638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 12738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 12838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 12938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** Now that we've processed the file, build the UI outside the lock */ 13038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mFillingGroup.check(whichFilling); 13138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mAddMayoCheckbox.setChecked(addMayo); 13238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mAddTomatoCheckbox.setChecked(addTomato); 13338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 13438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** 13538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * We also want to record the new state when the user makes changes, 13638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * so install simple observers that do this 13738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */ 13838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mFillingGroup.setOnCheckedChangeListener( 13938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate new RadioGroup.OnCheckedChangeListener() { 14038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate public void onCheckedChanged(RadioGroup group, 14138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate int checkedId) { 14238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate // As with the checkbox listeners, rewrite the 14338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate // entire state file 14438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate Log.v(TAG, "New radio item selected: " + checkedId); 14538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate recordNewUIState(); 14638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 14738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate }); 14838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 14938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate CompoundButton.OnCheckedChangeListener checkListener 15038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate = new CompoundButton.OnCheckedChangeListener() { 15138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate public void onCheckedChanged(CompoundButton buttonView, 15238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate boolean isChecked) { 15338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate // Whichever one is altered, we rewrite the entire UI state 15438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate Log.v(TAG, "Checkbox toggled: " + buttonView); 15538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate recordNewUIState(); 15638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 15738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate }; 15838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mAddMayoCheckbox.setOnCheckedChangeListener(checkListener); 15938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mAddTomatoCheckbox.setOnCheckedChangeListener(checkListener); 16038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 16138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 16238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** 16338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Handy helper routine to write the UI data to a file. 16438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */ 16538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate void writeDataToFileLocked(RandomAccessFile file, 16638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate boolean addMayo, boolean addTomato, int whichFilling) 16738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate throws IOException { 16838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate file.setLength(0L); 16938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate file.writeInt(whichFilling); 17038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate file.writeBoolean(addMayo); 17138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate file.writeBoolean(addTomato); 17238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate Log.v(TAG, "NEW STATE: mayo=" + addMayo 17338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate + " tomato=" + addTomato 17438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate + " filling=" + whichFilling); 17538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 17638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 17738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** 17838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Another helper; this one reads the current UI state and writes that 17938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * to the persistent store, then tells the backup manager that we need 18038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * a backup. 18138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */ 18238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate void recordNewUIState() { 18338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate boolean addMayo = mAddMayoCheckbox.isChecked(); 18438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate boolean addTomato = mAddTomatoCheckbox.isChecked(); 18538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate int whichFilling = mFillingGroup.getCheckedRadioButtonId(); 18638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate try { 18738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate synchronized (HugeBackupActivity.sDataLock) { 18838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate RandomAccessFile file = new RandomAccessFile(mDataFile, "rw"); 18938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate writeDataToFileLocked(file, addMayo, addTomato, whichFilling); 19038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 19138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } catch (IOException e) { 19238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate Log.e(TAG, "Unable to record new UI state"); 19338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 19438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 19538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mBackupManager.dataChanged(); 19638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 19738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate 19838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** 19938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Click handler, designated in the layout, that runs a restore of the app's 20038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * most recent data when the button is pressed. 20138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */ 20238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate public void onRestoreButtonClick(View v) { 20338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate Log.v(TAG, "Requesting restore of our most recent data"); 20438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate mBackupManager.requestRestore( 20538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate new RestoreObserver() { 20638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate public void restoreFinished(int error) { 20738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate /** Done with the restore! Now draw the new state of our data */ 20838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate Log.v(TAG, "Restore finished, error = " + error); 20938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate populateUI(); 21038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 21138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 21238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate ); 21338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate } 21438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate} 215