From c29a801d1465442353cf0a41a67ef2915afd2373 Mon Sep 17 00:00:00 2001 From: marunjar Date: Mon, 13 Nov 2023 14:12:07 +0100 Subject: [PATCH 1/3] predictable order of results - refactor `TagsSearcher` and `UntaggedSearcher` as they have a lot in common - add method to apply relevance from history to any number of pojo - when tagged/untagged results are shown order is taken from history by default - add new preference for setting custom order of tagged/untagged results - collect all tag related preferences from `KISS Settings -> UX` in a `Tags` category - fixes #2187, #1609, #1463 --- .../main/java/fr/neamar/kiss/DataHandler.java | 47 ++++++++++- .../main/java/fr/neamar/kiss/db/DBHelper.java | 12 ++- .../kiss/searcher/ApplicationsSearcher.java | 9 ++- .../neamar/kiss/searcher/HistorySearcher.java | 20 ++--- .../kiss/searcher/PojoWithTagSearcher.java | 77 +++++++++++++++++++ .../fr/neamar/kiss/searcher/Searcher.java | 6 +- .../fr/neamar/kiss/searcher/TagsSearcher.java | 45 +---------- .../kiss/searcher/UntaggedSearcher.java | 42 ++-------- app/src/main/res/values/arrays.xml | 22 +++++- app/src/main/res/values/strings.xml | 4 + app/src/main/res/xml/preferences.xml | 64 ++++++++------- 11 files changed, 211 insertions(+), 137 deletions(-) create mode 100644 app/src/main/java/fr/neamar/kiss/searcher/PojoWithTagSearcher.java diff --git a/app/src/main/java/fr/neamar/kiss/DataHandler.java b/app/src/main/java/fr/neamar/kiss/DataHandler.java index 3155d5dd5..08bbb81c9 100644 --- a/app/src/main/java/fr/neamar/kiss/DataHandler.java +++ b/app/src/main/java/fr/neamar/kiss/DataHandler.java @@ -360,6 +360,7 @@ public void requestResults(String query, Searcher searcher) { * @param searcher the searcher currently running */ public void requestAllRecords(Searcher searcher) { + List collectedPojos = new ArrayList<>(); for (ProviderEntry entry : this.providers.values()) { if (searcher.isCancelled()) break; @@ -367,9 +368,11 @@ public void requestAllRecords(Searcher searcher) { continue; List pojos = entry.provider.getPojos(); - if (pojos != null) - searcher.addResults(pojos); + if (pojos != null) { + collectedPojos.addAll(pojos); + } } + searcher.addResults(collectedPojos); } /** @@ -380,11 +383,10 @@ public void requestAllRecords(Searcher searcher) { * * @param context android context * @param itemCount max number of items to retrieve, total number may be less (search or calls are not returned for instance) - * @param historyMode Recency vs Frecency vs Frequency vs Adaptive vs Alphabetically * @param itemsToExcludeById Items to exclude from history by their id * @return pojos in recent history */ - public List getHistory(Context context, int itemCount, String historyMode, Set itemsToExcludeById) { + public List getHistory(Context context, int itemCount, Set itemsToExcludeById) { // Pre-allocate array slots that are likely to be used based on the current maximum item // count List history = new ArrayList<>(Math.min(itemCount, 256)); @@ -393,9 +395,11 @@ public List getHistory(Context context, int itemCount, String historyMode, int extendedItemCount = itemCount + itemsToExcludeById.size(); // Read history + String historyMode = getHistoryMode(); List ids = DBHelper.getHistory(context, extendedItemCount, historyMode); // Find associated items + int size = ids.size(); for (int i = 0; i < ids.size(); i++) { // Ask all providers if they know this id Pojo pojo = getPojo(ids.get(i).record); @@ -408,6 +412,7 @@ public List getHistory(Context context, int itemCount, String historyMode, continue; } + pojo.relevance = size - i; history.add(pojo); // Break if maximum number of items have been retrieved @@ -419,6 +424,40 @@ public List getHistory(Context context, int itemCount, String historyMode, return history; } + /** + * Apply relevance from history to given pojos. + * + * @param pojos which needs to have relevance set + * @param historyMode + */ + public void applyRelevanceFromHistory(List pojos, String historyMode) { + // get length of history, this is needed so there are no entries missed + // if only number of displayed elements is used, this will result in parts of entries to be sorted by name + int historyLength = getHistoryLength(); + + Map relevance = new HashMap<>(); + if (!"alphabetically".equals(historyMode)) { + List ids = DBHelper.getHistory(context, historyLength, historyMode); + int size = ids.size(); + for (int i = 0; i < size; i++) { + relevance.put(ids.get(i).record, size - i); + } + } + + for (Pojo pojo : pojos) { + Integer calculated = relevance.get(pojo.id); + pojo.relevance = calculated != null ? calculated : 0; + } + } + + /** + * @return history mode from settings: Recency vs Frecency vs Frequency vs Adaptive vs Alphabetically + */ + public String getHistoryMode() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + return prefs.getString("history-mode", "recency"); + } + public int getHistoryLength() { return DBHelper.getHistoryLength(this.context); } diff --git a/app/src/main/java/fr/neamar/kiss/db/DBHelper.java b/app/src/main/java/fr/neamar/kiss/db/DBHelper.java index 8a3baf55f..72f47272a 100644 --- a/app/src/main/java/fr/neamar/kiss/db/DBHelper.java +++ b/app/src/main/java/fr/neamar/kiss/db/DBHelper.java @@ -195,13 +195,11 @@ public static int getHistoryLength(Context context) { // Cursor query (boolean distinct, String table, String[] columns, // String selection, String[] selectionArgs, String groupBy, String // having, String orderBy, String limit) - Cursor cursor = db.query(false, "history", new String[]{"COUNT(*)"}, null, null, - null, null, null, null); - - cursor.moveToFirst(); - int historyLength = cursor.getInt(0); - cursor.close(); - return historyLength; + try (Cursor cursor = db.query(false, "history", new String[]{"COUNT(*)"}, null, null, + null, null, null, null)) { + cursor.moveToFirst(); + return cursor.getInt(0); + } } /** diff --git a/app/src/main/java/fr/neamar/kiss/searcher/ApplicationsSearcher.java b/app/src/main/java/fr/neamar/kiss/searcher/ApplicationsSearcher.java index 48e668bf4..ecdb2d3a1 100644 --- a/app/src/main/java/fr/neamar/kiss/searcher/ApplicationsSearcher.java +++ b/app/src/main/java/fr/neamar/kiss/searcher/ApplicationsSearcher.java @@ -10,8 +10,8 @@ import fr.neamar.kiss.KissApplication; import fr.neamar.kiss.MainActivity; import fr.neamar.kiss.pojo.AppPojo; -import fr.neamar.kiss.pojo.ReversedNameComparator; import fr.neamar.kiss.pojo.Pojo; +import fr.neamar.kiss.pojo.ReversedNameComparator; import fr.neamar.kiss.pojo.ShortcutPojo; /** @@ -59,8 +59,13 @@ protected Void doInBackground(Void... voids) { @Override protected void onPostExecute(Void param) { super.onPostExecute(param); + + MainActivity activity = activityWeakReference.get(); + if (activity == null) + return; + // Build sections for fast scrolling - activityWeakReference.get().adapter.buildSections(); + activity.adapter.buildSections(); } /** diff --git a/app/src/main/java/fr/neamar/kiss/searcher/HistorySearcher.java b/app/src/main/java/fr/neamar/kiss/searcher/HistorySearcher.java index bd4262b95..50e89314e 100644 --- a/app/src/main/java/fr/neamar/kiss/searcher/HistorySearcher.java +++ b/app/src/main/java/fr/neamar/kiss/searcher/HistorySearcher.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Set; +import fr.neamar.kiss.DataHandler; import fr.neamar.kiss.KissApplication; import fr.neamar.kiss.MainActivity; import fr.neamar.kiss.db.ShortcutRecord; @@ -27,7 +28,7 @@ public HistorySearcher(MainActivity activity) { } @Override - int getMaxResultCount() { + protected int getMaxResultCount() { // Convert `"number-of-display-elements"` to double first before truncating to int to avoid // `java.lang.NumberFormatException` crashes for values larger than `Integer.MAX_VALUE` try { @@ -40,21 +41,22 @@ int getMaxResultCount() { @Override protected Void doInBackground(Void... voids) { // Ask for records - String historyMode = prefs.getString("history-mode", "recency"); boolean excludeFavorites = prefs.getBoolean("exclude-favorites-history", false); MainActivity activity = activityWeakReference.get(); if (activity == null) return null; + DataHandler dataHandler = KissApplication.getApplication(activity).getDataHandler(); + //Gather excluded - Set excludedFromHistory = KissApplication.getApplication(activity).getDataHandler().getExcludedFromHistory(); + Set excludedFromHistory = dataHandler.getExcludedFromHistory(); Set excludedPojoById = new HashSet<>(excludedFromHistory); // add ids of shortcuts for excluded apps if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { for (String id : excludedFromHistory) { - Pojo pojo = KissApplication.getApplication(activity).getDataHandler().getItemById(id); + Pojo pojo = dataHandler.getItemById(id); if (pojo instanceof AppPojo) { List shortcutInfos = ShortcutUtil.getShortcuts(activity, ((AppPojo) pojo).packageName); for (ShortcutInfo shortcutInfo : shortcutInfos) { @@ -69,18 +71,12 @@ protected Void doInBackground(Void... voids) { if (excludeFavorites) { // Gather favorites - for (Pojo favoritePojo : KissApplication.getApplication(activity).getDataHandler().getFavorites()) { + for (Pojo favoritePojo : dataHandler.getFavorites()) { excludedPojoById.add(favoritePojo.id); } } - List pojos = KissApplication.getApplication(activity).getDataHandler() - .getHistory(activity, getMaxResultCount(), historyMode, excludedPojoById); - - int size = pojos.size(); - for(int i = 0; i < size; i += 1) { - pojos.get(i).relevance = size - i; - } + List pojos = dataHandler.getHistory(activity, getMaxResultCount(), excludedPojoById); this.addResults(pojos); return null; diff --git a/app/src/main/java/fr/neamar/kiss/searcher/PojoWithTagSearcher.java b/app/src/main/java/fr/neamar/kiss/searcher/PojoWithTagSearcher.java new file mode 100644 index 000000000..d0eb0166a --- /dev/null +++ b/app/src/main/java/fr/neamar/kiss/searcher/PojoWithTagSearcher.java @@ -0,0 +1,77 @@ +package fr.neamar.kiss.searcher; + +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +import fr.neamar.kiss.KissApplication; +import fr.neamar.kiss.MainActivity; +import fr.neamar.kiss.pojo.Pojo; +import fr.neamar.kiss.pojo.PojoWithTags; + +/** + * Returns a list of all results that match the specified pojo with tags. + */ +public abstract class PojoWithTagSearcher extends Searcher { + + private final SharedPreferences prefs; + + public PojoWithTagSearcher(MainActivity activity, String query) { + super(activity, query); + prefs = PreferenceManager.getDefaultSharedPreferences(activity); + } + + @Override + protected Void doInBackground(Void... voids) { + MainActivity activity = activityWeakReference.get(); + if (activity == null) + return null; + + KissApplication.getApplication(activity).getDataHandler().requestAllRecords(this); + + return null; + } + + @Override + public boolean addResults(List pojos) { + List filteredPojos = new ArrayList<>(); + for (Pojo pojo : pojos) { + if (!(pojo instanceof PojoWithTags)) { + continue; + } + PojoWithTags pojoWithTags = (PojoWithTags) pojo; + if (acceptPojo(pojoWithTags)) { + filteredPojos.add(pojoWithTags); + } + } + + MainActivity activity = activityWeakReference.get(); + if (activity == null) { + return false; + } + + KissApplication.getApplication(activity).getDataHandler().applyRelevanceFromHistory(filteredPojos, getTaggedResultSortMode()); + + return super.addResults(filteredPojos); + } + + @NonNull + private String getTaggedResultSortMode() { + String sortMode = prefs.getString("tagged-result-sort-mode", "default"); + if ("default".equals(sortMode)) { + sortMode = KissApplication.getApplication(activityWeakReference.get()).getDataHandler().getHistoryMode(); + } + return sortMode; + + } + + protected int getMaxResultCount() { + return Integer.MAX_VALUE; + } + + abstract protected boolean acceptPojo(PojoWithTags pojoWithTags); +} diff --git a/app/src/main/java/fr/neamar/kiss/searcher/Searcher.java b/app/src/main/java/fr/neamar/kiss/searcher/Searcher.java index a7866d9c4..8d07f3d8f 100644 --- a/app/src/main/java/fr/neamar/kiss/searcher/Searcher.java +++ b/app/src/main/java/fr/neamar/kiss/searcher/Searcher.java @@ -49,7 +49,7 @@ PriorityQueue getPojoProcessor(Context context) { return new PriorityQueue<>(DEFAULT_MAX_RESULTS, new RelevanceComparator()); } - int getMaxResultCount() { + protected int getMaxResultCount() { return DEFAULT_MAX_RESULTS; } @@ -69,10 +69,6 @@ public boolean addResults(List pojos) { if (isCancelled()) return false; - MainActivity activity = activityWeakReference.get(); - if (activity == null) - return false; - return this.processedPojos.addAll(pojos); } diff --git a/app/src/main/java/fr/neamar/kiss/searcher/TagsSearcher.java b/app/src/main/java/fr/neamar/kiss/searcher/TagsSearcher.java index a4b978c69..4dce46d14 100644 --- a/app/src/main/java/fr/neamar/kiss/searcher/TagsSearcher.java +++ b/app/src/main/java/fr/neamar/kiss/searcher/TagsSearcher.java @@ -1,56 +1,19 @@ package fr.neamar.kiss.searcher; -import java.util.ArrayList; -import java.util.List; - -import fr.neamar.kiss.KissApplication; import fr.neamar.kiss.MainActivity; -import fr.neamar.kiss.pojo.Pojo; import fr.neamar.kiss.pojo.PojoWithTags; /** - * Returns a list of all applications that match the specified tag + * Returns a list of all results that match the specified tag */ - -public class TagsSearcher extends Searcher { +public class TagsSearcher extends PojoWithTagSearcher { public TagsSearcher(MainActivity activity, String query) { super(activity, query == null ? "" : query); } @Override - public boolean addResults(List pojos) { - List filteredPojos = new ArrayList<>(); - for (Pojo pojo : pojos) { - if (!(pojo instanceof PojoWithTags)) { - continue; - } - PojoWithTags pojoWithTags = (PojoWithTags) pojo; - if (pojoWithTags.getTags() == null || pojoWithTags.getTags().isEmpty()) { - continue; - } - - if (!pojoWithTags.getTags().contains(query)) { - continue; - } - - filteredPojos.add(pojoWithTags); - } - return super.addResults(filteredPojos); + protected boolean acceptPojo(PojoWithTags pojoWithTags) { + return pojoWithTags.getTags() != null && pojoWithTags.getTags().contains(query); } - @Override - protected Void doInBackground(Void... voids) { - MainActivity activity = activityWeakReference.get(); - if (activity == null) - return null; - - KissApplication.getApplication(activity).getDataHandler().requestAllRecords(this); - - return null; - } - - @Override - int getMaxResultCount() { - return 250; - } } diff --git a/app/src/main/java/fr/neamar/kiss/searcher/UntaggedSearcher.java b/app/src/main/java/fr/neamar/kiss/searcher/UntaggedSearcher.java index 9e8e5d654..382f77249 100644 --- a/app/src/main/java/fr/neamar/kiss/searcher/UntaggedSearcher.java +++ b/app/src/main/java/fr/neamar/kiss/searcher/UntaggedSearcher.java @@ -1,46 +1,20 @@ package fr.neamar.kiss.searcher; -import java.util.ArrayList; -import java.util.List; - -import fr.neamar.kiss.KissApplication; import fr.neamar.kiss.MainActivity; -import fr.neamar.kiss.pojo.Pojo; import fr.neamar.kiss.pojo.PojoWithTags; -public class UntaggedSearcher extends Searcher { +/** + * Returns a list of all results that has no tag + */ +public class UntaggedSearcher extends PojoWithTagSearcher { - public UntaggedSearcher(MainActivity activity ) - { - super( activity, "" ); + public UntaggedSearcher(MainActivity activity) { + super(activity, ""); } @Override - public boolean addResults(List pojos) { - List filteredPojos = new ArrayList<>(); - for (Pojo pojo : pojos) { - if (!(pojo instanceof PojoWithTags)) { - continue; - } - PojoWithTags pojoWithTags = (PojoWithTags) pojo; - if (pojoWithTags.getTags() != null && !pojoWithTags.getTags().isEmpty()) { - continue; - } - - filteredPojos.add(pojo); - } - return super.addResults(filteredPojos); - } - - @Override - protected Void doInBackground(Void... voids) { - MainActivity activity = activityWeakReference.get(); - if (activity == null) - return null; - - KissApplication.getApplication(activity).getDataHandler().requestAllRecords(this); - - return null; + protected boolean acceptPojo(PojoWithTags pojoWithTags) { + return pojoWithTags.getTags() == null || pojoWithTags.getTags().isEmpty(); } } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 7b38876ba..b90b358a1 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -32,6 +32,22 @@ adaptive alphabetically + + @string/tagged_result_sort_mode_default + @string/history_recency + @string/history_frecency + @string/history_frequency + @string/history_adaptive + @string/history_alphabetically + + + default + recency + frecency + frequency + adaptive + alphabetically + Bing|https://www.bing.com/search?q=%s Brave|https://search.brave.com/search?q=%s @@ -43,10 +59,8 @@ YouTube|https://www.youtube.com/results?search_query=%s Application Stores|market://search?q=%s - - - - + + @string/gesture_do_nothing @string/gesture_keyboard diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a71d79cac..4816284c2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -348,4 +348,8 @@ Display menu Hide keyboard Display quick settings + Default, like history mode + Tagged result sort mode + Select how tagged results should be sorted. + Tags \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 0a847e589..2ccb87ff7 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -349,15 +349,47 @@ android:key="double-tap" android:title="@string/double_tap_to_lock" /> + + + + + + + + + + + - - - - - - - - Date: Tue, 14 Nov 2023 09:44:53 +0100 Subject: [PATCH 2/3] better handling of alphabetical order --- .../main/java/fr/neamar/kiss/DataHandler.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/fr/neamar/kiss/DataHandler.java b/app/src/main/java/fr/neamar/kiss/DataHandler.java index 08bbb81c9..02730c19b 100644 --- a/app/src/main/java/fr/neamar/kiss/DataHandler.java +++ b/app/src/main/java/fr/neamar/kiss/DataHandler.java @@ -431,22 +431,28 @@ public List getHistory(Context context, int itemCount, Set itemsTo * @param historyMode */ public void applyRelevanceFromHistory(List pojos, String historyMode) { - // get length of history, this is needed so there are no entries missed - // if only number of displayed elements is used, this will result in parts of entries to be sorted by name - int historyLength = getHistoryLength(); + if ("alphabetically".equals(historyMode)) { + // "alphabetically" is special case because relevance needs to be set for all pojos instead of these from history. + // This is done by setting all relevance to zero which results in order by name from used comparator. + for (Pojo pojo : pojos) { + pojo.relevance = 0; + } + } else { + // Get length of history, this is needed so there are no entries missed. + // If only number of displayed elements is used, this will result in more entries to be sorted by name. + int historyLength = getHistoryLength(); - Map relevance = new HashMap<>(); - if (!"alphabetically".equals(historyMode)) { + Map relevance = new HashMap<>(); List ids = DBHelper.getHistory(context, historyLength, historyMode); int size = ids.size(); for (int i = 0; i < size; i++) { relevance.put(ids.get(i).record, size - i); } - } - for (Pojo pojo : pojos) { - Integer calculated = relevance.get(pojo.id); - pojo.relevance = calculated != null ? calculated : 0; + for (Pojo pojo : pojos) { + Integer calculated = relevance.get(pojo.id); + pojo.relevance = calculated != null ? calculated : 0; + } } } From bfb206615317a806d085622775a7bd826cfdefde Mon Sep 17 00:00:00 2001 From: marunjar Date: Tue, 14 Nov 2023 16:24:01 +0100 Subject: [PATCH 3/3] improve preference description --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4816284c2..a0c4d6ab1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -350,6 +350,6 @@ Display quick settings Default, like history mode Tagged result sort mode - Select how tagged results should be sorted. + Select how results should be sorted when shown by tag Tags \ No newline at end of file