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
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,15 @@ class SearchRecyclerViewAdapter :
(AppConfig.getInstance().mainActivityContext as MainActivity?)
?.appbar?.searchView?.hideSearchView()
}
teleportIV.setOnClickListener {
val (file, _) = getItem(adapterPosition)
if (!file.isDirectory) {
(AppConfig.getInstance().mainActivityContext as MainActivity?)
?.teleportToFile(file.path)
(AppConfig.getInstance().mainActivityContext as MainActivity?)
?.appbar?.searchView?.hideSearchView()
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,44 @@ public void goToMain(String path, boolean hideFab) {
}
}

public void teleportToFile(String filePath) {
ParentAndFileName split = splitParentAndFileName(filePath);
if (split == null) {
goToMain(filePath);
return;
}
scrollToFileName = split.fileName;
goToMain(split.parentPath);
}

/**
* Splits a file path into its parent directory path and file name. Returns null if the path has
* no parent (no "/" found).
*/
static ParentAndFileName splitParentAndFileName(String filePath) {
int lastSlash = filePath.lastIndexOf("/");
if (lastSlash <= 0) {
return null;
}
String parentPath = filePath.substring(0, lastSlash);
String fileName = filePath.substring(lastSlash + 1);
return new ParentAndFileName(parentPath, fileName);
}

/**
* Simple holder for a file's parent path and name. Not an android.util.Pair to keep it usable in
* plain JUnit tests (android.* classes are stubbed out in non-Robolectric unit tests).
*/
static class ParentAndFileName {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for not using HybridFile here? Does this work with external searches: SMB, FTP, cloud, etc?

final String parentPath;
final String fileName;

ParentAndFileName(String parentPath, String fileName) {
this.parentPath = parentPath;
this.fileName = fileName;
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
Expand Down Expand Up @@ -2451,11 +2489,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 @@ -2547,8 +2585,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
Binary file added app/src/main/res/drawable/ic_location_on_24dp.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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_location_on_24dp"
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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 org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test

/**
* Tests for [MainActivity.splitParentAndFileName], used by the search
* "teleport to file" feature to navigate to a file's parent folder.
*/
class MainActivityTeleportTest {

/** Verifies a normal file path is split into its parent directory and file name. */
@Test
fun `splits a normal file path into parent and file name`() {
val result = MainActivity.splitParentAndFileName("/storage/emulated/0/Documents/report.pdf")

assertEquals("/storage/emulated/0/Documents", result?.parentPath)
assertEquals("report.pdf", result?.fileName)
}

/** Verifies a path with only one directory level splits correctly. */
@Test
fun `splits a path with a single directory level`() {
val result = MainActivity.splitParentAndFileName("/storage/file.txt")

assertEquals("/storage", result?.parentPath)
assertEquals("file.txt", result?.fileName)
}

/** Verifies null is returned when the path has no parent directory. */
@Test
fun `returns null when path has no parent directory`() {
val result = MainActivity.splitParentAndFileName("file.txt")

assertNull(result)
}

/** Verifies file names with spaces and special characters split correctly. */
@Test
fun `handles file names containing spaces and special characters`() {
val result =
MainActivity.splitParentAndFileName(
"/storage/emulated/0/WhatsApp/Media/Yasmin Hosam (1).pdf",
)

assertEquals("/storage/emulated/0/WhatsApp/Media", result?.parentPath)
assertEquals("Yasmin Hosam (1).pdf", result?.fileName)
}
}