package cgeo.geocaching.settings; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.os.Build; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import java.io.File; import java.io.FileFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.apps.navi.NavigationAppFactory.NavigationAppsEnum; import cgeo.geocaching.connector.capability.ICredentials; import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.connector.gc.GCConstants; import cgeo.geocaching.connector.gc.GCMemberState; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.list.StoredList; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.log.LogTypeTrackable; import cgeo.geocaching.log.TrackableComparator; import cgeo.geocaching.maps.LivemapStrategy; import cgeo.geocaching.maps.MapMode; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.maps.google.v1.GoogleMapProvider; import cgeo.geocaching.maps.interfaces.GeoPointImpl; import cgeo.geocaching.maps.interfaces.MapProvider; import cgeo.geocaching.maps.interfaces.MapSource; import cgeo.geocaching.maps.mapsforge.MapsforgeMapProvider; import cgeo.geocaching.maps.mapsforge.MapsforgeMapProvider.OfflineMapSource; import cgeo.geocaching.maps.routing.Routing; import cgeo.geocaching.maps.routing.RoutingMode; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.playservices.GooglePlayServices; import cgeo.geocaching.sensors.MagnetometerAndAccelerometerProvider; import cgeo.geocaching.sensors.OrientationProvider; import cgeo.geocaching.sensors.RotationProvider; import cgeo.geocaching.storage.DataStore; import cgeo.geocaching.storage.LocalStorage; import cgeo.geocaching.utils.CryptUtils; import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.FileUtils.FileSelector; import cgeo.geocaching.utils.Log; /** * General c:geo preferences/settings set by the user */ public class Settings { /** * On opening a map, we limit the _initial_ zoom. The user can still zoom out afterwards. */ private static final int INITIAL_MAP_ZOOM_LIMIT = 16; /** * Separator char for preferences with multiple elements. */ private static final char SEPARATOR_CHAR = ','; private static final int SHOW_WP_THRESHOLD_DEFAULT = 10; public static final int SHOW_WP_THRESHOLD_MAX = 50; private static final int MAP_SOURCE_DEFAULT = GoogleMapProvider.GOOGLE_MAP_ID.hashCode(); private static final String PHONE_MODEL_AND_SDK = Build.MODEL + "/" + Build.VERSION.SDK_INT; // twitter api keys @NonNull private static final String TWITTER_KEY_CONSUMER_PUBLIC = CryptUtils.rot13("ESnsCvAv3kEupF1GCR3jGj"); @NonNull private static final String TWITTER_KEY_CONSUMER_SECRET = CryptUtils.rot13("7vQWceACV9umEjJucmlpFe9FCMZSeqIqfkQ2BnhV9x"); private static boolean useCompass = true; public enum CoordInputFormatEnum { Plain, Deg, Min, Sec; static final int DEFAULT_INT_VALUE = Min.ordinal(); public static CoordInputFormatEnum fromInt(final int id) { final CoordInputFormatEnum[] values = CoordInputFormatEnum.values(); if (id < 0 || id >= values.length) { return Min; } return values[id]; } } private static final SharedPreferences sharedPrefs = PreferenceManager .getDefaultSharedPreferences(CgeoApplication.getInstance().getBaseContext()); static { migrateSettings(); Log.setDebug(sharedPrefs.getBoolean(getKey(R.string.pref_debug), false)); } /** * Cache the mapsource locally. If that is an offline map source, each request would potentially access the * underlying map file, leading to delays. */ private static MapSource mapSource; protected Settings() { throw new InstantiationError(); } private static void migrateSettings() { final int latestPreferencesVersion = 3; final int currentVersion = getInt(R.string.pref_settingsversion, 0); // No need to migrate if we are up to date. if (currentVersion == latestPreferencesVersion) { return; } // No need to migrate if we don't have older settings, defaults will be used instead. final String preferencesNameV0 = "cgeo.pref"; final SharedPreferences prefsV0 = CgeoApplication.getInstance().getSharedPreferences(preferencesNameV0, Context.MODE_PRIVATE); if (currentVersion == 0 && prefsV0.getAll().isEmpty()) { final Editor e = sharedPrefs.edit(); e.putInt(getKey(R.string.pref_settingsversion), latestPreferencesVersion); e.apply(); return; } if (currentVersion < 1) { // migrate from non standard file location and integer based boolean types final Editor e = sharedPrefs.edit(); e.putString(getKey(R.string.pref_temp_twitter_token_secret), prefsV0.getString(getKey(R.string.pref_temp_twitter_token_secret), null)); e.putString(getKey(R.string.pref_temp_twitter_token_public), prefsV0.getString(getKey(R.string.pref_temp_twitter_token_public), null)); e.putBoolean(getKey(R.string.pref_help_shown), prefsV0.getInt(getKey(R.string.pref_help_shown), 0) != 0); e.putFloat(getKey(R.string.pref_anylongitude), prefsV0.getFloat(getKey(R.string.pref_anylongitude), 0)); e.putFloat(getKey(R.string.pref_anylatitude), prefsV0.getFloat(getKey(R.string.pref_anylatitude), 0)); e.putBoolean(getKey(R.string.pref_offlinemaps), prefsV0.getInt(getKey(R.string.pref_offlinemaps), 1) != 0); e.putBoolean(getKey(R.string.pref_offlinewpmaps), prefsV0.getInt(getKey(R.string.pref_offlinewpmaps), 0) != 0); e.putString(getKey(R.string.pref_webDeviceCode), prefsV0.getString(getKey(R.string.pref_webDeviceCode), null)); e.putString(getKey(R.string.pref_webDeviceName), prefsV0.getString(getKey(R.string.pref_webDeviceName), null)); e.putBoolean(getKey(R.string.pref_maplive), prefsV0.getInt(getKey(R.string.pref_maplive), 1) != 0); e.putInt(getKey(R.string.pref_mapsource), prefsV0.getInt(getKey(R.string.pref_mapsource), MAP_SOURCE_DEFAULT)); e.putBoolean(getKey(R.string.pref_twitter), prefsV0.getInt(getKey(R.string.pref_twitter), 0) != 0); e.putBoolean(getKey(R.string.pref_showaddress), prefsV0.getInt(getKey(R.string.pref_showaddress), 1) != 0); e.putBoolean(getKey(R.string.pref_maptrail), prefsV0.getInt(getKey(R.string.pref_maptrail), 1) != 0); e.putInt(getKey(R.string.pref_lastmapzoom), prefsV0.getInt(getKey(R.string.pref_lastmapzoom), 14)); e.putBoolean(getKey(R.string.pref_livelist), prefsV0.getInt(getKey(R.string.pref_livelist), 1) != 0); e.putBoolean(getKey(R.string.pref_units_imperial), prefsV0.getInt(getKey(R.string.pref_units_imperial), 1) != 1); e.putBoolean(getKey(R.string.pref_skin), prefsV0.getInt(getKey(R.string.pref_skin), 0) != 0); e.putInt(getKey(R.string.pref_lastusedlist), prefsV0.getInt(getKey(R.string.pref_lastusedlist), StoredList.STANDARD_LIST_ID)); e.putString(getKey(R.string.pref_cachetype), prefsV0.getString(getKey(R.string.pref_cachetype), CacheType.ALL.id)); e.putString(getKey(R.string.pref_twitter_token_secret), prefsV0.getString(getKey(R.string.pref_twitter_token_secret), null)); e.putString(getKey(R.string.pref_twitter_token_public), prefsV0.getString(getKey(R.string.pref_twitter_token_public), null)); e.putInt(getKey(R.string.pref_version), prefsV0.getInt(getKey(R.string.pref_version), 0)); e.putBoolean(getKey(R.string.pref_ratingwanted), prefsV0.getBoolean(getKey(R.string.pref_ratingwanted), true)); e.putBoolean(getKey(R.string.pref_friendlogswanted), prefsV0.getBoolean(getKey(R.string.pref_friendlogswanted), true)); e.putBoolean(getKey(R.string.pref_useenglish), prefsV0.getBoolean(getKey(R.string.pref_useenglish), false)); e.putBoolean(getKey(R.string.pref_usecompass), prefsV0.getInt(getKey(R.string.pref_usecompass), 1) != 0); e.putBoolean(getKey(R.string.pref_trackautovisit), prefsV0.getBoolean(getKey(R.string.pref_trackautovisit), false)); e.putBoolean(getKey(R.string.pref_sigautoinsert), prefsV0.getBoolean(getKey(R.string.pref_sigautoinsert), false)); e.putBoolean(getKey(R.string.pref_logimages), prefsV0.getBoolean(getKey(R.string.pref_logimages), false)); e.putBoolean(getKey(R.string.pref_excludedisabled), prefsV0.getInt(getKey(R.string.pref_excludedisabled), 0) != 0); e.putBoolean(getKey(R.string.pref_excludemine), prefsV0.getInt(getKey(R.string.pref_excludemine), 0) != 0); e.putString(getKey(R.string.pref_mapfile), prefsV0.getString(getKey(R.string.pref_mapfile), null)); e.putString(getKey(R.string.pref_signature), prefsV0.getString(getKey(R.string.pref_signature), null)); e.putString(getKey(R.string.pref_pass_vote), prefsV0.getString(getKey(R.string.pref_pass_vote), null)); e.putString(getKey(R.string.pref_password), prefsV0.getString(getKey(R.string.pref_password), null)); e.putString(getKey(R.string.pref_username), prefsV0.getString(getKey(R.string.pref_username), null)); e.putString(getKey(R.string.pref_memberstatus), prefsV0.getString(getKey(R.string.pref_memberstatus), "")); e.putInt(getKey(R.string.pref_coordinputformat), prefsV0.getInt(getKey(R.string.pref_coordinputformat), CoordInputFormatEnum.DEFAULT_INT_VALUE)); e.putBoolean(getKey(R.string.pref_log_offline), prefsV0.getBoolean(getKey(R.string.pref_log_offline), false)); e.putBoolean(getKey(R.string.pref_choose_list), prefsV0.getBoolean(getKey(R.string.pref_choose_list), true)); e.putBoolean(getKey(R.string.pref_loaddirectionimg), prefsV0.getBoolean(getKey(R.string.pref_loaddirectionimg), true)); e.putString(getKey(R.string.pref_gccustomdate), prefsV0.getString(getKey(R.string.pref_gccustomdate), GCConstants.DEFAULT_GC_DATE)); e.putInt(getKey(R.string.pref_showwaypointsthreshold), prefsV0.getInt(getKey(R.string.pref_showwaypointsthreshold), SHOW_WP_THRESHOLD_DEFAULT)); e.putBoolean(getKey(R.string.pref_opendetailslastpage), prefsV0.getBoolean(getKey(R.string.pref_opendetailslastpage), false)); e.putInt(getKey(R.string.pref_lastdetailspage), prefsV0.getInt(getKey(R.string.pref_lastdetailspage), 1)); e.putInt(getKey(R.string.pref_defaultNavigationTool), prefsV0.getInt(getKey(R.string.pref_defaultNavigationTool), NavigationAppsEnum.COMPASS.id)); e.putInt(getKey(R.string.pref_defaultNavigationTool2), prefsV0.getInt(getKey(R.string.pref_defaultNavigationTool2), NavigationAppsEnum.INTERNAL_MAP.id)); e.putInt(getKey(R.string.pref_livemapstrategy), prefsV0.getInt(getKey(R.string.pref_livemapstrategy), LivemapStrategy.AUTO.id)); e.putBoolean(getKey(R.string.pref_debug), prefsV0.getBoolean(getKey(R.string.pref_debug), false)); e.putInt(getKey(R.string.pref_livemaphintshowcount), prefsV0.getInt(getKey(R.string.pref_livemaphintshowcount), 0)); e.putInt(getKey(R.string.pref_settingsversion), 1); // mark migrated e.apply(); } // changes for new settings dialog if (currentVersion < 2) { final Editor e = sharedPrefs.edit(); e.putBoolean(getKey(R.string.pref_units_imperial), useImperialUnits()); // show waypoints threshold now as a slider int wpThreshold = getWayPointsThreshold(); if (wpThreshold < 0) { wpThreshold = 0; } else if (wpThreshold > SHOW_WP_THRESHOLD_MAX) { wpThreshold = SHOW_WP_THRESHOLD_MAX; } e.putInt(getKey(R.string.pref_showwaypointsthreshold), wpThreshold); // KEY_MAP_SOURCE must be string, because it is the key for a ListPreference now final int ms = sharedPrefs.getInt(getKey(R.string.pref_mapsource), MAP_SOURCE_DEFAULT); e.remove(getKey(R.string.pref_mapsource)); e.putString(getKey(R.string.pref_mapsource), String.valueOf(ms)); // navigation tool ids must be string, because ListPreference uses strings as keys final int dnt1 = sharedPrefs.getInt(getKey(R.string.pref_defaultNavigationTool), NavigationAppsEnum.COMPASS.id); final int dnt2 = sharedPrefs.getInt(getKey(R.string.pref_defaultNavigationTool2), NavigationAppsEnum.INTERNAL_MAP.id); e.remove(getKey(R.string.pref_defaultNavigationTool)); e.remove(getKey(R.string.pref_defaultNavigationTool2)); e.putString(getKey(R.string.pref_defaultNavigationTool), String.valueOf(dnt1)); e.putString(getKey(R.string.pref_defaultNavigationTool2), String.valueOf(dnt2)); // defaults for gpx directories e.putString(getKey(R.string.pref_gpxImportDir), LocalStorage.getDefaultGpxDirectory().getPath()); e.putString(getKey(R.string.pref_gpxExportDir), LocalStorage.getDefaultGpxDirectory().getPath()); e.putInt(getKey(R.string.pref_settingsversion), 2); // mark migrated e.apply(); } if (currentVersion < 3) { final Editor e = sharedPrefs.edit(); Log.i("Moving field-notes"); FileUtils.move(LocalStorage.getLegacyFieldNotesDirectory(), LocalStorage.getFieldNotesDirectory()); Log.i("Moving gpx ex- and import dirs"); if (getGpxExportDir().equals(LocalStorage.getLegacyGpxDirectory().getPath())) { e.putString(getKey(R.string.pref_gpxExportDir), LocalStorage.getDefaultGpxDirectory().getPath()); } if (getGpxImportDir().equals(LocalStorage.getLegacyGpxDirectory().getPath())) { e.putString(getKey(R.string.pref_gpxImportDir), LocalStorage.getDefaultGpxDirectory().getPath()); } FileUtils.move(LocalStorage.getLegacyGpxDirectory(), LocalStorage.getDefaultGpxDirectory()); Log.i("Moving local spoilers"); FileUtils.move(LocalStorage.getLegacyLocalSpoilersDirectory(), LocalStorage.getLocalSpoilersDirectory()); Log.i("Moving db files"); FileUtils.moveTo(new File(LocalStorage.getLegacyExternalCgeoDirectory(), DataStore.DB_FILE_NAME), LocalStorage.getExternalDbDirectory()); FileUtils.moveTo(new File(LocalStorage.getLegacyExternalCgeoDirectory(), DataStore.DB_FILE_NAME + DataStore.DB_FILE_CORRUPTED_EXTENSION), LocalStorage.getBackupDirectory()); FileUtils.moveTo(new File(LocalStorage.getLegacyExternalCgeoDirectory(), DataStore.DB_FILE_NAME_BACKUP), LocalStorage.getBackupDirectory()); FileUtils.moveTo(new File(LocalStorage.getLegacyExternalCgeoDirectory(), DataStore.DB_FILE_NAME_BACKUP + DataStore.DB_FILE_CORRUPTED_EXTENSION), LocalStorage.getBackupDirectory()); Log.i("Moving geocache data"); final FileFilter geocacheDirectories = new FileFilter() { @Override public boolean accept(final File pathname) { final String name = pathname.getName(); return pathname.isDirectory() && (HtmlImage.SHARED.equals(name) || LocalStorage.GEOCACHE_FILE_PATTERN.matcher(name).find()); } }; final File[] list = LocalStorage.getLegacyExternalCgeoDirectory().listFiles(geocacheDirectories); if (list != null) { for (final File file : list) { FileUtils.moveTo(file, LocalStorage.getGeocacheDataDirectory()); } } Log.i("Deleting legacy .cgeo dir"); FileUtils.deleteIgnoringFailure(LocalStorage.getLegacyExternalCgeoDirectory()); e.putString(getKey(R.string.pref_dataDir), LocalStorage.getExternalPrivateCgeoDirectory().getAbsolutePath()); e.putInt(getKey(R.string.pref_settingsversion), 3); // mark migrated e.apply(); } } private static String getKey(final int prefKeyId) { return CgeoApplication.getInstance().getString(prefKeyId); } static String getString(final int prefKeyId, final String defaultValue) { return sharedPrefs.getString(getKey(prefKeyId), defaultValue); } private static List<String> getStringList(final int prefKeyId, final String defaultValue) { return Arrays.asList(StringUtils.split(getString(prefKeyId, defaultValue), SEPARATOR_CHAR)); } private static int getInt(final int prefKeyId, final int defaultValue) { return sharedPrefs.getInt(getKey(prefKeyId), defaultValue); } private static long getLong(final int prefKeyId, final long defaultValue) { return sharedPrefs.getLong(getKey(prefKeyId), defaultValue); } private static boolean getBoolean(final int prefKeyId, final boolean defaultValue) { return sharedPrefs.getBoolean(getKey(prefKeyId), defaultValue); } private static float getFloat(final int prefKeyId, final float defaultValue) { return sharedPrefs.getFloat(getKey(prefKeyId), defaultValue); } protected static void putString(final int prefKeyId, final String value) { final SharedPreferences.Editor edit = sharedPrefs.edit(); edit.putString(getKey(prefKeyId), value); edit.apply(); } private static void putStringList(final int prefKeyId, final Iterable<?> elements) { putString(prefKeyId, StringUtils.join(elements, SEPARATOR_CHAR)); } protected static void putBoolean(final int prefKeyId, final boolean value) { final SharedPreferences.Editor edit = sharedPrefs.edit(); edit.putBoolean(getKey(prefKeyId), value); edit.apply(); } private static void putInt(final int prefKeyId, final int value) { final SharedPreferences.Editor edit = sharedPrefs.edit(); edit.putInt(getKey(prefKeyId), value); edit.apply(); } private static void putLong(final int prefKeyId, final long value) { final SharedPreferences.Editor edit = sharedPrefs.edit(); edit.putLong(getKey(prefKeyId), value); edit.apply(); } private static void putFloat(final int prefKeyId, final float value) { final SharedPreferences.Editor edit = sharedPrefs.edit(); edit.putFloat(getKey(prefKeyId), value); edit.apply(); } private static void remove(final int prefKeyId) { final SharedPreferences.Editor edit = sharedPrefs.edit(); edit.remove(getKey(prefKeyId)); edit.apply(); } private static boolean contains(final int prefKeyId) { return sharedPrefs.contains(getKey(prefKeyId)); } public static boolean hasGCCredentials() { return getGcCredentials().isValid(); } /** * Get login and password information of Geocaching.com. * * @return a pair either with (login, password) or (empty, empty) if no valid information is stored */ public static Credentials getGcCredentials() { return getCredentials(GCConnector.getInstance()); } /** * Get login and password information from preference key id. * * @param usernamePreferenceKey Username preference key id * @param passwordPreferenceKey Password preference key id * @return the credential information */ @NonNull public static Credentials getCredentials(final int usernamePreferenceKey, final int passwordPreferenceKey) { final String username = StringUtils.trim(getString(usernamePreferenceKey, StringUtils.EMPTY)); final String password = getString(passwordPreferenceKey, StringUtils.EMPTY); return new Credentials(username, password); } /** * Get login and password information. * * @param connector the connector to retrieve the login information from * @return the credential information */ @NonNull public static Credentials getCredentials(@NonNull final ICredentials connector) { return getCredentials(connector.getUsernamePreferenceKey(), connector.getPasswordPreferenceKey()); } /** * Set login and password information. * * @param connector the connector to retrieve the login information from * @param credentials the credential information to store */ public static void setCredentials(@NonNull final ICredentials connector, final Credentials credentials) { putString(connector.getUsernamePreferenceKey(), credentials.getUsernameRaw()); putString(connector.getPasswordPreferenceKey(), credentials.getPasswordRaw()); } public static String getUserName() { return StringUtils.trim(getString(R.string.pref_username, StringUtils.EMPTY)); } public static boolean isGCConnectorActive() { return getBoolean(R.string.pref_connectorGCActive, true); } public static boolean isECConnectorActive() { return getBoolean(R.string.pref_connectorECActive, false); } public static boolean isSUConnectorActive() { return getBoolean(R.string.pref_connectorSUActive, false); } public static boolean isGCPremiumMember() { return getGCMemberStatus().isPremium(); } public static GCMemberState getGCMemberStatus() { return GCMemberState.fromString(getString(R.string.pref_memberstatus, "")); } public static void setGCMemberStatus(final GCMemberState memberStatus) { putString(R.string.pref_memberstatus, memberStatus.englishWebsite); } @NonNull public static ImmutablePair<String, String> getTokenPair(final int tokenPublicPrefKey, final int tokenSecretPrefKey) { return new ImmutablePair<>(getString(tokenPublicPrefKey, null), getString(tokenSecretPrefKey, null)); } public static void setTokens(final int tokenPublicPrefKey, @Nullable final String tokenPublic, final int tokenSecretPrefKey, @Nullable final String tokenSecret) { if (tokenPublic == null) { remove(tokenPublicPrefKey); } else { putString(tokenPublicPrefKey, tokenPublic); } if (tokenSecret == null) { remove(tokenSecretPrefKey); } else { putString(tokenSecretPrefKey, tokenSecret); } } public static boolean isOCConnectorActive(final int isActivePrefKeyId) { return getBoolean(isActivePrefKeyId, false); } public static boolean hasOCAuthorization(final int tokenPublicPrefKeyId, final int tokenSecretPrefKeyId) { return StringUtils.isNotBlank(getString(tokenPublicPrefKeyId, "")) && StringUtils.isNotBlank(getString(tokenSecretPrefKeyId, "")); } public static boolean isGCVoteLoginValid() { return getGCVoteLogin().isValid(); } @NonNull public static Credentials getGCVoteLogin() { final String username = StringUtils.trimToNull(getString(R.string.pref_username, null)); final String password = getString(R.string.pref_pass_vote, null); return new Credentials(username, password); } @NonNull public static String getSignature() { return StringUtils.defaultString(getString(R.string.pref_signature, StringUtils.EMPTY)); } public static void setUseGooglePlayServices(final boolean value) { putBoolean(R.string.pref_googleplayservices, value); } public static boolean useGooglePlayServices() { if (!GooglePlayServices.isAvailable()) { return false; } return outdatedPhoneModelOrSdk() || getBoolean(R.string.pref_googleplayservices, true); } public static boolean useLowPowerMode() { return getBoolean(R.string.pref_lowpowermode, false); } /** * @param cacheType * The cache type used for future filtering */ public static void setCacheType(final CacheType cacheType) { if (cacheType == null) { remove(R.string.pref_cachetype); } else { putString(R.string.pref_cachetype, cacheType.id); } } public static int getLastDisplayedList() { return getInt(R.string.pref_lastusedlist, StoredList.STANDARD_LIST_ID); } /** * remember the last displayed cache list */ public static void setLastDisplayedList(final int listId) { putInt(R.string.pref_lastusedlist, listId); } public static Set<Integer> getLastSelectedLists() { final Set<Integer> lastSelectedLists = new HashSet<>(); for (final String lastSelectedListString : getStringList(R.string.pref_last_selected_lists, StringUtils.EMPTY)) { try { lastSelectedLists.add(Integer.valueOf(lastSelectedListString)); } catch (final NumberFormatException ignored) { } } return lastSelectedLists; } /** * remember the last selection in the dialog that assigns a cache to certain lists */ public static void setLastSelectedLists(final Set<Integer> lastSelectedLists) { putStringList(R.string.pref_last_selected_lists, lastSelectedLists); } public static void setWebNameCode(final String name, final String code) { putString(R.string.pref_webDeviceName, name); putString(R.string.pref_webDeviceCode, code); } public static MapProvider getMapProvider() { return getMapSource().getMapProvider(); } public static String getMapFile() { return getString(R.string.pref_mapfile, null); } static void setMapFile(final String mapFile) { putString(R.string.pref_mapfile, mapFile); if (mapFile != null) { setMapFileDirectory(new File(mapFile).getParent()); } } public static String getMapFileDirectory() { final String mapDir = getString(R.string.pref_mapDirectory, null); if (mapDir != null) { return mapDir; } final String mapFile = getMapFile(); if (mapFile != null) { return new File(mapFile).getParent(); } return null; } static void setMapFileDirectory(final String mapFileDirectory) { putString(R.string.pref_mapDirectory, mapFileDirectory); MapsforgeMapProvider.getInstance().updateOfflineMaps(); } private static boolean isValidMapFile() { return isValidMapFile(getMapFile()); } public static boolean isValidMapFile(final String mapFileIn) { return MapsforgeMapProvider.isValidMapFile(mapFileIn); } public static boolean isScaleMapsforgeText() { return getBoolean(R.string.pref_mapsforge_scale_text, true); } public static CoordInputFormatEnum getCoordInputFormat() { return CoordInputFormatEnum.fromInt(getInt(R.string.pref_coordinputformat, CoordInputFormatEnum.DEFAULT_INT_VALUE)); } public static void setCoordInputFormat(final CoordInputFormatEnum format) { putInt(R.string.pref_coordinputformat, format.ordinal()); } public static boolean getLogOffline() { return getBoolean(R.string.pref_log_offline, false); } public static boolean getChooseList() { return getBoolean(R.string.pref_choose_list, false); } public static boolean getLoadDirImg() { return !isGCPremiumMember() && getBoolean(R.string.pref_loaddirectionimg, true); } public static void setGcCustomDate(final String format) { putString(R.string.pref_gccustomdate, format); } /** * @return User selected date format on GC.com */ public static String getGcCustomDate() { // We might have some users whose stored value is null, which is invalid. In this case, we use the default. return StringUtils.defaultString(getString(R.string.pref_gccustomdate, GCConstants.DEFAULT_GC_DATE), GCConstants.DEFAULT_GC_DATE); } public static boolean isExcludeMyCaches() { return getBoolean(R.string.pref_excludemine, false); } public static boolean useEnglish() { return getBoolean(R.string.pref_useenglish, false); } public static void setUseEnglish(final boolean useEnglish) { putBoolean(R.string.pref_useenglish, useEnglish); } public static boolean isShowAddress() { return getBoolean(R.string.pref_showaddress, true); } public static boolean isExcludeDisabledCaches() { return getBoolean(R.string.pref_excludedisabled, false); } public static boolean isStoreOfflineMaps() { return getBoolean(R.string.pref_offlinemaps, true); } public static boolean isStoreOfflineWpMaps() { return getBoolean(R.string.pref_offlinewpmaps, false); } public static boolean isStoreLogImages() { return getBoolean(R.string.pref_logimages, false); } public static boolean isRatingWanted() { return getBoolean(R.string.pref_ratingwanted, true); } public static boolean isGeokretyConnectorActive() { return getBoolean(R.string.pref_connectorGeokretyActive, false); } public static boolean isGeokretyCacheActive() { return getBoolean(R.string.pref_geokrety_cache, true); } static boolean hasGeokretyAuthorization() { return StringUtils.isNotBlank(getGeokretySecId()); } public static String getGeokretySecId() { return getString(R.string.pref_fakekey_geokrety_authorization, null); } public static void setGeokretySecId(final String secid) { putString(R.string.pref_fakekey_geokrety_authorization, secid); } public static String getTokenSecret(final int prefKeyId) { return getString(prefKeyId, StringUtils.EMPTY); } public static void setTokenSecret(final int prefKeyId, final String secretToken) { putString(prefKeyId, secretToken); } public static boolean isRegisteredForGeokretyLogging() { return getGeokretySecId() != null; } /** * Retrieve showed popup counter for warning about logging Trackable recommend Geocode * * @return number of times the popup has appeared */ public static int getLogTrackableWithoutGeocodeShowCount() { return getInt(R.string.pref_logtrackablewithoutgeocodeshowcount, 0); } /** * Store showed popup counter for warning about logging Trackable recommend Geocode * * @param showCount the count to save */ public static void setLogTrackableWithoutGeocodeShowCount(final int showCount) { putInt(R.string.pref_logtrackablewithoutgeocodeshowcount, showCount); } public static boolean isFriendLogsWanted() { if (!hasGCCredentials()) { // don't show a friends log if the user is anonymous return false; } return getBoolean(R.string.pref_friendlogswanted, true); } public static boolean isLiveList() { return getBoolean(R.string.pref_livelist, true); } public static boolean isTrackableAutoVisit() { return getBoolean(R.string.pref_trackautovisit, false); } public static boolean isAutoInsertSignature() { return getBoolean(R.string.pref_sigautoinsert, false); } static void setUseImperialUnits(final boolean useImperialUnits) { putBoolean(R.string.pref_units_imperial, useImperialUnits); } public static boolean useImperialUnits() { return getBoolean(R.string.pref_units_imperial, useImperialUnitsByDefault()); } private static boolean useImperialUnitsByDefault() { final String countryCode = Locale.getDefault().getCountry(); return "US".equals(countryCode) // USA || "LR".equals(countryCode) // Liberia || "MM".equals(countryCode); // Burma } public static boolean isLiveMap() { return getBoolean(R.string.pref_maplive, true); } public static void setLiveMap(final boolean live) { putBoolean(R.string.pref_maplive, live); } public static boolean isMapTrail() { return getBoolean(R.string.pref_maptrail, false); } public static void setMapTrail(final boolean showTrail) { putBoolean(R.string.pref_maptrail, showTrail); } /** * whether to show a direction line on the map */ public static boolean isMapDirection() { return getBoolean(R.string.pref_map_direction, true); } public static void setMapDirection(final boolean showDirection) { putBoolean(R.string.pref_map_direction, showDirection); } /** * Get last used zoom of the internal map. Differentiate between two use cases for a map of multiple caches (e.g. * live map) and the map of a single cache (which is often zoomed in more deep). */ public static int getMapZoom(final MapMode mapMode) { if (mapMode == MapMode.SINGLE || mapMode == MapMode.COORDS) { return getCacheZoom(); } return getMapZoom(); } public static void setMapZoom(final MapMode mapMode, final int zoomLevel) { if (mapMode == MapMode.SINGLE || mapMode == MapMode.COORDS) { setCacheZoom(zoomLevel); } else { setMapZoom(zoomLevel); } } /** * @return zoom used for the (live) map */ private static int getMapZoom() { return Math.max(getInt(R.string.pref_lastmapzoom, 14), INITIAL_MAP_ZOOM_LIMIT); } private static void setMapZoom(final int mapZoomLevel) { putInt(R.string.pref_lastmapzoom, mapZoomLevel); } /** * @return zoom used for the map of a single cache */ private static int getCacheZoom() { return Math.max(getInt(R.string.pref_cache_zoom, 14), INITIAL_MAP_ZOOM_LIMIT); } private static void setCacheZoom(final int zoomLevel) { putInt(R.string.pref_cache_zoom, zoomLevel); } public static GeoPointImpl getMapCenter() { return getMapProvider().getMapItemFactory() .getGeoPointBase(new Geopoint(getInt(R.string.pref_lastmaplat, 0) / 1e6, getInt(R.string.pref_lastmaplon, 0) / 1e6)); } public static void setMapCenter(final GeoPointImpl mapViewCenter) { putInt(R.string.pref_lastmaplat, mapViewCenter.getLatitudeE6()); putInt(R.string.pref_lastmaplon, mapViewCenter.getLongitudeE6()); } @NonNull public static synchronized MapSource getMapSource() { if (mapSource != null) { return mapSource; } final int id = getConvertedMapId(); mapSource = MapProviderFactory.getMapSource(id); if (mapSource != null) { // don't use offline maps if the map file is not valid if (!(mapSource instanceof OfflineMapSource) || isValidMapFile()) { return mapSource; } } // fallback to first available map return MapProviderFactory.getDefaultSource(); } private static final int GOOGLEMAP_BASEID = 30; private static final int MAP = 1; private static final int SATELLITE = 2; private static final int MFMAP_BASEID = 40; private static final int MAPNIK = 1; private static final int CYCLEMAP = 3; private static final int OFFLINE = 4; private static final int HISTORY_SIZE = 10; /** * Convert old preference ids for maps (based on constant values) into new hash based ids. */ private static int getConvertedMapId() { final int id = Integer.parseInt(getString(R.string.pref_mapsource, String.valueOf(MAP_SOURCE_DEFAULT))); switch (id) { case GOOGLEMAP_BASEID + MAP: return GoogleMapProvider.GOOGLE_MAP_ID.hashCode(); case GOOGLEMAP_BASEID + SATELLITE: return GoogleMapProvider.GOOGLE_SATELLITE_ID.hashCode(); case MFMAP_BASEID + MAPNIK: return MapsforgeMapProvider.MAPSFORGE_MAPNIK_ID.hashCode(); case MFMAP_BASEID + CYCLEMAP: return MapsforgeMapProvider.MAPSFORGE_CYCLEMAP_ID.hashCode(); case MFMAP_BASEID + OFFLINE: { final String mapFile = getMapFile(); if (StringUtils.isNotEmpty(mapFile)) { return mapFile.hashCode(); } break; } default: break; } return id; } public static synchronized void setMapSource(final MapSource newMapSource) { putString(R.string.pref_mapsource, String.valueOf(newMapSource.getNumericalId())); if (newMapSource instanceof OfflineMapSource) { setMapFile(((OfflineMapSource) newMapSource).getFileName()); } // cache the value mapSource = newMapSource; } public static void setAnyCoordinates(final Geopoint coords) { if (coords != null) { putFloat(R.string.pref_anylatitude, (float) coords.getLatitude()); putFloat(R.string.pref_anylongitude, (float) coords.getLongitude()); } else { remove(R.string.pref_anylatitude); remove(R.string.pref_anylongitude); } } public static Geopoint getAnyCoordinates() { if (contains(R.string.pref_anylatitude) && contains(R.string.pref_anylongitude)) { final float lat = getFloat(R.string.pref_anylatitude, 0); final float lon = getFloat(R.string.pref_anylongitude, 0); return new Geopoint(lat, lon); } return null; } public static boolean isUseCompass() { return useCompass; } public static void setUseCompass(final boolean value) { useCompass = value; } public static boolean isLightSkin() { return getBoolean(R.string.pref_skin, false); } @NonNull public static String getTwitterKeyConsumerPublic() { return TWITTER_KEY_CONSUMER_PUBLIC; } @NonNull public static String getTwitterKeyConsumerSecret() { return TWITTER_KEY_CONSUMER_SECRET; } public static String getWebDeviceCode() { return getString(R.string.pref_webDeviceCode, null); } public static boolean isRegisteredForSend2cgeo() { return getWebDeviceCode() != null; } static String getWebDeviceName() { return getString(R.string.pref_webDeviceName, Build.MODEL); } /** * @return The cache type used for filtering or ALL if no filter is active. * Returns never null */ @NonNull public static CacheType getCacheType() { return CacheType.getById(getString(R.string.pref_cachetype, CacheType.ALL.id)); } /** * The Threshold for the showing of child waypoints */ public static int getWayPointsThreshold() { return getInt(R.string.pref_showwaypointsthreshold, SHOW_WP_THRESHOLD_DEFAULT); } static void setShowWaypointsThreshold(final int threshold) { putInt(R.string.pref_showwaypointsthreshold, threshold); } public static boolean isUseTwitter() { return getBoolean(R.string.pref_twitter, false); } private static void setUseTwitter(final boolean useTwitter) { putBoolean(R.string.pref_twitter, useTwitter); } public static boolean isTwitterLoginValid() { return !StringUtils.isBlank(getTokenPublic()) && !StringUtils.isBlank(getTokenSecret()); } public static String getTokenPublic() { return getString(R.string.pref_twitter_token_public, null); } public static String getTokenSecret() { return getString(R.string.pref_twitter_token_secret, null); } static boolean hasTwitterAuthorization() { return StringUtils.isNotBlank(getTokenPublic()) && StringUtils.isNotBlank(getTokenSecret()); } public static void setTwitterTokens(@Nullable final String tokenPublic, @Nullable final String tokenSecret, final boolean enableTwitter) { putString(R.string.pref_twitter_token_public, tokenPublic); putString(R.string.pref_twitter_token_secret, tokenSecret); if (tokenPublic != null) { remove(R.string.pref_temp_twitter_token_public); remove(R.string.pref_temp_twitter_token_secret); } setUseTwitter(enableTwitter); } public static void setTwitterTempTokens(@Nullable final String tokenPublic, @Nullable final String tokenSecret) { putString(R.string.pref_temp_twitter_token_public, tokenPublic); putString(R.string.pref_temp_twitter_token_secret, tokenSecret); } @NonNull public static ImmutablePair<String, String> getTempToken() { final String tokenPublic = getString(R.string.pref_temp_twitter_token_public, null); final String tokenSecret = getString(R.string.pref_temp_twitter_token_secret, null); return new ImmutablePair<>(tokenPublic, tokenSecret); } public static int getVersion() { return getInt(R.string.pref_version, 0); } public static void setVersion(final int version) { putInt(R.string.pref_version, version); } public static boolean isOpenLastDetailsPage() { return getBoolean(R.string.pref_opendetailslastpage, false); } public static int getLastDetailsPage() { return getInt(R.string.pref_lastdetailspage, 1); } public static void setLastDetailsPage(final int index) { putInt(R.string.pref_lastdetailspage, index); } public static int getDefaultNavigationTool() { return Integer.parseInt(getString( R.string.pref_defaultNavigationTool, String.valueOf(NavigationAppsEnum.COMPASS.id))); } public static int getDefaultNavigationTool2() { return Integer.parseInt(getString( R.string.pref_defaultNavigationTool2, String.valueOf(NavigationAppsEnum.INTERNAL_MAP.id))); } @NonNull public static LivemapStrategy getLiveMapStrategy() { return LivemapStrategy.getById(getInt(R.string.pref_livemapstrategy, LivemapStrategy.AUTO.id)); } public static void setLiveMapStrategy(final LivemapStrategy strategy) { putInt(R.string.pref_livemapstrategy, strategy.id); } public static boolean isDebug() { return Log.isDebug(); } public static int getLiveMapHintShowCount() { return getInt(R.string.pref_livemaphintshowcount, 0); } public static void setLiveMapHintShowCount(final int showCount) { putInt(R.string.pref_livemaphintshowcount, showCount); } public static boolean isDbOnSDCard() { return getBoolean(R.string.pref_dbonsdcard, false); } public static void setDbOnSDCard(final boolean dbOnSDCard) { putBoolean(R.string.pref_dbonsdcard, dbOnSDCard); } public static String getGpxExportDir() { return getString(R.string.pref_gpxExportDir, LocalStorage.getDefaultGpxDirectory().getPath()); } public static String getGpxImportDir() { return getString(R.string.pref_gpxImportDir, LocalStorage.getDefaultGpxDirectory().getPath()); } public static String getExternalPrivateCgeoDirectory() { return getString(R.string.pref_dataDir, null); } public static void setExternalPrivateCgeoDirectory(final String extDir) { putString(R.string.pref_dataDir, extDir); } public static boolean getShareAfterExport() { return getBoolean(R.string.pref_shareafterexport, true); } public static void setShareAfterExport(final boolean shareAfterExport) { putBoolean(R.string.pref_shareafterexport, shareAfterExport); } public static boolean getIncludeFoundStatus() { return getBoolean(R.string.pref_includefoundstatus, true); } public static void setIncludeFoundStatus(final boolean includeFoundStatus) { putBoolean(R.string.pref_includefoundstatus, includeFoundStatus); } /** * Get Trackable inventory sort method based on the last Trackable inventory sort method. * * @return * The Trackable Sort Method previously used. */ public static TrackableComparator getTrackableComparator() { return TrackableComparator.findByName(getString(R.string.pref_trackable_inventory_sort, "")); } /** * Set Trackable inventory sort method. * * @param trackableSortMethod * The Trackable Sort Method to remember */ public static void setTrackableComparator(final TrackableComparator trackableSortMethod) { putString(R.string.pref_trackable_inventory_sort, trackableSortMethod.name()); } /** * Obtain Trackable action from the last Trackable log. * * @return * The last Trackable Action or RETRIEVED_IT */ public static int getTrackableAction() { return getInt(R.string.pref_trackableaction, LogTypeTrackable.RETRIEVED_IT.id); } /** * Save Trackable action from the last Trackable log. * * @param trackableAction * The Trackable Action to remember */ public static void setTrackableAction(final int trackableAction) { putInt(R.string.pref_trackableaction, trackableAction); } private static String getCustomRenderThemeBaseFolder() { return getString(R.string.pref_renderthemepath, ""); } public static String getCustomRenderThemeFilePath() { return getString(R.string.pref_renderthemefile, ""); } public static void setCustomRenderThemeFile(final String customRenderThemeFile) { putString(R.string.pref_renderthemefile, customRenderThemeFile); } public static File[] getMapThemeFiles() { final File directory = new File(getCustomRenderThemeBaseFolder()); final List<File> result = new ArrayList<>(); FileUtils.listDir(result, directory, new ExtensionsBasedFileSelector(new String[] { "xml" }), null); return result.toArray(new File[result.size()]); } private static class ExtensionsBasedFileSelector implements FileSelector { private final String[] extensions; ExtensionsBasedFileSelector(final String[] extensions) { this.extensions = extensions; } @Override public boolean isSelected(final File file) { final String filename = file.getName(); for (final String ext : extensions) { if (StringUtils.endsWithIgnoreCase(filename, ext)) { return true; } } return false; } @Override public boolean shouldEnd() { return false; } } /** * @return true if plain text log wanted */ public static boolean getPlainLogs() { return getBoolean(R.string.pref_plainLogs, false); } /** * Force set the plain text log preference * * @param plainLogs * wanted or not */ public static void setPlainLogs(final boolean plainLogs) { putBoolean(R.string.pref_plainLogs, plainLogs); } public static boolean getUseNativeUa() { return getBoolean(R.string.pref_nativeUa, false); } @NonNull public static String getCacheTwitterMessage() { return StringUtils.defaultString(getString(R.string.pref_twitter_cache_message, "I found [NAME] ([URL]).")); } @NonNull public static String getTrackableTwitterMessage() { return StringUtils.defaultString(getString(R.string.pref_twitter_trackable_message, "I touched [NAME] ([URL]).")); } public static int getLogImageScale() { return getInt(R.string.pref_logImageScale, -1); } public static void setLogImageScale(final int scale) { putInt(R.string.pref_logImageScale, scale); } public static void setExcludeMine(final boolean exclude) { putBoolean(R.string.pref_excludemine, exclude); } public static void setExcludeDisabled(final boolean exclude) { putBoolean(R.string.pref_excludedisabled, exclude); } static void setLogin(final String username, final String password) { if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { // erase username and password remove(R.string.pref_username); remove(R.string.pref_password); return; } // save username and password putString(R.string.pref_username, StringUtils.trim(username)); putString(R.string.pref_password, password); } public static long getFieldnoteExportDate() { return getLong(R.string.pref_fieldNoteExportDate, 0); } /** * Remember date of last field note export. */ public static void setFieldnoteExportDate(final long date) { putLong(R.string.pref_fieldNoteExportDate, date); } public static boolean isUseNavigationApp(final NavigationAppsEnum navApp) { return getBoolean(navApp.preferenceKey, true); } /** * Remember the state of the "Upload" checkbox in the field notes export dialog. */ public static void setFieldNoteExportUpload(final boolean upload) { putBoolean(R.string.pref_fieldNoteExportUpload, upload); } public static boolean getFieldNoteExportUpload() { return getBoolean(R.string.pref_fieldNoteExportUpload, true); } /** * Remember the state of the "Only new" checkbox in the field notes export dialog. */ public static void setFieldNoteExportOnlyNew(final boolean onlyNew) { putBoolean(R.string.pref_fieldNoteExportOnlyNew, onlyNew); } public static boolean getFieldNoteExportOnlyNew() { return getBoolean(R.string.pref_fieldNoteExportOnlyNew, false); } public static String getECIconSet() { return getString(R.string.pref_ec_icons, "1"); } /* Store last checksum of changelog for changelog display */ public static long getLastChangelogChecksum() { return getLong(R.string.pref_changelog_last_checksum, 0); } public static void setLastChangelogChecksum(final long checksum) { putLong(R.string.pref_changelog_last_checksum, checksum); } public static List<String> getLastOpenedCaches() { final List<String> history = getStringList(R.string.pref_caches_history, StringUtils.EMPTY); return history.subList(0, Math.min(HISTORY_SIZE, history.size())); } public static void addCacheToHistory(@NonNull final String geocode) { final List<String> history = new ArrayList<>(getLastOpenedCaches()); // bring entry to front, if it already existed history.remove(geocode); history.add(0, geocode); putStringList(R.string.pref_caches_history, history); } public static boolean useNewMapAsDefault() { return getBoolean(R.string.pref_new_map_as_default, false); } static void setUseNewMapAsDefault(final boolean useNewMapAsDefault) { putBoolean(R.string.pref_new_map_as_default, useNewMapAsDefault); } public static boolean useHardwareAcceleration() { return outdatedPhoneModelOrSdk() ? HwAccel.hwAccelShouldBeEnabled() : getBoolean(R.string.pref_hardware_acceleration, HwAccel.hwAccelShouldBeEnabled()); } static void setUseHardwareAcceleration(final boolean useHardwareAcceleration) { putBoolean(R.string.pref_hardware_acceleration, useHardwareAcceleration); storePhoneModelAndSdk(); } private static boolean outdatedPhoneModelOrSdk() { return !StringUtils.equals(PHONE_MODEL_AND_SDK, getString(R.string.pref_phone_model_and_sdk, null)); } private static void storePhoneModelAndSdk() { putString(R.string.pref_phone_model_and_sdk, PHONE_MODEL_AND_SDK); } public static String getLastCacheLog() { return getString(R.string.pref_last_cache_log, StringUtils.EMPTY); } public static void setLastCacheLog(final String log) { putString(R.string.pref_last_cache_log, log); } public static String getLastTrackableLog() { return getString(R.string.pref_last_trackable_log, StringUtils.EMPTY); } public static void setLastTrackableLog(final String log) { putString(R.string.pref_last_trackable_log, log); } @Nullable public static String getHomeLocation() { return getString(R.string.pref_home_location, null); } public static void setHomeLocation(@NonNull final String homeLocation) { putString(R.string.pref_home_location, homeLocation); } public static void setForceOrientationSensor(final boolean forceOrientationSensor) { putBoolean(R.string.pref_force_orientation_sensor, forceOrientationSensor); } public static boolean useOrientationSensor(final Context context) { return OrientationProvider.hasOrientationSensor(context) && (getBoolean(R.string.pref_force_orientation_sensor, false) || !(RotationProvider.hasRotationSensor(context) || MagnetometerAndAccelerometerProvider.hasMagnetometerAndAccelerometerSensors(context)) ); } /** * Get avatar URL by connector. * * @param connector the connector to retrieve the avatar information from * @return the avatar url */ @NonNull public static String getAvatarUrl(@NonNull final ICredentials connector) { return getString(connector.getAvatarPreferenceKey(), null); } /** * Set avatar URL by connector. * * @param connector the connector to retrieve the avatar information from * @param avatarUrl the avatar url information to store */ public static void setAvatarUrl(@NonNull final ICredentials connector, final String avatarUrl) { putString(connector.getAvatarPreferenceKey(), avatarUrl); } /** * Return the locale that should be used to display information to the user. * * @return either the system locale or an English one, depending on the settings */ public static Locale getApplicationLocale() { return Settings.useEnglish() ? Locale.ENGLISH : Locale.getDefault(); } public static void setRoutingMode(@NonNull final RoutingMode mode) { putString(R.string.pref_map_routing, mode.parameterValue); Routing.invalidateRouting(); } public static RoutingMode getRoutingMode() { return RoutingMode.fromString(getString(R.string.pref_map_routing, "foot")); } }