forked from upstream-mirrors/OpenTracks
Remove backup and restore, move reset to advanced setting
This commit is contained in:
@@ -71,8 +71,6 @@ limitations under the License.
|
|||||||
<string name="weight_key">weight</string>
|
<string name="weight_key">weight</string>
|
||||||
<!-- Keys for non-persisted preferences. Sorted alphabetically. DO NOT TRANSLATE. -->
|
<!-- Keys for non-persisted preferences. Sorted alphabetically. DO NOT TRANSLATE. -->
|
||||||
<string name="settings_advanced_key">settingsAdvanced</string>
|
<string name="settings_advanced_key">settingsAdvanced</string>
|
||||||
<string name="settings_backup_key">settingsBackup</string>
|
|
||||||
<string name="settings_backup_reset_key">settingsBackupReset</string>
|
|
||||||
<string name="settings_chart_key">settingsChart</string>
|
<string name="settings_chart_key">settingsChart</string>
|
||||||
<string name="settings_google_key">settingsGoogle</string>
|
<string name="settings_google_key">settingsGoogle</string>
|
||||||
<string name="settings_map_key">settingsMap</string>
|
<string name="settings_map_key">settingsMap</string>
|
||||||
@@ -81,7 +79,6 @@ limitations under the License.
|
|||||||
<string name="settings_map_slow_display_key">settingsMapSlowDisplay</string>
|
<string name="settings_map_slow_display_key">settingsMapSlowDisplay</string>
|
||||||
<string name="settings_recording_key">settingsRecording</string>
|
<string name="settings_recording_key">settingsRecording</string>
|
||||||
<string name="settings_reset_key">settingsReset</string>
|
<string name="settings_reset_key">settingsReset</string>
|
||||||
<string name="settings_restore_key">settingsRestore</string>
|
|
||||||
<string name="settings_sensor_key">settingsSensor</string>
|
<string name="settings_sensor_key">settingsSensor</string>
|
||||||
<string name="settings_sensor_ant_key">settingsSensorAnt</string>
|
<string name="settings_sensor_ant_key">settingsSensorAnt</string>
|
||||||
<string name="settings_sensor_ant_reset_bike_cadence_sensor_key">settingsSensorAntResetBikeCadenceSensor</string>
|
<string name="settings_sensor_ant_reset_bike_cadence_sensor_key">settingsSensorAntResetBikeCadenceSensor</string>
|
||||||
|
|||||||
@@ -1551,100 +1551,6 @@ limitations under the License.
|
|||||||
Photo size
|
Photo size
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<!-- Settings Backup Reset -->
|
|
||||||
<string name="settings_backup_reset"
|
|
||||||
translation_description="In the 'Settings' page, the option backup and reset.
|
|
||||||
|
|
||||||
Character limit: ~26 (1 line)">
|
|
||||||
Backup & reset
|
|
||||||
</string>
|
|
||||||
|
|
||||||
<!-- Settings Backup -->
|
|
||||||
<string name="settings_backup"
|
|
||||||
translation_description="In the 'Backup and reset' settings, the option to backup to the
|
|
||||||
external storage.
|
|
||||||
|
|
||||||
Character limit: ~26 (1 line)">
|
|
||||||
Backup
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_confirm_message"
|
|
||||||
translation_description="In the 'Backup and reset' settings, after pressing the 'Backup' option,
|
|
||||||
the confirmation message.
|
|
||||||
|
|
||||||
%1$s the directory. E.g., '/MyTracks/backups'.">
|
|
||||||
This will write a full copy of all your tracks and settings to ‘%1$s’ on the
|
|
||||||
external storage.
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_confirm_title"
|
|
||||||
translation_description="In the 'Backup and reset' settings, after pressing the 'Backup' option,
|
|
||||||
the title of the confirmation dialog.
|
|
||||||
|
|
||||||
Character limit: ~26 (1 line)">
|
|
||||||
Backup now?
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_now_error"
|
|
||||||
translation_description="When backing up to the external storage, the error message.">
|
|
||||||
Unable to backup.
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_now_progress_message"
|
|
||||||
translation_description="In the 'Backup and reset' settings, after pressing the 'Backup' option,
|
|
||||||
the progress message telling the user that it is writing all tracks and settings to the
|
|
||||||
external storage.">
|
|
||||||
Writing all tracks and settings…
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_now_success"
|
|
||||||
translation_description="When backing up to the external storage, the success message">
|
|
||||||
Finished backup.
|
|
||||||
</string>
|
|
||||||
|
|
||||||
<!-- Settings Backup Restore -->
|
|
||||||
<string name="settings_backup_restore"
|
|
||||||
translation_description="In the 'Backup and reset' settings, the option to restore from the
|
|
||||||
external storage.
|
|
||||||
|
|
||||||
Character limit: ~26 (1 line)">
|
|
||||||
Restore
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_restore_confirm_message"
|
|
||||||
translation_description="In the 'Backup and reset' settings, after pressing the 'Restore'
|
|
||||||
option, the confirmation message.">
|
|
||||||
Restoring a backup from external storage will delete all your tracks and settings, and update
|
|
||||||
them with the backup copy. Google Drive sync will be turned off. No tracks will be deleted on
|
|
||||||
Google Drive and on external storage.
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_restore_confirm_title"
|
|
||||||
translation_description="In the 'Backup and reset' settings, after pressing the 'Restore'
|
|
||||||
option, the title of the confirmation dialog.">
|
|
||||||
Restore now?
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_restore_error"
|
|
||||||
translation_description="When restoring from backup, the error message.">
|
|
||||||
Unable to restore from backup.
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_restore_no_backups"
|
|
||||||
translation_description="In the 'Backup and reset' settings, after pressing the 'Restore'
|
|
||||||
option, the message in the alert dialog informing the user that there is no backups to restore
|
|
||||||
from.
|
|
||||||
|
|
||||||
%1$s the directory. E.g., '/MyTracks/backups'.">
|
|
||||||
No backups found in %1$s.
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_restore_progress_message"
|
|
||||||
translation_description="In the 'Backup and reset' settings, after pressing the 'Restore'
|
|
||||||
option, the progress message informing that My Tracks is reading all tracks and settings from
|
|
||||||
the backup on the external storage.">
|
|
||||||
Reading all tracks and settings…
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_restore_select_title"
|
|
||||||
translation_description="In the 'Backup and reset' settings, after pressing the 'Restore'
|
|
||||||
option, the title in the picker dialog asking the user to select a backup.">
|
|
||||||
Select a backup to restore
|
|
||||||
</string>
|
|
||||||
<string name="settings_backup_restore_success"
|
|
||||||
translation_description="When restoring from backup, the success message.">
|
|
||||||
Finished restoring from backup.
|
|
||||||
</string>
|
|
||||||
|
|
||||||
<!-- Settings Chart -->
|
<!-- Settings Chart -->
|
||||||
<string name="settings_chart_by_distance"
|
<string name="settings_chart_by_distance"
|
||||||
translation_description="In the 'Chart' settings, the option to display the x axis by
|
translation_description="In the 'Chart' settings, the option to display the x axis by
|
||||||
|
|||||||
@@ -26,4 +26,8 @@ limitations under the License.
|
|||||||
android:summaryOff="@string/settings_sharing_allow_access_summary"
|
android:summaryOff="@string/settings_sharing_allow_access_summary"
|
||||||
android:summaryOn="@string/settings_sharing_allow_access_summary"
|
android:summaryOn="@string/settings_sharing_allow_access_summary"
|
||||||
android:title="@string/settings_sharing_allow_access" />
|
android:title="@string/settings_sharing_allow_access" />
|
||||||
|
<Preference
|
||||||
|
android:key="@string/settings_reset_key"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/settings_reset" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright 2012 Google Inc.
|
|
||||||
|
|
||||||
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.
|
|
||||||
-->
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:title="@string/settings_backup_reset" >
|
|
||||||
<Preference
|
|
||||||
android:key="@string/settings_backup_key"
|
|
||||||
android:persistent="false"
|
|
||||||
android:title="@string/settings_backup" />
|
|
||||||
<Preference
|
|
||||||
android:key="@string/settings_restore_key"
|
|
||||||
android:persistent="false"
|
|
||||||
android:title="@string/settings_backup_restore" />
|
|
||||||
<Preference
|
|
||||||
android:key="@string/settings_reset_key"
|
|
||||||
android:persistent="false"
|
|
||||||
android:title="@string/settings_reset" />
|
|
||||||
</PreferenceScreen>
|
|
||||||
@@ -40,10 +40,6 @@ limitations under the License.
|
|||||||
android:key="@string/settings_sensor_key"
|
android:key="@string/settings_sensor_key"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
android:title="@string/settings_sensor" />
|
android:title="@string/settings_sensor" />
|
||||||
<Preference
|
|
||||||
android:key="@string/settings_backup_reset_key"
|
|
||||||
android:persistent="false"
|
|
||||||
android:title="@string/settings_backup_reset" />
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="@string/settings_advanced_key"
|
android:key="@string/settings_advanced_key"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.io.backup;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Activity to backup data to the SD card.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class BackupActivity extends Activity {
|
|
||||||
|
|
||||||
private static final int DIALOG_PROGRESS_ID = 0;
|
|
||||||
|
|
||||||
private BackupAsyncTask backupAsyncTask;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
Object retained = getLastNonConfigurationInstance();
|
|
||||||
if (retained instanceof BackupAsyncTask) {
|
|
||||||
backupAsyncTask = (BackupAsyncTask) retained;
|
|
||||||
backupAsyncTask.setActivity(this);
|
|
||||||
} else {
|
|
||||||
backupAsyncTask = new BackupAsyncTask(this);
|
|
||||||
backupAsyncTask.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object onRetainNonConfigurationInstance() {
|
|
||||||
backupAsyncTask.setActivity(null);
|
|
||||||
return backupAsyncTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog onCreateDialog(int id) {
|
|
||||||
if (id != DIALOG_PROGRESS_ID) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return DialogUtils.createSpinnerProgressDialog(
|
|
||||||
this, R.string.settings_backup_now_progress_message, new DialogInterface.OnCancelListener() {
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invokes when the associated AsyncTask completes.
|
|
||||||
*
|
|
||||||
* @param success true if the AsyncTask is successful
|
|
||||||
* @param messageId message id to display to user
|
|
||||||
*/
|
|
||||||
public void onAsyncTaskCompleted(boolean success, int messageId) {
|
|
||||||
removeDialog(DIALOG_PROGRESS_ID);
|
|
||||||
Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the progress dialog.
|
|
||||||
*/
|
|
||||||
public void showProgressDialog() {
|
|
||||||
showDialog(DIALOG_PROGRESS_ID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.io.backup;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.util.FileUtils;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AsyncTask to backup data to the SD card.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class BackupAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|
||||||
|
|
||||||
private static final String TAG = BackupAsyncTask.class.getSimpleName();
|
|
||||||
|
|
||||||
private BackupActivity backupActivity;
|
|
||||||
private final ExternalFileBackup externalFileBackup;
|
|
||||||
|
|
||||||
// true if the AsyncTask result is success
|
|
||||||
private boolean success;
|
|
||||||
|
|
||||||
// true if the AsyncTask has completed
|
|
||||||
private boolean completed;
|
|
||||||
|
|
||||||
// message id to return to the activity
|
|
||||||
private int messageId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an AsyncTask.
|
|
||||||
*
|
|
||||||
* @param backupActivity the activity currently associated with this
|
|
||||||
* AsyncTask
|
|
||||||
*/
|
|
||||||
public BackupAsyncTask(BackupActivity backupActivity) {
|
|
||||||
this.backupActivity = backupActivity;
|
|
||||||
this.externalFileBackup = new ExternalFileBackup(backupActivity);
|
|
||||||
success = false;
|
|
||||||
completed = false;
|
|
||||||
messageId = R.string.settings_backup_now_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current {@link BackupActivity} associated with this AyncTask.
|
|
||||||
*
|
|
||||||
* @param activity the current {@link BackupActivity}, can be null
|
|
||||||
*/
|
|
||||||
public void setActivity(BackupActivity activity) {
|
|
||||||
this.backupActivity = activity;
|
|
||||||
if (completed && backupActivity != null) {
|
|
||||||
backupActivity.onAsyncTaskCompleted(success, messageId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
if (backupActivity != null) {
|
|
||||||
backupActivity.showProgressDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(Void... params) {
|
|
||||||
if (!FileUtils.isExternalStorageWriteable()) {
|
|
||||||
messageId = R.string.external_storage_not_writable;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!externalFileBackup.isBackupsDirectoryAvailable(true)) {
|
|
||||||
messageId = R.string.external_storage_not_writable;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
externalFileBackup.writeToDefaultFile();
|
|
||||||
messageId = R.string.settings_backup_now_success;
|
|
||||||
return true;
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.d(TAG, "IO exception", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean result) {
|
|
||||||
success = result;
|
|
||||||
completed = true;
|
|
||||||
if (backupActivity != null) {
|
|
||||||
backupActivity.onAsyncTaskCompleted(success, messageId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.io.backup;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.content.ContentTypeIds;
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.MergeCursor;
|
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Database dumper which is able to write only part of the database
|
|
||||||
* according to some query.
|
|
||||||
*
|
|
||||||
* This dumper is symmetrical to {@link DatabaseImporter}.
|
|
||||||
*
|
|
||||||
* @author Rodrigo Damazio
|
|
||||||
*/
|
|
||||||
class DatabaseDumper {
|
|
||||||
|
|
||||||
/** The names of the columns being dumped. */
|
|
||||||
private final String[] columnNames;
|
|
||||||
/** The types of the columns being dumped. */
|
|
||||||
private final byte[] columnTypes;
|
|
||||||
/** Whether to output null fields. */
|
|
||||||
private final boolean outputNullFields;
|
|
||||||
|
|
||||||
// Temporary state
|
|
||||||
private int[] columnIndices;
|
|
||||||
private boolean[] hasFields;
|
|
||||||
|
|
||||||
public DatabaseDumper(String[] columnNames, byte[] columnTypes,
|
|
||||||
boolean outputNullFields) {
|
|
||||||
if (columnNames.length != columnTypes.length) {
|
|
||||||
throw new IllegalArgumentException("Names don't match types");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.columnNames = columnNames;
|
|
||||||
this.columnTypes = columnTypes;
|
|
||||||
this.outputNullFields = outputNullFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the header plus all rows that can be read from the given cursor.
|
|
||||||
* This assumes the cursor will have the same column and column indices on
|
|
||||||
* every row (and thus may not work with a {@link MergeCursor}).
|
|
||||||
*/
|
|
||||||
public void writeAllRows(Cursor cursor, DataOutputStream writer)
|
|
||||||
throws IOException {
|
|
||||||
writeHeaders(cursor, cursor.getCount(), writer);
|
|
||||||
|
|
||||||
if (!cursor.moveToFirst()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
writeOneRow(cursor, writer);
|
|
||||||
} while (cursor.moveToNext());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes just the headers for the data that will come from the given cursor.
|
|
||||||
* The headers include column information and the number of rows that will be
|
|
||||||
* written.
|
|
||||||
*
|
|
||||||
* @param cursor the cursor to get columns from
|
|
||||||
* @param numRows the number of rows that will be later written
|
|
||||||
* @param writer the output to write to
|
|
||||||
* @throws IOException if there are errors while writing
|
|
||||||
*/
|
|
||||||
public void writeHeaders(Cursor cursor, int numRows, DataOutputStream writer)
|
|
||||||
throws IOException {
|
|
||||||
initializeCachedValues(cursor);
|
|
||||||
writeQueryMetadata(numRows, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the current row from the cursor. The cursor is not advanced.
|
|
||||||
* This must be called after {@link #writeHeaders}.
|
|
||||||
*
|
|
||||||
* @param cursor the cursor to write data from
|
|
||||||
* @param writer the output to write to
|
|
||||||
* @throws IOException if there are any errors while writing
|
|
||||||
*/
|
|
||||||
public void writeOneRow(Cursor cursor, DataOutputStream writer)
|
|
||||||
throws IOException {
|
|
||||||
if (columnIndices == null) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Cannot write rows before writing the header");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (columnIndices.length > Long.SIZE) {
|
|
||||||
throw new IllegalArgumentException("Too many fields");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build a bitmap of which fields are present
|
|
||||||
long fields = 0;
|
|
||||||
for (int i = 0; i < columnIndices.length; i++) {
|
|
||||||
hasFields[i] = !cursor.isNull(columnIndices[i]);
|
|
||||||
fields |= (hasFields[i] ? 1 : 0) << i;
|
|
||||||
}
|
|
||||||
writer.writeLong(fields);
|
|
||||||
|
|
||||||
// Actually write the present fields
|
|
||||||
for (int i = 0; i < columnIndices.length; i++) {
|
|
||||||
if (hasFields[i]) {
|
|
||||||
writeCell(columnIndices[i], columnTypes[i], cursor, writer);
|
|
||||||
} else if (outputNullFields) {
|
|
||||||
writeDummyCell(columnTypes[i], writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the column indices and other temporary state for reading from
|
|
||||||
* the given cursor.
|
|
||||||
*/
|
|
||||||
private void initializeCachedValues(Cursor cursor) {
|
|
||||||
// These indices are constant for every row (unless we're fed a MergeCursor)
|
|
||||||
if (cursor instanceof MergeCursor) {
|
|
||||||
throw new IllegalArgumentException("Cannot use a MergeCursor");
|
|
||||||
}
|
|
||||||
|
|
||||||
columnIndices = new int[columnNames.length];
|
|
||||||
for (int i = 0; i < columnNames.length; i++) {
|
|
||||||
String columnName = columnNames[i];
|
|
||||||
columnIndices[i] = cursor.getColumnIndexOrThrow(columnName);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasFields = new boolean[columnIndices.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes metadata about the query to be dumped.
|
|
||||||
*
|
|
||||||
* @param numRows the number of rows that will be dumped
|
|
||||||
* @param writer the output to write to
|
|
||||||
* @throws IOException if there are any errors while writing
|
|
||||||
*/
|
|
||||||
private void writeQueryMetadata(
|
|
||||||
int numRows, DataOutputStream writer) throws IOException {
|
|
||||||
// Write column data
|
|
||||||
writer.writeInt(columnNames.length);
|
|
||||||
for (int i = 0; i < columnNames.length; i++) {
|
|
||||||
String columnName = columnNames[i];
|
|
||||||
byte columnType = columnTypes[i];
|
|
||||||
writer.writeUTF(columnName);
|
|
||||||
writer.writeByte(columnType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the number of rows
|
|
||||||
writer.writeInt(numRows);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a single cell of the database to the output.
|
|
||||||
*
|
|
||||||
* @param columnIdx the column index to read from
|
|
||||||
* @param columnTypeId the type of the column to be read
|
|
||||||
* @param cursor the cursor to read from
|
|
||||||
* @param writer the output to write to
|
|
||||||
* @throws IOException if there are any errors while writing
|
|
||||||
*/
|
|
||||||
private void writeCell(
|
|
||||||
int columnIdx, byte columnTypeId, Cursor cursor, DataOutputStream writer)
|
|
||||||
throws IOException {
|
|
||||||
switch (columnTypeId) {
|
|
||||||
case ContentTypeIds.LONG_TYPE_ID:
|
|
||||||
writer.writeLong(cursor.getLong(columnIdx));
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.DOUBLE_TYPE_ID:
|
|
||||||
writer.writeDouble(cursor.getDouble(columnIdx));
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.FLOAT_TYPE_ID:
|
|
||||||
writer.writeFloat(cursor.getFloat(columnIdx));
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.BOOLEAN_TYPE_ID:
|
|
||||||
writer.writeBoolean(cursor.getInt(columnIdx) != 0);
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.INT_TYPE_ID:
|
|
||||||
writer.writeInt(cursor.getInt(columnIdx));
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.STRING_TYPE_ID:
|
|
||||||
writer.writeUTF(cursor.getString(columnIdx));
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.BLOB_TYPE_ID: {
|
|
||||||
byte[] blob = cursor.getBlob(columnIdx);
|
|
||||||
writer.writeInt(blob.length);
|
|
||||||
writer.write(blob);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Type " + columnTypeId + " not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a dummy cell value to the output.
|
|
||||||
*
|
|
||||||
* @param columnTypeId the type of the value to write
|
|
||||||
* @throws IOException if there are any errors while writing
|
|
||||||
*/
|
|
||||||
private void writeDummyCell(byte columnTypeId, DataOutputStream writer)
|
|
||||||
throws IOException {
|
|
||||||
switch (columnTypeId) {
|
|
||||||
case ContentTypeIds.LONG_TYPE_ID:
|
|
||||||
writer.writeLong(0L);
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.DOUBLE_TYPE_ID:
|
|
||||||
writer.writeDouble(0.0);
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.FLOAT_TYPE_ID:
|
|
||||||
writer.writeFloat(0.0f);
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.BOOLEAN_TYPE_ID:
|
|
||||||
writer.writeBoolean(false);
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.INT_TYPE_ID:
|
|
||||||
writer.writeInt(0);
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.STRING_TYPE_ID:
|
|
||||||
writer.writeUTF("");
|
|
||||||
return;
|
|
||||||
case ContentTypeIds.BLOB_TYPE_ID:
|
|
||||||
writer.writeInt(0);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Type " + columnTypeId + " not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.io.backup;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.content.ContentTypeIds;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Database importer which reads values written by {@link DatabaseDumper}.
|
|
||||||
*
|
|
||||||
* @author Rodrigo Damazio
|
|
||||||
*/
|
|
||||||
public class DatabaseImporter {
|
|
||||||
|
|
||||||
/** Maximum number of entries in a bulk insertion */
|
|
||||||
private static final int DEFAULT_BULK_SIZE = 1024;
|
|
||||||
|
|
||||||
private final Uri destinationUri;
|
|
||||||
private final ContentResolver resolver;
|
|
||||||
private final boolean readNullFields;
|
|
||||||
private final int bulkSize;
|
|
||||||
|
|
||||||
// Metadata read from the reader
|
|
||||||
private String[] columnNames;
|
|
||||||
private byte[] columnTypes;
|
|
||||||
|
|
||||||
public DatabaseImporter(Uri destinationUri, ContentResolver resolver,
|
|
||||||
boolean readNullFields) {
|
|
||||||
this(destinationUri, resolver, readNullFields, DEFAULT_BULK_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected DatabaseImporter(Uri destinationUri, ContentResolver resolver,
|
|
||||||
boolean readNullFields, int bulkSize) {
|
|
||||||
this.destinationUri = destinationUri;
|
|
||||||
this.resolver = resolver;
|
|
||||||
this.readNullFields = readNullFields;
|
|
||||||
this.bulkSize = bulkSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the header which includes metadata about the table being imported.
|
|
||||||
*
|
|
||||||
* @throws IOException if there are any problems while reading
|
|
||||||
*/
|
|
||||||
private void readHeaders(DataInputStream reader) throws IOException {
|
|
||||||
int numColumns = reader.readInt();
|
|
||||||
columnNames = new String[numColumns];
|
|
||||||
columnTypes = new byte[numColumns];
|
|
||||||
for (int i = 0; i < numColumns; i++) {
|
|
||||||
columnNames[i] = reader.readUTF();
|
|
||||||
columnTypes[i] = reader.readByte();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports all rows from the reader into the database.
|
|
||||||
* Insertion is done in bulks for efficiency.
|
|
||||||
*
|
|
||||||
* @throws IOException if there are any errors while reading
|
|
||||||
*/
|
|
||||||
public void importAllRows(DataInputStream reader) throws IOException {
|
|
||||||
readHeaders(reader);
|
|
||||||
|
|
||||||
ContentValues[] valueBulk = new ContentValues[bulkSize];
|
|
||||||
int numValues = 0;
|
|
||||||
|
|
||||||
int numRows = reader.readInt();
|
|
||||||
int numColumns = columnNames.length;
|
|
||||||
|
|
||||||
// For each row
|
|
||||||
for (int r = 0; r < numRows; r++) {
|
|
||||||
if (valueBulk[numValues] == null) {
|
|
||||||
valueBulk[numValues] = new ContentValues(numColumns);
|
|
||||||
} else {
|
|
||||||
// Reuse values objects
|
|
||||||
valueBulk[numValues].clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the fields bitmap
|
|
||||||
long fields = reader.readLong();
|
|
||||||
for (int c = 0; c < numColumns; c++) {
|
|
||||||
if ((fields & 1) == 1) {
|
|
||||||
// Field is present, read into values
|
|
||||||
readOneCell(columnNames[c], columnTypes[c], valueBulk[numValues],
|
|
||||||
reader);
|
|
||||||
} else if (readNullFields) {
|
|
||||||
// Field not present but still written, read and discard
|
|
||||||
readOneCell(columnNames[c], columnTypes[c], null, reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
fields >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
numValues++;
|
|
||||||
|
|
||||||
// If we have enough values, flush them as a bulk insertion
|
|
||||||
if (numValues >= bulkSize) {
|
|
||||||
doBulkInsert(valueBulk);
|
|
||||||
numValues = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do a final bulk insert with the leftovers
|
|
||||||
if (numValues > 0) {
|
|
||||||
ContentValues[] leftovers = new ContentValues[numValues];
|
|
||||||
System.arraycopy(valueBulk, 0, leftovers, 0, numValues);
|
|
||||||
doBulkInsert(leftovers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void doBulkInsert(ContentValues[] values) {
|
|
||||||
resolver.bulkInsert(destinationUri, values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a single cell from the reader.
|
|
||||||
*
|
|
||||||
* @param name the name of the column to be read
|
|
||||||
* @param typeId the type ID of the column to be read
|
|
||||||
* @param values the {@link ContentValues} object to put the read cell value
|
|
||||||
* in - if null, the value is just discarded
|
|
||||||
* @throws IOException if there are any problems while reading
|
|
||||||
*/
|
|
||||||
private void readOneCell(String name, byte typeId, ContentValues values,
|
|
||||||
DataInputStream reader) throws IOException {
|
|
||||||
switch (typeId) {
|
|
||||||
case ContentTypeIds.BOOLEAN_TYPE_ID: {
|
|
||||||
boolean value = reader.readBoolean();
|
|
||||||
if (values != null) { values.put(name, value); }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case ContentTypeIds.LONG_TYPE_ID: {
|
|
||||||
long value = reader.readLong();
|
|
||||||
if (values != null) { values.put(name, value); }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case ContentTypeIds.DOUBLE_TYPE_ID: {
|
|
||||||
double value = reader.readDouble();
|
|
||||||
if (values != null) { values.put(name, value); }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case ContentTypeIds.FLOAT_TYPE_ID: {
|
|
||||||
Float value = reader.readFloat();
|
|
||||||
if (values != null) { values.put(name, value); }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case ContentTypeIds.INT_TYPE_ID: {
|
|
||||||
int value = reader.readInt();
|
|
||||||
if (values != null) { values.put(name, value); }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case ContentTypeIds.STRING_TYPE_ID: {
|
|
||||||
String value = reader.readUTF();
|
|
||||||
if (values != null) { values.put(name, value); }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case ContentTypeIds.BLOB_TYPE_ID: {
|
|
||||||
int blobLength = reader.readInt();
|
|
||||||
if (blobLength != 0) {
|
|
||||||
byte[] blob = new byte[blobLength];
|
|
||||||
int readBytes = reader.read(blob, 0, blobLength);
|
|
||||||
if (readBytes != blobLength) {
|
|
||||||
throw new IOException(String.format(Locale.US,
|
|
||||||
"Short read on column %s; expected %d bytes, read %d",
|
|
||||||
name, blobLength, readBytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values != null) {
|
|
||||||
values.put(name, blob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new IOException("Read unknown type " + typeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,277 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.io.backup;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.Constants;
|
|
||||||
import com.google.android.apps.mytracks.content.TrackPointsColumns;
|
|
||||||
import com.google.android.apps.mytracks.content.TracksColumns;
|
|
||||||
import com.google.android.apps.mytracks.content.WaypointsColumns;
|
|
||||||
import com.google.android.apps.mytracks.util.FileUtils;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipFile;
|
|
||||||
import java.util.zip.ZipOutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for writing or reading single-file backups.
|
|
||||||
*
|
|
||||||
* @author Rodrigo Damazio
|
|
||||||
*/
|
|
||||||
class ExternalFileBackup {
|
|
||||||
|
|
||||||
private static final String TAG = ExternalFileBackup.class.getSimpleName();
|
|
||||||
|
|
||||||
// Filename format - in UTC
|
|
||||||
private static final SimpleDateFormat BACKUP_FILENAME_FORMAT = new SimpleDateFormat(
|
|
||||||
"'backup-'yyyy-MM-dd_HH-mm-ss'.zip'", Locale.US);
|
|
||||||
static {
|
|
||||||
BACKUP_FILENAME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int BACKUP_FORMAT_VERSION = 1;
|
|
||||||
private static final String ZIP_ENTRY_NAME =
|
|
||||||
"backup.mytracks.v" + BACKUP_FORMAT_VERSION;
|
|
||||||
private static final int COMPRESSION_LEVEL = 8;
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public ExternalFileBackup(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the backups directory is (or can be made) available.
|
|
||||||
*
|
|
||||||
* @param create whether to try creating the directory if it doesn't exist
|
|
||||||
*/
|
|
||||||
public boolean isBackupsDirectoryAvailable(boolean create) {
|
|
||||||
return getBackupsDirectory(create) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the backup directory, or null if not available.
|
|
||||||
*
|
|
||||||
* @param create whether to try creating the directory if it doesn't exist
|
|
||||||
*/
|
|
||||||
private File getBackupsDirectory(boolean create) {
|
|
||||||
String directoryPath = FileUtils.getPath(FileUtils.BACKUPS_DIR);
|
|
||||||
final File dir = new File(directoryPath);
|
|
||||||
Log.d(TAG, "Dir: " + dir.getAbsolutePath());
|
|
||||||
if (create) {
|
|
||||||
// Try to create - if that fails, return null
|
|
||||||
return FileUtils.ensureDirectoryExists(dir) ? dir : null;
|
|
||||||
} else {
|
|
||||||
// Return it if it already exists, otherwise return null
|
|
||||||
return FileUtils.isDirectory(dir) ? dir : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of available backups to be restored.
|
|
||||||
*/
|
|
||||||
public Date[] getAvailableBackups() {
|
|
||||||
File dir = getBackupsDirectory(false);
|
|
||||||
if (dir == null) { return null; }
|
|
||||||
String[] fileNames = dir.list();
|
|
||||||
|
|
||||||
List<Date> backupDates = new ArrayList<Date>(fileNames.length);
|
|
||||||
for (int i = 0; i < fileNames.length; i++) {
|
|
||||||
String fileName = fileNames[i];
|
|
||||||
try {
|
|
||||||
backupDates.add(BACKUP_FILENAME_FORMAT.parse(fileName));
|
|
||||||
} catch (ParseException e) {
|
|
||||||
// Not a backup file, ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return backupDates.toArray(new Date[backupDates.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the backup to the default file.
|
|
||||||
*/
|
|
||||||
public void writeToDefaultFile() throws IOException {
|
|
||||||
writeToFile(getFileForDate(new Date()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores the backup from the given date.
|
|
||||||
*/
|
|
||||||
public void restoreFromDate(Date when) throws IOException {
|
|
||||||
restoreFromFile(getFileForDate(when));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces the proper file descriptor for the given backup date.
|
|
||||||
*/
|
|
||||||
private File getFileForDate(Date when) {
|
|
||||||
File dir = getBackupsDirectory(false);
|
|
||||||
String fileName = BACKUP_FILENAME_FORMAT.format(when);
|
|
||||||
File file = new File(dir, fileName);
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronously writes a backup to the given file.
|
|
||||||
*/
|
|
||||||
private void writeToFile(File outputFile) throws IOException {
|
|
||||||
Log.d(TAG, "Writing backup to file " + outputFile.getAbsolutePath());
|
|
||||||
|
|
||||||
// Create all the auxiliary classes that will do the writing
|
|
||||||
PreferenceBackupHelper preferencesHelper = new PreferenceBackupHelper(context);
|
|
||||||
DatabaseDumper trackDumper = new DatabaseDumper(
|
|
||||||
TracksColumns.COLUMNS,
|
|
||||||
TracksColumns.COLUMN_TYPES,
|
|
||||||
false);
|
|
||||||
DatabaseDumper waypointDumper = new DatabaseDumper(
|
|
||||||
WaypointsColumns.COLUMNS,
|
|
||||||
WaypointsColumns.COLUMN_TYPES,
|
|
||||||
false);
|
|
||||||
DatabaseDumper pointDumper = new DatabaseDumper(
|
|
||||||
TrackPointsColumns.COLUMNS,
|
|
||||||
TrackPointsColumns.COLUMN_TYPES,
|
|
||||||
false);
|
|
||||||
|
|
||||||
// Open the target for writing
|
|
||||||
FileOutputStream outputStream = new FileOutputStream(outputFile);
|
|
||||||
ZipOutputStream compressedStream = new ZipOutputStream(outputStream);
|
|
||||||
compressedStream.setLevel(COMPRESSION_LEVEL);
|
|
||||||
compressedStream.putNextEntry(new ZipEntry(ZIP_ENTRY_NAME));
|
|
||||||
DataOutputStream outWriter = new DataOutputStream(compressedStream);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Dump the entire contents of each table
|
|
||||||
ContentResolver contentResolver = context.getContentResolver();
|
|
||||||
Cursor tracksCursor = null;
|
|
||||||
try {
|
|
||||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, null);
|
|
||||||
if (tracksCursor != null) {
|
|
||||||
trackDumper.writeAllRows(tracksCursor, outWriter);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (tracksCursor != null) {
|
|
||||||
tracksCursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Cursor waypointsCursor = null;
|
|
||||||
try {
|
|
||||||
waypointsCursor = contentResolver.query(
|
|
||||||
WaypointsColumns.CONTENT_URI, null, null, null, null);
|
|
||||||
if (waypointsCursor != null) {
|
|
||||||
waypointDumper.writeAllRows(waypointsCursor, outWriter);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (waypointsCursor != null) {
|
|
||||||
waypointsCursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Cursor pointsCursor = null;
|
|
||||||
try {
|
|
||||||
pointsCursor = contentResolver.query(
|
|
||||||
TrackPointsColumns.CONTENT_URI, null, null, null, null);
|
|
||||||
if (pointsCursor != null) {
|
|
||||||
pointDumper.writeAllRows(pointsCursor, outWriter);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (pointsCursor != null) {
|
|
||||||
pointsCursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump preferences
|
|
||||||
SharedPreferences preferences = context.getSharedPreferences(
|
|
||||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
|
||||||
preferencesHelper.exportPreferences(preferences, outWriter);
|
|
||||||
} catch (IOException e) {
|
|
||||||
// We tried to delete the partially created file, but do nothing
|
|
||||||
// if that also fails.
|
|
||||||
if (!outputFile.delete()) {
|
|
||||||
Log.w(TAG, "Failed to delete file " + outputFile.getAbsolutePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
compressedStream.closeEntry();
|
|
||||||
compressedStream.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronously restores the backup from the given file.
|
|
||||||
*/
|
|
||||||
private void restoreFromFile(File inputFile) throws IOException {
|
|
||||||
Log.d(TAG, "Restoring from file " + inputFile.getAbsolutePath());
|
|
||||||
|
|
||||||
PreferenceBackupHelper preferencesHelper = new PreferenceBackupHelper(context);
|
|
||||||
ContentResolver resolver = context.getContentResolver();
|
|
||||||
DatabaseImporter trackImporter =
|
|
||||||
new DatabaseImporter(TracksColumns.CONTENT_URI, resolver, false);
|
|
||||||
DatabaseImporter waypointImporter =
|
|
||||||
new DatabaseImporter(WaypointsColumns.CONTENT_URI, resolver, false);
|
|
||||||
DatabaseImporter pointImporter =
|
|
||||||
new DatabaseImporter(TrackPointsColumns.CONTENT_URI, resolver, false);
|
|
||||||
|
|
||||||
ZipFile zipFile = new ZipFile(inputFile, ZipFile.OPEN_READ);
|
|
||||||
ZipEntry zipEntry = zipFile.getEntry(ZIP_ENTRY_NAME);
|
|
||||||
if (zipEntry == null) {
|
|
||||||
throw new IOException("Invalid backup ZIP file");
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream compressedStream = zipFile.getInputStream(zipEntry);
|
|
||||||
DataInputStream reader = new DataInputStream(compressedStream);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Delete all previous contents of the tables and preferences.
|
|
||||||
resolver.delete(TracksColumns.CONTENT_URI, null, null);
|
|
||||||
resolver.delete(TrackPointsColumns.CONTENT_URI, null, null);
|
|
||||||
resolver.delete(WaypointsColumns.CONTENT_URI, null, null);
|
|
||||||
|
|
||||||
// Import the new contents of each table
|
|
||||||
trackImporter.importAllRows(reader);
|
|
||||||
waypointImporter.importAllRows(reader);
|
|
||||||
pointImporter.importAllRows(reader);
|
|
||||||
|
|
||||||
// Restore preferences
|
|
||||||
SharedPreferences preferences = context.getSharedPreferences(
|
|
||||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
|
||||||
preferencesHelper.importPreferences(reader, preferences);
|
|
||||||
} finally {
|
|
||||||
compressedStream.close();
|
|
||||||
zipFile.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.io.backup;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.TrackListActivity;
|
|
||||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.IntentUtils;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An activity to restore data from the SD card.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class RestoreActivity extends Activity {
|
|
||||||
|
|
||||||
public static final String EXTRA_DATE = "date";
|
|
||||||
|
|
||||||
private static final String TAG = RestoreActivity.class.getSimpleName();
|
|
||||||
private static final int DIALOG_PROGRESS_ID = 0;
|
|
||||||
|
|
||||||
private RestoreAsyncTask restoreAsyncTask;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
Object retained = getLastNonConfigurationInstance();
|
|
||||||
if (retained instanceof RestoreAsyncTask) {
|
|
||||||
restoreAsyncTask = (RestoreAsyncTask) retained;
|
|
||||||
restoreAsyncTask.setActivity(this);
|
|
||||||
} else {
|
|
||||||
long date = getIntent().getLongExtra(EXTRA_DATE, -1L);
|
|
||||||
if (date == -1L) {
|
|
||||||
Log.d(TAG, "Invalid date");
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
restoreAsyncTask = new RestoreAsyncTask(this, new Date(date));
|
|
||||||
restoreAsyncTask.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object onRetainNonConfigurationInstance() {
|
|
||||||
restoreAsyncTask.setActivity(null);
|
|
||||||
return restoreAsyncTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog onCreateDialog(int id) {
|
|
||||||
if (id != DIALOG_PROGRESS_ID) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return DialogUtils.createSpinnerProgressDialog(this,
|
|
||||||
R.string.settings_backup_restore_progress_message, new DialogInterface.OnCancelListener() {
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invokes when the associated AsyncTask completes.
|
|
||||||
*
|
|
||||||
* @param success true if the AsyncTask is successful
|
|
||||||
* @param messageId message id to display to user
|
|
||||||
*/
|
|
||||||
public void onAsyncTaskCompleted(boolean success, int messageId) {
|
|
||||||
removeDialog(DIALOG_PROGRESS_ID);
|
|
||||||
Toast.makeText(this, messageId, success ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
|
|
||||||
Intent intent = IntentUtils.newIntent(this, TrackListActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the progress dialog.
|
|
||||||
*/
|
|
||||||
public void showProgressDialog() {
|
|
||||||
showDialog(DIALOG_PROGRESS_ID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.io.backup;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.io.sync.SyncUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AsyncTask to restore data from the SD card.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class RestoreAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|
||||||
|
|
||||||
private static final String TAG = RestoreAsyncTask.class.getSimpleName();
|
|
||||||
|
|
||||||
private RestoreActivity restoreActivity;
|
|
||||||
private final Date date;
|
|
||||||
private final ExternalFileBackup externalFileBackup;
|
|
||||||
|
|
||||||
// true if the AsyncTask result is success
|
|
||||||
private boolean success;
|
|
||||||
|
|
||||||
// true if the AsyncTask has completed
|
|
||||||
private boolean completed;
|
|
||||||
|
|
||||||
// message id to return to the activity
|
|
||||||
private int messageId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an AsyncTask.
|
|
||||||
*
|
|
||||||
* @param restoreActivity the activity currently associated with this
|
|
||||||
* AsyncTask
|
|
||||||
* @param date the date to retore from
|
|
||||||
*/
|
|
||||||
public RestoreAsyncTask(RestoreActivity restoreActivity, Date date) {
|
|
||||||
this.restoreActivity = restoreActivity;
|
|
||||||
this.date = date;
|
|
||||||
this.externalFileBackup = new ExternalFileBackup(restoreActivity);
|
|
||||||
success = false;
|
|
||||||
completed = false;
|
|
||||||
messageId = R.string.settings_backup_restore_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the current {@link RestoreActivity} associated with this AyncTask.
|
|
||||||
*
|
|
||||||
* @param activity the current {@link RestoreActivity}, can be null
|
|
||||||
*/
|
|
||||||
public void setActivity(RestoreActivity activity) {
|
|
||||||
this.restoreActivity = activity;
|
|
||||||
if (completed && restoreActivity != null) {
|
|
||||||
restoreActivity.onAsyncTaskCompleted(success, messageId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
if (restoreActivity != null) {
|
|
||||||
restoreActivity.showProgressDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(Void... params) {
|
|
||||||
try {
|
|
||||||
PreferencesUtils.setBoolean(
|
|
||||||
restoreActivity, R.string.drive_sync_key, PreferencesUtils.DRIVE_SYNC_DEFAULT);
|
|
||||||
SyncUtils.disableSync(restoreActivity);
|
|
||||||
externalFileBackup.restoreFromDate(date);
|
|
||||||
SyncUtils.clearSyncState(restoreActivity);
|
|
||||||
messageId = R.string.settings_backup_restore_success;
|
|
||||||
return true;
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.d(TAG, "IO exception", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean result) {
|
|
||||||
success = result;
|
|
||||||
completed = true;
|
|
||||||
if (restoreActivity != null) {
|
|
||||||
restoreActivity.onAsyncTaskCompleted(success, messageId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.io.backup;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.FileUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.IntentUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.StringUtils;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An activity to choose a date to restore from.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class RestoreChooserActivity extends Activity {
|
|
||||||
|
|
||||||
private static final Comparator<Date> REVERSE_DATE_ORDER = new Comparator<Date>() {
|
|
||||||
@Override
|
|
||||||
public int compare(Date s1, Date s2) {
|
|
||||||
return s2.compareTo(s1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private static final int DIALOG_CHOOSER_ID = 0;
|
|
||||||
|
|
||||||
private Date[] backupDates;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
ExternalFileBackup externalFileBackup = new ExternalFileBackup(this);
|
|
||||||
|
|
||||||
// Get the list of existing backups
|
|
||||||
if (!FileUtils.isExternalStorageAvailable()) {
|
|
||||||
Toast.makeText(this, R.string.external_storage_not_available, Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!externalFileBackup.isBackupsDirectoryAvailable(false)) {
|
|
||||||
showNoBackupMessage();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
backupDates = externalFileBackup.getAvailableBackups();
|
|
||||||
if (backupDates == null || backupDates.length == 0) {
|
|
||||||
showNoBackupMessage();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backupDates.length == 1) {
|
|
||||||
Intent intent = IntentUtils.newIntent(this, RestoreActivity.class)
|
|
||||||
.putExtra(RestoreActivity.EXTRA_DATE, backupDates[0].getTime());
|
|
||||||
startActivity(intent);
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Arrays.sort(backupDates, REVERSE_DATE_ORDER);
|
|
||||||
showDialog(DIALOG_CHOOSER_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog onCreateDialog(int id) {
|
|
||||||
if (id != DIALOG_CHOOSER_ID) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String items[] = new String[backupDates.length];
|
|
||||||
for (int i = 0; i < backupDates.length; i++) {
|
|
||||||
items[i] = StringUtils.formatDateTime(this, backupDates[i].getTime());
|
|
||||||
}
|
|
||||||
final Dialog dialog = new AlertDialog.Builder(this).setCancelable(true)
|
|
||||||
.setItems(items, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int which) {
|
|
||||||
Intent intent = IntentUtils.newIntent(
|
|
||||||
RestoreChooserActivity.this, RestoreActivity.class)
|
|
||||||
.putExtra(RestoreActivity.EXTRA_DATE, backupDates[which].getTime());
|
|
||||||
startActivity(intent);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialogInterface) {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}).setTitle(R.string.settings_backup_restore_select_title).create();
|
|
||||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onShow(DialogInterface dialogInterface) {
|
|
||||||
DialogUtils.setDialogTitleDivider(RestoreChooserActivity.this, dialog);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows a no backup message.
|
|
||||||
*/
|
|
||||||
private void showNoBackupMessage() {
|
|
||||||
String message = getString(R.string.settings_backup_restore_no_backups,
|
|
||||||
FileUtils.getPathDisplayName(FileUtils.BACKUPS_DIR));
|
|
||||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+141
-25
@@ -16,17 +16,26 @@
|
|||||||
|
|
||||||
package com.google.android.apps.mytracks.settings;
|
package com.google.android.apps.mytracks.settings;
|
||||||
|
|
||||||
|
import com.google.android.apps.mytracks.Constants;
|
||||||
|
import com.google.android.apps.mytracks.io.sync.SyncUtils;
|
||||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
import com.google.android.apps.mytracks.util.DialogUtils;
|
||||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||||
import com.google.android.maps.mytracks.R;
|
import com.google.android.maps.mytracks.R;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.CheckBoxPreference;
|
import android.preference.CheckBoxPreference;
|
||||||
import android.preference.ListPreference;
|
import android.preference.ListPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.Preference.OnPreferenceChangeListener;
|
import android.preference.Preference.OnPreferenceChangeListener;
|
||||||
|
import android.preference.Preference.OnPreferenceClickListener;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An activity for advanced settings.
|
* An activity for advanced settings.
|
||||||
@@ -35,9 +44,38 @@ import android.preference.Preference.OnPreferenceChangeListener;
|
|||||||
*/
|
*/
|
||||||
public class AdvancedSettingsActivity extends AbstractSettingsActivity {
|
public class AdvancedSettingsActivity extends AbstractSettingsActivity {
|
||||||
|
|
||||||
|
private static final String TAG = AdvancedSettingsActivity.class.getSimpleName();
|
||||||
|
|
||||||
private static final int DIALOG_CONFIRM_ALLOW_ACCESS_ID = 0;
|
private static final int DIALOG_CONFIRM_ALLOW_ACCESS_ID = 0;
|
||||||
|
private static final int DIALOG_CONFIRM_RESET_ID = 1;
|
||||||
|
|
||||||
private CheckBoxPreference allowAccessCheckBoxPreference;
|
private CheckBoxPreference allowAccessCheckBoxPreference;
|
||||||
|
private Preference resetPreference;
|
||||||
|
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
|
||||||
|
* Anonymous inner class will get garbage collected.
|
||||||
|
*/
|
||||||
|
private final OnSharedPreferenceChangeListener
|
||||||
|
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||||
|
if (key != null && key.equals(PreferencesUtils.getKey(
|
||||||
|
AdvancedSettingsActivity.this, R.string.recording_track_id_key))) {
|
||||||
|
recordingTrackId = PreferencesUtils.getLong(
|
||||||
|
AdvancedSettingsActivity.this, R.string.recording_track_id_key);
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
updateUi();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
@@ -45,20 +83,6 @@ public class AdvancedSettingsActivity extends AbstractSettingsActivity {
|
|||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
addPreferencesFromResource(R.xml.advanced_settings);
|
addPreferencesFromResource(R.xml.advanced_settings);
|
||||||
|
|
||||||
allowAccessCheckBoxPreference = (CheckBoxPreference) findPreference(
|
|
||||||
getString(R.string.allow_access_key));
|
|
||||||
allowAccessCheckBoxPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
|
||||||
if ((Boolean) newValue) {
|
|
||||||
showDialog(DIALOG_CONFIRM_ALLOW_ACCESS_ID);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.photo_size_key));
|
ListPreference preference = (ListPreference) findPreference(getString(R.string.photo_size_key));
|
||||||
int value = PreferencesUtils.getInt(
|
int value = PreferencesUtils.getInt(
|
||||||
this, R.string.photo_size_key, PreferencesUtils.PHOTO_SIZE_DEFAULT);
|
this, R.string.photo_size_key, PreferencesUtils.PHOTO_SIZE_DEFAULT);
|
||||||
@@ -67,22 +91,72 @@ public class AdvancedSettingsActivity extends AbstractSettingsActivity {
|
|||||||
String[] summary = new String[values.length];
|
String[] summary = new String[values.length];
|
||||||
setPhotoSizeSummaryAndOptions(summary, options, values);
|
setPhotoSizeSummaryAndOptions(summary, options, values);
|
||||||
configureListPreference(preference, summary, options, values, String.valueOf(value), null);
|
configureListPreference(preference, summary, options, values, String.valueOf(value), null);
|
||||||
|
|
||||||
|
allowAccessCheckBoxPreference = (CheckBoxPreference) findPreference(
|
||||||
|
getString(R.string.allow_access_key));
|
||||||
|
allowAccessCheckBoxPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||||
|
if ((Boolean) newValue) {
|
||||||
|
showDialog(DIALOG_CONFIRM_ALLOW_ACCESS_ID);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
resetPreference = findPreference(getString(R.string.settings_reset_key));
|
||||||
|
resetPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference arg0) {
|
||||||
|
showDialog(DIALOG_CONFIRM_RESET_ID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
|
||||||
|
updateUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Dialog onCreateDialog(int id) {
|
protected Dialog onCreateDialog(int id) {
|
||||||
if (id != DIALOG_CONFIRM_ALLOW_ACCESS_ID) {
|
switch (id) {
|
||||||
return null;
|
case DIALOG_CONFIRM_ALLOW_ACCESS_ID:
|
||||||
|
return DialogUtils.createConfirmationDialog(this,
|
||||||
|
R.string.settings_sharing_allow_access_confirm_title,
|
||||||
|
getString(R.string.settings_sharing_allow_access_confirm_message),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int button) {
|
||||||
|
allowAccessCheckBoxPreference.setChecked(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case DIALOG_CONFIRM_RESET_ID:
|
||||||
|
return DialogUtils.createConfirmationDialog(this, R.string.settings_reset_confirm_title,
|
||||||
|
getString(R.string.settings_reset_confirm_message),
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface d, int button) {
|
||||||
|
onResetPreferencesConfirmed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return DialogUtils.createConfirmationDialog(this,
|
|
||||||
R.string.settings_sharing_allow_access_confirm_title,
|
|
||||||
getString(R.string.settings_sharing_allow_access_confirm_message),
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int button) {
|
|
||||||
allowAccessCheckBoxPreference.setChecked(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setPhotoSizeSummaryAndOptions(String[] summary, String[] options, String[] values) {
|
private void setPhotoSizeSummaryAndOptions(String[] summary, String[] options, String[] values) {
|
||||||
@@ -101,4 +175,46 @@ public class AdvancedSettingsActivity extends AbstractSettingsActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the UI based on the recording state.
|
||||||
|
*/
|
||||||
|
private void updateUi() {
|
||||||
|
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||||
|
resetPreference.setEnabled(!isRecording);
|
||||||
|
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback when the user confirms resetting all settings.
|
||||||
|
*/
|
||||||
|
private void onResetPreferencesConfirmed() {
|
||||||
|
// Change preferences in a separate thread
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Log.i(TAG, "Resetting all settings");
|
||||||
|
|
||||||
|
// Actually wipe preferences and save synchronously
|
||||||
|
sharedPreferences.edit().clear().commit();
|
||||||
|
|
||||||
|
SyncUtils.disableSync(AdvancedSettingsActivity.this);
|
||||||
|
SyncUtils.clearSyncState(AdvancedSettingsActivity.this);
|
||||||
|
|
||||||
|
// Give UI feedback in the UI thread
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Toast.makeText(
|
||||||
|
AdvancedSettingsActivity.this, R.string.settings_reset_done, Toast.LENGTH_SHORT)
|
||||||
|
.show();
|
||||||
|
// Restart the settings activity so all changes are loaded
|
||||||
|
Intent intent = getIntent()
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-224
@@ -1,224 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.settings;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.Constants;
|
|
||||||
import com.google.android.apps.mytracks.io.backup.BackupActivity;
|
|
||||||
import com.google.android.apps.mytracks.io.backup.RestoreChooserActivity;
|
|
||||||
import com.google.android.apps.mytracks.io.sync.SyncUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.DialogUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.FileUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.IntentUtils;
|
|
||||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
|
||||||
import com.google.android.maps.mytracks.R;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.preference.Preference;
|
|
||||||
import android.preference.Preference.OnPreferenceClickListener;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An activity for accessing the backup and reset settings.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class BackupResetSettingsActivity extends AbstractSettingsActivity {
|
|
||||||
|
|
||||||
private static final String TAG = BackupResetSettingsActivity.class.getSimpleName();
|
|
||||||
private static final int DIALOG_CONFIRM_BACKUP_ID = 0;
|
|
||||||
private static final int DIALOG_CONFIRM_RESTORE_ID = 1;
|
|
||||||
private static final int DIALOG_CONFIRM_RESET_ID = 2;
|
|
||||||
|
|
||||||
private SharedPreferences sharedPreferences;
|
|
||||||
private Preference backupPreference;
|
|
||||||
private Preference restoreNowPreference;
|
|
||||||
private Preference resetPreference;
|
|
||||||
|
|
||||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
|
|
||||||
* Anonymous inner class will get garbage collected.
|
|
||||||
*/
|
|
||||||
private final OnSharedPreferenceChangeListener
|
|
||||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
|
||||||
if (key != null && key.equals(PreferencesUtils.getKey(
|
|
||||||
BackupResetSettingsActivity.this, R.string.recording_track_id_key))) {
|
|
||||||
recordingTrackId = PreferencesUtils.getLong(
|
|
||||||
BackupResetSettingsActivity.this, R.string.recording_track_id_key);
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
updateUi();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle bundle) {
|
|
||||||
super.onCreate(bundle);
|
|
||||||
addPreferencesFromResource(R.xml.backup_reset_settings);
|
|
||||||
|
|
||||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
|
||||||
|
|
||||||
backupPreference = findPreference(getString(R.string.settings_backup_key));
|
|
||||||
backupPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
showDialog(DIALOG_CONFIRM_BACKUP_ID);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
restoreNowPreference = findPreference(getString(R.string.settings_restore_key));
|
|
||||||
restoreNowPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
showDialog(DIALOG_CONFIRM_RESTORE_ID);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
resetPreference = findPreference(getString(R.string.settings_reset_key));
|
|
||||||
resetPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference arg0) {
|
|
||||||
showDialog(DIALOG_CONFIRM_RESET_ID);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Dialog onCreateDialog(int id) {
|
|
||||||
String message;
|
|
||||||
Dialog dialog;
|
|
||||||
switch (id) {
|
|
||||||
case DIALOG_CONFIRM_BACKUP_ID:
|
|
||||||
message = getString(R.string.settings_backup_confirm_message,
|
|
||||||
FileUtils.getPathDisplayName(FileUtils.BACKUPS_DIR));
|
|
||||||
dialog = DialogUtils.createConfirmationDialog(this, R.string.settings_backup_confirm_title,
|
|
||||||
message, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface d, int which) {
|
|
||||||
Intent intent = IntentUtils.newIntent(
|
|
||||||
BackupResetSettingsActivity.this, BackupActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case DIALOG_CONFIRM_RESTORE_ID:
|
|
||||||
message = getString(R.string.settings_backup_restore_confirm_message);
|
|
||||||
dialog = DialogUtils.createConfirmationDialog(this,
|
|
||||||
R.string.settings_backup_restore_confirm_title, message,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface d, int which) {
|
|
||||||
Intent intent = IntentUtils.newIntent(
|
|
||||||
BackupResetSettingsActivity.this, RestoreChooserActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case DIALOG_CONFIRM_RESET_ID:
|
|
||||||
message = getString(R.string.settings_reset_confirm_message);
|
|
||||||
dialog = DialogUtils.createConfirmationDialog(this, R.string.settings_reset_confirm_title,
|
|
||||||
message, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface d, int button) {
|
|
||||||
onResetPreferencesConfirmed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dialog = null;
|
|
||||||
}
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
|
||||||
recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
|
|
||||||
updateUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the UI based on the recording state.
|
|
||||||
*/
|
|
||||||
private void updateUi() {
|
|
||||||
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
|
||||||
String summary = isRecording ? getString(R.string.settings_not_while_recording) : "";
|
|
||||||
backupPreference.setEnabled(!isRecording);
|
|
||||||
backupPreference.setSummary(summary);
|
|
||||||
restoreNowPreference.setEnabled(!isRecording);
|
|
||||||
restoreNowPreference.setSummary(summary);
|
|
||||||
resetPreference.setEnabled(!isRecording);
|
|
||||||
resetPreference.setSummary(summary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback when the user confirms resetting all settings.
|
|
||||||
*/
|
|
||||||
private void onResetPreferencesConfirmed() {
|
|
||||||
// Change preferences in a separate thread
|
|
||||||
new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Log.i(TAG, "Resetting all settings");
|
|
||||||
|
|
||||||
// Actually wipe preferences and save synchronously
|
|
||||||
sharedPreferences.edit().clear().commit();
|
|
||||||
|
|
||||||
SyncUtils.disableSync(BackupResetSettingsActivity.this);
|
|
||||||
SyncUtils.clearSyncState(BackupResetSettingsActivity.this);
|
|
||||||
|
|
||||||
// Give UI feedback in the UI thread
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Toast.makeText(
|
|
||||||
BackupResetSettingsActivity.this, R.string.settings_reset_done, Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
// Restart the settings activity so all changes are loaded
|
|
||||||
Intent intent = getIntent()
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -44,7 +44,6 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
|||||||
configPreference(R.string.settings_stats_key, StatsSettingsActivity.class);
|
configPreference(R.string.settings_stats_key, StatsSettingsActivity.class);
|
||||||
configPreference(R.string.settings_recording_key, RecordingSettingsActivity.class);
|
configPreference(R.string.settings_recording_key, RecordingSettingsActivity.class);
|
||||||
configPreference(R.string.settings_sensor_key, SensorSettingsActivity.class);
|
configPreference(R.string.settings_sensor_key, SensorSettingsActivity.class);
|
||||||
configPreference(R.string.settings_backup_reset_key, BackupResetSettingsActivity.class);
|
|
||||||
configPreference(R.string.settings_advanced_key, AdvancedSettingsActivity.class);
|
configPreference(R.string.settings_advanced_key, AdvancedSettingsActivity.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,267 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.io.backup;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.content.ContentTypeIds;
|
|
||||||
|
|
||||||
import android.database.MatrixCursor;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link DatabaseDumper}.
|
|
||||||
*
|
|
||||||
* @author Rodrigo Damazio
|
|
||||||
*/
|
|
||||||
public class DatabaseDumperTest extends TestCase {
|
|
||||||
/**
|
|
||||||
* This class is the same as {@link MatrixCursor}, except this class
|
|
||||||
* implements {@link #getBlob} ({@link MatrixCursor} leaves it
|
|
||||||
* unimplemented).
|
|
||||||
*/
|
|
||||||
private class BlobAwareMatrixCursor extends MatrixCursor {
|
|
||||||
public BlobAwareMatrixCursor(String[] columnNames) {
|
|
||||||
super(columnNames);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public byte[] getBlob(int columnIndex) {
|
|
||||||
return getString(columnIndex).getBytes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String[] COLUMN_NAMES = {
|
|
||||||
"intCol", "longCol", "floatCol", "doubleCol", "stringCol", "boolCol", "blobCol"
|
|
||||||
};
|
|
||||||
private static final byte[] COLUMN_TYPES = {
|
|
||||||
ContentTypeIds.INT_TYPE_ID, ContentTypeIds.LONG_TYPE_ID,
|
|
||||||
ContentTypeIds.FLOAT_TYPE_ID, ContentTypeIds.DOUBLE_TYPE_ID,
|
|
||||||
ContentTypeIds.STRING_TYPE_ID, ContentTypeIds.BOOLEAN_TYPE_ID,
|
|
||||||
ContentTypeIds.BLOB_TYPE_ID
|
|
||||||
};
|
|
||||||
private static final String[][] FAKE_DATA = {
|
|
||||||
{ "42", "123456789", "3.1415", "2.72", "lolcat", "1", "blob" },
|
|
||||||
{ null, "123456789", "3.1415", "2.72", "lolcat", "1", "blob" },
|
|
||||||
{ "42", null, "3.1415", "2.72", "lolcat", "1", "blob" },
|
|
||||||
{ "42", "123456789", null, "2.72", "lolcat", "1", "blob" },
|
|
||||||
{ "42", "123456789", "3.1415", null, "lolcat", "1", "blob" },
|
|
||||||
{ "42", "123456789", "3.1415", "2.72", null, "1", "blob" },
|
|
||||||
{ "42", "123456789", "3.1415", "2.72", "lolcat", null, "blob" },
|
|
||||||
{ "42", "123456789", "3.1415", "2.72", "lolcat", "1", null },
|
|
||||||
};
|
|
||||||
private static final long[] EXPECTED_FIELD_SETS = {
|
|
||||||
0x7F, 0x7E, 0x7D, 0x7B, 0x77, 0x6F, 0x5F, 0x3F
|
|
||||||
};
|
|
||||||
|
|
||||||
private BlobAwareMatrixCursor cursor;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
// Add fake data to the cursor
|
|
||||||
cursor = new BlobAwareMatrixCursor(COLUMN_NAMES);
|
|
||||||
for (String[] row : FAKE_DATA) {
|
|
||||||
cursor.addRow(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testWriteAllRows_noNulls() throws Exception {
|
|
||||||
testWriteAllRows(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testWriteAllRows_withNulls() throws Exception {
|
|
||||||
testWriteAllRows(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testWriteAllRows(boolean hasNullFields) throws Exception {
|
|
||||||
// Dump it
|
|
||||||
DatabaseDumper dumper = new DatabaseDumper(COLUMN_NAMES, COLUMN_TYPES, hasNullFields);
|
|
||||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);
|
|
||||||
DataOutputStream writer = new DataOutputStream(outStream );
|
|
||||||
dumper.writeAllRows(cursor, writer);
|
|
||||||
|
|
||||||
// Read the results
|
|
||||||
byte[] result = outStream.toByteArray();
|
|
||||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(result);
|
|
||||||
DataInputStream reader = new DataInputStream(inputStream);
|
|
||||||
|
|
||||||
// Verify the header
|
|
||||||
assertHeader(reader);
|
|
||||||
|
|
||||||
// Verify the number of rows
|
|
||||||
assertEquals(FAKE_DATA.length, reader.readInt());
|
|
||||||
|
|
||||||
// Row 0 -- everything populated
|
|
||||||
assertEquals(EXPECTED_FIELD_SETS[0], reader.readLong());
|
|
||||||
assertEquals(42, reader.readInt());
|
|
||||||
assertEquals(123456789L, reader.readLong());
|
|
||||||
assertEquals(3.1415f, reader.readFloat());
|
|
||||||
assertEquals(2.72, reader.readDouble());
|
|
||||||
assertEquals("lolcat", reader.readUTF());
|
|
||||||
assertTrue(reader.readBoolean());
|
|
||||||
assertEquals(4, reader.readInt());
|
|
||||||
assertEquals("blob", readBlob(reader, 4));
|
|
||||||
|
|
||||||
// Row 1 -- no int
|
|
||||||
assertEquals(EXPECTED_FIELD_SETS[1], reader.readLong());
|
|
||||||
if (hasNullFields) reader.readInt();
|
|
||||||
assertEquals(123456789L, reader.readLong());
|
|
||||||
assertEquals(3.1415f, reader.readFloat());
|
|
||||||
assertEquals(2.72, reader.readDouble());
|
|
||||||
assertEquals("lolcat", reader.readUTF());
|
|
||||||
assertTrue(reader.readBoolean());
|
|
||||||
assertEquals(4, reader.readInt());
|
|
||||||
assertEquals("blob", readBlob(reader, 4));
|
|
||||||
|
|
||||||
// Row 2 -- no long
|
|
||||||
assertEquals(EXPECTED_FIELD_SETS[2], reader.readLong());
|
|
||||||
assertEquals(42, reader.readInt());
|
|
||||||
if (hasNullFields) reader.readLong();
|
|
||||||
assertEquals(3.1415f, reader.readFloat());
|
|
||||||
assertEquals(2.72, reader.readDouble());
|
|
||||||
assertEquals("lolcat", reader.readUTF());
|
|
||||||
assertTrue(reader.readBoolean());
|
|
||||||
assertEquals(4, reader.readInt());
|
|
||||||
assertEquals("blob", readBlob(reader, 4));
|
|
||||||
|
|
||||||
// Row 3 -- no float
|
|
||||||
assertEquals(EXPECTED_FIELD_SETS[3], reader.readLong());
|
|
||||||
assertEquals(42, reader.readInt());
|
|
||||||
assertEquals(123456789L, reader.readLong());
|
|
||||||
if (hasNullFields) reader.readFloat();
|
|
||||||
assertEquals(2.72, reader.readDouble());
|
|
||||||
assertEquals("lolcat", reader.readUTF());
|
|
||||||
assertTrue(reader.readBoolean());
|
|
||||||
assertEquals(4, reader.readInt());
|
|
||||||
assertEquals("blob", readBlob(reader, 4));
|
|
||||||
|
|
||||||
// Row 4 -- no double
|
|
||||||
assertEquals(EXPECTED_FIELD_SETS[4], reader.readLong());
|
|
||||||
assertEquals(42, reader.readInt());
|
|
||||||
assertEquals(123456789L, reader.readLong());
|
|
||||||
assertEquals(3.1415f, reader.readFloat());
|
|
||||||
if (hasNullFields) reader.readDouble();
|
|
||||||
assertEquals("lolcat", reader.readUTF());
|
|
||||||
assertTrue(reader.readBoolean());
|
|
||||||
assertEquals(4, reader.readInt());
|
|
||||||
assertEquals("blob", readBlob(reader, 4));
|
|
||||||
|
|
||||||
// Row 5 -- no string
|
|
||||||
assertEquals(EXPECTED_FIELD_SETS[5], reader.readLong());
|
|
||||||
assertEquals(42, reader.readInt());
|
|
||||||
assertEquals(123456789L, reader.readLong());
|
|
||||||
assertEquals(3.1415f, reader.readFloat());
|
|
||||||
assertEquals(2.72, reader.readDouble());
|
|
||||||
if (hasNullFields) reader.readUTF();
|
|
||||||
assertTrue(reader.readBoolean());
|
|
||||||
assertEquals(4, reader.readInt());
|
|
||||||
assertEquals("blob", readBlob(reader, 4));
|
|
||||||
|
|
||||||
// Row 6 -- no boolean
|
|
||||||
assertEquals(EXPECTED_FIELD_SETS[6], reader.readLong());
|
|
||||||
assertEquals(42, reader.readInt());
|
|
||||||
assertEquals(123456789L, reader.readLong());
|
|
||||||
assertEquals(3.1415f, reader.readFloat());
|
|
||||||
assertEquals(2.72, reader.readDouble());
|
|
||||||
assertEquals("lolcat", reader.readUTF());
|
|
||||||
if (hasNullFields) reader.readBoolean();
|
|
||||||
assertEquals(4, reader.readInt());
|
|
||||||
assertEquals("blob", readBlob(reader, 4));
|
|
||||||
|
|
||||||
// Row 7 -- no blob
|
|
||||||
assertEquals(EXPECTED_FIELD_SETS[7], reader.readLong());
|
|
||||||
assertEquals(42, reader.readInt());
|
|
||||||
assertEquals(123456789L, reader.readLong());
|
|
||||||
assertEquals(3.1415f, reader.readFloat());
|
|
||||||
assertEquals(2.72, reader.readDouble());
|
|
||||||
assertEquals("lolcat", reader.readUTF());
|
|
||||||
assertTrue(reader.readBoolean());
|
|
||||||
if (hasNullFields) {
|
|
||||||
int length = reader.readInt();
|
|
||||||
readBlob(reader, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testFewerRows() throws Exception {
|
|
||||||
// Dump only the first two rows
|
|
||||||
DatabaseDumper dumper = new DatabaseDumper(COLUMN_NAMES, COLUMN_TYPES, false);
|
|
||||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);
|
|
||||||
DataOutputStream writer = new DataOutputStream(outStream);
|
|
||||||
|
|
||||||
dumper.writeHeaders(cursor, 2, writer);
|
|
||||||
cursor.moveToFirst();
|
|
||||||
dumper.writeOneRow(cursor, writer);
|
|
||||||
cursor.moveToNext();
|
|
||||||
dumper.writeOneRow(cursor, writer);
|
|
||||||
|
|
||||||
// Read the results
|
|
||||||
byte[] result = outStream.toByteArray();
|
|
||||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(result);
|
|
||||||
DataInputStream reader = new DataInputStream(inputStream);
|
|
||||||
|
|
||||||
// Verify the header
|
|
||||||
assertHeader(reader);
|
|
||||||
|
|
||||||
// Verify the number of rows
|
|
||||||
assertEquals(2, reader.readInt());
|
|
||||||
|
|
||||||
// Row 0
|
|
||||||
assertEquals(EXPECTED_FIELD_SETS[0], reader.readLong());
|
|
||||||
assertEquals(42, reader.readInt());
|
|
||||||
assertEquals(123456789L, reader.readLong());
|
|
||||||
assertEquals(3.1415f, reader.readFloat());
|
|
||||||
assertEquals(2.72, reader.readDouble());
|
|
||||||
assertEquals("lolcat", reader.readUTF());
|
|
||||||
assertTrue(reader.readBoolean());
|
|
||||||
assertEquals(4, reader.readInt());
|
|
||||||
assertEquals("blob", readBlob(reader, 4));
|
|
||||||
|
|
||||||
// Row 1
|
|
||||||
assertEquals(EXPECTED_FIELD_SETS[1], reader.readLong());
|
|
||||||
// Null field not read
|
|
||||||
assertEquals(123456789L, reader.readLong());
|
|
||||||
assertEquals(3.1415f, reader.readFloat());
|
|
||||||
assertEquals(2.72, reader.readDouble());
|
|
||||||
assertEquals("lolcat", reader.readUTF());
|
|
||||||
assertTrue(reader.readBoolean());
|
|
||||||
assertEquals(4, reader.readInt());
|
|
||||||
assertEquals("blob", readBlob(reader, 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertHeader(DataInputStream reader) throws IOException {
|
|
||||||
assertEquals(COLUMN_NAMES.length, reader.readInt());
|
|
||||||
for (int i = 0; i < COLUMN_NAMES.length; i++) {
|
|
||||||
assertEquals(COLUMN_NAMES[i], reader.readUTF());
|
|
||||||
assertEquals(COLUMN_TYPES[i], reader.readByte());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readBlob(InputStream reader, int length) throws Exception {
|
|
||||||
if (length == 0) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] blob = new byte[length];
|
|
||||||
assertEquals(length, reader.read(blob));
|
|
||||||
return new String(blob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-293
@@ -1,293 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* 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.google.android.apps.mytracks.io.backup;
|
|
||||||
|
|
||||||
import com.google.android.apps.mytracks.content.ContentTypeIds;
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link DatabaseImporter}.
|
|
||||||
*
|
|
||||||
* @author Rodrigo Damazio
|
|
||||||
*/
|
|
||||||
public class DatabaseImporterTest extends TestCase {
|
|
||||||
|
|
||||||
private static final Uri DESTINATION_URI = Uri.parse("http://www.google.com/");
|
|
||||||
private static final int TEST_BULK_SIZE = 10;
|
|
||||||
private ArrayList<ContentValues> insertedValues;
|
|
||||||
|
|
||||||
private class TestableDatabaseImporter extends DatabaseImporter {
|
|
||||||
public TestableDatabaseImporter(boolean readNullFields) {
|
|
||||||
super(DESTINATION_URI, null, readNullFields, TEST_BULK_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doBulkInsert(ContentValues[] values) {
|
|
||||||
insertedValues.ensureCapacity(insertedValues.size() + values.length);
|
|
||||||
|
|
||||||
// We need to make a copy of the values since the objects are re-used
|
|
||||||
for (ContentValues contentValues : values) {
|
|
||||||
insertedValues.add(new ContentValues(contentValues));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
insertedValues = new ArrayList<ContentValues>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testImportAllRows() throws Exception {
|
|
||||||
testImportAllRows(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testImportAllRows_readNullFields() throws Exception {
|
|
||||||
testImportAllRows(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testImportAllRows(boolean readNullFields) throws Exception {
|
|
||||||
// Create a fake data stream to be read
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
|
|
||||||
DataOutputStream writer = new DataOutputStream(outputStream);
|
|
||||||
|
|
||||||
writeFullHeader(writer);
|
|
||||||
|
|
||||||
// Add the number of rows
|
|
||||||
writer.writeInt(2);
|
|
||||||
|
|
||||||
// Add a row with all fields present
|
|
||||||
writer.writeLong(0x7F);
|
|
||||||
writer.writeInt(42);
|
|
||||||
writer.writeBoolean(true);
|
|
||||||
writer.writeUTF("lolcat");
|
|
||||||
writer.writeFloat(3.1415f);
|
|
||||||
writer.writeDouble(2.72);
|
|
||||||
writer.writeLong(123456789L);
|
|
||||||
writer.writeInt(4);
|
|
||||||
writer.writeBytes("blob");
|
|
||||||
|
|
||||||
// Add a row with some missing fields
|
|
||||||
writer.writeLong(0x15);
|
|
||||||
writer.writeInt(42);
|
|
||||||
if (readNullFields) writer.writeBoolean(false);
|
|
||||||
writer.writeUTF("lolcat");
|
|
||||||
if (readNullFields) writer.writeFloat(0.0f);
|
|
||||||
writer.writeDouble(2.72);
|
|
||||||
if (readNullFields) writer.writeLong(0L);
|
|
||||||
if (readNullFields) writer.writeInt(0); // empty blob
|
|
||||||
|
|
||||||
writer.flush();
|
|
||||||
|
|
||||||
// Do the importing
|
|
||||||
DatabaseImporter importer = new TestableDatabaseImporter(readNullFields);
|
|
||||||
byte[] dataBytes = outputStream.toByteArray();
|
|
||||||
importer.importAllRows(new DataInputStream(new ByteArrayInputStream(dataBytes)));
|
|
||||||
|
|
||||||
assertEquals(2, insertedValues.size());
|
|
||||||
|
|
||||||
// Verify the first row
|
|
||||||
ContentValues value = insertedValues.get(0);
|
|
||||||
assertEquals(value.toString(), 7, value.size());
|
|
||||||
|
|
||||||
assertValue(42, "col1", value);
|
|
||||||
assertValue(true, "col2", value);
|
|
||||||
assertValue("lolcat", "col3", value);
|
|
||||||
assertValue(3.1415f, "col4", value);
|
|
||||||
assertValue(2.72, "col5", value);
|
|
||||||
assertValue(123456789L, "col6", value);
|
|
||||||
assertBlobValue("blob", "col7", value);
|
|
||||||
|
|
||||||
// Verify the second row
|
|
||||||
value = insertedValues.get(1);
|
|
||||||
assertEquals(value.toString(), 3, value.size());
|
|
||||||
|
|
||||||
assertValue(42, "col1", value);
|
|
||||||
assertValue("lolcat", "col3", value);
|
|
||||||
assertValue(2.72, "col5", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testImportAllRows_noRows() throws Exception {
|
|
||||||
// Create a fake data stream to be read
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
|
|
||||||
DataOutputStream writer = new DataOutputStream(outputStream);
|
|
||||||
|
|
||||||
writeFullHeader(writer);
|
|
||||||
|
|
||||||
// Add the number of rows
|
|
||||||
writer.writeInt(0);
|
|
||||||
|
|
||||||
writer.flush();
|
|
||||||
|
|
||||||
// Do the importing
|
|
||||||
DatabaseImporter importer = new TestableDatabaseImporter(false);
|
|
||||||
byte[] dataBytes = outputStream.toByteArray();
|
|
||||||
importer.importAllRows(new DataInputStream(new ByteArrayInputStream(dataBytes)));
|
|
||||||
|
|
||||||
assertTrue(insertedValues.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testImportAllRows_emptyRows() throws Exception {
|
|
||||||
testImportAllRowsWithEmptyRows(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testImportAllRows_emptyRowsWithNulls() throws Exception {
|
|
||||||
testImportAllRowsWithEmptyRows(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testImportAllRowsWithEmptyRows(boolean readNullFields) throws Exception {
|
|
||||||
// Create a fake data stream to be read
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
|
|
||||||
DataOutputStream writer = new DataOutputStream(outputStream);
|
|
||||||
|
|
||||||
writeFullHeader(writer);
|
|
||||||
|
|
||||||
// Add the number of rows
|
|
||||||
writer.writeInt(3);
|
|
||||||
|
|
||||||
// Add 2 rows with no fields
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
writer.writeLong(0);
|
|
||||||
if (readNullFields) {
|
|
||||||
writer.writeInt(0);
|
|
||||||
writer.writeBoolean(false);
|
|
||||||
writer.writeUTF("");
|
|
||||||
writer.writeFloat(0.0f);
|
|
||||||
writer.writeDouble(0.0);
|
|
||||||
writer.writeLong(0L);
|
|
||||||
writer.writeInt(0); // empty blob
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a row with some missing fields
|
|
||||||
writer.writeLong(0x15);
|
|
||||||
writer.writeInt(42);
|
|
||||||
if (readNullFields) writer.writeBoolean(false);
|
|
||||||
writer.writeUTF("lolcat");
|
|
||||||
if (readNullFields) writer.writeFloat(0.0f);
|
|
||||||
writer.writeDouble(2.72);
|
|
||||||
if (readNullFields) writer.writeLong(0L);
|
|
||||||
if (readNullFields) writer.writeInt(0); // empty blob
|
|
||||||
|
|
||||||
writer.flush();
|
|
||||||
|
|
||||||
// Do the importing
|
|
||||||
DatabaseImporter importer = new TestableDatabaseImporter(readNullFields);
|
|
||||||
byte[] dataBytes = outputStream.toByteArray();
|
|
||||||
importer.importAllRows(new DataInputStream(new ByteArrayInputStream(dataBytes)));
|
|
||||||
|
|
||||||
assertEquals(insertedValues.toString(), 3, insertedValues.size());
|
|
||||||
|
|
||||||
ContentValues value = insertedValues.get(0);
|
|
||||||
assertEquals(value.toString(), 0, value.size());
|
|
||||||
|
|
||||||
value = insertedValues.get(1);
|
|
||||||
assertEquals(value.toString(), 0, value.size());
|
|
||||||
|
|
||||||
// Verify the third row (only one with values)
|
|
||||||
value = insertedValues.get(2);
|
|
||||||
assertEquals(value.toString(), 3, value.size());
|
|
||||||
|
|
||||||
assertFalse(value.containsKey("col2"));
|
|
||||||
assertFalse(value.containsKey("col4"));
|
|
||||||
assertFalse(value.containsKey("col6"));
|
|
||||||
|
|
||||||
assertValue(42, "col1", value);
|
|
||||||
assertValue("lolcat", "col3", value);
|
|
||||||
assertValue(2.72, "col5", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testImportAllRows_bulks() throws Exception {
|
|
||||||
// Create a fake data stream to be read
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
|
|
||||||
DataOutputStream writer = new DataOutputStream(outputStream);
|
|
||||||
|
|
||||||
// Add the header
|
|
||||||
writer.writeInt(2);
|
|
||||||
writer.writeUTF("col1");
|
|
||||||
writer.writeByte(ContentTypeIds.INT_TYPE_ID);
|
|
||||||
writer.writeUTF("col2");
|
|
||||||
writer.writeByte(ContentTypeIds.STRING_TYPE_ID);
|
|
||||||
|
|
||||||
// Add lots of rows (so the insertions are split in multiple bulks)
|
|
||||||
int numRows = TEST_BULK_SIZE * 5 / 2;
|
|
||||||
writer.writeInt(numRows);
|
|
||||||
for (int i = 0; i < numRows; i++) {
|
|
||||||
writer.writeLong(3);
|
|
||||||
writer.writeInt(i);
|
|
||||||
writer.writeUTF(Integer.toString(i * 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.flush();
|
|
||||||
|
|
||||||
// Do the importing
|
|
||||||
DatabaseImporter importer = new TestableDatabaseImporter(false);
|
|
||||||
byte[] dataBytes = outputStream.toByteArray();
|
|
||||||
importer.importAllRows(new DataInputStream(new ByteArrayInputStream(dataBytes)));
|
|
||||||
|
|
||||||
// Verify the rows
|
|
||||||
assertEquals(numRows, insertedValues.size());
|
|
||||||
for (int i = 0; i < numRows; i++) {
|
|
||||||
ContentValues value = insertedValues.get(i);
|
|
||||||
assertEquals(value.toString(), 2, value.size());
|
|
||||||
assertValue(i, "col1", value);
|
|
||||||
assertValue(Integer.toString(i * 2), "col2", value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeFullHeader(DataOutputStream writer) throws IOException {
|
|
||||||
// Add the header
|
|
||||||
writer.writeInt(7);
|
|
||||||
writer.writeUTF("col1");
|
|
||||||
writer.writeByte(ContentTypeIds.INT_TYPE_ID);
|
|
||||||
writer.writeUTF("col2");
|
|
||||||
writer.writeByte(ContentTypeIds.BOOLEAN_TYPE_ID);
|
|
||||||
writer.writeUTF("col3");
|
|
||||||
writer.writeByte(ContentTypeIds.STRING_TYPE_ID);
|
|
||||||
writer.writeUTF("col4");
|
|
||||||
writer.writeByte(ContentTypeIds.FLOAT_TYPE_ID);
|
|
||||||
writer.writeUTF("col5");
|
|
||||||
writer.writeByte(ContentTypeIds.DOUBLE_TYPE_ID);
|
|
||||||
writer.writeUTF("col6");
|
|
||||||
writer.writeByte(ContentTypeIds.LONG_TYPE_ID);
|
|
||||||
writer.writeUTF("col7");
|
|
||||||
writer.writeByte(ContentTypeIds.BLOB_TYPE_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> void assertValue(T expectedValue, String name, ContentValues values) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
T value = (T) values.get(name);
|
|
||||||
assertNotNull(value);
|
|
||||||
assertEquals(expectedValue, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertBlobValue(String expectedValue, String name, ContentValues values ){
|
|
||||||
byte[] blob = values.getAsByteArray(name);
|
|
||||||
assertEquals(expectedValue, new String(blob));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-67
@@ -271,73 +271,6 @@ public class SettingTest extends ActivityInstrumentationTestCase2<TrackListActiv
|
|||||||
assertEquals(!isImperial, chartView.isMetricUnits());
|
assertEquals(!isImperial, chartView.isMetricUnits());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the backup.
|
|
||||||
*/
|
|
||||||
public void testBackup() {
|
|
||||||
// Delete all backup at first.
|
|
||||||
EndToEndTestUtils.deleteExportedFiles(EndToEndTestUtils.BACKUPS);
|
|
||||||
EndToEndTestUtils.createTrackIfEmpty(3, true);
|
|
||||||
|
|
||||||
// Write to SD card.
|
|
||||||
EndToEndTestUtils.findMenuItem(activityMyTracks.getString(R.string.menu_settings), true);
|
|
||||||
EndToEndTestUtils.SOLO.clickOnText(activityMyTracks.getString(R.string.settings_backup_reset));
|
|
||||||
EndToEndTestUtils.SOLO.clickOnText(activityMyTracks.getString(R.string.settings_backup));
|
|
||||||
assertTrue(EndToEndTestUtils.SOLO.waitForText(
|
|
||||||
activityMyTracks.getString(R.string.settings_backup_now_success), 0,
|
|
||||||
EndToEndTestUtils.SUPER_LONG_WAIT_TIME));
|
|
||||||
instrumentation.waitForIdleSync();
|
|
||||||
|
|
||||||
// Delete all tracks.
|
|
||||||
EndToEndTestUtils.SOLO.goBack();
|
|
||||||
EndToEndTestUtils.SOLO.goBack();
|
|
||||||
EndToEndTestUtils.deleteAllTracks();
|
|
||||||
instrumentation.waitForIdleSync();
|
|
||||||
|
|
||||||
// Read from SD card.
|
|
||||||
EndToEndTestUtils.findMenuItem(activityMyTracks.getString(R.string.menu_settings), true);
|
|
||||||
EndToEndTestUtils.SOLO.clickOnText(activityMyTracks.getString(R.string.settings_backup_reset));
|
|
||||||
EndToEndTestUtils.SOLO.clickOnText(activityMyTracks.getString(R.string.settings_backup));
|
|
||||||
EndToEndTestUtils.SOLO
|
|
||||||
.clickOnText(activityMyTracks.getString(R.string.settings_backup_restore));
|
|
||||||
EndToEndTestUtils.getButtonOnScreen(activityMyTracks.getString(R.string.generic_yes), true,
|
|
||||||
true);
|
|
||||||
|
|
||||||
// Now there should be 2 backups.
|
|
||||||
instrumentation.waitForIdleSync();
|
|
||||||
assertEquals(2, EndToEndTestUtils.SOLO.getCurrentViews(ListView.class).get(0).getCount());
|
|
||||||
|
|
||||||
// Click the first one.
|
|
||||||
EndToEndTestUtils.SOLO.clickOnView(EndToEndTestUtils.SOLO.getCurrentViews(ListView.class)
|
|
||||||
.get(0).getChildAt(0));
|
|
||||||
assertTrue(EndToEndTestUtils.SOLO.waitForText(
|
|
||||||
activityMyTracks.getString(R.string.settings_backup_restore_success), 0,
|
|
||||||
EndToEndTestUtils.SUPER_LONG_WAIT_TIME));
|
|
||||||
|
|
||||||
// Check restore track.
|
|
||||||
instrumentation.waitForIdleSync();
|
|
||||||
boolean isContainTrack = EndToEndTestUtils.SOLO.searchText(EndToEndTestUtils.trackName);
|
|
||||||
EndToEndTestUtils.findMenuItem(activityMyTracks.getString(R.string.menu_settings), true);
|
|
||||||
EndToEndTestUtils.SOLO.clickOnText(activityMyTracks.getString(R.string.settings_backup_reset));
|
|
||||||
EndToEndTestUtils.SOLO
|
|
||||||
.clickOnText(activityMyTracks.getString(R.string.settings_backup_restore));
|
|
||||||
EndToEndTestUtils.getButtonOnScreen(activityMyTracks.getString(R.string.generic_yes), true,
|
|
||||||
true);
|
|
||||||
instrumentation.waitForIdleSync();
|
|
||||||
|
|
||||||
// Click the second one.
|
|
||||||
EndToEndTestUtils.SOLO.clickOnView(EndToEndTestUtils.SOLO.getCurrentViews(ListView.class)
|
|
||||||
.get(0).getChildAt(1));
|
|
||||||
assertTrue(EndToEndTestUtils.SOLO.waitForText(
|
|
||||||
activityMyTracks.getString(R.string.settings_backup_restore_success), 0,
|
|
||||||
EndToEndTestUtils.SUPER_LONG_WAIT_TIME));
|
|
||||||
|
|
||||||
// For the older backup contains tracks and the newer backup is empty. The
|
|
||||||
// search result of twice must be different.
|
|
||||||
instrumentation.waitForIdleSync();
|
|
||||||
assertTrue(isContainTrack != EndToEndTestUtils.SOLO.searchText(EndToEndTestUtils.trackName));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the setting of recording. This test changes the default name and
|
* Tests the setting of recording. This test changes the default name and
|
||||||
* activity of tracks, and the creates a new track to check it.
|
* activity of tracks, and the creates a new track to check it.
|
||||||
|
|||||||
Reference in New Issue
Block a user