AttachFileToConversationRunnable.java

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