Ensure unique names are used when saving batches of files.

fork-5.53.8
Alex Hart 2021-12-21 15:33:35 -04:00 zatwierdzone przez Greyson Parrelli
rodzic bad1cc1571
commit 282639469d
1 zmienionych plików z 42 dodań i 4 usunięć

Wyświetl plik

@ -18,6 +18,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import org.signal.core.util.MapUtil;
import org.signal.core.util.StreamUtil; import org.signal.core.util.StreamUtil;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
@ -32,8 +33,12 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTask.Attachment, Void, Pair<Integer, String>> { public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTask.Attachment, Void, Pair<Integer, String>> {
@ -47,6 +52,8 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
private final int attachmentCount; private final int attachmentCount;
private final Map<Uri, Set<String>> batchOperationNameCache = new HashMap<>();
public SaveAttachmentTask(Context context) { public SaveAttachmentTask(Context context) {
this(context, 1); this(context, 1);
} }
@ -95,7 +102,14 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
private @Nullable String saveAttachment(Context context, Attachment attachment) throws IOException private @Nullable String saveAttachment(Context context, Attachment attachment) throws IOException
{ {
String contentType = Objects.requireNonNull(MediaUtil.getCorrectedMimeType(attachment.contentType)); String contentType = Objects.requireNonNull(MediaUtil.getCorrectedMimeType(attachment.contentType));
String fileName = generateOutputFileName(contentType, attachment.date); String fileName = attachment.fileName;
if (fileName == null) {
fileName = generateOutputFileName(contentType, attachment.date);
}
fileName = sanitizeOutputFileName(fileName);
Uri outputUri = getMediaStoreContentUriForType(contentType); Uri outputUri = getMediaStoreContentUriForType(contentType);
Uri mediaUri = createOutputUri(outputUri, contentType, fileName); Uri mediaUri = createOutputUri(outputUri, contentType, fileName);
ContentValues updateValues = new ContentValues(); ContentValues updateValues = new ContentValues();
@ -210,6 +224,10 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
return base + "." + extension; return base + "." + extension;
} }
private String sanitizeOutputFileName(@NonNull String fileName) {
return new File(fileName).getName();
}
private @Nullable Uri createOutputUri(@NonNull Uri outputUri, @NonNull String contentType, @NonNull String fileName) private @Nullable Uri createOutputUri(@NonNull Uri outputUri, @NonNull String contentType, @NonNull String fileName)
throws IOException throws IOException
{ {
@ -236,7 +254,7 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
File outputFile = new File(outputDirectory, base + "." + extension); File outputFile = new File(outputDirectory, base + "." + extension);
int i = 0; int i = 0;
while (outputFile.exists()) { while (pathInCache(outputUri, outputFile.getPath()) || outputFile.exists()) {
outputFile = new File(outputDirectory, base + "-" + (++i) + "." + extension); outputFile = new File(outputDirectory, base + "-" + (++i) + "." + extension);
} }
@ -244,6 +262,7 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
throw new IOException("Specified name would not be visible"); throw new IOException("Specified name would not be visible");
} }
putInCache(outputUri, outputFile.getPath());
return Uri.fromFile(outputFile); return Uri.fromFile(outputFile);
} else { } else {
String dir = getExternalPathForType(contentType); String dir = getExternalPathForType(contentType);
@ -254,17 +273,36 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
String outputFileName = fileName; String outputFileName = fileName;
String dataPath = String.format("%s/%s", dir, outputFileName); String dataPath = String.format("%s/%s", dir, outputFileName);
int i = 0; int i = 0;
while (pathTaken(outputUri, dataPath)) { while (pathInCache(outputUri, dataPath) || pathTaken(outputUri, dataPath)) {
Log.d(TAG, "The content exists. Rename and check again."); Log.d(TAG, "The content exists. Rename and check again.");
outputFileName = base + "-" + (++i) + "." + extension; outputFileName = base + "-" + (++i) + "." + extension;
dataPath = String.format("%s/%s", dir, outputFileName); dataPath = String.format("%s/%s", dir, outputFileName);
} }
putInCache(outputUri, outputFileName);
contentValues.put(MediaStore.MediaColumns.DATA, dataPath); contentValues.put(MediaStore.MediaColumns.DATA, dataPath);
} }
return getContext().getContentResolver().insert(outputUri, contentValues); return getContext().getContentResolver().insert(outputUri, contentValues);
} }
private void putInCache(@NonNull Uri outputUri, @NonNull String dataPath) {
Set<String> pathSet = MapUtil.getOrDefault(batchOperationNameCache, outputUri, new HashSet<>());
if (!pathSet.add(dataPath)) {
throw new IllegalStateException("Path already used in data set.");
}
batchOperationNameCache.put(outputUri, pathSet);
}
private boolean pathInCache(@NonNull Uri outputUri, @NonNull String dataPath) {
Set<String> pathSet = batchOperationNameCache.get(outputUri);
if (pathSet == null) {
return false;
}
return pathSet.contains(dataPath);
}
private boolean pathTaken(@NonNull Uri outputUri, @NonNull String dataPath) throws IOException { private boolean pathTaken(@NonNull Uri outputUri, @NonNull String dataPath) throws IOException {
try (Cursor cursor = getContext().getContentResolver().query(outputUri, try (Cursor cursor = getContext().getContentResolver().query(outputUri,
new String[] { MediaStore.MediaColumns.DATA }, new String[] { MediaStore.MediaColumns.DATA },