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);