persist file size across aborts

Daniel Gultsch created

fixes #3601

Change summary

src/main/java/eu/siacs/conversations/entities/Message.java            |   4 
src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java |  27 
src/main/java/eu/siacs/conversations/persistance/FileBackend.java     | 100 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java     |   4 
src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java   |   8 
src/main/java/eu/siacs/conversations/utils/MessageUtils.java          |   4 
src/main/java/eu/siacs/conversations/utils/UIHelper.java              |   2 
7 files changed, 85 insertions(+), 64 deletions(-)

Detailed changes

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) {

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<String, Bitmap> 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<String, Bitmap> 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<Attachment> convertToAttachments(List<DatabaseBackend.FilePath> relativeFilePaths) {
         List<Attachment> 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();

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");
         }
     }
 

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<Message> 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<Message> 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);

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;
+	}
 }

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 {