Skip to content

Commit

Permalink
Merge pull request #2202 from marunjar/fixed_result_order
Browse files Browse the repository at this point in the history
predictable order of results
  • Loading branch information
marunjar authored Nov 15, 2023
2 parents 111d502 + bfb2066 commit 3219939
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 137 deletions.
53 changes: 49 additions & 4 deletions app/src/main/java/fr/neamar/kiss/DataHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -360,16 +360,19 @@ public void requestResults(String query, Searcher searcher) {
* @param searcher the searcher currently running
*/
public void requestAllRecords(Searcher searcher) {
List<Pojo> collectedPojos = new ArrayList<>();
for (ProviderEntry entry : this.providers.values()) {
if (searcher.isCancelled())
break;
if (entry.provider == null)
continue;

List<? extends Pojo> pojos = entry.provider.getPojos();
if (pojos != null)
searcher.addResults(pojos);
if (pojos != null) {
collectedPojos.addAll(pojos);
}
}
searcher.addResults(collectedPojos);
}

/**
Expand All @@ -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<Pojo> getHistory(Context context, int itemCount, String historyMode, Set<String> itemsToExcludeById) {
public List<Pojo> getHistory(Context context, int itemCount, Set<String> itemsToExcludeById) {
// Pre-allocate array slots that are likely to be used based on the current maximum item
// count
List<Pojo> history = new ArrayList<>(Math.min(itemCount, 256));
Expand All @@ -393,9 +395,11 @@ public List<Pojo> getHistory(Context context, int itemCount, String historyMode,
int extendedItemCount = itemCount + itemsToExcludeById.size();

// Read history
String historyMode = getHistoryMode();
List<ValuedHistoryRecord> 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);
Expand All @@ -408,6 +412,7 @@ public List<Pojo> getHistory(Context context, int itemCount, String historyMode,
continue;
}

pojo.relevance = size - i;
history.add(pojo);

// Break if maximum number of items have been retrieved
Expand All @@ -419,6 +424,46 @@ public List<Pojo> 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<? extends Pojo> pojos, String historyMode) {
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<String, Integer> relevance = new HashMap<>();
List<ValuedHistoryRecord> 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);
}
Expand Down
12 changes: 5 additions & 7 deletions app/src/main/java/fr/neamar/kiss/db/DBHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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();
}

/**
Expand Down
20 changes: 8 additions & 12 deletions app/src/main/java/fr/neamar/kiss/searcher/HistorySearcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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<String> excludedFromHistory = KissApplication.getApplication(activity).getDataHandler().getExcludedFromHistory();
Set<String> excludedFromHistory = dataHandler.getExcludedFromHistory();
Set<String> 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<ShortcutInfo> shortcutInfos = ShortcutUtil.getShortcuts(activity, ((AppPojo) pojo).packageName);
for (ShortcutInfo shortcutInfo : shortcutInfos) {
Expand All @@ -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<Pojo> 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<Pojo> pojos = dataHandler.getHistory(activity, getMaxResultCount(), excludedPojoById);

this.addResults(pojos);
return null;
Expand Down
77 changes: 77 additions & 0 deletions app/src/main/java/fr/neamar/kiss/searcher/PojoWithTagSearcher.java
Original file line number Diff line number Diff line change
@@ -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<? extends Pojo> pojos) {
List<Pojo> 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);
}
6 changes: 1 addition & 5 deletions app/src/main/java/fr/neamar/kiss/searcher/Searcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ PriorityQueue<Pojo> getPojoProcessor(Context context) {
return new PriorityQueue<>(DEFAULT_MAX_RESULTS, new RelevanceComparator());
}

int getMaxResultCount() {
protected int getMaxResultCount() {
return DEFAULT_MAX_RESULTS;
}

Expand All @@ -69,10 +69,6 @@ public boolean addResults(List<? extends Pojo> pojos) {
if (isCancelled())
return false;

MainActivity activity = activityWeakReference.get();
if (activity == null)
return false;

return this.processedPojos.addAll(pojos);
}

Expand Down
45 changes: 4 additions & 41 deletions app/src/main/java/fr/neamar/kiss/searcher/TagsSearcher.java
Original file line number Diff line number Diff line change
@@ -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 ? "<tags>" : query);
}

@Override
public boolean addResults(List<? extends Pojo> pojos) {
List<Pojo> 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;
}
}
Loading

0 comments on commit 3219939

Please sign in to comment.