package com.afollestad.silk.caching;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.os.Handler;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
class SilkCacheBase<Item extends SilkComparable<Item>> extends SilkCacheBaseLimiter<Item> {
public SilkCacheBase(Context context, String name, Class<?> cls) {
this(context, name, cls, null);
}
public SilkCacheBase(Context context, String name, Class<?> cls, Handler handler) {
super(context, name);
mCls = cls;
if (handler == null) mHandler = new Handler();
else mHandler = handler;
}
private final static File CACHE_DIR = new File(Environment.getExternalStorageDirectory(), ".silk_cache");
private final Handler mHandler;
private List<Item> mBuffer;
private boolean isChanged;
private final Class<?> mCls;
private Kryo getKryo() {
Kryo mKryo = new Kryo();
mKryo.register(mCls);
return mKryo;
}
public final boolean isChanged() {
return isChanged;
}
protected List<Item> getBuffer() {
return mBuffer;
}
protected Handler getHandler() {
return mHandler;
}
protected File getCacheFile() {
return new File(CACHE_DIR, getName() + ".cache");
}
protected void markChanged() {
isChanged = true;
}
protected void loadItems() {
File cacheFile = getCacheFile();
if (hasExpiration()) {
long expiration = getExpiration();
long now = Calendar.getInstance().getTimeInMillis();
if (now >= expiration) {
// Cache is expired
cacheFile.delete();
log("Cache has expired, re-creating...");
setExpiration(-1);
}
}
mBuffer = new ArrayList<Item>();
if (cacheFile.exists()) {
Input input = null;
try {
Kryo kryo = getKryo();
input = new Input(new FileInputStream(cacheFile));
while (true) {
final Object item = kryo.readObjectOrNull(input, mCls);
if (item != null) mBuffer.add((Item) item);
else break;
}
} catch (Exception e) {
e.printStackTrace();
log("Error loading items -- " + e.getMessage());
} finally {
if (input != null) input.close();
log("Read " + mBuffer.size() + " items from the cache file");
}
} else log("Cache file doesn't exist (" + cacheFile.getAbsolutePath() + ").");
}
public final long getExpiration() {
SharedPreferences prefs = getContext().getSharedPreferences("[silk-cache-expiration]", Context.MODE_PRIVATE);
return prefs.getLong(getName(), -1);
}
public final boolean hasExpiration() {
return getExpiration() > -1;
}
public final void setExpiration(long dateTime) {
SharedPreferences.Editor prefs = getContext().getSharedPreferences("[silk-cache-expiration]", Context.MODE_PRIVATE).edit();
if (dateTime < 0)
prefs.remove(getName());
else prefs.putLong(getName(), dateTime);
prefs.commit();
}
public final void setExpiration(int weeks, int days, int hours, int minutes) {
long now = Calendar.getInstance().getTimeInMillis();
now += (1000 * 60) * minutes; // 60 seconds in a minute
now += (1000 * 60 * 60) * hours; // 60 minutes in an hour
now += (1000 * 60 * 60 * 24) * days; // 24 hours in a day
now += (1000 * 60 * 60 * 24 * 7) * weeks; // 7 days in a week
setExpiration(now);
}
public final void setLimiter(SilkCacheLimiter limiter) {
if (limiter == null) {
getLimiterPrefs().edit().remove(getName()).commit();
} else {
getLimiterPrefs().edit().putString(getName(), limiter.toString()).commit();
// Perform limiting if necessary
if (atLimit(mBuffer)) {
mBuffer = performLimit(mBuffer);
}
}
}
public final void commit() throws Exception {
final File cacheFile = getCacheFile();
if (!isChanged) {
throw new IllegalStateException("The cache has not been modified since initialization or the last commit.");
} else if (mBuffer.size() == 0) {
if (cacheFile.exists()) {
log("Deleting: " + cacheFile.getName());
cacheFile.delete();
}
return;
}
// Perform limiting if necessary
if (atLimit(mBuffer)) {
mBuffer = performLimit(mBuffer);
}
CACHE_DIR.mkdirs();
Kryo kryo = getKryo();
Output output = new Output(new FileOutputStream(cacheFile));
for (Item item : mBuffer)
kryo.writeObject(output, item);
output.close();
log("Committed " + mBuffer.size() + " items.");
isChanged = false;
}
public final void commit(final SilkCache.SimpleCommitCallback callback) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
commit();
mHandler.post(new Runnable() {
@Override
public void run() {
if (callback != null && callback instanceof SilkCache.CommitCallback)
((SilkCache.CommitCallback) callback).onCommitted();
}
});
} catch (final Exception e) {
log("Commit error: " + e.getMessage());
mHandler.post(new Runnable() {
@Override
public void run() {
if (callback != null) callback.onError(e);
}
});
}
}
});
t.setPriority(Thread.MAX_PRIORITY);
t.start();
}
}