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			Bitmap originalBitmap;
 98			try {
 99				originalBitmap = BitmapFactory.decodeStream(is);
100				is.close();
101			} catch (OutOfMemoryError e) {
102				is.close();
103				Log.d("xmppService","catched out of memory. try again");
104				if (image != null) {
105					is = context.getContentResolver().openInputStream(image);
106				} else {
107					is = new FileInputStream(getIncomingFile());
108				}
109				BitmapFactory.Options options = new BitmapFactory.Options();
110				options.inSampleSize = 2;
111				originalBitmap = BitmapFactory.decodeStream(is, null, options);
112				is.close();
113			}
114			if (originalBitmap == null) {
115				throw new ImageCopyException(R.string.error_not_an_image_file);
116			}
117			if (image == null) {
118				getIncomingFile().delete();
119			}
120			Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE);
121			OutputStream os = new FileOutputStream(file);
122			boolean success = scalledBitmap.compress(
123					Bitmap.CompressFormat.WEBP, 75, os);
124			if (!success) {
125				throw new ImageCopyException(R.string.error_compressing_image);
126			}
127			os.flush();
128			os.close();
129			long size = file.getSize();
130			int width = scalledBitmap.getWidth();
131			int height = scalledBitmap.getHeight();
132			message.setBody("" + size + "," + width + "," + height);
133			return file;
134		} catch (FileNotFoundException e) {
135			throw new ImageCopyException(R.string.error_file_not_found);
136		} catch (IOException e) {
137			throw new ImageCopyException(R.string.error_io_exception);
138		} catch (SecurityException e) {
139			throw new ImageCopyException(
140					R.string.error_security_exception_during_image_copy);
141		}
142	}
143
144	public Bitmap getImageFromMessage(Message message) {
145		return BitmapFactory.decodeFile(getJingleFile(message)
146				.getAbsolutePath());
147	}
148
149	public Bitmap getThumbnail(Message message, int size, boolean cacheOnly)
150			throws FileNotFoundException {
151		Bitmap thumbnail = thumbnailCache.get(message.getUuid());
152		if ((thumbnail == null) && (!cacheOnly)) {
153			Bitmap fullsize = BitmapFactory.decodeFile(getJingleFile(message)
154					.getAbsolutePath());
155			if (fullsize == null) {
156				throw new FileNotFoundException();
157			}
158			thumbnail = resize(fullsize, size);
159			this.thumbnailCache.put(message.getUuid(), thumbnail);
160		}
161		return thumbnail;
162	}
163
164	public void removeFiles(Conversation conversation) {
165		String prefix = context.getFilesDir().getAbsolutePath();
166		String path = prefix + "/" + conversation.getAccount().getJid() + "/"
167				+ conversation.getContactJid();
168		File file = new File(path);
169		try {
170			this.deleteFile(file);
171		} catch (IOException e) {
172			Log.d("xmppService",
173					"error deleting file: " + file.getAbsolutePath());
174		}
175	}
176
177	private void deleteFile(File f) throws IOException {
178		if (f.isDirectory()) {
179			for (File c : f.listFiles())
180				deleteFile(c);
181		}
182		f.delete();
183	}
184
185	public File getIncomingFile() {
186		return new File(context.getFilesDir().getAbsolutePath() + "/incoming");
187	}
188
189	public class ImageCopyException extends Exception {
190		private static final long serialVersionUID = -1010013599132881427L;
191		private int resId;
192
193		public ImageCopyException(int resId) {
194			this.resId = resId;
195		}
196
197		public int getResId() {
198			return resId;
199		}
200	}
201}