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.
*
* 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 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 geocodes = storage.loadBatchOfStoredGeocodes(detailedOnly, coords, cacheType, listId);
return new SearchResult(geocodes, getAllStoredCachesCount(true, cacheType, listId));
}
/** {@link cgData#loadHistoryOfSearchedLocations()} */
public List getHistoryOfSearchedLocations() {
return storage.loadHistoryOfSearchedLocations();
}
public SearchResult getHistoryOfCaches(final boolean detailedOnly, final CacheType cacheType) {
final Set 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 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 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 caches, int listId) {
storage.moveToList(caches, listId);
}
/** {@link cgData#moveToList(List, int)} */
public void markDropped(List 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 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)
* this might be either
*
* - geocode
* - name of a cache
* - action like twittering
*
*
* @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 caches) {
ArrayList geocodes = new ArrayList(caches.size());
for (cgCache cache : caches) {
geocodes.add(cache.getGeocode());
}
storage.setVisitDate(geocodes, 0);
}
/** {@link cgData#getLists(Resources)} */
public List 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 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 loadFlags) {
return storage.loadCache(geocode, loadFlags);
}
/** {@link cgData#loadCaches} */
public Set loadCaches(final Set geocodes, final EnumSet 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 saveFlags) {
return storage.saveCache(cache, saveFlags);
}
/** {@link cgData#removeCache} */
public void removeCache(String geocode, EnumSet removeFlags) {
storage.removeCache(geocode, removeFlags);
}
/** {@link cgData#removeCaches} */
public void removeCaches(final Set geocodes, EnumSet removeFlags) {
storage.removeCaches(geocodes, removeFlags);
}
public Set getWaypointsInViewport(final Viewport viewport, boolean excludeMine, boolean excludeDisabled) {
return storage.loadWaypoints(viewport, excludeMine, excludeDisabled);
}
public boolean isLiveMapHintShown() {
return liveMapHintShown;
}
public void setLiveMapHintShown() {
liveMapHintShown = true;
}
}