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}