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