From 0920b0c14d1f4f562a1fbd2b95d4adb4d5ccec09 Mon Sep 17 00:00:00 2001 From: cketti Date: Fri, 7 Oct 2011 20:29:03 +0200 Subject: [PATCH] Changed the way SettingsDescription is used Added ability to rewrite the string representation used internally to something "pretty" on export. Now only settings that have entries is GlobalSettings and AccountSettings are exported. This prevents export of newer settings that are left in the preference storage when downgrading. --- .../fsck/k9/preferences/AccountSettings.java | 395 ++++++++-------- .../fsck/k9/preferences/GlobalSettings.java | 299 ++++++------ src/com/fsck/k9/preferences/Settings.java | 428 +++++++++++++----- .../fsck/k9/preferences/StorageExporter.java | 28 +- 4 files changed, 677 insertions(+), 473 deletions(-) diff --git a/src/com/fsck/k9/preferences/AccountSettings.java b/src/com/fsck/k9/preferences/AccountSettings.java index 7e7c2c5c6..4666f3afa 100644 --- a/src/com/fsck/k9/preferences/AccountSettings.java +++ b/src/com/fsck/k9/preferences/AccountSettings.java @@ -1,18 +1,16 @@ package com.fsck.k9.preferences; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import android.content.SharedPreferences; -import android.net.Uri; -import android.util.Log; import com.fsck.k9.Account; import com.fsck.k9.K9; import com.fsck.k9.R; import com.fsck.k9.Account.FolderMode; import com.fsck.k9.Account.ScrollButtons; import com.fsck.k9.crypto.Apg; -import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.preferences.Settings.*; @@ -22,144 +20,73 @@ public class AccountSettings { static { SETTINGS = new LinkedHashMap(); - // mandatory - /* - SETTINGS.put("storeUri", - SD(SettingType.STRING, Settings.EXCEPTION_DEFAULT_VALUE, new StoreUriValidator())); - SETTINGS.put("transportUri", - SD(SettingType.STRING, Settings.EXCEPTION_DEFAULT_VALUE, - new TransportUriValidator())); - */ - - SETTINGS.put("archiveFolderName", - SD(SettingType.STRING, "Archive", null)); - SETTINGS.put("autoExpandFolderName", - SD(SettingType.STRING, "INBOX", null)); + SETTINGS.put("archiveFolderName", new StringSetting("Archive")); + SETTINGS.put("autoExpandFolderName", new StringSetting("INBOX")); SETTINGS.put("automaticCheckIntervalMinutes", - SD(SettingType.INTEGER, -1, new ResourceArrayValidator( - R.array.account_settings_check_frequency_values))); - SETTINGS.put("chipColor", - SD(SettingType.INTEGER, 0xff0000ff, Settings.SOLID_COLOR_VALIDATOR)); - SETTINGS.put("cryptoApp", - SD(SettingType.STRING, Apg.NAME, null)); - SETTINGS.put("cryptoAutoSignature", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("deletePolicy", - SD(SettingType.STRING, 0, new ResourceArrayValidator( - R.array.account_setup_delete_policy_values))); - SETTINGS.put("displayCount", - SD(SettingType.STRING, K9.DEFAULT_VISIBLE_LIMIT, new ResourceArrayValidator( - R.array.account_settings_display_count_values))); - SETTINGS.put("draftsFolderName", - SD(SettingType.STRING, "Drafts", null)); - SETTINGS.put("enableMoveButtons", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("expungePolicy", - SD(SettingType.STRING, Account.EXPUNGE_IMMEDIATELY, new ResourceArrayValidator( - R.array.account_setup_expunge_policy_values))); + new IntegerResourceSetting(-1, R.array.account_settings_check_frequency_values)); + SETTINGS.put("chipColor", new ColorSetting(0xFF0000FF)); + SETTINGS.put("cryptoApp", new StringSetting(Apg.NAME)); + SETTINGS.put("cryptoAutoSignature", new BooleanSetting(false)); + SETTINGS.put("deletePolicy", new DeletePolicySetting(Account.DELETE_POLICY_NEVER)); + SETTINGS.put("displayCount", new IntegerResourceSetting(K9.DEFAULT_VISIBLE_LIMIT, + R.array.account_settings_display_count_values)); + SETTINGS.put("draftsFolderName", new StringSetting("Drafts")); + SETTINGS.put("enableMoveButtons", new BooleanSetting(false)); + SETTINGS.put("expungePolicy", new StringResourceSetting(Account.EXPUNGE_IMMEDIATELY, + R.array.account_setup_expunge_policy_values)); SETTINGS.put("folderDisplayMode", - SD(SettingType.ENUM, FolderMode.NOT_SECOND_CLASS, new ResourceArrayValidator( - R.array.account_settings_folder_display_mode_values))); - SETTINGS.put("folderPushMode", - SD(SettingType.ENUM, FolderMode.FIRST_CLASS, new ResourceArrayValidator( - R.array.account_settings_folder_push_mode_values))); - SETTINGS.put("folderSyncMode", - SD(SettingType.ENUM, FolderMode.FIRST_CLASS, new ResourceArrayValidator( - R.array.folder_settings_folder_sync_mode_values))); + new EnumSetting(FolderMode.class, FolderMode.NOT_SECOND_CLASS)); + SETTINGS.put("folderPushMode", new EnumSetting(FolderMode.class, FolderMode.FIRST_CLASS)); + SETTINGS.put("folderSyncMode", new EnumSetting(FolderMode.class, FolderMode.FIRST_CLASS)); SETTINGS.put("folderTargetMode", - SD(SettingType.ENUM, FolderMode.NOT_SECOND_CLASS, new ResourceArrayValidator( - R.array.account_settings_folder_target_mode_values))); - SETTINGS.put("goToUnreadMessageSearch", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("hideButtonsEnum", - SD(SettingType.ENUM, ScrollButtons.NEVER, new ResourceArrayValidator( - R.array.account_settings_hide_buttons_values))); + new EnumSetting(FolderMode.class, FolderMode.NOT_SECOND_CLASS)); + SETTINGS.put("goToUnreadMessageSearch", new BooleanSetting(false)); + SETTINGS.put("hideButtonsEnum", new EnumSetting(ScrollButtons.class, ScrollButtons.NEVER)); SETTINGS.put("hideMoveButtonsEnum", - SD(SettingType.ENUM, ScrollButtons.NEVER, new ResourceArrayValidator( - R.array.account_settings_hide_move_buttons_values))); - SETTINGS.put("idleRefreshMinutes", - SD(SettingType.INTEGER, 24, new ResourceArrayValidator( - R.array.idle_refresh_period_values))); - SETTINGS.put("led", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("ledColor", - SD(SettingType.INTEGER, 0xff0000ff, Settings.SOLID_COLOR_VALIDATOR)); - SETTINGS.put("localStorageProvider", - SD(SettingType.STRING, new StorageProviderDefaultValue(), - new StorageProviderValidator())); - SETTINGS.put("maxPushFolders", - SD(SettingType.INTEGER, 10, Settings.POSITIVE_INTEGER_VALIDATOR)); - SETTINGS.put("maximumAutoDownloadMessageSize", - SD(SettingType.ENUM, 32768, new ResourceArrayValidator( - R.array.account_settings_autodownload_message_size_values))); - SETTINGS.put("maximumPolledMessageAge", - SD(SettingType.ENUM, -1, new ResourceArrayValidator( - R.array.account_settings_message_age_values))); + new EnumSetting(ScrollButtons.class, ScrollButtons.NEVER)); + SETTINGS.put("idleRefreshMinutes", new IntegerResourceSetting(24, + R.array.idle_refresh_period_values)); + SETTINGS.put("led", new BooleanSetting(true)); + SETTINGS.put("ledColor", new ColorSetting(0xFF0000FF)); + SETTINGS.put("localStorageProvider", new StorageProviderSetting()); + SETTINGS.put("maxPushFolders", new IntegerRangeSetting(0, 100, 10)); + SETTINGS.put("maximumAutoDownloadMessageSize", new IntegerResourceSetting(32768, + R.array.account_settings_autodownload_message_size_values)); + SETTINGS.put("maximumPolledMessageAge", new IntegerResourceSetting(-1, + R.array.account_settings_message_age_values)); SETTINGS.put("messageFormat", - SD(SettingType.ENUM, Account.DEFAULT_MESSAGE_FORMAT, new ResourceArrayValidator( - R.array.account_settings_message_format_values))); - SETTINGS.put("notificationUnreadCount", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("notifyMailCheck", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("notifyNewMail", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("notifySelfNewMail", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("pushPollOnConnect", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("quotePrefix", - SD(SettingType.STRING, Account.DEFAULT_QUOTE_PREFIX, null)); + new EnumSetting(Account.MessageFormat.class, Account.DEFAULT_MESSAGE_FORMAT)); + SETTINGS.put("notificationUnreadCount", new BooleanSetting(true)); + SETTINGS.put("notifyMailCheck", new BooleanSetting(false)); + SETTINGS.put("notifyNewMail", new BooleanSetting(false)); + SETTINGS.put("notifySelfNewMail", new BooleanSetting(true)); + SETTINGS.put("pushPollOnConnect", new BooleanSetting(true)); + SETTINGS.put("quotePrefix", new StringSetting(Account.DEFAULT_QUOTE_PREFIX)); SETTINGS.put("quoteStyle", - SD(SettingType.ENUM, Account.DEFAULT_QUOTE_STYLE, new ResourceArrayValidator( - R.array.account_settings_quote_style_values))); - SETTINGS.put("replyAfterQuote", - SD(SettingType.BOOLEAN, Account.DEFAULT_REPLY_AFTER_QUOTE, null)); - SETTINGS.put("ring", - SD(SettingType.BOOLEAN, true, null)); + new EnumSetting(Account.QuoteStyle.class, Account.DEFAULT_QUOTE_STYLE)); + SETTINGS.put("replyAfterQuote", new BooleanSetting(Account.DEFAULT_REPLY_AFTER_QUOTE)); + SETTINGS.put("ring", new BooleanSetting(true)); SETTINGS.put("ringtone", - SD(SettingType.STRING, "content://settings/system/notification_sound", - new RingtoneValidator())); - SETTINGS.put("saveAllHeaders", - SD(SettingType.BOOLEAN, true, null)); + new RingtoneSetting("content://settings/system/notification_sound")); + SETTINGS.put("saveAllHeaders", new BooleanSetting(true)); SETTINGS.put("searchableFolders", - SD(SettingType.ENUM, Account.Searchable.ALL, new ResourceArrayValidator( - R.array.account_settings_searchable_values))); - SETTINGS.put("sentFolderName", - SD(SettingType.STRING, "Sent", null)); + new EnumSetting(Account.Searchable.class, Account.Searchable.ALL)); + SETTINGS.put("sentFolderName", new StringSetting("Sent")); SETTINGS.put("showPicturesEnum", - SD(SettingType.ENUM, Account.ShowPictures.NEVER, new ResourceArrayValidator( - R.array.account_settings_show_pictures_values))); - SETTINGS.put("signatureBeforeQuotedText", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("spamFolderName", - SD(SettingType.STRING, "Spam", null)); - SETTINGS.put("subscribedFoldersOnly", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("syncRemoteDeletions", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("trashFolderName", - SD(SettingType.STRING, "Trash", null)); - SETTINGS.put("useCompression.MOBILE", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("useCompression.OTHER", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("useCompression.WIFI", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("vibrate", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("vibratePattern", - SD(SettingType.INTEGER, 0, new ResourceArrayValidator( - R.array.account_settings_vibrate_pattern_values))); - SETTINGS.put("vibrateTimes", - SD(SettingType.INTEGER, 5, new ResourceArrayValidator( - R.array.account_settings_vibrate_times_label))); - } - - // Just to have shorter lines in SETTINGS initialization - private static SettingsDescription SD(SettingType type, - Object defaultValue, ISettingValidator validator) { - return new SettingsDescription(type, defaultValue, validator); + new EnumSetting(Account.ShowPictures.class, Account.ShowPictures.NEVER)); + SETTINGS.put("signatureBeforeQuotedText", new BooleanSetting(false)); + SETTINGS.put("spamFolderName", new StringSetting("Spam")); + SETTINGS.put("subscribedFoldersOnly", new BooleanSetting(false)); + SETTINGS.put("syncRemoteDeletions", new BooleanSetting(true)); + SETTINGS.put("trashFolderName", new StringSetting("Trash")); + SETTINGS.put("useCompression.MOBILE", new BooleanSetting(true)); + SETTINGS.put("useCompression.OTHER", new BooleanSetting(true)); + SETTINGS.put("useCompression.WIFI", new BooleanSetting(true)); + SETTINGS.put("vibrate", new BooleanSetting(false)); + SETTINGS.put("vibratePattern", new IntegerResourceSetting(0, + R.array.account_settings_vibrate_pattern_values)); + SETTINGS.put("vibrateTimes", new IntegerResourceSetting(5, + R.array.account_settings_vibrate_times_label)); } public static Map validate(Map importedSettings, @@ -179,98 +106,148 @@ public class AccountSettings { return result; } + /** + * An integer resource setting. + * + *

+ * Basically a {@link PseudoEnumSetting} that is initialized from a resource array containing + * integer strings. + *

+ */ + public static class IntegerResourceSetting extends PseudoEnumSetting { + private final Map mMapping; + + public IntegerResourceSetting(int defaultValue, int resId) { + super(defaultValue); + + Map mapping = new HashMap(); + String[] values = K9.app.getResources().getStringArray(resId); + for (String value : values) { + int intValue = Integer.parseInt(value); + mapping.put(intValue, value); + } + mMapping = Collections.unmodifiableMap(mapping); + } - public static class StorageProviderDefaultValue implements IDefaultValue { @Override - public Object computeDefaultValue(String key, Map validatedSettings) { + protected Map getMapping() { + return mMapping; + } + + @Override + public Object fromString(String value) throws InvalidSettingValueException { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new InvalidSettingValueException(); + } + } + } + + /** + * A string resource setting. + * + *

+ * Basically a {@link PseudoEnumSetting} that is initialized from a resource array. + *

+ */ + public static class StringResourceSetting extends PseudoEnumSetting { + private final Map mMapping; + + public StringResourceSetting(String defaultValue, int resId) { + super(defaultValue); + + Map mapping = new HashMap(); + String[] values = K9.app.getResources().getStringArray(resId); + for (String value : values) { + mapping.put(value, value); + } + mMapping = Collections.unmodifiableMap(mapping); + } + + @Override + protected Map getMapping() { + return mMapping; + } + + @Override + public Object fromString(String value) throws InvalidSettingValueException { + if (!mMapping.containsKey(value)) { + throw new InvalidSettingValueException(); + } + return value; + } + } + + /** + * The notification ringtone setting. + */ + public static class RingtoneSetting extends SettingsDescription { + public RingtoneSetting(String defaultValue) { + super(defaultValue); + } + + @Override + public Object fromString(String value) { + //TODO: add validation + return value; + } + } + + /** + * The storage provider setting. + */ + public static class StorageProviderSetting extends SettingsDescription { + public StorageProviderSetting() { + super(null); + } + + @Override + public Object getDefaultValue() { return StorageManager.getInstance(K9.app).getDefaultProviderId(); } - } - - public static class StorageProviderValidator implements ISettingValidator { @Override - public boolean isValid(String key, String value, Map validatedSettings) { - Map providers = StorageManager.getInstance(K9.app).getAvailableProviders(); - for (String storageProvider : providers.keySet()) { - if (storageProvider.equals(value)) { - return true; - } + public Object fromString(String value) { + StorageManager storageManager = StorageManager.getInstance(K9.app); + Map providers = storageManager.getAvailableProviders(); + if (providers.containsKey(value)) { + return value; } - return false; + throw new RuntimeException("Validation failed"); } } - public static class RingtoneValidator implements ISettingValidator { - @Override - public boolean isValid(String key, String value, Map validatedSettings) { - // TODO implement - return true; - } - } + /** + * The delete policy setting. + */ + public static class DeletePolicySetting extends PseudoEnumSetting { + private Map mMapping; + + public DeletePolicySetting(int defaultValue) { + super(defaultValue); + Map mapping = new HashMap(); + mapping.put(Account.DELETE_POLICY_NEVER, "NEVER"); + mapping.put(Account.DELETE_POLICY_ON_DELETE, "DELETE"); + mapping.put(Account.DELETE_POLICY_MARK_AS_READ, "MARK_AS_READ"); + mMapping = Collections.unmodifiableMap(mapping); + } - public static class StoreUriValidator implements ISettingValidator { @Override - public boolean isValid(String key, String value, Map validatedSettings) { + protected Map getMapping() { + return mMapping; + } + + @Override + public Object fromString(String value) throws InvalidSettingValueException { try { - String uriString = Utility.base64Decode(value); - if (!uriString.startsWith("imap") && !uriString.startsWith("pop3") && - !uriString.startsWith("webdav")) { - return false; + Integer deletePolicy = Integer.parseInt(value); + if (mMapping.containsKey(deletePolicy)) { + return deletePolicy; } + } catch (NumberFormatException e) { /* do nothing */ } - //TODO: check complete scheme (imap+ssl etc.) - - Uri uri = Uri.parse(uriString); - String[] userInfoParts = uri.getUserInfo().split(":"); - if (userInfoParts.length < 2) { - return false; - } - //TODO: check if username and password are urlencoded - - String host = uri.getHost(); - if (host == null || host.length() == 0) { - return false; - } - - //TODO: check store specifics - - return true; - } catch (Exception e) { Log.e(K9.LOG_TAG, "oops", e); /* Ignore */ } - - return false; - } - } - - public static class TransportUriValidator implements ISettingValidator { - @Override - public boolean isValid(String key, String value, Map validatedSettings) { - try { - String uriString = Utility.base64Decode(value); - if (!uriString.startsWith("smtp") && !uriString.startsWith("webdav")) { - return false; - } - - //TODO: check complete scheme (smtp+ssl etc.) - - Uri uri = Uri.parse(uriString); - String[] userInfoParts = uri.getUserInfo().split(":"); - if (userInfoParts.length < 2) { - return false; - } - //TODO: check if username and password are urlencoded - - String host = uri.getHost(); - if (host == null || host.length() == 0) { - return false; - } - - //TODO: check store specifics - - return true; - } catch (Exception e) { Log.e(K9.LOG_TAG, "oops", e); /* Ignore */ } - - return false; + throw new InvalidSettingValueException(); } } } diff --git a/src/com/fsck/k9/preferences/GlobalSettings.java b/src/com/fsck/k9/preferences/GlobalSettings.java index 5c2291b24..e146bd450 100644 --- a/src/com/fsck/k9/preferences/GlobalSettings.java +++ b/src/com/fsck/k9/preferences/GlobalSettings.java @@ -1,6 +1,7 @@ package com.fsck.k9.preferences; import java.text.SimpleDateFormat; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -12,122 +13,63 @@ import com.fsck.k9.helper.DateFormatter; import com.fsck.k9.preferences.Settings.*; public class GlobalSettings { - public static final ISettingValidator FONT_SIZE_VALIDATOR = new DipFontSizeValidator(); - public static final ISettingValidator TIME_VALIDATOR = new TimeValidator(); - public static final Map SETTINGS; static { SETTINGS = new LinkedHashMap(); - SETTINGS.put("animations", - SD(SettingType.BOOLEAN, false, null)); + SETTINGS.put("animations", new BooleanSetting(false)); SETTINGS.put("backgroundOperations", - SD(SettingType.ENUM, K9.BACKGROUND_OPS.WHEN_CHECKED, new ResourceArrayValidator( - R.array.background_ops_values))); - SETTINGS.put("changeRegisteredNameColor", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("compactLayouts", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("confirmDelete", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("countSearchMessages", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("dateFormat", - SD(SettingType.ENUM, DateFormatter.DEFAULT_FORMAT, new DateFormatValidator())); - SETTINGS.put("enableDebugLogging", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("enableSensitiveLogging", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("fontSizeAccountDescription", - SD(SettingType.INTEGER, 14, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeAccountName", - SD(SettingType.INTEGER, 18, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeFolderName", - SD(SettingType.INTEGER, 22, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeFolderStatus", - SD(SettingType.INTEGER, 14, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeMessageListDate", - SD(SettingType.INTEGER, 14, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeMessageListPreview", - SD(SettingType.INTEGER, 14, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeMessageListSender", - SD(SettingType.INTEGER, 14, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeMessageListSubject", - SD(SettingType.INTEGER, 16, FONT_SIZE_VALIDATOR)); + new EnumSetting(K9.BACKGROUND_OPS.class, K9.BACKGROUND_OPS.WHEN_CHECKED)); + SETTINGS.put("changeRegisteredNameColor", new BooleanSetting(false)); + SETTINGS.put("compactLayouts", new BooleanSetting(false)); + SETTINGS.put("confirmDelete", new BooleanSetting(false)); + SETTINGS.put("countSearchMessages", new BooleanSetting(false)); + SETTINGS.put("dateFormat", new DateFormatSetting(DateFormatter.DEFAULT_FORMAT)); + SETTINGS.put("enableDebugLogging", new BooleanSetting(false)); + SETTINGS.put("enableSensitiveLogging", new BooleanSetting(false)); + SETTINGS.put("fontSizeAccountDescription", new FontSizeSetting(FontSizes.SMALL)); + SETTINGS.put("fontSizeAccountName", new FontSizeSetting(FontSizes.MEDIUM)); + SETTINGS.put("fontSizeFolderName", new FontSizeSetting(FontSizes.LARGE)); + SETTINGS.put("fontSizeFolderStatus", new FontSizeSetting(FontSizes.SMALL)); + SETTINGS.put("fontSizeMessageListDate", new FontSizeSetting(FontSizes.SMALL)); + SETTINGS.put("fontSizeMessageListPreview", new FontSizeSetting(FontSizes.SMALL)); + SETTINGS.put("fontSizeMessageListSender", new FontSizeSetting(FontSizes.SMALL)); + SETTINGS.put("fontSizeMessageListSubject", new FontSizeSetting(FontSizes.FONT_16DIP)); SETTINGS.put("fontSizeMessageViewAdditionalHeaders", - SD(SettingType.INTEGER, 12, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeMessageViewCC", - SD(SettingType.INTEGER, 12, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeMessageViewContent", - SD(SettingType.INTEGER, 3, new WebViewFontSizeValidator())); - SETTINGS.put("fontSizeMessageViewDate", - SD(SettingType.INTEGER, 10, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeMessageViewSender", - SD(SettingType.INTEGER, 14, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeMessageViewSubject", - SD(SettingType.INTEGER, 12, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeMessageViewTime", - SD(SettingType.INTEGER, 10, FONT_SIZE_VALIDATOR)); - SETTINGS.put("fontSizeMessageViewTo", - SD(SettingType.INTEGER, 12, FONT_SIZE_VALIDATOR)); - SETTINGS.put("gesturesEnabled", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("hideSpecialAccounts", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("keyguardPrivacy", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("language", - SD(SettingType.STRING, "", new ResourceArrayValidator( - R.array.settings_language_values))); - SETTINGS.put("manageBack", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("measureAccounts", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("messageListCheckboxes", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("messageListPreviewLines", - SD(SettingType.INTEGER, 2, Settings.POSITIVE_INTEGER_VALIDATOR)); - SETTINGS.put("messageListStars", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("messageListTouchable", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("messageViewFixedWidthFont", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("messageViewReturnToList", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("mobileOptimizedLayout", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("quietTimeEnabled", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("quietTimeEnds", - SD(SettingType.STRING, "7:00", TIME_VALIDATOR)); - SETTINGS.put("quietTimeStarts", - SD(SettingType.STRING, "21:00", TIME_VALIDATOR)); - SETTINGS.put("registeredNameColor", - SD(SettingType.INTEGER, 0xFF00008F, Settings.SOLID_COLOR_VALIDATOR)); - SETTINGS.put("showContactName", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("showCorrespondentNames", - SD(SettingType.BOOLEAN, true, null)); - SETTINGS.put("startIntegratedInbox", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("theme", - SD(SettingType.INTEGER, android.R.style.Theme_Light, new ThemeValidator())); - SETTINGS.put("useGalleryBugWorkaround", - SD(SettingType.BOOLEAN, new GalleryBugWorkaroundDefaultValue(), null)); - SETTINGS.put("useVolumeKeysForListNavigation", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("useVolumeKeysForNavigation", - SD(SettingType.BOOLEAN, false, null)); - SETTINGS.put("zoomControlsEnabled", - SD(SettingType.BOOLEAN, false, null)); - } - - // Just to have shorter lines in SETTINGS initialization - private static SettingsDescription SD(SettingType type, - Object defaultValue, ISettingValidator validator) { - return new SettingsDescription(type, defaultValue, validator); + new FontSizeSetting(FontSizes.FONT_12DIP)); + SETTINGS.put("fontSizeMessageViewCC", new FontSizeSetting(FontSizes.FONT_12DIP)); + SETTINGS.put("fontSizeMessageViewContent", new WebFontSizeSetting(3)); + SETTINGS.put("fontSizeMessageViewDate", new FontSizeSetting(FontSizes.FONT_10DIP)); + SETTINGS.put("fontSizeMessageViewSender", new FontSizeSetting(FontSizes.SMALL)); + SETTINGS.put("fontSizeMessageViewSubject", new FontSizeSetting(FontSizes.FONT_12DIP)); + SETTINGS.put("fontSizeMessageViewTime", new FontSizeSetting(FontSizes.FONT_10DIP)); + SETTINGS.put("fontSizeMessageViewTo", new FontSizeSetting(FontSizes.FONT_12DIP)); + SETTINGS.put("gesturesEnabled", new BooleanSetting(true)); + SETTINGS.put("hideSpecialAccounts", new BooleanSetting(false)); + SETTINGS.put("keyguardPrivacy", new BooleanSetting(false)); + SETTINGS.put("language", new LanguageSetting()); + SETTINGS.put("manageBack", new BooleanSetting(false)); + SETTINGS.put("measureAccounts", new BooleanSetting(true)); + SETTINGS.put("messageListCheckboxes", new BooleanSetting(false)); + SETTINGS.put("messageListPreviewLines", new IntegerRangeSetting(1, 100, 2)); + SETTINGS.put("messageListStars", new BooleanSetting(true)); + SETTINGS.put("messageListTouchable", new BooleanSetting(false)); + SETTINGS.put("messageViewFixedWidthFont", new BooleanSetting(false)); + SETTINGS.put("messageViewReturnToList", new BooleanSetting(false)); + SETTINGS.put("mobileOptimizedLayout", new BooleanSetting(false)); + SETTINGS.put("quietTimeEnabled", new BooleanSetting(false)); + SETTINGS.put("quietTimeEnds", new TimeSetting("7:00")); + SETTINGS.put("quietTimeStarts", new TimeSetting("21:00")); + SETTINGS.put("registeredNameColor", new ColorSetting(0xFF00008F)); + SETTINGS.put("showContactName", new BooleanSetting(false)); + SETTINGS.put("showCorrespondentNames", new BooleanSetting(true)); + SETTINGS.put("startIntegratedInbox", new BooleanSetting(false)); + SETTINGS.put("theme", new ThemeSetting(android.R.style.Theme_Light)); + SETTINGS.put("useGalleryBugWorkaround", new GalleryBugWorkaroundSetting()); + SETTINGS.put("useVolumeKeysForListNavigation", new BooleanSetting(false)); + SETTINGS.put("useVolumeKeysForNavigation", new BooleanSetting(false)); + SETTINGS.put("zoomControlsEnabled", new BooleanSetting(false)); } public static Map validate(Map importedSettings) { @@ -145,72 +87,141 @@ public class GlobalSettings { return result; } + /** + * The gallery bug work-around setting. + * + *

+ * The default value varies depending on whether you have a version of Gallery 3D installed + * that contains the bug we work around. + *

+ * + * @see K9#isGalleryBuggy() + */ + public static class GalleryBugWorkaroundSetting extends BooleanSetting { + public GalleryBugWorkaroundSetting() { + super(false); + } - public static class GalleryBugWorkaroundDefaultValue implements IDefaultValue { @Override - public Object computeDefaultValue(String key, Map validatedSettings) { + public Object getDefaultValue() { return K9.isGalleryBuggy(); } } - public static class DipFontSizeValidator implements ISettingValidator { - @Override - public boolean isValid(String key, String value, Map validatedSettings) { - int val = Integer.parseInt(value); - switch (val) { - case FontSizes.FONT_10DIP: - case FontSizes.FONT_12DIP: - case FontSizes.SMALL: - case FontSizes.FONT_16DIP: - case FontSizes.MEDIUM: - case FontSizes.FONT_20DIP: - case FontSizes.LARGE: - return true; - default: - return false; + /** + * The language setting. + * + *

+ * Valid values are read from {@code settings_language_values} in + * {@code res/values/arrays.xml}. + *

+ */ + public static class LanguageSetting extends PseudoEnumSetting { + private final Map mMapping; + + public LanguageSetting() { + super(""); + + Map mapping = new HashMap(); + String[] values = K9.app.getResources().getStringArray(R.array.settings_language_values); + for (String value : values) { + if (value.length() == 0) { + mapping.put("", "default"); + } else { + mapping.put(value, value); + } } + mMapping = Collections.unmodifiableMap(mapping); + } + + @Override + protected Map getMapping() { + return mMapping; + } + + @Override + public Object fromString(String value) throws InvalidSettingValueException { + if (mMapping.containsKey(value)) { + return value; + } + + throw new InvalidSettingValueException(); } } - public static class WebViewFontSizeValidator implements ISettingValidator { + /** + * The theme setting. + */ + public static class ThemeSetting extends PseudoEnumSetting { + private final Map mMapping; + + public ThemeSetting(int defaultValue) { + super(defaultValue); + + Map mapping = new HashMap(); + mapping.put(android.R.style.Theme_Light, "light"); + mapping.put(android.R.style.Theme, "dark"); + mMapping = Collections.unmodifiableMap(mapping); + } + @Override - public boolean isValid(String key, String value, Map validatedSettings) { - int val = Integer.parseInt(value); - return (val >= 1 && val <= 5); + protected Map getMapping() { + return mMapping; + } + + @Override + public Object fromString(String value) throws InvalidSettingValueException { + try { + Integer theme = Integer.parseInt(value); + if (mMapping.containsKey(theme)) { + return theme; + } + } catch (NumberFormatException e) { /* do nothing */ } + + throw new InvalidSettingValueException(); } } - public static class TimeValidator implements ISettingValidator { - @Override - public boolean isValid(String key, String value, Map validatedSettings) { - return value.matches(TimePickerPreference.VALIDATION_EXPRESSION); + /** + * A date format setting. + */ + public static class DateFormatSetting extends SettingsDescription { + public DateFormatSetting(String defaultValue) { + super(defaultValue); } - } - public static class DateFormatValidator implements ISettingValidator { @Override - public boolean isValid(String key, String value, Map validatedSettings) { + public Object fromString(String value) throws InvalidSettingValueException { try { // The placeholders "SHORT" and "MEDIUM" are fine. if (DateFormatter.SHORT_FORMAT.equals(value) || DateFormatter.MEDIUM_FORMAT.equals(value)) { - return true; + return value; } // If the SimpleDateFormat constructor doesn't throw an exception, we're good. new SimpleDateFormat(value); - return true; + return value; } catch (Exception e) { - return false; + throw new InvalidSettingValueException(); } } } - public static class ThemeValidator implements ISettingValidator { + /** + * A time setting. + */ + public static class TimeSetting extends SettingsDescription { + public TimeSetting(String defaultValue) { + super(defaultValue); + } + @Override - public boolean isValid(String key, String value, Map validatedSettings) { - int val = Integer.parseInt(value); - return (val == android.R.style.Theme_Light || val == android.R.style.Theme); + public Object fromString(String value) throws InvalidSettingValueException { + if (!value.matches(TimePickerPreference.VALIDATION_EXPRESSION)) { + throw new InvalidSettingValueException(); + } + return value; } } } diff --git a/src/com/fsck/k9/preferences/Settings.java b/src/com/fsck/k9/preferences/Settings.java index 98a97717d..fca782242 100644 --- a/src/com/fsck/k9/preferences/Settings.java +++ b/src/com/fsck/k9/preferences/Settings.java @@ -1,8 +1,13 @@ package com.fsck.k9.preferences; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; + import android.util.Log; + +import com.fsck.k9.FontSizes; import com.fsck.k9.K9; /* @@ -10,10 +15,6 @@ import com.fsck.k9.K9; * - add support for different settings versions (validate old version and upgrade to new format) * - use the default values defined in GlobalSettings and AccountSettings when creating new * accounts - * - use the settings description to decide which keys to export - * - convert internal representation to a "pretty" format when exporting (e.g. we want to export - * the value "light" rather than the integer constant for android.R.style.Theme_Light); revert - * that conversion when importing * - think of a better way to validate enums than to use the resource arrays (i.e. get rid of * ResourceArrayValidator); maybe even use the settings description for the settings UI * - add unit test that validates the default values are actually valid according to the validator @@ -33,13 +34,6 @@ public class Settings { */ public static final int VERSION = 1; - public static final IDefaultValue EXCEPTION_DEFAULT_VALUE = new ExceptionDefaultValue(); - - public static final ISettingValidator BOOLEAN_VALIDATOR = new BooleanValidator(); - public static final ISettingValidator INTEGER_VALIDATOR = new IntegerValidator(); - public static final ISettingValidator POSITIVE_INTEGER_VALIDATOR = new PositiveIntegerValidator(); - public static final ISettingValidator SOLID_COLOR_VALIDATOR = new SolidColorValidator(); - public static Map validate(Map settings, Map importedSettings, boolean useDefaultValues) { @@ -54,12 +48,14 @@ public class Settings { ((useDefaultValues) ? " Using default value." : "")); useDefaultValue = useDefaultValues; } else { - String importedValue = importedSettings.get(key); - if (Settings.isValid(desc, key, importedValue, validatedSettings)) { + String prettyValue = importedSettings.get(key); + try { + Object internalValue = desc.fromPrettyString(prettyValue); + String importedValue = desc.toString(internalValue); validatedSettings.put(key, importedValue); useDefaultValue = false; - } else { - Log.v(K9.LOG_TAG, "Key \"" + key + "\" has invalid value \"" + importedValue + + } catch (InvalidSettingValueException e) { + Log.v(K9.LOG_TAG, "Key \"" + key + "\" has invalid value \"" + prettyValue + "\" in imported file. " + ((useDefaultValues) ? "Using default value." : "Skipping.")); useDefaultValue = useDefaultValues; @@ -67,142 +63,346 @@ public class Settings { } if (useDefaultValue) { - Object defaultValue; - if (desc.defaultValue instanceof IDefaultValue) { - defaultValue = ((IDefaultValue)desc.defaultValue).computeDefaultValue(key, validatedSettings); - } else { - defaultValue = desc.defaultValue; - } - - validatedSettings.put(key, defaultValue.toString()); + Object defaultValue = desc.getDefaultValue(); + validatedSettings.put(key, desc.toString(defaultValue)); } } return validatedSettings; } - public static boolean isValid(SettingsDescription desc, String key, String value, - Map validatedSettings) { - try { - switch (desc.type) { - case BOOLEAN: - if (!Settings.BOOLEAN_VALIDATOR.isValid(key, value, validatedSettings)) { - return false; - } - break; - case INTEGER: - if (!Settings.INTEGER_VALIDATOR.isValid(key, value, validatedSettings)) { - return false; - } - break; - default: - break; - } - if (desc.validator != null) { - return desc.validator.isValid(key, value, validatedSettings); - } + /** + * Indicates an invalid setting value. + * + * @see SettingsDescription#fromString(String) + * @see SettingsDescription#fromPrettyString(String) + */ + public static class InvalidSettingValueException extends Exception { + private static final long serialVersionUID = 1L; + } - return true; - } catch (Exception e) { - Log.e(K9.LOG_TAG, "Exception while running validator for value \"" + value + "\"", e); - return false; + /** + * Describes a setting. + * + *

+ * Instances of this class are used to convert the string representations of setting values to + * an internal representation (e.g. an integer) and back. + *

+ * Currently we use two different string representations: + *

+ *
    + *
  1. + * The one that is used by the internal preference {@link Storage}. It is usually obtained by + * calling {@code toString()} on the internal representation of the setting value (see e.g. + * {@link K9#save(android.content.SharedPreferences.Editor)}). + *
  2. + *
  3. + * The "pretty" version that is used by the import/export settings file (e.g. colors are + * exported in #rrggbb format instead of a integer string like "-8734021"). + *
  4. + *
+ *

+ * Note: + * For the future we should aim to get rid of the "internal" string representation. The + * "pretty" version makes reading a database dump easier and the performance impact should be + * negligible. + *

+ */ + public static abstract class SettingsDescription { + /** + * The setting's default value (internal representation). + */ + protected Object mDefaultValue; + + public SettingsDescription(Object defaultValue) { + mDefaultValue = defaultValue; + } + + /** + * Get the default value. + * + * @return The internal representation of the default value. + */ + public Object getDefaultValue() { + return mDefaultValue; + } + + /** + * Convert a setting's value to the string representation. + * + * @param value + * The internal representation of a setting's value. + * + * @return The string representation of {@code value}. + */ + public String toString(Object value) { + return value.toString(); + } + + /** + * Parse the string representation of a setting's value . + * + * @param value + * The string representation of a setting's value. + * + * @return The internal representation of the setting's value. + * + * @throws InvalidSettingValueException + * If {@code value} contains an invalid value. + */ + public abstract Object fromString(String value) throws InvalidSettingValueException; + + /** + * Convert a setting value to the "pretty" string representation. + * + * @param value + * The setting's value. + * + * @return A pretty-printed version of the setting's value. + */ + public String toPrettyString(Object value) { + return toString(value); + } + + /** + * Convert the pretty-printed version of a setting's value to the internal representation. + * + * @param value + * The pretty-printed version of the setting's value. See + * {@link #toPrettyString(Object)}. + * + * @return The internal representation of the setting's value. + * + * @throws InvalidSettingValueException + * If {@code value} contains an invalid value. + */ + public Object fromPrettyString(String value) throws InvalidSettingValueException { + return fromString(value); } } - public enum SettingType { - BOOLEAN, - INTEGER, - STRING, - ENUM - } - - public static class SettingsDescription { - public final SettingType type; - public final Object defaultValue; - public final ISettingValidator validator; - - protected SettingsDescription(SettingType type, - Object defaultValue, ISettingValidator validator) { - this.type = type; - this.defaultValue = defaultValue; - this.validator = validator; + /** + * A string setting. + */ + public static class StringSetting extends SettingsDescription { + public StringSetting(String defaultValue) { + super(defaultValue); } - } - public interface IDefaultValue { - Object computeDefaultValue(String key, Map validatedSettings); - } - - public static class ExceptionDefaultValue implements IDefaultValue { @Override - public Object computeDefaultValue(String key, Map validatedSettings) { - throw new RuntimeException("There is no default value for key \"" + key + "\"."); - } - - } - - public interface ISettingValidator { - boolean isValid(String key, String value, Map validatedSettings); - } - - public static class BooleanValidator implements ISettingValidator { - @Override - public boolean isValid(String key, String value, Map validatedSettings) { - return Boolean.TRUE.toString().equals(value) || Boolean.FALSE.toString().equals(value); + public Object fromString(String value) { + return value; } } - public static class IntegerValidator implements ISettingValidator { + /** + * A boolean setting. + */ + public static class BooleanSetting extends SettingsDescription { + public BooleanSetting(boolean defaultValue) { + super(defaultValue); + } + @Override - public boolean isValid(String key, String value, Map validatedSettings) { - try { - Integer.parseInt(value); + public Object fromString(String value) throws InvalidSettingValueException { + if (Boolean.TRUE.toString().equals(value)) { return true; - } catch (NumberFormatException e) { + } else if (Boolean.FALSE.toString().equals(value)) { return false; } + throw new InvalidSettingValueException(); } } - public static class PositiveIntegerValidator implements ISettingValidator { - @Override - public boolean isValid(String key, String value, Map validatedSettings) { - return (Integer.parseInt(value) >= 0); - } - } - - public static class ResourceArrayValidator implements ISettingValidator { - private final int mResource; - - public ResourceArrayValidator(int res) { - mResource = res; + /** + * A color setting. + */ + public static class ColorSetting extends SettingsDescription { + public ColorSetting(int defaultValue) { + super(defaultValue); } @Override - public boolean isValid(String key, String value, Map validatedSettings) { + public Object fromString(String value) throws InvalidSettingValueException { try { - String[] values = K9.app.getResources().getStringArray(mResource); + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new InvalidSettingValueException(); + } + } - for (String validValue : values) { - if (validValue.equals(value)) { - return true; - } + @Override + public String toPrettyString(Object value) { + int color = ((Integer) value) & 0x00FFFFFF; + return String.format("#%06x", color); + } + + @Override + public Object fromPrettyString(String value) throws InvalidSettingValueException { + try { + if (value.length() == 7) { + return Integer.parseInt(value.substring(1), 16) | 0xFF000000; } + } catch (NumberFormatException e) { /* do nothing */ } + + throw new InvalidSettingValueException(); + } + } + + /** + * An {@code Enum} setting. + * + *

+ * {@link Enum#toString()} is used to obtain the "pretty" string representation. + *

+ */ + public static class EnumSetting extends SettingsDescription { + private Class> mEnumClass; + + public EnumSetting(Class> enumClass, Object defaultValue) { + super(defaultValue); + mEnumClass = enumClass; + } + + @SuppressWarnings("unchecked") + @Override + public Object fromString(String value) throws InvalidSettingValueException { + try { + return Enum.valueOf((Class)mEnumClass, value); } catch (Exception e) { - Log.e(K9.LOG_TAG, "Something went wrong during validation of key " + key, e); + throw new InvalidSettingValueException(); + } + } + } + + /** + * A setting that has multiple valid values but doesn't use an {@link Enum} internally. + * + * @param + * The type of the internal representation (e.g. {@code Integer}). + */ + public abstract static class PseudoEnumSetting extends SettingsDescription { + public PseudoEnumSetting(Object defaultValue) { + super(defaultValue); + } + + protected abstract Map getMapping(); + + @Override + public String toPrettyString(Object value) { + return getMapping().get(value); + } + + @Override + public Object fromPrettyString(String value) throws InvalidSettingValueException { + for (Entry entry : getMapping().entrySet()) { + if (entry.getValue().equals(value)) { + return entry.getKey(); + } } - return false; + throw new InvalidSettingValueException(); } } - public static class SolidColorValidator implements ISettingValidator { - @Override - public boolean isValid(String key, String value, Map validatedSettings) { - int color = Integer.parseInt(value); - return ((color & 0xFF000000) == 0xFF000000); + /** + * A font size setting. + */ + public static class FontSizeSetting extends PseudoEnumSetting { + private final Map mMapping; + + public FontSizeSetting(int defaultValue) { + super(defaultValue); + + Map mapping = new HashMap(); + mapping.put(FontSizes.FONT_10DIP, "tiniest"); + mapping.put(FontSizes.FONT_12DIP, "tiny"); + mapping.put(FontSizes.SMALL, "smaller"); + mapping.put(FontSizes.FONT_16DIP, "small"); + mapping.put(FontSizes.MEDIUM, "medium"); + mapping.put(FontSizes.FONT_20DIP, "large"); + mapping.put(FontSizes.LARGE, "larger"); + mMapping = Collections.unmodifiableMap(mapping); } + @Override + protected Map getMapping() { + return mMapping; + } + + @Override + public Object fromString(String value) throws InvalidSettingValueException { + try { + Integer fontSize = Integer.parseInt(value); + if (mMapping.containsKey(fontSize)) { + return fontSize; + } + } catch (NumberFormatException e) { /* do nothing */ } + + throw new InvalidSettingValueException(); + } + } + + /** + * A {@link android.webkit.WebView} font size setting. + */ + public static class WebFontSizeSetting extends PseudoEnumSetting { + private final Map mMapping; + + public WebFontSizeSetting(int defaultValue) { + super(defaultValue); + + Map mapping = new HashMap(); + mapping.put(1, "smallest"); + mapping.put(2, "smaller"); + mapping.put(3, "normal"); + mapping.put(4, "larger"); + mapping.put(5, "largest"); + mMapping = Collections.unmodifiableMap(mapping); + } + + @Override + protected Map getMapping() { + return mMapping; + } + + @Override + public Object fromString(String value) throws InvalidSettingValueException { + try { + Integer fontSize = Integer.parseInt(value); + if (mMapping.containsKey(fontSize)) { + return fontSize; + } + } catch (NumberFormatException e) { /* do nothing */ } + + throw new InvalidSettingValueException(); + } + } + + /** + * An integer settings whose values a limited to a certain range. + */ + public static class IntegerRangeSetting extends SettingsDescription { + private int mStart; + private int mEnd; + + public IntegerRangeSetting(int start, int end, int defaultValue) { + super(defaultValue); + mStart = start; + mEnd = end; + } + + @Override + public Object fromString(String value) throws InvalidSettingValueException { + try { + int intValue = Integer.parseInt(value); + if (mStart <= intValue && intValue <= mEnd) { + return intValue; + } + } catch (NumberFormatException e) { /* do nothing */ } + + throw new InvalidSettingValueException(); + } } } diff --git a/src/com/fsck/k9/preferences/StorageExporter.java b/src/com/fsck/k9/preferences/StorageExporter.java index ba98559cd..4bccb544e 100644 --- a/src/com/fsck/k9/preferences/StorageExporter.java +++ b/src/com/fsck/k9/preferences/StorageExporter.java @@ -29,6 +29,8 @@ import com.fsck.k9.mail.Store; import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.store.LocalStore; +import com.fsck.k9.preferences.Settings.InvalidSettingValueException; +import com.fsck.k9.preferences.Settings.SettingsDescription; public class StorageExporter { @@ -183,11 +185,16 @@ public class StorageExporter { Map prefs) throws IOException { for (String key : GlobalSettings.SETTINGS.keySet()) { - Object value = prefs.get(key); + String valueString = prefs.get(key).toString(); - if (value != null) { - String outputValue = value.toString(); + try { + SettingsDescription setting = GlobalSettings.SETTINGS.get(key); + Object value = setting.fromString(valueString); + String outputValue = setting.toPrettyString(value); writeKeyValue(serializer, key, outputValue); + } catch (InvalidSettingValueException e) { + Log.w(K9.LOG_TAG, "Global setting \"" + key + "\" has invalid value \"" + + valueString + "\" in preference storage. This shouldn't happen!"); } } } @@ -268,7 +275,7 @@ public class StorageExporter { serializer.startTag(null, SETTINGS_ELEMENT); for (Map.Entry entry : prefs.entrySet()) { String key = entry.getKey(); - String value = entry.getValue().toString(); + String valueString = entry.getValue().toString(); String[] comps = key.split("\\."); if (comps.length < 2) { @@ -310,9 +317,18 @@ public class StorageExporter { keyPart = secondPart; } - if (AccountSettings.SETTINGS.containsKey(keyPart)) { + SettingsDescription setting = AccountSettings.SETTINGS.get(keyPart); + if (setting != null) { // Only export account settings that can be found in AccountSettings.SETTINGS - writeKeyValue(serializer, keyPart, value); + try { + Object value = setting.fromString(valueString); + String pretty = setting.toPrettyString(value); + writeKeyValue(serializer, keyPart, pretty); + } catch (InvalidSettingValueException e) { + Log.w(K9.LOG_TAG, "Account setting \"" + keyPart + "\" (" + + account.getDescription() + ") has invalid value \"" + valueString + + "\" in preference storage. This shouldn't happen!"); + } } } serializer.endTag(null, SETTINGS_ELEMENT);