FileBackend.java

  1package eu.siacs.conversations.persistance;
  2
  3import java.io.File;
  4import java.io.FileInputStream;
  5import java.io.FileNotFoundException;
  6import java.io.FileOutputStream;
  7import java.io.IOException;
  8import java.io.InputStream;
  9import java.io.OutputStream;
 10
 11import android.content.Context;
 12import android.graphics.Bitmap;
 13import android.graphics.BitmapFactory;
 14import android.net.Uri;
 15import android.util.Log;
 16import android.util.LruCache;
 17import eu.siacs.conversations.R;
 18import eu.siacs.conversations.entities.Conversation;
 19import eu.siacs.conversations.entities.Message;
 20import eu.siacs.conversations.xmpp.jingle.JingleFile;
 21
 22public class FileBackend {
 23
 24	private static int IMAGE_SIZE = 1920;
 25
 26	private Context context;
 27	private LruCache<String, Bitmap> thumbnailCache;
 28
 29	public FileBackend(Context context) {
 30		this.context = context;
 31		int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
 32		int cacheSize = maxMemory / 8;
 33		thumbnailCache = new LruCache<String, Bitmap>(cacheSize) {
 34			@Override
 35			protected int sizeOf(String key, Bitmap bitmap) {
 36				return bitmap.getByteCount() / 1024;
 37			}
 38		};
 39
 40	}
 41
 42	public LruCache<String, Bitmap> getThumbnailCache() {
 43		return thumbnailCache;
 44	}
 45
 46	public JingleFile getJingleFile(Message message) {
 47		return getJingleFile(message, true);
 48	}
 49
 50	public JingleFile getJingleFile(Message message, boolean decrypted) {
 51		Conversation conversation = message.getConversation();
 52		String prefix = context.getFilesDir().getAbsolutePath();
 53		String path = prefix + "/" + conversation.getAccount().getJid() + "/"
 54				+ conversation.getContactJid();
 55		String filename;
 56		if ((decrypted) || (message.getEncryption() == Message.ENCRYPTION_NONE)) {
 57			filename = message.getUuid() + ".webp";
 58		} else {
 59			filename = message.getUuid() + ".webp.pgp";
 60		}
 61		return new JingleFile(path + "/" + filename);
 62	}
 63
 64	public Bitmap resize(Bitmap originalBitmap, int size) {
 65		int w = originalBitmap.getWidth();
 66		int h = originalBitmap.getHeight();
 67		if (Math.max(w, h) > size) {
 68			int scalledW;
 69			int scalledH;
 70			if (w <= h) {
 71				scalledW = (int) (w / ((double) h / size));
 72				scalledH = size;
 73			} else {
 74				scalledW = size;
 75				scalledH = (int) (h / ((double) w / size));
 76			}
 77			Bitmap scalledBitmap = Bitmap.createScaledBitmap(originalBitmap,
 78					scalledW, scalledH, true);
 79			return scalledBitmap;
 80		} else {
 81			return originalBitmap;
 82		}
 83	}
 84
 85	public JingleFile copyImageToPrivateStorage(Message message, Uri image)
 86			throws ImageCopyException {
 87		try {
 88			InputStream is;
 89			if (image != null) {
 90				is = context.getContentResolver().openInputStream(image);
 91			} else {
 92				is = new FileInputStream(getIncomingFile());
 93			}
 94			JingleFile file = getJingleFile(message);
 95			file.getParentFile().mkdirs();
 96			file.createNewFile();
 97			OutputStream os = new FileOutputStream(file);
 98			Bitmap originalBitmap = BitmapFactory.decodeStream(is);
 99			if (originalBitmap == null) {
100				os.close();
101				throw new ImageCopyException(R.string.error_not_an_image_file);
102			}
103			is.close();
104			if (image == null) {
105				getIncomingFile().delete();
106			}
107			Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE);
108			boolean success = scalledBitmap.compress(
109					Bitmap.CompressFormat.WEBP, 75, os);
110			if (!success) {
111				throw new ImageCopyException(R.string.error_compressing_image);
112			}
113			os.flush();
114			os.close();
115			long size = file.getSize();
116			int width = scalledBitmap.getWidth();
117			int height = scalledBitmap.getHeight();
118			message.setBody("" + size + "," + width + "," + height);
119			return file;
120		} catch (FileNotFoundException e) {
121			throw new ImageCopyException(R.string.error_file_not_found);
122		} catch (IOException e) {
123			throw new ImageCopyException(R.string.error_io_exception);
124		} catch (SecurityException e) {
125			throw new ImageCopyException(
126					R.string.error_security_exception_during_image_copy);
127		}
128	}
129
130	public Bitmap getImageFromMessage(Message message) {
131		return BitmapFactory.decodeFile(getJingleFile(message)
132				.getAbsolutePath());
133	}
134
135	public Bitmap getThumbnail(Message message, int size, boolean cacheOnly)
136			throws FileNotFoundException {
137		Bitmap thumbnail = thumbnailCache.get(message.getUuid());
138		if ((thumbnail == null) && (!cacheOnly)) {
139			Bitmap fullsize = BitmapFactory.decodeFile(getJingleFile(message)
140					.getAbsolutePath());
141			if (fullsize == null) {
142				throw new FileNotFoundException();
143			}
144			thumbnail = resize(fullsize, size);
145			this.thumbnailCache.put(message.getUuid(), thumbnail);
146		}
147		return thumbnail;
148	}
149
150	public void removeFiles(Conversation conversation) {
151		String prefix = context.getFilesDir().getAbsolutePath();
152		String path = prefix + "/" + conversation.getAccount().getJid() + "/"
153				+ conversation.getContactJid();
154		File file = new File(path);
155		try {
156			this.deleteFile(file);
157		} catch (IOException e) {
158			Log.d("xmppService",
159					"error deleting file: " + file.getAbsolutePath());
160		}
161	}
162
163	private void deleteFile(File f) throws IOException {
164		if (f.isDirectory()) {
165			for (File c : f.listFiles())
166				deleteFile(c);
167		}
168		f.delete();
169	}
170
171	public File getIncomingFile() {
172		return new File(context.getFilesDir().getAbsolutePath() + "/incoming");
173	}
174
175	public class ImageCopyException extends Exception {
176		private static final long serialVersionUID = -1010013599132881427L;
177		private int resId;
178
179		public ImageCopyException(int resId) {
180			this.resId = resId;
181		}
182
183		public int getResId() {
184			return resId;
185		}
186	}
187}