ArViewer  Android Version
Arvos - Augmented reality viewer open source
 All Classes Namespaces Files Functions Variables
ArvosCache.java
Go to the documentation of this file.
1 /*
2  Copyright (C) 2013, Peter Graf
3 
4  This file is part of Arvos - AR Viewer Open Source for Android.
5  Arvos is free software.
6 
7  This program is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program. If not, see <http://www.gnu.org/licenses/>.
19 
20  For more information on the AR Viewer Open Source or Peter Graf,
21  please see: http://www.mission-base.com/.
22  */
23 
24 package com.mission_base.arviewer_android;
25 
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.FileOutputStream;
29 import java.io.InputStreamReader;
30 import java.io.OutputStreamWriter;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.TreeSet;
34 
35 import android.app.Activity;
36 import android.graphics.Bitmap;
37 import android.graphics.BitmapFactory;
38 import android.os.Environment;
39 
57 public class ArvosCache
58 {
59  private static String mLock = "lock";
60  private static ArvosCache instance;
61 
65  public static final String mExtension = ".arvos";
66 
67  private TreeSet<ArvosCacheEntry> mEntrySet = new TreeSet<ArvosCacheEntry>();
68  private HashMap<String, ArvosCacheEntry> mEntryMap = new HashMap<String, ArvosCacheEntry>();
69  private long mSize = 0;
70  private File mCacheDir = null;
71 
72  private long mMaxAge;
73  private long mMaxSize;
74  private long mMaxFiles;
75 
76  private ArvosCache(long maxAge, long maxFiles, long maxSize)
77  {
78  mMaxAge = maxAge;
79  mMaxFiles = maxFiles;
80  mMaxSize = maxSize;
81  }
82 
83  private Activity mActivity;
84 
97  public static void initialize(Activity activity, long maxAge, long maxFiles, long maxSize)
98  {
99  if (instance == null)
100  {
101  synchronized (mLock)
102  {
103  if (instance == null)
104  {
105  instance = new ArvosCache(maxAge, maxFiles, maxSize);
106  instance.mActivity = activity;
107  }
108  }
109  }
110  }
111 
112  private static ArvosCache getInstance()
113  {
114  if (instance.mCacheDir == null)
115  {
116  synchronized (mLock)
117  {
118  if (instance.mCacheDir == null)
119  {
120  instance.init();
121  }
122  }
123  }
124  return instance;
125  }
126 
127  private void init()
128  {
129  if (instance.IsExternalStorageAvailableAndWriteable())
130  {
131  mCacheDir = mActivity.getExternalCacheDir();
132  }
133  if (mCacheDir == null)
134  {
135  mCacheDir = mActivity.getCacheDir();
136  }
137  mCacheDir = new File(mCacheDir, "webcachedir");
138  if (!mCacheDir.exists())
139  {
140  mCacheDir.mkdirs();
141  }
142 
143  File[] mFiles = mCacheDir.listFiles();
144  if (mFiles == null)
145  {
146  return;
147  }
148 
149  char[] inputBuffer = new char[1];
150  StringBuilder sb = new StringBuilder();
151 
152  for (File file : mFiles)
153  {
154  Long lastAccessTime = getLastAccessTime(file);
155  if (lastAccessTime == null)
156  {
157  continue;
158  }
159 
160  try
161  {
162  FileInputStream fIn = new FileInputStream(file);
163  InputStreamReader isr = new InputStreamReader(fIn);
164 
165  try
166  {
167  sb.setLength(0);
168  int length = 0;
169  while (isr.read(inputBuffer) == 1)
170  {
171  length++;
172 
173  if ('\n' == inputBuffer[0])
174  {
175  ArvosCacheEntry entry = new ArvosCacheEntry();
176  entry.url = sb.toString();
177  entry.urlLength = length;
178  entry.lastAccessTime = lastAccessTime;
179  entry.fileLength = file.length();
180  mEntrySet.add(entry);
181  break;
182  }
183 
184  sb.append(inputBuffer[0]);
185  }
186  }
187  finally
188  {
189  isr.close();
190  }
191  }
192  catch (Exception e)
193  {
194  continue;
195  }
196  }
197  for (Iterator<ArvosCacheEntry> iterator = mEntrySet.iterator(); iterator.hasNext();)
198  {
199  ArvosCacheEntry entry = iterator.next();
200  if (mEntryMap.containsKey(entry.url))
201  {
202  iterator.remove();
203  delete(entry);
204  }
205 
206  mEntryMap.put(entry.url, entry);
207  mSize += entry.fileLength;
208  }
209  cleanup();
210  }
211 
212  private Long getLastAccessTime(File file)
213  {
214  if (!file.isFile())
215  {
216  return null;
217  }
218  String name = file.getName();
219  if (!name.endsWith(mExtension))
220  {
221  return null;
222  }
223  try
224  {
225  return Long.valueOf(name.replace(mExtension, ""));
226  }
227  catch (Exception e)
228  {
229  }
230  return null;
231  }
232 
233  private Long getLastAccessTime()
234  {
235  long now = System.currentTimeMillis();
236  if (!mEntrySet.isEmpty())
237  {
238  if (mEntrySet.last().lastAccessTime.compareTo(Long.valueOf(now)) >= 0)
239  {
240  return Long.valueOf(mEntrySet.last().lastAccessTime + 1);
241  }
242  }
243  return Long.valueOf(now);
244  }
245 
246  private void cleanup()
247  {
248  long now = System.currentTimeMillis();
249 
250  while (mEntrySet.size() > 1 && (mMaxAge > 0L && (now - mEntrySet.first().lastAccessTime > mMaxAge)) || (mMaxSize > 0L && mSize > mMaxSize)
251  || (mMaxFiles > 0L && mEntrySet.size() > mMaxFiles))
252  {
253  delete(mEntrySet.first());
254  }
255  }
256 
260  public static void clear()
261  {
262  getInstance().clearCache();
263  }
264 
265  private void clearCache()
266  {
267  synchronized (mLock)
268  {
269  while (!mEntrySet.isEmpty())
270  {
271  delete(mEntrySet.first());
272  }
273  }
274  }
275 
276  private void delete(ArvosCacheEntry entry)
277  {
278  File file = new File(mCacheDir, entry.getFileName());
279  if (file.exists())
280  {
281  file.delete();
282  }
283  mEntryMap.remove(entry.url);
284  if (mEntrySet.contains(entry))
285  {
286  mEntrySet.remove(entry);
287  }
288  mSize -= entry.fileLength;
289  }
290 
298  public static Bitmap getBitmap(String url)
299  {
300  if (!Arvos.getInstance().mUseCache)
301  {
302  return null;
303  }
304  return getInstance().getCachedBitmap(ArvosHttpRequest.urlEncode(url));
305  }
306 
307  private Bitmap getCachedBitmap(String url)
308  {
309  synchronized (mLock)
310  {
311  ArvosCacheEntry entry = mEntryMap.get(url);
312  if (entry == null)
313  {
314  return null;
315  }
316  File file = new File(mCacheDir, entry.getFileName());
317  try
318  {
319  if (file.exists())
320  {
321  Bitmap bitmap = null;
322  FileInputStream inputStream = new FileInputStream(file);
323  try
324  {
325  if (entry.urlLength == inputStream.skip(entry.urlLength))
326  {
327  bitmap = BitmapFactory.decodeStream(inputStream);
328  }
329  }
330  finally
331  {
332  inputStream.close();
333  }
334  if (bitmap != null)
335  {
336  mEntrySet.remove(entry);
337  entry.lastAccessTime = getLastAccessTime();
338  mEntrySet.add(entry);
339  file.renameTo(new File(mCacheDir, entry.getFileName()));
340  return bitmap;
341  }
342  }
343  delete(entry);
344  }
345  catch (Exception e)
346  {
347  }
348  return null;
349  }
350  }
351 
360  public static void add(String url, Bitmap bitmap)
361  {
362  if (!Arvos.getInstance().mUseCache)
363  {
364  return;
365  }
366  getInstance().addBitmap(ArvosHttpRequest.urlEncode(url), bitmap);
367  }
368 
369  private void addBitmap(String url, Bitmap bitmap)
370  {
371  synchronized (mLock)
372  {
373  ArvosCacheEntry other = mEntryMap.get(url);
374  if (other != null)
375  {
376  delete(other);
377  }
378 
379  ArvosCacheEntry entry = new ArvosCacheEntry();
380  entry.url = url;
381  entry.lastAccessTime = getLastAccessTime();
382  File file = new File(mCacheDir, entry.getFileName());
383 
384  try
385  {
386  FileOutputStream fOut = new FileOutputStream(file);
387  OutputStreamWriter osw = new OutputStreamWriter(fOut);
388 
389  try
390  {
391  osw.write(url + "\n");
392  osw.flush();
393  entry.urlLength = (int) file.length();
394 
395  bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
396  }
397  finally
398  {
399  osw.close();
400  entry.fileLength = file.length();
401  }
402  }
403  catch (Exception e)
404  {
405  file.delete();
406  entry = null;
407  }
408  if (entry != null)
409  {
410  mSize += entry.fileLength;
411  mEntrySet.add(entry);
412  mEntryMap.put(entry.url, entry);
413  }
414  cleanup();
415  }
416  }
417 
418  private boolean IsExternalStorageAvailableAndWriteable()
419  {
420  boolean externalStorageAvailable = false;
421  boolean externalStorageWriteable = false;
422  String state = Environment.getExternalStorageState();
423 
424  if (Environment.MEDIA_MOUNTED.equals(state))
425  {
426  externalStorageAvailable = externalStorageWriteable = true;
427  }
428  else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
429  {
430  externalStorageAvailable = true;
431  externalStorageWriteable = false;
432  }
433  else
434  {
435  externalStorageAvailable = externalStorageWriteable = false;
436  }
437  return externalStorageAvailable && externalStorageWriteable;
438  }
439 }