Fix crash when attempting to save octet-stream data to a media directory.

This commit is contained in:
Cody Henthorne 2022-02-15 09:33:04 -05:00 committed by Greyson Parrelli
parent 874067909d
commit 6164152b15

View file

@ -87,12 +87,17 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
for (Attachment attachment : attachments) { for (Attachment attachment : attachments) {
if (attachment != null) { if (attachment != null) {
directory = saveAttachment(context, attachment); directory = saveAttachment(context, attachment);
if (directory == null) return new Pair<>(FAILURE, null); if (directory == null) {
return new Pair<>(FAILURE, null);
}
} }
} }
if (attachments.length > 1) return new Pair<>(SUCCESS, null); if (attachments.length > 1) {
else return new Pair<>(SUCCESS, directory); return new Pair<>(SUCCESS, null);
} else {
return new Pair<>(SUCCESS, directory);
}
} catch (IOException ioe) { } catch (IOException ioe) {
Log.w(TAG, ioe); Log.w(TAG, ioe);
return new Pair<>(FAILURE, null); return new Pair<>(FAILURE, null);
@ -110,28 +115,22 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
fileName = sanitizeOutputFileName(fileName); fileName = sanitizeOutputFileName(fileName);
Uri outputUri = getMediaStoreContentUriForType(contentType); CreateMediaUriResult result = createMediaUri(getMediaStoreContentUriForType(contentType), contentType, fileName);
Uri mediaUri = createOutputUri(outputUri, contentType, fileName);
ContentValues updateValues = new ContentValues(); ContentValues updateValues = new ContentValues();
if (mediaUri == null) {
Log.w(TAG, "Failed to create mediaUri for " + contentType);
return null;
}
try (InputStream inputStream = PartAuthority.getAttachmentStream(context, attachment.uri)) { try (InputStream inputStream = PartAuthority.getAttachmentStream(context, attachment.uri)) {
if (inputStream == null) { if (inputStream == null) {
return null; return null;
} }
if (Objects.equals(outputUri.getScheme(), ContentResolver.SCHEME_FILE)) { if (Objects.equals(result.outputUri.getScheme(), ContentResolver.SCHEME_FILE)) {
try (OutputStream outputStream = new FileOutputStream(mediaUri.getPath())) { try (OutputStream outputStream = new FileOutputStream(result.mediaUri.getPath())) {
StreamUtil.copy(inputStream, outputStream); StreamUtil.copy(inputStream, outputStream);
MediaScannerConnection.scanFile(context, new String[]{mediaUri.getPath()}, new String[]{contentType}, null); MediaScannerConnection.scanFile(context, new String[] { result.mediaUri.getPath() }, new String[] { contentType }, null);
} }
} else { } else {
try (OutputStream outputStream = context.getContentResolver().openOutputStream(mediaUri, "w")) { try (OutputStream outputStream = context.getContentResolver().openOutputStream(result.mediaUri, "w")) {
long total = StreamUtil.copy(inputStream, outputStream); long total = StreamUtil.copy(inputStream, outputStream);
if (total > 0) { if (total > 0) {
updateValues.put(MediaStore.MediaColumns.SIZE, total); updateValues.put(MediaStore.MediaColumns.SIZE, total);
@ -145,10 +144,10 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
} }
if (updateValues.size() > 0) { if (updateValues.size() > 0) {
getContext().getContentResolver().update(mediaUri, updateValues, null, null); getContext().getContentResolver().update(result.mediaUri, updateValues, null, null);
} }
return outputUri.getLastPathSegment(); return result.outputUri.getLastPathSegment();
} }
private @NonNull Uri getMediaStoreContentUriForType(@NonNull String contentType) { private @NonNull Uri getMediaStoreContentUriForType(@NonNull String contentType) {
@ -186,7 +185,7 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
/** /**
* Returns a path to a shared media (or documents) directory for the type of the file. * Returns a path to a shared media (or documents) directory for the type of the file.
* * <p>
* Note that this method attempts to create a directory if the path returned from * Note that this method attempts to create a directory if the path returned from
* Environment object does not exist yet. The attempt may fail in which case it attempts * Environment object does not exist yet. The attempt may fail in which case it attempts
* to return the default "Document" path. It finally returns null if it also fails. * to return the default "Document" path. It finally returns null if it also fails.
@ -228,7 +227,7 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
return new File(fileName).getName(); return new File(fileName).getName();
} }
private @Nullable Uri createOutputUri(@NonNull Uri outputUri, @NonNull String contentType, @NonNull String fileName) private @NonNull CreateMediaUriResult createMediaUri(@NonNull Uri outputUri, @NonNull String contentType, @NonNull String fileName)
throws IOException throws IOException
{ {
String[] fileParts = getFileNameParts(fileName); String[] fileParts = getFileNameParts(fileName);
@ -241,6 +240,16 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
mimeType = contentType; mimeType = contentType;
} }
if (MediaUtil.isOctetStream(mimeType)) {
if (outputUri.equals(StorageUtil.getAudioUri())) {
mimeType = "audio/*";
} else if (outputUri.equals(StorageUtil.getVideoUri())) {
mimeType = "video/*";
} else if (outputUri.equals(StorageUtil.getImageUri())) {
mimeType = "image/*";
}
}
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName); contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType); contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
@ -250,6 +259,7 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
if (Build.VERSION.SDK_INT > 28) { if (Build.VERSION.SDK_INT > 28) {
int i = 0; int i = 0;
String displayName = fileName; String displayName = fileName;
while (pathInCache(outputUri, displayName) || displayNameTaken(outputUri, displayName)) { while (pathInCache(outputUri, displayName) || displayNameTaken(outputUri, displayName)) {
displayName = base + "-" + (++i) + "." + extension; displayName = base + "-" + (++i) + "." + extension;
} }
@ -271,7 +281,7 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
} }
putInCache(outputUri, outputFile.getPath()); putInCache(outputUri, outputFile.getPath());
return Uri.fromFile(outputFile); return new CreateMediaUriResult(outputUri, Uri.fromFile(outputFile));
} else { } else {
String dir = getExternalPathForType(contentType); String dir = getExternalPathForType(contentType);
if (dir == null) { if (dir == null) {
@ -290,7 +300,16 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
contentValues.put(MediaStore.MediaColumns.DATA, dataPath); contentValues.put(MediaStore.MediaColumns.DATA, dataPath);
} }
return getContext().getContentResolver().insert(outputUri, contentValues); try {
return new CreateMediaUriResult(outputUri, getContext().getContentResolver().insert(outputUri, contentValues));
} catch (RuntimeException e) {
if (e instanceof IllegalArgumentException || e.getCause() instanceof IllegalArgumentException) {
Log.w(TAG, "Unable to create uri in " + outputUri + " with mimeType [" + mimeType + "]");
return new CreateMediaUriResult(StorageUtil.getDownloadUri(), getContext().getContentResolver().insert(StorageUtil.getDownloadUri(), contentValues));
} else {
throw e;
}
}
} }
private void putInCache(@NonNull Uri outputUri, @NonNull String dataPath) { private void putInCache(@NonNull Uri outputUri, @NonNull String dataPath) {
@ -346,8 +365,11 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
result[0] = tokens[0]; result[0] = tokens[0];
if (tokens.length > 1) result[1] = tokens[1]; if (tokens.length > 1) {
else result[1] = ""; result[1] = tokens[1];
} else {
result[1] = "";
}
return result; return result;
} }
@ -361,18 +383,15 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
switch (result.first()) { switch (result.first()) {
case FAILURE: case FAILURE:
Toast.makeText(context, Toast.makeText(context,
context.getResources().getQuantityText(R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, context.getResources().getQuantityText(R.plurals.ConversationFragment_error_while_saving_attachments_to_sd_card, attachmentCount),
attachmentCount),
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG).show();
break; break;
case SUCCESS: case SUCCESS:
String message = !TextUtils.isEmpty(result.second()) ? context.getResources().getString(R.string.SaveAttachmentTask_saved_to, result.second()) String message = !TextUtils.isEmpty(result.second()) ? context.getResources().getString(R.string.SaveAttachmentTask_saved_to, result.second()) : context.getResources().getString(R.string.SaveAttachmentTask_saved);
: context.getResources().getString(R.string.SaveAttachmentTask_saved);
Toast.makeText(context, message, Toast.LENGTH_LONG).show(); Toast.makeText(context, message, Toast.LENGTH_LONG).show();
break; break;
case WRITE_ACCESS_FAILURE: case WRITE_ACCESS_FAILURE:
Toast.makeText(context, R.string.ConversationFragment_unable_to_write_to_sd_card_exclamation, Toast.makeText(context, R.string.ConversationFragment_unable_to_write_to_sd_card_exclamation, Toast.LENGTH_LONG).show();
Toast.LENGTH_LONG).show();
break; break;
} }
} }
@ -396,6 +415,16 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
} }
} }
private static class CreateMediaUriResult {
final Uri outputUri;
final Uri mediaUri;
private CreateMediaUriResult(Uri outputUri, Uri mediaUri) {
this.outputUri = outputUri;
this.mediaUri = mediaUri;
}
}
public static void showWarningDialog(Context context, OnClickListener onAcceptListener) { public static void showWarningDialog(Context context, OnClickListener onAcceptListener) {
showWarningDialog(context, onAcceptListener, 1); showWarningDialog(context, onAcceptListener, 1);
} }