package org.wordpress.android.ui.prefs; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.text.TextUtils; import org.wordpress.android.BuildConfig; import org.wordpress.android.WordPress; import org.wordpress.android.analytics.AnalyticsTracker; import org.wordpress.android.analytics.AnalyticsTracker.Stat; import org.wordpress.android.fluxc.model.PostModel; import org.wordpress.android.models.PeopleListFilter; import org.wordpress.android.models.ReaderTag; import org.wordpress.android.models.ReaderTagType; import org.wordpress.android.ui.ActivityId; import org.wordpress.android.ui.comments.CommentsListFragment.CommentStatusCriteria; import org.wordpress.android.ui.reader.utils.ReaderUtils; import org.wordpress.android.ui.stats.StatsTimeframe; import org.wordpress.android.util.StringUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class AppPrefs { private static final int THEME_IMAGE_SIZE_WIDTH_DEFAULT = 400; private static final int MAX_PENDING_DRAFTS_AMOUNT = 100; public static final int MAX_RECENTLY_PICKED_SITES = 4; public interface PrefKey { String name(); String toString(); } /** * Application related preferences. When the user disconnects, these preferences are erased. */ public enum DeletablePrefKey implements PrefKey { // name of last shown activity LAST_ACTIVITY_STR, // last selected tag in the reader READER_TAG_NAME, READER_TAG_TYPE, // title of the last active page in ReaderSubsActivity READER_SUBS_PAGE_TITLE, // email retrieved and attached to mixpanel profile MIXPANEL_EMAIL_ADDRESS, // index of the last active tab in main activity MAIN_TAB_INDEX, // index of the last active item in Stats activity STATS_ITEM_INDEX, // Keep the associations between each widget_id/blog_id added to the app STATS_WIDGET_KEYS_BLOGS, // last data stored for the Stats Widgets STATS_WIDGET_DATA, // aztec editor enabled AZTEC_EDITOR_ENABLED, // visual editor enabled VISUAL_EDITOR_ENABLED, // Store the number of times Stats are loaded without errors. It's used to show the Widget promo dialog. STATS_WIDGET_PROMO_ANALYTICS, // index of the last active status type in Comments activity COMMENTS_STATUS_TYPE_INDEX, // index of the last active people list filter in People Management activity PEOPLE_LIST_FILTER_INDEX, // selected site in the main activity SELECTED_SITE_LOCAL_ID, // wpcom ID of the last push notification received PUSH_NOTIFICATIONS_LAST_NOTE_ID, // local time of the last push notification received PUSH_NOTIFICATIONS_LAST_NOTE_TIME, // local IDs of sites recently chosen in the site picker RECENTLY_PICKED_SITE_IDS, // list of last time a notification has been created for a draft PENDING_DRAFTS_NOTIFICATION_LAST_NOTIFICATION_DATES, } /** * These preferences won't be deleted when the user disconnects. They should be used for device specifics or user * independent prefs. */ public enum UndeletablePrefKey implements PrefKey { // Theme image size retrieval THEME_IMAGE_SIZE_WIDTH, // index of the last app-version LAST_APP_VERSION_INDEX, // visual editor available VISUAL_EDITOR_AVAILABLE, // When we need to show the new editor promo dialog AZTEC_EDITOR_PROMO_REQUIRED, // Global plans features GLOBAL_PLANS_PLANS_FEATURES, // When we need to sync IAP data with the wpcom backend IAP_SYNC_REQUIRED, // When we need to show the Gravatar Change Promo Tooltip GRAVATAR_CHANGE_PROMO_REQUIRED, // When we need to show the snackbar indicating how notifications can be navigated through SWIPE_TO_NAVIGATE_NOTIFICATIONS, // Same as above but for the reader SWIPE_TO_NAVIGATE_READER, // access token migrated to AccountStore, must wait for network calls to return before app access ACCESS_TOKEN_MIGRATED, // Self-hosted sites migration to FluxC SELF_HOSTED_SITES_MIGRATED_TO_FLUXC, // Draft migration to FluxC DRAFTS_MIGRATED_TO_FLUXC, // aztec editor available AZTEC_EDITOR_AVAILABLE, // smart toast counters SMART_TOAST_PHOTO_PICKER_LONG_PRESS_COUNTER, SMART_TOAST_WP_MEDIA_BROWSER_LONG_PRESS_COUNTER, SMART_TOAST_COMMENTS_LONG_PRESS_COUNTER } private static SharedPreferences prefs() { return PreferenceManager.getDefaultSharedPreferences(WordPress.getContext()); } private static String getString(PrefKey key) { return getString(key, ""); } private static String getString(PrefKey key, String defaultValue) { return prefs().getString(key.name(), defaultValue); } private static void setString(PrefKey key, String value) { SharedPreferences.Editor editor = prefs().edit(); if (TextUtils.isEmpty(value)) { editor.remove(key.name()); } else { editor.putString(key.name(), value); } editor.apply(); } private static long getLong(PrefKey key) { try { String value = getString(key); return Long.parseLong(value); } catch (NumberFormatException e) { return 0; } } private static void setLong(PrefKey key, long value) { setString(key, Long.toString(value)); } private static int getInt(PrefKey key, int def) { try { String value = getString(key); if (value.isEmpty()) { return def; } return Integer.parseInt(value); } catch (NumberFormatException e) { return def; } } public static int getInt(PrefKey key) { return getInt(key, 0); } public static void setInt(PrefKey key, int value) { setString(key, Integer.toString(value)); } private static boolean getBoolean(PrefKey key, boolean def) { String value = getString(key, Boolean.toString(def)); return Boolean.parseBoolean(value); } private static void setBoolean(PrefKey key, boolean value) { setString(key, Boolean.toString(value)); } private static void remove(PrefKey key) { prefs().edit().remove(key.name()).apply(); } // Exposed methods /** * remove all user-related preferences */ public static void reset() { SharedPreferences.Editor editor = prefs().edit(); for (DeletablePrefKey key : DeletablePrefKey.values()) { editor.remove(key.name()); } editor.apply(); } public static ReaderTag getReaderTag() { String tagName = getString(DeletablePrefKey.READER_TAG_NAME); if (TextUtils.isEmpty(tagName)) { return null; } int tagType = getInt(DeletablePrefKey.READER_TAG_TYPE); return ReaderUtils.getTagFromTagName(tagName, ReaderTagType.fromInt(tagType)); } public static void setReaderTag(ReaderTag tag) { if (tag != null && !TextUtils.isEmpty(tag.getTagSlug())) { setString(DeletablePrefKey.READER_TAG_NAME, tag.getTagSlug()); setInt(DeletablePrefKey.READER_TAG_TYPE, tag.tagType.toInt()); } else { prefs().edit() .remove(DeletablePrefKey.READER_TAG_NAME.name()) .remove(DeletablePrefKey.READER_TAG_TYPE.name()) .apply(); } } /** * title of the last active page in ReaderSubsActivity - this is stored rather than * the index of the page so we can re-order pages without affecting this value */ public static String getReaderSubsPageTitle() { return getString(DeletablePrefKey.READER_SUBS_PAGE_TITLE); } public static void setReaderSubsPageTitle(String pageTitle) { setString(DeletablePrefKey.READER_SUBS_PAGE_TITLE, pageTitle); } public static StatsTimeframe getStatsTimeframe() { int idx = getInt(DeletablePrefKey.STATS_ITEM_INDEX); StatsTimeframe[] timeframeValues = StatsTimeframe.values(); if (timeframeValues.length < idx) { return timeframeValues[0]; } else { return timeframeValues[idx]; } } public static void setStatsTimeframe(StatsTimeframe timeframe) { if (timeframe != null) { setInt(DeletablePrefKey.STATS_ITEM_INDEX, timeframe.ordinal()); } else { prefs().edit() .remove(DeletablePrefKey.STATS_ITEM_INDEX.name()) .apply(); } } public static CommentStatusCriteria getCommentsStatusFilter() { int idx = getInt(DeletablePrefKey.COMMENTS_STATUS_TYPE_INDEX); CommentStatusCriteria[] commentStatusValues = CommentStatusCriteria.values(); if (commentStatusValues.length < idx) { return commentStatusValues[0]; } else { return commentStatusValues[idx]; } } public static void setCommentsStatusFilter(CommentStatusCriteria commentStatus) { if (commentStatus != null) { setInt(DeletablePrefKey.COMMENTS_STATUS_TYPE_INDEX, commentStatus.ordinal()); } else { prefs().edit() .remove(DeletablePrefKey.COMMENTS_STATUS_TYPE_INDEX.name()) .apply(); } } public static PeopleListFilter getPeopleListFilter() { int idx = getInt(DeletablePrefKey.PEOPLE_LIST_FILTER_INDEX); PeopleListFilter[] values = PeopleListFilter.values(); if (values.length < idx) { return values[0]; } else { return values[idx]; } } public static void setPeopleListFilter(PeopleListFilter peopleListFilter) { if (peopleListFilter != null) { setInt(DeletablePrefKey.PEOPLE_LIST_FILTER_INDEX, peopleListFilter.ordinal()); } else { prefs().edit() .remove(DeletablePrefKey.PEOPLE_LIST_FILTER_INDEX.name()) .apply(); } } // Store the version code of the app. Used to check it the app was upgraded. public static int getLastAppVersionCode() { return getInt(UndeletablePrefKey.LAST_APP_VERSION_INDEX); } public static void setLastAppVersionCode(int versionCode) { setInt(UndeletablePrefKey.LAST_APP_VERSION_INDEX, versionCode); } /** * name of the last shown activity - used at startup to restore the previously selected * activity, also used by analytics tracker */ public static String getLastActivityStr() { return getString(DeletablePrefKey.LAST_ACTIVITY_STR, ActivityId.UNKNOWN.name()); } public static void setLastActivityStr(String value) { setString(DeletablePrefKey.LAST_ACTIVITY_STR, value); } public static void resetLastActivityStr() { remove(DeletablePrefKey.LAST_ACTIVITY_STR); } // Mixpanel email retrieval check public static String getMixpanelUserEmail() { return getString(DeletablePrefKey.MIXPANEL_EMAIL_ADDRESS, null); } public static void setMixpanelUserEmail(String email) { setString(DeletablePrefKey.MIXPANEL_EMAIL_ADDRESS, email); } public static int getMainTabIndex() { return getInt(DeletablePrefKey.MAIN_TAB_INDEX); } public static void setMainTabIndex(int index) { setInt(DeletablePrefKey.MAIN_TAB_INDEX, index); } // Stats Widgets public static void resetStatsWidgetsKeys() { remove(DeletablePrefKey.STATS_WIDGET_KEYS_BLOGS); } public static String getStatsWidgetsKeys() { return getString(DeletablePrefKey.STATS_WIDGET_KEYS_BLOGS); } public static void setStatsWidgetsKeys(String widgetData) { setString(DeletablePrefKey.STATS_WIDGET_KEYS_BLOGS, widgetData); } public static String getStatsWidgetsData() { return getString(DeletablePrefKey.STATS_WIDGET_DATA); } public static void setStatsWidgetsData(String widgetData) { setString(DeletablePrefKey.STATS_WIDGET_DATA, widgetData); } public static void resetStatsWidgetsData() { remove(DeletablePrefKey.STATS_WIDGET_DATA); } // Themes public static void setThemeImageSizeWidth(int width) { setInt(UndeletablePrefKey.THEME_IMAGE_SIZE_WIDTH, width); } public static int getThemeImageSizeWidth() { int value = getInt(UndeletablePrefKey.THEME_IMAGE_SIZE_WIDTH); if (value == 0) { return THEME_IMAGE_SIZE_WIDTH_DEFAULT; } else { return getInt(UndeletablePrefKey.THEME_IMAGE_SIZE_WIDTH); } } // Aztec Editor public static void setAztecEditorEnabled(boolean isEnabled) { setBoolean(DeletablePrefKey.AZTEC_EDITOR_ENABLED, isEnabled); AnalyticsTracker.track(isEnabled ? Stat.EDITOR_AZTEC_TOGGLED_ON : Stat.EDITOR_AZTEC_TOGGLED_OFF); } public static boolean isAztecEditorEnabled() { return isAztecEditorAvailable() && getBoolean(DeletablePrefKey.AZTEC_EDITOR_ENABLED, true); } public static void setAztecEditorAvailable(boolean aztecEditorAvailable) { setBoolean(UndeletablePrefKey.AZTEC_EDITOR_AVAILABLE, aztecEditorAvailable); if (aztecEditorAvailable) { AnalyticsTracker.track(Stat.EDITOR_AZTEC_ENABLED); } } public static boolean isAztecEditorAvailable() { return BuildConfig.AZTEC_EDITOR_AVAILABLE || getBoolean(UndeletablePrefKey.AZTEC_EDITOR_AVAILABLE, false); } // Visual Editor public static void setVisualEditorEnabled(boolean visualEditorEnabled) { setBoolean(DeletablePrefKey.VISUAL_EDITOR_ENABLED, visualEditorEnabled); AnalyticsTracker.track(visualEditorEnabled ? Stat.EDITOR_TOGGLED_ON : Stat.EDITOR_TOGGLED_OFF); } public static void setVisualEditorAvailable(boolean visualEditorAvailable) { setBoolean(UndeletablePrefKey.VISUAL_EDITOR_AVAILABLE, visualEditorAvailable); if (visualEditorAvailable) { AnalyticsTracker.track(Stat.EDITOR_ENABLED_NEW_VERSION); } } public static boolean isVisualEditorAvailable() { return getBoolean(UndeletablePrefKey.VISUAL_EDITOR_AVAILABLE, true); } public static boolean isVisualEditorEnabled() { return isVisualEditorAvailable() && getBoolean(DeletablePrefKey.VISUAL_EDITOR_ENABLED, !isAztecEditorEnabled()); } public static boolean isNewEditorPromoRequired() { return getBoolean(UndeletablePrefKey.AZTEC_EDITOR_PROMO_REQUIRED, true); } public static void setNewEditorPromoRequired(boolean required) { setBoolean(UndeletablePrefKey.AZTEC_EDITOR_PROMO_REQUIRED, required); } public static boolean isGravatarChangePromoRequired() { return getBoolean(UndeletablePrefKey.GRAVATAR_CHANGE_PROMO_REQUIRED, true); } public static void setGravatarChangePromoRequired(boolean required) { setBoolean(UndeletablePrefKey.GRAVATAR_CHANGE_PROMO_REQUIRED, required); } // Store the number of times Stats are loaded successfully before showing the Promo Dialog public static void bumpAnalyticsForStatsWidgetPromo() { int current = getAnalyticsForStatsWidgetPromo(); setInt(DeletablePrefKey.STATS_WIDGET_PROMO_ANALYTICS, current + 1); } public static int getAnalyticsForStatsWidgetPromo() { return getInt(DeletablePrefKey.STATS_WIDGET_PROMO_ANALYTICS); } public static void setGlobalPlansFeatures(String jsonOfFeatures) { if (jsonOfFeatures != null) { setString(UndeletablePrefKey.GLOBAL_PLANS_PLANS_FEATURES, jsonOfFeatures); } else { remove(UndeletablePrefKey.GLOBAL_PLANS_PLANS_FEATURES); } } public static String getGlobalPlansFeatures() { return getString(UndeletablePrefKey.GLOBAL_PLANS_PLANS_FEATURES, ""); } public static boolean isInAppPurchaseRefreshRequired() { return getBoolean(UndeletablePrefKey.IAP_SYNC_REQUIRED, false); } public static void setInAppPurchaseRefreshRequired(boolean required) { setBoolean(UndeletablePrefKey.IAP_SYNC_REQUIRED, required); } public static int getSelectedSite() { return getInt(DeletablePrefKey.SELECTED_SITE_LOCAL_ID, -1); } public static void setSelectedSite(int selectedSite) { setInt(DeletablePrefKey.SELECTED_SITE_LOCAL_ID, selectedSite); } public static String getLastPushNotificationWpcomNoteId() { return getString(DeletablePrefKey.PUSH_NOTIFICATIONS_LAST_NOTE_ID); } public static void setLastPushNotificationWpcomNoteId(String noteID) { setString(DeletablePrefKey.PUSH_NOTIFICATIONS_LAST_NOTE_ID, noteID); } public static long getLastPushNotificationTime() { return getLong(DeletablePrefKey.PUSH_NOTIFICATIONS_LAST_NOTE_TIME); } public static void setLastPushNotificationTime(long time) { setLong(DeletablePrefKey.PUSH_NOTIFICATIONS_LAST_NOTE_ID, time); } public static boolean isNotificationsSwipeToNavigateShown() { return getBoolean(UndeletablePrefKey.SWIPE_TO_NAVIGATE_NOTIFICATIONS, false); } public static void setNotificationsSwipeToNavigateShown(boolean alreadyShown) { setBoolean(UndeletablePrefKey.SWIPE_TO_NAVIGATE_NOTIFICATIONS, alreadyShown); } public static boolean isReaderSwipeToNavigateShown() { return getBoolean(UndeletablePrefKey.SWIPE_TO_NAVIGATE_READER, false); } public static void setReaderSwipeToNavigateShown(boolean alreadyShown) { setBoolean(UndeletablePrefKey.SWIPE_TO_NAVIGATE_READER, alreadyShown); } public static long getPendingDraftsLastNotificationDate(PostModel post) { String key = DeletablePrefKey.PENDING_DRAFTS_NOTIFICATION_LAST_NOTIFICATION_DATES.name() + "-" + post.getId(); return prefs().getLong(key, 0); } public static void setPendingDraftsLastNotificationDate(PostModel post, long timestamp) { String key = DeletablePrefKey.PENDING_DRAFTS_NOTIFICATION_LAST_NOTIFICATION_DATES.name() + "-" + post.getId(); SharedPreferences.Editor editor = prefs().edit(); editor.putLong(key, timestamp); editor.apply(); } /* * returns a list of local IDs of sites recently chosen in the site picker */ public static ArrayList<Integer> getRecentlyPickedSiteIds() { String idsAsString = getString(DeletablePrefKey.RECENTLY_PICKED_SITE_IDS, ""); List<String> items = Arrays.asList(idsAsString.split(",")); ArrayList<Integer> siteIds = new ArrayList<>(); for (String item : items) { siteIds.add(StringUtils.stringToInt(item)); } return siteIds; } /* * adds a local site ID to the top of list of recently chosen sites */ public static void addRecentlyPickedSiteId(Integer localId) { if (localId == 0) return; ArrayList<Integer> currentIds = getRecentlyPickedSiteIds(); // remove this ID if it already exists in the list int index = currentIds.indexOf(localId); if (index > -1) { currentIds.remove(index); } // add this ID to the front of the list currentIds.add(0, localId); // remove at max if (currentIds.size() > MAX_RECENTLY_PICKED_SITES) { currentIds.remove(MAX_RECENTLY_PICKED_SITES); } // store in prefs String idsAsString = TextUtils.join(",", currentIds); setString(DeletablePrefKey.RECENTLY_PICKED_SITE_IDS, idsAsString); } public static boolean wasAccessTokenMigrated() { return getBoolean(UndeletablePrefKey.ACCESS_TOKEN_MIGRATED, false); } public static void setAccessTokenMigrated(boolean migrated) { setBoolean(UndeletablePrefKey.ACCESS_TOKEN_MIGRATED, migrated); } public static boolean wereSelfHostedSitesMigratedToFluxC() { return getBoolean(UndeletablePrefKey.SELF_HOSTED_SITES_MIGRATED_TO_FLUXC, false); } public static void setSelfHostedSitesMigratedToFluxC(boolean migrated) { setBoolean(UndeletablePrefKey.SELF_HOSTED_SITES_MIGRATED_TO_FLUXC, migrated); } public static boolean wereDraftsMigratedToFluxC() { return getBoolean(UndeletablePrefKey.DRAFTS_MIGRATED_TO_FLUXC, false); } public static void setDraftsMigratedToFluxC(boolean migrated) { setBoolean(UndeletablePrefKey.DRAFTS_MIGRATED_TO_FLUXC, migrated); } }