-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Feature/search teleport to file #4713
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
base: release/4.0
Are you sure you want to change the base?
Changes from all commits
9713c31
578543f
f19ba54
f9aca24
8f00e28
ab0b27b
4f2d5a6
b0465ff
cc522c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| /* | ||
| * Copyright (C) 2014-2026 Arpit Khurana <arpitkh96@gmail.com>, Vishal Nehra <vishalmeham2@gmail.com>, | ||
| * Emmanuel Messulam<emmanuelbendavid@gmail.com>, Raymond Lai <airwave209gt at gmail.com> and Contributors. | ||
| * | ||
| * This file is part of Amaze File Manager. | ||
| * | ||
| * Amaze File Manager is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the License, or | ||
| * (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| package com.amaze.filemanager.ui.activities | ||
|
|
||
| import androidx.test.espresso.Espresso.onView | ||
| import androidx.test.espresso.assertion.ViewAssertions.matches | ||
| import androidx.test.espresso.matcher.ViewMatchers.isDisplayed | ||
| import androidx.test.espresso.matcher.ViewMatchers.withText | ||
| import androidx.test.ext.junit.rules.ActivityScenarioRule | ||
| import androidx.test.filters.LargeTest | ||
| import com.amaze.filemanager.fileoperations.filesystem.OpenMode | ||
| import com.amaze.filemanager.filesystem.HybridFile | ||
| import org.junit.After | ||
| import org.junit.Before | ||
| import org.junit.Rule | ||
| import org.junit.Test | ||
| import java.io.File | ||
|
|
||
| /** | ||
| * Instrumented (emulator/device) test verifying that [MainActivity.teleportToFile] | ||
| * actually scrolls the target file into view within the real file-list RecyclerView, | ||
| * on top of the headless assertions in [MainActivityTeleportTest]. | ||
| * | ||
| * Uses plain Espresso view matching (checking the target file's name is displayed on | ||
| * screen) rather than accessing MainFragment's `listView`/`adapter` fields directly, | ||
| * since those are private -- this keeps the test decoupled from internal implementation | ||
| * details, following the same style as the existing TextEditorActivityEspressoTest. | ||
| */ | ||
| @LargeTest | ||
| class MainActivityTeleportScrollInstrumentedTest { | ||
| @get:Rule | ||
| val activityRule = ActivityScenarioRule(MainActivity::class.java) | ||
|
|
||
| private lateinit var testDir: File | ||
| private lateinit var targetFile: File | ||
|
|
||
| /** | ||
| * Creates a temp directory (inside the app's own external files dir, which needs no | ||
| * runtime permission) with enough files that the target file starts off-screen and a | ||
| * real scroll is required to bring it into view. | ||
| */ | ||
| @Before | ||
| fun setUp() { | ||
| // Use the shared storage root (NOT getExternalFilesDir / Android/data), since Amaze | ||
| // treats any path under Android/data specially (Scoped Storage) and prompts for SAF | ||
| // access before browsing there, even for its own app-specific folder. The shared | ||
| // root instead relies on MANAGE_EXTERNAL_STORAGE, which must be pre-granted via: | ||
| // adb shell appops set <applicationId> MANAGE_EXTERNAL_STORAGE allow | ||
| testDir = File(android.os.Environment.getExternalStorageDirectory(), "AmazeTeleportScrollTest") | ||
| val created = testDir.mkdirs() | ||
| check(created || testDir.isDirectory) { | ||
| "Failed to create test directory at ${testDir.absolutePath} " + | ||
| "(mkdirs() returned $created, exists=${testDir.exists()}, isDirectory=${testDir.isDirectory}). " + | ||
| "Did you run: adb shell appops set <applicationId> MANAGE_EXTERNAL_STORAGE allow ?" | ||
| } | ||
|
|
||
| for (i in 1..40) { | ||
| val file = File(testDir, "file_%02d.txt".format(i)) | ||
| check(file.createNewFile() || file.exists()) { | ||
| "Failed to create ${file.absolutePath} - parent exists: ${testDir.exists()}" | ||
| } | ||
| } | ||
| targetFile = File(testDir, "file_40.txt") | ||
| } | ||
|
|
||
| /** | ||
| * Removes the temp test directory after the test finishes. | ||
| */ | ||
| @After | ||
| fun tearDown() { | ||
| if (::testDir.isInitialized) { | ||
| testDir.deleteRecursively() | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Verifies the target file's row becomes visible on screen after teleportToFile is called. | ||
| */ | ||
| @Test | ||
| fun testTeleportScrollsTargetFileIntoView() { | ||
| activityRule.scenario.onActivity { activity -> | ||
| val file = HybridFile(OpenMode.FILE, targetFile.absolutePath) | ||
| activity.teleportToFile(file) | ||
| } | ||
|
|
||
| // Give the async directory load + scroll a moment to complete before asserting. | ||
| Thread.sleep(2000) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No sleeps, use event based callbacks, unless it is a hack, which should be documented. Also, the emulator should not have animations of any kind when running instrumented tests, per docs. |
||
|
|
||
| onView(withText(targetFile.name)) | ||
| .check(matches(isDisplayed())) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1074,6 +1074,17 @@ public void goToMain(String path, boolean hideFab) { | |
| } | ||
| } | ||
|
|
||
| public void teleportToFile(HybridFile file) { | ||
| String parentPath = file.getParent(this); | ||
| if (parentPath == null) { | ||
| scrollToFileName = null; | ||
| goToMain(file.getPath()); | ||
| return; | ||
| } | ||
| scrollToFileName = file.getName(this); | ||
| goToMain(parentPath); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean onCreateOptionsMenu(Menu menu) { | ||
| MenuInflater menuInflater = getMenuInflater(); | ||
|
|
@@ -2456,11 +2467,11 @@ private void initLeftRightAndTopDragListeners(boolean destroy, boolean shouldInv | |
| /** | ||
| * Invoke {@link FtpServerFragment#changeFTPServerPath(String)} to change FTP server share path. | ||
| * | ||
| * @param dialog | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't reorder lines to leave |
||
| * @param folder selected folder | ||
| * @see FtpServerFragment#changeFTPServerPath(String) | ||
| * @see FolderChooserDialog | ||
| * @see com.afollestad.materialdialogs.folderselector.FolderChooserDialog.FolderCallback | ||
| * @param dialog | ||
| * @param folder selected folder | ||
| */ | ||
| @Override | ||
| public void onFolderSelection(@NonNull FolderChooserDialog dialog, @NonNull File folder) { | ||
|
|
@@ -2540,6 +2551,10 @@ public String getScrollToFileName() { | |
| return this.scrollToFileName; | ||
| } | ||
|
|
||
| public void setScrollToFileName(String scrollToFileName) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add |
||
| this.scrollToFileName = scrollToFileName; | ||
| } | ||
|
|
||
| /** | ||
| * Set list item selected value | ||
| * | ||
|
|
@@ -2552,8 +2567,8 @@ public void setListItemSelected(boolean value) { | |
| /** | ||
| * Do nothing other than dismissing the folder selection dialog. | ||
| * | ||
| * @see com.afollestad.materialdialogs.folderselector.FolderChooserDialog.FolderCallback | ||
| * @param dialog | ||
| * @see com.afollestad.materialdialogs.folderselector.FolderChooserDialog.FolderCallback | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't reorder lines to leave @see below other stuff. |
||
| */ | ||
| @Override | ||
| public void onFolderChooserDismissed(@NonNull FolderChooserDialog dialog) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -228,20 +228,14 @@ class MainFragmentViewModel : ViewModel() { | |
| /** | ||
| * Get the position of an item | ||
| */ | ||
| fun getScrollPosition(title: String): MutableLiveData<Int> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was this changed? Doesn't it have performance implications?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i was having a bug in teleporting to a file already in the currently open folder didn't scroll or highlight anything. Root cause is that i fixed it by moving
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While fixing that I also noticed |
||
| val mutableLiveData: MutableLiveData<Int> = MutableLiveData(-1) | ||
|
|
||
| viewModelScope.launch(Dispatchers.IO) { | ||
| adapterListItems?.forEachIndexed { index, item -> | ||
| if (item.layoutElementParcelable != null && | ||
| item.layoutElementParcelable?.title.equals(title) | ||
| ) { | ||
| item.setChecked(true) | ||
| mutableLiveData.postValue(index) | ||
| } | ||
| fun getScrollPosition(title: String): Int { | ||
| adapterListItems?.forEachIndexed { index, item -> | ||
| if (item.layoutElementParcelable?.title == title) { | ||
| item.setChecked(true) | ||
| return index | ||
| } | ||
| } | ||
| return mutableLiveData | ||
| return -1 | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <!-- drawable/ic_folder_arrow_right_outline.xml --> | ||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| android:height="24dp" | ||
| android:width="24dp" | ||
| android:viewportWidth="24" | ||
| android:viewportHeight="24"> | ||
| <path | ||
| android:fillColor="#000000" | ||
| android:pathData="M13 19C13 19.34 13.04 19.67 13.09 20H4C2.9 20 2 19.11 2 18V6C2 4.89 2.89 4 4 4H10L12 6H20C21.1 6 22 6.89 22 8V13.81C21.39 13.46 20.72 13.22 20 13.09V8H4V18H13.09C13.04 18.33 13 18.66 13 19M23 19L20 16V18H16V20H20V22L23 19Z" /></vector> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In an emulator test, run as much as possible via the UI, in this case, run a search and press the teleport to file button. You might want to implement this as a Roboletric test, but that is your choice..