kopia lustrzana https://github.com/ryukoposting/Signal-Android
Ensure unique names are used when saving batches of files.
rodzic
bad1cc1571
commit
282639469d
|
@ -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 },
|
||||||
|
|
Ładowanie…
Reference in New Issue