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			if (message.getEncryption() == Message.ENCRYPTION_OTR) {
 60				filename = message.getUuid() + ".webp";
 61			} else {
 62				filename = message.getUuid() + ".webp.pgp";
 63			}
 64		}
 65		return new JingleFile(path + "/" + filename);
 66	}
 67
 68	public Bitmap resize(Bitmap originalBitmap, int size) {
 69		int w = originalBitmap.getWidth();
 70		int h = originalBitmap.getHeight();
 71		if (Math.max(w, h) > size) {
 72			int scalledW;
 73			int scalledH;
 74			if (w <= h) {
 75				scalledW = (int) (w / ((double) h / size));
 76				scalledH = size;
 77			} else {
 78				scalledW = size;
 79				scalledH = (int) (h / ((double) w / size));
 80			}
 81			Bitmap scalledBitmap = Bitmap.createScaledBitmap(originalBitmap,
 82					scalledW, scalledH, true);
 83			return scalledBitmap;
 84		} else {
 85			return originalBitmap;
 86		}
 87	}
 88	
 89	public JingleFile copyImageToPrivateStorage(Message message, Uri image) throws ImageCopyException {
 90		return this.copyImageToPrivateStorage(message, image,0);
 91	}
 92
 93	private JingleFile copyImageToPrivateStorage(Message message, Uri image, int sampleSize)
 94			throws ImageCopyException {
 95		try {
 96			InputStream is;
 97			if (image != null) {
 98				is = context.getContentResolver().openInputStream(image);
 99			} else {
100				is = new FileInputStream(getIncomingFile());
101			}
102			JingleFile file = getJingleFile(message);
103			file.getParentFile().mkdirs();
104			file.createNewFile();
105			Bitmap originalBitmap;
106			BitmapFactory.Options options = new BitmapFactory.Options();
107			int inSampleSize = (int) Math.pow(2, sampleSize);
108			Log.d("xmppService","reading bitmap with sample size "+inSampleSize);
109			options.inSampleSize = inSampleSize;
110			originalBitmap = BitmapFactory.decodeStream(is, null, options);
111			is.close();
112			if (originalBitmap == null) {
113				throw new ImageCopyException(R.string.error_not_an_image_file);
114			}
115			if (image == null) {
116				getIncomingFile().delete();
117			}
118			Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE);
119			OutputStream os = new FileOutputStream(file);
120			boolean success = scalledBitmap.compress(
121					Bitmap.CompressFormat.WEBP, 75, os);
122			if (!success) {
123				throw new ImageCopyException(R.string.error_compressing_image);
124			}
125			os.flush();
126			os.close();
127			long size = file.getSize();
128			int width = scalledBitmap.getWidth();
129			int height = scalledBitmap.getHeight();
130			message.setBody("" + size + "," + width + "," + height);
131			return file;
132		} catch (FileNotFoundException e) {
133			throw new ImageCopyException(R.string.error_file_not_found);
134		} catch (IOException e) {
135			throw new ImageCopyException(R.string.error_io_exception);
136		} catch (SecurityException e) {
137			throw new ImageCopyException(
138					R.string.error_security_exception_during_image_copy);
139		} catch (OutOfMemoryError e) {
140			++sampleSize;
141			if (sampleSize<=3) {
142				return copyImageToPrivateStorage(message, image, sampleSize);
143			} else {
144				throw new ImageCopyException(R.string.error_out_of_memory);
145			}
146		}
147	}
148
149	public Bitmap getImageFromMessage(Message message) {
150		return BitmapFactory.decodeFile(getJingleFile(message)
151				.getAbsolutePath());
152	}
153
154	public Bitmap getThumbnail(Message message, int size, boolean cacheOnly)
155			throws FileNotFoundException {
156		Bitmap thumbnail = thumbnailCache.get(message.getUuid());
157		if ((thumbnail == null) && (!cacheOnly)) {
158			Bitmap fullsize = BitmapFactory.decodeFile(getJingleFile(message)
159					.getAbsolutePath());
160			if (fullsize == null) {
161				throw new FileNotFoundException();
162			}
163			thumbnail = resize(fullsize, size);
164			this.thumbnailCache.put(message.getUuid(), thumbnail);
165		}
166		return thumbnail;
167	}
168
169	public void removeFiles(Conversation conversation) {
170		String prefix = context.getFilesDir().getAbsolutePath();
171		String path = prefix + "/" + conversation.getAccount().getJid() + "/"
172				+ conversation.getContactJid();
173		File file = new File(path);
174		try {
175			this.deleteFile(file);
176		} catch (IOException e) {
177			Log.d("xmppService",
178					"error deleting file: " + file.getAbsolutePath());
179		}
180	}
181
182	private void deleteFile(File f) throws IOException {
183		if (f.isDirectory()) {
184			for (File c : f.listFiles())
185				deleteFile(c);
186		}
187		f.delete();
188	}
189
190	public File getIncomingFile() {
191		return new File(context.getFilesDir().getAbsolutePath() + "/incoming");
192	}
193
194	public class ImageCopyException extends Exception {
195		private static final long serialVersionUID = -1010013599132881427L;
196		private int resId;
197
198		public ImageCopyException(int resId) {
199			this.resId = resId;
200		}
201
202		public int getResId() {
203			return resId;
204		}
205	}
206}