AttachFileToConversationRunnable.java

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