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