diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index 4fe2494f66b55544ea55f8211f1387cc12e48e39..af53574c5005c121b4c4380a5fe94ff8af3be8b3 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -268,7 +268,8 @@ public class FileBackend { return inSampleSize; } - private static Dimensions getVideoDimensions(Context context, Uri uri) throws NotAVideoFile, IOException { + private static Dimensions getVideoDimensions(Context context, Uri uri) + throws NotAVideoFile, IOException { MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); try { mediaMetadataRetriever.setDataSource(context, uri); @@ -663,16 +664,14 @@ public class FileBackend { } } - private void copyFileToPrivateStorage(File file, Uri uri) throws FileCopyException { + private void copyFileToPrivateStorage(final File file, final Uri uri) throws FileCopyException { final var parentDirectory = file.getParentFile(); if (parentDirectory != null && parentDirectory.mkdirs()) { - Log.d(Config.LOGTAG,"created directory "+parentDirectory.getAbsolutePath()); + Log.d(Config.LOGTAG, "created directory " + parentDirectory.getAbsolutePath()); } try { if (file.createNewFile()) { - Log.d( - Config.LOGTAG, - "copy file (" + uri.toString() + ") to private storage " + file.getAbsolutePath()); + Log.d(Config.LOGTAG, "created empty file " + file.getAbsolutePath()); } } catch (final IOException e) { throw new FileCopyException(R.string.error_unable_to_create_temporary_file); @@ -708,9 +707,10 @@ public class FileBackend { } } - public void copyFileToPrivateStorage(Message message, Uri uri, String type) + public void copyFileToPrivateStorage(final Message message, final Uri uri, final String type) throws FileCopyException { - final String mime = MimeUtils.guessMimeTypeFromUriAndMime(mXmppConnectionService, uri, type); + final String mime = + MimeUtils.guessMimeTypeFromUriAndMime(mXmppConnectionService, uri, type); Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage (mime=" + mime + ")"); String extension = MimeUtils.guessExtensionFromMimeType(mime); if (extension == null) { @@ -1500,7 +1500,7 @@ public class FileBackend { return calcSampleSize(options, size); } - public void updateFileParams(Message message) { + public void updateFileParams(final Message message) { updateFileParams(message, null); } diff --git a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java index f86371be6157f642638e28ae7b9881d6bdc9c6fd..9c75badb2f420a85fa47529e3979bcb439ad045c 100644 --- a/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java +++ b/src/main/java/eu/siacs/conversations/services/AttachFileToConversationRunnable.java @@ -11,12 +11,6 @@ import androidx.annotation.NonNull; import com.otaliastudios.transcoder.Transcoder; import com.otaliastudios.transcoder.TranscoderListener; -import java.io.File; -import java.io.FileNotFoundException; -import java.util.Objects; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; @@ -27,6 +21,12 @@ import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.utils.MimeUtils; import eu.siacs.conversations.utils.TranscoderStrategies; +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Objects; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + public class AttachFileToConversationRunnable implements Runnable, TranscoderListener { private final XmppConnectionService mXmppConnectionService; @@ -38,16 +38,26 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis private final long originalFileSize; private int currentProgress = -1; - AttachFileToConversationRunnable(XmppConnectionService xmppConnectionService, Uri uri, String type, Message message, UiCallback callback) { + AttachFileToConversationRunnable( + XmppConnectionService xmppConnectionService, + Uri uri, + String type, + Message message, + UiCallback callback) { this.uri = uri; this.type = type; this.mXmppConnectionService = xmppConnectionService; this.message = message; this.callback = callback; - final String mimeType = MimeUtils.guessMimeTypeFromUriAndMime(mXmppConnectionService, uri, type); - final int autoAcceptFileSize = mXmppConnectionService.getResources().getInteger(R.integer.auto_accept_filesize); + final String mimeType = + MimeUtils.guessMimeTypeFromUriAndMime(mXmppConnectionService, uri, type); + final int autoAcceptFileSize = + mXmppConnectionService.getResources().getInteger(R.integer.auto_accept_filesize); this.originalFileSize = FileBackend.getFileSize(mXmppConnectionService, uri); - this.isVideoMessage = (mimeType != null && mimeType.startsWith("video/")) && originalFileSize > autoAcceptFileSize && !"uncompressed".equals(getVideoCompression()); + this.isVideoMessage = + (mimeType != null && mimeType.startsWith("video/")) + && originalFileSize > autoAcceptFileSize + && !"uncompressed".equals(getVideoCompression()); } boolean isVideoMessage() { @@ -67,7 +77,9 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis } } else { try { - mXmppConnectionService.getFileBackend().copyFileToPrivateStorage(message, uri, type); + mXmppConnectionService + .getFileBackend() + .copyFileToPrivateStorage(message, uri, type); mXmppConnectionService.getFileBackend().updateFileParams(message); if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { final PgpEngine pgpEngine = mXmppConnectionService.getPgpEngine(); @@ -80,16 +92,26 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis mXmppConnectionService.sendMessage(message); callback.success(message); } - } catch (FileBackend.FileCopyException e) { + } catch (final FileBackend.FileCopyException e) { callback.error(e.getResId(), message); } } } + private void fallbackToProcessAsFile() { + final var file = mXmppConnectionService.getFileBackend().getFile(message); + if (file.exists() && file.delete()) { + Log.d(Config.LOGTAG, "deleted preexisting file " + file.getAbsolutePath()); + } + XmppConnectionService.FILE_ATTACHMENT_EXECUTOR.execute(this::processAsFile); + } + private void processAsVideo() throws FileNotFoundException { Log.d(Config.LOGTAG, "processing file as video"); mXmppConnectionService.startOngoingVideoTranscodingForegroundNotification(); - mXmppConnectionService.getFileBackend().setupRelativeFilePath(message, String.format("%s.%s", message.getUuid(), "mp4")); + mXmppConnectionService + .getFileBackend() + .setupRelativeFilePath(message, String.format("%s.%s", message.getUuid(), "mp4")); final DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message); if (Objects.requireNonNull(file.getParentFile()).mkdirs()) { Log.d(Config.LOGTAG, "created parent directory for video file"); @@ -99,16 +121,23 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis final Future future; try { - future = Transcoder.into(file.getAbsolutePath()). - addDataSource(mXmppConnectionService, uri) - .setVideoTrackStrategy(highQuality ? TranscoderStrategies.VIDEO_720P : TranscoderStrategies.VIDEO_360P) - .setAudioTrackStrategy(highQuality ? TranscoderStrategies.AUDIO_HQ : TranscoderStrategies.AUDIO_MQ) - .setListener(this) - .transcode(); + future = + Transcoder.into(file.getAbsolutePath()) + .addDataSource(mXmppConnectionService, uri) + .setVideoTrackStrategy( + highQuality + ? TranscoderStrategies.VIDEO_720P + : TranscoderStrategies.VIDEO_360P) + .setAudioTrackStrategy( + highQuality + ? TranscoderStrategies.AUDIO_HQ + : TranscoderStrategies.AUDIO_MQ) + .setListener(this) + .transcode(); } catch (final RuntimeException e) { // transcode can already throw if there is an invalid file format or a platform bug mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification(); - processAsFile(); + fallbackToProcessAsFile(); return; } try { @@ -118,9 +147,9 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis } catch (final ExecutionException e) { if (e.getCause() instanceof Error) { mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification(); - processAsFile(); + fallbackToProcessAsFile(); } else { - Log.d(Config.LOGTAG, "ignoring execution exception. Should get handled by onTranscodeFiled() instead", e); + Log.d(Config.LOGTAG, "ignoring execution exception. Handled by onTranscodeFiled()"); } } } @@ -130,7 +159,9 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis final int p = (int) Math.round(progress * 100); if (p > currentProgress) { currentProgress = p; - mXmppConnectionService.getNotificationService().updateFileAddingNotification(p, message); + mXmppConnectionService + .getNotificationService() + .updateFileAddingNotification(p, message); } } @@ -139,11 +170,15 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification(); final File file = mXmppConnectionService.getFileBackend().getFile(message); long convertedFileSize = mXmppConnectionService.getFileBackend().getFile(message).getSize(); - Log.d(Config.LOGTAG, "originalFileSize=" + originalFileSize + " convertedFileSize=" + convertedFileSize); + Log.d( + Config.LOGTAG, + "originalFileSize=" + originalFileSize + " convertedFileSize=" + convertedFileSize); if (originalFileSize != 0 && convertedFileSize >= originalFileSize) { if (file.delete()) { - Log.d(Config.LOGTAG, "original file size was smaller. deleting and processing as file"); - processAsFile(); + Log.d( + Config.LOGTAG, + "original file size was smaller. deleting and processing as file"); + fallbackToProcessAsFile(); return; } else { Log.d(Config.LOGTAG, "unable to delete converted file"); @@ -161,14 +196,14 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis @Override public void onTranscodeCanceled() { mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification(); - processAsFile(); + fallbackToProcessAsFile(); } @Override public void onTranscodeFailed(@NonNull final Throwable exception) { mXmppConnectionService.stopOngoingVideoTranscodingForegroundNotification(); Log.d(Config.LOGTAG, "video transcoding failed", exception); - processAsFile(); + fallbackToProcessAsFile(); } @Override @@ -176,7 +211,7 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis if (this.isVideoMessage()) { try { processAsVideo(); - } catch (FileNotFoundException e) { + } catch (final FileNotFoundException e) { processAsFile(); } } else { @@ -189,7 +224,9 @@ public class AttachFileToConversationRunnable implements Runnable, TranscoderLis } public static String getVideoCompression(final Context context) { - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - return preferences.getString("video_compression", context.getResources().getString(R.string.video_compression)); + final SharedPreferences preferences = + PreferenceManager.getDefaultSharedPreferences(context); + return preferences.getString( + "video_compression", context.getResources().getString(R.string.video_compression)); } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index c145bebf7d47727be8b6c0dedcc5712353776818..dce715e7a18f1a5480d0a3cc0a64780ddeb8ea48 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -205,7 +205,7 @@ public class XmppConnectionService extends Service { public final CountDownLatch restoredFromDatabaseLatch = new CountDownLatch(1); private final static Executor FILE_OBSERVER_EXECUTOR = Executors.newSingleThreadExecutor(); - private final static Executor FILE_ATTACHMENT_EXECUTOR = Executors.newSingleThreadExecutor(); + public final static Executor FILE_ATTACHMENT_EXECUTOR = Executors.newSingleThreadExecutor(); private final ScheduledExecutorService internalPingExecutor = Executors.newSingleThreadScheduledExecutor(); private final static SerialSingleThreadExecutor VIDEO_COMPRESSION_EXECUTOR = new SerialSingleThreadExecutor("VideoCompression");