From ed4a73e1c7925d3744542193579c0ea25e439eb4 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 26 Dec 2019 18:58:54 +0100 Subject: [PATCH] persist file size across aborts fixes #3601 --- .../siacs/conversations/entities/Message.java | 4 + .../http/HttpDownloadConnection.java | 27 +++-- .../persistance/FileBackend.java | 100 +++++++++--------- .../ui/ConversationFragment.java | 4 +- .../ui/adapter/MessageAdapter.java | 8 +- .../conversations/utils/MessageUtils.java | 4 + .../siacs/conversations/utils/UIHelper.java | 2 +- 7 files changed, 85 insertions(+), 64 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 1d34ec4ec..360103aa0 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -653,6 +653,10 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable } } + public boolean isOOb() { + return oob; + } + public static class MergeSeparator { } diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index b7cf0947a..bd52e8ad2 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -74,8 +74,11 @@ public class HttpDownloadConnection implements Transferable { public void init(boolean interactive) { this.message.setTransferable(this); try { + final Message.FileParams fileParams = message.getFileParams(); if (message.hasFileOnRemoteHost()) { - mUrl = CryptoHelper.toHttpsUrl(message.getFileParams().url); + mUrl = CryptoHelper.toHttpsUrl(fileParams.url); + } else if (message.isOOb() && fileParams.url != null && fileParams.size > 0) { + mUrl = fileParams.url; } else { mUrl = CryptoHelper.toHttpsUrl(new URL(message.getBody().split("\n")[0])); } @@ -139,7 +142,7 @@ public class HttpDownloadConnection implements Transferable { mHttpConnectionManager.updateConversationUi(true); } - private void decryptOmemoFile() throws Exception { + private void decryptOmemoFile() { final DownloadableFile outputFile = mXmppConnectionService.getFileBackend().getFile(message, true); if (outputFile.getParentFile().mkdirs()) { @@ -171,9 +174,6 @@ public class HttpDownloadConnection implements Transferable { } private void finish() throws Exception { - if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { - decryptOmemoFile(); - } message.setTransferable(null); mHttpConnectionManager.finishConnection(this); boolean notify = acceptedAutomatically && !message.isRead(); @@ -189,6 +189,12 @@ public class HttpDownloadConnection implements Transferable { }); } + private void decryptIfNeeded() { + if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { + decryptOmemoFile(); + } + } + private void changeStatus(int status) { this.mStatus = status; mHttpConnectionManager.updateConversationUi(true); @@ -296,10 +302,10 @@ public class HttpDownloadConnection implements Transferable { retrieveFailed(e); return; } - //TODO at this stage we probably also want to persist the file size in the body of the - // message via a similar mechansim as updateFileParams() - essentially body needs to read - // "url|filesize" - // afterwards a file that failed to download mid way will not display 'check file size' anymore + final Message.FileParams fileParams = message.getFileParams(); + FileBackend.updateFileParams(message, fileParams.url, size); + message.setOob(true); + mXmppConnectionService.databaseBackend.updateMessage(message, true); file.setExpectedSize(size); message.resetFileParams(); if (mHttpConnectionManager.hasStoragePermission() @@ -383,8 +389,9 @@ public class HttpDownloadConnection implements Transferable { try { changeStatus(STATUS_DOWNLOADING); download(); - finish(); + decryptIfNeeded(); updateImageBounds(); + finish(); } catch (SSLHandshakeException e) { changeStatus(STATUS_OFFER); } catch (Exception e) { diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index 3c049bf48..48f3d4343 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -3,7 +3,6 @@ package eu.siacs.conversations.persistance; import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -74,10 +73,8 @@ public class FileBackend { private static final SimpleDateFormat IMAGE_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US); private static final String FILE_PROVIDER = ".files"; - - private XmppConnectionService mXmppConnectionService; - private static final float IGNORE_PADDING = 0.15f; + private XmppConnectionService mXmppConnectionService; public FileBackend(XmppConnectionService service) { this.mXmppConnectionService = service; @@ -158,7 +155,7 @@ public class FileBackend { } public static String getBackupDirectory(String app) { - return Environment.getExternalStorageDirectory().getAbsolutePath() + "/"+app+"/Backup/"; + return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + app + "/Backup/"; } private static Bitmap rotate(Bitmap bitmap, int degree) { @@ -257,31 +254,6 @@ public class FileBackend { return inSampleSize; } - public Bitmap getPreviewForUri(Attachment attachment, int size, boolean cacheOnly) { - final String key = "attachment_"+attachment.getUuid().toString()+"_"+String.valueOf(size); - final LruCache cache = mXmppConnectionService.getBitmapCache(); - Bitmap bitmap = cache.get(key); - if (bitmap != null || cacheOnly) { - return bitmap; - } - if (attachment.getMime() != null && attachment.getMime().startsWith("video/")) { - bitmap = cropCenterSquareVideo(attachment.getUri(), size); - drawOverlay(bitmap, paintOverlayBlack(bitmap) ? R.drawable.play_video_black : R.drawable.play_video_white, 0.75f); - } else { - bitmap = cropCenterSquare(attachment.getUri(), size); - if (bitmap != null && "image/gif".equals(attachment.getMime())) { - Bitmap withGifOverlay = bitmap.copy(Bitmap.Config.ARGB_8888, true); - drawOverlay(withGifOverlay, paintOverlayBlack(withGifOverlay) ? R.drawable.play_gif_black : R.drawable.play_gif_white, 1.0f); - bitmap.recycle(); - bitmap = withGifOverlay; - } - } - if (bitmap != null) { - cache.put(key, bitmap); - } - return bitmap; - } - private static Dimensions getVideoDimensions(Context context, Uri uri) throws NotAVideoFile { MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); try { @@ -421,19 +393,6 @@ public class FileBackend { } } - private void createNoMedia(File diretory) { - final File noMedia = new File(diretory, ".nomedia"); - if (!noMedia.exists()) { - try { - if (!noMedia.createNewFile()) { - Log.d(Config.LOGTAG, "created nomedia file " + noMedia.getAbsolutePath()); - } - } catch (Exception e) { - Log.d(Config.LOGTAG, "could not create nomedia file"); - } - } - } - public static Uri getMediaUri(Context context, File file) { final String filePath = file.getAbsolutePath(); final Cursor cursor; @@ -455,6 +414,50 @@ public class FileBackend { } } + public static void updateFileParams(Message message, URL url, long size) { + final StringBuilder body = new StringBuilder(); + body.append(url.toString()).append('|').append(size); + message.setBody(body.toString()); + } + + public Bitmap getPreviewForUri(Attachment attachment, int size, boolean cacheOnly) { + final String key = "attachment_" + attachment.getUuid().toString() + "_" + String.valueOf(size); + final LruCache cache = mXmppConnectionService.getBitmapCache(); + Bitmap bitmap = cache.get(key); + if (bitmap != null || cacheOnly) { + return bitmap; + } + if (attachment.getMime() != null && attachment.getMime().startsWith("video/")) { + bitmap = cropCenterSquareVideo(attachment.getUri(), size); + drawOverlay(bitmap, paintOverlayBlack(bitmap) ? R.drawable.play_video_black : R.drawable.play_video_white, 0.75f); + } else { + bitmap = cropCenterSquare(attachment.getUri(), size); + if (bitmap != null && "image/gif".equals(attachment.getMime())) { + Bitmap withGifOverlay = bitmap.copy(Bitmap.Config.ARGB_8888, true); + drawOverlay(withGifOverlay, paintOverlayBlack(withGifOverlay) ? R.drawable.play_gif_black : R.drawable.play_gif_white, 1.0f); + bitmap.recycle(); + bitmap = withGifOverlay; + } + } + if (bitmap != null) { + cache.put(key, bitmap); + } + return bitmap; + } + + private void createNoMedia(File diretory) { + final File noMedia = new File(diretory, ".nomedia"); + if (!noMedia.exists()) { + try { + if (!noMedia.createNewFile()) { + Log.d(Config.LOGTAG, "created nomedia file " + noMedia.getAbsolutePath()); + } + } catch (Exception e) { + Log.d(Config.LOGTAG, "could not create nomedia file"); + } + } + } + public void updateMediaScanner(File file) { updateMediaScanner(file, null); } @@ -472,7 +475,7 @@ public class FileBackend { if (callback != null && file.getAbsolutePath().equals(path)) { callback.run(); } else { - Log.d(Config.LOGTAG,"media scanner scanned wrong file"); + Log.d(Config.LOGTAG, "media scanner scanned wrong file"); if (callback != null) { callback.run(); } @@ -506,7 +509,7 @@ public class FileBackend { } public DownloadableFile getFileForPath(String path) { - return getFileForPath(path,MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(path))); + return getFileForPath(path, MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(path))); } public DownloadableFile getFileForPath(String path, String mime) { @@ -548,7 +551,7 @@ public class FileBackend { public List convertToAttachments(List relativeFilePaths) { List attachments = new ArrayList<>(); - for(DatabaseBackend.FilePath relativeFilePath : relativeFilePaths) { + for (DatabaseBackend.FilePath relativeFilePath : relativeFilePaths) { final String mime = MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(relativeFilePath.path)); final File file = getFileForPath(relativeFilePath.path, mime); attachments.add(Attachment.of(relativeFilePath.uuid, file, mime)); @@ -988,7 +991,7 @@ public class FileBackend { avatar.height = bitmap.getHeight(); return avatar; } catch (OutOfMemoryError e) { - Log.d(Config.LOGTAG,"unable to convert avatar to base64 due to low memory"); + Log.d(Config.LOGTAG, "unable to convert avatar to base64 due to low memory"); return null; } catch (Exception e) { return null; @@ -1116,7 +1119,7 @@ public class FileBackend { return cropCenterSquare(input, size); } } catch (FileNotFoundException | SecurityException e) { - Log.d(Config.LOGTAG,"unable to open file "+image.toString(), e); + Log.d(Config.LOGTAG, "unable to open file " + image.toString(), e); return null; } finally { close(is); @@ -1228,7 +1231,6 @@ public class FileBackend { message.setType(privateMessage ? Message.TYPE_PRIVATE_FILE : (image ? Message.TYPE_IMAGE : Message.TYPE_FILE)); } - private int getMediaRuntime(File file) { try { MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 1eaa1861f..724855ee7 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1424,8 +1424,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke Log.d(Config.LOGTAG, "type: " + transferable.getClass().getName()); Toast.makeText(getActivity(), R.string.not_connected_try_again, Toast.LENGTH_SHORT).show(); } - } else if (message.treatAsDownloadable() || message.hasFileOnRemoteHost()) { + } else if (message.treatAsDownloadable() || message.hasFileOnRemoteHost() || MessageUtils.unInitiatedButKnownSize(message)) { createNewConnection(message); + } else { + Log.d(Config.LOGTAG,message.getConversation().getAccount()+": unable to start downloadable"); } } diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index a8a93d4e8..e3afab37a 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -70,6 +70,7 @@ import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.EmojiWrapper; import eu.siacs.conversations.utils.Emoticons; import eu.siacs.conversations.utils.GeoHelper; +import eu.siacs.conversations.utils.MessageUtils; import eu.siacs.conversations.utils.StylingHelper; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.mam.MamReference; @@ -184,7 +185,7 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie final Transferable transferable = message.getTransferable(); boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI && message.getMergedStatus() <= Message.STATUS_RECEIVED; - if (message.isFileOrImage() || transferable != null) { + if (message.isFileOrImage() || transferable != null || MessageUtils.unInitiatedButKnownSize(message)) { FileParams params = message.getFileParams(); filesize = params.size > 0 ? UIHelper.filesizeToString(params.size) : null; if (transferable != null && (transferable.getStatus() == Transferable.STATUS_FAILED || transferable.getStatus() == Transferable.STATUS_CANCELLED)) { @@ -733,8 +734,9 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie }); final Transferable transferable = message.getTransferable(); - if (message.isDeleted() || (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING)) { - if (transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER) { + final boolean unInitiatedButKnownSize = MessageUtils.unInitiatedButKnownSize(message); + if (unInitiatedButKnownSize || message.isDeleted() || (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING)) { + if (unInitiatedButKnownSize || transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER) { displayDownloadableMessage(viewHolder, message, activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)), darkBackground); } else if (transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) { displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)), darkBackground); diff --git a/src/main/java/eu/siacs/conversations/utils/MessageUtils.java b/src/main/java/eu/siacs/conversations/utils/MessageUtils.java index ca3416579..e5e4fbd97 100644 --- a/src/main/java/eu/siacs/conversations/utils/MessageUtils.java +++ b/src/main/java/eu/siacs/conversations/utils/MessageUtils.java @@ -91,4 +91,8 @@ public class MessageUtils { public static String filterLtrRtl(String body) { return LTR_RTL.matcher(body).replaceFirst(EMPTY_STRING); } + + public static boolean unInitiatedButKnownSize(Message message) { + return message.getType() == Message.TYPE_TEXT && message.getTransferable() == null && message.isOOb() && message.getFileParams().size > 0 && message.getFileParams().url != null; + } } diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java index c99c1c1b2..47fec58c6 100644 --- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java @@ -306,7 +306,7 @@ public class UIHelper { UIHelper.getMessageDisplayName(message) + " "), false); } else if (message.isGeoUri()) { return new Pair<>(context.getString(R.string.location), true); - } else if (message.treatAsDownloadable()) { + } else if (message.treatAsDownloadable() || MessageUtils.unInitiatedButKnownSize(message)) { return new Pair<>(context.getString(R.string.x_file_offered_for_download, getFileDescriptionString(context, message)), true); } else {