package com.battlelancer.seriesguide.util; import android.content.Context; import android.preference.PreferenceManager; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeMap; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; /** * Very primitive timestamp based search history backed by {@link android.content.SharedPreferences}. * It's only fast for a low number of history entries. */ public class SearchHistory { private static final String KEY_SEARCH_HISTORY_BASE = "recent-searches-"; private static final int MAX_HISTORY_SIZE = 10; private static final int ISO_8601_DATETIME_NOMILLIS_UTC_LENGTH = 20; private static final int DATETIME_PREFIX_LENGTH = ISO_8601_DATETIME_NOMILLIS_UTC_LENGTH + 1; private static final DateTimeFormatter ISO_DATETIME_FORMATTER = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC(); private static final Comparator<String> NATURAL_ORDER_REVERSE = new Comparator<String>() { @Override public int compare(String lhs, String rhs) { return -lhs.compareTo(rhs); } }; private final Context context; private final String settingsKey; // timestamp+query -> query private final TreeMap<String, String> searchHistory; // query -> timestamp+query private final HashMap<String, String> queryMap; /** * Initialize search history by loading last saved history from {@link * android.content.SharedPreferences}. * * @param historyTag Appended to settings key where history is stored. */ public SearchHistory(Context context, String historyTag) { this.context = context; this.settingsKey = KEY_SEARCH_HISTORY_BASE + historyTag; // load current history Set<String> storedSearchHistory = PreferenceManager.getDefaultSharedPreferences(context) .getStringSet(settingsKey, null); if (storedSearchHistory == null) { this.searchHistory = new TreeMap<>(NATURAL_ORDER_REVERSE); this.queryMap = new HashMap<>(); } else { TreeMap<String, String> map = new TreeMap<>(NATURAL_ORDER_REVERSE); HashMap<String, String> queryMap = new HashMap<>(); for (String historyEntry : storedSearchHistory) { String query = historyEntry.substring(DATETIME_PREFIX_LENGTH, historyEntry.length()); map.put(historyEntry, query); queryMap.put(query, historyEntry); } this.searchHistory = map; this.queryMap = queryMap; } } public synchronized List<String> getSearchHistory() { return new ArrayList<>(searchHistory.values()); } /** * Save a search query to history. If the query already exists, its timestamp is updated. * * @return {@code false} if the query was already in search history. */ public synchronized boolean saveRecentSearch(String query) { // prevent duplicate entries String historyEntry = queryMap.get(query); if (historyEntry != null) { searchHistory.remove(historyEntry); } // add new entry String now = new DateTime().toString(ISO_DATETIME_FORMATTER); String newHistoryEntry = now + " " + query; searchHistory.put(now + " " + query, query); queryMap.put(query, newHistoryEntry); // trim to size if (searchHistory.size() > MAX_HISTORY_SIZE) { while (searchHistory.size() > MAX_HISTORY_SIZE) { // remove oldest entry (= lowest date value, sorted first) String removedQuery = searchHistory.remove(searchHistory.lastKey()); queryMap.remove(removedQuery); } } // save history Set<String> historySet = new HashSet<>(searchHistory.keySet()); PreferenceManager.getDefaultSharedPreferences(context) .edit() .putStringSet(settingsKey, historySet) .apply(); return historyEntry == null; } /** * Clear the search history from memory, starts a request to clear it from disk as well. */ public synchronized void clearHistory() { PreferenceManager.getDefaultSharedPreferences(context).edit().remove(settingsKey).apply(); searchHistory.clear(); } }