package carnero.cgeo.mapcommon; import gnu.android.app.appmanualclient.*; import android.app.Activity; import android.app.ProgressDialog; import java.util.ArrayList; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Message; import carnero.cgeo.R; import carnero.cgeo.cgBase; import carnero.cgeo.cgCache; import carnero.cgeo.cgCoord; import carnero.cgeo.cgDirection; import carnero.cgeo.cgGeo; import carnero.cgeo.cgSettings; import carnero.cgeo.cgUpdateDir; import carnero.cgeo.cgUpdateLoc; import carnero.cgeo.cgUser; import carnero.cgeo.cgWarning; import carnero.cgeo.cgWaypoint; import carnero.cgeo.cgeoapplication; import carnero.cgeo.mapinterfaces.ActivityImpl; import carnero.cgeo.mapinterfaces.CacheOverlayItemImpl; import carnero.cgeo.mapinterfaces.GeoPointImpl; import carnero.cgeo.mapinterfaces.MapControllerImpl; import carnero.cgeo.mapinterfaces.MapFactory; import carnero.cgeo.mapinterfaces.MapViewImpl; import carnero.cgeo.mapinterfaces.UserOverlayItemImpl; import android.util.Log; import android.view.View; import android.view.WindowManager; import android.widget.ImageView; import carnero.cgeo.cgSearch; import java.util.HashMap; import java.util.Locale; public class cgeomap extends MapBase { private Resources res = null; private Activity activity = null; private MapViewImpl mapView = null; private MapControllerImpl mapController = null; private cgSettings settings = null; private cgBase base = null; private cgWarning warning = null; private cgeoapplication app = null; private SharedPreferences.Editor prefsEdit = null; private cgGeo geo = null; private cgDirection dir = null; private cgUpdateLoc geoUpdate = new UpdateLoc(); private cgUpdateDir dirUpdate = new UpdateDir(); // from intent private boolean fromDetailIntent = false; private Long searchIdIntent = null; private String geocodeIntent = null; private Double latitudeIntent = null; private Double longitudeIntent = null; private String waypointTypeIntent = null; // status data private Long searchId = null; private String token = null; private boolean noMapTokenShowed = false; // map status data private boolean followMyLocation = false; private Integer centerLatitude = null; private Integer centerLongitude = null; private Integer spanLatitude = null; private Integer spanLongitude = null; private Integer centerLatitudeUsers = null; private Integer centerLongitudeUsers = null; private Integer spanLatitudeUsers = null; private Integer spanLongitudeUsers = null; // thread private LoadTimer loadTimer = null; private UsersTimer usersTimer = null; private LoadThread loadThread = null; private DownloadThread downloadThread = null; private DisplayThread displayThread = null; private UsersThread usersThread = null; private DisplayUsersThread displayUsersThread = null; private LoadDetails loadDetailsThread = null; private volatile long loadThreadRun = 0l; private volatile long downloadThreadRun = 0l; private volatile long usersThreadRun = 0l; private volatile boolean downloaded = false; // overlays private cgMapOverlay overlayCaches = null; private cgUsersOverlay overlayUsers = null; private cgOverlayScale overlayScale = null; private cgMapMyOverlay overlayMyLoc = null; // data for overlays private int cachesCnt = 0; private HashMap<Integer, Drawable> iconsCache = new HashMap<Integer, Drawable>(); private ArrayList<cgCache> caches = new ArrayList<cgCache>(); private ArrayList<cgUser> users = new ArrayList<cgUser>(); private ArrayList<cgCoord> coordinates = new ArrayList<cgCoord>(); // storing for offline private ProgressDialog waitDialog = null; private int detailTotal = 0; private int detailProgress = 0; private Long detailProgressTime = 0l; // views private ImageView myLocSwitch = null; // other things private boolean live = true; // live map (live, dead) or rest (displaying caches on map) private boolean liveChanged = false; // previous state for loadTimer private boolean centered = false; // if map is already centered private boolean alreadyCentered = false; // -""- for setting my location // handlers final private Handler displayHandler = new Handler() { @Override public void handleMessage(Message msg) { final int what = msg.what; if (what == 0) { // set title final StringBuilder title = new StringBuilder(); if (live == true) { title.append(res.getString(R.string.map_live)); } else { title.append(res.getString(R.string.map_map)); } if (caches != null && cachesCnt > 0) { title.append(" "); title.append("["); title.append(caches.size()); title.append("]"); } base.setTitle(activity, title.toString()); } else if (what == 1 && mapView != null) { mapView.invalidate(); } } }; final private Handler showProgressHandler = new Handler() { @Override public void handleMessage(Message msg) { final int what = msg.what; if (what == 0) { base.showProgress(activity, false); } else if (what == 1) { base.showProgress(activity, true); } } }; final private Handler loadDetailsHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 0) { if (waitDialog != null) { Float diffTime = new Float((System.currentTimeMillis() - detailProgressTime) / 1000); // seconds left Float oneCache = diffTime / detailProgress; // left time per cache Float etaTime = (detailTotal - detailProgress) * oneCache; // seconds remaining waitDialog.setProgress(detailProgress); if (etaTime < 40) { waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + res.getString(R.string.caches_eta_ltm)); } else if (etaTime < 90) { waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + String.format(Locale.getDefault(), "%.0f", (etaTime / 60)) + " " + res.getString(R.string.caches_eta_min)); } else { waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + String.format(Locale.getDefault(), "%.0f", (etaTime / 60)) + " " + res.getString(R.string.caches_eta_mins)); } } } else { if (waitDialog != null) { waitDialog.dismiss(); waitDialog.setOnCancelListener(null); } if (geo == null) { geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); } if (settings.useCompass == 1 && dir == null) { dir = app.startDir(activity, dirUpdate, warning); } } } }; final private Handler noMapTokenHandler = new Handler() { @Override public void handleMessage(Message msg) { if (!noMapTokenShowed) { warning.showToast(res.getString(R.string.map_token_err)); noMapTokenShowed = true; } } }; public cgeomap(ActivityImpl activity) { super(activity); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // class init res = this.getResources(); activity = this.getActivity(); app = (cgeoapplication) activity.getApplication(); app.setAction(null); settings = new cgSettings(activity, activity.getSharedPreferences(cgSettings.preferences, 0)); base = new cgBase(app, settings, activity.getSharedPreferences(cgSettings.preferences, 0)); warning = new cgWarning(activity); prefsEdit = activity.getSharedPreferences(cgSettings.preferences, 0).edit(); MapFactory mapFactory = settings.getMapFactory(); // reset status noMapTokenShowed = false; // set layout activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // set layout if (settings.skin == 1) { activity.setTheme(R.style.light); } else { activity.setTheme(R.style.dark); } activity.setContentView(settings.getMapFactory().getMapLayoutId()); base.setTitle(activity, res.getString(R.string.map_map)); if (geo == null) { geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); } if (settings.useCompass == 1 && dir == null) { dir = app.startDir(activity, dirUpdate, warning); } mapView = (MapViewImpl) activity.findViewById(mapFactory.getMapViewId()); mapView.setMapSource(settings); if (!mapView.needsScaleOverlay()) { mapView.setBuiltinScale(true); } // initialize map if (settings.maptype == cgSettings.mapSatellite) { mapView.setSatellite(true); } else { mapView.setSatellite(false); } mapView.setBuiltInZoomControls(true); mapView.displayZoomControls(true); mapView.preLoad(); // initialize overlays mapView.clearOverlays(); if (overlayMyLoc == null) { overlayMyLoc = new cgMapMyOverlay(settings); mapView.addOverlay(mapFactory.getOverlayBaseWrapper(overlayMyLoc)); } if (settings.publicLoc > 0 && overlayUsers == null) { overlayUsers = mapView.createAddUsersOverlay(activity, getResources().getDrawable(R.drawable.user_location)); } if (overlayCaches == null) { overlayCaches = mapView.createAddMapOverlay(settings, mapView.getContext(), getResources().getDrawable(R.drawable.marker), fromDetailIntent); } if (overlayScale == null && mapView.needsScaleOverlay()) { overlayScale = new cgOverlayScale(activity, settings); mapView.addOverlay(mapFactory.getOverlayBaseWrapper(overlayScale)); } mapView.invalidate(); mapController = mapView.getMapController(); mapController.setZoom(settings.mapzoom); // start location and directory services if (geo != null) { geoUpdate.updateLoc(geo); } if (dir != null) { dirUpdate.updateDir(dir); } // get parameters Bundle extras = activity.getIntent().getExtras(); if (extras != null) { fromDetailIntent = extras.getBoolean("detail"); searchIdIntent = extras.getLong("searchid"); geocodeIntent = extras.getString("geocode"); latitudeIntent = extras.getDouble("latitude"); longitudeIntent = extras.getDouble("longitude"); waypointTypeIntent = extras.getString("wpttype"); if (searchIdIntent == 0l) { searchIdIntent = null; } if (latitudeIntent == 0.0) { latitudeIntent = null; } if (longitudeIntent == 0.0) { longitudeIntent = null; } } // live or death if (searchIdIntent == null && geocodeIntent == null && (latitudeIntent == null || longitudeIntent == null)) { live = true; } else { live = false; } // google analytics if (live) { base.sendAnal(activity, "/map/live"); followMyLocation = true; } else { base.sendAnal(activity, "/map/normal"); followMyLocation = false; if (geocodeIntent != null || searchIdIntent != null || (latitudeIntent != null && longitudeIntent != null)) { centerMap(geocodeIntent, searchIdIntent, latitudeIntent, longitudeIntent); } } setMyLoc(null); startTimer(); } @Override public void onResume() { super.onResume(); settings.load(); app.setAction(null); if (geo == null) { geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); } if (settings.useCompass == 1 && dir == null) { dir = app.startDir(activity, dirUpdate, warning); } if (geo != null) { geoUpdate.updateLoc(geo); } if (dir != null) { dirUpdate.updateDir(dir); } startTimer(); } @Override public void onStop() { if (loadTimer != null) { loadTimer.stopIt(); loadTimer = null; } if (usersTimer != null) { usersTimer.stopIt(); usersTimer = null; } if (dir != null) { dir = app.removeDir(); } if (geo != null) { geo = app.removeGeo(); } savePrefs(); if (mapView != null) { mapView.destroyDrawingCache(); } super.onStop(); } @Override public void onPause() { if (loadTimer != null) { loadTimer.stopIt(); loadTimer = null; } if (usersTimer != null) { usersTimer.stopIt(); usersTimer = null; } if (dir != null) { dir = app.removeDir(); } if (geo != null) { geo = app.removeGeo(); } savePrefs(); if (mapView != null) { mapView.destroyDrawingCache(); } super.onPause(); } @Override public void onDestroy() { if (loadTimer != null) { loadTimer.stopIt(); loadTimer = null; } if (usersTimer != null) { usersTimer.stopIt(); usersTimer = null; } if (dir != null) { dir = app.removeDir(); } if (geo != null) { geo = app.removeGeo(); } savePrefs(); if (mapView != null) { mapView.destroyDrawingCache(); } super.onDestroy(); } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, 1, 0, res.getString(R.string.caches_on_map)).setIcon(android.R.drawable.ic_menu_mapmode); menu.add(0, 3, 0, res.getString(R.string.map_live_disable)).setIcon(android.R.drawable.ic_menu_close_clear_cancel); menu.add(0, 4, 0, res.getString(R.string.caches_store_offline)).setIcon(android.R.drawable.ic_menu_set_as).setEnabled(false); menu.add(0, 2, 0, res.getString(R.string.map_trail_hide)).setIcon(android.R.drawable.ic_menu_recent_history); menu.add(0, 5, 0, res.getString(R.string.map_circles_hide)).setIcon(android.R.drawable.ic_menu_view); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); MenuItem item; try { item = menu.findItem(1); // view if (mapView != null && mapView.isSatellite() == false) { item.setTitle(res.getString(R.string.map_view_satellite)); } else { item.setTitle(res.getString(R.string.map_view_map)); } item = menu.findItem(2); // show trail if (settings.maptrail == 1) { item.setTitle(res.getString(R.string.map_trail_hide)); } else { item.setTitle(res.getString(R.string.map_trail_show)); } item = menu.findItem(3); // live map if (live == false) { item.setEnabled(false); item.setTitle(res.getString(R.string.map_live_enable)); } else { if (settings.maplive == 1) { item.setTitle(res.getString(R.string.map_live_disable)); } else { item.setTitle(res.getString(R.string.map_live_enable)); } } item = menu.findItem(4); // store loaded if (live && !isLoading() && app.getNotOfflineCount(searchId) > 0 && caches != null && caches.size() > 0) { item.setEnabled(true); } else { item.setEnabled(false); } item = menu.findItem(5); // show circles if (overlayCaches != null && overlayCaches.getCircles()) { item.setTitle(res.getString(R.string.map_circles_hide)); } else { item.setTitle(res.getString(R.string.map_circles_show)); } } catch (Exception e) { Log.e(cgSettings.tag, "cgeomap.onPrepareOptionsMenu: " + e.toString()); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { final int id = item.getItemId(); if (id == 1) { if (mapView != null && mapView.isSatellite() == false) { mapView.setSatellite(true); prefsEdit.putInt("maptype", cgSettings.mapSatellite); prefsEdit.commit(); } else { mapView.setSatellite(false); prefsEdit.putInt("maptype", cgSettings.mapClassic); prefsEdit.commit(); } return true; } else if (id == 2) { if (settings.maptrail == 1) { prefsEdit.putInt("maptrail", 0); prefsEdit.commit(); settings.maptrail = 0; } else { prefsEdit.putInt("maptrail", 1); prefsEdit.commit(); settings.maptrail = 1; } } else if (id == 3) { if (settings.maplive == 1) { settings.liveMapDisable(); } else { settings.liveMapEnable(); } liveChanged = true; searchId = null; searchIdIntent = null; } else if (id == 4) { if (live && !isLoading() && caches != null && !caches.isEmpty()) { final ArrayList<String> geocodes = new ArrayList<String>(); ArrayList<cgCache> cachesProtected = (ArrayList<cgCache>) caches.clone(); try { if (cachesProtected != null && cachesProtected.size() > 0) { final GeoPointImpl mapCenter = mapView.getMapViewCenter(); final int mapCenterLat = mapCenter.getLatitudeE6(); final int mapCenterLon = mapCenter.getLongitudeE6(); final int mapSpanLat = mapView.getLatitudeSpan(); final int mapSpanLon = mapView.getLongitudeSpan(); for (cgCache oneCache : cachesProtected) { if (oneCache != null && oneCache.latitude != null && oneCache.longitude != null) { if (base.isCacheInViewPort(mapCenterLat, mapCenterLon, mapSpanLat, mapSpanLon, oneCache.latitude, oneCache.longitude) && app.isOffline(oneCache.geocode, null) == false) { geocodes.add(oneCache.geocode); } } } } } catch (Exception e) { Log.e(cgSettings.tag, "cgeomap.onOptionsItemSelected.#4: " + e.toString()); } detailTotal = geocodes.size(); if (detailTotal == 0) { warning.showToast(res.getString(R.string.warn_save_nothing)); return true; } waitDialog = new ProgressDialog(activity); waitDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); waitDialog.setCancelable(true); waitDialog.setMax(detailTotal); waitDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { public void onCancel(DialogInterface arg0) { try { if (loadDetailsThread != null) { loadDetailsThread.stopIt(); } if (geo == null) { geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); } if (settings.useCompass == 1 && dir == null) { dir = app.startDir(activity, dirUpdate, warning); } } catch (Exception e) { Log.e(cgSettings.tag, "cgeocaches.onPrepareOptionsMenu.onCancel: " + e.toString()); } } }); Float etaTime = new Float((detailTotal * (float) 7) / 60); if (etaTime < 0.4) { waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + res.getString(R.string.caches_eta_ltm)); } else if (etaTime < 1.5) { waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + String.format(Locale.getDefault(), "%.0f", etaTime) + " " + res.getString(R.string.caches_eta_min)); } else { waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + String.format(Locale.getDefault(), "%.0f", etaTime) + " " + res.getString(R.string.caches_eta_mins)); } waitDialog.show(); detailProgressTime = System.currentTimeMillis(); loadDetailsThread = new LoadDetails(loadDetailsHandler, geocodes); loadDetailsThread.start(); return true; } } else if (id == 5) { if (overlayCaches == null) { return false; } overlayCaches.switchCircles(); } return false; } private void savePrefs() { if (mapView == null) { return; } if (mapView.isSatellite()) { prefsEdit.putInt("maptype", cgSettings.mapSatellite); settings.maptype = cgSettings.mapSatellite; } else { prefsEdit.putInt("maptype", cgSettings.mapClassic); settings.maptype = cgSettings.mapClassic; } if (prefsEdit == null) { prefsEdit = activity.getSharedPreferences(cgSettings.preferences, 0).edit(); } prefsEdit.putInt("mapzoom", mapView.getMapZoomLevel()); prefsEdit.commit(); } // set center of map to my location private void myLocationInMiddle() { if (geo == null) { return; } if (!followMyLocation) { return; } centerMap(geo.latitudeNow, geo.longitudeNow); } // class: update location private class UpdateLoc extends cgUpdateLoc { @Override public void updateLoc(cgGeo geo) { if (geo == null) { return; } try { if (overlayMyLoc == null && mapView != null) { overlayMyLoc = new cgMapMyOverlay(settings); mapView.addOverlay(settings.getMapFactory().getOverlayBaseWrapper(overlayMyLoc)); } if (overlayMyLoc != null && geo.location != null) { overlayMyLoc.setCoordinates(geo.location); } if (geo.latitudeNow != null && geo.longitudeNow != null) { if (followMyLocation == true) { myLocationInMiddle(); } } if (settings.useCompass == 0 || (geo.speedNow != null && geo.speedNow > 5)) { // use GPS when speed is higher than 18 km/h if (geo.bearingNow != null) { overlayMyLoc.setHeading(geo.bearingNow); } else { overlayMyLoc.setHeading(new Double(0)); } } } catch (Exception e) { Log.w(cgSettings.tag, "Failed to update location."); } } } // class: update direction private class UpdateDir extends cgUpdateDir { @Override public void updateDir(cgDirection dir) { if (dir == null || dir.directionNow == null) { return; } if (overlayMyLoc != null && mapView != null && (geo == null || geo.speedNow == null || geo.speedNow <= 5)) { // use compass when speed is lower than 18 km/h overlayMyLoc.setHeading(dir.directionNow); mapView.invalidate(); } } } public void startTimer() { if (latitudeIntent != null && longitudeIntent != null) { // display just one point (new DisplayPointThread()).start(); } else { // start timer if (loadTimer != null) { loadTimer.stopIt(); loadTimer = null; } loadTimer = new LoadTimer(); loadTimer.start(); } if (settings.publicLoc > 0) { if (usersTimer != null) { usersTimer.stopIt(); usersTimer = null; } usersTimer = new UsersTimer(); usersTimer.start(); } } // loading timer private class LoadTimer extends Thread { private volatile boolean stop = false; public void stopIt() { stop = true; if (loadThread != null) { loadThread.stopIt(); loadThread = null; } if (downloadThread != null) { downloadThread.stopIt(); downloadThread = null; } if (displayThread != null) { displayThread.stopIt(); displayThread = null; } } @Override public void run() { GeoPointImpl mapCenterNow; int centerLatitudeNow; int centerLongitudeNow; int spanLatitudeNow; int spanLongitudeNow; boolean moved = false; boolean force = false; long currentTime = 0; while (!stop) { try { sleep(250); if (mapView != null) { // get current viewport mapCenterNow = mapView.getMapViewCenter(); centerLatitudeNow = mapCenterNow.getLatitudeE6(); centerLongitudeNow = mapCenterNow.getLongitudeE6(); spanLatitudeNow = mapView.getLatitudeSpan(); spanLongitudeNow = mapView.getLongitudeSpan(); // check if map moved or zoomed moved = false; force = false; if (liveChanged) { moved = true; force = true; } else if (live && settings.maplive == 1 && downloaded == false) { moved = true; } else if (centerLatitude == null || centerLongitude == null) { moved = true; } else if (spanLatitude == null || spanLongitude == null) { moved = true; } else if (((Math.abs(spanLatitudeNow - spanLatitude) > 50) || (Math.abs(spanLongitudeNow - spanLongitude) > 50) || // changed zoom (Math.abs(centerLatitudeNow - centerLatitude) > (spanLatitudeNow / 4)) || (Math.abs(centerLongitudeNow - centerLongitude) > (spanLongitudeNow / 4)) // map moved ) && (cachesCnt <= 0 || caches == null || caches.isEmpty() || !base.isInViewPort(centerLatitude, centerLongitude, centerLatitudeNow, centerLongitudeNow, spanLatitude, spanLongitude, spanLatitudeNow, spanLongitudeNow))) { moved = true; } if (moved && caches != null && centerLatitude != null && centerLongitude != null && ((Math.abs(centerLatitudeNow - centerLatitude) > (spanLatitudeNow * 1.2)) || (Math.abs(centerLongitudeNow - centerLongitude) > (spanLongitudeNow * 1.2)))) { force = true; } //LeeB // save new values if (moved) { liveChanged = false; currentTime = System.currentTimeMillis(); if (1000 < (currentTime - loadThreadRun)) { // from web if (20000 < (currentTime - loadThreadRun)) { force = true; // probably stucked thread } if (force && loadThread != null && loadThread.isWorking()) { loadThread.stopIt(); try { sleep(100); } catch (Exception e) { // nothing } } if (loadThread != null && loadThread.isWorking()) { continue; } centerLatitude = centerLatitudeNow; centerLongitude = centerLongitudeNow; spanLatitude = spanLatitudeNow; spanLongitude = spanLongitudeNow; showProgressHandler.sendEmptyMessage(1); // show progress loadThread = new LoadThread(centerLatitude, centerLongitude, spanLatitude, spanLongitude); loadThread.setName("loadThread"); loadThread.start(); //loadThread will kick off downloadThread once it's done } } } if (!isLoading()) { showProgressHandler.sendEmptyMessage(0); // hide progress } yield(); } catch (Exception e) { Log.w(cgSettings.tag, "cgeomap.LoadTimer.run: " + e.toString()); } }; } } // loading timer private class UsersTimer extends Thread { private volatile boolean stop = false; public void stopIt() { stop = true; if (usersThread != null) { usersThread.stopIt(); usersThread = null; } if (displayUsersThread != null) { displayUsersThread.stopIt(); displayUsersThread = null; } } @Override public void run() { GeoPointImpl mapCenterNow; int centerLatitudeNow; int centerLongitudeNow; int spanLatitudeNow; int spanLongitudeNow; boolean moved = false; long currentTime = 0; while (!stop) { try { sleep(250); if (mapView != null) { // get current viewport mapCenterNow = mapView.getMapViewCenter(); centerLatitudeNow = mapCenterNow.getLatitudeE6(); centerLongitudeNow = mapCenterNow.getLongitudeE6(); spanLatitudeNow = mapView.getLatitudeSpan(); spanLongitudeNow = mapView.getLongitudeSpan(); // check if map moved or zoomed moved = false; currentTime = System.currentTimeMillis(); if (60000 < (currentTime - usersThreadRun)) { moved = true; } else if (centerLatitudeUsers == null || centerLongitudeUsers == null) { moved = true; } else if (spanLatitudeUsers == null || spanLongitudeUsers == null) { moved = true; } else if (((Math.abs(spanLatitudeNow - spanLatitudeUsers) > 50) || (Math.abs(spanLongitudeNow - spanLongitudeUsers) > 50) || // changed zoom (Math.abs(centerLatitudeNow - centerLatitudeUsers) > (spanLatitudeNow / 4)) || (Math.abs(centerLongitudeNow - centerLongitudeUsers) > (spanLongitudeNow / 4)) // map moved ) && !base.isInViewPort(centerLatitudeUsers, centerLongitudeUsers, centerLatitudeNow, centerLongitudeNow, spanLatitudeUsers, spanLongitudeUsers, spanLatitudeNow, spanLongitudeNow)) { moved = true; } // save new values if (moved && (1000 < (currentTime - usersThreadRun))) { if (usersThread != null && usersThread.isWorking()) { continue; } centerLatitudeUsers = centerLatitudeNow; centerLongitudeUsers = centerLongitudeNow; spanLatitudeUsers = spanLatitudeNow; spanLongitudeUsers = spanLongitudeNow; usersThread = new UsersThread(centerLatitude, centerLongitude, spanLatitude, spanLongitude); usersThread.start(); } } yield(); } catch (Exception e) { Log.w(cgSettings.tag, "cgeomap.LoadUsersTimer.run: " + e.toString()); } }; } } // load caches from database private class LoadThread extends DoThread { public LoadThread(long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { super(centerLatIn, centerLonIn, spanLatIn, spanLonIn); } @Override public void run() { try { stop = false; working = true; loadThreadRun = System.currentTimeMillis(); if (stop) { displayHandler.sendEmptyMessage(0); working = false; return; } //LeeB - I think this can be done better: //1. fetch and draw(in another thread) caches from the db (fast? db read will be the slow bit) //2. fetch and draw(in another thread) and then insert into the db caches from geocaching.com - dont draw/insert if exist in memory? // stage 1 - pull and render from the DB only if (settings.maplive == 0) { searchId = app.getStoredInViewport(centerLat, centerLon, spanLat, spanLon, settings.cacheType); } else { searchId = app.getCachedInViewport(centerLat, centerLon, spanLat, spanLon, settings.cacheType); } if (searchId != null) { downloaded = true; } if (stop) { displayHandler.sendEmptyMessage(0); working = false; return; } caches = app.getCaches(searchId); if (stop) { displayHandler.sendEmptyMessage(0); working = false; return; } //render if (displayThread != null && displayThread.isWorking()) { displayThread.stopIt(); } displayThread = new DisplayThread(centerLat, centerLon, spanLat, spanLon); displayThread.start(); if (stop) { displayThread.stopIt(); displayHandler.sendEmptyMessage(0); working = false; return; } //*** this needs to be in it's own thread // stage 2 - pull and render from geocaching.com //this should just fetch and insert into the db _and_ be cancel-able if the viewport changes if (settings.maplive >= 1) { if (downloadThread != null && downloadThread.isWorking()) { downloadThread.stopIt(); } downloadThread = new DownloadThread(centerLat, centerLon, spanLat, spanLon); downloadThread.setName("downloadThread"); downloadThread.start(); } } finally { working = false; } } } // load caches from internet private class DownloadThread extends DoThread { public DownloadThread(long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { super(centerLatIn, centerLonIn, spanLatIn, spanLonIn); } @Override public void run() { //first time we enter we have crappy long/lat.... try { stop = false; working = true; downloadThreadRun = System.currentTimeMillis(); if (stop) { displayHandler.sendEmptyMessage(0); working = false; return; } double latMin = (centerLat / 1e6) - ((spanLat / 1e6) / 2) - ((spanLat / 1e6) / 4); double latMax = (centerLat / 1e6) + ((spanLat / 1e6) / 2) + ((spanLat / 1e6) / 4); double lonMin = (centerLon / 1e6) - ((spanLon / 1e6) / 2) - ((spanLon / 1e6) / 4); double lonMax = (centerLon / 1e6) + ((spanLon / 1e6) / 2) + ((spanLon / 1e6) / 4); double llCache; if (latMin > latMax) { llCache = latMax; latMax = latMin; latMin = llCache; } if (lonMin > lonMax) { llCache = lonMax; lonMax = lonMin; lonMin = llCache; } //*** this needs to be in it's own thread // stage 2 - pull and render from geocaching.com //this should just fetch and insert into the db _and_ be cancel-able if the viewport changes if (token == null) { token = base.getMapUserToken(noMapTokenHandler); } if (stop) { displayHandler.sendEmptyMessage(0); working = false; return; } HashMap<String, String> params = new HashMap<String, String>(); params.put("usertoken", token); params.put("latitude-min", String.format((Locale) null, "%.6f", latMin)); params.put("latitude-max", String.format((Locale) null, "%.6f", latMax)); params.put("longitude-min", String.format((Locale) null, "%.6f", lonMin)); params.put("longitude-max", String.format((Locale) null, "%.6f", lonMax)); searchId = base.searchByViewport(params, 0); if (searchId != null) { downloaded = true; } if (stop) { displayHandler.sendEmptyMessage(0); working = false; return; } caches = app.getCaches(searchId, centerLat, centerLon, spanLat, spanLon); if (stop) { displayHandler.sendEmptyMessage(0); working = false; return; } //render if (displayThread != null && displayThread.isWorking()) { displayThread.stopIt(); } displayThread = new DisplayThread(centerLat, centerLon, spanLat, spanLon); displayThread.start(); } finally { working = false; } } } // display (down)loaded caches private class DisplayThread extends DoThread { public DisplayThread(long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { super(centerLatIn, centerLonIn, spanLatIn, spanLonIn); } @Override public void run() { try { stop = false; working = true; if (mapView == null || caches == null) { displayHandler.sendEmptyMessage(0); working = false; return; } // display caches final ArrayList<cgCache> cachesProtected = (ArrayList<cgCache>) caches.clone(); final ArrayList<CacheOverlayItemImpl> items = new ArrayList<CacheOverlayItemImpl>(); if (cachesProtected != null && !cachesProtected.isEmpty()) { int counter = 0; int icon = 0; Drawable pin = null; CacheOverlayItemImpl item = null; for (cgCache cacheOne : cachesProtected) { if (stop) { displayHandler.sendEmptyMessage(0); working = false; return; } if (cacheOne.latitude == null && cacheOne.longitude == null) { continue; } final cgCoord coord = new cgCoord(cacheOne); coordinates.add(coord); item = settings.getMapFactory().getCacheOverlayItem(coord, cacheOne.type); icon = base.getIcon(true, cacheOne.type, cacheOne.own, cacheOne.found, cacheOne.disabled || cacheOne.archived); pin = null; if (iconsCache.containsKey(icon)) { pin = iconsCache.get(icon); } else { pin = getResources().getDrawable(icon); pin.setBounds(0, 0, pin.getIntrinsicWidth(), pin.getIntrinsicHeight()); iconsCache.put(icon, pin); } item.setMarker(pin); items.add(item); /* counter++; if ((counter % 10) == 0) { overlayCaches.updateItems(items); displayHandler.sendEmptyMessage(1); } */ } overlayCaches.updateItems(items); displayHandler.sendEmptyMessage(1); cachesCnt = cachesProtected.size(); if (stop) { displayHandler.sendEmptyMessage(0); working = false; return; } // display cache waypoints if (cachesCnt == 1 && (geocodeIntent != null || searchIdIntent != null) && !live) { if (cachesCnt == 1 && live == false) { cgCache oneCache = cachesProtected.get(0); if (oneCache != null && oneCache.waypoints != null && !oneCache.waypoints.isEmpty()) { for (cgWaypoint oneWaypoint : oneCache.waypoints) { if (oneWaypoint.latitude == null && oneWaypoint.longitude == null) { continue; } cgCoord coord = new cgCoord(oneWaypoint); coordinates.add(coord); item = settings.getMapFactory().getCacheOverlayItem(coord, null); icon = base.getIcon(false, oneWaypoint.type, false, false, false); if (iconsCache.containsKey(icon)) { pin = iconsCache.get(icon); } else { pin = getResources().getDrawable(icon); pin.setBounds(0, 0, pin.getIntrinsicWidth(), pin.getIntrinsicHeight()); iconsCache.put(icon, pin); } item.setMarker(pin); items.add(item); } overlayCaches.updateItems(items); displayHandler.sendEmptyMessage(1); } } } } else { overlayCaches.updateItems(items); displayHandler.sendEmptyMessage(1); } cachesProtected.clear(); displayHandler.sendEmptyMessage(0); } finally { working = false; } } } // load users from Go 4 Cache private class UsersThread extends DoThread { public UsersThread(long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { super(centerLatIn, centerLonIn, spanLatIn, spanLonIn); } @Override public void run() { try { stop = false; working = true; usersThreadRun = System.currentTimeMillis(); if (stop) { return; } double latMin = (centerLat / 1e6) - ((spanLat / 1e6) / 2) - ((spanLat / 1e6) / 4); double latMax = (centerLat / 1e6) + ((spanLat / 1e6) / 2) + ((spanLat / 1e6) / 4); double lonMin = (centerLon / 1e6) - ((spanLon / 1e6) / 2) - ((spanLon / 1e6) / 4); double lonMax = (centerLon / 1e6) + ((spanLon / 1e6) / 2) + ((spanLon / 1e6) / 4); double llCache; if (latMin > latMax) { llCache = latMax; latMax = latMin; latMin = llCache; } if (lonMin > lonMax) { llCache = lonMax; lonMax = lonMin; lonMin = llCache; } users = base.getGeocachersInViewport(settings.getUsername(), latMin, latMax, lonMin, lonMax); if (stop) { return; } if (displayUsersThread != null && displayUsersThread.isWorking()) { displayUsersThread.stopIt(); } displayUsersThread = new DisplayUsersThread(users, centerLat, centerLon, spanLat, spanLon); displayUsersThread.start(); } finally { working = false; } } } // display users of Go 4 Cache private class DisplayUsersThread extends DoThread { private ArrayList<cgUser> users = null; public DisplayUsersThread(ArrayList<cgUser> usersIn, long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { super(centerLatIn, centerLonIn, spanLatIn, spanLonIn); users = usersIn; } @Override public void run() { try { stop = false; working = true; if (mapView == null || users == null || users.isEmpty()) { return; } // display users ArrayList<UserOverlayItemImpl> items = new ArrayList<UserOverlayItemImpl>(); int counter = 0; UserOverlayItemImpl item = null; for (cgUser userOne : users) { if (stop) { return; } if (userOne.latitude == null && userOne.longitude == null) { continue; } item = settings.getMapFactory().getUserOverlayItemBase(activity, userOne); items.add(item); counter++; if ((counter % 10) == 0) { overlayUsers.updateItems(items); displayHandler.sendEmptyMessage(1); } } overlayUsers.updateItems(items); } finally { working = false; } } } // display one point private class DisplayPointThread extends Thread { @Override public void run() { if (mapView == null || caches == null) { return; } if (latitudeIntent != null && longitudeIntent != null) { cgCoord coord = new cgCoord(); coord.type = "waypoint"; coord.latitude = latitudeIntent; coord.longitude = longitudeIntent; coord.name = "some place"; coordinates.add(coord); CacheOverlayItemImpl item = settings.getMapFactory().getCacheOverlayItem(coord, null); final int icon = base.getIcon(false, waypointTypeIntent, false, false, false); Drawable pin = null; if (iconsCache.containsKey(icon)) { pin = iconsCache.get(icon); } else { pin = getResources().getDrawable(icon); pin.setBounds(0, 0, pin.getIntrinsicWidth(), pin.getIntrinsicHeight()); iconsCache.put(icon, pin); } item.setMarker(pin); overlayCaches.updateItems(item); displayHandler.sendEmptyMessage(1); cachesCnt = 1; } else { cachesCnt = 0; } displayHandler.sendEmptyMessage(0); } } // parent for those above :) private class DoThread extends Thread { protected boolean working = true; protected boolean stop = false; protected long centerLat = 0l; protected long centerLon = 0l; protected long spanLat = 0l; protected long spanLon = 0l; public DoThread(long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { centerLat = centerLatIn; centerLon = centerLonIn; spanLat = spanLatIn; spanLon = spanLonIn; } public synchronized boolean isWorking() { return working; } public synchronized void stopIt() { stop = true; } } // get if map is loading something private synchronized boolean isLoading() { boolean loading = false; if (loadThread != null && loadThread.isWorking()) { loading = true; } else if (downloadThread != null && downloadThread.isWorking()) { loading = true; } else if (displayThread != null && displayThread.isWorking()) { loading = true; } return loading; } // store caches private class LoadDetails extends Thread { private Handler handler = null; private ArrayList<String> geocodes = null; private volatile boolean stop = false; private long last = 0l; public LoadDetails(Handler handlerIn, ArrayList<String> geocodesIn) { handler = handlerIn; geocodes = geocodesIn; } public void stopIt() { stop = true; } @Override public void run() { if (geocodes == null || geocodes.isEmpty()) { return; } if (dir != null) { dir = app.removeDir(); } if (geo != null) { geo = app.removeGeo(); } for (String geocode : geocodes) { try { if (stop == true) { break; } if (!app.isOffline(geocode, null)) { if ((System.currentTimeMillis() - last) < 1500) { try { int delay = 1000 + ((Double) (Math.random() * 1000)).intValue() - (int) (System.currentTimeMillis() - last); if (delay < 0) { delay = 500; } sleep(delay); } catch (Exception e) { // nothing } } if (stop == true) { Log.i(cgSettings.tag, "Stopped storing process."); break; } base.storeCache(app, activity, null, geocode, 1, handler); } } catch (Exception e) { Log.e(cgSettings.tag, "cgeocaches.LoadDetails.run: " + e.toString()); } finally { // one more cache over detailProgress++; handler.sendEmptyMessage(0); } yield(); last = System.currentTimeMillis(); } // we're done handler.sendEmptyMessage(1); } } // center map to desired location private void centerMap(Double latitude, Double longitude) { if (latitude == null || longitude == null) { return; } if (mapView == null) { return; } if (!alreadyCentered) { alreadyCentered = true; mapController.setCenter(makeGeoPoint(latitude, longitude)); } else { mapController.animateTo(makeGeoPoint(latitude, longitude)); } } // move map to view results of searchIdIntent private void centerMap(String geocodeCenter, Long searchIdCenter, Double latitudeCenter, Double longitudeCenter) { if (!centered && (geocodeCenter != null || searchIdIntent != null)) { try { ArrayList<Object> viewport; if (geocodeCenter != null) { viewport = app.getBounds(geocodeCenter); } else { viewport = app.getBounds(searchIdCenter); } Integer cnt = (Integer) viewport.get(0); Integer minLat = null; Integer maxLat = null; Integer minLon = null; Integer maxLon = null; if (viewport.get(1) != null) { minLat = new Double((Double) viewport.get(1) * 1e6).intValue(); } if (viewport.get(2) != null) { maxLat = new Double((Double) viewport.get(2) * 1e6).intValue(); } if (viewport.get(3) != null) { maxLon = new Double((Double) viewport.get(3) * 1e6).intValue(); } if (viewport.get(4) != null) { minLon = new Double((Double) viewport.get(4) * 1e6).intValue(); } if (cnt == null || cnt <= 0 || minLat == null || maxLat == null || minLon == null || maxLon == null) { return; } int centerLat = 0; int centerLon = 0; if ((Math.abs(maxLat) - Math.abs(minLat)) != 0) { centerLat = minLat + ((maxLat - minLat) / 2); } else { centerLat = maxLat; } if ((Math.abs(maxLon) - Math.abs(minLon)) != 0) { centerLon = minLon + ((maxLon - minLon) / 2); } else { centerLon = maxLon; } if (cnt != null && cnt > 0) { mapController.setCenter(settings.getMapFactory().getGeoPointBase(centerLat, centerLon)); if (Math.abs(maxLat - minLat) != 0 && Math.abs(maxLon - minLon) != 0) { mapController.zoomToSpan(Math.abs(maxLat - minLat), Math.abs(maxLon - minLon)); } } } catch (Exception e) { // nothing at all } centered = true; alreadyCentered = true; } else if (!centered && latitudeCenter != null && longitudeCenter != null) { try { mapController.setCenter(makeGeoPoint(latitudeCenter, longitudeCenter)); } catch (Exception e) { // nothing at all } centered = true; alreadyCentered = true; } } // switch My Location button image private void setMyLoc(Boolean status) { if (myLocSwitch == null) { myLocSwitch = (ImageView) activity.findViewById(R.id.my_position); } if (status == null) { if (followMyLocation == true) { myLocSwitch.setImageResource(R.drawable.my_location_on); } else { myLocSwitch.setImageResource(R.drawable.my_location_off); } } else { if (status == true) { myLocSwitch.setImageResource(R.drawable.my_location_on); } else { myLocSwitch.setImageResource(R.drawable.my_location_off); } } myLocSwitch.setOnClickListener(new MyLocationListener()); } // set my location listener private class MyLocationListener implements View.OnClickListener { public void onClick(View view) { if (myLocSwitch == null) { myLocSwitch = (ImageView) activity.findViewById(R.id.my_position); } if (followMyLocation == true) { followMyLocation = false; myLocSwitch.setImageResource(R.drawable.my_location_off); } else { followMyLocation = true; myLocationInMiddle(); myLocSwitch.setImageResource(R.drawable.my_location_on); } } } // make geopoint private GeoPointImpl makeGeoPoint(Double latitude, Double longitude) { return settings.getMapFactory().getGeoPointBase((int) (latitude * 1e6), (int) (longitude * 1e6)); } // close activity and open homescreen public void goHome(View view) { base.goHome(activity); } // open manual entry public void goManual(View view) { try { AppManualReaderClient.openManual( "c-geo", "c:geo-live-map", activity, "http://cgeo.carnero.cc/manual/"); } catch (Exception e) { // nothing } } }