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

Allow case sensitive local file rename #3593

Merged
merged 8 commits into from
Nov 17, 2022
106 changes: 67 additions & 39 deletions app/src/main/java/com/amaze/filemanager/filesystem/Operations.java
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,63 @@ public static void rename(

private final DataUtils dataUtils = DataUtils.getInstance();

private final Boolean isCaseSensitiveRename =
Copy link
Member

@EmmanuelMess EmmanuelMess Oct 26, 2022

Choose a reason for hiding this comment

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

Why is the case sensitive rename different? Shouldn't there just be filesystems that support case sensitive renames and filesystems that do not? And the renames be the same function?

Copy link
Author

Choose a reason for hiding this comment

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

This approach only cares about the value changed and not the type of filesystem. Although it does unnecessary double renaming in case-sensitive filesystem, it doesn't need to check the type of filesystem.

The 'localRename' function (can't think of a better name) is extracted from the original code to avoid code duplication.

Copy link
Member

Choose a reason for hiding this comment

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

Why is the case sensitive rename different? Shouldn't there just be filesystems that support case sensitive renames and filesystems that do not? And the renames be the same function?

I think this should be okay as we know for sure most file systems that android supports doesn't support case sensitivity (exception ext4 in which case it needs to be kernel enabled). But I think it would make a marginal performance improvement if second part of the AND condition in this is used first.

Copy link
Member

Choose a reason for hiding this comment

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

But I think it would make a marginal performance improvement if second part of the AND condition in this is used first.

I am more concerned on code legibility.

Copy link
Author

Choose a reason for hiding this comment

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

I added comment on the purpose of isCaseSensitiveRename variable and refactored double rename part. Hopefully it is clearer.

!oldFile.getSimpleName().equals(newFile.getSimpleName()) &&
oldFile.getSimpleName().equalsIgnoreCase(newFile.getSimpleName());

// random string that is appended to file to prevent name collision
private static final String TEMP_FILE_EXT = "qwerty";
VishalNehra marked this conversation as resolved.
Show resolved Hide resolved

private boolean defaultRename(
@NonNull HybridFile oldFile,
@NonNull HybridFile newFile
) {
File file = new File(oldFile.getPath());
File file1 = new File(newFile.getPath());
boolean result = false;

switch (oldFile.getMode()) {
case FILE:
int mode = checkFolder(file.getParentFile(), context);
if (mode == 2) {
errorCallBack.launchSAF(oldFile, newFile);
stevealexr marked this conversation as resolved.
Show resolved Hide resolved
} else if (mode == 1 || mode == 0) {
try {
RenameOperation.renameFolder(file, file1, context);
result = true;
} catch (ShellNotRunningException e) {
LOG.warn("failed to rename file in local filesystem", e);
}
boolean a = !file.exists() && file1.exists();
if (!a && rootMode) {
try {
RenameFileCommand.INSTANCE.renameFile(file.getPath(), file1.getPath());
result = true;
} catch (ShellNotRunningException e) {
LOG.warn("failed to rename file in local filesystem", e);
}
oldFile.setMode(OpenMode.ROOT);
newFile.setMode(OpenMode.ROOT);
a = !file.exists() && file1.exists();
}
errorCallBack.done(newFile, a);
Copy link
Member

Choose a reason for hiding this comment

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

Need a fallback mechanism in case the second rename fails, fallback should attempt to rename back to original file name.

Copy link
Author

@stevealexr stevealexr Oct 26, 2022

Choose a reason for hiding this comment

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

I added a simple check that attempt to rename back to original file name once, not sure if it is sufficient.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks, changes seem fine. It would be a good practice to LOG.warn the inner else condition in case second rename fails.
Also, codacy does seem to complain can you fix the issue:

Check warning on line 673 in app/src/main/java/com/amaze/filemanager/filesystem/Operations.java

Codacy Production
/ Codacy Static Code Analysis
app/src/main/java/com/amaze/filemanager/filesystem/Operations.java#L673
These nested if statements could be combined

Copy link
Author

Choose a reason for hiding this comment

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

I added a log message and combined those if statements.

}
break;
case ROOT:
try {
RenameFileCommand.INSTANCE.renameFile(file.getPath(), file1.getPath());
result = true;
} catch (ShellNotRunningException e) {
LOG.warn("failed to rename file in root", e);
}

newFile.setMode(OpenMode.ROOT);
errorCallBack.done(newFile, true);
break;
}
return result;
}

private Function<DocumentFile, Void> safRenameFile =
input -> {
boolean result = false;
Expand All @@ -462,7 +519,7 @@ protected Void doInBackground(Void... params) {
return null;
}

if (newFile.exists()) {
if (newFile.exists() && !isCaseSensitiveRename) {
errorCallBack.exists(newFile);
return null;
}
Expand Down Expand Up @@ -610,44 +667,15 @@ public Boolean executeWithFtpClient(@NonNull FTPClient ftpClient)
false));
return null;
} else {
File file = new File(oldFile.getPath());
File file1 = new File(newFile.getPath());
switch (oldFile.getMode()) {
case FILE:
int mode = checkFolder(file.getParentFile(), context);
if (mode == 2) {
errorCallBack.launchSAF(oldFile, newFile);
} else if (mode == 1 || mode == 0) {
try {
RenameOperation.renameFolder(file, file1, context);
} catch (ShellNotRunningException e) {
LOG.warn("failed to rename file in local filesystem", e);
}
boolean a = !file.exists() && file1.exists();
if (!a && rootMode) {
try {
RenameFileCommand.INSTANCE.renameFile(file.getPath(), file1.getPath());
} catch (ShellNotRunningException e) {
LOG.warn("failed to rename file in local filesystem", e);
}
oldFile.setMode(OpenMode.ROOT);
newFile.setMode(OpenMode.ROOT);
a = !file.exists() && file1.exists();
}
errorCallBack.done(newFile, a);
return null;
}
break;
case ROOT:
try {
RenameFileCommand.INSTANCE.renameFile(file.getPath(), file1.getPath());
} catch (ShellNotRunningException e) {
LOG.warn("failed to rename file in root", e);
}

newFile.setMode(OpenMode.ROOT);
errorCallBack.done(newFile, true);
break;
if (isCaseSensitiveRename) {
HybridFile tempFile = new HybridFile(
oldFile.mode, oldFile.getPath().concat(TEMP_FILE_EXT)
);
if (defaultRename(oldFile, tempFile)) {
defaultRename(tempFile, newFile);
}
} else {
defaultRename(oldFile, newFile);
}
}
return null;
Expand Down