AttachFileToConversationRunnable.java

  1package eu.siacs.conversations.services;
  2
  3import android.net.Uri;
  4import android.os.Build;
  5import android.os.ParcelFileDescriptor;
  6import android.util.Log;
  7
  8import net.ypresto.androidtranscoder.MediaTranscoder;
  9import net.ypresto.androidtranscoder.format.MediaFormatStrategy;
 10import net.ypresto.androidtranscoder.format.MediaFormatStrategyPresets;
 11
 12import java.io.File;
 13import java.io.FileDescriptor;
 14import java.io.FileNotFoundException;
 15import java.util.concurrent.ExecutionException;
 16import java.util.concurrent.Future;
 17import java.util.concurrent.atomic.AtomicInteger;
 18
 19import eu.siacs.conversations.Config;
 20import eu.siacs.conversations.R;
 21import eu.siacs.conversations.crypto.PgpEngine;
 22import eu.siacs.conversations.entities.DownloadableFile;
 23import eu.siacs.conversations.entities.Message;
 24import eu.siacs.conversations.persistance.FileBackend;
 25import eu.siacs.conversations.ui.UiCallback;
 26import eu.siacs.conversations.utils.MimeUtils;
 27
 28public class AttachFileToConversationRunnable implements Runnable, MediaTranscoder.Listener {
 29
 30	private final XmppConnectionService mXmppConnectionService;
 31	private final Message message;
 32	private final Uri uri;
 33	private final String type;
 34	private final UiCallback<Message> callback;
 35	private final boolean isVideoMessage;
 36	private final long originalFileSize;
 37	private int currentProgress = -1;
 38
 39	public AttachFileToConversationRunnable(XmppConnectionService xmppConnectionService, Uri uri, String type, Message message, UiCallback<Message> callback) {
 40		this.uri = uri;
 41		this.type = type;
 42		this.mXmppConnectionService = xmppConnectionService;
 43		this.message = message;
 44		this.callback = callback;
 45		final String mimeType = type != null ? type : MimeUtils.guessMimeTypeFromUri(mXmppConnectionService, uri);
 46		final int autoAcceptFileSize = mXmppConnectionService.getResources().getInteger(R.integer.auto_accept_filesize);
 47		this.originalFileSize = FileBackend.getFileSize(mXmppConnectionService,uri);
 48		this.isVideoMessage = (mimeType != null && mimeType.startsWith("video/")) && originalFileSize > autoAcceptFileSize;
 49	}
 50
 51	public boolean isVideoMessage() {
 52		return this.isVideoMessage;
 53	}
 54
 55	private void processAsFile() {
 56		final String path = mXmppConnectionService.getFileBackend().getOriginalPath(uri);
 57		if (path != null && !FileBackend.isPathBlacklisted(path)) {
 58			message.setRelativeFilePath(path);
 59			mXmppConnectionService.getFileBackend().updateFileParams(message);
 60			if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
 61				mXmppConnectionService.getPgpEngine().encrypt(message, callback);
 62			} else {
 63				mXmppConnectionService.sendMessage(message);
 64				callback.success(message);
 65			}
 66		} else {
 67			try {
 68				mXmppConnectionService.getFileBackend().copyFileToPrivateStorage(message, uri, type);
 69				mXmppConnectionService.getFileBackend().updateFileParams(message);
 70				if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
 71					final PgpEngine pgpEngine = mXmppConnectionService.getPgpEngine();
 72					if (pgpEngine != null) {
 73						pgpEngine.encrypt(message, callback);
 74					} else if (callback != null) {
 75						callback.error(R.string.unable_to_connect_to_keychain, null);
 76					}
 77				} else {
 78					mXmppConnectionService.sendMessage(message);
 79					callback.success(message);
 80				}
 81			} catch (FileBackend.FileCopyException e) {
 82				callback.error(e.getResId(), message);
 83			}
 84		}
 85	}
 86
 87	private void processAsVideo() throws FileNotFoundException {
 88		Log.d(Config.LOGTAG,"processing file as video");
 89		mXmppConnectionService.startForcingForegroundNotification();
 90		message.setRelativeFilePath(message.getUuid() + ".mp4");
 91		final DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message);
 92		final int runtime = mXmppConnectionService.getFileBackend().getMediaRuntime(uri);
 93		MediaFormatStrategy formatStrategy = runtime >= 8000 ? MediaFormatStrategyPresets.createExportPreset960x540Strategy() : MediaFormatStrategyPresets.createAndroid720pStrategy();
 94		file.getParentFile().mkdirs();
 95		final ParcelFileDescriptor parcelFileDescriptor = mXmppConnectionService.getContentResolver().openFileDescriptor(uri, "r");
 96		if (parcelFileDescriptor == null) {
 97			throw new FileNotFoundException("Parcel File Descriptor was null");
 98		}
 99		FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
100		Future<Void> future = MediaTranscoder.getInstance().transcodeVideo(fileDescriptor, file.getAbsolutePath(), formatStrategy, this);
101		try {
102			future.get();
103		} catch (InterruptedException e) {
104			throw new AssertionError(e);
105		} catch (ExecutionException e) {
106			Log.d(Config.LOGTAG,"ignoring execution exception. Should get handled by onTranscodeFiled() instead",e);
107		}
108	}
109
110	@Override
111	public void onTranscodeProgress(double progress) {
112		final int p = (int) Math.round(progress * 100);
113		if (p > currentProgress) {
114			currentProgress = p;
115			mXmppConnectionService.getNotificationService().updateFileAddingNotification(p,message);
116		}
117	}
118
119	@Override
120	public void onTranscodeCompleted() {
121		mXmppConnectionService.stopForcingForegroundNotification();
122		final File file = mXmppConnectionService.getFileBackend().getFile(message);
123		long convertedFileSize = mXmppConnectionService.getFileBackend().getFile(message).getSize();
124		Log.d(Config.LOGTAG,"originalFileSize="+originalFileSize+" convertedFileSize="+convertedFileSize);
125		if (originalFileSize != 0 && convertedFileSize >= originalFileSize) {
126			if (file.delete()) {
127				Log.d(Config.LOGTAG,"original file size was smaller. deleting and processing as file");
128				processAsFile();
129				return;
130			} else {
131				Log.d(Config.LOGTAG,"unable to delete converted file");
132			}
133		}
134		mXmppConnectionService.getFileBackend().updateFileParams(message);
135		if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
136			mXmppConnectionService.getPgpEngine().encrypt(message, callback);
137		} else {
138			mXmppConnectionService.sendMessage(message);
139			callback.success(message);
140		}
141	}
142
143	@Override
144	public void onTranscodeCanceled() {
145		mXmppConnectionService.stopForcingForegroundNotification();
146		processAsFile();
147	}
148
149	@Override
150	public void onTranscodeFailed(Exception e) {
151		mXmppConnectionService.stopForcingForegroundNotification();
152		Log.d(Config.LOGTAG,"video transcoding failed",e);
153		processAsFile();
154	}
155
156	@Override
157	public void run() {
158		if (isVideoMessage) {
159			try {
160				processAsVideo();
161			} catch (FileNotFoundException e) {
162				processAsFile();
163			}
164		} else {
165			processAsFile();
166		}
167	}
168
169}