1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-27 11:42:16 -05:00

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.
This commit is contained in:
cketti 2011-10-07 20:29:03 +02:00
parent 83ee4253d5
commit 0920b0c14d
4 changed files with 677 additions and 473 deletions

View File

@ -1,18 +1,16 @@
package com.fsck.k9.preferences; package com.fsck.k9.preferences;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri;
import android.util.Log;
import com.fsck.k9.Account; import com.fsck.k9.Account;
import com.fsck.k9.K9; import com.fsck.k9.K9;
import com.fsck.k9.R; import com.fsck.k9.R;
import com.fsck.k9.Account.FolderMode; import com.fsck.k9.Account.FolderMode;
import com.fsck.k9.Account.ScrollButtons; import com.fsck.k9.Account.ScrollButtons;
import com.fsck.k9.crypto.Apg; import com.fsck.k9.crypto.Apg;
import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.mail.store.StorageManager;
import com.fsck.k9.preferences.Settings.*; import com.fsck.k9.preferences.Settings.*;
@ -22,144 +20,73 @@ public class AccountSettings {
static { static {
SETTINGS = new LinkedHashMap<String, SettingsDescription>(); SETTINGS = new LinkedHashMap<String, SettingsDescription>();
// mandatory SETTINGS.put("archiveFolderName", new StringSetting("Archive"));
/* SETTINGS.put("autoExpandFolderName", new StringSetting("INBOX"));
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("automaticCheckIntervalMinutes", SETTINGS.put("automaticCheckIntervalMinutes",
SD(SettingType.INTEGER, -1, new ResourceArrayValidator( new IntegerResourceSetting(-1, R.array.account_settings_check_frequency_values));
R.array.account_settings_check_frequency_values))); SETTINGS.put("chipColor", new ColorSetting(0xFF0000FF));
SETTINGS.put("chipColor", SETTINGS.put("cryptoApp", new StringSetting(Apg.NAME));
SD(SettingType.INTEGER, 0xff0000ff, Settings.SOLID_COLOR_VALIDATOR)); SETTINGS.put("cryptoAutoSignature", new BooleanSetting(false));
SETTINGS.put("cryptoApp", SETTINGS.put("deletePolicy", new DeletePolicySetting(Account.DELETE_POLICY_NEVER));
SD(SettingType.STRING, Apg.NAME, null)); SETTINGS.put("displayCount", new IntegerResourceSetting(K9.DEFAULT_VISIBLE_LIMIT,
SETTINGS.put("cryptoAutoSignature", R.array.account_settings_display_count_values));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("draftsFolderName", new StringSetting("Drafts"));
SETTINGS.put("deletePolicy", SETTINGS.put("enableMoveButtons", new BooleanSetting(false));
SD(SettingType.STRING, 0, new ResourceArrayValidator( SETTINGS.put("expungePolicy", new StringResourceSetting(Account.EXPUNGE_IMMEDIATELY,
R.array.account_setup_delete_policy_values))); R.array.account_setup_expunge_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)));
SETTINGS.put("folderDisplayMode", SETTINGS.put("folderDisplayMode",
SD(SettingType.ENUM, FolderMode.NOT_SECOND_CLASS, new ResourceArrayValidator( new EnumSetting(FolderMode.class, FolderMode.NOT_SECOND_CLASS));
R.array.account_settings_folder_display_mode_values))); SETTINGS.put("folderPushMode", new EnumSetting(FolderMode.class, FolderMode.FIRST_CLASS));
SETTINGS.put("folderPushMode", SETTINGS.put("folderSyncMode", new EnumSetting(FolderMode.class, FolderMode.FIRST_CLASS));
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)));
SETTINGS.put("folderTargetMode", SETTINGS.put("folderTargetMode",
SD(SettingType.ENUM, FolderMode.NOT_SECOND_CLASS, new ResourceArrayValidator( new EnumSetting(FolderMode.class, FolderMode.NOT_SECOND_CLASS));
R.array.account_settings_folder_target_mode_values))); SETTINGS.put("goToUnreadMessageSearch", new BooleanSetting(false));
SETTINGS.put("goToUnreadMessageSearch", SETTINGS.put("hideButtonsEnum", new EnumSetting(ScrollButtons.class, ScrollButtons.NEVER));
SD(SettingType.BOOLEAN, false, null));
SETTINGS.put("hideButtonsEnum",
SD(SettingType.ENUM, ScrollButtons.NEVER, new ResourceArrayValidator(
R.array.account_settings_hide_buttons_values)));
SETTINGS.put("hideMoveButtonsEnum", SETTINGS.put("hideMoveButtonsEnum",
SD(SettingType.ENUM, ScrollButtons.NEVER, new ResourceArrayValidator( new EnumSetting(ScrollButtons.class, ScrollButtons.NEVER));
R.array.account_settings_hide_move_buttons_values))); SETTINGS.put("idleRefreshMinutes", new IntegerResourceSetting(24,
SETTINGS.put("idleRefreshMinutes", R.array.idle_refresh_period_values));
SD(SettingType.INTEGER, 24, new ResourceArrayValidator( SETTINGS.put("led", new BooleanSetting(true));
R.array.idle_refresh_period_values))); SETTINGS.put("ledColor", new ColorSetting(0xFF0000FF));
SETTINGS.put("led", SETTINGS.put("localStorageProvider", new StorageProviderSetting());
SD(SettingType.BOOLEAN, true, null)); SETTINGS.put("maxPushFolders", new IntegerRangeSetting(0, 100, 10));
SETTINGS.put("ledColor", SETTINGS.put("maximumAutoDownloadMessageSize", new IntegerResourceSetting(32768,
SD(SettingType.INTEGER, 0xff0000ff, Settings.SOLID_COLOR_VALIDATOR)); R.array.account_settings_autodownload_message_size_values));
SETTINGS.put("localStorageProvider", SETTINGS.put("maximumPolledMessageAge", new IntegerResourceSetting(-1,
SD(SettingType.STRING, new StorageProviderDefaultValue(), R.array.account_settings_message_age_values));
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)));
SETTINGS.put("messageFormat", SETTINGS.put("messageFormat",
SD(SettingType.ENUM, Account.DEFAULT_MESSAGE_FORMAT, new ResourceArrayValidator( new EnumSetting(Account.MessageFormat.class, Account.DEFAULT_MESSAGE_FORMAT));
R.array.account_settings_message_format_values))); SETTINGS.put("notificationUnreadCount", new BooleanSetting(true));
SETTINGS.put("notificationUnreadCount", SETTINGS.put("notifyMailCheck", new BooleanSetting(false));
SD(SettingType.BOOLEAN, true, null)); SETTINGS.put("notifyNewMail", new BooleanSetting(false));
SETTINGS.put("notifyMailCheck", SETTINGS.put("notifySelfNewMail", new BooleanSetting(true));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("pushPollOnConnect", new BooleanSetting(true));
SETTINGS.put("notifyNewMail", SETTINGS.put("quotePrefix", new StringSetting(Account.DEFAULT_QUOTE_PREFIX));
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));
SETTINGS.put("quoteStyle", SETTINGS.put("quoteStyle",
SD(SettingType.ENUM, Account.DEFAULT_QUOTE_STYLE, new ResourceArrayValidator( new EnumSetting(Account.QuoteStyle.class, Account.DEFAULT_QUOTE_STYLE));
R.array.account_settings_quote_style_values))); SETTINGS.put("replyAfterQuote", new BooleanSetting(Account.DEFAULT_REPLY_AFTER_QUOTE));
SETTINGS.put("replyAfterQuote", SETTINGS.put("ring", new BooleanSetting(true));
SD(SettingType.BOOLEAN, Account.DEFAULT_REPLY_AFTER_QUOTE, null));
SETTINGS.put("ring",
SD(SettingType.BOOLEAN, true, null));
SETTINGS.put("ringtone", SETTINGS.put("ringtone",
SD(SettingType.STRING, "content://settings/system/notification_sound", new RingtoneSetting("content://settings/system/notification_sound"));
new RingtoneValidator())); SETTINGS.put("saveAllHeaders", new BooleanSetting(true));
SETTINGS.put("saveAllHeaders",
SD(SettingType.BOOLEAN, true, null));
SETTINGS.put("searchableFolders", SETTINGS.put("searchableFolders",
SD(SettingType.ENUM, Account.Searchable.ALL, new ResourceArrayValidator( new EnumSetting(Account.Searchable.class, Account.Searchable.ALL));
R.array.account_settings_searchable_values))); SETTINGS.put("sentFolderName", new StringSetting("Sent"));
SETTINGS.put("sentFolderName",
SD(SettingType.STRING, "Sent", null));
SETTINGS.put("showPicturesEnum", SETTINGS.put("showPicturesEnum",
SD(SettingType.ENUM, Account.ShowPictures.NEVER, new ResourceArrayValidator( new EnumSetting(Account.ShowPictures.class, Account.ShowPictures.NEVER));
R.array.account_settings_show_pictures_values))); SETTINGS.put("signatureBeforeQuotedText", new BooleanSetting(false));
SETTINGS.put("signatureBeforeQuotedText", SETTINGS.put("spamFolderName", new StringSetting("Spam"));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("subscribedFoldersOnly", new BooleanSetting(false));
SETTINGS.put("spamFolderName", SETTINGS.put("syncRemoteDeletions", new BooleanSetting(true));
SD(SettingType.STRING, "Spam", null)); SETTINGS.put("trashFolderName", new StringSetting("Trash"));
SETTINGS.put("subscribedFoldersOnly", SETTINGS.put("useCompression.MOBILE", new BooleanSetting(true));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("useCompression.OTHER", new BooleanSetting(true));
SETTINGS.put("syncRemoteDeletions", SETTINGS.put("useCompression.WIFI", new BooleanSetting(true));
SD(SettingType.BOOLEAN, true, null)); SETTINGS.put("vibrate", new BooleanSetting(false));
SETTINGS.put("trashFolderName", SETTINGS.put("vibratePattern", new IntegerResourceSetting(0,
SD(SettingType.STRING, "Trash", null)); R.array.account_settings_vibrate_pattern_values));
SETTINGS.put("useCompression.MOBILE", SETTINGS.put("vibrateTimes", new IntegerResourceSetting(5,
SD(SettingType.BOOLEAN, true, null)); R.array.account_settings_vibrate_times_label));
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);
} }
public static Map<String, String> validate(Map<String, String> importedSettings, public static Map<String, String> validate(Map<String, String> importedSettings,
@ -179,98 +106,148 @@ public class AccountSettings {
return result; return result;
} }
/**
* An integer resource setting.
*
* <p>
* Basically a {@link PseudoEnumSetting} that is initialized from a resource array containing
* integer strings.
* </p>
*/
public static class IntegerResourceSetting extends PseudoEnumSetting<Integer> {
private final Map<Integer, String> mMapping;
public IntegerResourceSetting(int defaultValue, int resId) {
super(defaultValue);
Map<Integer, String> mapping = new HashMap<Integer, String>();
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 @Override
public Object computeDefaultValue(String key, Map<String, String> validatedSettings) { protected Map<Integer, String> 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.
*
* <p>
* Basically a {@link PseudoEnumSetting} that is initialized from a resource array.
* </p>
*/
public static class StringResourceSetting extends PseudoEnumSetting<String> {
private final Map<String, String> mMapping;
public StringResourceSetting(String defaultValue, int resId) {
super(defaultValue);
Map<String, String> mapping = new HashMap<String, String>();
String[] values = K9.app.getResources().getStringArray(resId);
for (String value : values) {
mapping.put(value, value);
}
mMapping = Collections.unmodifiableMap(mapping);
}
@Override
protected Map<String, String> 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(); return StorageManager.getInstance(K9.app).getDefaultProviderId();
} }
}
public static class StorageProviderValidator implements ISettingValidator {
@Override @Override
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { public Object fromString(String value) {
Map<String, String> providers = StorageManager.getInstance(K9.app).getAvailableProviders(); StorageManager storageManager = StorageManager.getInstance(K9.app);
for (String storageProvider : providers.keySet()) { Map<String, String> providers = storageManager.getAvailableProviders();
if (storageProvider.equals(value)) { if (providers.containsKey(value)) {
return true; return value;
}
} }
return false; throw new RuntimeException("Validation failed");
} }
} }
public static class RingtoneValidator implements ISettingValidator { /**
@Override * The delete policy setting.
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { */
// TODO implement public static class DeletePolicySetting extends PseudoEnumSetting<Integer> {
return true; private Map<Integer, String> mMapping;
}
} public DeletePolicySetting(int defaultValue) {
super(defaultValue);
Map<Integer, String> mapping = new HashMap<Integer, String>();
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 @Override
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { protected Map<Integer, String> getMapping() {
return mMapping;
}
@Override
public Object fromString(String value) throws InvalidSettingValueException {
try { try {
String uriString = Utility.base64Decode(value); Integer deletePolicy = Integer.parseInt(value);
if (!uriString.startsWith("imap") && !uriString.startsWith("pop3") && if (mMapping.containsKey(deletePolicy)) {
!uriString.startsWith("webdav")) { return deletePolicy;
return false;
} }
} catch (NumberFormatException e) { /* do nothing */ }
//TODO: check complete scheme (imap+ssl etc.) throw new InvalidSettingValueException();
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<String, String> 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;
} }
} }
} }

View File

@ -1,6 +1,7 @@
package com.fsck.k9.preferences; package com.fsck.k9.preferences;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -12,122 +13,63 @@ import com.fsck.k9.helper.DateFormatter;
import com.fsck.k9.preferences.Settings.*; import com.fsck.k9.preferences.Settings.*;
public class GlobalSettings { public class GlobalSettings {
public static final ISettingValidator FONT_SIZE_VALIDATOR = new DipFontSizeValidator();
public static final ISettingValidator TIME_VALIDATOR = new TimeValidator();
public static final Map<String, SettingsDescription> SETTINGS; public static final Map<String, SettingsDescription> SETTINGS;
static { static {
SETTINGS = new LinkedHashMap<String, SettingsDescription>(); SETTINGS = new LinkedHashMap<String, SettingsDescription>();
SETTINGS.put("animations", SETTINGS.put("animations", new BooleanSetting(false));
SD(SettingType.BOOLEAN, false, null));
SETTINGS.put("backgroundOperations", SETTINGS.put("backgroundOperations",
SD(SettingType.ENUM, K9.BACKGROUND_OPS.WHEN_CHECKED, new ResourceArrayValidator( new EnumSetting(K9.BACKGROUND_OPS.class, K9.BACKGROUND_OPS.WHEN_CHECKED));
R.array.background_ops_values))); SETTINGS.put("changeRegisteredNameColor", new BooleanSetting(false));
SETTINGS.put("changeRegisteredNameColor", SETTINGS.put("compactLayouts", new BooleanSetting(false));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("confirmDelete", new BooleanSetting(false));
SETTINGS.put("compactLayouts", SETTINGS.put("countSearchMessages", new BooleanSetting(false));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("dateFormat", new DateFormatSetting(DateFormatter.DEFAULT_FORMAT));
SETTINGS.put("confirmDelete", SETTINGS.put("enableDebugLogging", new BooleanSetting(false));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("enableSensitiveLogging", new BooleanSetting(false));
SETTINGS.put("countSearchMessages", SETTINGS.put("fontSizeAccountDescription", new FontSizeSetting(FontSizes.SMALL));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("fontSizeAccountName", new FontSizeSetting(FontSizes.MEDIUM));
SETTINGS.put("dateFormat", SETTINGS.put("fontSizeFolderName", new FontSizeSetting(FontSizes.LARGE));
SD(SettingType.ENUM, DateFormatter.DEFAULT_FORMAT, new DateFormatValidator())); SETTINGS.put("fontSizeFolderStatus", new FontSizeSetting(FontSizes.SMALL));
SETTINGS.put("enableDebugLogging", SETTINGS.put("fontSizeMessageListDate", new FontSizeSetting(FontSizes.SMALL));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("fontSizeMessageListPreview", new FontSizeSetting(FontSizes.SMALL));
SETTINGS.put("enableSensitiveLogging", SETTINGS.put("fontSizeMessageListSender", new FontSizeSetting(FontSizes.SMALL));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("fontSizeMessageListSubject", new FontSizeSetting(FontSizes.FONT_16DIP));
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));
SETTINGS.put("fontSizeMessageViewAdditionalHeaders", SETTINGS.put("fontSizeMessageViewAdditionalHeaders",
SD(SettingType.INTEGER, 12, FONT_SIZE_VALIDATOR)); new FontSizeSetting(FontSizes.FONT_12DIP));
SETTINGS.put("fontSizeMessageViewCC", SETTINGS.put("fontSizeMessageViewCC", new FontSizeSetting(FontSizes.FONT_12DIP));
SD(SettingType.INTEGER, 12, FONT_SIZE_VALIDATOR)); SETTINGS.put("fontSizeMessageViewContent", new WebFontSizeSetting(3));
SETTINGS.put("fontSizeMessageViewContent", SETTINGS.put("fontSizeMessageViewDate", new FontSizeSetting(FontSizes.FONT_10DIP));
SD(SettingType.INTEGER, 3, new WebViewFontSizeValidator())); SETTINGS.put("fontSizeMessageViewSender", new FontSizeSetting(FontSizes.SMALL));
SETTINGS.put("fontSizeMessageViewDate", SETTINGS.put("fontSizeMessageViewSubject", new FontSizeSetting(FontSizes.FONT_12DIP));
SD(SettingType.INTEGER, 10, FONT_SIZE_VALIDATOR)); SETTINGS.put("fontSizeMessageViewTime", new FontSizeSetting(FontSizes.FONT_10DIP));
SETTINGS.put("fontSizeMessageViewSender", SETTINGS.put("fontSizeMessageViewTo", new FontSizeSetting(FontSizes.FONT_12DIP));
SD(SettingType.INTEGER, 14, FONT_SIZE_VALIDATOR)); SETTINGS.put("gesturesEnabled", new BooleanSetting(true));
SETTINGS.put("fontSizeMessageViewSubject", SETTINGS.put("hideSpecialAccounts", new BooleanSetting(false));
SD(SettingType.INTEGER, 12, FONT_SIZE_VALIDATOR)); SETTINGS.put("keyguardPrivacy", new BooleanSetting(false));
SETTINGS.put("fontSizeMessageViewTime", SETTINGS.put("language", new LanguageSetting());
SD(SettingType.INTEGER, 10, FONT_SIZE_VALIDATOR)); SETTINGS.put("manageBack", new BooleanSetting(false));
SETTINGS.put("fontSizeMessageViewTo", SETTINGS.put("measureAccounts", new BooleanSetting(true));
SD(SettingType.INTEGER, 12, FONT_SIZE_VALIDATOR)); SETTINGS.put("messageListCheckboxes", new BooleanSetting(false));
SETTINGS.put("gesturesEnabled", SETTINGS.put("messageListPreviewLines", new IntegerRangeSetting(1, 100, 2));
SD(SettingType.BOOLEAN, true, null)); SETTINGS.put("messageListStars", new BooleanSetting(true));
SETTINGS.put("hideSpecialAccounts", SETTINGS.put("messageListTouchable", new BooleanSetting(false));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("messageViewFixedWidthFont", new BooleanSetting(false));
SETTINGS.put("keyguardPrivacy", SETTINGS.put("messageViewReturnToList", new BooleanSetting(false));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("mobileOptimizedLayout", new BooleanSetting(false));
SETTINGS.put("language", SETTINGS.put("quietTimeEnabled", new BooleanSetting(false));
SD(SettingType.STRING, "", new ResourceArrayValidator( SETTINGS.put("quietTimeEnds", new TimeSetting("7:00"));
R.array.settings_language_values))); SETTINGS.put("quietTimeStarts", new TimeSetting("21:00"));
SETTINGS.put("manageBack", SETTINGS.put("registeredNameColor", new ColorSetting(0xFF00008F));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("showContactName", new BooleanSetting(false));
SETTINGS.put("measureAccounts", SETTINGS.put("showCorrespondentNames", new BooleanSetting(true));
SD(SettingType.BOOLEAN, true, null)); SETTINGS.put("startIntegratedInbox", new BooleanSetting(false));
SETTINGS.put("messageListCheckboxes", SETTINGS.put("theme", new ThemeSetting(android.R.style.Theme_Light));
SD(SettingType.BOOLEAN, false, null)); SETTINGS.put("useGalleryBugWorkaround", new GalleryBugWorkaroundSetting());
SETTINGS.put("messageListPreviewLines", SETTINGS.put("useVolumeKeysForListNavigation", new BooleanSetting(false));
SD(SettingType.INTEGER, 2, Settings.POSITIVE_INTEGER_VALIDATOR)); SETTINGS.put("useVolumeKeysForNavigation", new BooleanSetting(false));
SETTINGS.put("messageListStars", SETTINGS.put("zoomControlsEnabled", new BooleanSetting(false));
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);
} }
public static Map<String, String> validate(Map<String, String> importedSettings) { public static Map<String, String> validate(Map<String, String> importedSettings) {
@ -145,72 +87,141 @@ public class GlobalSettings {
return result; return result;
} }
/**
* The gallery bug work-around setting.
*
* <p>
* The default value varies depending on whether you have a version of Gallery 3D installed
* that contains the bug we work around.
* </p>
*
* @see K9#isGalleryBuggy()
*/
public static class GalleryBugWorkaroundSetting extends BooleanSetting {
public GalleryBugWorkaroundSetting() {
super(false);
}
public static class GalleryBugWorkaroundDefaultValue implements IDefaultValue {
@Override @Override
public Object computeDefaultValue(String key, Map<String, String> validatedSettings) { public Object getDefaultValue() {
return K9.isGalleryBuggy(); return K9.isGalleryBuggy();
} }
} }
public static class DipFontSizeValidator implements ISettingValidator { /**
@Override * The language setting.
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { *
int val = Integer.parseInt(value); * <p>
switch (val) { * Valid values are read from {@code settings_language_values} in
case FontSizes.FONT_10DIP: * {@code res/values/arrays.xml}.
case FontSizes.FONT_12DIP: * </p>
case FontSizes.SMALL: */
case FontSizes.FONT_16DIP: public static class LanguageSetting extends PseudoEnumSetting<String> {
case FontSizes.MEDIUM: private final Map<String, String> mMapping;
case FontSizes.FONT_20DIP:
case FontSizes.LARGE: public LanguageSetting() {
return true; super("");
default:
return false; Map<String, String> mapping = new HashMap<String, String>();
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<String, String> 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<Integer> {
private final Map<Integer, String> mMapping;
public ThemeSetting(int defaultValue) {
super(defaultValue);
Map<Integer, String> mapping = new HashMap<Integer, String>();
mapping.put(android.R.style.Theme_Light, "light");
mapping.put(android.R.style.Theme, "dark");
mMapping = Collections.unmodifiableMap(mapping);
}
@Override @Override
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { protected Map<Integer, String> getMapping() {
int val = Integer.parseInt(value); return mMapping;
return (val >= 1 && val <= 5); }
@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 * A date format setting.
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { */
return value.matches(TimePickerPreference.VALIDATION_EXPRESSION); public static class DateFormatSetting extends SettingsDescription {
public DateFormatSetting(String defaultValue) {
super(defaultValue);
} }
}
public static class DateFormatValidator implements ISettingValidator {
@Override @Override
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { public Object fromString(String value) throws InvalidSettingValueException {
try { try {
// The placeholders "SHORT" and "MEDIUM" are fine. // The placeholders "SHORT" and "MEDIUM" are fine.
if (DateFormatter.SHORT_FORMAT.equals(value) || if (DateFormatter.SHORT_FORMAT.equals(value) ||
DateFormatter.MEDIUM_FORMAT.equals(value)) { DateFormatter.MEDIUM_FORMAT.equals(value)) {
return true; return value;
} }
// If the SimpleDateFormat constructor doesn't throw an exception, we're good. // If the SimpleDateFormat constructor doesn't throw an exception, we're good.
new SimpleDateFormat(value); new SimpleDateFormat(value);
return true; return value;
} catch (Exception e) { } 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 @Override
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { public Object fromString(String value) throws InvalidSettingValueException {
int val = Integer.parseInt(value); if (!value.matches(TimePickerPreference.VALIDATION_EXPRESSION)) {
return (val == android.R.style.Theme_Light || val == android.R.style.Theme); throw new InvalidSettingValueException();
}
return value;
} }
} }
} }

View File

@ -1,8 +1,13 @@
package com.fsck.k9.preferences; package com.fsck.k9.preferences;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import android.util.Log; import android.util.Log;
import com.fsck.k9.FontSizes;
import com.fsck.k9.K9; 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) * - 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 * - use the default values defined in GlobalSettings and AccountSettings when creating new
* accounts * 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 * - 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 * 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 * - 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 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<String, String> validate(Map<String, SettingsDescription> settings, public static Map<String, String> validate(Map<String, SettingsDescription> settings,
Map<String, String> importedSettings, boolean useDefaultValues) { Map<String, String> importedSettings, boolean useDefaultValues) {
@ -54,12 +48,14 @@ public class Settings {
((useDefaultValues) ? " Using default value." : "")); ((useDefaultValues) ? " Using default value." : ""));
useDefaultValue = useDefaultValues; useDefaultValue = useDefaultValues;
} else { } else {
String importedValue = importedSettings.get(key); String prettyValue = importedSettings.get(key);
if (Settings.isValid(desc, key, importedValue, validatedSettings)) { try {
Object internalValue = desc.fromPrettyString(prettyValue);
String importedValue = desc.toString(internalValue);
validatedSettings.put(key, importedValue); validatedSettings.put(key, importedValue);
useDefaultValue = false; useDefaultValue = false;
} else { } catch (InvalidSettingValueException e) {
Log.v(K9.LOG_TAG, "Key \"" + key + "\" has invalid value \"" + importedValue + Log.v(K9.LOG_TAG, "Key \"" + key + "\" has invalid value \"" + prettyValue +
"\" in imported file. " + "\" in imported file. " +
((useDefaultValues) ? "Using default value." : "Skipping.")); ((useDefaultValues) ? "Using default value." : "Skipping."));
useDefaultValue = useDefaultValues; useDefaultValue = useDefaultValues;
@ -67,142 +63,346 @@ public class Settings {
} }
if (useDefaultValue) { if (useDefaultValue) {
Object defaultValue; Object defaultValue = desc.getDefaultValue();
if (desc.defaultValue instanceof IDefaultValue) { validatedSettings.put(key, desc.toString(defaultValue));
defaultValue = ((IDefaultValue)desc.defaultValue).computeDefaultValue(key, validatedSettings);
} else {
defaultValue = desc.defaultValue;
}
validatedSettings.put(key, defaultValue.toString());
} }
} }
return validatedSettings; return validatedSettings;
} }
public static boolean isValid(SettingsDescription desc, String key, String value,
Map<String, String> 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) { * Describes a setting.
Log.e(K9.LOG_TAG, "Exception while running validator for value \"" + value + "\"", e); *
return false; * <p>
* Instances of this class are used to convert the string representations of setting values to
* an internal representation (e.g. an integer) and back.
* </p><p>
* Currently we use two different string representations:
* </p>
* <ol>
* <li>
* 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)}).
* </li>
* <li>
* 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").
* </li>
* </ol>
* <p>
* <strong>Note:</strong>
* 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.
* </p>
*/
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, * A string setting.
INTEGER, */
STRING, public static class StringSetting extends SettingsDescription {
ENUM public StringSetting(String defaultValue) {
} super(defaultValue);
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;
} }
}
public interface IDefaultValue {
Object computeDefaultValue(String key, Map<String, String> validatedSettings);
}
public static class ExceptionDefaultValue implements IDefaultValue {
@Override @Override
public Object computeDefaultValue(String key, Map<String, String> validatedSettings) { public Object fromString(String value) {
throw new RuntimeException("There is no default value for key \"" + key + "\"."); return value;
}
}
public interface ISettingValidator {
boolean isValid(String key, String value, Map<String, String> validatedSettings);
}
public static class BooleanValidator implements ISettingValidator {
@Override
public boolean isValid(String key, String value, Map<String, String> validatedSettings) {
return Boolean.TRUE.toString().equals(value) || Boolean.FALSE.toString().equals(value);
} }
} }
public static class IntegerValidator implements ISettingValidator { /**
* A boolean setting.
*/
public static class BooleanSetting extends SettingsDescription {
public BooleanSetting(boolean defaultValue) {
super(defaultValue);
}
@Override @Override
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { public Object fromString(String value) throws InvalidSettingValueException {
try { if (Boolean.TRUE.toString().equals(value)) {
Integer.parseInt(value);
return true; return true;
} catch (NumberFormatException e) { } else if (Boolean.FALSE.toString().equals(value)) {
return false; return false;
} }
throw new InvalidSettingValueException();
} }
} }
public static class PositiveIntegerValidator implements ISettingValidator { /**
@Override * A color setting.
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { */
return (Integer.parseInt(value) >= 0); public static class ColorSetting extends SettingsDescription {
} public ColorSetting(int defaultValue) {
} super(defaultValue);
public static class ResourceArrayValidator implements ISettingValidator {
private final int mResource;
public ResourceArrayValidator(int res) {
mResource = res;
} }
@Override @Override
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { public Object fromString(String value) throws InvalidSettingValueException {
try { try {
String[] values = K9.app.getResources().getStringArray(mResource); return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new InvalidSettingValueException();
}
}
for (String validValue : values) { @Override
if (validValue.equals(value)) { public String toPrettyString(Object value) {
return true; 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.
*
* <p>
* {@link Enum#toString()} is used to obtain the "pretty" string representation.
* </p>
*/
public static class EnumSetting extends SettingsDescription {
private Class<? extends Enum<?>> mEnumClass;
public EnumSetting(Class<? extends Enum<?>> enumClass, Object defaultValue) {
super(defaultValue);
mEnumClass = enumClass;
}
@SuppressWarnings("unchecked")
@Override
public Object fromString(String value) throws InvalidSettingValueException {
try {
return Enum.valueOf((Class<? extends Enum>)mEnumClass, value);
} catch (Exception e) { } 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 <A>
* The type of the internal representation (e.g. {@code Integer}).
*/
public abstract static class PseudoEnumSetting<A> extends SettingsDescription {
public PseudoEnumSetting(Object defaultValue) {
super(defaultValue);
}
protected abstract Map<A, String> getMapping();
@Override
public String toPrettyString(Object value) {
return getMapping().get(value);
}
@Override
public Object fromPrettyString(String value) throws InvalidSettingValueException {
for (Entry<A, String> entry : getMapping().entrySet()) {
if (entry.getValue().equals(value)) {
return entry.getKey();
}
} }
return false; throw new InvalidSettingValueException();
} }
} }
public static class SolidColorValidator implements ISettingValidator { /**
@Override * A font size setting.
public boolean isValid(String key, String value, Map<String, String> validatedSettings) { */
int color = Integer.parseInt(value); public static class FontSizeSetting extends PseudoEnumSetting<Integer> {
return ((color & 0xFF000000) == 0xFF000000); private final Map<Integer, String> mMapping;
public FontSizeSetting(int defaultValue) {
super(defaultValue);
Map<Integer, String> mapping = new HashMap<Integer, String>();
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<Integer, String> 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<Integer> {
private final Map<Integer, String> mMapping;
public WebFontSizeSetting(int defaultValue) {
super(defaultValue);
Map<Integer, String> mapping = new HashMap<Integer, String>();
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<Integer, String> 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();
}
} }
} }

View File

@ -29,6 +29,8 @@ import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.ServerSettings;
import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.Transport;
import com.fsck.k9.mail.store.LocalStore; import com.fsck.k9.mail.store.LocalStore;
import com.fsck.k9.preferences.Settings.InvalidSettingValueException;
import com.fsck.k9.preferences.Settings.SettingsDescription;
public class StorageExporter { public class StorageExporter {
@ -183,11 +185,16 @@ public class StorageExporter {
Map<String, Object> prefs) throws IOException { Map<String, Object> prefs) throws IOException {
for (String key : GlobalSettings.SETTINGS.keySet()) { for (String key : GlobalSettings.SETTINGS.keySet()) {
Object value = prefs.get(key); String valueString = prefs.get(key).toString();
if (value != null) { try {
String outputValue = value.toString(); SettingsDescription setting = GlobalSettings.SETTINGS.get(key);
Object value = setting.fromString(valueString);
String outputValue = setting.toPrettyString(value);
writeKeyValue(serializer, key, outputValue); 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); serializer.startTag(null, SETTINGS_ELEMENT);
for (Map.Entry<String, Object> entry : prefs.entrySet()) { for (Map.Entry<String, Object> entry : prefs.entrySet()) {
String key = entry.getKey(); String key = entry.getKey();
String value = entry.getValue().toString(); String valueString = entry.getValue().toString();
String[] comps = key.split("\\."); String[] comps = key.split("\\.");
if (comps.length < 2) { if (comps.length < 2) {
@ -310,9 +317,18 @@ public class StorageExporter {
keyPart = secondPart; 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 // 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); serializer.endTag(null, SETTINGS_ELEMENT);