Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sorting of search results #4003

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
package com.amaze.filemanager.adapters;

import static com.amaze.filemanager.filesystem.compressed.CompressedHelper.*;
import static com.amaze.filemanager.filesystem.files.FileListSorter.SORT_NONE_ON_TOP;
import static com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_COLORIZE_ICONS;
import static com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_SHOW_FILE_SIZE;
import static com.amaze.filemanager.ui.fragments.preferencefragments.PreferencesConstants.PREFERENCE_SHOW_GOBACK_BUTTON;
Expand Down Expand Up @@ -52,6 +51,7 @@
import com.amaze.filemanager.fileoperations.filesystem.OpenMode;
import com.amaze.filemanager.filesystem.PasteHelper;
import com.amaze.filemanager.filesystem.files.CryptUtil;
import com.amaze.filemanager.filesystem.files.sort.DirSortBy;
import com.amaze.filemanager.ui.ItemPopupMenu;
import com.amaze.filemanager.ui.activities.MainActivity;
import com.amaze.filemanager.ui.activities.superclasses.PreferenceActivity;
Expand Down Expand Up @@ -626,7 +626,7 @@ private void setItems(

public void createHeaders(boolean invalidate, List<IconDataParcelable> uris) {
if ((mainFragment.getMainFragmentViewModel() != null
&& mainFragment.getMainFragmentViewModel().getDsort() == SORT_NONE_ON_TOP)
&& mainFragment.getMainFragmentViewModel().getDsort() == DirSortBy.NONE_ON_TOP)
|| getItemsDigested() == null
|| getItemsDigested().isEmpty()) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import com.amaze.filemanager.fileoperations.filesystem.OpenMode;
import com.amaze.filemanager.filesystem.HybridFileParcelable;
import com.amaze.filemanager.filesystem.files.sort.ComparableParcelable;
import com.amaze.filemanager.ui.icons.Icons;
import com.amaze.filemanager.utils.Utils;

Expand All @@ -35,7 +36,7 @@
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;

public class LayoutElementParcelable implements Parcelable {
public class LayoutElementParcelable implements Parcelable, ComparableParcelable {

private static final String CURRENT_YEAR =
String.valueOf(Calendar.getInstance().get(Calendar.YEAR));
Expand Down Expand Up @@ -275,4 +276,25 @@ public LayoutElementParcelable[] newArray(int size) {
return new LayoutElementParcelable[size];
}
};

@Override
public boolean isDirectory() {
return isDirectory;
}

@NonNull
@Override
public String getParcelableName() {
return title;
}

@Override
public long getDate() {
return date;
}

@Override
public long getSize() {
return longSize;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@

import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.Q;
import static com.amaze.filemanager.filesystem.files.FileListSorter.SORT_ASC;
import static com.amaze.filemanager.filesystem.files.FileListSorter.SORT_DSC;

import java.io.File;
import java.lang.ref.WeakReference;
Expand Down Expand Up @@ -51,6 +49,7 @@
import com.amaze.filemanager.filesystem.SafRootHolder;
import com.amaze.filemanager.filesystem.cloud.CloudUtil;
import com.amaze.filemanager.filesystem.files.FileListSorter;
import com.amaze.filemanager.filesystem.files.sort.SortType;
import com.amaze.filemanager.filesystem.root.ListFilesCommand;
import com.amaze.filemanager.ui.activities.MainActivityViewModel;
import com.amaze.filemanager.ui.fragments.CloudSheetFragment;
Expand Down Expand Up @@ -254,17 +253,7 @@ private List<LayoutElementParcelable> getCachedMediaList(
private void postListCustomPathProcess(
@NonNull List<LayoutElementParcelable> list, @NonNull MainFragment mainFragment) {

int sortType = SortHandler.getSortType(context.get(), path);
int sortBy;
int isAscending;

if (sortType <= 3) {
sortBy = sortType;
isAscending = SORT_ASC;
} else {
isAscending = SORT_DSC;
sortBy = sortType - 4;
}
SortType sortType = SortHandler.getSortType(context.get(), path);

MainFragmentViewModel viewModel = mainFragment.getMainFragmentViewModel();

Expand All @@ -289,7 +278,7 @@ private void postListCustomPathProcess(
}
}

Collections.sort(list, new FileListSorter(viewModel.getDsort(), sortBy, isAscending));
Collections.sort(list, new FileListSorter(viewModel.getDsort(), sortType));
}

private @Nullable LayoutElementParcelable createListParcelables(HybridFileParcelable baseFile) {
Expand Down
16 changes: 10 additions & 6 deletions app/src/main/java/com/amaze/filemanager/database/SortHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import com.amaze.filemanager.application.AppConfig;
import com.amaze.filemanager.database.models.explorer.Sort;
import com.amaze.filemanager.filesystem.files.sort.SortType;

import android.content.Context;
import android.content.SharedPreferences;
Expand Down Expand Up @@ -60,31 +61,34 @@ public static SortHandler getInstance() {
return SortHandlerHolder.INSTANCE;
}

public static int getSortType(Context context, String path) {
public static SortType getSortType(Context context, String path) {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
final Set<String> onlyThisFloders =
sharedPref.getStringSet(PREFERENCE_SORTBY_ONLY_THIS, new HashSet<>());
final boolean onlyThis = onlyThisFloders.contains(path);
final int globalSortby = Integer.parseInt(sharedPref.getString("sortby", "0"));
SortType globalSortType = SortType.getDirectorySortType(globalSortby);
if (!onlyThis) {
return globalSortby;
return globalSortType;
}
Sort sort = SortHandler.getInstance().findEntry(path);
if (sort == null) {
return globalSortby;
return globalSortType;
}
return sort.type;
return SortType.getDirectorySortType(sort.type);
}

public void addEntry(Sort sort) {
public void addEntry(String path, SortType sortType) {
Sort sort = new Sort(path, sortType.toDirectorySortInt());
database.sortDao().insert(sort).subscribeOn(Schedulers.io()).subscribe();
}

public void clear(String path) {
database.sortDao().clear(path).subscribeOn(Schedulers.io()).subscribe();
}

public void updateEntry(Sort oldSort, Sort newSort) {
public void updateEntry(Sort oldSort, String newPath, SortType newSortType) {
Sort newSort = new Sort(newPath, newSortType.toDirectorySortInt());
database.sortDao().update(newSort).subscribeOn(Schedulers.io()).subscribe();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.slf4j.LoggerFactory;

import com.amaze.filemanager.fileoperations.filesystem.OpenMode;
import com.amaze.filemanager.filesystem.files.sort.ComparableParcelable;
import com.amaze.filemanager.filesystem.ftp.ExtensionsKt;
import com.amaze.filemanager.utils.Utils;

Expand All @@ -44,7 +45,7 @@
import net.schmizz.sshj.sftp.RemoteResourceInfo;
import net.schmizz.sshj.xfer.FilePermission;

public class HybridFileParcelable extends HybridFile implements Parcelable {
public class HybridFileParcelable extends HybridFile implements Parcelable, ComparableParcelable {
private final Logger LOG = LoggerFactory.getLogger(HybridFileParcelable.class);

private long date, size;
Expand Down Expand Up @@ -255,4 +256,10 @@ public int hashCode() {
result = 37 * result + (int) (date ^ date >>> 32);
return result;
}

@NonNull
@Override
public String getParcelableName() {
return getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,77 @@

package com.amaze.filemanager.filesystem.files

import androidx.annotation.IntDef
import com.amaze.filemanager.adapters.data.LayoutElementParcelable
import com.amaze.filemanager.filesystem.files.sort.ComparableParcelable
import com.amaze.filemanager.filesystem.files.sort.DirSortBy
import com.amaze.filemanager.filesystem.files.sort.SortBy
import com.amaze.filemanager.filesystem.files.sort.SortType
import java.lang.Long
import java.util.Locale

/**
* [Comparator] implementation to sort [LayoutElementParcelable]s.
*/
class FileListSorter(
@DirSortMode dirArg: Int,
@SortBy sortArg: Int,
@SortOrder ascArg: Int
) : Comparator<LayoutElementParcelable> {
dirArg: DirSortBy,
sortType: SortType,
searchTerm: String?
) : Comparator<ComparableParcelable> {
private var dirsOnTop = dirArg
private val asc = ascArg
private val sort = sortArg
private val asc: Int = sortType.sortOrder.sortFactor
private val sort: SortBy = sortType.sortBy

private val relevanceComparator: Comparator<ComparableParcelable> by lazy {
if (searchTerm == null) {
// no search term given, so every result is equally relevant
Comparator { _, _ ->
0
}
} else {
Comparator { o1, o2 ->
// Sorts in a way that least relevant is first
val comparator = compareBy<ComparableParcelable> {
// first we compare by the match percentage of the name
searchTerm.length.toDouble() / it.getParcelableName().length.toDouble()
}.thenBy {
// if match percentage is the same, we compare if the name starts with the match
it.getParcelableName().startsWith(searchTerm, ignoreCase = true)
}.thenBy { file ->
// if the match in the name could a word because it is surrounded by separators, it could be more relevant
// e.g. "my-cat" more relevant than "mysterious"
file.getParcelableName().split('-', '_', '.', ' ').any {
it.contentEquals(
searchTerm,
ignoreCase = true
)
}
}.thenBy { file ->
// sort by modification date as last resort
file.getDate()
}
// Reverts the sorting to make most relevant first
comparator.compare(o1, o2) * -1
}
}
}

/** Constructor for convenience if there is no searchTerm */
constructor(dirArg: DirSortBy, sortType: SortType) : this(dirArg, sortType, null)

private fun isDirectory(path: ComparableParcelable): Boolean {
return path.isDirectory()
}

private fun isDirectory(path: LayoutElementParcelable): Boolean {
return path.isDirectory
/** Compares the names of [file1] and [file2] */
private fun compareName(file1: ComparableParcelable, file2: ComparableParcelable): Int {
return file1.getParcelableName().compareTo(file2.getParcelableName(), ignoreCase = true)
}

/**
* Compares two elements and return negative, zero and positive integer if first argument is less
* than, equal to or greater than second
*/
override fun compare(file1: LayoutElementParcelable, file2: LayoutElementParcelable): Int {
override fun compare(file1: ComparableParcelable, file2: ComparableParcelable): Int {
/*File f1;

if(!file1.hasSymlink()) {
Expand All @@ -62,13 +108,13 @@ class FileListSorter(
} else {
f2=new File(file1.getSymlink());
}*/
if (dirsOnTop == SORT_DIR_ON_TOP) {
if (dirsOnTop == DirSortBy.DIR_ON_TOP) {
if (isDirectory(file1) && !isDirectory(file2)) {
return -1
} else if (isDirectory(file2) && !isDirectory(file1)) {
return 1
}
} else if (dirsOnTop == SORT_FILE_ON_TOP) {
} else if (dirsOnTop == DirSortBy.FILE_ON_TOP) {
if (isDirectory(file1) && !isDirectory(file2)) {
return 1
} else if (isDirectory(file2) && !isDirectory(file1)) {
Expand All @@ -77,67 +123,46 @@ class FileListSorter(
}

when (sort) {
SORT_BY_NAME -> {
SortBy.NAME -> {
// sort by name
return asc * file1.title.compareTo(file2.title, ignoreCase = true)
return asc * compareName(file1, file2)
}
SORT_BY_LAST_MODIFIED -> {
SortBy.LAST_MODIFIED -> {
// sort by last modified
return asc * java.lang.Long.valueOf(file1.date).compareTo(file2.date)
return asc * Long.valueOf(file1.getDate()).compareTo(file2.getDate())
}
SORT_BY_SIZE -> {
SortBy.SIZE -> {
// sort by size
return if (!file1.isDirectory && !file2.isDirectory) {
asc * java.lang.Long.valueOf(file1.longSize).compareTo(file2.longSize)
return if (!isDirectory(file1) && !isDirectory(file2)) {
asc * Long.valueOf(file1.getSize()).compareTo(file2.getSize())
} else {
file1.title.compareTo(file2.title, ignoreCase = true)
compareName(file1, file2)
}
}
SORT_BY_TYPE -> {
SortBy.TYPE -> {
// sort by type
return if (!file1.isDirectory && !file2.isDirectory) {
val ext_a = getExtension(file1.title)
val ext_b = getExtension(file2.title)
return if (!isDirectory(file1) && !isDirectory(file2)) {
val ext_a = getExtension(file1.getParcelableName())
val ext_b = getExtension(file2.getParcelableName())
val res = asc * ext_a.compareTo(ext_b)
if (res == 0) {
asc * file1.title.compareTo(file2.title, ignoreCase = true)
asc * compareName(file1, file2)
} else {
res
}
} else {
file1.title.compareTo(file2.title, ignoreCase = true)
compareName(file1, file2)
}
}
else -> return 0
SortBy.RELEVANCE -> {
// sort by relevance to the search query
return asc * relevanceComparator.compare(file1, file2)
}
}
}

companion object {

const val SORT_BY_NAME = 0
const val SORT_BY_LAST_MODIFIED = 1
const val SORT_BY_SIZE = 2
const val SORT_BY_TYPE = 3

const val SORT_DIR_ON_TOP = 0
const val SORT_FILE_ON_TOP = 1
const val SORT_NONE_ON_TOP = 2

const val SORT_ASC = 1
const val SORT_DSC = -1

@Retention(AnnotationRetention.SOURCE)
@IntDef(SORT_BY_NAME, SORT_BY_LAST_MODIFIED, SORT_BY_SIZE, SORT_BY_TYPE)
annotation class SortBy

@Retention(AnnotationRetention.SOURCE)
@IntDef(SORT_DIR_ON_TOP, SORT_FILE_ON_TOP, SORT_NONE_ON_TOP)
annotation class DirSortMode

@Retention(AnnotationRetention.SOURCE)
@IntDef(SORT_ASC, SORT_DSC)
annotation class SortOrder

/**
* Convenience method to get the file extension in given path.
*
Expand Down
Loading