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
118 changes: 81 additions & 37 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,74 @@ public static void rename(

private final DataUtils dataUtils = DataUtils.getInstance();

/**
* Determines whether double rename is required based on original and new file name regardless
* of the case-sensitivity of the filesystem
Copy link
Member

Choose a reason for hiding this comment

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

This only depends on the case sensitivity of the filesystem.

Copy link
Author

Choose a reason for hiding this comment

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

I change it to a filesystem case-sensitivity check by checking whether both upper case and lower case input name for file exists return true.

Copy link
Member

Choose a reason for hiding this comment

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

This new solution seem to be costly solution and calculating this each time is redundant. If a filesystem is case insensitive then it'll always be case insensitive, so you can persist the result of this in shared preferences and use that from next time.

Copy link
Author

Choose a reason for hiding this comment

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

There are multiple local filesystems like sdcard storage and internal storage. The types of filesystem need to be stored with the mount points. Rolling back to previous solution seems easier.

Copy link
Member

Choose a reason for hiding this comment

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

Sure please revert back to your old solution. Also note that the new code will double rename even when FS is insensitive and new file and old file names aren't similar at all.
oldFile.getSimpleName().equalsIgnoreCase(newFile.getSimpleName())
&& !oldFile.getSimpleName().equals(newFile.getSimpleName());

Please also note that I've increased the bounty price for this issue to 10USD you can claim once this PR is merged from the linked issue.

*/
private final boolean isCaseSensitiveRename =
oldFile.getSimpleName().equalsIgnoreCase(newFile.getSimpleName())
&& !oldFile.getSimpleName().equals(newFile.getSimpleName());

/**
* random string that is appended to file to prevent name collision, max file name is 255
* bytes
*/
private static final String TEMP_FILE_EXT = "u0CtHRqWUnvxIaeBQ@nY2umVm9MDyR1P";

private boolean localRename(@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 == 1 || mode == 0) {
try {
RenameOperation.renameFolder(file, file1, context);
} catch (ShellNotRunningException e) {
LOG.warn("failed to rename file in local filesystem", e);
}
result = !file.exists() && file1.exists();
if (!result && 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);
result = !file.exists() && file1.exists();
}
}
break;
case ROOT:
try {
result = RenameFileCommand.INSTANCE.renameFile(file.getPath(), file1.getPath());
} catch (ShellNotRunningException e) {
LOG.warn("failed to rename file in root", e);
}
newFile.setMode(OpenMode.ROOT);
break;
}
return result;
}

private boolean localDoubleRename(@NonNull HybridFile oldFile, @NonNull HybridFile newFile) {
HybridFile tempFile = new HybridFile(oldFile.mode, oldFile.getPath().concat(TEMP_FILE_EXT));
if (localRename(oldFile, tempFile)) {
if (localRename(tempFile, newFile)) {
return true;
} else {
// attempts to rollback
// changes the temporary file name back to original file name
LOG.warn("reverting temporary file rename");
return localRename(tempFile, oldFile);
}
}
return false;
}

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

if (newFile.exists()) {
if (newFile.exists() && !isCaseSensitiveRename) {
errorCallBack.exists(newFile);
return null;
}
Expand Down Expand Up @@ -611,44 +679,20 @@ public Boolean executeWithFtpClient(@NonNull FTPClient ftpClient)
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);
}
if (oldFile.getMode() == OpenMode.FILE) {
int mode = checkFolder(file.getParentFile(), context);
if (mode == 2) {
errorCallBack.launchSAF(oldFile, newFile);
}
}

newFile.setMode(OpenMode.ROOT);
errorCallBack.done(newFile, true);
break;
boolean result;
if (isCaseSensitiveRename) {
result = localDoubleRename(oldFile, newFile);
} else {
result = localRename(oldFile, newFile);
}
errorCallBack.done(newFile, result);
}
return null;
}
Expand Down