package de.tum.in.tumcampusapp.managers; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.graphics.Bitmap; import android.support.v4.util.LruCache; import android.widget.ImageView; import com.google.common.base.Optional; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import de.tum.in.tumcampusapp.activities.CurriculaActivity; import de.tum.in.tumcampusapp.auxiliary.AccessTokenManager; import de.tum.in.tumcampusapp.auxiliary.Const; import de.tum.in.tumcampusapp.auxiliary.NetUtils; import de.tum.in.tumcampusapp.auxiliary.Utils; import de.tum.in.tumcampusapp.models.tumo.CalendarRowSet; import de.tum.in.tumcampusapp.models.tumo.LecturesSearchRow; import de.tum.in.tumcampusapp.models.tumo.LecturesSearchRowSet; import de.tum.in.tumcampusapp.models.tumo.OrgItemList; import de.tum.in.tumcampusapp.models.tumo.TuitionList; import de.tum.in.tumcampusapp.tumonline.TUMOnlineConst; import de.tum.in.tumcampusapp.tumonline.TUMOnlineRequest; /** * TUMOnline cache manager, allows caching of TUMOnline requests */ public class CacheManager { public static final int CACHE_TYP_DATA = 0; public static final int CACHE_TYP_IMAGE = 1; /** * Validity's for entries in seconds */ public static final int VALIDITY_DO_NOT_CACHE = 0; public static final int VALIDITY_ONE_DAY = 86400; public static final int VALIDITY_TWO_DAYS = 2 * 86400; public static final int VALIDITY_FIFE_DAYS = 5 * 86400; public static final int VALIDITY_TEN_DAYS = 10 * 86400; public static final int VALIDITY_ONE_MONTH = 30 * 86400; public static final Map<ImageView, String> IMAGE_VIEWS = Collections.synchronizedMap(new WeakHashMap<ImageView, String>()); public static final LruCache<String, Bitmap> BITMAP_CACHE; private static SQLiteDatabase cacheDb; private Context mContext; static { int cacheSize = 4 * 1024 * 1024; // 4MiB BITMAP_CACHE = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getRowBytes() * bitmap.getHeight(); } }; } private static synchronized void initCacheDb(Context c) { if (cacheDb == null) { File dbFile = new File(c.getCacheDir().getAbsolutePath() + "/cache.db"); dbFile.getParentFile().mkdirs(); cacheDb = SQLiteDatabase.openOrCreateDatabase(dbFile, null); } } /** * Constructor, open/create database, create table if necessary * * @param context Context */ public CacheManager(Context context) { initCacheDb(context); mContext = context; // create table if needed cacheDb.execSQL("CREATE TABLE IF NOT EXISTS cache (url VARCHAR UNIQUE, data BLOB, " + "validity VARCHAR, max_age VARCHAR, typ INTEGER)"); // Delete all entries that are too old and delete corresponding image files cacheDb.beginTransaction(); Cursor cur = cacheDb.rawQuery("SELECT data FROM cache WHERE datetime()>max_age AND typ=1", null); if (cur.moveToFirst()) { do { File f = new File(cur.getString(0)); f.delete(); } while (cur.moveToNext()); } cur.close(); cacheDb.execSQL("DELETE FROM cache WHERE datetime()>max_age"); cacheDb.setTransactionSuccessful(); cacheDb.endTransaction(); } /** * Download usual tumOnline requests */ public void fillCache() { NetUtils net = new NetUtils(mContext); // Cache curricula urls if (shouldRefresh(CurriculaActivity.CURRICULA_URL)) { net.downloadJsonArray(CurriculaActivity.CURRICULA_URL, CacheManager.VALIDITY_ONE_MONTH, true); } // Cache news source images NewsManager news = new NewsManager(mContext); Cursor cur = news.getNewsSources(); if (cur.moveToFirst()) { do { String imgUrl = cur.getString(1); if (!imgUrl.isEmpty() && !"null".equals(imgUrl)) { net.downloadImage(imgUrl); } } while (cur.moveToNext()); } cur.close(); // Cache news images cur = news.getAllFromDb(mContext); if (cur.moveToFirst()) { do { String imgUrl = cur.getString(4); if (!"null".equals(imgUrl)) { net.downloadImage(imgUrl); } } while (cur.moveToNext()); } cur.close(); // Cache kino covers KinoManager km = new KinoManager(mContext); cur = km.getAllFromDb(); if (cur.moveToFirst()) { do { String imgUrl = cur.getString(cur.getColumnIndex(Const.JSON_COVER)); if (!"null".equals(imgUrl)) { net.downloadImage(imgUrl); } } while (cur.moveToNext()); } cur.close(); // acquire access token if (!new AccessTokenManager(mContext).hasValidAccessToken()) { return; } // ALL STUFF BELOW HERE NEEDS A VALID ACCESS TOKEN // Sync organisation tree TUMOnlineRequest<OrgItemList> requestHandler = new TUMOnlineRequest<>(TUMOnlineConst.ORG_TREE, mContext); if (shouldRefresh(requestHandler.getRequestURL())) { requestHandler.fetch(); } // Sync fee status TUMOnlineRequest<TuitionList> requestHandler2 = new TUMOnlineRequest<>(TUMOnlineConst.TUITION_FEE_STATUS, mContext); if (shouldRefresh(requestHandler2.getRequestURL())) { requestHandler2.fetch(); } // Sync lectures, details and appointments importLecturesFromTUMOnline(); // Sync calendar syncCalendar(); } public void syncCalendar() { TUMOnlineRequest<CalendarRowSet> requestHandler = new TUMOnlineRequest<>(TUMOnlineConst.CALENDER, mContext); requestHandler.setParameter("pMonateVor", "0"); requestHandler.setParameter("pMonateNach", "3"); if (shouldRefresh(requestHandler.getRequestURL())) { Optional<CalendarRowSet> set = requestHandler.fetch(); if (set.isPresent()) { CalendarManager calendarManager = new CalendarManager(mContext); calendarManager.importCalendar(set.get()); CalendarManager.QueryLocationsService.loadGeo(mContext); } } } /** * Checks if a new sync is needed or if data is up-to-date and returns the cache content * if data is up to date * * @param url Url from which data was cached * @return Data if valid version was found, null if no data is available */ public Optional<String> getFromCache(String url) { String result = null; try { Cursor c = cacheDb.rawQuery("SELECT data FROM cache WHERE url=? AND datetime()<max_age", new String[]{url}); if (c.getCount() == 1) { c.moveToFirst(); result = c.getString(0); } c.close(); } catch (SQLiteException e) { Utils.log(e); } return Optional.fromNullable(result); } /** * Checks if a new sync is needed or if data is up-to-date * * @param url Url from which data was cached * @return Data if valid version was found, null if no data is available */ public boolean shouldRefresh(String url) { boolean result = true; try { Cursor c = cacheDb.rawQuery("SELECT url FROM cache WHERE url=? AND datetime() < validity", new String[]{url}); if (c.getCount() == 1) { result = false; } c.close(); } catch (SQLiteException e) { Utils.log(e); } return result; } /** * Add a result to cache * * @param url url from where the data was fetched * @param data result */ public void addToCache(String url, String data, int validity, int typ) { if (validity == VALIDITY_DO_NOT_CACHE) { return; } cacheDb.execSQL("REPLACE INTO cache (url, data, validity, max_age, typ) " + "VALUES (?, ?, datetime('now','+" + (validity / 2) + " seconds'), " + "datetime('now','+" + validity + " seconds'), ?)", new String[]{url, data, String.valueOf(typ)}); } /** * this function allows us to import all lecture items from TUMOnline */ public void importLecturesFromTUMOnline() { // get my lectures TUMOnlineRequest<LecturesSearchRowSet> requestHandler = new TUMOnlineRequest<>(TUMOnlineConst.LECTURES_PERSONAL, mContext); if (!shouldRefresh(requestHandler.getRequestURL())) { return; } Optional<LecturesSearchRowSet> lecturesList = requestHandler.fetch(); if (!lecturesList.isPresent()) { return; } List<LecturesSearchRow> lectures = lecturesList.get().getLehrveranstaltungen(); if (lectures == null) { return; } ChatRoomManager manager = new ChatRoomManager(mContext); manager.replaceInto(lectures); } public static synchronized void clearCache(Context context) { cacheDb = null; try { Process proc = Runtime.getRuntime().exec("rm -r " + context.getCacheDir()); proc.waitFor(); } catch (InterruptedException | IOException e) { Utils.log("couldn't delete cache files " + e.toString()); } } }