package cgeo.geocaching; import cgeo.geocaching.cgData.StorageLocation; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LoadFlags.LoadFlag; import cgeo.geocaching.enumerations.LoadFlags.RemoveFlag; import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.network.StatusUpdater; import cgeo.geocaching.utils.IObserver; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; import android.app.Activity; import android.app.Application; import android.app.ProgressDialog; import android.content.res.Resources; import android.os.Handler; import android.os.Message; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; public class cgeoapplication extends Application { final private cgData storage = new cgData(); private String action = null; private volatile GeoDataProvider geo; private volatile DirectionProvider dir; public boolean firstRun = true; // c:geo is just launched public boolean showLoginToast = true; //login toast shown just once. private boolean databaseCleaned = false; // database was cleaned private boolean liveMapHintShown = false; // livemap hint has been shown final private StatusUpdater statusUpdater = new StatusUpdater(); private static cgeoapplication instance = null; public cgeoapplication() { instance = this; } public static cgeoapplication getInstance() { return instance; } @Override public void onCreate() { new Thread(statusUpdater).start(); } @Override public void onLowMemory() { Log.i("Cleaning applications cache."); storage.removeAllFromCache(); } @Override public void onTerminate() { Log.d("Terminating c:geo…"); storage.clean(); storage.closeDb(); super.onTerminate(); } public String backupDatabase() { return storage.backupDatabase(); } /** * Move the database to/from external storage in a new thread, * showing a progress window * * @param fromActivity */ public void moveDatabase(final Activity fromActivity) { final Resources res = this.getResources(); final ProgressDialog dialog = ProgressDialog.show(fromActivity, res.getString(R.string.init_dbmove_dbmove), res.getString(R.string.init_dbmove_running), true, false); final AtomicBoolean atomic = new AtomicBoolean(false); Thread moveThread = new Thread() { final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { dialog.dismiss(); boolean success = atomic.get(); String message = success ? res.getString(R.string.init_dbmove_success) : res.getString(R.string.init_dbmove_failed); ActivityMixin.helpDialog(fromActivity, res.getString(R.string.init_dbmove_dbmove), message); } }; @Override public void run() { atomic.set(storage.moveDatabase()); handler.sendMessage(handler.obtainMessage()); } }; moveThread.start(); } public static File isRestoreFile() { return cgData.isRestoreFile(); } /** * restore the database in a new thread, showing a progress window * * @param fromActivity * calling activity */ public void restoreDatabase(final Activity fromActivity) { final Resources res = this.getResources(); final ProgressDialog dialog = ProgressDialog.show(fromActivity, res.getString(R.string.init_backup_restore), res.getString(R.string.init_restore_running), true, false); final AtomicBoolean atomic = new AtomicBoolean(false); Thread restoreThread = new Thread() { final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { dialog.dismiss(); boolean restored = atomic.get(); String message = restored ? res.getString(R.string.init_restore_success) : res.getString(R.string.init_restore_failed); ActivityMixin.helpDialog(fromActivity, res.getString(R.string.init_backup_restore), message); } }; @Override public void run() { atomic.set(storage.restoreDatabase()); handler.sendMessage(handler.obtainMessage()); } }; restoreThread.start(); } /** * Register an observer to receive GeoData information. * <br/> * If there is a chance that no observers are registered before this * method is called, it is necessary to call it from a task implementing * a looper interface as the data provider will use listeners that * require a looper thread to run. * * @param observer a geodata observer */ public void addGeoObserver(final IObserver<? super IGeoData> observer) { currentGeoObject().addObserver(observer); } public void deleteGeoObserver(final IObserver<? super IGeoData> observer) { currentGeoObject().deleteObserver(observer); } private GeoDataProvider currentGeoObject() { if (geo == null) { synchronized(this) { if (geo == null) { geo = new GeoDataProvider(this); } } } return geo; } public IGeoData currentGeo() { return currentGeoObject().getMemory(); } public void addDirectionObserver(final IObserver<? super Float> observer) { currentDirObject().addObserver(observer); } public void deleteDirectionObserver(final IObserver<? super Float> observer) { currentDirObject().deleteObserver(observer); } private DirectionProvider currentDirObject() { if (dir == null) { synchronized(this) { if (dir == null) { dir = new DirectionProvider(this); } } } return dir; } public StatusUpdater getStatusUpdater() { return statusUpdater; } public boolean storageStatus() { return storage.status(); } public void cleanDatabase(boolean more) { if (databaseCleaned) { return; } storage.clean(more); databaseCleaned = true; } /** {@link cgData#isThere(String, String, boolean, boolean)} */ public boolean isThere(String geocode, String guid, boolean detailed, boolean checkTime) { return storage.isThere(geocode, guid, detailed, checkTime); } /** {@link cgData#isOffline(String, String)} */ public boolean isOffline(String geocode, String guid) { return storage.isOffline(geocode, guid); } /** {@link cgData#getGeocodeForGuid(String)} */ public String getGeocode(String guid) { return storage.getGeocodeForGuid(guid); } /** {@link cgData#getCacheidForGeocode(String)} */ public String getCacheid(String geocode) { return storage.getCacheidForGeocode(geocode); } public boolean hasUnsavedCaches(final SearchResult search) { if (search == null) { return false; } for (final String geocode : search.getGeocodes()) { if (!isOffline(geocode, null)) { return true; } } return false; } public cgTrackable getTrackableByGeocode(String geocode) { if (StringUtils.isBlank(geocode)) { return null; } return storage.loadTrackable(geocode); } /** {@link cgData#allDetailedThere()} */ public String[] geocodesInCache() { return storage.allDetailedThere(); } public Viewport getBounds(String geocode) { if (geocode == null) { return null; } return getBounds(Collections.singleton(geocode)); } /** {@link cgData#getBounds(Set)} */ public Viewport getBounds(final Set<String> geocodes) { return storage.getBounds(geocodes); } /** {@link cgData#loadBatchOfStoredGeocodes(boolean, Geopoint, CacheType, int)} */ public SearchResult getBatchOfStoredCaches(final boolean detailedOnly, final Geopoint coords, final CacheType cacheType, final int listId) { final Set<String> geocodes = storage.loadBatchOfStoredGeocodes(detailedOnly, coords, cacheType, listId); return new SearchResult(geocodes, getAllStoredCachesCount(true, cacheType, listId)); } /** {@link cgData#loadHistoryOfSearchedLocations()} */ public List<Destination> getHistoryOfSearchedLocations() { return storage.loadHistoryOfSearchedLocations(); } public SearchResult getHistoryOfCaches(final boolean detailedOnly, final CacheType cacheType) { final Set<String> geocodes = storage.loadBatchOfHistoricGeocodes(detailedOnly, cacheType); return new SearchResult(geocodes, getAllHistoricCachesCount()); } /** {@link cgData#loadCachedInViewport(Viewport, CacheType)} */ public SearchResult getCachedInViewport(final Viewport viewport, final CacheType cacheType) { final Set<String> geocodes = storage.loadCachedInViewport(viewport, cacheType); return new SearchResult(geocodes); } /** {@link cgData#loadStoredInViewport(Viewport, CacheType)} */ public SearchResult getStoredInViewport(final Viewport viewport, final CacheType cacheType) { final Set<String> geocodes = storage.loadStoredInViewport(viewport, cacheType); return new SearchResult(geocodes); } /** {@link cgData#getAllStoredCachesCount(boolean, CacheType, int)} */ public int getAllStoredCachesCount(final boolean detailedOnly, final CacheType cacheType) { return storage.getAllStoredCachesCount(detailedOnly, cacheType, 0); } /** {@link cgData#getAllStoredCachesCount(boolean, CacheType, int)} */ public int getAllStoredCachesCount(final boolean detailedOnly, final CacheType cacheType, final Integer list) { return storage.getAllStoredCachesCount(detailedOnly, cacheType, list); } /** {@link cgData#getAllHistoricCachesCount()} */ public int getAllHistoricCachesCount() { return storage.getAllHistoricCachesCount(); } /** {@link cgData#moveToList(List, int)} */ public void markStored(List<cgCache> caches, int listId) { storage.moveToList(caches, listId); } /** {@link cgData#moveToList(List, int)} */ public void markDropped(List<cgCache> caches) { storage.moveToList(caches, StoredList.TEMPORARY_LIST_ID); } /** {@link cgData#clearSearchedDestinations()} */ public boolean clearSearchedDestinations() { return storage.clearSearchedDestinations(); } /** {@link cgData#saveSearchedDestination(Destination)} */ public void saveSearchedDestination(Destination destination) { storage.saveSearchedDestination(destination); } /** {@link cgData#saveWaypoints(cgCache)} */ public boolean saveWaypoints(final cgCache cache) { return storage.saveWaypoints(cache); } public boolean saveWaypoint(int id, String geocode, cgWaypoint waypoint) { if (storage.saveWaypoint(id, geocode, waypoint)) { this.removeCache(geocode, EnumSet.of(RemoveFlag.REMOVE_CACHE)); return true; } return false; } /** {@link cgData#deleteWaypoint(int)} */ public boolean deleteWaypoint(int id) { return storage.deleteWaypoint(id); } public boolean saveTrackable(cgTrackable trackable) { return storage.saveTrackable(trackable); } /** {@link cgData#loadLogCounts(String)} */ public Map<LogType, Integer> loadLogCounts(String geocode) { return storage.loadLogCounts(geocode); } /** {@link cgData#loadWaypoint(int)} */ public cgWaypoint loadWaypoint(int id) { return storage.loadWaypoint(id); } /** * set the current action to be reported to Go4Cache (if enabled in settings)<br> * this might be either * <ul> * <li>geocode</li> * <li>name of a cache</li> * <li>action like twittering</li> * </ul> * * @param action */ public void setAction(String action) { this.action = action; } public String getAction() { return StringUtils.defaultString(action); } /** {@link cgData#saveLogOffline(String, Date, LogType, String)} */ public boolean saveLogOffline(String geocode, Date date, LogType logtype, String log) { return storage.saveLogOffline(geocode, date, logtype, log); } /** {@link cgData#loadLogOffline(String)} */ public LogEntry loadLogOffline(String geocode) { return storage.loadLogOffline(geocode); } /** {@link cgData#clearLogOffline(String)} */ public void clearLogOffline(String geocode) { storage.clearLogOffline(geocode); } /** {@link cgData#setVisitDate(List, long)} */ public void saveVisitDate(String geocode) { storage.setVisitDate(Collections.singletonList(geocode), System.currentTimeMillis()); } /** {@link cgData#setVisitDate(List, long)} */ public void clearVisitDate(List<cgCache> caches) { ArrayList<String> geocodes = new ArrayList<String>(caches.size()); for (cgCache cache : caches) { geocodes.add(cache.getGeocode()); } storage.setVisitDate(geocodes, 0); } /** {@link cgData#getLists(Resources)} */ public List<StoredList> getLists() { return storage.getLists(getResources()); } /** {@link cgData#getList(int, Resources)} */ public StoredList getList(int id) { return storage.getList(id, getResources()); } /** {@link cgData#createList(String)} */ public int createList(String title) { return storage.createList(title); } /** {@link cgData#renameList(int, String)} */ public int renameList(final int listId, final String title) { return storage.renameList(listId, title); } /** {@link cgData#removeList(int)} */ public boolean removeList(int id) { return storage.removeList(id); } /** {@link cgData#removeSearchedDestination(Destination)} */ public boolean removeSearchedDestinations(Destination destination) { return storage.removeSearchedDestination(destination); } /** {@link cgData#moveToList(List, int)} */ public void moveToList(List<cgCache> caches, int listId) { storage.moveToList(caches, listId); } /** {@link cgData#getCacheDescription(String)} */ public String getCacheDescription(String geocode) { return storage.getCacheDescription(geocode); } /** {@link cgData#loadCaches} */ public cgCache loadCache(final String geocode, final EnumSet<LoadFlag> loadFlags) { return storage.loadCache(geocode, loadFlags); } /** {@link cgData#loadCaches} */ public Set<cgCache> loadCaches(final Set<String> geocodes, final EnumSet<LoadFlag> loadFlags) { return storage.loadCaches(geocodes, loadFlags); } /** * Update a cache in the DB or in the CacheCace depending on it's storage location * * {@link cgData#saveCache} */ public boolean updateCache(cgCache cache) { return saveCache(cache, cache.getStorageLocation().contains(StorageLocation.DATABASE) ? LoadFlags.SAVE_ALL : EnumSet.of(SaveFlag.SAVE_CACHE)); } /** {@link cgData#saveCache} */ public boolean saveCache(cgCache cache, EnumSet<LoadFlags.SaveFlag> saveFlags) { return storage.saveCache(cache, saveFlags); } /** {@link cgData#removeCache} */ public void removeCache(String geocode, EnumSet<LoadFlags.RemoveFlag> removeFlags) { storage.removeCache(geocode, removeFlags); } /** {@link cgData#removeCaches} */ public void removeCaches(final Set<String> geocodes, EnumSet<LoadFlags.RemoveFlag> removeFlags) { storage.removeCaches(geocodes, removeFlags); } public Set<cgWaypoint> getWaypointsInViewport(final Viewport viewport, boolean excludeMine, boolean excludeDisabled) { return storage.loadWaypoints(viewport, excludeMine, excludeDisabled); } public boolean isLiveMapHintShown() { return liveMapHintShown; } public void setLiveMapHintShown() { liveMapHintShown = true; } }