package com.fsck.k9.preferences; import java.io.File; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap; import android.os.Environment; import com.fsck.k9.Account; import com.fsck.k9.Account.SortType; import com.fsck.k9.FontSizes; import com.fsck.k9.K9; import com.fsck.k9.K9.NotificationHideSubject; import com.fsck.k9.K9.NotificationQuickDelete; import com.fsck.k9.K9.SplitViewMode; import com.fsck.k9.K9.Theme; import com.fsck.k9.preferences.Settings.BooleanSetting; import com.fsck.k9.preferences.Settings.ColorSetting; import com.fsck.k9.preferences.Settings.EnumSetting; import com.fsck.k9.preferences.Settings.FontSizeSetting; import com.fsck.k9.preferences.Settings.IntegerRangeSetting; import com.fsck.k9.preferences.Settings.InvalidSettingValueException; import com.fsck.k9.preferences.Settings.SettingsDescription; import com.fsck.k9.preferences.Settings.SettingsUpgrader; import com.fsck.k9.preferences.Settings.StringSetting; import com.fsck.k9.preferences.Settings.V; import com.fsck.k9.preferences.Settings.WebFontSizeSetting; import static com.fsck.k9.K9.LockScreenNotificationVisibility; public class GlobalSettings { static final Map<String, TreeMap<Integer, SettingsDescription>> SETTINGS; private static final Map<Integer, SettingsUpgrader> UPGRADERS; static { Map<String, TreeMap<Integer, SettingsDescription>> s = new LinkedHashMap<>(); /* * When adding new settings here, be sure to increment {@link Settings.VERSION} * and use that for whatever you add here. */ s.put("animations", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("attachmentdefaultpath", Settings.versions( new V(1, new DirectorySetting(Environment.getExternalStorageDirectory())), new V(41, new DirectorySetting(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS))) )); s.put("backgroundOperations", Settings.versions( new V(1, new EnumSetting<>(K9.BACKGROUND_OPS.class, K9.BACKGROUND_OPS.WHEN_CHECKED_AUTO_SYNC)) )); s.put("changeRegisteredNameColor", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("confirmDelete", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("confirmDeleteStarred", Settings.versions( new V(2, new BooleanSetting(false)) )); s.put("confirmSpam", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("confirmMarkAllRead", Settings.versions( new V(44, new BooleanSetting(true)) )); s.put("countSearchMessages", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("enableDebugLogging", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("enableSensitiveLogging", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("fontSizeAccountDescription", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeAccountName", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeFolderName", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeFolderStatus", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageComposeInput", Settings.versions( new V(5, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageListDate", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageListPreview", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageListSender", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageListSubject", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageViewAdditionalHeaders", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageViewCC", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageViewContent", Settings.versions( new V(1, new WebFontSizeSetting(3)), new V(31, null) )); s.put("fontSizeMessageViewDate", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageViewSender", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageViewSubject", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageViewTime", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("fontSizeMessageViewTo", Settings.versions( new V(1, new FontSizeSetting(FontSizes.FONT_DEFAULT)) )); s.put("gesturesEnabled", Settings.versions( new V(1, new BooleanSetting(true)), new V(4, new BooleanSetting(false)) )); s.put("hideSpecialAccounts", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("keyguardPrivacy", Settings.versions( new V(1, new BooleanSetting(false)), new V(12, null) )); s.put("measureAccounts", Settings.versions( new V(1, new BooleanSetting(true)) )); s.put("messageListCheckboxes", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("messageListPreviewLines", Settings.versions( new V(1, new IntegerRangeSetting(1, 100, 2)) )); s.put("messageListStars", Settings.versions( new V(1, new BooleanSetting(true)) )); s.put("messageViewFixedWidthFont", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("messageViewReturnToList", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("messageViewShowNext", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("quietTimeEnabled", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("quietTimeEnds", Settings.versions( new V(1, new TimeSetting("7:00")) )); s.put("quietTimeStarts", Settings.versions( new V(1, new TimeSetting("21:00")) )); s.put("registeredNameColor", Settings.versions( new V(1, new ColorSetting(0xFF00008F)) )); s.put("showContactName", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("showCorrespondentNames", Settings.versions( new V(1, new BooleanSetting(true)) )); s.put("sortTypeEnum", Settings.versions( new V(10, new EnumSetting<>(SortType.class, Account.DEFAULT_SORT_TYPE)) )); s.put("sortAscending", Settings.versions( new V(10, new BooleanSetting(Account.DEFAULT_SORT_ASCENDING)) )); s.put("startIntegratedInbox", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("theme", Settings.versions( new V(1, new ThemeSetting(K9.Theme.LIGHT)) )); s.put("messageViewTheme", Settings.versions( new V(16, new ThemeSetting(K9.Theme.LIGHT)), new V(24, new SubThemeSetting(K9.Theme.USE_GLOBAL)) )); s.put("useVolumeKeysForListNavigation", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("useVolumeKeysForNavigation", Settings.versions( new V(1, new BooleanSetting(false)) )); s.put("wrapFolderNames", Settings.versions( new V(22, new BooleanSetting(false)) )); s.put("notificationHideSubject", Settings.versions( new V(12, new EnumSetting<>(NotificationHideSubject.class, NotificationHideSubject.NEVER)) )); s.put("useBackgroundAsUnreadIndicator", Settings.versions( new V(19, new BooleanSetting(true)) )); s.put("threadedView", Settings.versions( new V(20, new BooleanSetting(true)) )); s.put("splitViewMode", Settings.versions( new V(23, new EnumSetting<>(SplitViewMode.class, SplitViewMode.NEVER)) )); s.put("messageComposeTheme", Settings.versions( new V(24, new SubThemeSetting(K9.Theme.USE_GLOBAL)) )); s.put("fixedMessageViewTheme", Settings.versions( new V(24, new BooleanSetting(true)) )); s.put("showContactPicture", Settings.versions( new V(25, new BooleanSetting(true)) )); s.put("autofitWidth", Settings.versions( new V(28, new BooleanSetting(true)) )); s.put("colorizeMissingContactPictures", Settings.versions( new V(29, new BooleanSetting(true)) )); s.put("messageViewDeleteActionVisible", Settings.versions( new V(30, new BooleanSetting(true)) )); s.put("messageViewArchiveActionVisible", Settings.versions( new V(30, new BooleanSetting(false)) )); s.put("messageViewMoveActionVisible", Settings.versions( new V(30, new BooleanSetting(false)) )); s.put("messageViewCopyActionVisible", Settings.versions( new V(30, new BooleanSetting(false)) )); s.put("messageViewSpamActionVisible", Settings.versions( new V(30, new BooleanSetting(false)) )); s.put("fontSizeMessageViewContentPercent", Settings.versions( new V(31, new IntegerRangeSetting(40, 250, 100)) )); s.put("hideUserAgent", Settings.versions( new V(32, new BooleanSetting(false)) )); s.put("hideTimeZone", Settings.versions( new V(32, new BooleanSetting(false)) )); s.put("lockScreenNotificationVisibility", Settings.versions( new V(37, new EnumSetting<>(LockScreenNotificationVisibility.class, LockScreenNotificationVisibility.MESSAGE_COUNT)) )); s.put("confirmDeleteFromNotification", Settings.versions( new V(38, new BooleanSetting(true)) )); s.put("messageListSenderAboveSubject", Settings.versions( new V(38, new BooleanSetting(false)) )); s.put("notificationQuickDelete", Settings.versions( new V(38, new EnumSetting<>(NotificationQuickDelete.class, NotificationQuickDelete.NEVER)) )); s.put("notificationDuringQuietTimeEnabled", Settings.versions( new V(39, new BooleanSetting(true)) )); s.put("confirmDiscardMessage", Settings.versions( new V(40, new BooleanSetting(true)) )); s.put("pgpInlineDialogCounter", Settings.versions( new V(43, new IntegerRangeSetting(0, Integer.MAX_VALUE, 0)) )); s.put("pgpSignOnlyDialogCounter", Settings.versions( new V(45, new IntegerRangeSetting(0, Integer.MAX_VALUE, 0)) )); s.put("openPgpProvider", Settings.versions( new V(46, new StringSetting(K9.NO_OPENPGP_PROVIDER)) )); s.put("openPgpSupportSignOnly", Settings.versions( new V(47, new BooleanSetting(false)) )); SETTINGS = Collections.unmodifiableMap(s); Map<Integer, SettingsUpgrader> u = new HashMap<>(); u.put(12, new SettingsUpgraderV12()); u.put(24, new SettingsUpgraderV24()); u.put(31, new SettingsUpgraderV31()); UPGRADERS = Collections.unmodifiableMap(u); } static Map<String, Object> validate(int version, Map<String, String> importedSettings) { return Settings.validate(version, SETTINGS, importedSettings, false); } public static Set<String> upgrade(int version, Map<String, Object> validatedSettings) { return Settings.upgrade(version, UPGRADERS, SETTINGS, validatedSettings); } public static Map<String, String> convert(Map<String, Object> settings) { return Settings.convert(settings, SETTINGS); } static Map<String, String> getGlobalSettings(Storage storage) { Map<String, String> result = new HashMap<>(); for (String key : SETTINGS.keySet()) { String value = storage.getString(key, null); if (value != null) { result.put(key, value); } } return result; } /** * Upgrades the settings from version 11 to 12 * * Map the 'keyguardPrivacy' value to the new NotificationHideSubject enum. */ private static class SettingsUpgraderV12 implements SettingsUpgrader { @Override public Set<String> upgrade(Map<String, Object> settings) { Boolean keyguardPrivacy = (Boolean) settings.get("keyguardPrivacy"); if (keyguardPrivacy != null && keyguardPrivacy) { // current setting: only show subject when unlocked settings.put("notificationHideSubject", NotificationHideSubject.WHEN_LOCKED); } else { // always show subject [old default] settings.put("notificationHideSubject", NotificationHideSubject.NEVER); } return new HashSet<>(Collections.singletonList("keyguardPrivacy")); } } /** * Upgrades the settings from version 23 to 24. * * <p> * Set <em>messageViewTheme</em> to {@link K9.Theme#USE_GLOBAL} if <em>messageViewTheme</em> has * the same value as <em>theme</em>. * </p> */ private static class SettingsUpgraderV24 implements SettingsUpgrader { @Override public Set<String> upgrade(Map<String, Object> settings) { K9.Theme messageViewTheme = (K9.Theme) settings.get("messageViewTheme"); K9.Theme theme = (K9.Theme) settings.get("theme"); if (theme != null && messageViewTheme != null && theme == messageViewTheme) { settings.put("messageViewTheme", K9.Theme.USE_GLOBAL); } return null; } } /** * Upgrades the settings from version 30 to 31. * * <p> * Convert value from <em>fontSizeMessageViewContent</em> to * <em>fontSizeMessageViewContentPercent</em>. * </p> */ public static class SettingsUpgraderV31 implements SettingsUpgrader { @Override public Set<String> upgrade(Map<String, Object> settings) { int oldSize = (Integer) settings.get("fontSizeMessageViewContent"); int newSize = convertFromOldSize(oldSize); settings.put("fontSizeMessageViewContentPercent", newSize); return new HashSet<>(Collections.singletonList("fontSizeMessageViewContent")); } public static int convertFromOldSize(int oldSize) { switch (oldSize) { case 1: { return 40; } case 2: { return 75; } case 4: { return 175; } case 5: { return 250; } case 3: default: { return 100; } } } } static class ThemeSetting extends SettingsDescription<K9.Theme> { private static final String THEME_LIGHT = "light"; private static final String THEME_DARK = "dark"; ThemeSetting(K9.Theme defaultValue) { super(defaultValue); } @Override public K9.Theme fromString(String value) throws InvalidSettingValueException { try { Integer theme = Integer.parseInt(value); if (theme == K9.Theme.LIGHT.ordinal() || // We used to store the resource ID of the theme in the preference storage, // but don't use the database upgrade mechanism to update the values. So // we have to deal with the old format here. theme == android.R.style.Theme_Light) { return K9.Theme.LIGHT; } else if (theme == K9.Theme.DARK.ordinal() || theme == android.R.style.Theme) { return K9.Theme.DARK; } } catch (NumberFormatException e) { /* do nothing */ } throw new InvalidSettingValueException(); } @Override public K9.Theme fromPrettyString(String value) throws InvalidSettingValueException { if (THEME_LIGHT.equals(value)) { return K9.Theme.LIGHT; } else if (THEME_DARK.equals(value)) { return K9.Theme.DARK; } throw new InvalidSettingValueException(); } @Override public String toPrettyString(K9.Theme value) { switch (value) { case DARK: { return THEME_DARK; } default: { return THEME_LIGHT; } } } @Override public String toString(K9.Theme value) { return Integer.toString(value.ordinal()); } } private static class SubThemeSetting extends ThemeSetting { private static final String THEME_USE_GLOBAL = "use_global"; SubThemeSetting(Theme defaultValue) { super(defaultValue); } @Override public K9.Theme fromString(String value) throws InvalidSettingValueException { try { Integer theme = Integer.parseInt(value); if (theme == K9.Theme.USE_GLOBAL.ordinal()) { return K9.Theme.USE_GLOBAL; } return super.fromString(value); } catch (NumberFormatException e) { throw new InvalidSettingValueException(); } } @Override public K9.Theme fromPrettyString(String value) throws InvalidSettingValueException { if (THEME_USE_GLOBAL.equals(value)) { return K9.Theme.USE_GLOBAL; } return super.fromPrettyString(value); } @Override public String toPrettyString(K9.Theme value) { if (value == K9.Theme.USE_GLOBAL) { return THEME_USE_GLOBAL; } return super.toPrettyString(value); } } private static class TimeSetting extends SettingsDescription<String> { TimeSetting(String defaultValue) { super(defaultValue); } @Override public String fromString(String value) throws InvalidSettingValueException { if (!value.matches(TimePickerPreference.VALIDATION_EXPRESSION)) { throw new InvalidSettingValueException(); } return value; } } private static class DirectorySetting extends SettingsDescription<String> { DirectorySetting(File defaultPath) { super(defaultPath.toString()); } @Override public String fromString(String value) throws InvalidSettingValueException { try { if (new File(value).isDirectory()) { return value; } } catch (Exception e) { /* do nothing */ } throw new InvalidSettingValueException(); } } }