diff options
Diffstat (limited to 'main/src')
84 files changed, 1602 insertions, 1073 deletions
diff --git a/main/src/cgeo/calendar/ICalendar.java b/main/src/cgeo/calendar/ICalendar.java index 856de1a..933d248 100644 --- a/main/src/cgeo/calendar/ICalendar.java +++ b/main/src/cgeo/calendar/ICalendar.java @@ -15,4 +15,5 @@ public interface ICalendar { static final String PARAM_NAME = "name"; // cache name static final String PARAM_LOCATION = "location"; // cache location, or empty string static final String PARAM_COORDS = "coords"; // cache coords, or empty string + static final String PARAM_START_TIME_MINUTES = "time"; // time of start } diff --git a/main/src/cgeo/geocaching/AbstractPopupActivity.java b/main/src/cgeo/geocaching/AbstractPopupActivity.java index 588ba2a..a119949 100644 --- a/main/src/cgeo/geocaching/AbstractPopupActivity.java +++ b/main/src/cgeo/geocaching/AbstractPopupActivity.java @@ -4,12 +4,12 @@ import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.LoadFlags; -import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.gcvote.GCVote; import cgeo.geocaching.gcvote.GCVoteRating; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.HumanDistance; +import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.ui.CacheDetailsCreator; +import cgeo.geocaching.ui.LoggingUI; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; @@ -64,7 +64,7 @@ public abstract class AbstractPopupActivity extends AbstractActivity { protected void updateGeoData(final IGeoData geo) { try { if (geo.getCoords() != null && cache != null && cache.getCoords() != null) { - cacheDistance.setText(HumanDistance.getHumanDistance(geo.getCoords().distanceTo(cache.getCoords()))); + cacheDistance.setText(Units.getDistanceFromKilometers(geo.getCoords().distanceTo(cache.getCoords()))); cacheDistance.bringToFront(); } } catch (Exception e) { @@ -123,15 +123,6 @@ public abstract class AbstractPopupActivity extends AbstractActivity { geocode = cache.getGeocode().toUpperCase(); } - private void logOffline(int menuItem) { - cache.logOffline(this, LogType.getById(menuItem - MENU_LOG_VISIT_OFFLINE)); - } - - private void logVisit() { - cache.logVisit(this); - finish(); - } - private void showInBrowser() { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/seek/cache_details.aspx?wp=" + cache.getGeocode()))); } @@ -142,7 +133,6 @@ public abstract class AbstractPopupActivity extends AbstractActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // set layout - setTheme(R.style.transparent); setContentView(layout); setTitle(res.getString(R.string.detail)); @@ -174,7 +164,7 @@ public abstract class AbstractPopupActivity extends AbstractActivity { public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, MENU_DEFAULT_NAVIGATION, 0, NavigationAppFactory.getDefaultNavigationApplication().getName()).setIcon(R.drawable.ic_menu_compass); // default navigation tool menu.add(0, MENU_NAVIGATION, 0, res.getString(R.string.cache_menu_navigate)).setIcon(R.drawable.ic_menu_mapmode); - addVisitMenu(menu, cache); + LoggingUI.addMenuItems(menu, cache); menu.add(0, MENU_CACHES_AROUND, 0, res.getString(R.string.cache_menu_around)).setIcon(R.drawable.ic_menu_rotate); // caches around menu.add(0, MENU_SHOW_IN_BROWSER, 0, res.getString(R.string.cache_menu_browser)).setIcon(R.drawable.ic_menu_info_details); // browser @@ -188,21 +178,20 @@ public abstract class AbstractPopupActivity extends AbstractActivity { switch (menuItem) { case MENU_DEFAULT_NAVIGATION: navigateTo(); - break; + return true; case MENU_NAVIGATION: showNavigationMenu(); - break; + return true; case MENU_CACHES_AROUND: cachesAround(); - break; - case MENU_LOG_VISIT: - logVisit(); - break; + return true; case MENU_SHOW_IN_BROWSER: showInBrowser(); - break; - default: - logOffline(menuItem); + return true; + } + + if (LoggingUI.onMenuItemSelected(item, this, cache)) { + return true; } return true; @@ -224,7 +213,7 @@ public abstract class AbstractPopupActivity extends AbstractActivity { menu.findItem(MENU_NAVIGATION).setVisible(visible); menu.findItem(MENU_CACHES_AROUND).setVisible(visible); - menu.findItem(MENU_LOG_VISIT).setEnabled(Settings.isLogin()); + LoggingUI.onPrepareOptionsMenu(menu); } catch (Exception e) { // nothing } diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java index d9b4e96..93566c2 100644 --- a/main/src/cgeo/geocaching/CacheDetailActivity.java +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -12,15 +12,14 @@ import cgeo.geocaching.enumerations.CacheAttribute; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; import cgeo.geocaching.enumerations.LogType; -import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.geopoint.GeopointFormatter; -import cgeo.geocaching.geopoint.HumanDistance; -import cgeo.geocaching.geopoint.IConversion; +import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.ui.CacheDetailsCreator; import cgeo.geocaching.ui.DecryptTextClickListener; import cgeo.geocaching.ui.Formatter; +import cgeo.geocaching.ui.LoggingUI; import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.ClipboardUtils; @@ -46,7 +45,6 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -65,6 +63,7 @@ import android.text.Spannable; import android.text.Spanned; import android.text.format.DateUtils; import android.text.method.LinkMovementMethod; +import android.text.style.ForegroundColorSpan; import android.text.style.StrikethroughSpan; import android.text.style.StyleSpan; import android.view.ContextMenu; @@ -139,20 +138,13 @@ public class CacheDetailActivity extends AbstractActivity { final StringBuilder dist = new StringBuilder(); if (geo.getCoords() != null && cache != null && cache.getCoords() != null) { - dist.append(HumanDistance.getHumanDistance(geo.getCoords().distanceTo(cache.getCoords()))); + dist.append(Units.getDistanceFromKilometers(geo.getCoords().distanceTo(cache.getCoords()))); } if (cache != null && cache.getElevation() != null) { if (geo.getAltitude() != 0.0) { - final double diff = cache.getElevation() - geo.getAltitude(); - dist.append(diff >= 0 ? " ↗" : " ↘"); - if (Settings.isUseMetricUnits()) { - dist.append(Math.abs((int) diff)); - dist.append(" m"); - } else { - dist.append(Math.abs((int) (diff * IConversion.METERS_TO_FEET))); - dist.append(" ft"); - } + final float diff = (float) (cache.getElevation() - geo.getAltitude()); + dist.append(' ').append(Units.getElevation(diff)); } } @@ -565,7 +557,7 @@ public class CacheDetailActivity extends AbstractActivity { GeneralAppsFactory.addMenuItems(subMenu, cache); menu.add(1, MENU_CALENDAR, 0, res.getString(R.string.cache_menu_event)).setIcon(R.drawable.ic_menu_agenda); // add event to calendar - addVisitMenu(menu, cache); + LoggingUI.addMenuItems(menu, cache); menu.add(0, MENU_CACHES_AROUND, 0, res.getString(R.string.cache_menu_around)).setIcon(R.drawable.ic_menu_rotate); // caches around menu.add(1, MENU_BROWSER, 0, res.getString(R.string.cache_menu_browser)).setIcon(R.drawable.ic_menu_globe); // browser menu.add(0, MENU_SHARE, 0, res.getString(R.string.cache_menu_share)).setIcon(R.drawable.ic_menu_share); // share cache @@ -593,10 +585,6 @@ public class CacheDetailActivity extends AbstractActivity { case MENU_DEFAULT_NAVIGATION: startDefaultNavigation(); return true; - case MENU_LOG_VISIT: - refreshOnResume = true; - cache.logVisit(this); - return true; case MENU_BROWSER: cache.openInBrowser(this); return true; @@ -619,8 +607,11 @@ public class CacheDetailActivity extends AbstractActivity { if (GeneralAppsFactory.onMenuItemSelected(item, this, cache)) { return true; } + if (LoggingUI.onMenuItemSelected(item, this, cache)) { + refreshOnResume = true; + return true; + } - cache.logOffline(this, LogType.getById(menuItem - MENU_LOG_VISIT_OFFLINE)); return true; } @@ -796,7 +787,8 @@ public class CacheDetailActivity extends AbstractActivity { ICalendar.PARAM_URL, StringUtils.defaultString(cache.getUrl()), ICalendar.PARAM_COORDS, cache.getCoords() == null ? "" : cache.getCoords().format(GeopointFormatter.Format.LAT_LON_DECMINUTE_RAW), ICalendar.PARAM_LOCATION, StringUtils.defaultString(cache.getLocation()), - ICalendar.PARAM_SHORT_DESC, StringUtils.defaultString(cache.getShortDescription()) + ICalendar.PARAM_SHORT_DESC, StringUtils.defaultString(cache.getShortDescription()), + ICalendar.PARAM_START_TIME_MINUTES, StringUtils.defaultString(cache.guessEventTimeMinutes()) ); startActivity(new Intent(ICalendar.INTENT, @@ -894,8 +886,8 @@ public class CacheDetailActivity extends AbstractActivity { } // Use real owner name vice the one owner chose to display - if (StringUtils.isNotBlank(cache.getOwnerReal())) { - clickedItemText = cache.getOwnerReal(); + if (StringUtils.isNotBlank(cache.getOwnerUserId())) { + clickedItemText = cache.getOwnerUserId(); } else { clickedItemText = ((TextView) view).getText().toString(); } @@ -1344,6 +1336,9 @@ public class CacheDetailActivity extends AbstractActivity { if (cache.isDisabled() || cache.isArchived()) { // strike span.setSpan(new StrikethroughSpan(), 0, span.toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } + if (cache.isArchived()) { + span.setSpan(new ForegroundColorSpan(res.getColor(R.color.archived_cache_color)), 0, span.toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } details.add(R.string.cache_name, span); details.add(R.string.cache_type, cache.getType().getL10n()); @@ -1367,12 +1362,12 @@ public class CacheDetailActivity extends AbstractActivity { } // cache author - if (StringUtils.isNotBlank(cache.getOwner()) || StringUtils.isNotBlank(cache.getOwnerReal())) { + if (StringUtils.isNotBlank(cache.getOwnerDisplayName()) || StringUtils.isNotBlank(cache.getOwnerUserId())) { TextView ownerView = details.add(R.string.cache_owner, ""); - if (StringUtils.isNotBlank(cache.getOwner())) { - ownerView.setText(cache.getOwner(), TextView.BufferType.SPANNABLE); + if (StringUtils.isNotBlank(cache.getOwnerDisplayName())) { + ownerView.setText(cache.getOwnerDisplayName(), TextView.BufferType.SPANNABLE); } else { // OwnerReal guaranteed to be not blank based on above - ownerView.setText(cache.getOwnerReal(), TextView.BufferType.SPANNABLE); + ownerView.setText(cache.getOwnerUserId(), TextView.BufferType.SPANNABLE); } ownerView.setOnClickListener(new OwnerActionsClickListener()); } @@ -1830,7 +1825,7 @@ public class CacheDetailActivity extends AbstractActivity { } private Bitmap decode(final cgCache cache) { - return BitmapFactory.decodeFile(StaticMapsProvider.getMapFile(cache.getGeocode(), "preview", false).getPath()); + return StaticMapsProvider.getPreviewMap(cache.getGeocode()); } @Override @@ -2027,8 +2022,10 @@ public class CacheDetailActivity extends AbstractActivity { publishProgress(); } - // if description has HTML table, add a note at the end of the long description - if (unknownTagsHandler.isTableDetected() && descriptionView == view.findViewById(R.id.longdesc)) { + // If description has an HTML construct which may be problematic to render, add a note at the end of the long description. + // Technically, it may not be a table, but a pre, which has the same problems as a table, so the message is ok even though + // sometimes technically incorrect. + if (unknownTagsHandler.isProblematicDetected() && descriptionView == view.findViewById(R.id.longdesc)) { final int startPos = description.length(); ((Editable) description).append("\n\n").append(res.getString(R.string.cache_description_table_note)); ((Editable) description).setSpan(new StyleSpan(Typeface.ITALIC), startPos, description.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); @@ -2113,43 +2110,33 @@ public class CacheDetailActivity extends AbstractActivity { view = (ListView) getLayoutInflater().inflate(R.layout.cacheview_logs, null); // log count - if (cache.getLogCounts() != null) { - final StringBuilder text = new StringBuilder(200); - text.append(res.getString(R.string.cache_log_types)); - text.append(": "); - - // sort the log counts by type id ascending. that way the FOUND, DNF log types are the first and most visible ones - final List<Entry<LogType, Integer>> sortedLogCounts = new ArrayList<Entry<LogType, Integer>>(); - for (Entry<LogType, Integer> entry : cache.getLogCounts().entrySet()) { - sortedLogCounts.add(entry); // don't add these entries using addAll(), the iterator in the EntrySet can go wrong (see Findbugs) + final Map<LogType, Integer> logCounts = cache.getLogCounts(); + if (logCounts != null) { + final List<Entry<LogType, Integer>> sortedLogCounts = new ArrayList<Entry<LogType, Integer>>(logCounts.size()); + for (Entry<LogType, Integer> entry : logCounts.entrySet()) { + // it may happen that the label is unknown -> then avoid any output for this type + if (entry.getKey() != LogType.PUBLISH_LISTING && entry.getKey().getL10n() != null) { + sortedLogCounts.add(entry); + } } - Collections.sort(sortedLogCounts, new Comparator<Entry<LogType, Integer>>() { - - @Override - public int compare(Entry<LogType, Integer> logCountItem1, Entry<LogType, Integer> logCountItem2) { - return logCountItem1.getKey().compareTo(logCountItem2.getKey()); - } - }); + if (sortedLogCounts.size() > 0) { + // sort the log counts by type id ascending. that way the FOUND, DNF log types are the first and most visible ones + Collections.sort(sortedLogCounts, new Comparator<Entry<LogType, Integer>>() { - boolean showLogCounter = false; - for (Entry<LogType, Integer> pair : sortedLogCounts) { - String logTypeLabel = pair.getKey().getL10n(); - // it may happen that the label is unknown -> then avoid any output for this type - if (logTypeLabel != null && pair.getKey() != LogType.PUBLISH_LISTING) { - if (showLogCounter) { - text.append(", "); + @Override + public int compare(Entry<LogType, Integer> logCountItem1, Entry<LogType, Integer> logCountItem2) { + return logCountItem1.getKey().compareTo(logCountItem2.getKey()); } - text.append(pair.getValue().intValue()); - text.append("× "); - text.append(logTypeLabel); + }); + + ArrayList<String> labels = new ArrayList<String>(sortedLogCounts.size()); + for (Entry<LogType, Integer> pair : sortedLogCounts) { + labels.add(pair.getValue() + "× " + pair.getKey().getL10n()); } - showLogCounter = true; - } - if (showLogCounter) { final TextView countView = new TextView(CacheDetailActivity.this); - countView.setText(text.toString()); + countView.setText(res.getString(R.string.cache_log_types) + ": " + StringUtils.join(labels, ", ")); view.addHeaderView(countView, null, false); } } @@ -2317,23 +2304,10 @@ public class CacheDetailActivity extends AbstractActivity { } // info - final List<String> infoTextList = new ArrayList<String>(3); - if (WaypointType.ALL_TYPES_EXCEPT_OWN.contains(wpt.getWaypointType())) { - infoTextList.add(wpt.getWaypointType().getL10n()); - } - if (cgWaypoint.PREFIX_OWN.equalsIgnoreCase(wpt.getPrefix())) { - infoTextList.add(res.getString(R.string.waypoint_custom)); - } else { - if (StringUtils.isNotBlank(wpt.getPrefix())) { - infoTextList.add(wpt.getPrefix()); - } - if (StringUtils.isNotBlank(wpt.getLookup())) { - infoTextList.add(wpt.getLookup()); - } - } - if (CollectionUtils.isNotEmpty(infoTextList)) { + final String waypointInfo = Formatter.formatWaypointInfo(wpt); + if (StringUtils.isNotBlank(waypointInfo)) { final TextView infoView = (TextView) waypointView.findViewById(R.id.info); - infoView.setText(StringUtils.join(infoTextList, Formatter.SEPARATOR)); + infoView.setText(waypointInfo); infoView.setVisibility(View.VISIBLE); } diff --git a/main/src/cgeo/geocaching/DirectionProvider.java b/main/src/cgeo/geocaching/DirectionProvider.java index 7bb940f..14fd283 100644 --- a/main/src/cgeo/geocaching/DirectionProvider.java +++ b/main/src/cgeo/geocaching/DirectionProvider.java @@ -14,6 +14,12 @@ public class DirectionProvider extends MemorySubject<Float> implements SensorEve private final SensorManager sensorManager; + // Previous values signalled to observers to avoid resending the same value when the + // device doesn't change orientation. The orientation is usually given with a 1 degree + // precision by Android, so it is not uncommon to obtain exactly the same value several + // times. + private float previous = -1; + public DirectionProvider(final Context context) { sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); @@ -46,7 +52,11 @@ public class DirectionProvider extends MemorySubject<Float> implements SensorEve @Override public void onSensorChanged(final SensorEvent event) { - notifyObservers(event.values[0]); + final float direction = event.values[0]; + if (direction != previous) { + notifyObservers(direction); + previous = direction; + } } /** @@ -57,7 +67,7 @@ public class DirectionProvider extends MemorySubject<Float> implements SensorEve * @return the adjusted direction in degrees, in the [0, 360[ range */ public static float getDirectionNow(final Activity activity, final float direction) { - return Compatibility.getDirectionNow(direction, activity) % 360; + return Compatibility.getDirectionNow(direction, activity); } } diff --git a/main/src/cgeo/geocaching/GeoDataProvider.java b/main/src/cgeo/geocaching/GeoDataProvider.java index 441c871..1b9a0f5 100644 --- a/main/src/cgeo/geocaching/GeoDataProvider.java +++ b/main/src/cgeo/geocaching/GeoDataProvider.java @@ -168,11 +168,41 @@ class GeoDataProvider extends MemorySubject<IGeoData> { GeoDataProvider(final Context context) { geoManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); unregisterer.start(); - // Start with an empty GeoData just in case someone queries it before we get + + final Location initialLocation = new Location(LAST_LOCATION_PSEUDO_PROVIDER); + try { + // Try to find a sensible initial location from the last locations known to Android. + final Location lastGpsLocation = geoManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); + final Location lastNetworkLocation = geoManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); + + // If both providers are non-null, take the most recent one + if (lastGpsLocation != null && lastNetworkLocation != null) { + if (lastGpsLocation.getTime() >= lastNetworkLocation.getTime()) { + copyCoords(initialLocation, lastGpsLocation); + } else { + copyCoords(initialLocation, lastNetworkLocation); + } + } else if (lastGpsLocation != null) { + copyCoords(initialLocation, lastGpsLocation); + } else if (lastNetworkLocation != null) { + copyCoords(initialLocation, lastNetworkLocation); + } else { + Log.i("GeoDataProvider: no last known location available"); + } + } catch (final Exception e) { + // This error is non-fatal as its only consequence is that we will start with a dummy location + // instead of a previously known one. + Log.e("GeoDataProvider: error when retrieving last known location", e); + } + // Start with an historical GeoData just in case someone queries it before we get // a chance to get any information. - notifyObservers(new GeoData(new Location(LAST_LOCATION_PSEUDO_PROVIDER), false, 0, 0)); + notifyObservers(new GeoData(initialLocation, false, 0, 0)); } + private static void copyCoords(final Location target, final Location source) { + target.setLatitude(source.getLatitude()); + target.setLongitude(source.getLongitude()); + } private void registerListeners() { geoManager.addGpsStatusListener(gpsStatusListener); diff --git a/main/src/cgeo/geocaching/ICache.java b/main/src/cgeo/geocaching/ICache.java index a4f5eda..d76bacc 100644 --- a/main/src/cgeo/geocaching/ICache.java +++ b/main/src/cgeo/geocaching/ICache.java @@ -17,12 +17,12 @@ public interface ICache extends IBasicCache { /** * @return Displayed owner, might differ from the real owner */ - public String getOwner(); + public String getOwnerDisplayName(); /** * @return GC username of the (actual) owner, might differ from the owner. Never empty. */ - public String getOwnerReal(); + public String getOwnerUserId(); /** * @return true if the user is the owner of the cache, false else diff --git a/main/src/cgeo/geocaching/LiveMapInfo.java b/main/src/cgeo/geocaching/LiveMapInfo.java index 822fbf6..2fee940 100644 --- a/main/src/cgeo/geocaching/LiveMapInfo.java +++ b/main/src/cgeo/geocaching/LiveMapInfo.java @@ -13,7 +13,6 @@ public class LiveMapInfo extends AbstractActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setTheme(R.style.transparent); setContentView(R.layout.livemapinfo); final int showCount = Settings.getLiveMapHintShowCount(); diff --git a/main/src/cgeo/geocaching/LogEntry.java b/main/src/cgeo/geocaching/LogEntry.java index 31a9703..e03dc66 100644 --- a/main/src/cgeo/geocaching/LogEntry.java +++ b/main/src/cgeo/geocaching/LogEntry.java @@ -1,6 +1,7 @@ package cgeo.geocaching; import cgeo.geocaching.enumerations.LogType; +import cgeo.geocaching.utils.DateUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -94,15 +95,6 @@ public final class LogEntry { } public int daysSinceLog() { - final Calendar logDate = Calendar.getInstance(); - logDate.setTimeInMillis(date); - logDate.set(Calendar.SECOND, 0); - logDate.set(Calendar.MINUTE, 0); - logDate.set(Calendar.HOUR, 0); - final Calendar today = Calendar.getInstance(); - today.set(Calendar.SECOND, 0); - today.set(Calendar.MINUTE, 0); - today.set(Calendar.HOUR, 0); - return (int) Math.round((today.getTimeInMillis() - logDate.getTimeInMillis()) / 86400000d); + return DateUtils.daysSince(date); } } diff --git a/main/src/cgeo/geocaching/LogTrackableActivity.java b/main/src/cgeo/geocaching/LogTrackableActivity.java index 2a0d503..578bd62 100644 --- a/main/src/cgeo/geocaching/LogTrackableActivity.java +++ b/main/src/cgeo/geocaching/LogTrackableActivity.java @@ -247,8 +247,8 @@ public class LogTrackableActivity extends AbstractActivity implements DateDialog }); final Button dateButton = (Button) findViewById(R.id.date); - dateButton.setText(Formatter.formatShortDate(date.getTime().getTime())); dateButton.setOnClickListener(new DateListener()); + setDate(date); if (tweetBox == null) { tweetBox = (LinearLayout) findViewById(R.id.tweet_box); @@ -281,7 +281,7 @@ public class LogTrackableActivity extends AbstractActivity implements DateDialog date = dateIn; final Button dateButton = (Button) findViewById(R.id.date); - dateButton.setText(Formatter.formatShortDate(date.getTime().getTime())); + dateButton.setText(Formatter.formatShortDateVerbally(date.getTime().getTime())); } public void setType(LogType type) { diff --git a/main/src/cgeo/geocaching/Settings.java b/main/src/cgeo/geocaching/Settings.java index 2d86c2f..bbcb80b 100644 --- a/main/src/cgeo/geocaching/Settings.java +++ b/main/src/cgeo/geocaching/Settings.java @@ -94,6 +94,7 @@ public final class Settings { private static final String KEY_SETTINGS_VERSION = "settingsversion"; private static final String KEY_DB_ON_SDCARD = "dbonsdcard"; private static final String KEY_LAST_TRACKABLE_ACTION = "trackableaction"; + private static final String KEY_SHARE_AFTER_EXPORT = "shareafterexport"; private final static int unitsMetric = 1; @@ -202,7 +203,7 @@ public final class Settings { e.putBoolean(KEY_HIDE_LIVE_MAP_HINT, old.getInt(KEY_HIDE_LIVE_MAP_HINT, 0) != 0); e.putInt(KEY_LIVE_MAP_HINT_SHOW_COUNT, old.getInt(KEY_LIVE_MAP_HINT_SHOW_COUNT, 0)); - e.putInt(KEY_SETTINGS_VERSION, 1) ; // mark migrated + e.putInt(KEY_SETTINGS_VERSION, 1); // mark migrated e.commit(); } } @@ -1109,7 +1110,6 @@ public final class Settings { }); } - public static boolean isDebug() { return Log.isDebug(); } @@ -1117,7 +1117,8 @@ public final class Settings { public static void setDebug(final boolean debug) { editSharedSettings(new PrefRunnable() { - @Override public void edit(Editor edit) { + @Override + public void edit(Editor edit) { edit.putBoolean(KEY_DEBUG, debug); } }); @@ -1158,7 +1159,6 @@ public final class Settings { public static void setDbOnSDCard(final boolean dbOnSDCard) { editSharedSettings(new PrefRunnable() { - @Override public void edit(Editor edit) { edit.putBoolean(KEY_DB_ON_SDCARD, dbOnSDCard); @@ -1166,6 +1166,15 @@ public final class Settings { }); } + public static void setShareAfterExport(final boolean shareAfterExport) { + editSharedSettings(new PrefRunnable() { + @Override + public void edit(Editor edit) { + edit.putBoolean(KEY_SHARE_AFTER_EXPORT, shareAfterExport); + } + }); + } + public static int getTrackableAction() { return sharedPrefs.getInt(KEY_LAST_TRACKABLE_ACTION, LogType.RETRIEVED_IT.id); } @@ -1178,6 +1187,10 @@ public final class Settings { edit.putInt(KEY_LAST_TRACKABLE_ACTION, trackableAction); } }); + } + + public static boolean getShareAfterExport() { + return sharedPrefs.getBoolean(KEY_SHARE_AFTER_EXPORT, true); } public static String getPreferencesName() { diff --git a/main/src/cgeo/geocaching/SettingsActivity.java b/main/src/cgeo/geocaching/SettingsActivity.java index b87bd73..d70b300 100644 --- a/main/src/cgeo/geocaching/SettingsActivity.java +++ b/main/src/cgeo/geocaching/SettingsActivity.java @@ -19,6 +19,7 @@ import cgeo.geocaching.utils.LogTemplateProvider; import cgeo.geocaching.utils.LogTemplateProvider.LogTemplate; import ch.boye.httpclientandroidlib.HttpResponse; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -57,6 +58,7 @@ public class SettingsActivity extends AbstractActivity { private ProgressDialog loginDialog = null; private ProgressDialog webDialog = null; + private boolean enableTemplatesMenu = false; private Handler logInHandler = new Handler() { @Override @@ -115,7 +117,6 @@ public class SettingsActivity extends AbstractActivity { init(); } }; - protected boolean enableTemplatesMenu = false; public SettingsActivity() { super("c:geo-configuration"); @@ -135,12 +136,6 @@ public class SettingsActivity extends AbstractActivity { } @Override - public void onResume() { - super.onResume(); - - } - - @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -594,7 +589,7 @@ public class SettingsActivity extends AbstractActivity { // Default navigation tool settings Spinner defaultNavigationToolSelector = (Spinner) findViewById(R.id.default_navigation_tool); - final List<NavigationAppsEnum> apps = NavigationAppFactory.getInstalledNavigationApps(); + final List<NavigationAppsEnum> apps = NavigationAppFactory.getInstalledDefaultNavigationApps(); ArrayAdapter<NavigationAppsEnum> naviAdapter = new ArrayAdapter<NavigationAppsEnum>(this, android.R.layout.simple_spinner_item, apps) { @Override public View getView(int position, View convertView, ViewGroup parent) { @@ -602,6 +597,7 @@ public class SettingsActivity extends AbstractActivity { textView.setText(getItem(position).app.getName()); return textView; } + @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { TextView textView = (TextView) super.getDropDownView(position, convertView, parent); diff --git a/main/src/cgeo/geocaching/StaticMapsActivity.java b/main/src/cgeo/geocaching/StaticMapsActivity.java index a95f4d2..7ddc4e0 100644 --- a/main/src/cgeo/geocaching/StaticMapsActivity.java +++ b/main/src/cgeo/geocaching/StaticMapsActivity.java @@ -135,42 +135,28 @@ public class StaticMapsActivity extends AbstractActivity { factory = new BitmapFactory(); } - for (int level = 1; level <= 5; level++) { - try { - if (waypoint_id != null) { - final Bitmap image = BitmapFactory.decodeFile(StaticMapsProvider.getMapFile(geocode, "wp" + waypoint_id + "_" + level, false).getPath()); - if (image != null) { - maps.add(image); - } - } else { - final Bitmap image = BitmapFactory.decodeFile(StaticMapsProvider.getMapFile(geocode, "" + level, false).getPath()); - if (image != null) { - maps.add(image); - } - } - } catch (Exception e) { - Log.e("StaticMapsActivity.LoadMapsThread.run.1: " + e.toString()); - } - } - - if (maps.isEmpty()) { + // try downloading 2 times + for (int trials = 0; trials < 2; trials++) { for (int level = 1; level <= 5; level++) { try { if (waypoint_id != null) { - final Bitmap image = BitmapFactory.decodeFile(StaticMapsProvider.getMapFile(geocode, "wp" + waypoint_id + "_" + level, false).getPath()); + final Bitmap image = StaticMapsProvider.getWaypointMap(geocode, waypoint_id, level); if (image != null) { maps.add(image); } } else { - final Bitmap image = BitmapFactory.decodeFile(StaticMapsProvider.getMapFile(geocode, "" + level, false).getPath()); + final Bitmap image = StaticMapsProvider.getCacheMap(geocode, level); if (image != null) { maps.add(image); } } } catch (Exception e) { - Log.e("StaticMapsActivity.LoadMapsThread.run.2: " + e.toString()); + Log.e("StaticMapsActivity.LoadMapsThread.run: " + e.toString()); } } + if (!maps.isEmpty()) { + break; + } } loadMapsHandler.sendMessage(Message.obtain()); @@ -201,13 +187,13 @@ public class StaticMapsActivity extends AbstractActivity { if (waypoint_id == null) { showToast(res.getString(R.string.info_storing_static_maps)); StaticMapsProvider.storeCacheStaticMap(cache, this, true); - return StaticMapsProvider.doesExistStaticMapForCache(geocode); + return StaticMapsProvider.hasStaticMapForCache(geocode); } final cgWaypoint waypoint = cache.getWaypointById(waypoint_id); if (waypoint != null) { showToast(res.getString(R.string.info_storing_static_maps)); StaticMapsProvider.storeWaypointStaticMap(cache, this, waypoint, true); - return StaticMapsProvider.doesExistStaticMapForWaypoint(geocode, waypoint_id); + return StaticMapsProvider.hasStaticMapForWaypoint(geocode, waypoint_id); } showToast(res.getString(R.string.err_detail_not_load_map_static)); return false; diff --git a/main/src/cgeo/geocaching/StaticMapsProvider.java b/main/src/cgeo/geocaching/StaticMapsProvider.java index 030efeb..6de1291 100644 --- a/main/src/cgeo/geocaching/StaticMapsProvider.java +++ b/main/src/cgeo/geocaching/StaticMapsProvider.java @@ -8,11 +8,14 @@ import cgeo.geocaching.network.Parameters; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; + import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import android.app.Activity; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.util.DisplayMetrics; import android.view.Display; import android.view.WindowManager; @@ -21,6 +24,7 @@ import java.io.File; import java.util.concurrent.TimeUnit; public class StaticMapsProvider { + private static final String PREFIX_PREVIEW = "preview"; private static final String GOOGLE_STATICMAP_URL = "http://maps.google.com/maps/api/staticmap"; private static final String SATELLITE = "satellite"; private static final String ROADMAP = "roadmap"; @@ -32,7 +36,7 @@ public class StaticMapsProvider { /** ThreadPool restricting this to 1 Thread. **/ private static final BlockingThreadPool pool = new BlockingThreadPool(1, Thread.MIN_PRIORITY); - public static File getMapFile(final String geocode, String prefix, final boolean createDirs) { + private static File getMapFile(final String geocode, String prefix, final boolean createDirs) { return LocalStorage.getStorageFile(geocode, MAP_FILENAME_PREFIX + prefix, false, createDirs); } @@ -48,7 +52,7 @@ public class StaticMapsProvider { final Parameters params = new Parameters( "center", latlonMap, "zoom", String.valueOf(zoom), - "size", width + "x" + height, + "size", String.valueOf(width) + 'x' + String.valueOf(height), "maptype", mapType, "markers", "icon:" + markerUrl + '|' + shadow + latlonMap, "sensor", "false"); @@ -80,7 +84,7 @@ public class StaticMapsProvider { downloadMaps(cache, display); } - public static void downloadMaps(cgCache cache, Display display) { + private static void downloadMaps(cgCache cache, Display display) { if ((!Settings.isStoreOfflineMaps() && !Settings.isStoreOfflineWpMaps()) || StringUtils.isBlank(cache.getGeocode())) { return; } @@ -114,7 +118,7 @@ public class StaticMapsProvider { String wpLatlonMap = waypoint.getCoords().format(Format.LAT_LON_DECDEGREE_COMMA); String wpMarkerUrl = getWpMarkerUrl(waypoint); // download map images in separate background thread for higher performance - downloadMaps(geocode, wpMarkerUrl, WAYPOINT_PREFIX + waypoint.getId() + "_", wpLatlonMap, edge, null, waitForResult); + downloadMaps(geocode, wpMarkerUrl, WAYPOINT_PREFIX + waypoint.getId() + '_', wpLatlonMap, edge, null, waitForResult); } public static void storeCacheStaticMap(cgCache cache, Activity activity, final boolean waitForResult) { @@ -130,7 +134,7 @@ public class StaticMapsProvider { continue; } final String wpMarkerUrl = getWpMarkerUrl(waypoint); - waypoints.put("markers", "icon:" + wpMarkerUrl + "|" + waypoint.getCoords().format(Format.LAT_LON_DECDEGREE_COMMA)); + waypoints.put("markers", "icon:" + wpMarkerUrl + '|' + waypoint.getCoords().format(Format.LAT_LON_DECDEGREE_COMMA)); } // download map images in separate background thread for higher performance final String cacheMarkerUrl = getCacheMarkerUrl(cache); @@ -145,7 +149,7 @@ public class StaticMapsProvider { display.getMetrics(metrics); final int width = metrics.widthPixels; final int height = (int) (110 * metrics.density); - downloadMap(cache.getGeocode(), 15, ROADMAP, markerUrl, "preview", "shadow:false|", latlonMap, width, height, null); + downloadMap(cache.getGeocode(), 15, ROADMAP, markerUrl, PREFIX_PREVIEW, "shadow:false|", latlonMap, width, height, null); } private static int guessMaxDisplaySide(Display display) { @@ -182,14 +186,15 @@ public class StaticMapsProvider { } private static String getCacheMarkerUrl(final cgCache cache) { - String type = cache.getType().id; + StringBuilder url = new StringBuilder(MARKERS_URL); + url.append("marker_cache_").append(cache.getType().id); if (cache.isFound()) { - type += "_found"; + url.append("_found"); } else if (cache.isDisabled()) { - type += "_disabled"; + url.append("_disabled"); } - - return MARKERS_URL + "marker_cache_" + type + ".png"; + url.append(".png"); + return url.toString(); } private static String getWpMarkerUrl(final cgWaypoint waypoint) { @@ -198,11 +203,12 @@ public class StaticMapsProvider { } public static void removeWpStaticMaps(int wp_id, final String geocode) { + if (wp_id <= 0) { + return; + } for (int level = 1; level <= 5; level++) { try { - if (wp_id > 0) { - StaticMapsProvider.getMapFile(geocode, WAYPOINT_PREFIX + wp_id + "_" + level, false).delete(); - } + StaticMapsProvider.getMapFile(geocode, WAYPOINT_PREFIX + wp_id + '_' + level, false).delete(); } catch (Exception e) { Log.e("StaticMapsProvider.removeWpStaticMaps: " + e.toString()); } @@ -215,9 +221,9 @@ public class StaticMapsProvider { * @param geocode * @return <code>true</code> if at least one mapfile exists; <code>false</code> otherwise */ - public static boolean doesExistStaticMapForCache(String geocode) { + public static boolean hasStaticMapForCache(String geocode) { for (int level = 1; level <= 5; level++) { - File mapFile = StaticMapsProvider.getMapFile(geocode, "" + level, false); + File mapFile = StaticMapsProvider.getMapFile(geocode, String.valueOf(level), false); if (mapFile != null && mapFile.exists()) { return true; } @@ -232,7 +238,7 @@ public class StaticMapsProvider { * @param waypointId * @return <code>true</code> if at least one mapfile exists; <code>false</code> otherwise */ - public static boolean doesExistStaticMapForWaypoint(String geocode, int waypointId) { + public static boolean hasStaticMapForWaypoint(String geocode, int waypointId) { for (int level = 1; level <= 5; level++) { File mapFile = StaticMapsProvider.getMapFile(geocode, WAYPOINT_PREFIX + waypointId + "_" + level, false); if (mapFile != null && mapFile.exists()) { @@ -241,4 +247,16 @@ public class StaticMapsProvider { } return false; } + + public static Bitmap getPreviewMap(final String geocode) { + return BitmapFactory.decodeFile(StaticMapsProvider.getMapFile(geocode, PREFIX_PREVIEW, false).getPath()); + } + + public static Bitmap getWaypointMap(final String geocode, int waypoint_id, int level) { + return BitmapFactory.decodeFile(StaticMapsProvider.getMapFile(geocode, WAYPOINT_PREFIX + waypoint_id + "_" + level, false).getPath()); + } + + public static Bitmap getCacheMap(final String geocode, int level) { + return BitmapFactory.decodeFile(StaticMapsProvider.getMapFile(geocode, String.valueOf(level), false).getPath()); + } } diff --git a/main/src/cgeo/geocaching/VisitCacheActivity.java b/main/src/cgeo/geocaching/VisitCacheActivity.java index c211157..e5410e7 100644 --- a/main/src/cgeo/geocaching/VisitCacheActivity.java +++ b/main/src/cgeo/geocaching/VisitCacheActivity.java @@ -510,7 +510,7 @@ public class VisitCacheActivity extends AbstractActivity implements DateDialog.D }); final Button dateButton = (Button) findViewById(R.id.date); - dateButton.setText(Formatter.formatShortDate(date.getTime().getTime())); + setDate(date); dateButton.setOnClickListener(new DateListener()); final EditText logView = (EditText) findViewById(R.id.log); @@ -548,7 +548,7 @@ public class VisitCacheActivity extends AbstractActivity implements DateDialog.D date = dateIn; final Button dateButton = (Button) findViewById(R.id.date); - dateButton.setText(Formatter.formatShortDate(date.getTime().getTime())); + dateButton.setText(Formatter.formatShortDateVerbally(date.getTime().getTime())); } public void setType(LogType type) { @@ -602,6 +602,7 @@ public class VisitCacheActivity extends AbstractActivity implements DateDialog.D @Override public void onClick(View arg0) { + //TODO: unify this method and the code in init() app.clearLogOffline(geocode); if (alreadyFound) { @@ -615,8 +616,8 @@ public class VisitCacheActivity extends AbstractActivity implements DateDialog.D setType(typeSelected); final Button dateButton = (Button) findViewById(R.id.date); - dateButton.setText(Formatter.formatShortDate(date.getTime().getTime())); dateButton.setOnClickListener(new DateListener()); + setDate(date); final EditText logView = (EditText) findViewById(R.id.log); logView.setText(""); diff --git a/main/src/cgeo/geocaching/activity/AbstractActivity.java b/main/src/cgeo/geocaching/activity/AbstractActivity.java index 5a90238..a648486 100644 --- a/main/src/cgeo/geocaching/activity/AbstractActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractActivity.java @@ -1,7 +1,6 @@ package cgeo.geocaching.activity; import cgeo.geocaching.Settings; -import cgeo.geocaching.cgCache; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.network.Cookies; @@ -10,7 +9,6 @@ import android.app.Activity; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.view.Menu; import android.view.View; import android.widget.EditText; @@ -90,10 +88,6 @@ public abstract class AbstractActivity extends Activity implements IAbstractActi ActivityMixin.keepScreenOn(this, keepScreenOn); } - public void addVisitMenu(Menu menu, cgCache cache) { - ActivityMixin.addVisitMenu(this, menu, cache); - } - protected static void disableSuggestions(final EditText edit) { Compatibility.disableSuggestions(edit); } diff --git a/main/src/cgeo/geocaching/activity/AbstractListActivity.java b/main/src/cgeo/geocaching/activity/AbstractListActivity.java index a209b23..dc9c0fe 100644 --- a/main/src/cgeo/geocaching/activity/AbstractListActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractListActivity.java @@ -1,6 +1,5 @@ package cgeo.geocaching.activity; -import cgeo.geocaching.cgCache; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.compatibility.Compatibility; @@ -8,7 +7,6 @@ import android.app.ListActivity; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.view.Menu; import android.view.View; public abstract class AbstractListActivity extends ListActivity implements @@ -85,10 +83,6 @@ public abstract class AbstractListActivity extends ListActivity implements ActivityMixin.setTitle(this, title); } - public void addVisitMenu(Menu menu, cgCache cache) { - ActivityMixin.addVisitMenu(this, menu, cache); - } - @Override public void invalidateOptionsMenuCompatible() { Compatibility.invalidateOptionsMenu(this); diff --git a/main/src/cgeo/geocaching/activity/ActivityMixin.java b/main/src/cgeo/geocaching/activity/ActivityMixin.java index 8d5611b..8900593 100644 --- a/main/src/cgeo/geocaching/activity/ActivityMixin.java +++ b/main/src/cgeo/geocaching/activity/ActivityMixin.java @@ -2,10 +2,8 @@ package cgeo.geocaching.activity; import cgeo.geocaching.R; import cgeo.geocaching.Settings; -import cgeo.geocaching.cgCache; import cgeo.geocaching.cgeo; import cgeo.geocaching.compatibility.Compatibility; -import cgeo.geocaching.enumerations.LogType; import org.apache.commons.lang3.StringUtils; @@ -14,11 +12,8 @@ import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.view.Gravity; -import android.view.Menu; -import android.view.SubMenu; import android.view.View; import android.view.WindowManager; import android.widget.ProgressBar; @@ -27,10 +22,7 @@ import android.widget.Toast; import gnu.android.app.appmanualclient.AppManualReaderClient; -import java.util.List; - public final class ActivityMixin { - private static final int MENU_ICON_LOG_VISIT = R.drawable.ic_menu_edit; public final static void goHome(final Activity fromActivity) { final Intent intent = new Intent(fromActivity, cgeo.class); @@ -129,27 +121,6 @@ public final class ActivityMixin { helpDialog(activity, title, message, null); } - protected static void addVisitMenu(IAbstractActivity activity, Menu menu, cgCache cache) { - if (cache == null) { - return; - } - if (!cache.supportsLogging()) { - return; - } - Resources res = ((Activity) activity).getResources(); - if (Settings.getLogOffline()) { - SubMenu logMenu = menu.addSubMenu(1, IAbstractActivity.MENU_LOG_VISIT_OFFLINE, 0, res.getString(R.string.cache_menu_visit_offline)).setIcon(MENU_ICON_LOG_VISIT); - List<LogType> logTypes = cache.getPossibleLogTypes(); - for (LogType logType : logTypes) { - logMenu.add(1, IAbstractActivity.MENU_LOG_VISIT_OFFLINE + logType.id, 0, logType.getL10n()); - } - logMenu.add(1, IAbstractActivity.MENU_LOG_VISIT, 0, res.getString(R.string.cache_menu_visit)); - } - else { - menu.add(1, IAbstractActivity.MENU_LOG_VISIT, 0, res.getString(R.string.cache_menu_visit)).setIcon(MENU_ICON_LOG_VISIT); - } - } - public static void keepScreenOn(final Activity abstractActivity, boolean keepScreenOn) { if (keepScreenOn) { abstractActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); diff --git a/main/src/cgeo/geocaching/activity/IAbstractActivity.java b/main/src/cgeo/geocaching/activity/IAbstractActivity.java index 2503b99..04709c6 100644 --- a/main/src/cgeo/geocaching/activity/IAbstractActivity.java +++ b/main/src/cgeo/geocaching/activity/IAbstractActivity.java @@ -3,8 +3,6 @@ package cgeo.geocaching.activity; import android.view.View; public interface IAbstractActivity { - static final int MENU_LOG_VISIT = 100; - static final int MENU_LOG_VISIT_OFFLINE = 101; public void goHome(View view); diff --git a/main/src/cgeo/geocaching/activity/Progress.java b/main/src/cgeo/geocaching/activity/Progress.java index dbe4700..7a1995d 100644 --- a/main/src/cgeo/geocaching/activity/Progress.java +++ b/main/src/cgeo/geocaching/activity/Progress.java @@ -27,6 +27,7 @@ public class Progress { dialog = ProgressDialog.show(context, title, message, indeterminate, cancelMessage != null); dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); dialog.setProgress(0); + dialog.setCanceledOnTouchOutside(false); this.progress = 0; if (cancelMessage != null) { dialog.setCancelMessage(cancelMessage); @@ -38,6 +39,7 @@ public class Progress { if (dialog == null) { dialog = new ProgressDialog(context); dialog.setProgress(0); + dialog.setCanceledOnTouchOutside(false); this.progress = 0; dialog.setTitle(title); dialog.setMessage(message); diff --git a/main/src/cgeo/geocaching/apps/AbstractApp.java b/main/src/cgeo/geocaching/apps/AbstractApp.java index 9acc0ae..5bed2d9 100644 --- a/main/src/cgeo/geocaching/apps/AbstractApp.java +++ b/main/src/cgeo/geocaching/apps/AbstractApp.java @@ -47,6 +47,11 @@ public abstract class AbstractApp implements App { } @Override + public boolean isDefaultNavigationApp() { + return true; + } + + @Override public String getName() { return name; } diff --git a/main/src/cgeo/geocaching/apps/AbstractLocusApp.java b/main/src/cgeo/geocaching/apps/AbstractLocusApp.java index 8f5160a..ac6fc1c 100644 --- a/main/src/cgeo/geocaching/apps/AbstractLocusApp.java +++ b/main/src/cgeo/geocaching/apps/AbstractLocusApp.java @@ -123,7 +123,7 @@ public abstract class AbstractLocusApp extends AbstractApp { pg.archived = cache.isArchived(); pg.premiumOnly = cache.isPremiumMembersOnly(); pg.name = cache.getName(); - pg.placedBy = cache.getOwner(); + pg.placedBy = cache.getOwnerDisplayName(); if (cache.getHiddenDate() != null) { pg.hidden = ISO8601DATE.format(cache.getHiddenDate().getTime()); } diff --git a/main/src/cgeo/geocaching/apps/App.java b/main/src/cgeo/geocaching/apps/App.java index 2e5e730..9d6d371 100644 --- a/main/src/cgeo/geocaching/apps/App.java +++ b/main/src/cgeo/geocaching/apps/App.java @@ -5,6 +5,8 @@ import cgeo.geocaching.cgCache; public interface App { public boolean isInstalled(); + public boolean isDefaultNavigationApp(); + public String getName(); int getId(); diff --git a/main/src/cgeo/geocaching/apps/cache/GeneralAppsFactory.java b/main/src/cgeo/geocaching/apps/cache/GeneralAppsFactory.java index 22ce677..98e7db8 100644 --- a/main/src/cgeo/geocaching/apps/cache/GeneralAppsFactory.java +++ b/main/src/cgeo/geocaching/apps/cache/GeneralAppsFactory.java @@ -31,14 +31,15 @@ public final class GeneralAppsFactory extends AbstractAppFactory { public static boolean onMenuItemSelected(final MenuItem item, Activity activity, cgCache cache) { final GeneralApp app = (GeneralApp) getAppFromMenuItem(item, apps); - if (app != null) { - try { - return app.invoke(activity, cache); - } catch (Exception e) { - Log.e("GeneralAppsFactory.onMenuItemSelected: " + e.toString()); - } + if (app == null) { + return false; + } + try { + app.invoke(activity, cache); + } catch (Exception e) { + Log.e("GeneralAppsFactory.onMenuItemSelected: " + e.toString()); } - return false; + return true; } } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java index 5817763..ca8c7db 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java @@ -2,14 +2,14 @@ package cgeo.geocaching.apps.cache.navi; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgWaypoint; -import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.apps.AbstractApp; import android.app.Activity; /** * navigation app for simple point navigation (no differentiation between cache/waypoint/point) */ -abstract class AbstractPointNavigationApp extends AbstractNavigationApp { +abstract class AbstractPointNavigationApp extends AbstractApp implements CacheNavigationApp, WaypointNavigationApp, GeopointNavigationApp { protected AbstractPointNavigationApp(String name, String intent) { super(name, intent); @@ -20,46 +20,22 @@ abstract class AbstractPointNavigationApp extends AbstractNavigationApp { } @Override - public final boolean invoke(Activity activity, cgCache cache, cgWaypoint waypoint, Geopoint coords) { - if (cache == null && waypoint == null && coords == null) { - return false; - } - - try { - if (isInstalled()) { - final Geopoint point = getCoordinates(cache, waypoint, coords); - if (point != null) { - navigate(activity, point); - return true; - } - } - } catch (Exception e) { - // nothing - } - - return false; + public void navigate(Activity activity, cgCache cache) { + navigate(activity, cache.getCoords()); } - protected abstract void navigate(Activity activity, Geopoint point); - - /** - * Return the first of the cache coordinates, the waypoint coordinates or the extra coordinates. <code>null</code> - * entities are skipped. - * - * @param cache a cache - * @param waypoint a waypoint - * @param coords extra coordinates - * @return the first non-null coordinates, or null if none are set - */ - private static Geopoint getCoordinates(final cgCache cache, final cgWaypoint waypoint, final Geopoint coords) { - if (cache != null && cache.getCoords() != null) { - return cache.getCoords(); - } + @Override + public void navigate(Activity activity, cgWaypoint waypoint) { + navigate(activity, waypoint.getCoords()); + } - if (waypoint != null && waypoint.getCoords() != null) { - return waypoint.getCoords(); - } + @Override + public boolean isEnabled(cgCache cache) { + return cache.getCoords() != null; + } - return coords; + @Override + public boolean isEnabled(cgWaypoint waypoint) { + return waypoint.getCoords() != null; } -}
\ No newline at end of file +} diff --git a/main/src/cgeo/geocaching/apps/cache/navi/AbstractStaticMapsApp.java b/main/src/cgeo/geocaching/apps/cache/navi/AbstractStaticMapsApp.java index 266acfd..dacb03f 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/AbstractStaticMapsApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/AbstractStaticMapsApp.java @@ -8,13 +8,13 @@ import cgeo.geocaching.cgCache; import cgeo.geocaching.cgWaypoint; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; -import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.apps.AbstractApp; import org.apache.commons.lang3.StringUtils; import android.app.Activity; -abstract class AbstractStaticMapsApp extends AbstractNavigationApp { +abstract class AbstractStaticMapsApp extends AbstractApp implements CacheNavigationApp, WaypointNavigationApp { public AbstractStaticMapsApp(String name) { super(name, null); } @@ -24,28 +24,29 @@ abstract class AbstractStaticMapsApp extends AbstractNavigationApp { return true; } + @Override + public boolean isDefaultNavigationApp() { + return false; + } + protected static boolean hasStaticMap(cgCache cache) { - if (cache != null) { - String geocode = cache.getGeocode(); - if (StringUtils.isNotEmpty(geocode) && cgeoapplication.getInstance().isOffline(geocode, null)) { - return StaticMapsProvider.doesExistStaticMapForCache(geocode); - } + String geocode = cache.getGeocode(); + if (StringUtils.isNotEmpty(geocode) && cgeoapplication.getInstance().isOffline(geocode, null)) { + return StaticMapsProvider.hasStaticMapForCache(geocode); } return false; } protected static boolean hasStaticMap(cgWaypoint waypoint) { - if (waypoint != null) { - String geocode = waypoint.getGeocode(); - int id = waypoint.getId(); - if (StringUtils.isNotEmpty(geocode) && cgeoapplication.getInstance().isOffline(geocode, null)) { - return StaticMapsProvider.doesExistStaticMapForWaypoint(geocode, id); - } + String geocode = waypoint.getGeocode(); + int id = waypoint.getId(); + if (StringUtils.isNotEmpty(geocode) && cgeoapplication.getInstance().isOffline(geocode, null)) { + return StaticMapsProvider.hasStaticMapForWaypoint(geocode, id); } return false; } - protected static boolean invoke(final Activity activity, final cgCache cache, final cgWaypoint waypoint, final boolean download) { + protected static boolean invokeStaticMaps(final Activity activity, final cgCache cache, final cgWaypoint waypoint, final boolean download) { final ILogable logable = cache != null && cache.getListId() != 0 ? cache : waypoint; final String geocode = StringUtils.upperCase(logable.getGeocode()); if (geocode == null) { @@ -56,11 +57,4 @@ abstract class AbstractStaticMapsApp extends AbstractNavigationApp { StaticMapsActivity.startActivity(activity, geocode, download, waypoint); return true; } - - - @Override - public boolean isEnabled(Geopoint geopoint) { - return false; - } - } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/CacheNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/CacheNavigationApp.java new file mode 100644 index 0000000..e47150f --- /dev/null +++ b/main/src/cgeo/geocaching/apps/cache/navi/CacheNavigationApp.java @@ -0,0 +1,17 @@ +package cgeo.geocaching.apps.cache.navi; + +import cgeo.geocaching.cgCache; +import cgeo.geocaching.apps.App; + +import android.app.Activity; + +/** + * interface for navigation to a cache + * + */ +public interface CacheNavigationApp extends App { + void navigate(final Activity activity, final cgCache cache); + + @Override + boolean isEnabled(final cgCache cache); +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/apps/cache/navi/CompassApp.java b/main/src/cgeo/geocaching/apps/cache/navi/CompassApp.java index 1576617..5275d53 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/CompassApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/CompassApp.java @@ -4,11 +4,13 @@ import cgeo.geocaching.R; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgWaypoint; import cgeo.geocaching.cgeonavigate; +import cgeo.geocaching.apps.AbstractApp; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.ui.Formatter; import android.app.Activity; -class CompassApp extends AbstractNavigationApp { +class CompassApp extends AbstractApp implements CacheNavigationApp, WaypointNavigationApp, GeopointNavigationApp { CompassApp() { super(getString(R.string.compass_title), null); @@ -20,22 +22,30 @@ class CompassApp extends AbstractNavigationApp { } @Override - public boolean invoke(Activity activity, cgCache cache, cgWaypoint waypoint, final Geopoint coords) { - - if (cache != null && cache.getGeocode() != null) { - cgeonavigate.startActivity(activity, cache.getGeocode(), cache.getName(), cache.getCoords(), null); - return true; - } - if (waypoint != null && waypoint.getCoords() != null) { - cgeonavigate.startActivity(activity, waypoint.getPrefix() + "/" + waypoint.getLookup(), waypoint.getName(), waypoint.getCoords(), null); - return true; - } - if (coords != null) { - cgeonavigate.startActivity(activity, getString(R.string.navigation_direct_navigation), getString(R.string.navigation_target), coords, null); - return true; - } - // search is not handled here - return false; + public void navigate(Activity activity, Geopoint coords) { + cgeonavigate.startActivity(activity, getString(R.string.navigation_direct_navigation), getString(R.string.navigation_target), coords, null); + } + + @Override + public void navigate(Activity activity, cgWaypoint waypoint) { + cgeonavigate.startActivity(activity, waypoint.getPrefix() + "/" + waypoint.getLookup(), waypoint.getName(), waypoint.getCoords(), null, + waypoint.getWaypointType().getL10n()); + } + + @Override + public boolean isEnabled(cgWaypoint waypoint) { + return waypoint.getCoords() != null; + } + + @Override + public void navigate(Activity activity, cgCache cache) { + cgeonavigate.startActivity(activity, cache.getGeocode(), cache.getName(), cache.getCoords(), null, + Formatter.formatCacheInfoShort(cache)); + } + + @Override + public boolean isEnabled(cgCache cache) { + return cache.getGeocode() != null; } }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/apps/cache/navi/DownloadStaticMapsApp.java b/main/src/cgeo/geocaching/apps/cache/navi/DownloadStaticMapsApp.java index 1625ef7..f093937 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/DownloadStaticMapsApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/DownloadStaticMapsApp.java @@ -3,7 +3,6 @@ package cgeo.geocaching.apps.cache.navi; import cgeo.geocaching.R; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgWaypoint; -import cgeo.geocaching.geopoint.Geopoint; import android.app.Activity; @@ -14,23 +13,22 @@ class DownloadStaticMapsApp extends AbstractStaticMapsApp { } @Override - public boolean invoke(Activity activity, cgCache cache, cgWaypoint waypoint, final Geopoint coords) { - return invoke(activity, cache, waypoint, true); - } - - @Override public boolean isEnabled(cgCache cache) { - if (cache == null) { - return false; - } return !hasStaticMap(cache); } @Override public boolean isEnabled(cgWaypoint waypoint) { - if (waypoint == null) { - return false; - } return !hasStaticMap(waypoint); } + + @Override + public void navigate(Activity activity, cgCache cache) { + invokeStaticMaps(activity, cache, null, true); + } + + @Override + public void navigate(Activity activity, cgWaypoint waypoint) { + invokeStaticMaps(activity, null, waypoint, true); + } } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/GeopointNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/GeopointNavigationApp.java new file mode 100644 index 0000000..fe4fd5d --- /dev/null +++ b/main/src/cgeo/geocaching/apps/cache/navi/GeopointNavigationApp.java @@ -0,0 +1,13 @@ +package cgeo.geocaching.apps.cache.navi; + +import cgeo.geocaching.geopoint.Geopoint; + +import android.app.Activity; + +/** + * interface for navigation to a coordinate. This one cannot be enabled/disabled. + * + */ +public interface GeopointNavigationApp { + void navigate(final Activity activity, final Geopoint coords); +} diff --git a/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsApp.java b/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsApp.java index 4e6e62b..eac33cc 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsApp.java @@ -21,7 +21,7 @@ class GoogleMapsApp extends AbstractPointNavigationApp { } @Override - protected void navigate(Activity activity, Geopoint point) { + public void navigate(Activity activity, Geopoint point) { // INFO: q parameter works with Google Maps, but breaks cooperation with all other apps try { activity.startActivity(new Intent(Intent.ACTION_VIEW, diff --git a/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java index 31d99e9..7258e11 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java @@ -3,8 +3,6 @@ package cgeo.geocaching.apps.cache.navi; import cgeo.geocaching.IGeoData; import cgeo.geocaching.R; import cgeo.geocaching.Settings; -import cgeo.geocaching.cgCache; -import cgeo.geocaching.cgWaypoint; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.geopoint.Geopoint; @@ -14,7 +12,7 @@ import android.app.Activity; import android.content.Intent; import android.net.Uri; -class GoogleNavigationApp extends AbstractNavigationApp { +class GoogleNavigationApp extends AbstractPointNavigationApp { GoogleNavigationApp() { super(getString(R.string.cache_menu_tbt), null); @@ -25,33 +23,8 @@ class GoogleNavigationApp extends AbstractNavigationApp { return true; } - @Override - public boolean invoke(final Activity activity, final cgCache cache, final cgWaypoint waypoint, final Geopoint coords) { - if (activity == null) { - return false; - } - + private static boolean navigateToCoordinates(Activity activity, final Geopoint coords) { IGeoData geo = cgeoapplication.getInstance().currentGeo(); - boolean navigationResult = false; - if (coords != null) { - navigationResult = navigateToCoordinates(geo, activity, coords); - } - else if (waypoint != null) { - navigationResult = navigateToCoordinates(geo, activity, waypoint.getCoords()); - } - else if (cache != null) { - navigationResult = navigateToCoordinates(geo, activity, cache.getCoords()); - } - - if (!navigationResult) { - ActivityMixin.showToast(activity, getString(R.string.err_navigation_no)); - return false; - } - - return true; - } - - private static boolean navigateToCoordinates(IGeoData geo, Activity activity, final Geopoint coords) { final Geopoint coordsNow = geo == null ? null : geo.getCoords(); // Google Navigation @@ -89,4 +62,10 @@ class GoogleNavigationApp extends AbstractNavigationApp { return false; } + @Override + public void navigate(Activity activity, Geopoint coords) { + if (!navigateToCoordinates(activity, coords)) { + ActivityMixin.showToast(activity, getString(R.string.err_navigation_no)); + } + } }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/apps/cache/navi/InternalMap.java b/main/src/cgeo/geocaching/apps/cache/navi/InternalMap.java index f1e03ed..8185f40 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/InternalMap.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/InternalMap.java @@ -3,37 +3,46 @@ package cgeo.geocaching.apps.cache.navi; import cgeo.geocaching.R; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgWaypoint; +import cgeo.geocaching.apps.AbstractApp; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.maps.CGeoMap; import android.app.Activity; -class InternalMap extends AbstractNavigationApp { +class InternalMap extends AbstractApp implements CacheNavigationApp, WaypointNavigationApp, GeopointNavigationApp { InternalMap() { super(getString(R.string.cache_menu_map), null); } @Override - public boolean invoke(Activity activity, cgCache cache, cgWaypoint waypoint, final Geopoint coords) { - if (cache != null) { - CGeoMap.startActivityGeoCode(activity, cache.getGeocode()); - // may need some code from CGeoMap.startActivitySearch(activity, search, cache != null ? cache.getGeocode() : null, true); - } - else if (waypoint != null) { - CGeoMap.startActivityCoords(activity, waypoint.getCoords(), waypoint.getWaypointType(), waypoint.getName()); - } - else if (coords != null) { - CGeoMap.startActivityCoords(activity, coords, WaypointType.WAYPOINT, null); - } - + public boolean isInstalled() { return true; } @Override - public boolean isInstalled() { - return true; + public void navigate(Activity activity, Geopoint coords) { + CGeoMap.startActivityCoords(activity, coords, WaypointType.WAYPOINT, null); } + @Override + public void navigate(Activity activity, cgWaypoint waypoint) { + CGeoMap.startActivityCoords(activity, waypoint.getCoords(), waypoint.getWaypointType(), waypoint.getName()); + } + + @Override + public boolean isEnabled(cgWaypoint waypoint) { + return waypoint.getCoords() != null; + } + + @Override + public void navigate(Activity activity, cgCache cache) { + CGeoMap.startActivityGeoCode(activity, cache.getGeocode()); + } + + @Override + public boolean isEnabled(cgCache cache) { + return cache.getCoords() != null; + } } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/LocusApp.java b/main/src/cgeo/geocaching/apps/cache/navi/LocusApp.java index d71a474..a20f2ce 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/LocusApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/LocusApp.java @@ -3,45 +3,30 @@ package cgeo.geocaching.apps.cache.navi; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgWaypoint; import cgeo.geocaching.apps.AbstractLocusApp; -import cgeo.geocaching.geopoint.Geopoint; import android.app.Activity; -import java.util.ArrayList; +import java.util.Collections; -class LocusApp extends AbstractLocusApp implements NavigationApp { +class LocusApp extends AbstractLocusApp implements CacheNavigationApp, WaypointNavigationApp { + + @Override + public boolean isEnabled(cgWaypoint waypoint) { + return waypoint.getCoords() != null; + } /** * Show a single cache with waypoints or a single waypoint in Locus. * This method constructs a list of cache and waypoints only. * - * @see AbstractLocusApp#showInLocus */ @Override - public boolean invoke(Activity activity, cgCache cache, cgWaypoint waypoint, final Geopoint coords) { - - final ArrayList<Object> points = new ArrayList<Object>(); - - // add cache if present - if (cache != null && cache.getCoords() != null) { - points.add(cache); - } - - // add waypoint if present - if (waypoint != null && waypoint.getCoords() != null) { - points.add(waypoint); - } - - return showInLocus(points, true, false, activity); - } - - @Override - public boolean isEnabled(cgWaypoint waypoint) { - return waypoint != null; + public void navigate(Activity activity, cgWaypoint waypoint) { + showInLocus(Collections.singletonList(waypoint), true, false, activity); } @Override - public boolean isEnabled(Geopoint geopoint) { - return geopoint != null; + public void navigate(Activity activity, cgCache cache) { + showInLocus(Collections.singletonList(cache), true, false, activity); } } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java index 95f6408..bf0517f 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java @@ -7,8 +7,8 @@ import cgeo.geocaching.cgWaypoint; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.apps.AbstractAppFactory; +import cgeo.geocaching.apps.App; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.utils.Log; import android.app.Activity; import android.app.AlertDialog; @@ -48,7 +48,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { /** The external navigon app */ NAVIGON(new NavigonApp(), 10); - NavigationAppsEnum(NavigationApp app, int id) { + NavigationAppsEnum(App app, int id) { this.app = app; this.id = id; } @@ -56,7 +56,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { /** * The app instance to use */ - public final NavigationApp app; + public final App app; /** * The id - used in c:geo settings */ @@ -119,7 +119,17 @@ public final class NavigationAppFactory extends AbstractAppFactory { for (NavigationAppsEnum navApp : getInstalledNavigationApps()) { if ((showInternalMap || !(navApp.app instanceof InternalMap)) && (showDefaultNavigation || defaultNavigationTool != navApp.id)) { - if (navApp.app.isEnabled(cache) || navApp.app.isEnabled(waypoint) || navApp.app.isEnabled(destination)) { + boolean add = false; + if (cache != null && navApp.app instanceof CacheNavigationApp && ((CacheNavigationApp) navApp.app).isEnabled(cache)) { + add = true; + } + if (waypoint != null && navApp.app instanceof WaypointNavigationApp && ((WaypointNavigationApp) navApp.app).isEnabled(waypoint)) { + add = true; + } + if (destination != null && navApp.app instanceof GeopointNavigationApp) { + add = true; + } + if (add) { items.add(navApp); } } @@ -134,7 +144,18 @@ public final class NavigationAppFactory extends AbstractAppFactory { @Override public void onClick(DialogInterface dialog, int item) { NavigationAppsEnum selectedItem = adapter.getItem(item); - selectedItem.app.invoke(activity, cache, waypoint, destination); + if (cache != null) { + CacheNavigationApp cacheApp = (CacheNavigationApp) selectedItem.app; + cacheApp.navigate(activity, cache); + } + else if (waypoint != null) { + WaypointNavigationApp waypointApp = (WaypointNavigationApp) selectedItem.app; + waypointApp.navigate(activity, waypoint); + } + else { + GeopointNavigationApp geopointApp = (GeopointNavigationApp) selectedItem.app; + geopointApp.navigate(activity, destination); + } } }); final AlertDialog alert = builder.create(); @@ -157,6 +178,21 @@ public final class NavigationAppFactory extends AbstractAppFactory { } /** + * Returns all installed navigation apps for default navigation. + * + * @return + */ + public static List<NavigationAppsEnum> getInstalledDefaultNavigationApps() { + final List<NavigationAppsEnum> installedNavigationApps = new ArrayList<NavigationAppsEnum>(); + for (NavigationAppsEnum appEnum : NavigationAppsEnum.values()) { + if (appEnum.app.isInstalled() && appEnum.app.isDefaultNavigationApp()) { + installedNavigationApps.add(appEnum); + } + } + return installedNavigationApps; + } + + /** * This offset is used to build unique menu ids to avoid collisions of ids in menus */ private static final int MENU_ITEM_OFFSET = 12345; @@ -174,16 +210,22 @@ public final class NavigationAppFactory extends AbstractAppFactory { */ public static void addMenuItems(final Menu menu, final cgCache cache) { for (NavigationAppsEnum navApp : getInstalledNavigationApps()) { - if (navApp.app.isEnabled(cache)) { - menu.add(0, MENU_ITEM_OFFSET + navApp.id, 0, navApp.app.getName()); + if (navApp.app instanceof CacheNavigationApp) { + CacheNavigationApp cacheApp = (CacheNavigationApp) navApp.app; + if (cacheApp.isEnabled(cache)) { + menu.add(0, MENU_ITEM_OFFSET + navApp.id, 0, navApp.app.getName()); + } } } } public static void addMenuItems(final Menu menu, final cgWaypoint waypoint) { for (NavigationAppsEnum navApp : getInstalledNavigationApps()) { - if (navApp.app.isEnabled(waypoint)) { - menu.add(0, MENU_ITEM_OFFSET + navApp.id, 0, navApp.app.getName()); + if (navApp.app instanceof WaypointNavigationApp) { + WaypointNavigationApp waypointApp = (WaypointNavigationApp) navApp.app; + if (waypointApp.isEnabled(waypoint)) { + menu.add(0, MENU_ITEM_OFFSET + navApp.id, 0, navApp.app.getName()); + } } } } @@ -197,29 +239,39 @@ public final class NavigationAppFactory extends AbstractAppFactory { * @return */ public static boolean onMenuItemSelected(final MenuItem item, Activity activity, cgCache cache) { - return invokeApp(activity, cache, null, null, getAppFromMenuItem(item)); + final App menuItem = getAppFromMenuItem(item); + navigateCache(activity, cache, menuItem); + return menuItem != null; + } + + private static void navigateCache(Activity activity, cgCache cache, App app) { + if (app instanceof CacheNavigationApp) { + CacheNavigationApp cacheApp = (CacheNavigationApp) app; + cacheApp.navigate(activity, cache); + } } public static boolean onMenuItemSelected(final MenuItem item, Activity activity, cgWaypoint waypoint) { - return invokeApp(activity, null, waypoint, null, getAppFromMenuItem(item)); + final App menuItem = getAppFromMenuItem(item); + navigateWaypoint(activity, waypoint, menuItem); + return menuItem != null; } - private static boolean invokeApp(Activity activity, cgCache cache, cgWaypoint waypoint, final Geopoint destination, final NavigationApp app) { - if (app == null) { - return false; + private static void navigateWaypoint(Activity activity, cgWaypoint waypoint, App app) { + if (app instanceof WaypointNavigationApp) { + WaypointNavigationApp waypointApp = (WaypointNavigationApp) app; + waypointApp.navigate(activity, waypoint); } - if (cache == null && waypoint == null && destination == null) { - return false; - } - try { - return app.invoke(activity, cache, waypoint, destination); - } catch (Exception e) { - Log.e("NavigationAppFactory.onMenuItemSelected: " + e.toString()); + } + + private static void navigateGeopoint(Activity activity, Geopoint destination, App app) { + if (app instanceof GeopointNavigationApp) { + GeopointNavigationApp geopointApp = (GeopointNavigationApp) app; + geopointApp.navigate(activity, destination); } - return false; } - private static NavigationApp getAppFromMenuItem(MenuItem item) { + private static App getAppFromMenuItem(MenuItem item) { final int id = item.getItemId(); for (NavigationAppsEnum navApp : NavigationAppsEnum.values()) { if (MENU_ITEM_OFFSET + navApp.id == id) { @@ -243,10 +295,10 @@ public final class NavigationAppFactory extends AbstractAppFactory { return; } - invokeApp(activity, cache, null, null, getDefaultNavigationApplication(defaultNavigation)); + navigateCache(activity, cache, getDefaultNavigationApplication(defaultNavigation)); } - private static NavigationApp getDefaultNavigationApplication(int defaultNavigation) { + private static App getDefaultNavigationApplication(int defaultNavigation) { if (defaultNavigation == 2) { return getNavigationAppFromSetting(Settings.getDefaultNavigationTool2()); } @@ -264,7 +316,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { ActivityMixin.showToast(activity, cgeoapplication.getInstance().getString(R.string.err_location_unknown)); return; } - invokeApp(activity, null, waypoint, null, getDefaultNavigationApplication(defaultNavigation)); + navigateWaypoint(activity, waypoint, getDefaultNavigationApplication(defaultNavigation)); } /** @@ -279,7 +331,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { return; } - invokeApp(activity, null, null, destination, getDefaultNavigationApplication(defaultNavigation)); + navigateGeopoint(activity, destination, getDefaultNavigationApplication(defaultNavigation)); } /** @@ -287,11 +339,11 @@ public final class NavigationAppFactory extends AbstractAppFactory { * * @return never <code>null</code> */ - public static NavigationApp getDefaultNavigationApplication() { + public static App getDefaultNavigationApplication() { return getNavigationAppFromSetting(Settings.getDefaultNavigationTool()); } - private static NavigationApp getNavigationAppFromSetting(final int defaultNavigationTool) { + private static App getNavigationAppFromSetting(final int defaultNavigationTool) { final List<NavigationAppsEnum> installedNavigationApps = getInstalledNavigationApps(); for (NavigationAppsEnum navigationApp : installedNavigationApps) { diff --git a/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java b/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java index 69f63dc..7ea86fb 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java @@ -16,7 +16,7 @@ class NavigonApp extends AbstractPointNavigationApp { } @Override - protected void navigate(Activity activity, Geopoint point) { + public void navigate(Activity activity, Geopoint point) { final Intent intent = new Intent(INTENT); /* diff --git a/main/src/cgeo/geocaching/apps/cache/navi/OruxMapsApp.java b/main/src/cgeo/geocaching/apps/cache/navi/OruxMapsApp.java index 27ec2fa..24ef81b 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/OruxMapsApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/OruxMapsApp.java @@ -15,7 +15,7 @@ class OruxMapsApp extends AbstractPointNavigationApp { } @Override - protected void navigate(Activity activity, Geopoint point) { + public void navigate(Activity activity, Geopoint point) { final Intent intent = new Intent(INTENT); intent.putExtra("latitude", point.getLatitude());//latitude, wgs84 datum intent.putExtra("longitude", point.getLongitude());//longitude, wgs84 datum diff --git a/main/src/cgeo/geocaching/apps/cache/navi/RMapsApp.java b/main/src/cgeo/geocaching/apps/cache/navi/RMapsApp.java index 8044cc6..a481813 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/RMapsApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/RMapsApp.java @@ -3,6 +3,7 @@ package cgeo.geocaching.apps.cache.navi; import cgeo.geocaching.R; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgWaypoint; +import cgeo.geocaching.apps.AbstractApp; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter.Format; @@ -11,7 +12,7 @@ import android.content.Intent; import java.util.ArrayList; -class RMapsApp extends AbstractNavigationApp { +class RMapsApp extends AbstractApp implements CacheNavigationApp, WaypointNavigationApp, GeopointNavigationApp { private static final String INTENT = "com.robert.maps.action.SHOW_POINTS"; @@ -20,29 +21,30 @@ class RMapsApp extends AbstractNavigationApp { } @Override - public boolean invoke(Activity activity, cgCache cache, cgWaypoint waypoint, final Geopoint coords) { - try { - final ArrayList<String> locations = new ArrayList<String>(); - if (cache != null && cache.getCoords() != null) { - locations.add(cache.getCoords().format(Format.LAT_LON_DECDEGREE_COMMA) - + ";" + cache.getGeocode() - + ";" + cache.getName()); - } else if (waypoint != null && waypoint.getCoords() != null) { - locations.add(waypoint.getCoords().format(Format.LAT_LON_DECDEGREE_COMMA) - + ";" + waypoint.getLookup() - + ";" + waypoint.getName()); - } - - if (!locations.isEmpty()) { - final Intent intent = new Intent(INTENT); - intent.putStringArrayListExtra("locations", locations); - activity.startActivity(intent); - return true; - } - } catch (Exception e) { - // nothing - } - - return false; + public void navigate(Activity activity, cgWaypoint waypoint) { + navigate(activity, waypoint.getCoords(), waypoint.getLookup(), waypoint.getName()); + } + + private static void navigate(Activity activity, Geopoint coords, String code, String name) { + final ArrayList<String> locations = new ArrayList<String>(); + locations.add(coords.format(Format.LAT_LON_DECDEGREE_COMMA) + ";" + code + ";" + name); + final Intent intent = new Intent(INTENT); + intent.putStringArrayListExtra("locations", locations); + activity.startActivity(intent); + } + + @Override + public boolean isEnabled(cgWaypoint waypoint) { + return waypoint.getCoords() != null; + } + + @Override + public void navigate(Activity activity, cgCache cache) { + navigate(activity, cache.getCoords(), cache.getGeocode(), cache.getName()); + } + + @Override + public void navigate(Activity activity, Geopoint coords) { + navigate(activity, coords, "", ""); } } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/RadarApp.java b/main/src/cgeo/geocaching/apps/cache/navi/RadarApp.java index 3374691..b01539c 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/RadarApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/RadarApp.java @@ -16,7 +16,7 @@ class RadarApp extends AbstractPointNavigationApp { } @Override - protected void navigate(Activity activity, Geopoint point) { + public void navigate(Activity activity, Geopoint point) { final Intent radarIntent = new Intent(INTENT); radarIntent.putExtra("latitude", (float) point.getLatitude()); radarIntent.putExtra("longitude", (float) point.getLongitude()); diff --git a/main/src/cgeo/geocaching/apps/cache/navi/StaticMapApp.java b/main/src/cgeo/geocaching/apps/cache/navi/StaticMapApp.java index d805656..6be027f 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/StaticMapApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/StaticMapApp.java @@ -3,7 +3,6 @@ package cgeo.geocaching.apps.cache.navi; import cgeo.geocaching.R; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgWaypoint; -import cgeo.geocaching.geopoint.Geopoint; import android.app.Activity; @@ -14,23 +13,22 @@ class StaticMapApp extends AbstractStaticMapsApp { } @Override - public boolean invoke(Activity activity, cgCache cache, cgWaypoint waypoint, final Geopoint coords) { - return invoke(activity, cache, waypoint, false); - } - - @Override public boolean isEnabled(cgCache cache) { - if (cache == null) { - return false; - } return hasStaticMap(cache); } @Override public boolean isEnabled(cgWaypoint waypoint) { - if (waypoint == null) { - return false; - } return hasStaticMap(waypoint); } + + @Override + public void navigate(Activity activity, cgCache cache) { + invokeStaticMaps(activity, cache, null, false); + } + + @Override + public void navigate(Activity activity, cgWaypoint waypoint) { + invokeStaticMaps(activity, null, waypoint, false); + } } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java b/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java index 41f958d..012b94f 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java @@ -22,7 +22,7 @@ class StreetviewApp extends AbstractPointNavigationApp { } @Override - protected void navigate(Activity activity, Geopoint point) { + public void navigate(Activity activity, Geopoint point) { try { activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("google.streetview:cbll=" + point.getLatitude() + "," + point.getLongitude()))); diff --git a/main/src/cgeo/geocaching/apps/cache/navi/WaypointNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/WaypointNavigationApp.java new file mode 100644 index 0000000..7d3a706 --- /dev/null +++ b/main/src/cgeo/geocaching/apps/cache/navi/WaypointNavigationApp.java @@ -0,0 +1,15 @@ +package cgeo.geocaching.apps.cache.navi; + +import cgeo.geocaching.cgWaypoint; + +import android.app.Activity; + +/** + * interface for navigation to a waypoint + * + */ +public interface WaypointNavigationApp { + void navigate(final Activity activity, final cgWaypoint waypoint); + + boolean isEnabled(final cgWaypoint waypoint); +} diff --git a/main/src/cgeo/geocaching/backup/CentralBackupAgent.java b/main/src/cgeo/geocaching/backup/CentralBackupAgent.java index aef2b7b..f6b9024 100644 --- a/main/src/cgeo/geocaching/backup/CentralBackupAgent.java +++ b/main/src/cgeo/geocaching/backup/CentralBackupAgent.java @@ -2,9 +2,11 @@ package cgeo.geocaching.backup; import cgeo.geocaching.Settings; +import android.annotation.TargetApi; import android.app.backup.BackupAgentHelper; import android.app.backup.SharedPreferencesBackupHelper; +@TargetApi(8) public class CentralBackupAgent extends BackupAgentHelper { private static final String PREFS_BACKUP_KEY = "prefs"; diff --git a/main/src/cgeo/geocaching/cgCache.java b/main/src/cgeo/geocaching/cgCache.java index c765678..b8590e5 100644 --- a/main/src/cgeo/geocaching/cgCache.java +++ b/main/src/cgeo/geocaching/cgCache.java @@ -1,6 +1,7 @@ package cgeo.geocaching; import cgeo.geocaching.cgData.StorageLocation; +import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.activity.IAbstractActivity; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; @@ -61,8 +62,8 @@ public class cgCache implements ICache, IWaypoint { private CacheType cacheType = CacheType.UNKNOWN; private String name = ""; private Spannable nameSp = null; - private String owner = ""; - private String ownerReal = ""; + private String ownerDisplayName = ""; + private String ownerUserId = ""; private Date hidden = null; private String hint = ""; private CacheSize size = CacheSize.UNKNOWN; @@ -189,11 +190,11 @@ public class cgCache implements ICache, IWaypoint { if (StringUtils.isBlank(nameSp)) { nameSp = other.nameSp; } - if (StringUtils.isBlank(owner)) { - owner = other.owner; + if (StringUtils.isBlank(ownerDisplayName)) { + ownerDisplayName = other.ownerDisplayName; } - if (StringUtils.isBlank(ownerReal)) { - ownerReal = other.ownerReal; + if (StringUtils.isBlank(ownerUserId)) { + ownerUserId = other.ownerUserId; } if (hidden == null) { hidden = other.hidden; @@ -316,8 +317,8 @@ public class cgCache implements ICache, IWaypoint { disabled == other.disabled && archived == other.archived && listId == other.listId && - StringUtils.equalsIgnoreCase(owner, other.owner) && - StringUtils.equalsIgnoreCase(ownerReal, other.ownerReal) && + StringUtils.equalsIgnoreCase(ownerDisplayName, other.ownerDisplayName) && + StringUtils.equalsIgnoreCase(ownerUserId, other.ownerUserId) && StringUtils.equalsIgnoreCase(description, other.description) && StringUtils.equalsIgnoreCase(personalNote, other.personalNote) && StringUtils.equalsIgnoreCase(shortdesc, other.shortdesc) && @@ -404,33 +405,33 @@ public class cgCache implements ICache, IWaypoint { ((Activity) fromActivity).startActivity(logVisitIntent); } - public void logOffline(final IAbstractActivity fromActivity, final LogType logType) { + public void logOffline(final Activity fromActivity, final LogType logType) { final boolean mustIncludeSignature = StringUtils.isNotBlank(Settings.getSignature()) && Settings.isAutoInsertSignature(); final String initial = mustIncludeSignature ? LogTemplateProvider.applyTemplates(Settings.getSignature(), true) : ""; logOffline(fromActivity, initial, Calendar.getInstance(), logType); } - void logOffline(final IAbstractActivity fromActivity, final String log, Calendar date, final LogType logType) { + void logOffline(final Activity fromActivity, final String log, Calendar date, final LogType logType) { if (logType == LogType.UNKNOWN) { return; } - cgeoapplication app = (cgeoapplication) ((Activity) fromActivity).getApplication(); + cgeoapplication app = (cgeoapplication) fromActivity.getApplication(); final boolean status = app.saveLogOffline(geocode, date.getTime(), logType, log); - Resources res = ((Activity) fromActivity).getResources(); + Resources res = fromActivity.getResources(); if (status) { - fromActivity.showToast(res.getString(R.string.info_log_saved)); + ActivityMixin.showToast(fromActivity, res.getString(R.string.info_log_saved)); app.saveVisitDate(geocode); logOffline = true; notifyChange(); } else { - fromActivity.showToast(res.getString(R.string.err_log_post_failed)); + ActivityMixin.showToast(fromActivity, res.getString(R.string.err_log_post_failed)); } } public List<LogType> getPossibleLogTypes() { - boolean isOwner = owner != null && owner.equalsIgnoreCase(Settings.getUsername()); + boolean isOwner = getOwnerUserId() != null && getOwnerUserId().equalsIgnoreCase(Settings.getUsername()); List<LogType> logTypes = new ArrayList<LogType>(); if (isEventCache()) { logTypes.add(LogType.WILL_ATTEND); @@ -506,8 +507,8 @@ public class cgCache implements ICache, IWaypoint { } @Override - public String getOwner() { - return owner; + public String getOwnerDisplayName() { + return ownerDisplayName; } @Override @@ -548,8 +549,8 @@ public class cgCache implements ICache, IWaypoint { } @Override - public String getOwnerReal() { - return ownerReal; + public String getOwnerUserId() { + return ownerUserId; } @Override @@ -988,12 +989,12 @@ public class cgCache implements ICache, IWaypoint { this.name = name; } - public void setOwner(String owner) { - this.owner = owner; + public void setOwnerDisplayName(String ownerDisplayName) { + this.ownerDisplayName = ownerDisplayName; } - public void setOwnerReal(String ownerReal) { - this.ownerReal = ownerReal; + public void setOwnerUserId(String ownerUserId) { + this.ownerUserId = ownerUserId; } public void setHint(String hint) { @@ -1403,11 +1404,11 @@ public class cgCache implements ICache, IWaypoint { if (getDifficulty() == 0.0) { Log.e("difficulty not parsed correctly"); } - if (StringUtils.isBlank(getOwner())) { - Log.e("owner not parsed correctly"); + if (StringUtils.isBlank(getOwnerDisplayName())) { + Log.e("owner display name not parsed correctly"); } - if (StringUtils.isBlank(getOwnerReal())) { - Log.e("owner real not parsed correctly"); + if (StringUtils.isBlank(getOwnerUserId())) { + Log.e("owner user id real not parsed correctly"); } if (getHiddenDate() == null) { Log.e("hidden not parsed correctly"); @@ -1549,4 +1550,45 @@ public class cgCache implements ICache, IWaypoint { return listId >= StoredList.STANDARD_LIST_ID; } -} + /** + * guess an event start time from the description + * + * @return start time in minutes after midnight + */ + public String guessEventTimeMinutes() { + if (!isEventCache()) { + return null; + } + // 12:34 + final Pattern time = Pattern.compile("\\b(\\d{1,2})\\:(\\d\\d)\\b"); + final Matcher matcher = time.matcher(getDescription()); + while (matcher.find()) { + try { + final int hours = Integer.valueOf(matcher.group(1)); + final int minutes = Integer.valueOf(matcher.group(2)); + if (hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60) { + return String.valueOf(hours * 60 + minutes); + } + } catch (NumberFormatException e) { + // cannot happen, but static code analysis doesn't know + } + } + // 12 o'clock + final String hourLocalized = cgeoapplication.getInstance().getString(R.string.cache_time_full_hours); + if (StringUtils.isNotBlank(hourLocalized)) { + final Pattern fullHours = Pattern.compile("\\b(\\d{1,2})\\s+" + Pattern.quote(hourLocalized), Pattern.CASE_INSENSITIVE); + final Matcher matcherHours = fullHours.matcher(getDescription()); + if (matcherHours.find()) { + try { + final int hours = Integer.valueOf(matcherHours.group(1)); + if (hours >= 0 && hours < 24) { + return String.valueOf(hours * 60); + } + } catch (NumberFormatException e) { + // cannot happen, but static code analysis doesn't know + } + } + } + return null; + } +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/cgData.java b/main/src/cgeo/geocaching/cgData.java index 4831524..53ac334 100644 --- a/main/src/cgeo/geocaching/cgData.java +++ b/main/src/cgeo/geocaching/cgData.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.res.Resources; import android.database.Cursor; +import android.database.DatabaseUtils; import android.database.DatabaseUtils.InsertHelper; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; @@ -1003,8 +1004,8 @@ public class cgData { values.put("type", cache.getType().id); values.put("name", cache.getName()); values.put("own", cache.isOwn() ? 1 : 0); - values.put("owner", cache.getOwner()); - values.put("owner_real", cache.getOwnerReal()); + values.put("owner", cache.getOwnerDisplayName()); + values.put("owner_real", cache.getOwnerUserId()); if (cache.getHiddenDate() == null) { values.put("hidden", 0); } else { @@ -1467,22 +1468,28 @@ public class cgData { init(); - final Cursor cursor = database.query( - dbTableCaches, - CACHE_COLUMNS, - cgData.whereGeocodeIn(geocodes), - null, - null, - null, - null, - null); + final StringBuilder query = new StringBuilder("SELECT "); + for (int i = 0; i < CACHE_COLUMNS.length; i++) { + query.append(i > 0 ? ", " : "").append(dbTableCaches).append('.').append(CACHE_COLUMNS[i]).append(' '); + } + query.append(',').append(dbTableLogsOffline).append(".log"); + query.append(" FROM ").append(dbTableCaches); + if (loadFlags.contains(LoadFlag.LOAD_OFFLINE_LOG)) { + query.append(" LEFT OUTER JOIN ").append(dbTableLogsOffline).append(" ON ( ").append(dbTableCaches).append(".geocode == ").append(dbTableLogsOffline).append(".geocode) "); + } + + query.append(" WHERE ").append(dbTableCaches).append('.'); + query.append(cgData.whereGeocodeIn(geocodes)); + + Cursor cursor = database.rawQuery(query.toString(), null); try { if (!cursor.moveToFirst()) { return Collections.emptySet(); } final Set<cgCache> caches = new HashSet<cgCache>(); + int logIndex = -1; do { //Extracted Method = LOADDBMINIMAL cgCache cache = cgData.createCacheFromDatabaseContent(cursor); @@ -1532,7 +1539,10 @@ public class cgData { } if (loadFlags.contains(LoadFlag.LOAD_OFFLINE_LOG)) { - cache.setLogOffline(hasLogOffline(cache.getGeocode())); + if (logIndex < 0) { + logIndex = cursor.getColumnIndex("log"); + } + cache.setLogOffline(!cursor.isNull(logIndex)); } cache.addStorageLocation(StorageLocation.DATABASE); cacheCache.putCacheInCache(cache); @@ -1545,6 +1555,7 @@ public class cgData { } } + /** * Builds a where for a viewport with the size enhanced by 50%. * @@ -1624,8 +1635,8 @@ public class cgData { cache.setType(CacheType.getById(cursor.getString(cacheColumnIndex[8]))); cache.setName(cursor.getString(cacheColumnIndex[9])); cache.setOwn(cursor.getInt(cacheColumnIndex[10]) == 1); - cache.setOwner(cursor.getString(cacheColumnIndex[11])); - cache.setOwnerReal(cursor.getString(cacheColumnIndex[12])); + cache.setOwnerDisplayName(cursor.getString(cacheColumnIndex[11])); + cache.setOwnerUserId(cursor.getString(cacheColumnIndex[12])); long dateValue = cursor.getLong(cacheColumnIndex[13]); if (dateValue != 0) { cache.setHidden(new Date(dateValue)); @@ -2116,13 +2127,13 @@ public class cgData { if (cacheType == CacheType.ALL) { sql = "select count(_id) from " + dbTableCaches + listSql; } else { - sql = "select count(_id) from " + dbTableCaches + " where type = \"" + cacheType.id + "\"" + listSqlW; + sql = "select count(_id) from " + dbTableCaches + " where type = " + DatabaseUtils.sqlEscapeString(cacheType.id) + listSqlW; } } else { if (cacheType == CacheType.ALL) { sql = "select count(_id) from " + dbTableCaches + " where detailed = 1" + listSqlW; } else { - sql = "select count(_id) from " + dbTableCaches + " where detailed = 1 and type = \"" + cacheType.id + "\"" + listSqlW; + sql = "select count(_id) from " + dbTableCaches + " where detailed = 1 and type = " + DatabaseUtils.sqlEscapeString(cacheType.id) + listSqlW; } } SQLiteStatement compiledStmnt = database.compileStatement(sql); @@ -2179,9 +2190,8 @@ public class cgData { } if (cacheType != CacheType.ALL) { - specifySql.append(" and type = \""); - specifySql.append(cacheType.id); - specifySql.append('"'); + specifySql.append(" and type = "); + specifySql.append(DatabaseUtils.sqlEscapeString(cacheType.id)); } try { @@ -2237,9 +2247,8 @@ public class cgData { specifySql.append(" and detailed = 1"); } if (cacheType != CacheType.ALL) { - specifySql.append(" and type = \""); - specifySql.append(cacheType.id); - specifySql.append('"'); + specifySql.append(" and type = "); + specifySql.append(DatabaseUtils.sqlEscapeString(cacheType.id)); } try { @@ -2312,9 +2321,8 @@ public class cgData { // cacheType limitation if (cacheType != CacheType.ALL) { - where.append(" and type = \""); - where.append(cacheType.id); - where.append('"'); + where.append(" and type = "); + where.append(DatabaseUtils.sqlEscapeString(cacheType.id)); } // offline caches only @@ -2458,7 +2466,7 @@ public class cgData { // Drop caches from the database final ArrayList<String> quotedGeocodes = new ArrayList<String>(geocodes.size()); for (final String geocode : geocodes) { - quotedGeocodes.add('"' + geocode + '"'); + quotedGeocodes.add(DatabaseUtils.sqlEscapeString(geocode)); } final String geocodeList = StringUtils.join(quotedGeocodes.toArray(), ','); final String baseWhereClause = "geocode in (" + geocodeList + ")"; @@ -2470,7 +2478,7 @@ public class cgData { database.delete(dbTableLogs, baseWhereClause, null); database.delete(dbTableLogCount, baseWhereClause, null); database.delete(dbTableLogsOffline, baseWhereClause, null); - database.delete(dbTableWaypoints, baseWhereClause + " and type <> \"own\"", null); + database.delete(dbTableWaypoints, baseWhereClause + " and type <> 'own'", null); database.delete(dbTableTrackables, baseWhereClause, null); database.setTransactionSuccessful(); } finally { @@ -2486,41 +2494,29 @@ public class cgData { public boolean saveLogOffline(String geocode, Date date, LogType type, String log) { if (StringUtils.isBlank(geocode)) { + Log.e("cgData.saveLogOffline: cannot log a blank geocode"); return false; } if (LogType.UNKNOWN == type && StringUtils.isBlank(log)) { + Log.e("cgData.saveLogOffline: cannot log an unknown log type and no message"); return false; } init(); - boolean status = false; - ContentValues values = new ContentValues(); + final ContentValues values = new ContentValues(); values.put("geocode", geocode); values.put("updated", System.currentTimeMillis()); values.put("type", type.id); values.put("log", log); values.put("date", date.getTime()); - try { - if (hasLogOffline(geocode)) { - final int rows = database.update(dbTableLogsOffline, values, "geocode = ?", new String[] { geocode }); - - if (rows > 0) { - status = true; - } - } else { - final long id = database.insert(dbTableLogsOffline, null, values); - - if (id > 0) { - status = true; - } - } - } catch (Exception e) { - Log.e("cgData.saveLogOffline: " + e.toString()); + if (hasLogOffline(geocode)) { + final int rows = database.update(dbTableLogsOffline, values, "geocode = ?", new String[] { geocode }); + return rows > 0; } - - return status; + final long id = database.insert(dbTableLogsOffline, null, values); + return id != -1; } public LogEntry loadLogOffline(String geocode) { @@ -2917,14 +2913,10 @@ public class cgData { if (all.length() > 0) { all.append(", "); } - all.append('"'); - all.append(geocode); - all.append('"'); + all.append(DatabaseUtils.sqlEscapeString(geocode)); } - where.append("geocode in ("); - where.append(all); - where.append(')'); + where.append("geocode in (").append(all).append(')'); } return where.toString(); diff --git a/main/src/cgeo/geocaching/cgWaypoint.java b/main/src/cgeo/geocaching/cgWaypoint.java index c847b24..af6bab5 100644 --- a/main/src/cgeo/geocaching/cgWaypoint.java +++ b/main/src/cgeo/geocaching/cgWaypoint.java @@ -12,7 +12,7 @@ import java.util.List; public class cgWaypoint implements IWaypoint, Comparable<cgWaypoint> { - static final String PREFIX_OWN = "OWN"; + public static final String PREFIX_OWN = "OWN"; private static final int ORDER_UNDEFINED = -2; private int id = 0; private String geocode = "geocode"; diff --git a/main/src/cgeo/geocaching/cgeo.java b/main/src/cgeo/geocaching/cgeo.java index 1c81a63..5bf728f 100644 --- a/main/src/cgeo/geocaching/cgeo.java +++ b/main/src/cgeo/geocaching/cgeo.java @@ -6,8 +6,7 @@ import cgeo.geocaching.connector.gc.Login; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.HumanDistance; -import cgeo.geocaching.geopoint.IConversion; +import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.maps.CGeoMap; import cgeo.geocaching.network.StatusUpdater.Status; import cgeo.geocaching.ui.Formatter; @@ -628,11 +627,7 @@ public class cgeo extends AbstractActivity { if (geo.getAccuracy() >= 0) { int speed = Math.round(geo.getSpeed()) * 60 * 60 / 1000; - if (Settings.isUseMetricUnits()) { - navAccuracy.setText("±" + Math.round(geo.getAccuracy()) + " m" + Formatter.SEPARATOR + speed + " km/h"); - } else { - navAccuracy.setText("±" + Math.round(geo.getAccuracy() * IConversion.METERS_TO_FEET) + " ft" + Formatter.SEPARATOR + speed / IConversion.MILES_TO_KILOMETER + " mph"); - } + navAccuracy.setText("±" + Units.getDistanceFromMeters(geo.getAccuracy()) + Formatter.SEPARATOR + Units.getSpeed(speed)); } else { navAccuracy.setText(null); } @@ -646,7 +641,7 @@ public class cgeo extends AbstractActivity { } } else { if (geo.getAltitude() != 0.0) { - final String humanAlt = HumanDistance.getHumanDistance((float) geo.getAltitude() / 1000); + final String humanAlt = Units.getDistanceFromKilometers((float) geo.getAltitude() / 1000); navLocation.setText(geo.getCoords() + " | " + humanAlt); } else { navLocation.setText(geo.getCoords().toString()); diff --git a/main/src/cgeo/geocaching/cgeocaches.java b/main/src/cgeo/geocaching/cgeocaches.java index 3be48ca..340122a 100644 --- a/main/src/cgeo/geocaching/cgeocaches.java +++ b/main/src/cgeo/geocaching/cgeocaches.java @@ -12,7 +12,6 @@ import cgeo.geocaching.connector.gc.SearchHandler; import cgeo.geocaching.enumerations.CacheListType; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; -import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.export.ExportFactory; import cgeo.geocaching.files.GPXImporter; @@ -24,27 +23,17 @@ import cgeo.geocaching.network.Cookies; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.sorting.CacheComparator; -import cgeo.geocaching.sorting.DateComparator; -import cgeo.geocaching.sorting.DifficultyComparator; +import cgeo.geocaching.sorting.ComparatorUserInterface; import cgeo.geocaching.sorting.EventDateComparator; -import cgeo.geocaching.sorting.FindsComparator; -import cgeo.geocaching.sorting.GeocodeComparator; -import cgeo.geocaching.sorting.InventoryComparator; -import cgeo.geocaching.sorting.NameComparator; -import cgeo.geocaching.sorting.PopularityComparator; -import cgeo.geocaching.sorting.RatingComparator; -import cgeo.geocaching.sorting.SizeComparator; -import cgeo.geocaching.sorting.StateComparator; -import cgeo.geocaching.sorting.StorageTimeComparator; -import cgeo.geocaching.sorting.TerrainComparator; import cgeo.geocaching.sorting.VisitComparator; -import cgeo.geocaching.sorting.VoteComparator; import cgeo.geocaching.ui.CacheListAdapter; +import cgeo.geocaching.ui.LoggingUI; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.RunnableWithArgument; import ch.boye.httpclientandroidlib.HttpResponse; + import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -71,10 +60,8 @@ import android.widget.TextView; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; public class cgeocaches extends AbstractListActivity { @@ -93,17 +80,7 @@ public class cgeocaches extends AbstractListActivity { private static final int MENU_CREATE_LIST = 7; private static final int MENU_DROP_LIST = 8; private static final int MENU_INVERT_SELECTION = 9; - private static final int MENU_SORT_DISTANCE = 10; - private static final int MENU_SORT_DIFFICULTY = 11; - private static final int MENU_SORT_TERRAIN = 12; - private static final int MENU_SORT_SIZE = 13; - private static final int MENU_SORT_FAVORITES = 14; - private static final int MENU_SORT_NAME = 15; - private static final int MENU_SORT_GEOCODE = 16; private static final int MENU_SWITCH_LIST = 17; - private static final int MENU_SORT_RATING = 18; - private static final int MENU_SORT_VOTE = 19; - private static final int MENU_SORT_INVENTORY = 20; private static final int MENU_IMPORT_WEB = 21; private static final int MENU_EXPORT = 22; private static final int MENU_REMOVE_FROM_HISTORY = 23; @@ -113,18 +90,14 @@ public class cgeocaches extends AbstractListActivity { private static final int SUBMENU_SHOW_MAP = 54; private static final int SUBMENU_MANAGE_LISTS = 55; private static final int SUBMENU_MANAGE_OFFLINE = 56; - private static final int SUBMENU_SORT = 57; + private static final int MENU_SORT = 57; private static final int SUBMENU_MANAGE_HISTORY = 60; - private static final int MENU_SORT_DATE = 61; - private static final int MENU_SORT_FINDS = 62; - private static final int MENU_SORT_STATE = 63; private static final int MENU_RENAME_LIST = 64; private static final int MENU_DROP_CACHES_AND_LIST = 65; private static final int MENU_DEFAULT_NAVIGATION = 66; private static final int MENU_NAVIGATION = 69; private static final int MENU_STORE_CACHE = 73; private static final int MENU_FILTER = 74; - private static final int MENU_SORT_STORAGE = 75; private static final int MSG_DONE = -1; private static final int MSG_RESTART_GEO_AND_DIR = -2; @@ -133,7 +106,6 @@ public class cgeocaches extends AbstractListActivity { private String action = null; private CacheListType type = null; private Geopoint coords = null; - private CacheType cacheType = Settings.getCacheType(); private SearchResult search = null; private final List<cgCache> cacheList = new ArrayList<cgCache>(); private CacheListAdapter adapter = null; @@ -141,7 +113,6 @@ public class cgeocaches extends AbstractListActivity { private View listFooter = null; private TextView listFooterText = null; private final Progress progress = new Progress(); - private Float northHeading = 0f; private String title = ""; private int detailTotal = 0; private int detailProgress = 0; @@ -151,41 +122,31 @@ public class cgeocaches extends AbstractListActivity { private int listId = StoredList.TEMPORARY_LIST_ID; // Only meaningful for the OFFLINE type private final GeoDirHandler geoDirHandler = new GeoDirHandler() { - @Override + @Override public void updateGeoData(final IGeoData geo) { if (adapter == null) { return; } - try { - if (geo.getCoords() != null) { - adapter.setActualCoordinates(geo.getCoords()); - } - - if (!Settings.isUseCompass() || geo.getSpeed() > 5) { // use GPS when speed is higher than 18 km/h - if (!Settings.isUseCompass()) { - adapter.setActualHeading(geo.getBearing()); - } - if (northHeading != null) { - adapter.setActualHeading(northHeading); - } - } - } catch (Exception e) { - Log.w("Failed to UpdateLocation location."); + if (geo.getCoords() != null) { + adapter.setActualCoordinates(geo.getCoords()); + } + if (!Settings.isUseCompass() || geo.getSpeed() > 5) { // use GPS when speed is higher than 18 km/h + adapter.setActualHeading(geo.getBearing()); } } - @Override - public void updateDirection(final float direction) { - if (!Settings.isLiveList()) { - return; - } + @Override + public void updateDirection(final float direction) { + if (adapter == null || !Settings.isLiveList()) { + return; + } - northHeading = DirectionProvider.getDirectionNow(cgeocaches.this, direction); - if (northHeading != null && adapter != null && (app.currentGeo().getSpeed() <= 5)) { // use compass when speed is lower than 18 km/h) { - adapter.setActualHeading(northHeading); - } - } + if (app.currentGeo().getSpeed() <= 5) { // use compass when speed is lower than 18 km/h) { + final float northHeading = DirectionProvider.getDirectionNow(cgeocaches.this, direction); + adapter.setActualHeading(northHeading); + } + } }; private ContextMenuInfo lastMenuInfo; @@ -202,8 +163,6 @@ public class cgeocaches extends AbstractListActivity { try { if (search != null) { setTitle(title + " [" + search.getCount() + ']'); - cacheList.clear(); - cacheList.addAll(search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB)); } else { setTitle(title); } @@ -248,11 +207,7 @@ public class cgeocaches extends AbstractListActivity { return; } - final Geopoint coordsNow = app.currentGeo().getCoords(); - if (coordsNow != null) { - adapter.setActualCoordinates(coordsNow); - adapter.setActualHeading(northHeading); - } + setAdapterCurrentCoordinates(false); } catch (Exception e) { showToast(res.getString(R.string.err_detail_cache_find_any)); Log.e("cgeocaches.loadCachesHandler: " + e.toString()); @@ -283,8 +238,7 @@ public class cgeocaches extends AbstractListActivity { try { if (search != null) { setTitle(title + " [" + search.getCount() + "]"); - cacheList.clear(); - cacheList.addAll(search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB)); + replaceCacheListFromSearch(); if (adapter != null) { adapter.reFilter(); } @@ -307,11 +261,7 @@ public class cgeocaches extends AbstractListActivity { return; } - final Geopoint coordsNow = app.currentGeo().getCoords(); - if (coordsNow != null) { - adapter.setActualCoordinates(coordsNow); - adapter.setActualHeading(northHeading); - } + setAdapterCurrentCoordinates(false); } catch (Exception e) { showToast(res.getString(R.string.err_detail_cache_find_next)); Log.e("cgeocaches.loadNextPageHandler: " + e.toString()); @@ -325,6 +275,12 @@ public class cgeocaches extends AbstractListActivity { } } }; + + private void replaceCacheListFromSearch() { + cacheList.clear(); + cacheList.addAll(search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB)); + } + private Handler loadDetailsHandler = new Handler() { @Override @@ -364,11 +320,7 @@ public class cgeocaches extends AbstractListActivity { } } - final Geopoint coordsNow = app.currentGeo().getCoords(); - if (coordsNow != null) { - adapter.setActualCoordinates(coordsNow); - adapter.setActualHeading(northHeading); - } + setAdapterCurrentCoordinates(false); showProgress(false); progress.dismiss(); @@ -410,8 +362,7 @@ public class cgeocaches extends AbstractListActivity { adapter.setSelectMode(false); } - cacheList.clear(); - cacheList.addAll(search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB)); + replaceCacheListFromSearch(); progress.dismiss(); } @@ -428,8 +379,7 @@ public class cgeocaches extends AbstractListActivity { refreshCurrentList(); - cacheList.clear(); - cacheList.addAll(search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB)); + replaceCacheListFromSearch(); progress.dismiss(); } @@ -487,7 +437,6 @@ public class cgeocaches extends AbstractListActivity { else { extras = new Bundle(); } - cacheType = Settings.getCacheType(); if (isInvokedFromAttachment()) { type = CacheListType.OFFLINE; if (coords == null) { @@ -613,8 +562,8 @@ public class cgeocaches extends AbstractListActivity { title = res.getString(R.string.map_map); setTitle(title); showProgress(true); - SearchResult result = (SearchResult) extras.get(EXTRAS_SEARCH); - search = new SearchResult(result); + search = (SearchResult) extras.get(EXTRAS_SEARCH); + replaceCacheListFromSearch(); loadCachesHandler.sendMessage(Message.obtain()); break; default: @@ -668,27 +617,33 @@ public class cgeocaches extends AbstractListActivity { if (adapter != null) { adapter.setSelectMode(false); - final Geopoint coordsNow = app.currentGeo().getCoords(); - if (coordsNow != null) { - adapter.setActualCoordinates(coordsNow); - adapter.setActualHeading(northHeading); - adapter.forceSort(); - } + setAdapterCurrentCoordinates(true); } if (loadCachesHandler != null && search != null) { + replaceCacheListFromSearch(); loadCachesHandler.sendEmptyMessage(0); } // refresh standard list if it has changed (new caches downloaded) if (type == CacheListType.OFFLINE && listId >= StoredList.STANDARD_LIST_ID && search != null) { - SearchResult newSearch = cgeoapplication.getInstance().getBatchOfStoredCaches(true, coords, cacheType, listId); + SearchResult newSearch = cgeoapplication.getInstance().getBatchOfStoredCaches(true, coords, Settings.getCacheType(), listId); if (newSearch != null && newSearch.getTotal() != search.getTotal()) { refreshCurrentList(); } } } + private void setAdapterCurrentCoordinates(final boolean forceSort) { + final Geopoint coordsNow = app.currentGeo().getCoords(); + if (coordsNow != null) { + adapter.setActualCoordinates(coordsNow); + if (forceSort) { + adapter.forceSort(); + } + } + } + @Override public void onDestroy() { if (adapter != null) { @@ -710,34 +665,7 @@ public class cgeocaches extends AbstractListActivity { menu.add(0, MENU_FILTER, 0, res.getString(R.string.caches_filter)).setIcon(R.drawable.ic_menu_filter); if (type != CacheListType.HISTORY) { - final SubMenu subMenuSort = menu.addSubMenu(0, SUBMENU_SORT, 0, res.getString(R.string.caches_sort)).setIcon(R.drawable.ic_menu_sort_alphabetically); - subMenuSort.setHeaderTitle(res.getString(R.string.caches_sort_title)); - - // sort the context menu labels alphabetically for easier reading - final Map<String, Integer> comparators = new HashMap<String, Integer>(); - comparators.put(res.getString(R.string.caches_sort_distance), MENU_SORT_DISTANCE); - comparators.put(res.getString(R.string.caches_sort_difficulty), MENU_SORT_DIFFICULTY); - comparators.put(res.getString(R.string.caches_sort_terrain), MENU_SORT_TERRAIN); - comparators.put(res.getString(R.string.caches_sort_size), MENU_SORT_SIZE); - comparators.put(res.getString(R.string.caches_sort_favorites), MENU_SORT_FAVORITES); - comparators.put(res.getString(R.string.caches_sort_name), MENU_SORT_NAME); - comparators.put(res.getString(R.string.caches_sort_gccode), MENU_SORT_GEOCODE); - comparators.put(res.getString(R.string.caches_sort_rating), MENU_SORT_RATING); - comparators.put(res.getString(R.string.caches_sort_vote), MENU_SORT_VOTE); - comparators.put(res.getString(R.string.caches_sort_inventory), MENU_SORT_INVENTORY); - comparators.put(res.getString(R.string.caches_sort_date), MENU_SORT_DATE); - comparators.put(res.getString(R.string.caches_sort_finds), MENU_SORT_FINDS); - comparators.put(res.getString(R.string.caches_sort_state), MENU_SORT_STATE); - comparators.put(res.getString(R.string.caches_sort_storage), MENU_SORT_STORAGE); - - final List<String> sortedLabels = new ArrayList<String>(comparators.keySet()); - Collections.sort(sortedLabels); - for (String label : sortedLabels) { - Integer id = comparators.get(label); - subMenuSort.add(1, id, 0, label).setCheckable(true).setChecked(id == MENU_SORT_DISTANCE); - } - - subMenuSort.setGroupCheckable(1, true, true); + menu.add(0, MENU_SORT, 0, res.getString(R.string.caches_sort)).setIcon(R.drawable.ic_menu_sort_alphabetically); } menu.add(0, MENU_SWITCH_SELECT_MODE, 0, res.getString(R.string.caches_select_mode)).setIcon(R.drawable.ic_menu_agenda); @@ -806,7 +734,7 @@ public class cgeocaches extends AbstractListActivity { setVisible(menu, MENU_SWITCH_SELECT_MODE, !isEmpty); setVisible(menu, SUBMENU_MANAGE_HISTORY, !isEmpty); setVisible(menu, SUBMENU_SHOW_MAP, !isEmpty); - setVisible(menu, SUBMENU_SORT, !isEmpty); + setVisible(menu, MENU_SORT, !isEmpty); setVisible(menu, MENU_REFRESH_STORED, !isEmpty && (isConcrete || type != CacheListType.OFFLINE)); setVisible(menu, MENU_DROP_CACHES, !isEmpty); setVisible(menu, MENU_DROP_CACHES_AND_LIST, isConcrete && !isEmpty); @@ -916,52 +844,10 @@ public class cgeocaches extends AbstractListActivity { } invalidateOptionsMenuCompatible(); return false; - case MENU_SORT_DISTANCE: - setComparator(item, null); - return false; - case MENU_SORT_DIFFICULTY: - setComparator(item, new DifficultyComparator()); - return false; - case MENU_SORT_TERRAIN: - setComparator(item, new TerrainComparator()); - return false; - case MENU_SORT_SIZE: - setComparator(item, new SizeComparator()); - return false; - case MENU_SORT_FAVORITES: - setComparator(item, new PopularityComparator()); - return false; - case MENU_SORT_NAME: - setComparator(item, new NameComparator()); - return false; - case MENU_SORT_GEOCODE: - setComparator(item, new GeocodeComparator()); - return false; - case MENU_SORT_STORAGE: - setComparator(item, new StorageTimeComparator()); - return false; case MENU_SWITCH_LIST: selectList(null); invalidateOptionsMenuCompatible(); return false; - case MENU_SORT_RATING: - setComparator(item, new RatingComparator()); - return false; - case MENU_SORT_VOTE: - setComparator(item, new VoteComparator()); - return false; - case MENU_SORT_INVENTORY: - setComparator(item, new InventoryComparator()); - return false; - case MENU_SORT_DATE: - setComparator(item, new DateComparator()); - return true; - case MENU_SORT_FINDS: - setComparator(item, new FindsComparator(app)); - return true; - case MENU_SORT_STATE: - setComparator(item, new StateComparator()); - return true; case MENU_FILTER: new FilterUserInterface(this).selectFilter(new RunnableWithArgument<IFilter>() { @Override @@ -978,6 +864,14 @@ public class cgeocaches extends AbstractListActivity { } }); return true; + case MENU_SORT: + new ComparatorUserInterface(this).selectComparator(adapter.getCacheComparator(), new RunnableWithArgument<CacheComparator>() { + @Override + public void run(CacheComparator selectedComparator) { + setComparator(selectedComparator); + } + }); + return true; case MENU_IMPORT_WEB: importWeb(); return false; @@ -997,12 +891,10 @@ public class cgeocaches extends AbstractListActivity { return CacheListAppFactory.onMenuItemSelected(item, cacheList, this, search); } - private void setComparator(MenuItem item, - CacheComparator comparator) { + private void setComparator(final CacheComparator comparator) { if (adapter != null) { adapter.setComparator(comparator); } - item.setChecked(true); } @Override @@ -1032,7 +924,7 @@ public class cgeocaches extends AbstractListActivity { if (cache.getCoords() != null) { menu.add(0, MENU_DEFAULT_NAVIGATION, 0, NavigationAppFactory.getDefaultNavigationApplication().getName()); menu.add(1, MENU_NAVIGATION, 0, res.getString(R.string.cache_menu_navigate)).setIcon(R.drawable.ic_menu_mapmode); - addVisitMenu(menu, cache); + LoggingUI.addMenuItems(menu, cache); menu.add(0, MENU_CACHE_DETAILS, 0, res.getString(R.string.cache_menu_details)); } if (cache.isOffline()) { @@ -1094,9 +986,6 @@ public class cgeocaches extends AbstractListActivity { case MENU_NAVIGATION: NavigationAppFactory.showNavigationMenu(this, cache, null, null); break; - case MENU_LOG_VISIT: - cache.logVisit(this); - break; case MENU_CACHE_DETAILS: final Intent cachesIntent = new Intent(this, CacheDetailActivity.class); cachesIntent.putExtra("geocode", cache.getGeocode().toUpperCase()); @@ -1107,6 +996,7 @@ public class cgeocaches extends AbstractListActivity { cache.drop(new Handler() { @Override public void handleMessage(Message msg) { + adapter.notifyDataSetChanged(); refreshCurrentList(); } }); @@ -1134,8 +1024,7 @@ public class cgeocaches extends AbstractListActivity { // in Android: // https://code.google.com/p/android/issues/detail?id=7139 lastMenuInfo = info; - // create a search for a single cache (as if in details view) - cache.logOffline(this, LogType.getById(id - MENU_LOG_VISIT_OFFLINE)); + LoggingUI.onMenuItemSelected(item, this, cache); } return true; @@ -1381,6 +1270,7 @@ public class cgeocaches extends AbstractListActivity { @Override public void run() { search = cgeoapplication.getInstance().getBatchOfStoredCaches(true, coords, Settings.getCacheType(), listId); + replaceCacheListFromSearch(); loadCachesHandler.sendMessage(Message.obtain()); } } @@ -1389,6 +1279,7 @@ public class cgeocaches extends AbstractListActivity { @Override public void run() { search = cgeoapplication.getInstance().getHistoryOfCaches(true, coords != null ? Settings.getCacheType() : CacheType.ALL); + replaceCacheListFromSearch(); loadCachesHandler.sendMessage(Message.obtain()); } } @@ -1414,7 +1305,8 @@ public class cgeocaches extends AbstractListActivity { @Override public void runSearch() { - search = GCParser.searchByCoords(coords, cacheType, Settings.isShowCaptcha()); + search = GCParser.searchByCoords(coords, Settings.getCacheType(), Settings.isShowCaptcha()); + replaceCacheListFromSearch(); } } @@ -1428,7 +1320,8 @@ public class cgeocaches extends AbstractListActivity { @Override public void runSearch() { - search = GCParser.searchByKeyword(keyword, cacheType, Settings.isShowCaptcha()); + search = GCParser.searchByKeyword(keyword, Settings.getCacheType(), Settings.isShowCaptcha()); + replaceCacheListFromSearch(); } } @@ -1442,7 +1335,8 @@ public class cgeocaches extends AbstractListActivity { @Override public void runSearch() { - search = GCParser.searchByUsername(username, cacheType, Settings.isShowCaptcha()); + search = GCParser.searchByUsername(username, Settings.getCacheType(), Settings.isShowCaptcha()); + replaceCacheListFromSearch(); } } @@ -1456,7 +1350,8 @@ public class cgeocaches extends AbstractListActivity { @Override public void runSearch() { - search = GCParser.searchByOwner(username, cacheType, Settings.isShowCaptcha()); + search = GCParser.searchByOwner(username, Settings.getCacheType(), Settings.isShowCaptcha()); + replaceCacheListFromSearch(); } } diff --git a/main/src/cgeo/geocaching/cgeoimages.java b/main/src/cgeo/geocaching/cgeoimages.java index b2f7ad6..6de6444 100644 --- a/main/src/cgeo/geocaching/cgeoimages.java +++ b/main/src/cgeo/geocaching/cgeoimages.java @@ -34,7 +34,7 @@ import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; +import java.util.LinkedList; import java.util.List; public class cgeoimages extends AbstractActivity { @@ -55,7 +55,11 @@ public class cgeoimages extends AbstractActivity { private BitmapDrawable currentDrawable; private cgImage currentImage; - static private final Collection<Bitmap> bitmaps = Collections.synchronizedCollection(new ArrayList<Bitmap>()); + // We could use a Set here, but we will insert no duplicates, so there is no need to check for uniqueness. + private final Collection<Bitmap> bitmaps = new LinkedList<Bitmap>(); + private int message; + private boolean offline; + private ArrayList<cgImage> imageNames; private void loadImages(final List<cgImage> images, final int progressMessage, final boolean offline) { @@ -175,31 +179,34 @@ public class cgeoimages extends AbstractActivity { setTitle(res.getString(img_type == SPOILER_IMAGES ? R.string.cache_spoiler_images_title : R.string.cache_log_images_title)); inflater = getLayoutInflater(); - if (imagesView == null) { - imagesView = (LinearLayout) findViewById(R.id.spoiler_list); - } + imagesView = (LinearLayout) findViewById(R.id.spoiler_list); - final ArrayList<cgImage> images = extras.getParcelableArrayList("images"); - if (CollectionUtils.isEmpty(images)) { + imageNames = extras.getParcelableArrayList("images"); + if (CollectionUtils.isEmpty(imageNames)) { showToast(res.getString(R.string.warn_load_images)); finish(); return; } - final int message = img_type == SPOILER_IMAGES ? R.string.cache_spoiler_images_loading : R.string.cache_log_images_loading; - final boolean offline = app.isOffline(geocode, null) && (img_type == SPOILER_IMAGES || Settings.isStoreLogImages()); + message = img_type == SPOILER_IMAGES ? R.string.cache_spoiler_images_loading : R.string.cache_log_images_loading; + offline = app.isOffline(geocode, null) && (img_type == SPOILER_IMAGES || Settings.isStoreLogImages()); + } - loadImages(images, message, offline); + @Override + public void onStart() { + super.onStart(); + loadImages(imageNames, message, offline); } @Override - public void onDestroy() { + public void onStop() { // Reclaim native memory faster than the finalizers would - for (Bitmap b : bitmaps) { + imagesView.removeAllViews(); + for (final Bitmap b : bitmaps) { b.recycle(); } bitmaps.clear(); - super.onDestroy(); + super.onStop(); } private void viewImageInStandardApp(final BitmapDrawable image) { diff --git a/main/src/cgeo/geocaching/cgeonavigate.java b/main/src/cgeo/geocaching/cgeonavigate.java index 567ae74..53470b3 100644 --- a/main/src/cgeo/geocaching/cgeonavigate.java +++ b/main/src/cgeo/geocaching/cgeonavigate.java @@ -2,8 +2,7 @@ package cgeo.geocaching; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.HumanDistance; -import cgeo.geocaching.geopoint.IConversion; +import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.maps.CGeoMap; import cgeo.geocaching.ui.CompassView; import cgeo.geocaching.utils.GeoDirHandler; @@ -18,6 +17,7 @@ import android.os.PowerManager; import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; +import android.view.View; import android.widget.TextView; import java.util.ArrayList; @@ -29,6 +29,7 @@ public class cgeonavigate extends AbstractActivity { private static final String EXTRAS_COORDS = "coords"; private static final String EXTRAS_NAME = "name"; private static final String EXTRAS_GEOCODE = "geocode"; + private static final String EXTRAS_CACHE_INFO = "cacheinfo"; private static final List<IWaypoint> coordinates = new ArrayList<IWaypoint>(); private static final int MENU_MAP = 0; private static final int MENU_SWITCH_COMPASS_GPS = 1; @@ -37,6 +38,7 @@ public class cgeonavigate extends AbstractActivity { private float cacheHeading = 0; private String title = null; private String name = null; + private String info = null; private TextView navType = null; private TextView navAccuracy = null; private TextView navSatellites = null; @@ -65,6 +67,7 @@ public class cgeonavigate extends AbstractActivity { title = geocode; name = extras.getString(EXTRAS_NAME); dstCoords = (Geopoint) extras.getParcelable(EXTRAS_COORDS); + info = extras.getString(EXTRAS_CACHE_INFO); if (StringUtils.isNotBlank(name)) { if (StringUtils.isNotBlank(title)) { @@ -86,6 +89,7 @@ public class cgeonavigate extends AbstractActivity { // set header setTitle(); setDestCoords(); + setCacheInfo(); // get textviews once compassView = (CompassView) findViewById(R.id.rose); @@ -179,6 +183,7 @@ public class cgeonavigate extends AbstractActivity { dstCoords = coordinate.getCoords(); setTitle(); setDestCoords(); + setCacheInfo(); updateDistanceInfo(app.currentGeo()); Log.d("destination set: " + title + " (" + dstCoords + ")"); @@ -204,6 +209,16 @@ public class cgeonavigate extends AbstractActivity { ((TextView) findViewById(R.id.destination)).setText(dstCoords.toString()); } + private void setCacheInfo() { + final TextView cacheInfoView = (TextView) findViewById(R.id.cacheinfo); + if (info == null) { + cacheInfoView.setVisibility(View.GONE); + return; + } + cacheInfoView.setVisibility(View.VISIBLE); + cacheInfoView.setText(info); + } + private void updateDistanceInfo(final IGeoData geo) { if (geo.getCoords() == null || dstCoords == null) { return; @@ -217,7 +232,7 @@ public class cgeonavigate extends AbstractActivity { } cacheHeading = geo.getCoords().bearingTo(dstCoords); - distanceView.setText(HumanDistance.getHumanDistance(geo.getCoords().distanceTo(dstCoords))); + distanceView.setText(Units.getDistanceFromKilometers(geo.getCoords().distanceTo(dstCoords))); headingView.setText(Math.round(cacheHeading) + "°"); } @@ -233,29 +248,22 @@ public class cgeonavigate extends AbstractActivity { } if (geo.getCoords() != null) { - String satellites; - if (geo.getSatellitesFixed() > 0) { - satellites = res.getString(R.string.loc_sat) + ": " + geo.getSatellitesFixed() + "/" + geo.getSatellitesVisible(); - } else if (geo.getSatellitesVisible() >= 0) { - satellites = res.getString(R.string.loc_sat) + ": 0/" + geo.getSatellitesVisible(); - } else { - satellites = ""; + if (geo.getSatellitesVisible() >= 0) { + navSatellites.setText(res.getString(R.string.loc_sat) + ": " + geo.getSatellitesFixed() + "/" + geo.getSatellitesVisible()); + } + else { + navSatellites.setText(""); } - navSatellites.setText(satellites); navType.setText(res.getString(geo.getLocationProvider().resourceId)); if (geo.getAccuracy() >= 0) { - if (Settings.isUseMetricUnits()) { - navAccuracy.setText("±" + Math.round(geo.getAccuracy()) + " m"); - } else { - navAccuracy.setText("±" + Math.round(geo.getAccuracy() * IConversion.METERS_TO_FEET) + " ft"); - } + navAccuracy.setText("±" + Units.getDistanceFromMeters(geo.getAccuracy())); } else { navAccuracy.setText(null); } if (geo.getAltitude() != 0.0f) { - final String humanAlt = HumanDistance.getHumanDistance((float) geo.getAltitude() / 1000); + final String humanAlt = Units.getDistanceFromMeters((float) geo.getAltitude()); navLocation.setText(geo.getCoords() + " | " + humanAlt); } else { navLocation.setText(geo.getCoords().toString()); @@ -290,7 +298,8 @@ public class cgeonavigate extends AbstractActivity { } } - public static void startActivity(final Context context, final String geocode, final String displayedName, final Geopoint coords, final Collection<IWaypoint> coordinatesWithType) { + public static void startActivity(final Context context, final String geocode, final String displayedName, final Geopoint coords, final Collection<IWaypoint> coordinatesWithType, + final String info) { coordinates.clear(); if (coordinatesWithType != null) { // avoid possible NPE coordinates.addAll(coordinatesWithType); @@ -302,6 +311,12 @@ public class cgeonavigate extends AbstractActivity { if (null != displayedName) { navigateIntent.putExtra(EXTRAS_NAME, displayedName); } + navigateIntent.putExtra(EXTRAS_CACHE_INFO, info); context.startActivity(navigateIntent); } + + public static void startActivity(final Context context, final String geocode, final String displayedName, final Geopoint coords, final Collection<IWaypoint> coordinatesWithType) { + cgeonavigate.startActivity(context, geocode, displayedName, coords, coordinatesWithType, null); + } + } diff --git a/main/src/cgeo/geocaching/cgeotrackable.java b/main/src/cgeo/geocaching/cgeotrackable.java index c635e41..acd351d 100644 --- a/main/src/cgeo/geocaching/cgeotrackable.java +++ b/main/src/cgeo/geocaching/cgeotrackable.java @@ -3,7 +3,7 @@ package cgeo.geocaching; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.connector.gc.GCParser; import cgeo.geocaching.enumerations.LogType; -import cgeo.geocaching.geopoint.HumanDistance; +import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.ui.CacheDetailsCreator; import cgeo.geocaching.ui.Formatter; @@ -166,7 +166,7 @@ public class cgeotrackable extends AbstractActivity { // trackable distance if (trackable.getDistance() >= 0) { - details.add(R.string.trackable_distance, HumanDistance.getHumanDistance(trackable.getDistance())); + details.add(R.string.trackable_distance, Units.getDistanceFromKilometers(trackable.getDistance())); } // trackable goal diff --git a/main/src/cgeo/geocaching/compatibility/AndroidLevel11.java b/main/src/cgeo/geocaching/compatibility/AndroidLevel11.java index f933071..a425ee5 100644 --- a/main/src/cgeo/geocaching/compatibility/AndroidLevel11.java +++ b/main/src/cgeo/geocaching/compatibility/AndroidLevel11.java @@ -1,10 +1,12 @@ package cgeo.geocaching.compatibility; +import android.annotation.TargetApi; import android.app.Activity; /** * Android level 11 support */ +@TargetApi(11) public class AndroidLevel11 implements AndroidLevel11Interface { @Override diff --git a/main/src/cgeo/geocaching/compatibility/AndroidLevel8.java b/main/src/cgeo/geocaching/compatibility/AndroidLevel8.java index 859fdf1..ea5a795 100644 --- a/main/src/cgeo/geocaching/compatibility/AndroidLevel8.java +++ b/main/src/cgeo/geocaching/compatibility/AndroidLevel8.java @@ -2,10 +2,12 @@ package cgeo.geocaching.compatibility; import cgeo.geocaching.utils.Log; +import android.annotation.TargetApi; import android.app.Activity; import android.app.backup.BackupManager; import android.view.Display; +@TargetApi(8) public class AndroidLevel8 implements AndroidLevel8Interface { @Override diff --git a/main/src/cgeo/geocaching/compatibility/Compatibility.java b/main/src/cgeo/geocaching/compatibility/Compatibility.java index d869c58..0821655 100644 --- a/main/src/cgeo/geocaching/compatibility/Compatibility.java +++ b/main/src/cgeo/geocaching/compatibility/Compatibility.java @@ -1,6 +1,7 @@ package cgeo.geocaching.compatibility; import cgeo.geocaching.activity.AbstractActivity; +import cgeo.geocaching.utils.AngleUtils; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.reflect.MethodUtils; @@ -40,23 +41,22 @@ public final class Compatibility { /** * Add 90, 180 or 270 degrees to the given rotation. - * <br/> - * Note: the result is not normalized and may fall outside your desired range. * * @param directionNowPre the direction in degrees before adjustment * @param activity the activity whose rotation is used to adjust the direction - * @return the adjusted direction + * @return the adjusted direction, in the [0, 360[ range */ public static float getDirectionNow(final float directionNowPre, final Activity activity) { + float offset = 0; if (isLevel8) { try { final int rotation = level8.getRotation(activity); if (rotation == Surface.ROTATION_90) { - return directionNowPre + 90; + offset = 90; } else if (rotation == Surface.ROTATION_180) { - return directionNowPre + 180; + offset = 180; } else if (rotation == Surface.ROTATION_270) { - return directionNowPre + 270; + offset = 270; } } catch (final Exception e) { // This should never happen: IllegalArgumentException, IllegalAccessException or InvocationTargetException @@ -66,10 +66,10 @@ public final class Compatibility { final Display display = activity.getWindowManager().getDefaultDisplay(); final int rotation = display.getOrientation(); if (rotation == Configuration.ORIENTATION_LANDSCAPE) { - return directionNowPre + 90; + offset = 90; } } - return directionNowPre; + return AngleUtils.normalize(directionNowPre + offset); } public static void dataChanged(final String name) { diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java index 72c5259..ff1b442 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java @@ -37,11 +37,12 @@ public final class GCConstants { public final static Pattern PATTERN_NAME = Pattern.compile("<span id=\"ctl00_ContentBody_CacheName\">(.*?)</span>"); public final static Pattern PATTERN_DIFFICULTY = Pattern.compile("<span id=\"ctl00_ContentBody_uxLegendScale\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\""); public final static Pattern PATTERN_TERRAIN = Pattern.compile("<span id=\"ctl00_ContentBody_Localize[\\d]+\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\""); - public final static Pattern PATTERN_OWNERREAL = Pattern.compile("<a id=\"ctl00_ContentBody_uxFindLinksHiddenByThisUser\" href=\"[^\"]*/seek/nearest\\.aspx\\?u=(.*?)\""); + public final static Pattern PATTERN_OWNER_USERID = Pattern.compile("<a id=\"ctl00_ContentBody_uxFindLinksHiddenByThisUser\" href=\"[^\"]*/seek/nearest\\.aspx\\?u=(.*?)\""); public final static Pattern PATTERN_FOUND = Pattern.compile("<a id=\"ctl00_ContentBody_hlFoundItLog\"[^<]*<img src=\".*/images/stockholm/16x16/check\\.gif\"[^>]*>[^<]*</a>[^<]*</p>"); public final static Pattern PATTERN_FOUND_ALTERNATIVE = Pattern.compile("<div class=\"StatusInformationWidget FavoriteWidget\""); - public final static Pattern PATTERN_OWNER = Pattern.compile("<div id=\"ctl00_ContentBody_mcd1\">[^<]+<a href=\"[^\"]+\">([^<]+)</a></div>"); - public final static Pattern PATTERN_TYPE = Pattern.compile("<img src=\"[^\"]*/WptTypes/\\d+\\.gif\" alt=\"([^\"]+?)\" title=\"[^\"]+\" />"); + public final static Pattern PATTERN_FOUND_DATE = Pattern.compile("You logged this as Found on ([^.]+?)\\.[^<]*</a>[^<]*</p>"); + public final static Pattern PATTERN_OWNER_DISPLAYNAME = Pattern.compile("<div id=\"ctl00_ContentBody_mcd1\">[^<]+<a href=\"[^\"]+\">([^<]+)</a></div>"); + public final static Pattern PATTERN_TYPE = Pattern.compile("<img src=\"[^\"]*/WptTypes/\\d+\\.gif\" alt=\"([^\"]+?)\"[^>]*>"); public final static Pattern PATTERN_HIDDEN = Pattern.compile("<div id=\"ctl00_ContentBody_mcd2\">\\W*Hidden[\\s:]*([^<]+?)</div>"); public final static Pattern PATTERN_HIDDENEVENT = Pattern.compile("Event\\s*Date\\s*:\\s*([^<]+)<div id=\"calLinks\">", Pattern.DOTALL); public final static Pattern PATTERN_FAVORITE = Pattern.compile("<div id=\"pnlFavoriteCache\">"); // without 'class="hideMe"' inside the tag ! @@ -54,8 +55,7 @@ public final class GCConstants { public final static Pattern PATTERN_ATTRIBUTES = Pattern.compile("<h3 class=\"WidgetHeader\">[^<]*<img[^>]+>\\W*Attributes[^<]*</h3>[^<]*<div class=\"WidgetBody\">((?:[^<]*<img src=\"[^\"]+\" alt=\"[^\"]+\"[^>]*>)+?)[^<]*<p"); /** Two groups ! */ public final static Pattern PATTERN_ATTRIBUTESINSIDE = Pattern.compile("[^<]*<img src=\"([^\"]+)\" alt=\"([^\"]+?)\""); - public final static Pattern PATTERN_SPOILERS = Pattern.compile("<p class=\"NoPrint\">\\s+((?:<a href=\"http://img\\.geocaching\\.com/cache/[^.]+\\.jpg\"[^>]+><img class=\"StatusIcon\"[^>]+><span>[^<]+</span></a><br />(?:[^<]+<br /><br />)?)+)\\s+</p>"); - public final static Pattern PATTERN_SPOILERSINSIDE = Pattern.compile("<a href=\"(http://img\\.geocaching\\.com/cache/[^.]+\\.jpg)\"[^>]+><img class=\"StatusIcon\"[^>]+><span>([^<]+)</span></a><br />(?:([^<]+)<br /><br />)?"); + public final static Pattern PATTERN_SPOILER_IMAGE = Pattern.compile("<a href=\"(http://img\\.geocaching\\.com/cache/[^.]+\\.jpg)\"[^>]+><img[^>]+><span>([^<]+)</span></a>(?:<br />([^<]+)<br /><br />)?"); public final static Pattern PATTERN_INVENTORY = Pattern.compile("<span id=\"ctl00_ContentBody_uxTravelBugList_uxInventoryLabel\">\\W*Inventory[^<]*</span>[^<]*</h3>[^<]*<div class=\"WidgetBody\">([^<]*<ul>(([^<]*<li>[^<]*<a href=\"[^\"]+\"[^>]*>[^<]*<img src=\"[^\"]+\"[^>]*>[^<]*<span>[^<]+<\\/span>[^<]*<\\/a>[^<]*<\\/li>)+)[^<]*<\\/ul>)?"); public final static Pattern PATTERN_INVENTORYINSIDE = Pattern.compile("[^<]*<li>[^<]*<a href=\"[a-z0-9\\-\\_\\.\\?\\/\\:\\@]*\\/track\\/details\\.aspx\\?guid=([0-9a-z\\-]+)[^\"]*\"[^>]*>[^<]*<img src=\"[^\"]+\"[^>]*>[^<]*<span>([^<]+)<\\/span>[^<]*<\\/a>[^<]*<\\/li>"); public final static Pattern PATTERN_WATCHLIST = Pattern.compile("icon_stop_watchlist.gif"); diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index a8509b8..c71b859 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCMap.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -10,7 +10,7 @@ import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; import cgeo.geocaching.enumerations.LiveMapStrategy.StrategyFlag; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.IConversion; +import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.ui.Formatter; @@ -88,7 +88,7 @@ public class GCMap { JSONObject typeObj = dataObject.getJSONObject("type"); cache.setType(CacheType.getByPattern(typeObj.getString("text"))); // Traditional Cache JSONObject ownerObj = dataObject.getJSONObject("owner"); - cache.setOwner(ownerObj.getString("text")); + cache.setOwnerDisplayName(ownerObj.getString("text")); result.addCache(cache); @@ -243,12 +243,7 @@ public class GCMap { SearchResult result = searchByViewport(viewport, tokens, strategy); if (Settings.isDebug()) { - String text = Formatter.SEPARATOR + strategy.getL10n() + Formatter.SEPARATOR; - if (Settings.isUseMetricUnits()) { - text += speed + " km/h"; - } else { - text += speed / IConversion.MILES_TO_KILOMETER + " mph"; - } + StringBuilder text = new StringBuilder(Formatter.SEPARATOR).append(strategy.getL10n()).append(Formatter.SEPARATOR).append(Units.getSpeed(speed)); result.setUrl(result.getUrl() + text); } diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index 053c248..2ca505d 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -44,6 +44,7 @@ import android.net.Uri; import android.text.Html; import android.text.Spannable; import android.text.Spanned; +import android.text.style.ForegroundColorSpan; import android.text.style.StrikethroughSpan; import java.net.URLDecoder; @@ -225,6 +226,9 @@ public abstract class GCParser { if (cache.isDisabled() || cache.isArchived()) { // strike cache.getNameSp().setSpan(new StrikethroughSpan(), 0, cache.getNameSp().toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } + if (cache.isArchived()) { + cache.getNameSp().setSpan(new ForegroundColorSpan(cgeoapplication.getInstance().getResources().getColor(R.color.archived_cache_color)), 0, cache.getNameSp().toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } } searchResult.addCache(cache); @@ -378,9 +382,9 @@ public abstract class GCParser { cache.setName(cacheName); // owner real name - cache.setOwnerReal(URLDecoder.decode(BaseUtils.getMatch(page, GCConstants.PATTERN_OWNERREAL, true, cache.getOwnerReal()))); + cache.setOwnerUserId(URLDecoder.decode(BaseUtils.getMatch(page, GCConstants.PATTERN_OWNER_USERID, true, cache.getOwnerUserId()))); - cache.setOwn(StringUtils.equalsIgnoreCase(cache.getOwnerReal(), Settings.getUsername())); + cache.setOwn(StringUtils.equalsIgnoreCase(cache.getOwnerUserId(), Settings.getUsername())); String tableInside = page; @@ -406,7 +410,7 @@ public abstract class GCParser { } // owner - cache.setOwner(StringEscapeUtils.unescapeHtml4(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_OWNER, true, cache.getOwner())).toString()); + cache.setOwnerDisplayName(StringEscapeUtils.unescapeHtml4(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_OWNER_DISPLAYNAME, true, cache.getOwnerDisplayName()))); // hidden try { @@ -436,6 +440,17 @@ public abstract class GCParser { // cache found cache.setFound(BaseUtils.matches(page, GCConstants.PATTERN_FOUND) || BaseUtils.matches(page, GCConstants.PATTERN_FOUND_ALTERNATIVE)); + // cache found date + try { + final String foundDateString = BaseUtils.getMatch(page, GCConstants.PATTERN_FOUND_DATE, true, null); + if (StringUtils.isNotBlank(foundDateString)) { + cache.setVisitedDate(Login.parseGcCustomDate(foundDateString).getTime()); + } + } catch (ParseException e) { + // failed to parse cache found date + Log.w("GCParser.parseCache: Failed to parse cache found date"); + } + // cache type cache.setType(CacheType.getByPattern(BaseUtils.getMatch(page, GCConstants.PATTERN_TYPE, true, cache.getType().id))); @@ -508,35 +523,32 @@ public abstract class GCParser { // cache spoilers try { - final String spoilers = BaseUtils.getMatch(page, GCConstants.PATTERN_SPOILERS, false, null); - if (null != spoilers) { - if (CancellableHandler.isCancelled(handler)) { - return null; - } - CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_spoilers); + if (CancellableHandler.isCancelled(handler)) { + return null; + } + CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_spoilers); - final Matcher matcherSpoilersInside = GCConstants.PATTERN_SPOILERSINSIDE.matcher(spoilers); + final Matcher matcherSpoilersInside = GCConstants.PATTERN_SPOILER_IMAGE.matcher(page); - while (matcherSpoilersInside.find()) { - // the original spoiler URL (include .../display/... contains a low-resolution image - // if we shorten the URL we get the original-resolution image - String url = matcherSpoilersInside.group(1).replace("/display", ""); + while (matcherSpoilersInside.find()) { + // the original spoiler URL (include .../display/... contains a low-resolution image + // if we shorten the URL we get the original-resolution image + String url = matcherSpoilersInside.group(1).replace("/display", ""); - String title = null; - if (matcherSpoilersInside.group(2) != null) { - title = matcherSpoilersInside.group(2); - } - String description = null; - if (matcherSpoilersInside.group(3) != null) { - description = matcherSpoilersInside.group(3); - } - final cgImage spoiler = new cgImage(url, title, description); + String title = null; + if (matcherSpoilersInside.group(2) != null) { + title = matcherSpoilersInside.group(2); + } + String description = null; + if (matcherSpoilersInside.group(3) != null) { + description = matcherSpoilersInside.group(3); + } + final cgImage spoiler = new cgImage(url, title, description); - if (cache.getSpoilers() == null) { - cache.setSpoilers(new ArrayList<cgImage>()); - } - cache.getSpoilers().add(spoiler); + if (cache.getSpoilers() == null) { + cache.setSpoilers(new ArrayList<cgImage>()); } + cache.getSpoilers().add(spoiler); } } catch (Exception e) { // failed to parse cache spoilers diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java index 39f81f0..8c79aca 100644 --- a/main/src/cgeo/geocaching/connector/gc/Login.java +++ b/main/src/cgeo/geocaching/connector/gc/Login.java @@ -144,7 +144,7 @@ public abstract class Login { return StatusCode.NO_ERROR; } - public static void setActualCachesFound(final int found) { + static void setActualCachesFound(final int found) { actualCachesFound = found; } @@ -152,7 +152,7 @@ public abstract class Login { return actualStatus; } - public static void setActualStatus(final String status) { + private static void setActualStatus(final String status) { actualStatus = status; } @@ -160,7 +160,7 @@ public abstract class Login { return actualLoginStatus; } - public static void setActualLoginStatus(boolean loginStatus) { + private static void setActualLoginStatus(boolean loginStatus) { actualLoginStatus = loginStatus; } @@ -168,7 +168,7 @@ public abstract class Login { return actualUserName; } - public static void setActualUserName(String userName) { + private static void setActualUserName(String userName) { actualUserName = userName; } diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java index 0212a64..8bdb9a4 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java @@ -133,7 +133,7 @@ final public class OkapiClient { // not used: url final JSONObject owner = response.getJSONObject(CACHE_OWNER); - cache.setOwner(parseUser(owner)); + cache.setOwnerDisplayName(parseUser(owner)); cache.getLogCounts().put(LogType.FOUND_IT, response.getInt(CACHE_FOUNDS)); cache.getLogCounts().put(LogType.DIDNT_FIND_IT, response.getInt(CACHE_NOTFOUNDS)); diff --git a/main/src/cgeo/geocaching/enumerations/CacheType.java b/main/src/cgeo/geocaching/enumerations/CacheType.java index be66de1..730c989 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheType.java +++ b/main/src/cgeo/geocaching/enumerations/CacheType.java @@ -31,7 +31,14 @@ public enum CacheType { /** No real cache type -> filter */ ALL("all", "display all caches", "9a79e6ce-3344-409c-bbe9-496530baf758", R.string.all_types, R.drawable.type_unknown); + /** + * id field is used when for storing caches in the database. + */ public final String id; + /** + * human readable name of the cache type<br> + * used in web parsing as well as for gpx import/export. + */ public final String pattern; public final String guid; private final int stringId; diff --git a/main/src/cgeo/geocaching/export/FieldnoteExport.java b/main/src/cgeo/geocaching/export/FieldnoteExport.java index ad39acd..e87555f 100644 --- a/main/src/cgeo/geocaching/export/FieldnoteExport.java +++ b/main/src/cgeo/geocaching/export/FieldnoteExport.java @@ -169,7 +169,7 @@ class FieldnoteExport extends AbstractExport { Writer fw = null; try { os = new FileOutputStream(exportFile); - fw = new OutputStreamWriter(os, "ISO-8859-1"); // gc.com doesn't support neither UTF-8 nor html entities + fw = new OutputStreamWriter(os, "UTF-16"); fw.write(fieldNoteBuffer.toString()); } catch (IOException e) { Log.e("FieldnoteExport.ExportTask export", e); diff --git a/main/src/cgeo/geocaching/export/GpxExport.java b/main/src/cgeo/geocaching/export/GpxExport.java index 156d9d4..9bc3963 100644 --- a/main/src/cgeo/geocaching/export/GpxExport.java +++ b/main/src/cgeo/geocaching/export/GpxExport.java @@ -2,6 +2,7 @@ package cgeo.geocaching.export; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; +import cgeo.geocaching.Settings; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgWaypoint; import cgeo.geocaching.cgeoapplication; @@ -16,9 +17,15 @@ import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringEscapeUtils; import android.app.Activity; +import android.app.AlertDialog; import android.app.ProgressDialog; +import android.content.Intent; +import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; import java.io.BufferedWriter; import java.io.File; @@ -26,6 +33,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -39,7 +47,48 @@ class GpxExport extends AbstractExport { @Override public void export(final List<cgCache> caches, final Activity activity) { - new ExportTask(caches, activity).execute((Void) null); + if (null == activity) { + // No activity given, so no user interaction possible. + // Start export with default parameters. + new ExportTask(caches, activity).execute((Void) null); + + } else { + // Show configuration dialog + new ExportOptionsDialog(caches, activity).show(); + } + } + + /** + * A dialog to allow the user to set options for the export. + * + * Currently available option is: opening of share menu after successful export + */ + private class ExportOptionsDialog extends AlertDialog { + public ExportOptionsDialog(final List<cgCache> caches, final Activity activity) { + super(activity); + + View layout = activity.getLayoutInflater().inflate(R.layout.gpx_export_dialog, null); + setView(layout); + + final CheckBox shareOption = (CheckBox) layout.findViewById(R.id.share); + + shareOption.setChecked(Settings.getShareAfterExport()); + + shareOption.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Settings.setShareAfterExport(shareOption.isChecked()); + } + }); + + ((Button) layout.findViewById(R.id.export)).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + new ExportTask(caches, activity).execute((Void) null); + } + }); + } } private class ExportTask extends AsyncTask<Void, Integer, Boolean> { @@ -88,7 +137,6 @@ class GpxExport extends AbstractExport { gpx.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); gpx.write("<gpx version=\"1.0\" creator=\"c:geo - http://www.cgeo.org\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/0\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0/1 http://www.groundspeak.com/cache/1/0/1/cache.xsd\">"); - for (int i = 0; i < caches.size(); i++) { // reload the cache. otherwise logs, attributes and other detailed information is not available final cgCache cache = cgeoapplication.getInstance().loadCache(caches.get(i).getGeocode(), LoadFlags.LOAD_ALL_DB_ONLY); @@ -113,6 +161,14 @@ class GpxExport extends AbstractExport { gpx.write(StringEscapeUtils.escapeXml(cache.getName())); gpx.write("</desc>"); + gpx.write("<url>"); + gpx.write(cache.getUrl()); + gpx.write("</url>"); + + gpx.write("<urlname>"); + gpx.write(StringEscapeUtils.escapeXml(cache.getName())); + gpx.write("</urlname>"); + gpx.write("<sym>"); gpx.write(cache.isFound() ? "Geocache Found" : "Geocache"); gpx.write("</sym>"); @@ -122,7 +178,9 @@ class GpxExport extends AbstractExport { gpx.write("</type>"); gpx.write("<groundspeak:cache "); - gpx.write("available=\""); + gpx.write("id=\""); + gpx.write(cache.getCacheId()); + gpx.write("\" available=\""); gpx.write(!cache.isDisabled() ? "True" : "False"); gpx.write("\" archived=\""); gpx.write(cache.isArchived() ? "True" : "False"); @@ -134,11 +192,11 @@ class GpxExport extends AbstractExport { gpx.write("</groundspeak:name>"); gpx.write("<groundspeak:placed_by>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getOwner())); + gpx.write(StringEscapeUtils.escapeXml(cache.getOwnerDisplayName())); gpx.write("</groundspeak:placed_by>"); gpx.write("<groundspeak:owner>"); - gpx.write(StringEscapeUtils.escapeXml(cache.getOwnerReal())); + gpx.write(StringEscapeUtils.escapeXml(cache.getOwnerUserId())); gpx.write("</groundspeak:owner>"); gpx.write("<groundspeak:type>"); @@ -225,35 +283,75 @@ class GpxExport extends AbstractExport { } private void writeWaypoints(final cgCache cache) throws IOException { + List<cgWaypoint> waypoints = cache.getWaypoints(); + List<cgWaypoint> ownWaypoints = new ArrayList<cgWaypoint>(waypoints.size()); + List<cgWaypoint> originWaypoints = new ArrayList<cgWaypoint>(waypoints.size()); for (cgWaypoint wp : cache.getWaypoints()) { - gpx.write("<wpt lat=\""); - final Geopoint coords = wp.getCoords(); - gpx.write(coords != null ? Double.toString(coords.getLatitude()) : ""); // TODO: check whether is the best way to handle unknown waypoint coordinates - gpx.write("\" lon=\""); - gpx.write(coords != null ? Double.toString(coords.getLongitude()) : ""); - gpx.write("\">"); - - gpx.write("<name>"); - gpx.write(StringEscapeUtils.escapeXml(wp.getPrefix())); - gpx.write(StringEscapeUtils.escapeXml(cache.getGeocode().substring(2))); - gpx.write("</name>"); - - gpx.write("<cmt />"); - - gpx.write("<desc>"); - gpx.write(StringEscapeUtils.escapeXml(wp.getNote())); - gpx.write("</desc>"); - - gpx.write("<sym>"); - gpx.write(StringEscapeUtils.escapeXml(wp.getWaypointType().toString())); //TODO: Correct identifier string - gpx.write("</sym>"); - - gpx.write("<type>Waypoint|"); - gpx.write(StringEscapeUtils.escapeXml(wp.getWaypointType().toString())); //TODO: Correct identifier string - gpx.write("</type>"); - - gpx.write("</wpt>"); + if (wp.isUserDefined()) { + ownWaypoints.add(wp); + } else { + originWaypoints.add(wp); + } + } + int maxPrefix = 0; + for (cgWaypoint wp : originWaypoints) { + String prefix = wp.getPrefix(); + try { + maxPrefix = Math.max(Integer.parseInt(prefix), maxPrefix); + } catch (NumberFormatException ex) { + Log.e("Unexpected origin waypoint prefix='" + prefix + "'", ex); + } + writeCacheWaypoint(wp, prefix); } + for (cgWaypoint wp : ownWaypoints) { + maxPrefix++; + String prefix = String.valueOf(maxPrefix); + if (prefix.length() == 1) { + prefix = "0" + prefix; + } + writeCacheWaypoint(wp, prefix); + } + } + + /** + * Writes one waypoint entry for cache waypoint. + * + * @param cache + * The + * @param wp + * @param prefix + * @throws IOException + */ + private void writeCacheWaypoint(final cgWaypoint wp, final String prefix) throws IOException { + gpx.write("<wpt lat=\""); + final Geopoint coords = wp.getCoords(); + gpx.write(coords != null ? Double.toString(coords.getLatitude()) : ""); // TODO: check whether is the best way to handle unknown waypoint coordinates + gpx.write("\" lon=\""); + gpx.write(coords != null ? Double.toString(coords.getLongitude()) : ""); + gpx.write("\">"); + + gpx.write("<name>"); + gpx.write(StringEscapeUtils.escapeXml(prefix)); + gpx.write(StringEscapeUtils.escapeXml(wp.getGeocode().substring(2))); + gpx.write("</name>"); + + gpx.write("<cmt>"); + gpx.write(StringEscapeUtils.escapeXml(wp.getNote())); + gpx.write("</cmt>"); + + gpx.write("<desc>"); + gpx.write(StringEscapeUtils.escapeXml(wp.getName())); + gpx.write("</desc>"); + + gpx.write("<sym>"); + gpx.write(StringEscapeUtils.escapeXml(wp.getWaypointType().toString())); //TODO: Correct identifier string + gpx.write("</sym>"); + + gpx.write("<type>Waypoint|"); + gpx.write(StringEscapeUtils.escapeXml(wp.getWaypointType().toString())); //TODO: Correct identifier string + gpx.write("</type>"); + + gpx.write("</wpt>"); } private void writeLogs(final cgCache cache) throws IOException { @@ -322,6 +420,13 @@ class GpxExport extends AbstractExport { progress.dismiss(); if (result) { ActivityMixin.showToast(activity, getName() + ' ' + getString(R.string.export_exportedto) + ": " + exportFile.toString()); + if (Settings.getShareAfterExport()) { + Intent shareIntent = new Intent(); + shareIntent.setAction(Intent.ACTION_SEND); + shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(exportFile)); + shareIntent.setType("application/xml"); + activity.startActivity(Intent.createChooser(shareIntent, getString(R.string.export_gpx_to))); + } } else { ActivityMixin.showToast(activity, getString(R.string.export_failed)); } diff --git a/main/src/cgeo/geocaching/files/FileList.java b/main/src/cgeo/geocaching/files/FileList.java index 431fe3b..08c1940 100644 --- a/main/src/cgeo/geocaching/files/FileList.java +++ b/main/src/cgeo/geocaching/files/FileList.java @@ -19,15 +19,17 @@ import android.widget.ArrayAdapter; import java.io.File; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; public abstract class FileList<T extends ArrayAdapter<File>> extends AbstractListActivity { private static final int MSG_SEARCH_WHOLE_SD_CARD = 1; - private List<File> files = new ArrayList<File>(); + private final List<File> files = new ArrayList<File>(); private T adapter = null; private ProgressDialog waitDialog = null; - private loadFiles searchingThread = null; + private SearchFilesThread searchingThread = null; private boolean endSearching = false; private int listId = StoredList.STANDARD_LIST_ID; final private Handler changeWaitDialogHandler = new Handler() { @@ -114,7 +116,7 @@ public abstract class FileList<T extends ArrayAdapter<File>> extends AbstractLis ); endSearching = false; - searchingThread = new loadFiles(); + searchingThread = new SearchFilesThread(); searchingThread.start(); } @@ -145,14 +147,14 @@ public abstract class FileList<T extends ArrayAdapter<File>> extends AbstractLis */ protected abstract void setTitle(); - private class loadFiles extends Thread { + private class SearchFilesThread extends Thread { public void notifyEnd() { endSearching = true; } @Override public void run() { - List<File> list = new ArrayList<File>(); + final List<File> list = new ArrayList<File>(); try { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { @@ -181,7 +183,13 @@ public abstract class FileList<T extends ArrayAdapter<File>> extends AbstractLis changeWaitDialogHandler.sendMessage(Message.obtain(changeWaitDialogHandler, 0, "loaded directories")); files.addAll(list); - list.clear(); + Collections.sort(files, new Comparator<File>() { + + @Override + public int compare(File lhs, File rhs) { + return lhs.getName().compareToIgnoreCase(rhs.getName()); + } + }); loadFilesHandler.sendMessage(Message.obtain(loadFilesHandler)); } diff --git a/main/src/cgeo/geocaching/files/GPXParser.java b/main/src/cgeo/geocaching/files/GPXParser.java index e083f58..454c494 100644 --- a/main/src/cgeo/geocaching/files/GPXParser.java +++ b/main/src/cgeo/geocaching/files/GPXParser.java @@ -51,6 +51,7 @@ public abstract class GPXParser extends FileParser { */ private static final Pattern patternGeocode = Pattern.compile("([A-Z][0-9A-Z]+)"); private static final Pattern patternGuid = Pattern.compile(".*" + Pattern.quote("guid=") + "([0-9a-z\\-]+)", Pattern.CASE_INSENSITIVE); + private static final Pattern patternUrlGeocode = Pattern.compile(".*" + Pattern.quote("wp=") + "([A-Z][0-9A-Z]+)", Pattern.CASE_INSENSITIVE); /** * supported groundspeak extensions of the GPX format */ @@ -228,7 +229,7 @@ public abstract class GPXParser extends FileParser { static Date parseDate(String inputUntrimmed) throws ParseException { String input = inputUntrimmed.trim(); - // remove milli seconds to reduce number of needed patterns + // remove milliseconds to reduce number of needed patterns final Matcher matcher = PATTERN_MILLISECONDS.matcher(input); input = matcher.replaceFirst(""); if (input.contains("Z")) { @@ -254,8 +255,13 @@ public abstract class GPXParser extends FileParser { public void start(Attributes attrs) { try { if (attrs.getIndex("lat") > -1 && attrs.getIndex("lon") > -1) { - cache.setCoords(new Geopoint(Double.valueOf(attrs.getValue("lat")), - Double.valueOf(attrs.getValue("lon")))); + final String latitude = attrs.getValue("lat"); + final String longitude = attrs.getValue("lon"); + // latitude and longitude are required attributes, but we export them empty for waypoints without coordinates + if (StringUtils.isNotBlank(latitude) && StringUtils.isNotBlank(longitude)) { + cache.setCoords(new Geopoint(Double.valueOf(latitude), + Double.valueOf(longitude))); + } } } catch (Exception e) { Log.w("Failed to parse waypoint's latitude and/or longitude."); @@ -299,8 +305,7 @@ public abstract class GPXParser extends FileParser { result.put(key, cache); showProgressMessage(progressHandler, progressStream.getProgress()); } else if (StringUtils.isNotBlank(cache.getName()) - && cache.getCoords() != null - && StringUtils.contains(type, "waypoint")) { + && StringUtils.containsIgnoreCase(type, "waypoint")) { addWaypointToCache(); } @@ -422,6 +427,11 @@ public abstract class GPXParser extends FileParser { cache.setGuid(guid); } } + final Matcher matcherCode = patternUrlGeocode.matcher(url); + if (matcherCode.matches()) { + String geocode = matcherCode.group(1); + cache.setGeocode(geocode); + } } }); @@ -485,8 +495,17 @@ public abstract class GPXParser extends FileParser { gcCache.getChild(nsGC, "owner").setEndTextElementListener(new EndTextElementListener() { @Override - public void end(String cacheOwner) { - cache.setOwner(validate(cacheOwner)); + public void end(String ownerUserId) { + cache.setOwnerUserId(validate(ownerUserId)); + } + }); + + // waypoint.cache.getOwner() + gcCache.getChild(nsGC, "placed_by").setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String ownerDisplayName) { + cache.setOwnerDisplayName(validate(ownerDisplayName)); } }); @@ -775,9 +794,17 @@ public abstract class GPXParser extends FileParser { return WaypointType.TRAILHEAD; } else if ("final location".equalsIgnoreCase(sym)) { return WaypointType.FINAL; - } else { - return WaypointType.WAYPOINT; } + // this is not fully correct, but lets also look for localized waypoint types + for (WaypointType waypointType : WaypointType.ALL_TYPES_EXCEPT_OWN) { + final String localized = waypointType.getL10n(); + if (StringUtils.isNotEmpty(localized)) { + if (localized.equalsIgnoreCase(sym)) { + return waypointType; + } + } + } + return WaypointType.WAYPOINT; } private void findGeoCode(final String input) { diff --git a/main/src/cgeo/geocaching/filter/FilterUserInterface.java b/main/src/cgeo/geocaching/filter/FilterUserInterface.java index 002511a..230bc91 100644 --- a/main/src/cgeo/geocaching/filter/FilterUserInterface.java +++ b/main/src/cgeo/geocaching/filter/FilterUserInterface.java @@ -3,7 +3,6 @@ package cgeo.geocaching.filter; import cgeo.geocaching.R; import cgeo.geocaching.Settings; import cgeo.geocaching.cgeoapplication; -import cgeo.geocaching.activity.IAbstractActivity; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.RunnableWithArgument; @@ -35,11 +34,11 @@ public final class FilterUserInterface { } } - private final IAbstractActivity activity; + private final Activity activity; private final ArrayList<FactoryEntry> registry; private final Resources res; - public FilterUserInterface(final IAbstractActivity activity) { + public FilterUserInterface(final Activity activity) { this.activity = activity; this.res = cgeoapplication.getInstance().getResources(); @@ -73,10 +72,10 @@ public final class FilterUserInterface { } public void selectFilter(final RunnableWithArgument<IFilter> runAfterwards) { - final AlertDialog.Builder builder = new AlertDialog.Builder((Activity) activity); + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(R.string.caches_filter); - final ArrayAdapter<FactoryEntry> adapter = new ArrayAdapter<FactoryEntry>((Activity) activity, android.R.layout.select_dialog_item, registry); + final ArrayAdapter<FactoryEntry> adapter = new ArrayAdapter<FactoryEntry>(activity, android.R.layout.select_dialog_item, registry); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override @@ -107,10 +106,10 @@ public final class FilterUserInterface { return; } - final AlertDialog.Builder builder = new AlertDialog.Builder((Activity) activity); + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle(menuTitle); - final ArrayAdapter<IFilter> adapter = new ArrayAdapter<IFilter>((Activity) activity, android.R.layout.select_dialog_item, filters); + final ArrayAdapter<IFilter> adapter = new ArrayAdapter<IFilter>(activity, android.R.layout.select_dialog_item, filters); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { diff --git a/main/src/cgeo/geocaching/geopoint/HumanDistance.java b/main/src/cgeo/geocaching/geopoint/HumanDistance.java deleted file mode 100644 index 25d1bb7..0000000 --- a/main/src/cgeo/geocaching/geopoint/HumanDistance.java +++ /dev/null @@ -1,49 +0,0 @@ -package cgeo.geocaching.geopoint; - -import cgeo.geocaching.Settings; - -import org.apache.commons.lang3.tuple.ImmutablePair; - -public class HumanDistance { - - public static ImmutablePair<Double, String> scaleUnit(final double distanceKilometers) { - double distance; - String units; - if (Settings.isUseMetricUnits()) { - if (distanceKilometers >= 1) { - distance = distanceKilometers; - units = "km"; - } else { - distance = distanceKilometers * 1000; - units = "m"; - } - } else { - distance = distanceKilometers / IConversion.MILES_TO_KILOMETER; - if (distance >= 0.1) { - units = "mi"; - } else { - distance *= 5280; - units = "ft"; - } - } - return new ImmutablePair<Double, String>(distance, units); - } - - public static String getHumanDistance(final Float distanceKilometers) { - if (distanceKilometers == null) { - return "?"; - } - - final ImmutablePair<Double, String> scaled = scaleUnit(distanceKilometers); - String formatString; - if (scaled.left >= 100) { - formatString = "%.0f"; - } else if (scaled.left >= 10) { - formatString = "%.1f"; - } else { - formatString = "%.2f"; - } - - return String.format(formatString + " %s", scaled.left, scaled.right); - } -} diff --git a/main/src/cgeo/geocaching/geopoint/Units.java b/main/src/cgeo/geocaching/geopoint/Units.java new file mode 100644 index 0000000..3da3ad9 --- /dev/null +++ b/main/src/cgeo/geocaching/geopoint/Units.java @@ -0,0 +1,73 @@ +package cgeo.geocaching.geopoint; + +import cgeo.geocaching.Settings; + +import org.apache.commons.lang3.tuple.ImmutablePair; + +public class Units { + + public static ImmutablePair<Double, String> scaleDistance(final double distanceKilometers) { + double distance; + String units; + if (Settings.isUseMetricUnits()) { + if (distanceKilometers >= 1) { + distance = distanceKilometers; + units = "km"; + } else { + distance = distanceKilometers * 1000; + units = "m"; + } + } else { + distance = distanceKilometers / IConversion.MILES_TO_KILOMETER; + if (distance >= 0.1) { + units = "mi"; + } else { + distance *= 5280; + units = "ft"; + } + } + return new ImmutablePair<Double, String>(distance, units); + } + + public static String getDistanceFromKilometers(final Float distanceKilometers) { + if (distanceKilometers == null) { + return "?"; + } + + final ImmutablePair<Double, String> scaled = scaleDistance(distanceKilometers); + String formatString; + if (scaled.left >= 100) { + formatString = "%.0f"; + } else if (scaled.left >= 10) { + formatString = "%.1f"; + } else { + formatString = "%.2f"; + } + + return String.format(formatString + " %s", scaled.left, scaled.right); + } + + /** + * Get human readable elevation, depending on settings for metric units. + * Result is rounded to full meters/feet, as the sensors don't have that precision anyway. + * + * @param meters + * @return + */ + public static String getElevation(float meters) { + final ImmutablePair<Double, String> scaled = scaleDistance(meters / 1000f); + return (meters >= 0 ? "↥ " : "↧ ") + String.format("%d %s", Math.abs(Math.round(scaled.left)), scaled.right); + } + + public static String getDistanceFromMeters(float meters) { + return getDistanceFromKilometers(meters / 1000f); + } + + public static String getSpeed(float kilometersPerHour) { + final String speed = getDistanceFromKilometers(kilometersPerHour); + if (speed.endsWith("mi")) { + return speed.substring(0, speed.length() - 2) + "mph"; + } + return speed + (Settings.isUseMetricUnits() ? "/h" : "ph"); + } +} diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index ce9d4e4..cf90430 100644 --- a/main/src/cgeo/geocaching/maps/CGeoMap.java +++ b/main/src/cgeo/geocaching/maps/CGeoMap.java @@ -85,6 +85,19 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto /** max. number of caches displayed in the Live Map */ public static final int MAX_CACHES = 500; + /**Controls the behaviour of the map*/ + public enum MapMode { + /** Live Map where caches are loaded from online */ + LIVE_ONLINE, + /** Live Map where caches are loaded only from database */ + LIVE_OFFLINE, + /** Map around some coordinates */ + COORDS, + /** Map with a single cache (no reload on move) */ + SINGLE, + /** Map with a list of caches (no reload on move) */ + LIST + } /** Handler Messages */ private static final int HIDE_PROGRESS = 0; private static final int SHOW_PROGRESS = 1; @@ -99,6 +112,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto private static final String EXTRAS_WPTTYPE = "wpttype"; private static final String EXTRAS_MAPSTATE = "mapstate"; private static final String EXTRAS_SEARCH = "search"; + private static final String EXTRAS_MAP_MODE = "map_mode"; + private static final int MENU_SELECT_MAPVIEW = 1; private static final int MENU_MAP_LIVE = 2; private static final int MENU_STORE_CACHES = 3; @@ -129,7 +144,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto private WaypointType waypointTypeIntent = null; private int[] mapStateIntent = null; // status data - private SearchResult search = null; + /** Last search result used for displaying header */ + private SearchResult lastSearchResult = null; private String[] tokens = null; private boolean noMapTokenShowed = false; // map status data @@ -160,7 +176,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto private static final int[][] INSET_USERMODIFIEDCOORDS = { { 21, 28, 0, 0 }, { 19, 25, 0, 0 } }; // bottom right, 12x12 / 26x26 private static final int[][] INSET_PERSONALNOTE = { { 0, 28, 21, 0 }, { 0, 25, 19, 0 } }; // bottom left, 12x12 / 26x26 - private static SparseArray<LayerDrawable> overlaysCache = new SparseArray<LayerDrawable>(); + private SparseArray<LayerDrawable> overlaysCache = new SparseArray<LayerDrawable>(); private int cachesCnt = 0; /** List of caches in the viewport */ private LeastRecentlyUsedSet<cgCache> caches = null; @@ -173,8 +189,11 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto private long detailProgressTime = 0L; // views private ImageSwitcher myLocSwitch = null; + + /**Controls the map behaviour*/ + private MapMode mapMode = null; // other things - private boolean live = true; // live map (live, dead) or rest (displaying caches on map) + // 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 @@ -205,7 +224,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto // set title final StringBuilder title = new StringBuilder(); - if (live) { + if (mapMode == MapMode.LIVE_ONLINE) { title.append(res.getString(R.string.map_live)); } else { title.append(mapTitle); @@ -220,8 +239,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto title.append(']'); } - if (Settings.isDebug() && search != null && StringUtils.isNotBlank(search.getUrl())) { - title.append('[').append(search.getUrl()).append(']'); + if (Settings.isDebug() && lastSearchResult != null && StringUtils.isNotBlank(lastSearchResult.getUrl())) { + title.append('[').append(lastSearchResult.getUrl()).append(']'); } ActivityMixin.setTitle(activity, title.toString()); @@ -363,6 +382,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto // Get parameters from the intent final Bundle extras = activity.getIntent().getExtras(); if (extras != null) { + mapMode = (MapMode) extras.get(EXTRAS_MAP_MODE); searchIntent = (SearchResult) extras.getParcelable(EXTRAS_SEARCH); geocodeIntent = extras.getString(EXTRAS_GEOCODE); coordsIntent = (Geopoint) extras.getParcelable(EXTRAS_COORDS); @@ -370,6 +390,9 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto mapStateIntent = extras.getIntArray(EXTRAS_MAPSTATE); mapTitle = extras.getString(EXTRAS_MAP_TITLE); } + else { + mapMode = Settings.isLiveMap() ? MapMode.LIVE_ONLINE : MapMode.LIVE_OFFLINE; + } if (StringUtils.isBlank(mapTitle)) { mapTitle = res.getString(R.string.map_map); } @@ -429,11 +452,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto mapView.getMapController().setZoom(Settings.getMapZoom()); mapView.getMapController().setCenter(Settings.getMapCenter()); - // live map, if no arguments are given - live = (searchIntent == null && geocodeIntent == null && coordsIntent == null); - if (null == mapStateIntent) { - followMyLocation = live; + followMyLocation = mapMode == MapMode.LIVE_OFFLINE || mapMode == MapMode.LIVE_ONLINE; } else { followMyLocation = 1 == mapStateIntent[3]; if ((overlayCaches.getCircles() ? 1 : 0) != mapStateIntent[4]) { @@ -523,6 +543,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto mapView.destroyDrawingCache(); } + overlaysCache.clear(); + super.onPause(); } @@ -579,19 +601,14 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } item = menu.findItem(MENU_MAP_LIVE); // live map - if (live) { - if (Settings.isLiveMap()) { - item.setTitle(res.getString(R.string.map_live_disable)); - } else { - item.setTitle(res.getString(R.string.map_live_enable)); - } + if (mapMode == MapMode.LIVE_ONLINE) { + item.setTitle(res.getString(R.string.map_live_disable)); } else { - item.setEnabled(false); item.setTitle(res.getString(R.string.map_live_enable)); } final Set<String> geocodesInViewport = getGeocodesForCachesInViewport(); - menu.findItem(MENU_STORE_CACHES).setEnabled(live && !isLoading() && CollectionUtils.isNotEmpty(geocodesInViewport) && app.hasUnsavedCaches(new SearchResult(geocodesInViewport))); + menu.findItem(MENU_STORE_CACHES).setEnabled(isLiveMode() && !isLoading() && CollectionUtils.isNotEmpty(geocodesInViewport) && app.hasUnsavedCaches(new SearchResult(geocodesInViewport))); item = menu.findItem(MENU_CIRCLE_MODE); // show circles if (overlayCaches != null && overlayCaches.getCircles()) { @@ -601,9 +618,9 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } item = menu.findItem(MENU_AS_LIST); - item.setEnabled(live && !isLoading()); + item.setEnabled(isLiveMode() && !isLoading()); - menu.findItem(SUBMENU_STRATEGY).setEnabled(live); + menu.findItem(SUBMENU_STRATEGY).setEnabled(isLiveMode()); } catch (Exception e) { Log.e("cgeomap.onPrepareOptionsMenu: " + e.toString()); } @@ -611,6 +628,10 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto return true; } + private boolean isLiveMode() { + return mapMode == MapMode.LIVE_OFFLINE || mapMode == MapMode.LIVE_ONLINE; + } + @Override public boolean onOptionsItemSelected(MenuItem item) { final int id = item.getItemId(); @@ -621,8 +642,9 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto return true; case MENU_MAP_LIVE: Settings.setLiveMap(!Settings.isLiveMap()); + mapMode = Settings.isLiveMap() ? MapMode.LIVE_ONLINE : MapMode.LIVE_OFFLINE; liveChanged = true; - search = null; + lastSearchResult = null; searchIntent = null; ActivityMixin.invalidateOptionsMenu(activity); return true; @@ -797,6 +819,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } mapIntent.putExtra(EXTRAS_WPTTYPE, waypointTypeIntent != null ? waypointTypeIntent.id : null); mapIntent.putExtra(EXTRAS_MAP_TITLE, mapTitle); + mapIntent.putExtra(EXTRAS_MAP_MODE, mapMode); final int[] mapState = currentMapState(); if (mapState != null) { @@ -1013,7 +1036,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto if (liveChanged) { moved = true; - } else if (live && Settings.isLiveMap() && !downloaded) { + } else if (mapMode == MapMode.LIVE_ONLINE && !downloaded) { moved = true; } else if (viewport == null) { moved = true; @@ -1124,38 +1147,31 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto @Override public void run() { - /** - * True if we are currently showing the live map or a single points through coordsIntent. - */ - final boolean isLiveMapOrCoords = searchIntent == null && geocodeIntent == null; - try { showProgressHandler.sendEmptyMessage(SHOW_PROGRESS); loadThreadRun = System.currentTimeMillis(); + SearchResult searchResult; // stage 1 - pull and render from the DB only for live map - if (isLiveMapOrCoords) { - // live map - if (!live || !Settings.isLiveMap()) { - search = new SearchResult(app.getStoredInViewport(viewport, Settings.getCacheType())); - } else { - search = new SearchResult(app.getCachedInViewport(viewport, Settings.getCacheType())); - } + if (mapMode == MapMode.LIVE_ONLINE) { + searchResult = new SearchResult(app.getCachedInViewport(viewport, Settings.getCacheType())); + } else if (mapMode == MapMode.LIVE_OFFLINE) { + searchResult = new SearchResult(app.getStoredInViewport(viewport, Settings.getCacheType())); } else { // map started from another activity - search = new SearchResult(searchIntent); + searchResult = new SearchResult(searchIntent); if (geocodeIntent != null) { - search.addGeocode(geocodeIntent); + searchResult.addGeocode(geocodeIntent); } } - if (search != null) { - downloaded = true; - Set<cgCache> cachesFromSearchResult = search.getCachesFromSearchResult(LoadFlags.LOAD_WAYPOINTS); - caches.addAll(cachesFromSearchResult); - } + downloaded = true; + Set<cgCache> cachesFromSearchResult = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_WAYPOINTS); + // to update the caches they have to be removed first + caches.removeAll(cachesFromSearchResult); + caches.addAll(cachesFromSearchResult); - if (live) { + if (isLiveMode()) { final boolean excludeMine = Settings.isExcludeMyCaches(); final boolean excludeDisabled = Settings.isExcludeDisabledCaches(); @@ -1170,8 +1186,9 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto countVisibleCaches(); if (cachesCnt < Settings.getWayPointsThreshold() || geocodeIntent != null) { waypoints.clear(); - if (isLiveMapOrCoords) { + if (isLiveMode() || mapMode == MapMode.COORDS) { //All visible waypoints + //FIXME apply type filter waypoints.addAll(app.getWaypointsInViewport(viewport, Settings.isExcludeMyCaches(), Settings.isExcludeDisabledCaches())); } else { //All waypoints from the viewed caches @@ -1184,9 +1201,10 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto //render displayExecutor.execute(new DisplayRunnable(viewport)); - if (live && Settings.isLiveMap()) { + if (mapMode == MapMode.LIVE_ONLINE) { downloadExecutor.execute(new DownloadRunnable(viewport)); } + lastSearchResult = searchResult; } finally { showProgressHandler.sendEmptyMessage(HIDE_PROGRESS); // hide progress } @@ -1210,6 +1228,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto showProgressHandler.sendEmptyMessage(SHOW_PROGRESS); // show progress int count = 0; + SearchResult searchResult; do { if (tokens == null) { @@ -1219,10 +1238,10 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } } - search = ConnectorFactory.searchByViewport(viewport.resize(0.8), tokens); - if (search != null) { + searchResult = ConnectorFactory.searchByViewport(viewport.resize(0.8), tokens); + if (searchResult != null) { downloaded = true; - if (search.getError() == StatusCode.NOT_LOGGED_IN) { + if (searchResult.getError() == StatusCode.NOT_LOGGED_IN) { Login.login(); tokens = null; } else { @@ -1233,11 +1252,12 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } while (count < 2); - if (search != null) { - Set<cgCache> result = search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB); + if (searchResult != null) { + Set<cgCache> result = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB); // to update the caches they have to be removed first caches.removeAll(result); caches.addAll(result); + lastSearchResult = searchResult; } //render @@ -1277,7 +1297,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto if (!cachesToDisplay.isEmpty()) { // Only show waypoints for single view or setting // when less than showWaypointsthreshold Caches shown - if (cachesToDisplay.size() == 1 || (cachesCnt < Settings.getWayPointsThreshold())) { + if (mapMode == MapMode.SINGLE || (cachesCnt < Settings.getWayPointsThreshold())) { for (cgWaypoint waypoint : waypointsToDisplay) { if (waypoint == null || waypoint.getCoords() == null) { @@ -1642,6 +1662,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto public static void startActivitySearch(final Activity fromActivity, final SearchResult search, final String title) { final Intent mapIntent = newIntent(fromActivity); mapIntent.putExtra(EXTRAS_SEARCH, search); + mapIntent.putExtra(EXTRAS_MAP_MODE, MapMode.LIST); if (StringUtils.isNotBlank(title)) { mapIntent.putExtra(CGeoMap.EXTRAS_MAP_TITLE, title); } @@ -1649,11 +1670,14 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } public static void startActivityLiveMap(final Activity fromActivity) { - fromActivity.startActivity(newIntent(fromActivity)); + final Intent mapIntent = newIntent(fromActivity); + mapIntent.putExtra(EXTRAS_MAP_MODE, Settings.isLiveMap() ? MapMode.LIVE_ONLINE : MapMode.LIVE_OFFLINE); + fromActivity.startActivity(mapIntent); } public static void startActivityCoords(final Activity fromActivity, final Geopoint coords, final WaypointType type, final String title) { final Intent mapIntent = newIntent(fromActivity); + mapIntent.putExtra(EXTRAS_MAP_MODE, MapMode.COORDS); mapIntent.putExtra(EXTRAS_COORDS, coords); if (type != null) { mapIntent.putExtra(EXTRAS_WPTTYPE, type.id); @@ -1666,6 +1690,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto public static void startActivityGeoCode(final Activity fromActivity, final String geocode) { final Intent mapIntent = newIntent(fromActivity); + mapIntent.putExtra(EXTRAS_MAP_MODE, MapMode.SINGLE); mapIntent.putExtra(EXTRAS_GEOCODE, geocode); mapIntent.putExtra(EXTRAS_MAP_TITLE, geocode); fromActivity.startActivity(mapIntent); @@ -1705,7 +1730,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto .append(cache.getListId() > 0) .toHashCode(); - final LayerDrawable ldFromCache = CGeoMap.overlaysCache.get(hashcode); + final LayerDrawable ldFromCache = overlaysCache.get(hashcode); if (ldFromCache != null) { item.setMarker(ldFromCache); return item; @@ -1763,7 +1788,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto ld.setLayerInset(index++, inset[0], inset[1], inset[2], inset[3]); } - CGeoMap.overlaysCache.put(hashcode, ld); + overlaysCache.put(hashcode, ld); item.setMarker(ld); return item; diff --git a/main/src/cgeo/geocaching/maps/MapProviderFactory.java b/main/src/cgeo/geocaching/maps/MapProviderFactory.java index 3fb6ed0..7c0c8e5 100644 --- a/main/src/cgeo/geocaching/maps/MapProviderFactory.java +++ b/main/src/cgeo/geocaching/maps/MapProviderFactory.java @@ -15,12 +15,10 @@ public class MapProviderFactory { private final static int GOOGLEMAP_BASEID = 30; private final static int MFMAP_BASEID = 40; - private static MapProviderFactory instance = null; + private final static MapProvider[] mapProviders; + private final static SortedMap<Integer, MapSource> mapSources; - private final MapProvider[] mapProviders; - private SortedMap<Integer, MapSource> mapSources; - - private MapProviderFactory() { + static { // add GoogleMapProvider only if google api is available in order to support x86 android emulator if (isGoogleMapsInstalled()) { mapProviders = new MapProvider[] { new GoogleMapProvider(GOOGLEMAP_BASEID), new MapsforgeMapProvider(MFMAP_BASEID) }; @@ -45,29 +43,16 @@ public class MapProviderFactory { return googleMaps; } - private static synchronized void initInstance() { - if (null == instance) { - instance = new MapProviderFactory(); - } - } - - private static MapProviderFactory getInstance() { - if (null == instance) { - initInstance(); - } - return instance; - } - public static SortedMap<Integer, MapSource> getMapSources() { - return getInstance().mapSources; + return mapSources; } public static boolean isValidSourceId(int sourceId) { - return getInstance().mapSources.containsKey(sourceId); + return mapSources.containsKey(sourceId); } public static boolean isSameActivity(int sourceId1, int sourceId2) { - for (MapProvider mp : getInstance().mapProviders) { + for (MapProvider mp : mapProviders) { if (mp.isMySource(sourceId1) && mp.isMySource(sourceId2)) { return mp.isSameActivity(sourceId1, sourceId2); } @@ -76,17 +61,17 @@ public class MapProviderFactory { } public static MapProvider getMapProvider(int sourceId) { - for (MapProvider mp : getInstance().mapProviders) { + for (MapProvider mp : mapProviders) { if (mp.isMySource(sourceId)) { return mp; } } - return getInstance().mapProviders[0]; + return mapProviders[0]; } public static int getSourceOrdinalFromId(int sourceId) { int sourceOrdinal = 0; - for (int key : getInstance().mapSources.keySet()) { + for (int key : mapSources.keySet()) { if (sourceId == key) { return sourceOrdinal; } @@ -97,20 +82,18 @@ public class MapProviderFactory { public static int getSourceIdFromOrdinal(int sourceOrdinal) { int count = 0; - for (int key : getInstance().mapSources.keySet()) { + for (int key : mapSources.keySet()) { if (sourceOrdinal == count) { return key; } count++; } - return getInstance().mapSources.firstKey(); + return mapSources.firstKey(); } public static void addMapviewMenuItems(Menu parentMenu, int groupId, int currentSource) { - SortedMap<Integer, MapSource> mapSources = getInstance().mapSources; - - for (int key : mapSources.keySet()) { - parentMenu.add(groupId, key, 0, mapSources.get(key).getName()).setCheckable(true).setChecked(key == currentSource); + for (Integer key : mapSources.keySet()) { + parentMenu.add(groupId, key, 0, mapSources.get(key).getName()).setCheckable(true).setChecked(key.intValue() == currentSource); } } @@ -119,6 +102,6 @@ public class MapProviderFactory { } public static MapSource getMapSource(int sourceId) { - return getInstance().mapSources.get(sourceId); + return mapSources.get(Integer.valueOf(sourceId)); } } diff --git a/main/src/cgeo/geocaching/maps/ScaleOverlay.java b/main/src/cgeo/geocaching/maps/ScaleOverlay.java index 321624d..bee6acf 100644 --- a/main/src/cgeo/geocaching/maps/ScaleOverlay.java +++ b/main/src/cgeo/geocaching/maps/ScaleOverlay.java @@ -1,7 +1,7 @@ package cgeo.geocaching.maps; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.HumanDistance; +import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.GeoPointImpl; import cgeo.geocaching.maps.interfaces.MapProjectionImpl; @@ -62,7 +62,7 @@ public class ScaleOverlay implements GeneralOverlay { final Geopoint leftCoords = new Geopoint(center.getLatitudeE6() / 1e6, center.getLongitudeE6() / 1e6 - span / 2); final Geopoint rightCoords = new Geopoint(center.getLatitudeE6() / 1e6, center.getLongitudeE6() / 1e6 + span / 2); - final ImmutablePair<Double, String> scaled = HumanDistance.scaleUnit(leftCoords.distanceTo(rightCoords) * SCALE_WIDTH_FACTOR); + final ImmutablePair<Double, String> scaled = Units.scaleDistance(leftCoords.distanceTo(rightCoords) * SCALE_WIDTH_FACTOR); final double distanceRound = keepSignificantDigit(scaled.left); final double pixels = Math.round((mapView.getWidth() * SCALE_WIDTH_FACTOR / scaled.left) * distanceRound); diff --git a/main/src/cgeo/geocaching/network/HtmlImage.java b/main/src/cgeo/geocaching/network/HtmlImage.java index eac7063..288336d 100644 --- a/main/src/cgeo/geocaching/network/HtmlImage.java +++ b/main/src/cgeo/geocaching/network/HtmlImage.java @@ -9,6 +9,7 @@ import cgeo.geocaching.utils.ImageHelper; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; + import org.apache.commons.lang3.StringUtils; import android.content.Context; @@ -37,7 +38,10 @@ public class HtmlImage implements Html.ImageGetter { "flagcounter.com", "compteur-blog.net", "counter.digits.com", - "andyhoppe" + "andyhoppe", + "besucherzaehler-homepage.de", + "hitwebcounter.com", + "kostenloser-counter.eu" }; final private String geocode; diff --git a/main/src/cgeo/geocaching/sorting/ComparatorUserInterface.java b/main/src/cgeo/geocaching/sorting/ComparatorUserInterface.java new file mode 100644 index 0000000..bc63ef6 --- /dev/null +++ b/main/src/cgeo/geocaching/sorting/ComparatorUserInterface.java @@ -0,0 +1,118 @@ +package cgeo.geocaching.sorting; + +import cgeo.geocaching.R; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RunnableWithArgument; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.res.Resources; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +public class ComparatorUserInterface { + private final Activity activity; + private final ArrayList<ComparatorEntry> registry; + private final Resources res; + + private static final class ComparatorEntry { + private final String name; + private final Class<? extends CacheComparator> cacheComparator; + + public ComparatorEntry(final String name, final Class<? extends CacheComparator> cacheComparator) { + this.name = name; + this.cacheComparator = cacheComparator; + } + + @Override + public String toString() { + return name; + } + } + + public ComparatorUserInterface(final Activity activity) { + this.activity = activity; + res = activity.getResources(); + + registry = new ArrayList<ComparatorUserInterface.ComparatorEntry>(20); + + register(R.string.caches_sort_distance, null); + register(R.string.caches_sort_date_hidden, DateComparator.class); + register(R.string.caches_sort_difficulty, DifficultyComparator.class); + register(R.string.caches_sort_finds, FindsComparator.class); + register(R.string.caches_sort_gccode, GeocodeComparator.class); + register(R.string.caches_sort_inventory, InventoryComparator.class); + register(R.string.caches_sort_name, NameComparator.class); + register(R.string.caches_sort_favorites, PopularityComparator.class); + register(R.string.caches_sort_rating, RatingComparator.class); + register(R.string.caches_sort_size, SizeComparator.class); + register(R.string.caches_sort_state, StateComparator.class); + register(R.string.caches_sort_storage, StorageTimeComparator.class); + register(R.string.caches_sort_terrain, TerrainComparator.class); + register(R.string.caches_sort_date_logged, VisitComparator.class); + register(R.string.caches_sort_vote, VoteComparator.class); + + // sort the menu labels alphabetically for easier reading + Collections.sort(registry, new Comparator<ComparatorEntry>() { + + @Override + public int compare(ComparatorEntry lhs, ComparatorEntry rhs) { + return lhs.name.compareToIgnoreCase(rhs.name); + } + }); + } + + private void register(final int resourceId, Class<? extends CacheComparator> comparatorClass) { + registry.add(new ComparatorEntry(res.getString(resourceId), comparatorClass)); + } + + public void selectComparator(final CacheComparator current, final RunnableWithArgument<CacheComparator> runAfterwards) { + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(R.string.caches_sort_title); + + // adapter doesn't work correctly here, therefore using the string array based method + final String[] items = new String[registry.size()]; + for (int i = 0; i < items.length; i++) { + items[i] = registry.get(i).name; + } + builder.setSingleChoiceItems(items, getCurrentIndex(current), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int itemIndex) { + ComparatorEntry entry = registry.get(itemIndex); + try { + if (entry.cacheComparator == null) { + runAfterwards.run(null); + } + else { + CacheComparator comparator = entry.cacheComparator.newInstance(); + runAfterwards.run(comparator); + } + } catch (Exception e) { + Log.e("selectComparator", e); + } + dialog.dismiss(); + } + }); + + builder.create().show(); + } + + private int getCurrentIndex(final CacheComparator current) { + for (int index = 0; index < registry.size(); index++) { + final ComparatorEntry entry = registry.get(index); + if (current == null) { + if (entry.cacheComparator == null) { + return index; + } + } + else if (current.getClass().equals(entry.cacheComparator)) { + return index; + } + } + return -1; + } + +} diff --git a/main/src/cgeo/geocaching/sorting/DateComparator.java b/main/src/cgeo/geocaching/sorting/DateComparator.java index 7129905..3136d47 100644 --- a/main/src/cgeo/geocaching/sorting/DateComparator.java +++ b/main/src/cgeo/geocaching/sorting/DateComparator.java @@ -1,7 +1,9 @@ package cgeo.geocaching.sorting; import cgeo.geocaching.cgCache; +import cgeo.geocaching.cgeoapplication; +import java.util.ArrayList; import java.util.Date; /** @@ -16,10 +18,19 @@ public class DateComparator extends AbstractCacheComparator { @Override protected int compareCaches(cgCache cache1, cgCache cache2) { - Date date1 = cache1.getHiddenDate(); - Date date2 = cache2.getHiddenDate(); + final Date date1 = cache1.getHiddenDate(); + final Date date2 = cache2.getHiddenDate(); if (date1 != null && date2 != null) { - return date1.compareTo(date2); + final int dateDifference = date1.compareTo(date2); + // for equal dates, sort by distance + if (dateDifference == 0) { + final ArrayList<cgCache> list = new ArrayList<cgCache>(); + list.add(cache1); + list.add(cache2); + final DistanceComparator distanceComparator = new DistanceComparator(cgeoapplication.getInstance().currentGeo().getCoords(), list); + return distanceComparator.compare(cache1, cache2); + } + return dateDifference; } if (date1 != null) { return -1; diff --git a/main/src/cgeo/geocaching/sorting/FindsComparator.java b/main/src/cgeo/geocaching/sorting/FindsComparator.java index ad72ea3..6407b11 100644 --- a/main/src/cgeo/geocaching/sorting/FindsComparator.java +++ b/main/src/cgeo/geocaching/sorting/FindsComparator.java @@ -6,11 +6,7 @@ import cgeo.geocaching.enumerations.LogType; public class FindsComparator extends AbstractCacheComparator { - private cgeoapplication app; - - public FindsComparator(cgeoapplication app) { - this.app = app; - } + private final cgeoapplication app = cgeoapplication.getInstance(); @Override protected boolean canCompare(cgCache cache1, cgCache cache2) { @@ -25,15 +21,14 @@ public class FindsComparator extends AbstractCacheComparator { } private int getFindsCount(cgCache cache) { - int finds = 0; if (cache.getLogCounts().isEmpty()) { cache.setLogCounts(app.loadLogCounts(cache.getGeocode())); } Integer logged = cache.getLogCounts().get(LogType.FOUND_IT); if (logged != null) { - finds = logged; + return logged; } - return finds; + return 0; } } diff --git a/main/src/cgeo/geocaching/sorting/VisitComparator.java b/main/src/cgeo/geocaching/sorting/VisitComparator.java index 548ec7a..46d8c58 100644 --- a/main/src/cgeo/geocaching/sorting/VisitComparator.java +++ b/main/src/cgeo/geocaching/sorting/VisitComparator.java @@ -10,7 +10,7 @@ public class VisitComparator extends AbstractCacheComparator { @Override protected boolean canCompare(final cgCache cache1, final cgCache cache2) { - return cache1.getVisitedDate() > 0 && cache2.getVisitedDate() > 0; + return true; } @Override diff --git a/main/src/cgeo/geocaching/ui/AbstractUIFactory.java b/main/src/cgeo/geocaching/ui/AbstractUIFactory.java new file mode 100644 index 0000000..2351383 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/AbstractUIFactory.java @@ -0,0 +1,9 @@ +package cgeo.geocaching.ui; + +import cgeo.geocaching.cgeoapplication; + +import android.content.res.Resources; + +public class AbstractUIFactory { + protected final static Resources res = cgeoapplication.getInstance().getResources(); +} diff --git a/main/src/cgeo/geocaching/ui/AddressListAdapter.java b/main/src/cgeo/geocaching/ui/AddressListAdapter.java index 17a9289..eb8b516 100644 --- a/main/src/cgeo/geocaching/ui/AddressListAdapter.java +++ b/main/src/cgeo/geocaching/ui/AddressListAdapter.java @@ -4,7 +4,7 @@ import cgeo.geocaching.R; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.cgeocaches; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.HumanDistance; +import cgeo.geocaching.geopoint.Units; import org.apache.commons.lang3.StringUtils; @@ -73,7 +73,7 @@ public class AddressListAdapter extends ArrayAdapter<Address> { private CharSequence getDistanceText(final Address address) { if (location != null && address.hasLatitude() && address.hasLongitude()) { - return HumanDistance.getHumanDistance(location.distanceTo(new Geopoint(address.getLatitude(), address.getLongitude()))); + return Units.getDistanceFromKilometers(location.distanceTo(new Geopoint(address.getLatitude(), address.getLongitude()))); } return ""; diff --git a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java index 70fc2bb..2a83ddc 100644 --- a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java +++ b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java @@ -4,7 +4,7 @@ import cgeo.geocaching.R; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.HumanDistance; +import cgeo.geocaching.geopoint.Units; import org.apache.commons.lang3.StringUtils; @@ -148,7 +148,7 @@ public final class CacheDetailsCreator { } String text = "--"; if (distance != null) { - text = HumanDistance.getHumanDistance(distance); + text = Units.getDistanceFromKilometers(distance); } else if (cacheDistanceView != null) { // if there is already a distance in cacheDistance, use it instead of resetting to default. diff --git a/main/src/cgeo/geocaching/ui/CacheListAdapter.java b/main/src/cgeo/geocaching/ui/CacheListAdapter.java index fcb4e54..65d3fbc 100644 --- a/main/src/cgeo/geocaching/ui/CacheListAdapter.java +++ b/main/src/cgeo/geocaching/ui/CacheListAdapter.java @@ -7,7 +7,6 @@ import cgeo.geocaching.Settings; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheListType; -import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.filter.IFilter; import cgeo.geocaching.geopoint.Geopoint; @@ -18,7 +17,6 @@ import cgeo.geocaching.utils.AngleUtils; import cgeo.geocaching.utils.Log; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.HashCodeBuilder; import android.app.Activity; @@ -29,6 +27,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.text.Spannable; import android.text.Spanned; +import android.text.style.ForegroundColorSpan; import android.text.style.StrikethroughSpan; import android.util.SparseArray; import android.view.GestureDetector; @@ -264,19 +263,15 @@ public class CacheListAdapter extends ArrayAdapter<cgCache> { notifyDataSetChanged(); } - public void setActualCoordinates(final Geopoint coordsIn) { - if (coordsIn == null) { - return; - } - - coords = coordsIn; + public void setActualCoordinates(final Geopoint coords) { + this.coords = coords; updateSortByDistance(); for (final DistanceView distance : distances) { - distance.update(coordsIn); + distance.update(coords); } for (final CompassMiniView compass : compasses) { - compass.updateCurrentCoords(coordsIn); + compass.updateCurrentCoords(coords); } } @@ -296,7 +291,13 @@ public class CacheListAdapter extends ArrayAdapter<cgCache> { if (coords == null) { return; } + final ArrayList<cgCache> oldList = new ArrayList<cgCache>(list); Collections.sort(list, new DistanceComparator(coords, list)); + + // avoid an update if the list has not changed due to location update + if (list.equals(oldList)) { + return; + } notifyDataSetChanged(); lastSort = System.currentTimeMillis(); } @@ -311,7 +312,6 @@ public class CacheListAdapter extends ArrayAdapter<cgCache> { } azimuth = direction; - for (final CompassMiniView compass : compasses) { compass.updateAzimuth(azimuth); } @@ -395,6 +395,9 @@ public class CacheListAdapter extends ArrayAdapter<cgCache> { if (cache.isDisabled() || cache.isArchived()) { // strike cache.getNameSp().setSpan(new StrikethroughSpan(), 0, cache.getNameSp().toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } + if (cache.isArchived()) { + cache.getNameSp().setSpan(new ForegroundColorSpan(res.getColor(R.color.archived_cache_color)), 0, cache.getNameSp().toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } } holder.text.setText(cache.getNameSp(), TextView.BufferType.SPANNABLE); @@ -497,37 +500,9 @@ public class CacheListAdapter extends ArrayAdapter<cgCache> { holder.favourite.setBackgroundResource(favoriteBack); if (cacheListType == CacheListType.HISTORY && cache.getVisitedDate() > 0) { - final ArrayList<String> infos = new ArrayList<String>(); - infos.add(StringUtils.upperCase(cache.getGeocode())); - infos.add(Formatter.formatDate(cache.getVisitedDate())); - infos.add(Formatter.formatTime(cache.getVisitedDate())); - holder.info.setText(StringUtils.join(infos, Formatter.SEPARATOR)); + holder.info.setText(Formatter.formatCacheInfoHistory(cache)); } else { - final ArrayList<String> infos = new ArrayList<String>(); - if (StringUtils.isNotBlank(cache.getGeocode())) { - infos.add(cache.getGeocode()); - } - if (cache.hasDifficulty()) { - infos.add("D " + String.format("%.1f", cache.getDifficulty())); - } - if (cache.hasTerrain()) { - infos.add("T " + String.format("%.1f", cache.getTerrain())); - } - - // don't show "not chosen" for events and virtuals, that should be the normal case - if (cache.getSize() != CacheSize.UNKNOWN && cache.showSize()) { - infos.add(cache.getSize().getL10n()); - } else if (cache.isEventCache() && cache.getHiddenDate() != null) { - infos.add(Formatter.formatShortDate(cache.getHiddenDate().getTime())); - } - - if (cache.isPremiumMembersOnly()) { - infos.add(res.getString(R.string.cache_premium)); - } - if (cacheListType != CacheListType.OFFLINE && cacheListType != CacheListType.HISTORY && cache.getListId() > 0) { - infos.add(res.getString(R.string.cache_offline)); - } - holder.info.setText(StringUtils.join(infos, Formatter.SEPARATOR)); + holder.info.setText(Formatter.formatCacheInfoLong(cache, cacheListType)); } return v; diff --git a/main/src/cgeo/geocaching/ui/CompassView.java b/main/src/cgeo/geocaching/ui/CompassView.java index b328527..048e280 100644 --- a/main/src/cgeo/geocaching/ui/CompassView.java +++ b/main/src/cgeo/geocaching/ui/CompassView.java @@ -11,6 +11,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.util.AttributeSet; +import android.util.FloatMath; import android.view.View; public class CompassView extends View { @@ -136,9 +137,9 @@ public class CompassView extends View { // If the difference is smaller than 1 degree, do nothing as it // causes the arrow to vibrate. Round away from 0. if (diff > 1.0) { - offset = (float) Math.ceil(diff / 10.0); // for larger angles, rotate faster + offset = FloatMath.ceil(diff / 10.0f); // for larger angles, rotate faster } else if (diff < 1.0) { - offset = (float) Math.floor(diff / 10.0); + offset = FloatMath.floor(diff / 10.0f); } return AngleUtils.normalize(actual + offset); @@ -200,16 +201,16 @@ public class CompassView extends View { marginLeftTemp = (getWidth() - compassRoseWidth) / 2; marginTopTemp = (getHeight() - compassRoseHeight) / 2; - canvas.rotate((float) -azimuthTemp, canvasCenterX, canvasCenterY); + canvas.rotate(-azimuthTemp, canvasCenterX, canvasCenterY); canvas.drawBitmap(compassRose, marginLeftTemp, marginTopTemp, null); - canvas.rotate((float) azimuthTemp, canvasCenterX, canvasCenterY); + canvas.rotate(azimuthTemp, canvasCenterX, canvasCenterY); marginLeftTemp = (getWidth() - compassArrowWidth) / 2; marginTopTemp = (getHeight() - compassArrowHeight) / 2; - canvas.rotate((float) -azimuthRelative, canvasCenterX, canvasCenterY); + canvas.rotate(-azimuthRelative, canvasCenterX, canvasCenterY); canvas.drawBitmap(compassArrow, marginLeftTemp, marginTopTemp, null); - canvas.rotate((float) azimuthRelative, canvasCenterX, canvasCenterY); + canvas.rotate(azimuthRelative, canvasCenterX, canvasCenterY); marginLeftTemp = (getWidth() - compassOverlayWidth) / 2; marginTopTemp = (getHeight() - compassOverlayHeight) / 2; @@ -260,4 +261,4 @@ public class CompassView extends View { return result; } -}
\ No newline at end of file +} diff --git a/main/src/cgeo/geocaching/ui/DistanceView.java b/main/src/cgeo/geocaching/ui/DistanceView.java index 83de4aa..9611511 100644 --- a/main/src/cgeo/geocaching/ui/DistanceView.java +++ b/main/src/cgeo/geocaching/ui/DistanceView.java @@ -1,7 +1,7 @@ package cgeo.geocaching.ui; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.HumanDistance; +import cgeo.geocaching.geopoint.Units; import android.content.Context; import android.util.AttributeSet; @@ -30,11 +30,11 @@ public class DistanceView extends TextView { if (cacheCoords == null || coords == null) { return; } - setText(HumanDistance.getHumanDistance(coords.distanceTo(cacheCoords))); + setText(Units.getDistanceFromKilometers(coords.distanceTo(cacheCoords))); } public void setDistance(Float distance) { - setText("~" + HumanDistance.getHumanDistance(distance)); + setText("~" + Units.getDistanceFromKilometers(distance)); } public void clear() { diff --git a/main/src/cgeo/geocaching/ui/Formatter.java b/main/src/cgeo/geocaching/ui/Formatter.java index 6ee1a65..53a7276 100644 --- a/main/src/cgeo/geocaching/ui/Formatter.java +++ b/main/src/cgeo/geocaching/ui/Formatter.java @@ -1,10 +1,21 @@ package cgeo.geocaching.ui; +import cgeo.geocaching.R; +import cgeo.geocaching.cgCache; +import cgeo.geocaching.cgWaypoint; import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.enumerations.CacheListType; +import cgeo.geocaching.enumerations.CacheSize; +import cgeo.geocaching.enumerations.WaypointType; + +import org.apache.commons.lang3.StringUtils; import android.content.Context; import android.text.format.DateUtils; +import java.util.ArrayList; +import java.util.List; + public abstract class Formatter { /** Text separator used for formatting texts */ @@ -64,6 +75,26 @@ public abstract class Formatter { } /** + * Generate a numeric date string according to system-wide settings (locale, date format) + * such as "10/20/2010". Today and yesterday will be presented as strings "today" and "yesterday". + * + * @param date + * milliseconds since the epoch + * @return the formatted string + */ + public static String formatShortDateVerbally(long date) { + int diff = cgeo.geocaching.utils.DateUtils.daysSince(date); + switch (diff) { + case 0: + return cgeoapplication.getInstance().getString(R.string.log_today); + case 1: + return cgeoapplication.getInstance().getString(R.string.log_yesterday); + default: + return formatShortDate(date); + } + } + + /** * Generate a numeric date and time string according to system-wide settings (locale, * date format) such as "7 sept. at 12:35". * @@ -77,4 +108,64 @@ public abstract class Formatter { return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL); } + public static String formatCacheInfoLong(cgCache cache, CacheListType cacheListType) { + final ArrayList<String> infos = new ArrayList<String>(); + if (StringUtils.isNotBlank(cache.getGeocode())) { + infos.add(cache.getGeocode()); + } + + infos.add(Formatter.formatCacheInfoShort(cache)); + + if (cache.isPremiumMembersOnly()) { + infos.add(cgeoapplication.getInstance().getString(R.string.cache_premium)); + } + if (cacheListType != CacheListType.OFFLINE && cacheListType != CacheListType.HISTORY && cache.getListId() > 0) { + infos.add(cgeoapplication.getInstance().getString(R.string.cache_offline)); + } + return StringUtils.join(infos, Formatter.SEPARATOR); + } + + public static String formatCacheInfoShort(cgCache cache) { + final ArrayList<String> infos = new ArrayList<String>(); + if (cache.hasDifficulty()) { + infos.add("D " + String.format("%.1f", cache.getDifficulty())); + } + if (cache.hasTerrain()) { + infos.add("T " + String.format("%.1f", cache.getTerrain())); + } + + // don't show "not chosen" for events and virtuals, that should be the normal case + if (cache.getSize() != CacheSize.UNKNOWN && cache.showSize()) { + infos.add(cache.getSize().getL10n()); + } else if (cache.isEventCache() && cache.getHiddenDate() != null) { + infos.add(Formatter.formatShortDate(cache.getHiddenDate().getTime())); + } + return StringUtils.join(infos, Formatter.SEPARATOR); + } + + public static String formatCacheInfoHistory(cgCache cache) { + final ArrayList<String> infos = new ArrayList<String>(3); + infos.add(StringUtils.upperCase(cache.getGeocode())); + infos.add(Formatter.formatDate(cache.getVisitedDate())); + infos.add(Formatter.formatTime(cache.getVisitedDate())); + return StringUtils.join(infos, Formatter.SEPARATOR); + } + + public static String formatWaypointInfo(cgWaypoint waypoint) { + final List<String> infos = new ArrayList<String>(3); + if (WaypointType.ALL_TYPES_EXCEPT_OWN.contains(waypoint.getWaypointType())) { + infos.add(waypoint.getWaypointType().getL10n()); + } + if (cgWaypoint.PREFIX_OWN.equalsIgnoreCase(waypoint.getPrefix())) { + infos.add(cgeoapplication.getInstance().getString(R.string.waypoint_custom)); + } else { + if (StringUtils.isNotBlank(waypoint.getPrefix())) { + infos.add(waypoint.getPrefix()); + } + if (StringUtils.isNotBlank(waypoint.getLookup())) { + infos.add(waypoint.getLookup()); + } + } + return StringUtils.join(infos, Formatter.SEPARATOR); + } } diff --git a/main/src/cgeo/geocaching/ui/LoggingUI.java b/main/src/cgeo/geocaching/ui/LoggingUI.java new file mode 100644 index 0000000..0e048c3 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/LoggingUI.java @@ -0,0 +1,144 @@ +package cgeo.geocaching.ui; + +import cgeo.geocaching.LogEntry; +import cgeo.geocaching.R; +import cgeo.geocaching.Settings; +import cgeo.geocaching.cgCache; +import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.activity.IAbstractActivity; +import cgeo.geocaching.enumerations.LogType; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.ArrayAdapter; + +import java.util.ArrayList; +import java.util.List; + +public class LoggingUI extends AbstractUIFactory { + public static class LogTypeEntry { + private final LogType logType; + private final SpecialLogType specialLogType; + private final boolean isActive; + + public LogTypeEntry(final LogType logType, final SpecialLogType specialLogType, final boolean isActive) { + this.logType = logType; + this.specialLogType = specialLogType; + this.isActive = isActive; + } + + @Override + public String toString() { + if (logType == null) { + return specialLogType.getL10n(); + } + + String text = logType.getL10n(); + + if (isActive) { + text += " ✔"; + } + + return text; + } + } + + private enum SpecialLogType { + LOG_CACHE(R.string.cache_menu_visit), + CLEAR_LOG(R.string.log_clear); + + private final int stringId; + + private SpecialLogType(final int stringId) { + this.stringId = stringId; + } + + public final String getL10n() { + return res.getString(stringId); + } + } + + private static final int MENU_ICON_LOG_VISIT = R.drawable.ic_menu_edit; + private static final int MENU_LOG_VISIT = 100; + private static final int MENU_LOG_VISIT_OFFLINE = 101; + + public static void addMenuItems(final Menu menu, final cgCache cache) { + if (cache == null) { + return; + } + if (!cache.supportsLogging()) { + return; + } + if (Settings.getLogOffline()) { + menu.add(0, MENU_LOG_VISIT_OFFLINE, 0, res.getString(R.string.cache_menu_visit_offline)).setIcon(MENU_ICON_LOG_VISIT); + } + else { + menu.add(0, MENU_LOG_VISIT, 0, res.getString(R.string.cache_menu_visit)).setIcon(MENU_ICON_LOG_VISIT); + } + } + + public static boolean onMenuItemSelected(final MenuItem item, IAbstractActivity activity, cgCache cache) { + switch (item.getItemId()) { + case MENU_LOG_VISIT: + cache.logVisit(activity); + return true; + case MENU_LOG_VISIT_OFFLINE: + showOfflineMenu(cache, (Activity) activity); + return true; + default: + return false; + } + } + + private static void showOfflineMenu(final cgCache cache, final Activity activity) { + final LogEntry currentLog = cgeoapplication.getInstance().loadLogOffline(cache.getGeocode()); + final LogType currentLogType = currentLog == null ? null : currentLog.type; + + final List<LogType> logTypes = cache.getPossibleLogTypes(); + final ArrayList<LogTypeEntry> list = new ArrayList<LogTypeEntry>(); + for (LogType logType : logTypes) { + list.add(new LogTypeEntry(logType, null, logType == currentLogType)); + } + if (cache.isLogOffline()) { + list.add(new LogTypeEntry(null, SpecialLogType.CLEAR_LOG, false)); + } + list.add(new LogTypeEntry(null, SpecialLogType.LOG_CACHE, false)); + + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(R.string.cache_menu_visit_offline); + + final ArrayAdapter<LogTypeEntry> adapter = new ArrayAdapter<LogTypeEntry>(activity, android.R.layout.select_dialog_item, list); + + builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int item) { + final LogTypeEntry logTypeEntry = adapter.getItem(item); + if (logTypeEntry.logType == null) { + switch (logTypeEntry.specialLogType) { + case LOG_CACHE: + cache.logVisit((IAbstractActivity) activity); + break; + + case CLEAR_LOG: + cgeoapplication.getInstance().clearLogOffline(cache.getGeocode()); + } + } else { + cache.logOffline(activity, logTypeEntry.logType); + } + } + }); + + builder.create().show(); + + } + + public static void onPrepareOptionsMenu(Menu menu) { + final MenuItem item = menu.findItem(MENU_LOG_VISIT); + if (item != null) { + item.setEnabled(Settings.isLogin()); + } + } +} diff --git a/main/src/cgeo/geocaching/utils/DateUtils.java b/main/src/cgeo/geocaching/utils/DateUtils.java new file mode 100644 index 0000000..3004bdb --- /dev/null +++ b/main/src/cgeo/geocaching/utils/DateUtils.java @@ -0,0 +1,18 @@ +package cgeo.geocaching.utils; + +import java.util.Calendar; + +public class DateUtils { + public static int daysSince(long date) { + final Calendar logDate = Calendar.getInstance(); + logDate.setTimeInMillis(date); + logDate.set(Calendar.SECOND, 0); + logDate.set(Calendar.MINUTE, 0); + logDate.set(Calendar.HOUR, 0); + final Calendar today = Calendar.getInstance(); + today.set(Calendar.SECOND, 0); + today.set(Calendar.MINUTE, 0); + today.set(Calendar.HOUR, 0); + return (int) Math.round((today.getTimeInMillis() - logDate.getTimeInMillis()) / 86400000d); + } +} diff --git a/main/src/cgeo/geocaching/utils/UnknownTagsHandler.java b/main/src/cgeo/geocaching/utils/UnknownTagsHandler.java index 149605d..68bdb1f 100644 --- a/main/src/cgeo/geocaching/utils/UnknownTagsHandler.java +++ b/main/src/cgeo/geocaching/utils/UnknownTagsHandler.java @@ -12,7 +12,7 @@ public class UnknownTagsHandler implements TagHandler { private static final int UNDEFINED_POSITION = -1; private static int countCells = 0; int strikePos = UNDEFINED_POSITION; - private boolean tableDetected = false; + private boolean problematicDetected = false; @Override public void handleTag(boolean opening, String tag, Editable output, @@ -20,11 +20,13 @@ public class UnknownTagsHandler implements TagHandler { if (tag.equalsIgnoreCase("strike") || tag.equals("s")) { handleStrike(opening, output); } else if (tag.equalsIgnoreCase("table")) { - handleTable(); + handleProblematic(); } else if (tag.equalsIgnoreCase("td")) { handleTd(opening, output); } else if (tag.equalsIgnoreCase("tr")) { handleTr(opening, output); + } else if (tag.equalsIgnoreCase("pre")) { + handleProblematic(); } } @@ -40,12 +42,12 @@ public class UnknownTagsHandler implements TagHandler { } } - public boolean isTableDetected() { - return tableDetected; + public boolean isProblematicDetected() { + return problematicDetected; } - private void handleTable() { - tableDetected = true; + private void handleProblematic() { + problematicDetected = true; } private static void handleTd(boolean opening, Editable output) { |
