package com.androidol.util.tiles; import java.io.File; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import com.androidol.exceptions.EmptyCacheException; import com.androidol.util.Util; import com.androidol.constants.UtilConstants; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class TileFileSystemLoaderDB implements UtilConstants { // =========================================================== // fields // =========================================================== public static final String DATABASE_NAME = "osmaptilefscache_db"; // database name public static final int DATABASE_VERSION = 2; // database version public static final String T_FSCACHE = "t_fscache"; // name of the database table that keeps track of tile usage public static final String T_FSCACHE_NAME = "name_id"; // primary key for tiles in database (tile's url) public static final String T_FSCACHE_TIMESTAMP = "timestamp"; // time stamp of a tile public static final String T_FSCACHE_USAGECOUNT = "countused"; // how many times the tile has been used public static final String T_FSCACHE_FILESIZE = "filesize"; // file size of tile public static final String T_FSCACHE_CREATE_COMMAND = "CREATE TABLE IF NOT EXISTS " + T_FSCACHE + " (" + T_FSCACHE_NAME + " VARCHAR(255)," + T_FSCACHE_TIMESTAMP + " DATE NOT NULL," + T_FSCACHE_USAGECOUNT + " INTEGER NOT NULL DEFAULT 1," + T_FSCACHE_FILESIZE + " INTEGER NOT NULL," + " PRIMARY KEY(" + T_FSCACHE_NAME + ")" + ");"; public static final String T_FSCACHE_SELECT_LEAST_USED = "SELECT " + T_FSCACHE_NAME + "," + T_FSCACHE_FILESIZE + " FROM " + T_FSCACHE + " WHERE " + T_FSCACHE_USAGECOUNT + " = (SELECT MIN(" + T_FSCACHE_USAGECOUNT + ") FROM " + T_FSCACHE + ")"; public static final String T_FSCACHE_SELECT_OLDEST = "SELECT " + T_FSCACHE_NAME + "," + T_FSCACHE_FILESIZE + " FROM " + T_FSCACHE + " ORDER BY " + T_FSCACHE_TIMESTAMP + " ASC"; protected final Context context; protected final SQLiteDatabase database; protected final SimpleDateFormat DATE_FORMAT_ISO8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); private String TMP_COLUMN = "tmp"; /** * Constructor TileFileSystemLoaderDB * * @param context */ public TileFileSystemLoaderDB(Context context) { this.context = context; this.database = new DatabaseHelper(context).getWritableDatabase(); } /** * API Method: incrementUse * * @param aFormattedTileURLString */ public void incrementUse(final String formattedUrlString) { // increase the tile usage by 1 and also update the time stamp final Cursor c = this.database.rawQuery("UPDATE " + T_FSCACHE + " SET " + T_FSCACHE_USAGECOUNT + " = " + T_FSCACHE_USAGECOUNT + " + 1 , " + T_FSCACHE_TIMESTAMP + " = '" + getNowAsIso8601() + "' WHERE " + T_FSCACHE_NAME + " = '" + formattedUrlString + "'", null); c.close(); } /** * API Method: addTileOrIncrement * * @param url * @param fileSizeInByte * * @return fileSize * size of the tile image in bytes */ public int addTileOrIncrement(final String formattedUrlString, final int fileSizeInByte) { // select the tile from database final Cursor c = this.database.rawQuery("SELECT * FROM " + T_FSCACHE + " WHERE " + T_FSCACHE_NAME + " = '" + formattedUrlString + "'", null); // check if tile already existed final boolean existed = c.getCount() > 0; c.close(); //Util.printDebugMessage("Tile existed: " + existed); if (existed) { incrementUse(formattedUrlString); return 0; } else { insertNewTileInfo(formattedUrlString, fileSizeInByte); return fileSizeInByte; } } /** * API Method: removeTile * * @param formattedUrlString * @param fileSizeInByte */ public int removeTile(final String formattedUrlString) { // select the tile from database Util.printDebugMessage(" ...try to remove tile " + formattedUrlString + " from database..."); final Cursor c = this.database.rawQuery("SELECT * FROM " + T_FSCACHE + " WHERE " + T_FSCACHE_NAME + " = '" + formattedUrlString + "'", null); // check if tile already existed final boolean existed = (c.getCount() > 0); if(existed) { c.moveToFirst(); final int tileSize = c.getInt(c.getColumnIndexOrThrow(T_FSCACHE_FILESIZE)); this.database.delete(T_FSCACHE, T_FSCACHE_NAME + "='" + formattedUrlString + "'", null); Util.printDebugMessage(" ...tile " + formattedUrlString + " removed from database...tile size " + tileSize); c.close(); return tileSize; } else { c.close(); return 0; } } /** * API Method: insertNewTileInfo * * @param formattedUrlString * @param fileSizeInByte */ public void insertNewTileInfo(final String formattedUrlString, final int fileSizeInByte) { final ContentValues cv = new ContentValues(); cv.put(T_FSCACHE_NAME, formattedUrlString); cv.put(T_FSCACHE_TIMESTAMP, getNowAsIso8601()); cv.put(T_FSCACHE_FILESIZE, fileSizeInByte); this.database.insert(T_FSCACHE, null, cv); } /** * API Method: deleteOldest * * @param pSizeNeeded * @return size of cache being freed * @throws EmptyCacheException */ public int deleteOldest(final int sizeNeeded) throws EmptyCacheException { final Cursor c = this.database.rawQuery(T_FSCACHE_SELECT_OLDEST, null); final ArrayList<String> deleteFromDB = new ArrayList<String>(); int sizeGained = 0; if(c != null){ String fileNameOfDeleted; if(c.moveToFirst()) { do { final int sizeItem = c.getInt(c.getColumnIndexOrThrow(T_FSCACHE_FILESIZE)); sizeGained += sizeItem; fileNameOfDeleted = c.getString(c.getColumnIndexOrThrow(T_FSCACHE_NAME)); deleteFromDB.add(fileNameOfDeleted); // delete files stored in context //this.context.deleteFile(fileNameOfDeleted); // delete files stored on SD card try { File fileToDelete = new File(fileNameOfDeleted); if(fileToDelete.exists()) { fileToDelete.delete(); } } catch(Exception e) { Util.printErrorMessage("...error deleting cached tile...file name: " + fileNameOfDeleted); } Util.printDebugMessage("...deleted from file system: " + fileNameOfDeleted + " for " + sizeItem + " Bytes..."); } while(c.moveToNext() && sizeGained < sizeNeeded); } else { c.close(); throw new EmptyCacheException("...cache is empty...."); } c.close(); for(String fn : deleteFromDB) { this.database.delete(T_FSCACHE, T_FSCACHE_NAME + "='" + fn + "'", null); } } return sizeGained; } /** * API Method: getCacheUsedInByte * * @return total size of cache in use */ public int getCacheUsedInByte() { final Cursor c = this.database.rawQuery("SELECT SUM(" + T_FSCACHE_FILESIZE + ") AS " + TMP_COLUMN + " FROM " + T_FSCACHE, null); final int ret; if(c != null){ if(c.moveToFirst()) { ret = c.getInt(c.getColumnIndexOrThrow(TMP_COLUMN)); } else { ret = 0; } }else{ ret = 0; } c.close(); return ret; } /** * private method: getNowAsIso8601 * Get at the moment within ISO8601 format. * * @return * Date and time in ISO8601 format. */ private String getNowAsIso8601() { return DATE_FORMAT_ISO8601.format(new Date(System.currentTimeMillis())); } /** * private class DatabaseHelper */ private class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(final Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(T_FSCACHE_CREATE_COMMAND); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //Util.printDebugMessage("...upgrading database from version " + oldVersion + " to " + newVersion + "...all old data will be erased..."); db.execSQL("DROP TABLE IF EXISTS " + T_FSCACHE); onCreate(db); } } }