Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)

onView(withText(targetFile.name))
.check(matches(isDisplayed()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@
package com.amaze.filemanager.adapters

import android.content.Context
import android.graphics.PorterDuff
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DiffUtil
Expand Down Expand Up @@ -91,24 +93,29 @@ class SearchRecyclerViewAdapter :
holder.filePathTV.text = file.path.substring(0, file.path.lastIndexOf("/"))

holder.colorView.setBackgroundColor(getRandomColor(holder.colorView.context))
holder.teleportIV.setColorFilter(colorPreference.accent, PorterDuff.Mode.SRC_ATOP)

if (file.isDirectory) {
holder.colorView.setBackgroundColor(colorPreference.primaryFirstTab)
holder.teleportIV.visibility = View.GONE
} else {
holder.colorView.setBackgroundColor(colorPreference.accent)
holder.teleportIV.visibility = View.VISIBLE
}
}

inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val fileNameTV: AppCompatTextView
val filePathTV: AppCompatTextView
val colorView: View
val teleportIV: AppCompatImageView

init {

fileNameTV = view.findViewById(R.id.searchItemFileNameTV)
filePathTV = view.findViewById(R.id.searchItemFilePathTV)
colorView = view.findViewById(R.id.searchItemSampleColorView)
teleportIV = view.findViewById(R.id.searchItemTeleportIV)

view.setOnClickListener {

Expand All @@ -127,6 +134,14 @@ class SearchRecyclerViewAdapter :
(AppConfig.getInstance().mainActivityContext as MainActivity?)
?.appbar?.searchView?.hideSearchView()
}
teleportIV.setOnClickListener {
val (file, _) = getItem(adapterPosition)
if (!file.isDirectory) {
val activity = AppConfig.getInstance().mainActivityContext as MainActivity?
activity?.teleportToFile(file)
activity?.appbar?.searchView?.hideSearchView()
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -2456,11 +2467,11 @@ private void initLeftRightAndTopDragListeners(boolean destroy, boolean shouldInv
/**
* Invoke {@link FtpServerFragment#changeFTPServerPath(String)} to change FTP server share path.
*
* @param dialog
* @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) {
Expand Down Expand Up @@ -2552,8 +2563,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
*/
@Override
public void onFolderChooserDismissed(@NonNull FolderChooserDialog dialog) {
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_folder_arrow_right_outline.xml
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>
15 changes: 13 additions & 2 deletions app/src/main/res/layout/search_row_item.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/searchItemTeleportIV"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="12dp"
android:padding="2dp"
app:srcCompat="@drawable/ic_folder_arrow_right_outline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/searchItemFileNameTV"
android:layout_width="0dp"
Expand All @@ -30,7 +41,7 @@
android:layout_marginBottom="2dp"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@id/searchItemFilePathTV"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/searchItemTeleportIV"
app:layout_constraintStart_toEndOf="@id/searchItemSampleColorView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0" />
Expand All @@ -46,7 +57,7 @@
android:letterSpacing="0.05"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/searchItemTeleportIV"
app:layout_constraintStart_toEndOf="@id/searchItemSampleColorView"
app:layout_constraintTop_toBottomOf="@id/searchItemFileNameTV"
app:layout_constraintVertical_bias="0" />
Expand Down
Loading