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