Build a structure to allow for more easily creating new versions of
preferences Storage importers/exporters. Password/encryption key prompting is now down in centralized place. On import, the password prompt is given if the file to be imported uses an importer implementation that requires a password and no password is provided. On export, the password prompt is given if the chosen version is for an exporter that requires a password and no password was provided. For instance, for automatic backups, a password could be stored in preferences and provided to the exporter, so no password prompt would be given.
This commit is contained in:
parent
19bff64672
commit
89bdbdce94
16 changed files with 731 additions and 311 deletions
|
@ -1039,12 +1039,14 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
|
|||
<string name="settings_import_success_multiple">Imported <xliff:g id="numAccounts">%s</xliff:g> accounts from <xliff:g id="filename">%s</xliff:g></string>
|
||||
<string name="settings_import_success_single">Imported 1 account from <xliff:g id="filename">%s</xliff:g></string>
|
||||
<string name="settings_export_failure">Failed to export settings: <xliff:g id="reason">%s</xliff:g></string>
|
||||
<string name="settings_import_failure">Failed from import settings from <xliff:g id="filename">%s</xliff:g>:<xliff:g id="reason">%s</xliff:g></string>
|
||||
<string name="settings_import_failure">Failed to import settings from <xliff:g id="filename">%s</xliff:g>:<xliff:g id="reason">%s</xliff:g></string>
|
||||
<string name="settings_export_success_header">Export succeeded</string>
|
||||
<string name="settings_export_failed_header">Export failed</string>
|
||||
<string name="settings_import_success_header">Import succeeded</string>
|
||||
<string name="settings_import_failed_header">Import failed</string>
|
||||
|
||||
<string name="settings_unknown_version">Unable to handle file of version <xliff:g id="version">%s</xliff:g></string>
|
||||
|
||||
<string name="account_unavailable">Account \"<xliff:g id="account">%s</xliff:g>\" is unavailable; check storage</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
|
||||
package com.fsck.k9.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.ContentResolver;
|
||||
|
@ -15,31 +24,46 @@ import android.os.Bundle;
|
|||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.*;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.*;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
|
||||
import com.fsck.k9.*;
|
||||
import com.fsck.k9.helper.SizeFormatter;
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.AccountStats;
|
||||
import com.fsck.k9.BaseAccount;
|
||||
import com.fsck.k9.FontSizes;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.SearchAccount;
|
||||
import com.fsck.k9.SearchSpecification;
|
||||
import com.fsck.k9.activity.setup.AccountSettings;
|
||||
import com.fsck.k9.activity.setup.AccountSetupBasics;
|
||||
import com.fsck.k9.activity.setup.Prefs;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.controller.MessagingListener;
|
||||
import com.fsck.k9.helper.SizeFormatter;
|
||||
import com.fsck.k9.mail.Flag;
|
||||
import com.fsck.k9.mail.internet.MimeUtility;
|
||||
import com.fsck.k9.mail.store.StorageManager;
|
||||
import com.fsck.k9.view.ColorChip;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class Accounts extends K9ListActivity implements OnItemClickListener, OnClickListener {
|
||||
|
||||
/**
|
||||
|
@ -832,70 +856,78 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
|
|||
|
||||
private void onImport(Uri uri) {
|
||||
Log.i(K9.LOG_TAG, "onImport importing from URI " + uri.getPath());
|
||||
try {
|
||||
final String fileName = uri.getPath();
|
||||
ContentResolver resolver = getContentResolver();
|
||||
final InputStream is = resolver.openInputStream(uri);
|
||||
|
||||
PasswordEntryDialog dialog = new PasswordEntryDialog(this, getString(R.string.settings_encryption_password_prompt),
|
||||
new PasswordEntryDialog.PasswordEntryListener() {
|
||||
public void passwordChosen(String chosenPassword) {
|
||||
String toastText = Accounts.this.getString(R.string.settings_importing);
|
||||
Toast toast = Toast.makeText(Accounts.this.getApplication(), toastText, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
mHandler.progress(true);
|
||||
AsyncUIProcessor.getInstance(Accounts.this.getApplication()).importSettings(is, chosenPassword, new ImportListener() {
|
||||
public void failure(final String message, Exception e) {
|
||||
Accounts.this.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
mHandler.progress(false);
|
||||
showDialog(Accounts.this, R.string.settings_import_failed_header, Accounts.this.getString(R.string.settings_import_failure, fileName, message));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void importSuccess(final int numAccounts) {
|
||||
Accounts.this.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
mHandler.progress(false);
|
||||
String messageText =
|
||||
numAccounts != 1
|
||||
? Accounts.this.getString(R.string.settings_import_success_multiple, numAccounts, fileName)
|
||||
: Accounts.this.getString(R.string.settings_import_success_single, fileName);
|
||||
showDialog(Accounts.this, R.string.settings_import_success_header, messageText);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
String toastText = Accounts.this.getString(R.string.settings_import_failure, uri.getPath(), fnfe.getMessage());
|
||||
Toast toast = Toast.makeText(Accounts.this.getApplication(), toastText, 1);
|
||||
toast.show();
|
||||
}
|
||||
}
|
||||
|
||||
private static void showDialog(final Activity activity, int headerRes, String message) {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle(headerRes);
|
||||
builder.setMessage(message);
|
||||
builder.setPositiveButton(R.string.okay_action,
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
||||
final String fileName = uri.getPath();
|
||||
AsyncUIProcessor.getInstance(Accounts.this.getApplication()).importSettings(this, uri, new ImportListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
public void success(int numAccounts)
|
||||
{
|
||||
mHandler.progress(false);
|
||||
String messageText =
|
||||
numAccounts != 1
|
||||
? Accounts.this.getString(R.string.settings_import_success_multiple, numAccounts, fileName)
|
||||
: Accounts.this.getString(R.string.settings_import_success_single, fileName);
|
||||
showDialog(Accounts.this, R.string.settings_import_success_header, messageText);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(String message, Exception e)
|
||||
{
|
||||
mHandler.progress(false);
|
||||
showDialog(Accounts.this, R.string.settings_import_failed_header, Accounts.this.getString(R.string.settings_import_failure, fileName, e.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void canceled()
|
||||
{
|
||||
mHandler.progress(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void started()
|
||||
{
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mHandler.progress(true);
|
||||
String toastText = Accounts.this.getString(R.string.settings_importing);
|
||||
Toast toast = Toast.makeText(Accounts.this, toastText, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void showDialog(final Context context, final int headerRes, final String message) {
|
||||
this.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(headerRes);
|
||||
builder.setMessage(message);
|
||||
builder.setPositiveButton(R.string.okay_action,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class AccountsAdapter extends ArrayAdapter<BaseAccount> {
|
||||
public AccountsAdapter(BaseAccount[] accounts) {
|
||||
super(Accounts.this, 0, accounts);
|
||||
|
|
|
@ -5,8 +5,12 @@ import java.io.InputStream;
|
|||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
|
@ -33,7 +37,10 @@ public class AsyncUIProcessor {
|
|||
}
|
||||
return inst;
|
||||
}
|
||||
public void exportSettings(final String uuid, final String encryptionKey, final ExportListener listener) {
|
||||
public void execute(Runnable runnable) {
|
||||
threadPool.execute(runnable);
|
||||
}
|
||||
public void exportSettings(final Activity activity, final String version, final String uuid, final ExportListener listener) {
|
||||
threadPool.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
|
@ -46,11 +53,9 @@ public class AsyncUIProcessor {
|
|||
dir.mkdirs();
|
||||
File file = Utility.createUniqueFile(dir, "settings.k9s");
|
||||
String fileName = file.getAbsolutePath();
|
||||
StorageExporter.exportPreferences(mApplication, uuid, fileName, encryptionKey);
|
||||
if (listener != null) {
|
||||
listener.exportSuccess(fileName);
|
||||
}
|
||||
StorageExporter.exportPreferences(activity, version, uuid, fileName, null, listener);
|
||||
} catch (Exception e) {
|
||||
Log.w(K9.LOG_TAG, "Exception during export", e);
|
||||
listener.failure(e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
|
@ -58,43 +63,73 @@ public class AsyncUIProcessor {
|
|||
);
|
||||
|
||||
}
|
||||
public void importSettings(final String fileName, final String encryptionKey, final ImportListener listener) {
|
||||
|
||||
public void importSettings(final Activity activity, final Uri uri, final ImportListener listener) {
|
||||
threadPool.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
InputStream is = null;
|
||||
try {
|
||||
int numAccounts = StorageImporter.importPreferences(mApplication, fileName, encryptionKey);
|
||||
K9.setServicesEnabled(mApplication);
|
||||
if (listener != null) {
|
||||
listener.importSuccess(numAccounts);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
listener.failure(e.getLocalizedMessage(), e);
|
||||
ContentResolver resolver = mApplication.getContentResolver();
|
||||
is = resolver.openInputStream(uri);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.w(K9.LOG_TAG, "Exception while resolving Uri to InputStream", e);
|
||||
if (listener != null) {
|
||||
listener.failure(e.getLocalizedMessage(), e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final InputStream myIs = is;
|
||||
StorageImporter.importPreferences(activity, is, null, new ImportListener() {
|
||||
@Override
|
||||
public void failure(String message, Exception e) {
|
||||
quietClose(myIs);
|
||||
if (listener != null) {
|
||||
listener.failure(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void success(int numAccounts) {
|
||||
quietClose(myIs);
|
||||
if (listener != null) {
|
||||
listener.success(numAccounts);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void canceled() {
|
||||
quietClose(myIs);
|
||||
if (listener != null) {
|
||||
listener.canceled();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void started()
|
||||
{
|
||||
if (listener != null) {
|
||||
listener.started();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
);
|
||||
}
|
||||
public void importSettings(final InputStream inputStream, final String encryptionKey, final ImportListener listener) {
|
||||
threadPool.execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
int numAccounts = StorageImporter.importPreferences(mApplication, inputStream, encryptionKey);
|
||||
K9.setServicesEnabled(mApplication);
|
||||
if (listener != null) {
|
||||
listener.importSuccess(numAccounts);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
listener.failure(e.getLocalizedMessage(), e);
|
||||
}
|
||||
|
||||
private void quietClose(InputStream is)
|
||||
{
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.w(K9.LOG_TAG, "Unable to close inputStream", e);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -7,59 +7,90 @@ import android.widget.Toast;
|
|||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.preferences.StorageVersioning;
|
||||
|
||||
public class ExportHelper {
|
||||
public static void exportSettings(final Activity activity, final Progressable progressable, final Account account) {
|
||||
PasswordEntryDialog dialog = new PasswordEntryDialog(activity, activity.getString(R.string.settings_encryption_password_prompt),
|
||||
new PasswordEntryDialog.PasswordEntryListener() {
|
||||
public void passwordChosen(String chosenPassword) {
|
||||
String toastText = activity.getString(R.string.settings_exporting);
|
||||
Toast toast = Toast.makeText(activity, toastText, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
progressable.setProgress(true);
|
||||
String uuid = null;
|
||||
if (account != null) {
|
||||
uuid = account.getUuid();
|
||||
}
|
||||
AsyncUIProcessor.getInstance(activity.getApplication()).exportSettings(uuid, chosenPassword,
|
||||
new ExportListener() {
|
||||
public void failure(final String message, Exception e) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
progressable.setProgress(false);
|
||||
showDialog(activity, R.string.settings_export_failed_header, activity.getString(R.string.settings_export_failure, message));
|
||||
}
|
||||
});
|
||||
}
|
||||
public static void exportSettings(final Activity activity, final Account account, final ExportListener listener) {
|
||||
// Once there are more versions, build a UI to select which one to use. For now, use the encrypted/encoded version:
|
||||
String version = StorageVersioning.STORAGE_VERSION.VERSION1.getVersionString();
|
||||
String uuid = null;
|
||||
if (account != null) {
|
||||
uuid = account.getUuid();
|
||||
}
|
||||
AsyncUIProcessor.getInstance(activity.getApplication()).exportSettings(activity, version, uuid, new ExportListener() {
|
||||
|
||||
public void exportSuccess(final String fileName) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
progressable.setProgress(false);
|
||||
showDialog(activity, R.string.settings_export_success_header, activity.getString(R.string.settings_export_success, fileName));
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void canceled()
|
||||
{
|
||||
if (listener != null) {
|
||||
listener.canceled();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(String message, Exception e)
|
||||
{
|
||||
if (listener != null) {
|
||||
listener.failure(message, e);
|
||||
}
|
||||
showDialog(activity, R.string.settings_export_failed_header, activity.getString(R.string.settings_export_failure, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void started()
|
||||
{
|
||||
if (listener != null) {
|
||||
listener.started();
|
||||
}
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
String toastText = activity.getString(R.string.settings_exporting);
|
||||
Toast toast = Toast.makeText(activity, toastText, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
private static void showDialog(final Activity activity, int headerRes, String message) {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle(headerRes);
|
||||
builder.setMessage(message);
|
||||
builder.setPositiveButton(R.string.okay_action,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
public void success(String fileName)
|
||||
{
|
||||
if (listener != null) {
|
||||
listener.success(fileName);
|
||||
}
|
||||
showDialog(activity, R.string.settings_export_success_header, activity.getString(R.string.settings_export_success, fileName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void success()
|
||||
{
|
||||
// This one should never be called here because the AsyncUIProcessor will generate a filename
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void showDialog(final Activity activity, final int headerRes, final String message) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
|
||||
builder.show();
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle(headerRes);
|
||||
builder.setMessage(message);
|
||||
builder.setPositiveButton(R.string.okay_action,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
builder.show();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
package com.fsck.k9.activity;
|
||||
|
||||
public interface ExportListener {
|
||||
public void exportSuccess(String fileName);
|
||||
public void success(String fileName);
|
||||
public void success();
|
||||
|
||||
public void failure(String message, Exception e);
|
||||
|
||||
public void canceled();
|
||||
|
||||
public void started();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package com.fsck.k9.activity;
|
||||
|
||||
public interface ImportListener {
|
||||
public void importSuccess(int numAccounts);
|
||||
public void success(int numAccounts);
|
||||
|
||||
public void failure(String message, Exception e);
|
||||
|
||||
public void canceled();
|
||||
|
||||
public void started();
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import com.fsck.k9.K9;
|
|||
import com.fsck.k9.helper.DateFormatter;
|
||||
|
||||
|
||||
public class K9Activity extends Activity implements Progressable {
|
||||
public class K9Activity extends Activity {
|
||||
private GestureDetector gestureDetector;
|
||||
|
||||
protected ScrollView mTopView;
|
||||
|
@ -167,7 +167,39 @@ public class K9Activity extends Activity implements Progressable {
|
|||
}
|
||||
|
||||
public void onExport(final Account account) {
|
||||
ExportHelper.exportSettings(this, this, account);
|
||||
ExportHelper.exportSettings(this, account, new ExportListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void canceled()
|
||||
{
|
||||
setProgress(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(String message, Exception e)
|
||||
{
|
||||
setProgress(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void started()
|
||||
{
|
||||
setProgress(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void success(String fileName)
|
||||
{
|
||||
setProgress(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void success()
|
||||
{
|
||||
setProgress(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import com.fsck.k9.Account;
|
|||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.helper.DateFormatter;
|
||||
|
||||
public class K9ListActivity extends ListActivity implements Progressable {
|
||||
public class K9ListActivity extends ListActivity {
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
K9Activity.setLanguage(this, K9.getK9Language());
|
||||
|
@ -94,7 +94,39 @@ public class K9ListActivity extends ListActivity implements Progressable {
|
|||
}
|
||||
|
||||
public void onExport(final Account account) {
|
||||
ExportHelper.exportSettings(this, this, account);
|
||||
ExportHelper.exportSettings(this, account, new ExportListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void canceled()
|
||||
{
|
||||
setProgress(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(String message, Exception e)
|
||||
{
|
||||
setProgress(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void started()
|
||||
{
|
||||
setProgress(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void success(String fileName)
|
||||
{
|
||||
setProgress(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void success()
|
||||
{
|
||||
setProgress(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package com.fsck.k9.activity;
|
||||
|
||||
public interface Progressable {
|
||||
public void setProgress(boolean progress);
|
||||
}
|
11
src/com/fsck/k9/preferences/IStorageExporter.java
Normal file
11
src/com/fsck/k9/preferences/IStorageExporter.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package com.fsck.k9.preferences;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public interface IStorageExporter
|
||||
{
|
||||
public boolean needsKey();
|
||||
public void exportPreferences(Context context, String uuid, OutputStream os, String encryptionKey) throws StorageImportExportException;
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
package com.fsck.k9.preferences;
|
||||
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.preferences.StorageImporter.ImportElement;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public interface IStorageImporter {
|
||||
public abstract int importPreferences(Preferences preferences, SharedPreferences.Editor context, String data, String encryptionKey) throws StorageImportExportException;
|
||||
public boolean needsKey();
|
||||
public abstract int importPreferences(Preferences preferences, SharedPreferences.Editor context, ImportElement dataset, String encryptionKey) throws StorageImportExportException;
|
||||
}
|
|
@ -1,112 +1,134 @@
|
|||
package com.fsck.k9.preferences;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.activity.AsyncUIProcessor;
|
||||
import com.fsck.k9.activity.ExportListener;
|
||||
import com.fsck.k9.activity.PasswordEntryDialog;
|
||||
|
||||
public class StorageExporter {
|
||||
public static void exportPreferences(Context context, String uuid, String fileName, String encryptionKey) throws StorageImportExportException {
|
||||
|
||||
Log.i(K9.LOG_TAG, "Exporting preferences for account " + uuid + " to file " + fileName);
|
||||
File outFile = new File(fileName);
|
||||
OutputStream os = null;
|
||||
public class StorageExporter
|
||||
{
|
||||
private static void exportPreferences(Activity activity, String version, String uuid, String fileName, OutputStream os, String encryptionKey, final ExportListener listener) {
|
||||
try {
|
||||
os = new FileOutputStream(outFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new StorageImportExportException("Unable to export settings", e);
|
||||
IStorageExporter storageExporter = StorageVersioning.createExporter(version);
|
||||
if (storageExporter == null) {
|
||||
throw new StorageImportExportException(activity.getString(R.string.settings_unknown_version, version), null);
|
||||
}
|
||||
if (storageExporter.needsKey() && encryptionKey == null) {
|
||||
gatherPassword(activity, storageExporter, uuid, fileName, os, listener);
|
||||
}
|
||||
else
|
||||
{
|
||||
finishExport(activity, storageExporter, uuid, fileName, os, encryptionKey, listener);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
if (listener != null) {
|
||||
listener.failure(e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportPreferences(Activity activity, String version, String uuid, String fileName, String encryptionKey, final ExportListener listener) throws StorageImportExportException {
|
||||
exportPreferences(activity, version, uuid, fileName, null, encryptionKey, listener);
|
||||
}
|
||||
|
||||
public static void exportPrefererences(Activity activity, String version, String uuid, OutputStream os, String encryptionKey, final ExportListener listener) throws StorageImportExportException {
|
||||
exportPreferences(activity, version, uuid, null, os, encryptionKey, listener);
|
||||
}
|
||||
|
||||
private static void gatherPassword(final Activity activity, final IStorageExporter storageExporter, final String uuid, final String fileName, final OutputStream os, final ExportListener listener) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PasswordEntryDialog dialog = new PasswordEntryDialog(activity, activity.getString(R.string.settings_encryption_password_prompt),
|
||||
new PasswordEntryDialog.PasswordEntryListener() {
|
||||
public void passwordChosen(final String chosenPassword) {
|
||||
|
||||
AsyncUIProcessor.getInstance(activity.getApplication()).execute(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
finishExport(activity, storageExporter, uuid, fileName, os, chosenPassword, listener);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.w(K9.LOG_TAG, "Exception while finishing export", e);
|
||||
if (listener != null) {
|
||||
listener.failure(e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (listener != null) {
|
||||
listener.canceled();
|
||||
}
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static void finishExport(Activity activity, IStorageExporter storageExporter, String uuid, String fileName, OutputStream os, String encryptionKey, ExportListener listener) throws StorageImportExportException {
|
||||
boolean needToClose = false;
|
||||
if (listener != null) {
|
||||
listener.started();
|
||||
}
|
||||
try {
|
||||
exportPrefererences(context, uuid, os, encryptionKey);
|
||||
} finally {
|
||||
// This needs to be after the password prompt. If the user cancels the password, we do not want
|
||||
// to create the file needlessly
|
||||
if (os == null && fileName != null) {
|
||||
needToClose = true;
|
||||
File outFile = new File(fileName);
|
||||
os = new FileOutputStream(outFile);
|
||||
}
|
||||
if (os != null) {
|
||||
storageExporter.exportPreferences(activity, uuid, os, encryptionKey);
|
||||
if (listener != null) {
|
||||
if (fileName != null) {
|
||||
listener.success(fileName);
|
||||
}
|
||||
else {
|
||||
listener.success();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new StorageImportExportException("Internal error; no fileName or OutputStream", null);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new StorageImportExportException(e.getLocalizedMessage(), e);
|
||||
}
|
||||
finally {
|
||||
if (needToClose && os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (Exception e) {
|
||||
Log.i(K9.LOG_TAG, "Unable to close OutputStream for file " + fileName + ": " + e.getLocalizedMessage());
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.w(K9.LOG_TAG, "Unable to close OutputStream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(K9.LOG_TAG, "Exported preferences for account " + uuid + " to file " + fileName + " which is size " + outFile.length());
|
||||
}
|
||||
|
||||
public static void exportPrefererences(Context context, String uuid, OutputStream os, String encryptionKey) throws StorageImportExportException {
|
||||
try {
|
||||
Log.i(K9.LOG_TAG, "Exporting preferences for account " + uuid + " to OutputStream");
|
||||
K9Krypto krypto = new K9Krypto(encryptionKey, K9Krypto.MODE.ENCRYPT);
|
||||
OutputStreamWriter sw = new OutputStreamWriter(os);
|
||||
PrintWriter pf = new PrintWriter(sw);
|
||||
long keysEvaluated = 0;
|
||||
long keysExported = 0;
|
||||
pf.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
|
||||
pf.print("<k9settings version=\"1\"");
|
||||
pf.println(">");
|
||||
|
||||
Preferences preferences = Preferences.getPreferences(context);
|
||||
SharedPreferences storage = preferences.getPreferences();
|
||||
|
||||
Account[] accounts = preferences.getAccounts();
|
||||
Set<String> accountUuids = new HashSet<String>();
|
||||
for (Account account : accounts) {
|
||||
accountUuids.add(account.getUuid());
|
||||
}
|
||||
|
||||
Map < String, ? extends Object > prefs = storage.getAll();
|
||||
for (Map.Entry < String, ? extends Object > entry : prefs.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue().toString();
|
||||
//Log.i(K9.LOG_TAG, "Evaluating key " + key);
|
||||
keysEvaluated++;
|
||||
if (uuid != null) {
|
||||
String[] comps = key.split("\\.");
|
||||
String keyUuid = comps[0];
|
||||
//Log.i(K9.LOG_TAG, "Got key uuid " + keyUuid);
|
||||
if (uuid.equals(keyUuid) == false) {
|
||||
//Log.i(K9.LOG_TAG, "Skipping key " + key + " which is for another account or global");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
String[] comps = key.split("\\.");
|
||||
if (comps.length > 1) {
|
||||
String keyUuid = comps[0];
|
||||
if (accountUuids.contains(keyUuid) == false) {
|
||||
//Log.i(K9.LOG_TAG, "Skipping key " + key + " which is not for any current account");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
String keyEnc = krypto.encrypt(key);
|
||||
String valueEnc = krypto.encrypt(value);
|
||||
String output = keyEnc + ":" + valueEnc;
|
||||
//Log.i(K9.LOG_TAG, "For key " + key + ", output is " + output);
|
||||
pf.println(output);
|
||||
keysExported++;
|
||||
|
||||
}
|
||||
|
||||
pf.println("</k9settings>");
|
||||
pf.flush();
|
||||
|
||||
Log.i(K9.LOG_TAG, "Exported " + keysExported + " settings of " + keysEvaluated
|
||||
+ " total for preferences for account " + uuid);
|
||||
} catch (Exception e) {
|
||||
throw new StorageImportExportException("Unable to encrypt settings", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
92
src/com/fsck/k9/preferences/StorageExporterVersion1.java
Normal file
92
src/com/fsck/k9/preferences/StorageExporterVersion1.java
Normal file
|
@ -0,0 +1,92 @@
|
|||
package com.fsck.k9.preferences;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
|
||||
public class StorageExporterVersion1 implements IStorageExporter {
|
||||
public void exportPreferences(Context context, String uuid, OutputStream os, String encryptionKey) throws StorageImportExportException {
|
||||
try {
|
||||
Log.i(K9.LOG_TAG, "Exporting preferences for account " + uuid + " to OutputStream");
|
||||
K9Krypto krypto = new K9Krypto(encryptionKey, K9Krypto.MODE.ENCRYPT);
|
||||
OutputStreamWriter sw = new OutputStreamWriter(os);
|
||||
PrintWriter pf = new PrintWriter(sw);
|
||||
long keysEvaluated = 0;
|
||||
long keysExported = 0;
|
||||
pf.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
|
||||
pf.print("<k9settings version=\"1\"");
|
||||
pf.println(">");
|
||||
|
||||
Preferences preferences = Preferences.getPreferences(context);
|
||||
SharedPreferences storage = preferences.getPreferences();
|
||||
|
||||
Account[] accounts = preferences.getAccounts();
|
||||
Set<String> accountUuids = new HashSet<String>();
|
||||
for (Account account : accounts) {
|
||||
accountUuids.add(account.getUuid());
|
||||
}
|
||||
|
||||
Map < String, ? extends Object > prefs = storage.getAll();
|
||||
for (Map.Entry < String, ? extends Object > entry : prefs.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue().toString();
|
||||
//Log.i(K9.LOG_TAG, "Evaluating key " + key);
|
||||
keysEvaluated++;
|
||||
if (uuid != null) {
|
||||
String[] comps = key.split("\\.");
|
||||
String keyUuid = comps[0];
|
||||
//Log.i(K9.LOG_TAG, "Got key uuid " + keyUuid);
|
||||
if (uuid.equals(keyUuid) == false) {
|
||||
//Log.i(K9.LOG_TAG, "Skipping key " + key + " which is for another account or global");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
String[] comps = key.split("\\.");
|
||||
if (comps.length > 1) {
|
||||
String keyUuid = comps[0];
|
||||
if (accountUuids.contains(keyUuid) == false) {
|
||||
//Log.i(K9.LOG_TAG, "Skipping key " + key + " which is not for any current account");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
String keyEnc = krypto.encrypt(key);
|
||||
String valueEnc = krypto.encrypt(value);
|
||||
String output = keyEnc + ":" + valueEnc;
|
||||
//Log.i(K9.LOG_TAG, "For key " + key + ", output is " + output);
|
||||
pf.println(output);
|
||||
keysExported++;
|
||||
|
||||
}
|
||||
|
||||
pf.println("</k9settings>");
|
||||
pf.flush();
|
||||
|
||||
Log.i(K9.LOG_TAG, "Exported " + keysExported + " settings of " + keysEvaluated
|
||||
+ " total for preferences for account " + uuid);
|
||||
} catch (Exception e) {
|
||||
throw new StorageImportExportException("Unable to encrypt settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsKey()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,10 @@
|
|||
package com.fsck.k9.preferences;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
|
@ -18,43 +14,22 @@ import org.xml.sax.SAXException;
|
|||
import org.xml.sax.XMLReader;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import android.content.Context;
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.R;
|
||||
import com.fsck.k9.activity.AsyncUIProcessor;
|
||||
import com.fsck.k9.activity.ImportListener;
|
||||
import com.fsck.k9.activity.PasswordEntryDialog;
|
||||
import com.fsck.k9.helper.DateFormatter;
|
||||
|
||||
public class StorageImporter {
|
||||
public static int importPreferences(Context context, String fileName, String encryptionKey) throws StorageImportExportException {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(fileName);
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
throw new StorageImportExportException("Failure opening settings file " + fileName, fnfe);
|
||||
}
|
||||
|
||||
public static void importPreferences(Activity activity, InputStream is, String providedEncryptionKey, ImportListener listener) {
|
||||
try {
|
||||
int count = importPreferences(context, is, encryptionKey);
|
||||
return count;
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
Log.i(K9.LOG_TAG, "Unable to close InputStream for file " + fileName + ": " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public static int importPreferences(Context context, InputStream is, String encryptionKey) throws StorageImportExportException {
|
||||
try {
|
||||
Preferences preferences = Preferences.getPreferences(context);
|
||||
SharedPreferences storage = preferences.getPreferences();
|
||||
SharedPreferences.Editor editor = storage.edit();
|
||||
|
||||
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||
SAXParser sp = spf.newSAXParser();
|
||||
XMLReader xr = sp.getXMLReader();
|
||||
|
@ -63,48 +38,104 @@ public class StorageImporter {
|
|||
|
||||
xr.parse(new InputSource(is));
|
||||
|
||||
Element dataset = handler.getRootElement();
|
||||
ImportElement dataset = handler.getRootElement();
|
||||
String version = dataset.attributes.get("version");
|
||||
Log.i(K9.LOG_TAG, "Got settings file version " + version);
|
||||
|
||||
IStorageImporter storageImporter = null;
|
||||
if ("1".equals(version)) {
|
||||
storageImporter = new StorageImporterVersion1();
|
||||
} else {
|
||||
throw new StorageImportExportException("Unable to read file of version " + version
|
||||
+ "; (only version 1 is readable)");
|
||||
IStorageImporter storageImporter = StorageVersioning.createImporter(version);
|
||||
if (storageImporter == null)
|
||||
{
|
||||
throw new StorageImportExportException(activity.getString(R.string.settings_unknown_version, version));
|
||||
}
|
||||
int numAccounts = 0;
|
||||
if (storageImporter != null) {
|
||||
String data = dataset.data.toString();
|
||||
numAccounts = storageImporter.importPreferences(preferences, editor, data, encryptionKey);
|
||||
if (providedEncryptionKey != null || storageImporter.needsKey() == false) {
|
||||
Log.i(K9.LOG_TAG, "Version " + version + " settings file needs encryption key");
|
||||
finishImport(activity, storageImporter, dataset, providedEncryptionKey, listener);
|
||||
}
|
||||
else {
|
||||
gatherPassword(activity, storageImporter, dataset, listener);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (listener != null) {
|
||||
listener.failure(e.getLocalizedMessage(), e);
|
||||
}
|
||||
editor.commit();
|
||||
Preferences.getPreferences(context).refreshAccounts();
|
||||
DateFormatter.clearChosenFormat();
|
||||
K9.loadPrefs(Preferences.getPreferences(context));
|
||||
return numAccounts;
|
||||
} catch (SAXException se) {
|
||||
throw new StorageImportExportException("Failure reading settings file", se);
|
||||
} catch (IOException ie) {
|
||||
throw new StorageImportExportException("Failure reading settings file", ie);
|
||||
} catch (ParserConfigurationException pce) {
|
||||
throw new StorageImportExportException("Failure reading settings file", pce);
|
||||
}
|
||||
}
|
||||
|
||||
private static void finishImport(Activity context, IStorageImporter storageImporter, ImportElement dataset, String encryptionKey, ImportListener listener) throws StorageImportExportException {
|
||||
if (listener != null) {
|
||||
listener.started();
|
||||
}
|
||||
Preferences preferences = Preferences.getPreferences(context);
|
||||
SharedPreferences storage = preferences.getPreferences();
|
||||
SharedPreferences.Editor editor = storage.edit();
|
||||
int numAccounts = 0;
|
||||
if (storageImporter != null) {
|
||||
numAccounts = storageImporter.importPreferences(preferences, editor, dataset, encryptionKey);
|
||||
}
|
||||
editor.commit();
|
||||
Preferences.getPreferences(context).refreshAccounts();
|
||||
DateFormatter.clearChosenFormat();
|
||||
K9.loadPrefs(Preferences.getPreferences(context));
|
||||
K9.setServicesEnabled(context);
|
||||
if (listener != null) {
|
||||
listener.success(numAccounts);
|
||||
}
|
||||
}
|
||||
|
||||
private static void gatherPassword(final Activity activity, final IStorageImporter storageImporter, final ImportElement dataset, final ImportListener listener) {
|
||||
activity.runOnUiThread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PasswordEntryDialog dialog = new PasswordEntryDialog(activity, activity.getString(R.string.settings_encryption_password_prompt),
|
||||
new PasswordEntryDialog.PasswordEntryListener() {
|
||||
public void passwordChosen(final String chosenPassword) {
|
||||
AsyncUIProcessor.getInstance(activity.getApplication()).execute(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
finishImport(activity, storageImporter, dataset, chosenPassword, listener);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.w(K9.LOG_TAG, "Failure during import", e);
|
||||
if (listener != null) {
|
||||
listener.failure(e.getLocalizedMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (listener != null) {
|
||||
listener.canceled();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
private static class Element {
|
||||
public static class ImportElement {
|
||||
String name;
|
||||
Map<String, String> attributes = new HashMap<String, String>();
|
||||
Map<String, Element> subElements = new HashMap<String, Element>();
|
||||
Map<String, ImportElement> subElements = new HashMap<String, ImportElement>();
|
||||
StringBuilder data = new StringBuilder();
|
||||
}
|
||||
|
||||
private static class StorageImporterHandler extends DefaultHandler {
|
||||
private Element rootElement = new Element();
|
||||
private Stack<Element> mOpenTags = new Stack<Element>();
|
||||
private ImportElement rootElement = new ImportElement();
|
||||
private Stack<ImportElement> mOpenTags = new Stack<ImportElement>();
|
||||
|
||||
public Element getRootElement() {
|
||||
public ImportElement getRootElement() {
|
||||
return this.rootElement;
|
||||
}
|
||||
|
||||
|
@ -121,7 +152,7 @@ public class StorageImporter {
|
|||
public void startElement(String namespaceURI, String localName,
|
||||
String qName, Attributes attributes) throws SAXException {
|
||||
Log.i(K9.LOG_TAG, "Starting element " + localName);
|
||||
Element element = new Element();
|
||||
ImportElement element = new ImportElement();
|
||||
element.name = localName;
|
||||
mOpenTags.push(element);
|
||||
for (int i = 0; i < attributes.getLength(); i++) {
|
||||
|
@ -135,8 +166,8 @@ public class StorageImporter {
|
|||
@Override
|
||||
public void endElement(String namespaceURI, String localName, String qName) {
|
||||
Log.i(K9.LOG_TAG, "Ending element " + localName);
|
||||
Element element = mOpenTags.pop();
|
||||
Element superElement = mOpenTags.empty() ? null : mOpenTags.peek();
|
||||
ImportElement element = mOpenTags.pop();
|
||||
ImportElement superElement = mOpenTags.empty() ? null : mOpenTags.peek();
|
||||
if (superElement != null) {
|
||||
superElement.subElements.put(element.name, element);
|
||||
} else {
|
||||
|
|
|
@ -14,10 +14,13 @@ import android.util.Log;
|
|||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.preferences.StorageImporter.ImportElement;
|
||||
|
||||
public class StorageImporterVersion1 implements IStorageImporter {
|
||||
public int importPreferences(Preferences preferences, SharedPreferences.Editor editor, String data, String encryptionKey) throws StorageImportExportException {
|
||||
public int importPreferences(Preferences preferences, SharedPreferences.Editor editor, ImportElement dataset, String encryptionKey) throws StorageImportExportException {
|
||||
try {
|
||||
|
||||
String data = dataset.data.toString();
|
||||
List<Integer> accountNumbers = Account.getExistingAccountNumbers(preferences);
|
||||
Log.i(K9.LOG_TAG, "Existing accountNumbers = " + accountNumbers);
|
||||
Map<String, String> uuidMapping = new HashMap<String, String>();
|
||||
|
@ -83,4 +86,10 @@ public class StorageImporterVersion1 implements IStorageImporter {
|
|||
throw new StorageImportExportException("Unable to decrypt settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsKey()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
85
src/com/fsck/k9/preferences/StorageVersioning.java
Normal file
85
src/com/fsck/k9/preferences/StorageVersioning.java
Normal file
|
@ -0,0 +1,85 @@
|
|||
package com.fsck.k9.preferences;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class StorageVersioning
|
||||
{
|
||||
public enum STORAGE_VERSION {
|
||||
VERSION1(StorageImporterVersion1.class, StorageExporterVersion1.class, true, STORAGE_VERSION_1);
|
||||
|
||||
private Class<? extends IStorageImporter> importerClass;
|
||||
private Class<? extends IStorageExporter> exporterClass;
|
||||
private boolean needsKey;
|
||||
private String versionString;
|
||||
|
||||
|
||||
private STORAGE_VERSION(Class<? extends IStorageImporter> imclass, Class<? extends IStorageExporter> exclass, boolean nk, String vs) {
|
||||
importerClass = imclass;
|
||||
exporterClass = exclass;
|
||||
needsKey = nk;
|
||||
versionString = vs;
|
||||
|
||||
}
|
||||
public Class<? extends IStorageImporter> getImporterClass() {
|
||||
return importerClass;
|
||||
}
|
||||
public IStorageImporter createImporter() throws InstantiationException, IllegalAccessException {
|
||||
IStorageImporter storageImporter = importerClass.newInstance();
|
||||
return storageImporter;
|
||||
}
|
||||
public Class<? extends IStorageExporter> getExporterClass() {
|
||||
return exporterClass;
|
||||
}
|
||||
public IStorageExporter createExporter() throws InstantiationException, IllegalAccessException {
|
||||
IStorageExporter storageExporter = exporterClass.newInstance();
|
||||
return storageExporter;
|
||||
}
|
||||
public boolean needsKey() {
|
||||
return needsKey;
|
||||
}
|
||||
public String getVersionString() {
|
||||
return versionString;
|
||||
}
|
||||
}
|
||||
|
||||
// Never, ever re-use these numbers!
|
||||
private static final String STORAGE_VERSION_1 = "1";
|
||||
|
||||
public static Map<String, STORAGE_VERSION> versionMap = new HashMap<String, STORAGE_VERSION>();
|
||||
static {
|
||||
versionMap.put(STORAGE_VERSION.VERSION1.getVersionString(), STORAGE_VERSION.VERSION1);
|
||||
}
|
||||
|
||||
public static IStorageImporter createImporter(String version) throws InstantiationException, IllegalAccessException
|
||||
{
|
||||
STORAGE_VERSION storageVersion = versionMap.get(version);
|
||||
if (storageVersion == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return storageVersion.createImporter();
|
||||
}
|
||||
|
||||
public static IStorageExporter createExporter(String version) throws InstantiationException, IllegalAccessException
|
||||
{
|
||||
STORAGE_VERSION storageVersion = versionMap.get(version);
|
||||
if (storageVersion == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return storageVersion.createExporter();
|
||||
}
|
||||
|
||||
public Boolean needsKey(String version)
|
||||
{
|
||||
STORAGE_VERSION storageVersion = versionMap.get(version);
|
||||
if (storageVersion == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return storageVersion.needsKey();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue