persist file size across aborts

fixes #3601
This commit is contained in:
Daniel Gultsch 2019-12-26 18:58:54 +01:00
parent a60e29d4f4
commit ed4a73e1c7
7 changed files with 85 additions and 64 deletions

View File

@ -653,6 +653,10 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
} }
} }
public boolean isOOb() {
return oob;
}
public static class MergeSeparator { public static class MergeSeparator {
} }

View File

@ -74,8 +74,11 @@ public class HttpDownloadConnection implements Transferable {
public void init(boolean interactive) { public void init(boolean interactive) {
this.message.setTransferable(this); this.message.setTransferable(this);
try { try {
final Message.FileParams fileParams = message.getFileParams();
if (message.hasFileOnRemoteHost()) { 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 { } else {
mUrl = CryptoHelper.toHttpsUrl(new URL(message.getBody().split("\n")[0])); mUrl = CryptoHelper.toHttpsUrl(new URL(message.getBody().split("\n")[0]));
} }
@ -139,7 +142,7 @@ public class HttpDownloadConnection implements Transferable {
mHttpConnectionManager.updateConversationUi(true); mHttpConnectionManager.updateConversationUi(true);
} }
private void decryptOmemoFile() throws Exception { private void decryptOmemoFile() {
final DownloadableFile outputFile = mXmppConnectionService.getFileBackend().getFile(message, true); final DownloadableFile outputFile = mXmppConnectionService.getFileBackend().getFile(message, true);
if (outputFile.getParentFile().mkdirs()) { if (outputFile.getParentFile().mkdirs()) {
@ -171,9 +174,6 @@ public class HttpDownloadConnection implements Transferable {
} }
private void finish() throws Exception { private void finish() throws Exception {
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
decryptOmemoFile();
}
message.setTransferable(null); message.setTransferable(null);
mHttpConnectionManager.finishConnection(this); mHttpConnectionManager.finishConnection(this);
boolean notify = acceptedAutomatically && !message.isRead(); 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) { private void changeStatus(int status) {
this.mStatus = status; this.mStatus = status;
mHttpConnectionManager.updateConversationUi(true); mHttpConnectionManager.updateConversationUi(true);
@ -296,10 +302,10 @@ public class HttpDownloadConnection implements Transferable {
retrieveFailed(e); retrieveFailed(e);
return; return;
} }
//TODO at this stage we probably also want to persist the file size in the body of the final Message.FileParams fileParams = message.getFileParams();
// message via a similar mechansim as updateFileParams() - essentially body needs to read FileBackend.updateFileParams(message, fileParams.url, size);
// "url|filesize" message.setOob(true);
// afterwards a file that failed to download mid way will not display 'check file size' anymore mXmppConnectionService.databaseBackend.updateMessage(message, true);
file.setExpectedSize(size); file.setExpectedSize(size);
message.resetFileParams(); message.resetFileParams();
if (mHttpConnectionManager.hasStoragePermission() if (mHttpConnectionManager.hasStoragePermission()
@ -383,8 +389,9 @@ public class HttpDownloadConnection implements Transferable {
try { try {
changeStatus(STATUS_DOWNLOADING); changeStatus(STATUS_DOWNLOADING);
download(); download();
finish(); decryptIfNeeded();
updateImageBounds(); updateImageBounds();
finish();
} catch (SSLHandshakeException e) { } catch (SSLHandshakeException e) {
changeStatus(STATUS_OFFER); changeStatus(STATUS_OFFER);
} catch (Exception e) { } catch (Exception e) {

View File

@ -3,7 +3,6 @@ package eu.siacs.conversations.persistance;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; 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 SimpleDateFormat IMAGE_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US);
private static final String FILE_PROVIDER = ".files"; private static final String FILE_PROVIDER = ".files";
private XmppConnectionService mXmppConnectionService;
private static final float IGNORE_PADDING = 0.15f; private static final float IGNORE_PADDING = 0.15f;
private XmppConnectionService mXmppConnectionService;
public FileBackend(XmppConnectionService service) { public FileBackend(XmppConnectionService service) {
this.mXmppConnectionService = service; this.mXmppConnectionService = service;
@ -158,7 +155,7 @@ public class FileBackend {
} }
public static String getBackupDirectory(String app) { 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) { private static Bitmap rotate(Bitmap bitmap, int degree) {
@ -257,31 +254,6 @@ public class FileBackend {
return inSampleSize; 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 { private static Dimensions getVideoDimensions(Context context, Uri uri) throws NotAVideoFile {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
try { 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) { public static Uri getMediaUri(Context context, File file) {
final String filePath = file.getAbsolutePath(); final String filePath = file.getAbsolutePath();
final Cursor cursor; 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) { public void updateMediaScanner(File file) {
updateMediaScanner(file, null); updateMediaScanner(file, null);
} }
@ -472,7 +475,7 @@ public class FileBackend {
if (callback != null && file.getAbsolutePath().equals(path)) { if (callback != null && file.getAbsolutePath().equals(path)) {
callback.run(); callback.run();
} else { } else {
Log.d(Config.LOGTAG,"media scanner scanned wrong file"); Log.d(Config.LOGTAG, "media scanner scanned wrong file");
if (callback != null) { if (callback != null) {
callback.run(); callback.run();
} }
@ -506,7 +509,7 @@ public class FileBackend {
} }
public DownloadableFile getFileForPath(String path) { 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) { public DownloadableFile getFileForPath(String path, String mime) {
@ -548,7 +551,7 @@ public class FileBackend {
public List<Attachment> convertToAttachments(List<DatabaseBackend.FilePath> relativeFilePaths) { public List<Attachment> convertToAttachments(List<DatabaseBackend.FilePath> relativeFilePaths) {
List<Attachment> attachments = new ArrayList<>(); 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 String mime = MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(relativeFilePath.path));
final File file = getFileForPath(relativeFilePath.path, mime); final File file = getFileForPath(relativeFilePath.path, mime);
attachments.add(Attachment.of(relativeFilePath.uuid, file, mime)); attachments.add(Attachment.of(relativeFilePath.uuid, file, mime));
@ -988,7 +991,7 @@ public class FileBackend {
avatar.height = bitmap.getHeight(); avatar.height = bitmap.getHeight();
return avatar; return avatar;
} catch (OutOfMemoryError e) { } 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; return null;
} catch (Exception e) { } catch (Exception e) {
return null; return null;
@ -1116,7 +1119,7 @@ public class FileBackend {
return cropCenterSquare(input, size); return cropCenterSquare(input, size);
} }
} catch (FileNotFoundException | SecurityException e) { } 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; return null;
} finally { } finally {
close(is); close(is);
@ -1228,7 +1231,6 @@ public class FileBackend {
message.setType(privateMessage ? Message.TYPE_PRIVATE_FILE : (image ? Message.TYPE_IMAGE : Message.TYPE_FILE)); message.setType(privateMessage ? Message.TYPE_PRIVATE_FILE : (image ? Message.TYPE_IMAGE : Message.TYPE_FILE));
} }
private int getMediaRuntime(File file) { private int getMediaRuntime(File file) {
try { try {
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();

View File

@ -1424,8 +1424,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
Log.d(Config.LOGTAG, "type: " + transferable.getClass().getName()); Log.d(Config.LOGTAG, "type: " + transferable.getClass().getName());
Toast.makeText(getActivity(), R.string.not_connected_try_again, Toast.LENGTH_SHORT).show(); 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); createNewConnection(message);
} else {
Log.d(Config.LOGTAG,message.getConversation().getAccount()+": unable to start downloadable");
} }
} }

View File

@ -70,6 +70,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.EmojiWrapper; import eu.siacs.conversations.utils.EmojiWrapper;
import eu.siacs.conversations.utils.Emoticons; import eu.siacs.conversations.utils.Emoticons;
import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.MessageUtils;
import eu.siacs.conversations.utils.StylingHelper; import eu.siacs.conversations.utils.StylingHelper;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.mam.MamReference; import eu.siacs.conversations.xmpp.mam.MamReference;
@ -184,7 +185,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
final Transferable transferable = message.getTransferable(); final Transferable transferable = message.getTransferable();
boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
&& message.getMergedStatus() <= Message.STATUS_RECEIVED; && message.getMergedStatus() <= Message.STATUS_RECEIVED;
if (message.isFileOrImage() || transferable != null) { if (message.isFileOrImage() || transferable != null || MessageUtils.unInitiatedButKnownSize(message)) {
FileParams params = message.getFileParams(); FileParams params = message.getFileParams();
filesize = params.size > 0 ? UIHelper.filesizeToString(params.size) : null; filesize = params.size > 0 ? UIHelper.filesizeToString(params.size) : null;
if (transferable != null && (transferable.getStatus() == Transferable.STATUS_FAILED || transferable.getStatus() == Transferable.STATUS_CANCELLED)) { 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(); final Transferable transferable = message.getTransferable();
if (message.isDeleted() || (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING)) { final boolean unInitiatedButKnownSize = MessageUtils.unInitiatedButKnownSize(message);
if (transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER) { 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); 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) { } 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); displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)), darkBackground);

View File

@ -91,4 +91,8 @@ public class MessageUtils {
public static String filterLtrRtl(String body) { public static String filterLtrRtl(String body) {
return LTR_RTL.matcher(body).replaceFirst(EMPTY_STRING); 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;
}
} }

View File

@ -306,7 +306,7 @@ public class UIHelper {
UIHelper.getMessageDisplayName(message) + " "), false); UIHelper.getMessageDisplayName(message) + " "), false);
} else if (message.isGeoUri()) { } else if (message.isGeoUri()) {
return new Pair<>(context.getString(R.string.location), true); 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, return new Pair<>(context.getString(R.string.x_file_offered_for_download,
getFileDescriptionString(context, message)), true); getFileDescriptionString(context, message)), true);
} else { } else {