Skip to content

Commit 9cb6bea

Browse files
committed
Fix search not working in drawer category views
Drawer categories (Quick Access, Recent Files, Images, Videos, Audio, Documents, APKs, Trash Bin) use numeric paths ("0"–"7") instead of real filesystem directories. BasicSearch expects a filesystem path, so searches silently returned no results when run from these views. When the current view uses OpenMode.CUSTOM or OpenMode.TRASH_BIN, search now uses ListElementsSearch to filter the visible dataset (MainFragmentViewModel.listElements) instead of performing a filesystem search. Indexed and deep search modes are disabled for these views because they do not support filesystem-backed search escalation. Add ListElementsSearch unit tests covering matching, back/header filtering, and no-result behavior.
1 parent 2e38298 commit 9cb6bea

4 files changed

Lines changed: 218 additions & 8 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (C) 2014-2024 Arpit Khurana <arpitkh96@gmail.com>, Vishal Nehra <vishalmeham2@gmail.com>,
3+
* Emmanuel Messulam<emmanuelbendavid@gmail.com>, Raymond Lai <airwave209gt at gmail.com> and Contributors.
4+
*
5+
* This file is part of Amaze File Manager.
6+
*
7+
* Amaze File Manager is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem
22+
23+
import com.amaze.filemanager.adapters.data.LayoutElementParcelable
24+
import kotlinx.coroutines.isActive
25+
import kotlin.coroutines.coroutineContext
26+
27+
class ListElementsSearch(
28+
query: String,
29+
path: String,
30+
searchParameters: SearchParameters,
31+
private val items: List<LayoutElementParcelable>,
32+
) : FileSearch(query, path, searchParameters) {
33+
override suspend fun search(filter: SearchFilter) {
34+
for (item in items) {
35+
if (!coroutineContext.isActive) {
36+
break
37+
}
38+
39+
if (item.isBack || item.header) {
40+
continue
41+
}
42+
43+
val resultRange = filter.searchFilter(item.title)
44+
if (resultRange != null) {
45+
publishProgress(item.generateBaseFile(), resultRange)
46+
}
47+
}
48+
}
49+
}

app/src/main/java/com/amaze/filemanager/ui/activities/MainActivityViewModel.kt

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ import com.amaze.filemanager.adapters.data.LayoutElementParcelable
3434
import com.amaze.filemanager.application.AppConfig
3535
import com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem.BasicSearch
3636
import com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem.DeepSearch
37+
import com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem.FileSearch
3738
import com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem.IndexedSearch
39+
import com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem.ListElementsSearch
3840
import com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem.SearchParameters
3941
import com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem.SearchResult
4042
import com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem.searchParametersFromBoolean
@@ -117,18 +119,30 @@ class MainActivityViewModel(val applicationContext: Application) :
117119
query: String,
118120
): LiveData<List<SearchResult>> {
119121
val searchParameters = createSearchParameters(mainActivity)
120-
121122
val path = mainActivity.currentMainFragment?.currentPath ?: ""
123+
val openMode =
124+
mainActivity.currentMainFragment?.mainFragmentViewModel?.openMode ?: OpenMode.FILE
122125

123-
val basicSearch = BasicSearch(query, path, searchParameters, this.applicationContext)
126+
val fileSearch: FileSearch =
127+
if (openMode == OpenMode.CUSTOM || openMode == OpenMode.TRASH_BIN) {
128+
ListElementsSearch(
129+
query,
130+
path,
131+
searchParameters,
132+
mainActivity.currentMainFragment?.mainFragmentViewModel?.listElements
133+
?: emptyList(),
134+
)
135+
} else {
136+
BasicSearch(query, path, searchParameters, this.applicationContext)
137+
}
124138

125139
lastSearchJob =
126140
viewModelScope.launch(Dispatchers.IO) {
127-
basicSearch.search()
141+
fileSearch.search()
128142
}
129143

130-
lastSearchLiveData = basicSearch.foundFilesLiveData
131-
return basicSearch.foundFilesLiveData
144+
lastSearchLiveData = fileSearch.foundFilesLiveData
145+
return fileSearch.foundFilesLiveData
132146
}
133147

134148
/**

app/src/main/java/com/amaze/filemanager/ui/views/appbar/SearchView.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.amaze.filemanager.adapters.SearchRecyclerViewAdapter;
3434
import com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem.SearchResult;
3535
import com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem.SearchResultListSorter;
36+
import com.amaze.filemanager.fileoperations.filesystem.OpenMode;
3637
import com.amaze.filemanager.filesystem.files.sort.DirSortBy;
3738
import com.amaze.filemanager.filesystem.files.sort.SortBy;
3839
import com.amaze.filemanager.filesystem.files.sort.SortOrder;
@@ -272,9 +273,18 @@ private void basicSearch(String s) {
272273
searchResultsHintTV.setVisibility(View.VISIBLE);
273274
searchResultsSortButton.setVisibility(View.VISIBLE);
274275
searchResultsSortHintTV.setVisibility(View.VISIBLE);
275-
deepSearchContainer.setVisibility(View.VISIBLE);
276-
searchMode = 1;
277-
deepSearchButton.setText(mainActivity.getString(R.string.try_indexed_search));
276+
277+
OpenMode openMode =
278+
mainActivity.getCurrentMainFragment().getMainFragmentViewModel().getOpenMode();
279+
280+
if (openMode == OpenMode.CUSTOM || openMode == OpenMode.TRASH_BIN) {
281+
deepSearchContainer.setVisibility(View.GONE);
282+
searchMode = 0;
283+
} else {
284+
deepSearchContainer.setVisibility(View.VISIBLE);
285+
searchMode = 1;
286+
deepSearchButton.setText(mainActivity.getString(R.string.try_indexed_search));
287+
}
278288

279289
mainActivity
280290
.getCurrentMainFragment()
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright (C) 2014-2024 Arpit Khurana <arpitkh96@gmail.com>, Vishal Nehra <vishalmeham2@gmail.com>,
3+
* Emmanuel Messulam<emmanuelbendavid@gmail.com>, Raymond Lai <airwave209gt at gmail.com> and Contributors.
4+
*
5+
* This file is part of Amaze File Manager.
6+
*
7+
* Amaze File Manager is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.amaze.filemanager.asynchronous.asynctasks.searchfilesystem
22+
23+
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
24+
import androidx.test.core.app.ApplicationProvider
25+
import androidx.test.ext.junit.runners.AndroidJUnit4
26+
import com.amaze.filemanager.adapters.data.LayoutElementParcelable
27+
import com.amaze.filemanager.fileoperations.filesystem.OpenMode
28+
import kotlinx.coroutines.test.runTest
29+
import org.junit.Assert
30+
import org.junit.Rule
31+
import org.junit.Test
32+
import org.junit.runner.RunWith
33+
import java.util.EnumSet
34+
35+
@RunWith(AndroidJUnit4::class)
36+
class ListElementsSearchTest {
37+
@get:Rule
38+
val rule = InstantTaskExecutorRule()
39+
40+
@Test
41+
fun testSimpleSearchMatch() {
42+
val search =
43+
ListElementsSearch(
44+
"report",
45+
"/",
46+
EnumSet.noneOf(SearchParameter::class.java),
47+
listOf(
48+
layoutElement("Annual Report.pdf", "/drawer/Annual Report.pdf"),
49+
layoutElement("Vacation Photo.jpg", "/drawer/Vacation Photo.jpg"),
50+
),
51+
)
52+
53+
search.foundFilesLiveData.observeForever { actualResults ->
54+
Assert.assertNotNull(actualResults)
55+
Assert.assertEquals(listOf("Annual Report.pdf"), actualResults.map { it.file.getName() })
56+
Assert.assertEquals(listOf("/drawer/Annual Report.pdf"), actualResults.map { it.file.getPath() })
57+
Assert.assertEquals(listOf(7..12), actualResults.map { it.matchRange })
58+
}
59+
60+
runTest {
61+
search.search()
62+
}
63+
}
64+
65+
@Test
66+
fun testSkipsBackAndHeaderItems() {
67+
val search =
68+
ListElementsSearch(
69+
"report",
70+
"/",
71+
EnumSet.noneOf(SearchParameter::class.java),
72+
listOf(
73+
layoutElement("Report Back", "/drawer/back", isBack = true),
74+
layoutElement("Report Header", "/drawer/header", header = true),
75+
layoutElement("Report File.txt", "/drawer/Report File.txt"),
76+
),
77+
)
78+
79+
search.foundFilesLiveData.observeForever { actualResults ->
80+
Assert.assertNotNull(actualResults)
81+
Assert.assertEquals(listOf("Report File.txt"), actualResults.map { it.file.getName() })
82+
Assert.assertEquals(listOf("/drawer/Report File.txt"), actualResults.map { it.file.getPath() })
83+
}
84+
85+
runTest {
86+
search.search()
87+
}
88+
}
89+
90+
@Test
91+
fun testNoMatchDoesNotPublishResults() {
92+
var observerCalled = false
93+
94+
val search =
95+
ListElementsSearch(
96+
"report",
97+
"/",
98+
EnumSet.noneOf(SearchParameter::class.java),
99+
listOf(
100+
layoutElement("Vacation Photo.jpg", "/drawer/Vacation Photo.jpg"),
101+
layoutElement("Budget 2025.xlsx", "/drawer/Budget 2025.xlsx"),
102+
),
103+
)
104+
105+
search.foundFilesLiveData.observeForever {
106+
observerCalled = true
107+
}
108+
109+
runTest {
110+
search.search()
111+
}
112+
113+
Assert.assertFalse(observerCalled)
114+
}
115+
116+
private fun layoutElement(
117+
title: String,
118+
path: String,
119+
isBack: Boolean = false,
120+
header: Boolean = false,
121+
): LayoutElementParcelable =
122+
LayoutElementParcelable(
123+
ApplicationProvider.getApplicationContext(),
124+
isBack,
125+
title,
126+
path,
127+
"",
128+
"",
129+
"",
130+
0,
131+
header,
132+
"",
133+
false,
134+
false,
135+
OpenMode.FILE,
136+
)
137+
}

0 commit comments

Comments
 (0)