package cgeo.geocaching.maps.mapsforge.v6.caches; import cgeo.geocaching.SearchResult; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.gc.GCLogin; import cgeo.geocaching.connector.gc.MapTokens; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LoadFlags.RemoveFlag; import cgeo.geocaching.location.Viewport; import cgeo.geocaching.maps.mapsforge.v6.MapHandlers; import cgeo.geocaching.maps.mapsforge.v6.MfMapView; import cgeo.geocaching.models.Geocache; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.storage.DataStore; import cgeo.geocaching.utils.Log; import android.support.annotation.NonNull; import java.lang.ref.WeakReference; import java.util.EnumSet; import java.util.Locale; import java.util.Set; import java.util.concurrent.TimeUnit; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; import org.apache.commons.lang3.StringUtils; import org.mapsforge.map.layer.Layer; public class LiveCachesOverlay extends AbstractCachesOverlay { private final Disposable timer; private boolean downloading = false; public long loadThreadRun = -1; private MapTokens tokens; public LiveCachesOverlay(final int overlayId, final Set<GeoEntry> geoEntries, final MfMapView mapView, final Layer anchorLayer, final MapHandlers mapHandlers) { super(overlayId, geoEntries, mapView, anchorLayer, mapHandlers); this.timer = startTimer(); } private Disposable startTimer() { return Schedulers.newThread().schedulePeriodicallyDirect(new LoadTimerAction(this), 0, 250, TimeUnit.MILLISECONDS); } private static final class LoadTimerAction implements Runnable { @NonNull private final WeakReference<LiveCachesOverlay> overlayRef; private int previousZoom = -100; private Viewport previousViewport; LoadTimerAction(@NonNull final LiveCachesOverlay overlay) { this.overlayRef = new WeakReference<>(overlay); } @Override public void run() { final LiveCachesOverlay overlay = overlayRef.get(); if (overlay == null || overlay.isDownloading()) { return; } try { // get current viewport final Viewport viewportNow = overlay.getViewport(); // Since zoomNow is used only for local comparison purposes, // it is ok to use the Google Maps compatible zoom level of OSM Maps final int zoomNow = overlay.getMapZoomLevel(); // check if map moved or zoomed //TODO Portree Use Rectangle inside with bigger search window. That will stop reloading on every move final boolean moved = overlay.isInvalidated() || previousViewport == null || zoomNow != previousZoom || mapMoved(previousViewport, viewportNow) || !previousViewport.includes(viewportNow); // save new values if (moved) { final long currentTime = System.currentTimeMillis(); if (1000 < (currentTime - overlay.loadThreadRun)) { overlay.downloading = true; previousZoom = zoomNow; previousViewport = viewportNow; overlay.download(); } } } catch (final Exception e) { Log.w("LiveCachesOverlay.startLoadtimer.start", e); } finally { overlay.refreshed(); overlay.downloading = false; } } } private void download() { try { showProgress(); if (Settings.isGCConnectorActive() && tokens == null) { tokens = GCLogin.getInstance().getMapTokens(); if (StringUtils.isEmpty(tokens.getUserSession()) || StringUtils.isEmpty(tokens.getSessionToken())) { tokens = null; //TODO: show missing map token toast // if (!noMapTokenShowed) { // ActivityMixin.showToast(activity, res.getString(R.string.map_token_err)); // noMapTokenShowed = true; // } } } final SearchResult searchResult = ConnectorFactory.searchByViewport(getViewport().resize(1.2), tokens); final Set<Geocache> result = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB); AbstractCachesOverlay.filter(result); // update the caches // first remove filtered out final Set<String> filteredCodes = searchResult.getFilteredGeocodes(); Log.d("Filtering out " + filteredCodes.size() + " caches: " + filteredCodes.toString()); DataStore.removeCaches(filteredCodes, EnumSet.of(RemoveFlag.CACHE)); Log.d(String.format(Locale.ENGLISH, "Live caches found: %d", result.size())); //render fill(result); } finally { hideProgress(); } } @Override public void onDestroy() { timer.dispose(); super.onDestroy(); } public boolean isDownloading() { return downloading; } }