diff options
| author | blafoo <github@blafoo.de> | 2011-08-29 21:22:02 +0200 |
|---|---|---|
| committer | blafoo <github@blafoo.de> | 2011-08-29 21:22:02 +0200 |
| commit | c49f2c54eea35ddd69121503fc604c75326913f9 (patch) | |
| tree | e0c78545204c9374239206ed28a8c2c97ea80488 /src/cgeo/geocaching | |
| parent | fcf623e1aeaf1ca5c891ad59605d4e8759dfadc1 (diff) | |
| parent | 48ec48f87f7b736411f28cb76ac1add8c61d2e10 (diff) | |
| download | cgeo-c49f2c54eea35ddd69121503fc604c75326913f9.zip cgeo-c49f2c54eea35ddd69121503fc604c75326913f9.tar.gz cgeo-c49f2c54eea35ddd69121503fc604c75326913f9.tar.bz2 | |
Merge remote-tracking branch 'upstream/master' into javadoc
Diffstat (limited to 'src/cgeo/geocaching')
42 files changed, 1032 insertions, 577 deletions
diff --git a/src/cgeo/geocaching/LogTemplateProvider.java b/src/cgeo/geocaching/LogTemplateProvider.java index a6beda3..210f863 100644 --- a/src/cgeo/geocaching/LogTemplateProvider.java +++ b/src/cgeo/geocaching/LogTemplateProvider.java @@ -62,6 +62,14 @@ public class LogTemplateProvider { return base.formatTime(System.currentTimeMillis());
}
},
+ new LogTemplate("DATETIME", R.string.init_signature_template_datetime) {
+
+ @Override
+ String getValue(final cgBase base) {
+ final long currentTime = System.currentTimeMillis();
+ return base.formatFullDate(currentTime) + " " + base.formatTime(currentTime);
+ }
+ },
new LogTemplate("USER", R.string.init_signature_template_user) {
@Override
@@ -117,7 +125,7 @@ public class LogTemplateProvider { int findCount = -1;
try {
- final Pattern findPattern = Pattern.compile("<strong><img.+?icon_smile.+?title=\"Caches Found\" /> ([,\\d]+)</strong>", Pattern.CASE_INSENSITIVE);
+ final Pattern findPattern = Pattern.compile("<strong><img.+?icon_smile.+?title=\"Caches Found\" /> ([,\\d]+)", Pattern.CASE_INSENSITIVE);
final Matcher findMatcher = findPattern.matcher(page);
if (findMatcher.find()) {
if (findMatcher.groupCount() > 0) {
diff --git a/src/cgeo/geocaching/UnknownTagsHandler.java b/src/cgeo/geocaching/UnknownTagsHandler.java new file mode 100644 index 0000000..f1b418c --- /dev/null +++ b/src/cgeo/geocaching/UnknownTagsHandler.java @@ -0,0 +1,33 @@ +package cgeo.geocaching; + +import org.xml.sax.XMLReader; + +import android.text.Editable; +import android.text.Html.TagHandler; +import android.text.Spannable; +import android.text.style.StrikethroughSpan; + +public class UnknownTagsHandler implements TagHandler { + + private static final int UNDEFINED_POSITION = -1; + int strikePos = UNDEFINED_POSITION; + + public void handleTag(boolean opening, String tag, Editable output, + XMLReader xmlReader) { + if(tag.equalsIgnoreCase("strike") || tag.equals("s")) { + handleStrike(opening, output); + } + } + + private void handleStrike(boolean opening, Editable output) { + int length = output.length(); + if(opening) { + strikePos = length; + } else { + if (strikePos > UNDEFINED_POSITION) { + output.setSpan(new StrikethroughSpan(), strikePos, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + strikePos = UNDEFINED_POSITION; + } + } + } +} diff --git a/src/cgeo/geocaching/activity/ActivityMixin.java b/src/cgeo/geocaching/activity/ActivityMixin.java index 68a4403..acbb405 100644 --- a/src/cgeo/geocaching/activity/ActivityMixin.java +++ b/src/cgeo/geocaching/activity/ActivityMixin.java @@ -120,6 +120,9 @@ public final class ActivityMixin { } protected static void addVisitMenu(IAbstractActivity activity, Menu menu, cgCache cache) { + if (cache == null) { + return; + } if (!cache.supportsLogging()) { return; } diff --git a/src/cgeo/geocaching/cgBase.java b/src/cgeo/geocaching/cgBase.java index 92082a3..2483438 100644 --- a/src/cgeo/geocaching/cgBase.java +++ b/src/cgeo/geocaching/cgBase.java @@ -86,6 +86,7 @@ public class cgBase { private final static Pattern patternFavourite = Pattern.compile("<a id=\"uxFavContainerLink\"[^>]*>[^<]*<div[^<]*<span class=\"favorite-value\">[^\\d]*([0-9]+)[^\\d^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); private final static Pattern patternFound = Pattern.compile("<p>[^<]*<a id=\"ctl00_ContentBody_hlFoundItLog\"[^<]*<img src=\".*/images/stockholm/16x16/check\\.gif\"[^>]*>[^<]*</a>[^<]*</p>", Pattern.CASE_INSENSITIVE); + private final static Pattern patternFoundAlternative = Pattern.compile("<div class=\"StatusInformationWidget FavoriteWidget\"", Pattern.CASE_INSENSITIVE); private final static Pattern patternLatLon = Pattern.compile("<span id=\"ctl00_ContentBody_LatLon\"[^>]*>(<b>)?([^<]*)(<\\/b>)?<\\/span>", Pattern.CASE_INSENSITIVE); private final static Pattern patternLocation = Pattern.compile("<span id=\"ctl00_ContentBody_Location\"[^>]*>In ([^<]*)", Pattern.CASE_INSENSITIVE); private final static Pattern patternHint = Pattern.compile("<p>([^<]*<strong>)?\\W*Additional Hints([^<]*<\\/strong>)?[^\\(]*\\(<a[^>]+>Encrypt</a>\\)[^<]*<\\/p>[^<]*<div id=\"div_hint\"[^>]*>(.*)</div>[^<]*<div id=[\\'|\"]dk[\\'|\"]", Pattern.CASE_INSENSITIVE); @@ -94,7 +95,6 @@ public class cgBase { private final static Pattern patternDesc = Pattern.compile("<span id=\"ctl00_ContentBody_LongDescription\"[^>]*>" + "(.*)</span>[^<]*</div>[^<]*<p>[^<]*</p>[^<]*<p>[^<]*<strong>\\W*Additional Hints</strong>", Pattern.CASE_INSENSITIVE); private final static Pattern patternCountLogs = Pattern.compile("<span id=\"ctl00_ContentBody_lblFindCounts\"><p(.+?)<\\/p><\\/span>", Pattern.CASE_INSENSITIVE); private final static Pattern patternCountLog = Pattern.compile("src=\"\\/images\\/icons\\/(.+?).gif\"[^>]+> (\\d+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); - //private final static Pattern patternLogs = Pattern.compile("<table class=\"LogsTable\">(.*?)</table>\\s*<p", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); private final static Pattern patternLog = Pattern.compile("<tr><td class.+?<a href=\"/profile/\\?guid=.+?>(.+?)</a>.+?(?:logOwnerStats[^>]+><img[^>]+icon_smile.+?> ([,\\d]+).+?)?LogType.+?<img.+?/images/icons/([^\\.]+)\\..+?title=\"(.+?)\".+?LogDate.+?>(.+?)<.+?LogText.+?>(.*?)</p>(.*?)</div></div></div></td></tr>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); private final static Pattern patternLogImgs = Pattern.compile("href=\"(http://img.geocaching.com/cache/log/.+?)\".+?<span>([^<]*)", Pattern.CASE_INSENSITIVE); private final static Pattern patternAttributes = Pattern.compile("<h3 class=\"WidgetHeader\">[^<]*<img[^>]+>\\W*Attributes[^<]*</h3>[^<]*<div class=\"WidgetBody\">(([^<]*<img src=\"[^\"]+\" alt=\"[^\"]+\"[^>]*>)+)[^<]*<p", Pattern.CASE_INSENSITIVE); @@ -125,22 +125,22 @@ public class cgBase { "yyyy/MM/dd", "dd/MMM/yyyy", "MMM/dd/yyyy", - "dd MMM yy" + "dd MMM yy", + "dd/MM/yyyy" }; - + HashMap<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>(); - + for (String format : formats) { map.put(format, new SimpleDateFormat(format, Locale.ENGLISH)); } - + gcCustomDateFormats = Collections.unmodifiableMap(map); } public final static SimpleDateFormat dateTbIn1 = new SimpleDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 public final static SimpleDateFormat dateTbIn2 = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 public final static SimpleDateFormat dateSqlIn = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 2010-07-25 14:44:01 - public final static SimpleDateFormat dateGPXIn = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); // 2010-04-20T07:00:00Z private Resources res = null; private HashMap<String, String> cookies = new HashMap<String, String>(); private static final String passMatch = "[/\\?&]*[Pp]ass(word)?=[^&^#^$]+"; @@ -1313,14 +1313,17 @@ public class cgBase { } // cache found - try { - final Matcher matcherFound = patternFound.matcher(page); - if (matcherFound.find()) { - if (matcherFound.group() != null && matcherFound.group().length() > 0) { - cache.found = true; - } + try + { + final Matcher matcherFound = patternFound.matcher(page); + final Matcher matcherFoundAlternative = patternFoundAlternative.matcher(page); + + if (matcherFound.find() || matcherFoundAlternative.find()) { + cache.found = true; } - } catch (Exception e) { + } + catch (Exception e) + { // failed to parse found Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse found"); } @@ -1571,7 +1574,7 @@ public class cgBase { try { final Matcher matcherLogCounts = patternCountLogs.matcher(page); - + if (matcherLogCounts.find()) { final Matcher matcherLog = patternCountLog.matcher(matcherLogCounts.group(1)); @@ -1580,7 +1583,7 @@ public class cgBase { { String typeStr = matcherLog.group(1); String countStr = matcherLog.group(2); - + if (typeStr != null && typeStr.length() > 0 && logTypes.containsKey(typeStr.toLowerCase()) @@ -1601,72 +1604,67 @@ public class cgBase { // cache logs try { -// final Matcher matcherLogs = patternLogs.matcher(page); -// -// if (matcherLogs.find()) -// { - /* - 1- Author - 2- Finds-count - 3- Log type image name (e.g. "icon_smile") - 4- Type string (e.g. "Found it") - 5- Date string (e.g. "04/28/2010") - 6- Log text - 7- The rest (e.g. log-images, maybe faster) - */ - final Matcher matcherLog = patternLog.matcher(page);//(matcherLogs.group(1)); + /* + 1- Author + 2- Finds-count + 3- Log type image name (e.g. "icon_smile") + 4- Type string (e.g. "Found it") + 5- Date string (e.g. "04/28/2010") + 6- Log text + 7- The rest (e.g. log-images, maybe faster) + */ + final Matcher matcherLog = patternLog.matcher(page);//(matcherLogs.group(1)); + + while (matcherLog.find()) + { + final cgLog logDone = new cgLog(); - while (matcherLog.find()) + if (logTypes.containsKey(matcherLog.group(3).toLowerCase())) { - final cgLog logDone = new cgLog(); - - if (logTypes.containsKey(matcherLog.group(3).toLowerCase())) - { - logDone.type = logTypes.get(matcherLog.group(3).toLowerCase()); - } - else - { - logDone.type = logTypes.get("icon_note"); - } + logDone.type = logTypes.get(matcherLog.group(3).toLowerCase()); + } + else + { + logDone.type = logTypes.get("icon_note"); + } - try - { - logDone.date = parseGcCustomDate(matcherLog.group(5)).getTime(); - } - catch (ParseException e) - { - Log.w(cgSettings.tag, "Failed to parse log date."); - } + try + { + logDone.date = parseGcCustomDate(matcherLog.group(5)).getTime(); + } + catch (ParseException e) + { + Log.w(cgSettings.tag, "Failed to parse log date."); + } - logDone.author = Html.fromHtml(matcherLog.group(1)).toString(); + logDone.author = Html.fromHtml(matcherLog.group(1)).toString(); - if (null != matcherLog.group(2)) - { - logDone.found = Integer.parseInt(matcherLog.group(2).replaceAll(",", "")); - } + if (null != matcherLog.group(2)) + { + logDone.found = Integer.parseInt(matcherLog.group(2).replaceAll(",", "")); + } - logDone.log = matcherLog.group(6); + logDone.log = matcherLog.group(6); - final Matcher matcherImg = patternLogImgs.matcher(matcherLog.group(7)); - while (matcherImg.find()) + final Matcher matcherImg = patternLogImgs.matcher(matcherLog.group(7)); + while (matcherImg.find()) + { + final cgImage logImage = new cgImage(); + logImage.url = matcherImg.group(1); + logImage.title = matcherImg.group(2); + if (logDone.logImages == null) { - final cgImage logImage = new cgImage(); - logImage.url = matcherImg.group(1); - logImage.title = matcherImg.group(2); - if (logDone.logImages == null) - { - logDone.logImages = new ArrayList<cgImage>(); - } - logDone.logImages.add(logImage); + logDone.logImages = new ArrayList<cgImage>(); } + logDone.logImages.add(logImage); + } - if (null == cache.logs) - { - cache.logs = new ArrayList<cgLog>(); - } - cache.logs.add(logDone); + if (null == cache.logs) + { + cache.logs = new ArrayList<cgLog>(); } -// } + cache.logs.add(logDone); + } } catch (Exception e) { @@ -1896,16 +1894,27 @@ public class cgBase { // And BTW: You cannot even see that effect in the debugger, but must use a separate memory profiler! } - private static Date parseGcCustomDate(String input) + public Date parseGcCustomDate(String input) throws ParseException { if (input == null) { throw new ParseException("Input is null", 0); } - + input = input.trim(); + if (null != settings + //&& null != settings.getGcCustomDate() + && gcCustomDateFormats.containsKey(settings.getGcCustomDate())) + { + try + { + return gcCustomDateFormats.get(settings.getGcCustomDate()).parse(input); + } + catch (ParseException e) {} + } + for (SimpleDateFormat format : gcCustomDateFormats.values()) { try @@ -1914,9 +1923,25 @@ public class cgBase { } catch (ParseException e) {} } - + throw new ParseException("No matching pattern", 0); } + + public void detectGcCustomDate() + { + final String host = "www.geocaching.com"; + final String path = "/account/ManagePreferences.aspx"; + + final String result = request(false, host, path, "GET", null, false, false, false).getData(); + + final Pattern pattern = Pattern.compile("<option selected=\"selected\" value=\"([ /Mdy-]+)\">", Pattern.CASE_INSENSITIVE); + final Matcher matcher = pattern.matcher(result); + + if (matcher.find()) + { + settings.setGcCustomDate(matcher.group(1)); + } + } public cgRating getRating(String guid, String geocode) { ArrayList<String> guids = null; @@ -2079,10 +2104,10 @@ public class cgBase { final Pattern patternSpottedOwner = Pattern.compile("<dt>\\W*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\">In the hands of the owner[^<]*</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); final Pattern patternGoal = Pattern.compile("<h3>\\W*Current GOAL[^<]*</h3>[^<]*<p[^>]*>(.*)</p>[^<]*<h3>\\W*About This Item[^<]*</h3>", Pattern.CASE_INSENSITIVE); final Pattern patternDetailsImage = Pattern.compile("<h3>\\W*About This Item[^<]*</h3>([^<]*<p>([^<]*<img id=\"ctl00_ContentBody_BugDetails_BugImage\" class=\"[^\"]+\" src=\"([^\"]+)\"[^>]*>)?[^<]*</p>)?[^<]*<p[^>]*>(.*)</p>[^<]*<div id=\"ctl00_ContentBody_BugDetails_uxAbuseReport\">", Pattern.CASE_INSENSITIVE); - final Pattern patternLogs = Pattern.compile("<table class=\"TrackableItemLogTable Table\">(.*)<\\/table>[^<]*<ul", Pattern.CASE_INSENSITIVE); final Pattern patternIcon = Pattern.compile("<img id=\"ctl00_ContentBody_BugTypeImage\" class=\"TravelBugHeaderIcon\" src=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); final Pattern patternType = Pattern.compile("<img id=\"ctl00_ContentBody_BugTypeImage\" class=\"TravelBugHeaderIcon\" src=\"[^\"]+\" alt=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); final Pattern patternDistance = Pattern.compile("<h4[^>]*\\W*Tracking History \\(([0-9\\.,]+(km|mi))[^\\)]*\\)", Pattern.CASE_INSENSITIVE); + final Pattern patternLog = Pattern.compile("<tr class=\"Data.+?src=\"/images/icons/([^\\.]+).gif[^>]+> ([^<]+)</td>.+?guid.+?>([^<]+)</a>.+?guid=([^\"]+)\">([^<]+)</a>.+?<td colspan=\"4\">(.+?)(?:<ul.+?ul>)?\\s*</td>\\s*</tr>", Pattern.CASE_INSENSITIVE); final cgTrackable trackable = new cgTrackable(); @@ -2289,90 +2314,47 @@ public class cgBase { } // trackable logs - try { - final Matcher matcherLogs = patternLogs.matcher(page); - while (matcherLogs.find()) { - if (matcherLogs.groupCount() > 0) { - final Pattern patternLog = Pattern.compile("[^>]*>" + - "[^<]*<td[^<]*<img src=[\"|'].*\\/icons\\/([^\\.]+)\\.[a-z]{2,5}[\"|'][^>]*> (\\d+).(\\d+).(\\d+)[^<]*</td>" + - "[^<]*<td>[^<]*<a href=[^>]+>([^<]+)<.a>([^<]*|[^<]*<a href=[\"|'].*guid=([^\"]*)\">([^<]*)</a>[^<]*)</td>" + - "[^<]*<td>[^<]*</td>" + - "[^<]*<td[^<]*<a href=[^>]+>[^<]+</a>[^<]*</td>[^<]*</tr>" + - "[^<]*<tr[^>]*>[^<]*<td[^>]*>(.*?)</td>[^<]*</tr>.*" + - ""); - // 1 filename == type - // 2 month - // 3 date - // 4 year - // 5 user - // 6 action dependent - // 7 cache guid - // 8 cache name - // 9 text - final String[] logs = matcherLogs.group(1).split("<tr class=\"Data BorderTop"); - final int logsCnt = logs.length; - - for (int k = 1; k < logsCnt; k++) { - final Matcher matcherLog = patternLog.matcher(logs[k]); - if (matcherLog.find()) { - final cgLog logDone = new cgLog(); - - String logTmp = matcherLog.group(9); - logTmp = Pattern.compile("<p>").matcher(logTmp).replaceAll("\n"); - logTmp = Pattern.compile("<br[^>]*>").matcher(logTmp).replaceAll("\n"); - logTmp = Pattern.compile("<\\/p>").matcher(logTmp).replaceAll(""); - logTmp = Pattern.compile("\r+").matcher(logTmp).replaceAll("\n"); - - int day = -1; - try { - day = Integer.parseInt(matcherLog.group(3)); - } catch (Exception e) { - Log.w(cgSettings.tag, "Failed to parse logs date (day): " + e.toString()); - } - - int month = -1; - try { - month = Integer.parseInt(matcherLog.group(2)); - month -= 1; - } catch (Exception e) { - Log.w(cgSettings.tag, "Failed to parse logs date (month): " + e.toString()); - } - - int year = -1; - try { - year = Integer.parseInt(matcherLog.group(4)); - } catch (Exception e) { - Log.w(cgSettings.tag, "Failed to parse logs date (year): " + e.toString()); - } - - long logDate; - if (year > 0 && month >= 0 && day > 0) { - Calendar date = Calendar.getInstance(); - date.set(year, month, day, 12, 0, 0); - logDate = date.getTimeInMillis(); - logDate = (logDate / 1000L) * 1000L; - } else { - logDate = 0; - } - - if (logTypes.containsKey(matcherLog.group(1).toLowerCase())) { - logDone.type = logTypes.get(matcherLog.group(1).toLowerCase()); - } else { - logDone.type = logTypes.get("icon_note"); - } - - logDone.author = Html.fromHtml(matcherLog.group(5)).toString(); - logDone.date = logDate; - logDone.log = logTmp; - if (matcherLog.group(7) != null && matcherLog.group(8) != null) { - logDone.cacheGuid = matcherLog.group(7); - logDone.cacheName = matcherLog.group(8); - } - - trackable.logs.add(logDone); - } - } + try + { + final Matcher matcherLogs = patternLog.matcher(page); + /* + 1. Type (img) + 2. Date + 3. Author + 4. Cache-GUID + 5. Cache-name + 6. Logtext + */ + while (matcherLogs.find()) + { + final cgLog logDone = new cgLog(); + + if (logTypes.containsKey(matcherLogs.group(1).toLowerCase())) + { + logDone.type = logTypes.get(matcherLogs.group(1).toLowerCase()); + } + else + { + logDone.type = logTypes.get("icon_note"); + } + + logDone.author = Html.fromHtml(matcherLogs.group(3)).toString(); + + try + { + logDone.date = parseGcCustomDate(matcherLogs.group(2)).getTime(); + } + catch (ParseException e) {} + + logDone.log = matcherLogs.group(6).trim(); + + if (matcherLogs.group(4) != null && matcherLogs.group(5) != null) + { + logDone.cacheGuid = matcherLogs.group(4); + logDone.cacheName = matcherLogs.group(5); } + + trackable.logs.add(logDone); } } catch (Exception e) { // failed to parse logs diff --git a/src/cgeo/geocaching/cgCache.java b/src/cgeo/geocaching/cgCache.java index 72c1e86..d528aac 100644 --- a/src/cgeo/geocaching/cgCache.java +++ b/src/cgeo/geocaching/cgCache.java @@ -296,9 +296,15 @@ public class cgCache { return true; } - public boolean logOffline(final IAbstractActivity fromActivity, final int logType) { - logOffline(fromActivity, "", Calendar.getInstance(), logType); - return true; + public boolean logOffline(final IAbstractActivity fromActivity, final int logType, final cgSettings settings, final cgBase base) { + String log = ""; + if (settings.getSignature() != null + && settings.signatureAutoinsert + && settings.getSignature().length() > 0) { + log = LogTemplateProvider.applyTemplates(settings.getSignature(), base); + } + logOffline(fromActivity, log, Calendar.getInstance(), logType); + return true; } void logOffline(final IAbstractActivity fromActivity, final String log, Calendar date, final int logType) { diff --git a/src/cgeo/geocaching/cgSettings.java b/src/cgeo/geocaching/cgSettings.java index b2d2cf5..7c6be93 100644 --- a/src/cgeo/geocaching/cgSettings.java +++ b/src/cgeo/geocaching/cgSettings.java @@ -56,6 +56,7 @@ public class cgSettings { private static final String KEY_COORD_INPUT_FORMAT = "coordinputformat"; private static final String KEY_LOG_OFFLINE = "log_offline"; private static final String KEY_LOAD_DIRECTION_IMG = "loaddirectionimg"; + private static final String KEY_GC_CUSTOM_DATE = "gccustomdate"; private interface PrefRunnable { void edit(final Editor edit); @@ -608,4 +609,18 @@ public class cgSettings { public boolean getLoadDirImg() { return prefs.getBoolean(KEY_LOAD_DIRECTION_IMG, true); } + + void setGcCustomDate(final String format) { + editSettings(new PrefRunnable() { + + @Override + public void edit(Editor edit) { + edit.putString(KEY_GC_CUSTOM_DATE, format); + } + }); + } + + public String getGcCustomDate() { + return prefs.getString(KEY_GC_CUSTOM_DATE, null); + } } diff --git a/src/cgeo/geocaching/cgeo.java b/src/cgeo/geocaching/cgeo.java index 3a509db..df167fb 100644 --- a/src/cgeo/geocaching/cgeo.java +++ b/src/cgeo/geocaching/cgeo.java @@ -12,6 +12,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.location.Address; import android.location.Geocoder; @@ -31,6 +32,15 @@ import cgeo.geocaching.activity.ActivityMixin; public class cgeo extends AbstractActivity { + private static final String SCAN_INTENT = "com.google.zxing.client.android.SCAN"; + private static final int MENU_ABOUT = 0; + private static final int MENU_HELPERS = 1; + private static final int MENU_SETTINGS = 2; + private static final int MENU_HISTORY = 3; + private static final int MENU_SCAN = 4; + private static final int SCAN_REQUEST_CODE = 1; + private static final int MENU_OPEN_LIST = 100; + private Context context = null; private Integer version = null; private cgGeo geo = null; @@ -217,43 +227,100 @@ public class cgeo extends AbstractActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, 0, 0, res.getString(R.string.menu_about)).setIcon(android.R.drawable.ic_menu_help); - menu.add(0, 1, 0, res.getString(R.string.menu_helpers)).setIcon(android.R.drawable.ic_menu_add); - menu.add(0, 2, 0, res.getString(R.string.menu_settings)).setIcon(android.R.drawable.ic_menu_preferences); - menu.add(0, 3, 0, res.getString(R.string.menu_history)).setIcon(android.R.drawable.ic_menu_recent_history); + menu.add(0, MENU_SETTINGS, 0, res.getString(R.string.menu_settings)).setIcon(android.R.drawable.ic_menu_preferences); + menu.add(0, MENU_HISTORY, 0, res.getString(R.string.menu_history)).setIcon(android.R.drawable.ic_menu_recent_history); + menu.add(0, MENU_HELPERS, 0, res.getString(R.string.menu_helpers)).setIcon(android.R.drawable.ic_menu_add); + menu.add(0, MENU_SCAN, 0, res.getString(R.string.menu_scan)).setIcon(android.R.drawable.ic_menu_camera); + menu.add(0, MENU_ABOUT, 0, res.getString(R.string.menu_about)).setIcon(android.R.drawable.ic_menu_help); + return true; + } + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + MenuItem item = menu.findItem(MENU_SCAN); + if (item != null) { + item.setEnabled(isIntentAvailable(this, SCAN_INTENT)); + } return true; } + private static boolean isIntentAvailable(Context context, String intent) { + final PackageManager packageManager = context.getPackageManager(); + final List<ResolveInfo> list = packageManager.queryIntentActivities( + new Intent(intent), PackageManager.MATCH_DEFAULT_ONLY); + + return (list.size() > 0); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { final int id = item.getItemId(); - if (id == 0) { + switch (id) { + case MENU_ABOUT: showAbout(null); - return true; - } else if (id == 1) { + case MENU_HELPERS: context.startActivity(new Intent(context, cgeohelpers.class)); - return true; - } else if (id == 2) { + case MENU_SETTINGS: context.startActivity(new Intent(context, cgeoinit.class)); - return true; - } else if (id == 3) { + case MENU_HISTORY: final Intent cachesIntent = new Intent(context, cgeocaches.class); cachesIntent.putExtra("type", "history"); context.startActivity(cachesIntent); - return true; + case MENU_SCAN: + Intent intent = new Intent(SCAN_INTENT); + intent.putExtra("SCAN_MODE", "QR_CODE_MODE"); + startActivityForResult(intent, SCAN_REQUEST_CODE); + return true; + default: + break; } return false; } + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + if (requestCode == SCAN_REQUEST_CODE) { + if (resultCode == RESULT_OK) { + String scan = intent.getStringExtra("SCAN_RESULT"); + if (scan == null || scan.length() == 0) { + return; + } + String host = "http://coord.info/"; + if (scan.toLowerCase().startsWith(host)) { + String geocode = scan.substring(host.length()).trim(); + cgeodetail.startActivity(this, geocode); + } + else { + showToast(res.getString(R.string.unknown_scan)); + } + } else if (resultCode == RESULT_CANCELED) { + // do nothing + } + } + } + @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); + + // context menu for offline button + if (v.getId() == R.id.search_offline) { + ArrayList<cgList> cacheLists = app.getLists(); + int listCount = cacheLists.size(); + menu.setHeaderTitle(res.getString(R.string.list_title)); + for (int i = 0; i < listCount; i++) { + cgList list = cacheLists.get(i); + menu.add(Menu.NONE, MENU_OPEN_LIST + list.id, Menu.NONE, list.title); + } + return; + } + + // standard context menu menu.setHeaderTitle(res.getString(R.string.menu_filter)); //first add the most used types @@ -303,6 +370,11 @@ public class cgeo extends AbstractActivity { setFilterTitle(); return true; + } else if (id > MENU_OPEN_LIST) { + int listId = id - MENU_OPEN_LIST; + settings.saveLastList(listId); + cgeocaches.startActivityOffline(context); + return true; } else if (id > 0) { String itemTitle = item.getTitle().toString(); String choice = null; @@ -382,6 +454,7 @@ public class cgeo extends AbstractActivity { final RelativeLayout findByOffline = (RelativeLayout) findViewById(R.id.search_offline); findByOffline.setClickable(true); findByOffline.setOnClickListener(new cgeoFindByOfflineListener()); + registerForContextMenu(findByOffline); (new countBubbleUpdate()).start(); diff --git a/src/cgeo/geocaching/cgeoadvsearch.java b/src/cgeo/geocaching/cgeoadvsearch.java index d2b3fc7..f6f2159 100644 --- a/src/cgeo/geocaching/cgeoadvsearch.java +++ b/src/cgeo/geocaching/cgeoadvsearch.java @@ -438,9 +438,7 @@ public class cgeoadvsearch extends AbstractActivity { return; } - final Intent cachesIntent = new Intent(this, cgeodetail.class); - cachesIntent.putExtra("geocode", geocodeText.toUpperCase()); - startActivity(cachesIntent); + cgeodetail.startActivity(this, geocodeText); } private class findTrackableAction implements TextView.OnEditorActionListener { diff --git a/src/cgeo/geocaching/cgeoapplication.java b/src/cgeo/geocaching/cgeoapplication.java index e165c0f..fece371 100644 --- a/src/cgeo/geocaching/cgeoapplication.java +++ b/src/cgeo/geocaching/cgeoapplication.java @@ -746,6 +746,10 @@ public class cgeoapplication extends Application { return storage.loadInventory(geocode); } + public HashMap<Integer,Integer> loadLogCounts(String geocode) { + return storage.loadLogCounts(geocode); + } + public ArrayList<cgImage> loadSpoilers(String geocode) { return storage.loadSpoilers(geocode); } diff --git a/src/cgeo/geocaching/cgeocaches.java b/src/cgeo/geocaching/cgeocaches.java index 214969f..40b379f 100644 --- a/src/cgeo/geocaching/cgeocaches.java +++ b/src/cgeo/geocaching/cgeocaches.java @@ -16,6 +16,7 @@ import java.util.Locale; import android.app.AlertDialog; import android.app.ProgressDialog; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; @@ -50,6 +51,7 @@ import cgeo.geocaching.filter.cgFilterByType; import cgeo.geocaching.sorting.CacheComparator; import cgeo.geocaching.sorting.DateComparator; import cgeo.geocaching.sorting.DifficultyComparator; +import cgeo.geocaching.sorting.FindsComparator; import cgeo.geocaching.sorting.GeocodeComparator; import cgeo.geocaching.sorting.InventoryComparator; import cgeo.geocaching.sorting.NameComparator; @@ -61,6 +63,7 @@ import cgeo.geocaching.sorting.VoteComparator; public class cgeocaches extends AbstractListActivity { + private static final String EXTRAS_LIST_TYPE = "type"; private static final int MENU_COMPASS = 1; private static final int MENU_REFRESH_STORED = 2; private static final int MENU_CACHE_DETAILS = 4; @@ -120,11 +123,11 @@ public class cgeocaches extends AbstractListActivity { private static final int SUBMENU_IMPORT = 59; 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 CONTEXT_MENU_MOVE_TO_LIST = 1000; private static final int MENU_MOVE_SELECTED_OR_ALL_TO_LIST = 1200; - - + private String action = null; private String type = null; private Double latitude = null; @@ -342,7 +345,7 @@ public class cgeocaches extends AbstractListActivity { int secondsElapsed = (int)((System.currentTimeMillis() - detailProgressTime) / 1000); int minutesRemaining = (int) ((detailTotal - detailProgress) * secondsElapsed / ((detailProgress > 0) ? detailProgress : 1) / 60); - + waitDialog.setProgress(detailProgress); if (minutesRemaining < 1) { waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + res.getString(R.string.caches_eta_ltm)); @@ -398,9 +401,8 @@ public class cgeocaches extends AbstractListActivity { waitDialog.setMessage(res.getString(R.string.web_downloading)+" "+(String)msg.obj+"..."); } else if (msg.what == 2) { //Cache downloaded waitDialog.setMessage(res.getString(R.string.web_downloaded)+" "+(String)msg.obj+"."); - //Once a cache is downloaded I used switchList to refresh it. - switchList(listId, -1); - } else if (msg.what == -2) { + refreshCurrentList(); + } else if (msg.what == -2) { if (waitDialog != null) { waitDialog.dismiss(); waitDialog.setOnCancelListener(null); @@ -436,31 +438,25 @@ public class cgeocaches extends AbstractListActivity { @Override public void handleMessage(Message msg) { - setAdapter(); - - if (msg.what > -1) { - cacheList.get(msg.what).statusChecked = false; - } else { - if (adapter != null) { - adapter.setSelectMode(false, true); - } + if (adapter != null) { + adapter.setSelectMode(false, true); + } - switchList(listId, -1); + refreshCurrentList(); - cacheList.clear(); + cacheList.clear(); - final ArrayList<cgCache> cacheListTmp = app.getCaches(searchId); - if (cacheListTmp != null && cacheListTmp.isEmpty() == false) { - cacheList.addAll(cacheListTmp); - cacheListTmp.clear(); + final ArrayList<cgCache> cacheListTmp = app.getCaches(searchId); + if (cacheListTmp != null && cacheListTmp.isEmpty() == false) { + cacheList.addAll(cacheListTmp); + cacheListTmp.clear(); - Collections.sort((List<cgCache>)cacheList, gcComparator); - } + Collections.sort((List<cgCache>)cacheList, gcComparator); + } - if (waitDialog != null) { - waitDialog.dismiss(); - waitDialog.setOnCancelListener(null); - } + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog.setOnCancelListener(null); } } }; @@ -545,7 +541,7 @@ public class cgeocaches extends AbstractListActivity { // get parameters Bundle extras = getIntent().getExtras(); if (extras != null) { - type = extras.getString("type"); + type = extras.getString(EXTRAS_LIST_TYPE); latitude = extras.getDouble("latitude"); longitude = extras.getDouble("longitude"); cachetype = extras.getString("cachetype"); @@ -754,6 +750,7 @@ public class cgeocaches extends AbstractListActivity { 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); ArrayList<String> sortedLabels = new ArrayList<String>(comparators.keySet()); Collections.sort(sortedLabels); @@ -981,7 +978,10 @@ public class cgeocaches extends AbstractListActivity { return false; case MENU_SORT_DATE: setComparator(item, new DateComparator()); - return false; + return true; + case MENU_SORT_FINDS: + setComparator(item, new FindsComparator(app)); + return true; case SUBMENU_FILTER_TYPE: selectedFilter = res.getString(R.string.caches_filter_type); openContextMenu(getListView()); @@ -1032,7 +1032,7 @@ public class cgeocaches extends AbstractListActivity { if (adapter == null) { return; } - + if (contextMenuMoveToList) { createFakeContextMenuMoveToList(menu); return; @@ -1225,7 +1225,7 @@ public class cgeocaches extends AbstractListActivity { cgBase.dropCache(app, this, cache, new Handler() { @Override public void handleMessage(Message msg) { - switchList(listId, -1); // refresh + refreshCurrentList(); } }); return true; @@ -1236,8 +1236,7 @@ public class cgeocaches extends AbstractListActivity { } adapter.resetChecks(); - // refresh list by switching to the current list - switchListById(listId); + refreshCurrentList(); return true; } else if (id >= MENU_MOVE_SELECTED_OR_ALL_TO_LIST && id < MENU_MOVE_SELECTED_OR_ALL_TO_LIST + 100) { int newListId = id - MENU_MOVE_SELECTED_OR_ALL_TO_LIST; @@ -1250,8 +1249,7 @@ public class cgeocaches extends AbstractListActivity { } adapter.resetChecks(); - // refresh list by switching to the current list - switchListById(listId); + refreshCurrentList(); return true; } @@ -1260,19 +1258,19 @@ public class cgeocaches extends AbstractListActivity { // https://code.google.com/p/android/issues/detail?id=7139 lastMenuInfo = info; - // create a searchId for a single cache (as if in details view) - HashMap<String, String> params = new HashMap<String, String>(); - params.put("geocode", cache.geocode); - Long singleSearchId = base.searchByGeocode(params, 0, false); - - if (NavigationAppFactory.onMenuItemSelected(item, geo, this, - res, cache, singleSearchId, null, null)) { - return true; - } - if (cache != null) { + // create a searchId for a single cache (as if in details view) + HashMap<String, String> params = new HashMap<String, String>(); + params.put("geocode", cache.geocode); + Long singleSearchId = base.searchByGeocode(params, 0, false); + + if (NavigationAppFactory.onMenuItemSelected(item, geo, this, + res, cache, singleSearchId, null, null)) { + return true; + } + int logType = id - MENU_LOG_VISIT_OFFLINE; - cache.logOffline(this, logType); + cache.logOffline(this, logType, settings, base); } return true; } @@ -1414,11 +1412,13 @@ public class cgeocaches extends AbstractListActivity { } private void importGpx() { - final Intent intent = new Intent(this, cgeogpxes.class); - intent.putExtra("list", listId); - startActivity(intent); + cgeogpxes.startSubActivity(this, listId); + } - finish(); + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + refreshCurrentList(); } public void refreshStored() { @@ -2139,9 +2139,6 @@ public class cgeocaches extends AbstractListActivity { final ArrayList<cgCache> cacheListTemp = new ArrayList<cgCache>(cacheList); for (cgCache cache : cacheListTemp) { if (checked > 0 && cache.statusChecked == false) { - handler.sendEmptyMessage(0); - - yield(); continue; } @@ -2152,10 +2149,6 @@ public class cgeocaches extends AbstractListActivity { } app.markDropped(cache.geocode); - - handler.sendEmptyMessage(cacheList.indexOf(cache)); - - yield(); } catch (Exception e) { Log.e(cgSettings.tag, "cgeocaches.geocachesDropDetails: " + e.toString()); } @@ -2382,7 +2375,7 @@ public class cgeocaches extends AbstractListActivity { builder.setTitle(res.getString(R.string.list_title)); builder.setItems(listsTitle.toArray(items), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogInterface, int item) { - switchListByOrder(item); + switchListById(lists.get(item).id); return; } @@ -2390,30 +2383,11 @@ public class cgeocaches extends AbstractListActivity { builder.create().show(); } - public void switchListByOrder(int order) { - switchList(-1, order); - } - public void switchListById(int id) { - switchList(id, -1); - } - - private class MoveHandler extends Handler { - @Override - public void handleMessage(Message msg) { - Thread threadPure = new geocachesLoadByOffline(loadCachesHandler, latitude, longitude, msg.what); - threadPure.start(); - } - } - - public void switchList(int id, int order) { cgList list = null; if (id >= 0) { list = app.getList(id); - } else if (order >= 0) { - lists = app.getLists(); - list = lists.get(order); } else { return; } @@ -2433,6 +2407,14 @@ public class cgeocaches extends AbstractListActivity { (new moveCachesToList(listId, new MoveHandler())).start(); } + private class MoveHandler extends Handler { + @Override + public void handleMessage(Message msg) { + Thread threadPure = new geocachesLoadByOffline(loadCachesHandler, latitude, longitude, msg.what); + threadPure.start(); + } + } + private class moveCachesToList extends Thread { int listId = -1; Handler handler = null; @@ -2493,21 +2475,33 @@ public class cgeocaches extends AbstractListActivity { alert.show(); } + private void removeListInternal() { + boolean status = app.removeList(listId); + + if (status) { + showToast(res.getString(R.string.list_dialog_remove_ok)); + switchListById(1); + } else { + showToast(res.getString(R.string.list_dialog_remove_err)); + } + } + private void removeList() { + // if there are no caches on this list, don't bother the user with questions. + // there is no harm in deleting the list, he could recreate it easily + if (cacheList != null && cacheList.isEmpty()) { + removeListInternal(); + return; + } + + // ask him, if there are caches on the list final AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setTitle(R.string.list_dialog_remove_title); alert.setMessage(R.string.list_dialog_remove_description); alert.setPositiveButton(R.string.list_dialog_remove, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { - boolean status = app.removeList(listId); - - if (status) { - showToast(res.getString(R.string.list_dialog_remove_ok)); - switchListById(1); - } else { - showToast(res.getString(R.string.list_dialog_remove_err)); - } + removeListInternal(); } }); alert.setNegativeButton(res.getString(R.string.list_dialog_cancel), new DialogInterface.OnClickListener() { @@ -2542,4 +2536,14 @@ public class cgeocaches extends AbstractListActivity { ActivityMixin.goManual(this, "c:geo-nearby"); } } -} + + private void refreshCurrentList() { + switchListById(listId); + } + + public static void startActivityOffline(final Context context) { + final Intent cachesIntent = new Intent(context, cgeocaches.class); + cachesIntent.putExtra(EXTRAS_LIST_TYPE, "offline"); + context.startActivity(cachesIntent); + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgeocoords.java b/src/cgeo/geocaching/cgeocoords.java index 218a403..229b3a6 100644 --- a/src/cgeo/geocaching/cgeocoords.java +++ b/src/cgeo/geocaching/cgeocoords.java @@ -256,15 +256,16 @@ public class cgeocoords extends Dialog { } private static String addZeros(int value, int len) { - String zeros = ""; + StringBuilder zeros = new StringBuilder(); if (value == 0) { value = 1; } - while (value < Math.pow(10, len-1)) { - zeros += "0"; + double wantedLength = Math.pow(10, len-1); + while (value < wantedLength) { + zeros.append('0'); value *= 10; } - return zeros; + return zeros.toString(); } private class buttonClickListener implements View.OnClickListener { @@ -272,8 +273,11 @@ public class cgeocoords extends Dialog { @Override public void onClick(View v) { Button e = (Button) v; - char[] c = e.getText().toString().toCharArray(); - switch (c[0]) { + CharSequence text = e.getText(); + if (text == null || text.length() == 0) { + return; + } + switch (text.charAt(0)) { case 'N': e.setText("S"); break; @@ -421,8 +425,8 @@ public class cgeocoords extends Dialog { longitude = lonDeg + lonMin/60.0 + lonSec/60.0/60.0 + lonSecFrac/60.0/60.0; break; } - latitude *= (bLat.getText().toString() == "S" ? -1 : 1); - longitude *= (bLon.getText().toString() == "W" ? -1 : 1); + latitude *= (bLat.getText().toString().equalsIgnoreCase("S") ? -1 : 1); + longitude *= (bLon.getText().toString().equalsIgnoreCase("W") ? -1 : 1); } private class CoordinateFormatListener implements OnItemSelectedListener { diff --git a/src/cgeo/geocaching/cgeodetail.java b/src/cgeo/geocaching/cgeodetail.java index 7f5e7e5..225e345 100644 --- a/src/cgeo/geocaching/cgeodetail.java +++ b/src/cgeo/geocaching/cgeodetail.java @@ -17,6 +17,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; import android.database.Cursor; +import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -175,38 +176,43 @@ public class cgeodetail extends AbstractActivity { @Override public void handleMessage(Message message) { BitmapDrawable image = (BitmapDrawable) message.obj; + if (image == null) { + return; + } ScrollView scroll = (ScrollView) findViewById(R.id.details_list_box); final ImageView view = (ImageView) findViewById(R.id.map_preview); + if (view == null) { + return; + } + Bitmap bitmap = image.getBitmap(); + if (bitmap == null || bitmap.getWidth() <= 10) { + return; + } + view.setImageDrawable(image); - if (image != null && view != null) { - view.setImageDrawable(image); - - if (scroll.getScrollY() == 0) { - scroll.scrollTo(0, (int) (80 * pixelRatio)); - } - view.setVisibility(View.VISIBLE); - view.setOnClickListener(new View.OnClickListener() { + if (scroll.getScrollY() == 0) { + scroll.scrollTo(0, (int) (80 * pixelRatio)); + } + view.setVisibility(View.VISIBLE); + view.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - try { - registerForContextMenu(view); - openContextMenu(view); - } catch (Exception e) { - // nothing - } + @Override + public void onClick(View v) { + try { + registerForContextMenu(view); + openContextMenu(view); + } catch (Exception e) { + // nothing } - }); - } + } + }); } }; private Handler loadDescriptionHandler = new Handler() { @Override public void handleMessage(Message msg) { - if (longDesc == null && cache != null && cache.description != null) { - longDesc = Html.fromHtml(cache.description.trim(), new cgHtmlImg(cgeodetail.this, geocode, true, cache.reason, false), null); - } + parseLongDescription(); if (longDesc != null) { ((LinearLayout) findViewById(R.id.desc_box)).setVisibility(View.VISIBLE); @@ -542,7 +548,7 @@ public class cgeodetail extends AbstractActivity { } int logType = menuItem - MENU_LOG_VISIT_OFFLINE; - cache.logOffline(this, logType); + cache.logOffline(this, logType, settings, base); return true; } @@ -659,11 +665,12 @@ public class cgeodetail extends AbstractActivity { itemName.setText(res.getString(R.string.cache_type)); - String size = null; + String size = ""; if (cache.size != null && cache.size.length() > 0) { - size = " (" + cache.size + ")"; - } else { - size = ""; + // don't show "not chosen" for events, that should be the normal case + if (!(cache.isEventCache() && cache.size.equals("not chosen"))) { + size = " (" + cache.size + ")"; + } } if (cgBase.cacheTypesInv.containsKey(cache.type)) { // cache icon @@ -947,9 +954,7 @@ public class cgeodetail extends AbstractActivity { // cache long desc if (longDescDisplayed) { - if (longDesc == null && cache != null && cache.description != null) { - longDesc = Html.fromHtml(cache.description.trim(), new cgHtmlImg(this, geocode, true, cache.reason, false), null); - } + parseLongDescription(); if (longDesc != null && longDesc.length() > 0) { ((LinearLayout) findViewById(R.id.desc_box)).setVisibility(View.VISIBLE); @@ -1098,6 +1103,12 @@ public class cgeodetail extends AbstractActivity { if (geo != null) geoUpdate.updateLoc(geo); } + private void parseLongDescription() { + if (longDesc == null && cache != null && cache.description != null) { + longDesc = Html.fromHtml(cache.description.trim(), new cgHtmlImg(this, geocode, true, cache.reason, false), new UnknownTagsHandler()); + } + } + private RelativeLayout addStarRating(final LinearLayout detailsList, final String name, final float value) { RelativeLayout itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_layout, null); TextView itemName = (TextView) itemLayout.findViewById(R.id.name); @@ -1368,8 +1379,7 @@ public class cgeodetail extends AbstractActivity { if (cache == null || cache.description == null || handler == null) { return; } - - longDesc = Html.fromHtml(cache.description.trim(), new cgHtmlImg(cgeodetail.this, geocode, true, cache.reason, false), null); + parseLongDescription(); handler.sendMessage(new Message()); } } @@ -2071,4 +2081,10 @@ public class cgeodetail extends AbstractActivity { return descriptions; } + + public static void startActivity(final Context context, final String geocode) { + final Intent detailIntent = new Intent(context, cgeodetail.class); + detailIntent.putExtra("geocode", geocode.toUpperCase()); + context.startActivity(detailIntent); + } } diff --git a/src/cgeo/geocaching/cgeogpxes.java b/src/cgeo/geocaching/cgeogpxes.java index 64d5bf8..db26a1f 100644 --- a/src/cgeo/geocaching/cgeogpxes.java +++ b/src/cgeo/geocaching/cgeogpxes.java @@ -3,21 +3,24 @@ package cgeo.geocaching; import java.io.File; import java.util.ArrayList; -import cgeo.geocaching.files.FileList; -import cgeo.geocaching.files.GPXParser; -import cgeo.geocaching.files.LocParser; - +import android.app.Activity; import android.app.ProgressDialog; +import android.content.Intent; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; +import cgeo.geocaching.files.FileList; +import cgeo.geocaching.files.GPXParser; +import cgeo.geocaching.files.LocParser; public class cgeogpxes extends FileList<cgGPXListAdapter> { + private static final String EXTRAS_LIST_ID = "list"; + public cgeogpxes() { super(new String[] {"gpx" - // TODO , "loc" + // TODO , "loc" }); } @@ -70,14 +73,14 @@ public class cgeogpxes extends FileList<cgGPXListAdapter> { Bundle extras = getIntent().getExtras(); if (extras != null) { - listId = extras.getInt("list"); + listId = extras.getInt(EXTRAS_LIST_ID); } if (listId <= 0) { listId = 1; } } - + @Override protected void setTitle() { setTitle(res.getString(R.string.gpx_import_title)); @@ -118,4 +121,10 @@ public class cgeogpxes extends FileList<cgGPXListAdapter> { loadCachesHandler.sendMessage(new Message()); } } + + public static void startSubActivity(Activity fromActivity, int listId) { + final Intent intent = new Intent(fromActivity, cgeogpxes.class); + intent.putExtra(EXTRAS_LIST_ID, listId); + fromActivity.startActivityForResult(intent, 0); + } } diff --git a/src/cgeo/geocaching/cgeohelpers.java b/src/cgeo/geocaching/cgeohelpers.java index 056f631..85cf422 100644 --- a/src/cgeo/geocaching/cgeohelpers.java +++ b/src/cgeo/geocaching/cgeohelpers.java @@ -27,52 +27,37 @@ public class cgeohelpers extends AbstractActivity { settings.load(); } - public void installManual(View view) { - final Locale loc = Locale.getDefault(); - final String lng = loc.getLanguage(); - + private void installFromMarket(String marketId) { try { - if (lng.equalsIgnoreCase("de")) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:gnu.android.app.cgeomanual.de"))); - } else { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:gnu.android.app.cgeomanual.en"))); - } + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:" + marketId))); } catch (Exception e) { // market not available in standard emulator } - finish(); } - public void installLocus(View view) { - try { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:menion.android.locus"))); - } catch (Exception e) { - // market not available in standard emulator - } + public void installManual(View view) { + final Locale loc = Locale.getDefault(); + final String language = loc.getLanguage(); + if ("de".equalsIgnoreCase(language)) { + installFromMarket("gnu.android.app.cgeomanual.de"); + } + else { + installFromMarket("gnu.android.app.cgeomanual.en"); + } + } - finish(); + public void installLocus(View view) { + installFromMarket("menion.android.locus"); } public void installGpsStatus(View view) { - try { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:com.eclipsim.gpsstatus2"))); - } catch (Exception e) { - // market not available in standard emulator - } - - finish(); + installFromMarket("com.eclipsim.gpsstatus2"); } public void installBluetoothGps(View view) { - try { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:googoo.android.btgps"))); - } catch (Exception e) { - // market not available in standard emulator - } - - finish(); + installFromMarket("googoo.android.btgps"); } } diff --git a/src/cgeo/geocaching/cgeoinit.java b/src/cgeo/geocaching/cgeoinit.java index 46ba767..e3111e2 100644 --- a/src/cgeo/geocaching/cgeoinit.java +++ b/src/cgeo/geocaching/cgeoinit.java @@ -1024,7 +1024,12 @@ public class cgeoinit extends AbstractActivity { @Override public void run() { - logInHandler.sendEmptyMessage(base.login()); + final int loginResult = base.login(); + if (1 == loginResult) + { + base.detectGcCustomDate(); + } + logInHandler.sendEmptyMessage(loginResult); } }).start(); } diff --git a/src/cgeo/geocaching/cgeopopup.java b/src/cgeo/geocaching/cgeopopup.java index 5e78531..54be58c 100644 --- a/src/cgeo/geocaching/cgeopopup.java +++ b/src/cgeo/geocaching/cgeopopup.java @@ -186,9 +186,9 @@ public class cgeopopup extends AbstractActivity { if (NavigationAppFactory.onMenuItemSelected(item, geo, this, res, cache, null, null, null)) { return true; } - + int logType = menuItem - MENU_LOG_VISIT_OFFLINE; - cache.logOffline(this, logType); + cache.logOffline(this, logType, settings, base); return true; } diff --git a/src/cgeo/geocaching/connector/ConnectorFactory.java b/src/cgeo/geocaching/connector/ConnectorFactory.java index 9d8f955..d052231 100644 --- a/src/cgeo/geocaching/connector/ConnectorFactory.java +++ b/src/cgeo/geocaching/connector/ConnectorFactory.java @@ -4,7 +4,7 @@ import cgeo.geocaching.cgCache; public final class ConnectorFactory { private static final GCConnector GC_CONNECTOR = new GCConnector(); - private static final IConnector[] connectors = new IConnector[] {GC_CONNECTOR, new OCConnector()}; + private static final IConnector[] connectors = new IConnector[] {GC_CONNECTOR, new OCConnector(), new OXConnector()}; public static IConnector[] getConnectors() { return connectors; diff --git a/src/cgeo/geocaching/connector/OCConnector.java b/src/cgeo/geocaching/connector/OCConnector.java index efe4232..d23b3cd 100644 --- a/src/cgeo/geocaching/connector/OCConnector.java +++ b/src/cgeo/geocaching/connector/OCConnector.java @@ -2,6 +2,10 @@ package cgeo.geocaching.connector; import cgeo.geocaching.cgCache; +/** + * connector for OpenCaching.de (and several other country domains) + * + */ public class OCConnector extends AbstractConnector implements IConnector { @Override public boolean canHandle(String geocode) { diff --git a/src/cgeo/geocaching/connector/OXConnector.java b/src/cgeo/geocaching/connector/OXConnector.java new file mode 100644 index 0000000..75d404e --- /dev/null +++ b/src/cgeo/geocaching/connector/OXConnector.java @@ -0,0 +1,21 @@ +package cgeo.geocaching.connector; + +import cgeo.geocaching.cgCache; + +/** + * connector for OpenCaching.com + * + */ +public class OXConnector extends AbstractConnector implements IConnector { + + @Override + public boolean canHandle(String geocode) { + return geocode != null && geocode.toUpperCase().startsWith("OX"); + } + + @Override + public String getCacheUrl(cgCache cache) { + return "http://www.opencaching.com/#!geocache/" + cache.geocode; + } + +} diff --git a/src/cgeo/geocaching/files/GPXParser.java b/src/cgeo/geocaching/files/GPXParser.java index cc864ce..c96e30c 100644 --- a/src/cgeo/geocaching/files/GPXParser.java +++ b/src/cgeo/geocaching/files/GPXParser.java @@ -4,7 +4,11 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -17,7 +21,6 @@ import android.sax.EndElementListener; import android.sax.EndTextElementListener; import android.sax.RootElement; import android.sax.StartElementListener; -import android.text.Html; import android.util.Log; import android.util.Xml; import cgeo.geocaching.cgBase; @@ -31,43 +34,52 @@ import cgeo.geocaching.connector.ConnectorFactory; public abstract class GPXParser extends FileParser { - private cgSearch search = null; - private Handler handler = null; + private static final SimpleDateFormat formatSimple = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); // 2010-04-20T07:00:00Z + private static final SimpleDateFormat formatTimezone = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'.000'Z"); // 2010-04-20T01:01:03.000-04:00 + + private static final Pattern patternGeocode = Pattern.compile("([A-Z]{2}[0-9A-Z]+)", Pattern.CASE_INSENSITIVE); + private static final String[] nsGCList = new String[] { + "http://www.groundspeak.com/cache/1/1", // PQ 1.1 + "http://www.groundspeak.com/cache/1/0/1", // PQ 1.0.1 + "http://www.groundspeak.com/cache/1/0", // PQ 1.0 + }; + private cgeoapplication app = null; private int listId = 1; + private cgSearch search = null; + protected String namespace = null; + private String version; + private Handler handler = null; + private cgCache cache = new cgCache(); private cgTrackable trackable = new cgTrackable(); private cgLog log = new cgLog(); - private boolean htmlShort = true; - private boolean htmlLong = true; + private String type = null; private String sym = null; - protected String namespace = null; - private ArrayList<String> nsGCList = new ArrayList<String>(); - private static final Pattern patternGeocode = Pattern.compile("([A-Z]{2}[0-9A-Z]+)", Pattern.CASE_INSENSITIVE); private String name = null; private String cmt = null; private String desc = null; - private String version; public GPXParser(cgeoapplication appIn, int listIdIn, cgSearch searchIn, String namespaceIn, String versionIn) { app = appIn; listId = listIdIn; search = searchIn; - - nsGCList.add("http://www.groundspeak.com/cache/1/1"); // PQ 1.1 - nsGCList.add("http://www.groundspeak.com/cache/1/0/1"); // PQ 1.0.1 - nsGCList.add("http://www.groundspeak.com/cache/1/0"); // PQ 1.0 - namespace = namespaceIn; version = versionIn; } - public long parse(File file, Handler handlerIn) { - handler = handlerIn; - if (file == null) { - return 0l; + private static Date parseDate(String input) throws ParseException { + input = input.trim(); + if (input.length() >= 3 && input.charAt(input.length() - 3) == ':') { + String removeColon = input.substring(0, input.length() - 3) + input.substring(input.length() - 2); + return formatTimezone.parse(removeColon); } + return formatSimple.parse(input); + } + + public long parse(final InputStream stream, Handler handlerIn) { + handler = handlerIn; final RootElement root = new RootElement(namespace, "gpx"); final Element waypoint = root.getChild(namespace, "wpt"); @@ -75,6 +87,7 @@ public abstract class GPXParser extends FileParser { // waypoint - attributes waypoint.setStartElementListener(new StartElementListener() { + @Override public void start(Attributes attrs) { try { if (attrs.getIndex("lat") > -1) { @@ -92,6 +105,7 @@ public abstract class GPXParser extends FileParser { // waypoint waypoint.setEndElementListener(new EndElementListener() { + @Override public void end() { if (cache.geocode == null || cache.geocode.length() == 0) { // try to find geocode somewhere else @@ -114,8 +128,6 @@ public abstract class GPXParser extends FileParser { showFinishedMessage(handler, search); - htmlShort = true; - htmlLong = true; type = null; sym = null; name = null; @@ -130,9 +142,10 @@ public abstract class GPXParser extends FileParser { // waypoint.time waypoint.getChild(namespace, "time").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { try { - cache.hidden = cgBase.dateGPXIn.parse(body.trim()); + cache.hidden = parseDate(body); } catch (Exception e) { Log.w(cgSettings.tag, "Failed to parse cache date: " + e.toString()); } @@ -142,10 +155,11 @@ public abstract class GPXParser extends FileParser { // waypoint.name waypoint.getChild(namespace, "name").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { name = body; - final String content = Html.fromHtml(body).toString().trim(); + final String content = body.trim(); cache.name = content; findGeoCode(cache.name); @@ -156,28 +170,29 @@ public abstract class GPXParser extends FileParser { // waypoint.desc waypoint.getChild(namespace, "desc").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { desc = body; - final String content = Html.fromHtml(body).toString().trim(); - cache.shortdesc = content; + cache.shortdesc = validate(body); } }); // waypoint.cmt waypoint.getChild(namespace, "cmt").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { cmt = body; - final String content = Html.fromHtml(body).toString().trim(); - cache.description = content; + cache.description = validate(body); } }); // waypoint.type waypoint.getChild(namespace, "type").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { final String[] content = body.split("\\|"); if (content.length > 0) { @@ -189,6 +204,7 @@ public abstract class GPXParser extends FileParser { // waypoint.sym waypoint.getChild(namespace, "sym").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { body = body.toLowerCase(); sym = body; @@ -208,6 +224,7 @@ public abstract class GPXParser extends FileParser { gcCache.setStartElementListener(new StartElementListener() { + @Override public void start(Attributes attrs) { try { if (attrs.getIndex("id") > -1) { @@ -228,42 +245,43 @@ public abstract class GPXParser extends FileParser { // waypoint.cache.name gcCache.getChild(nsGC, "name").setEndTextElementListener(new EndTextElementListener() { - public void end(String body) { - final String content = Html.fromHtml(body).toString().trim(); - cache.name = validate(content); + @Override + public void end(String cacheName) { + cache.name = validate(cacheName); } }); // waypoint.cache.owner gcCache.getChild(nsGC, "owner").setEndTextElementListener(new EndTextElementListener() { - public void end(String body) { - final String content = Html.fromHtml(body).toString().trim(); - cache.owner = validate(content); + @Override + public void end(String cacheOwner) { + cache.owner = validate(cacheOwner); } }); // waypoint.cache.type gcCache.getChild(nsGC, "type").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { - String parsedString = validate(body.toLowerCase()); - setType(parsedString); + setType(validate(body.toLowerCase())); } }); // waypoint.cache.container gcCache.getChild(nsGC, "container").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { - final String content = body.toLowerCase(); - cache.size = validate(content); + cache.size = validate(body.toLowerCase()); } }); // waypoint.cache.difficulty gcCache.getChild(nsGC, "difficulty").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { try { cache.difficulty = new Float(body); @@ -276,6 +294,7 @@ public abstract class GPXParser extends FileParser { // waypoint.cache.terrain gcCache.getChild(nsGC, "terrain").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { try { cache.terrain = new Float(body); @@ -288,11 +307,12 @@ public abstract class GPXParser extends FileParser { // waypoint.cache.country gcCache.getChild(nsGC, "country").setEndTextElementListener(new EndTextElementListener() { - public void end(String body) { + @Override + public void end(String country) { if (cache.location == null || cache.location.length() == 0) { - cache.location = validate(body.trim()); + cache.location = validate(country); } else { - cache.location = cache.location + ", " + body.trim(); + cache.location = cache.location + ", " + country.trim(); } } }); @@ -300,11 +320,12 @@ public abstract class GPXParser extends FileParser { // waypoint.cache.state gcCache.getChild(nsGC, "state").setEndTextElementListener(new EndTextElementListener() { - public void end(String body) { + @Override + public void end(String state) { if (cache.location == null || cache.location.length() == 0) { - cache.location = validate(body.trim()); + cache.location = validate(state); } else { - cache.location = body.trim() + ", " + cache.location; + cache.location = state.trim() + ", " + cache.location; } } }); @@ -312,63 +333,25 @@ public abstract class GPXParser extends FileParser { // waypoint.cache.encoded_hints gcCache.getChild(nsGC, "encoded_hints").setEndTextElementListener(new EndTextElementListener() { - public void end(String body) { - cache.hint = validate(body.trim()); - } - }); - - // waypoint.cache.short_description - gcCache.getChild(nsGC, "short_description").setStartElementListener(new StartElementListener() { - - public void start(Attributes attrs) { - try { - if (attrs.getIndex("html") > -1) { - final String at = attrs.getValue("html"); - if (at.equalsIgnoreCase("false")) { - htmlShort = false; - } - } - } catch (Exception e) { - // nothing - } + @Override + public void end(String encoded) { + cache.hint = validate(encoded); } }); gcCache.getChild(nsGC, "short_description").setEndTextElementListener(new EndTextElementListener() { - public void end(String body) { - if (!htmlShort) { - cache.shortdesc = Html.fromHtml(body).toString(); - } else { - cache.shortdesc = body; - } - } - }); - - // waypoint.cache.long_description - gcCache.getChild(nsGC, "long_description").setStartElementListener(new StartElementListener() { - - public void start(Attributes attrs) { - try { - if (attrs.getIndex("html") > -1) { - if (attrs.getValue("html").equalsIgnoreCase("false")) { - htmlLong = false; - } - } - } catch (Exception e) { - // nothing - } + @Override + public void end(String shortDesc) { + cache.shortdesc = validate(shortDesc); } }); gcCache.getChild(nsGC, "long_description").setEndTextElementListener(new EndTextElementListener() { - public void end(String body) { - if (htmlLong == false) { - cache.description = Html.fromHtml(body).toString().trim(); - } else { - cache.description = body; - } + @Override + public void end(String desc) { + cache.description = validate(desc); } }); @@ -378,6 +361,7 @@ public abstract class GPXParser extends FileParser { // waypoint.cache.travelbugs.travelbug gcTBs.getChild(nsGC, "travelbug").setStartElementListener(new StartElementListener() { + @Override public void start(Attributes attrs) { trackable = new cgTrackable(); @@ -396,10 +380,12 @@ public abstract class GPXParser extends FileParser { gcTB.setEndElementListener(new EndElementListener() { + @Override public void end() { if (trackable.geocode != null && trackable.geocode.length() > 0 && trackable.name != null && trackable.name.length() > 0) { - if (cache.inventory == null) + if (cache.inventory == null) { cache.inventory = new ArrayList<cgTrackable>(); + } cache.inventory.add(trackable); } } @@ -408,9 +394,9 @@ public abstract class GPXParser extends FileParser { // waypoint.cache.travelbugs.travelbug.name gcTB.getChild(nsGC, "name").setEndTextElementListener(new EndTextElementListener() { - public void end(String body) { - String content = Html.fromHtml(body).toString(); - trackable.name = content; + @Override + public void end(String tbName) { + trackable.name = validate(tbName); } }); @@ -422,6 +408,7 @@ public abstract class GPXParser extends FileParser { gcLog.setStartElementListener(new StartElementListener() { + @Override public void start(Attributes attrs) { log = new cgLog(); @@ -437,10 +424,12 @@ public abstract class GPXParser extends FileParser { gcLog.setEndElementListener(new EndElementListener() { + @Override public void end() { if (log.log != null && log.log.length() > 0) { - if (cache.logs == null) + if (cache.logs == null) { cache.logs = new ArrayList<cgLog>(); + } cache.logs.add(log); } } @@ -449,9 +438,10 @@ public abstract class GPXParser extends FileParser { // waypoint.cache.logs.log.date gcLog.getChild(nsGC, "date").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { try { - log.date = cgBase.dateGPXIn.parse(body.trim()).getTime(); + log.date = parseDate(body).getTime(); } catch (Exception e) { Log.w(cgSettings.tag, "Failed to parse log date: " + e.toString()); } @@ -461,12 +451,13 @@ public abstract class GPXParser extends FileParser { // waypoint.cache.logs.log.type gcLog.getChild(nsGC, "type").setEndTextElementListener(new EndTextElementListener() { + @Override public void end(String body) { - final String content = body.trim().toLowerCase(); - if (cgBase.logTypes0.containsKey(content)) { - log.type = cgBase.logTypes0.get(content); + final String logType = validate(body).toLowerCase(); + if (cgBase.logTypes0.containsKey(logType)) { + log.type = cgBase.logTypes0.get(logType); } else { - log.type = 4; + log.type = cgBase.LOG_NOTE; } } }); @@ -474,42 +465,54 @@ public abstract class GPXParser extends FileParser { // waypoint.cache.logs.log.finder gcLog.getChild(nsGC, "finder").setEndTextElementListener(new EndTextElementListener() { - public void end(String body) { - String content = Html.fromHtml(body).toString(); - log.author = content; + @Override + public void end(String finderName) { + log.author = validate(finderName); } }); - // waypoint.cache.logs.log.finder + // waypoint.cache.logs.log.text gcLog.getChild(nsGC, "text").setEndTextElementListener(new EndTextElementListener() { - public void end(String body) { - String content = Html.fromHtml(body).toString(); - log.log = content; + @Override + public void end(String logText) { + log.log = validate(logText); } }); } - FileInputStream fis = null; boolean parsed = false; try { - fis = new FileInputStream(file); - } catch (FileNotFoundException e) { - Log.e(cgSettings.tag, "Cannot parse .gpx file " + file.getAbsolutePath() + " as GPX " + version + ": file not found!"); - } - try { - Xml.parse(fis, Xml.Encoding.UTF_8, root.getContentHandler()); + Xml.parse(stream, Xml.Encoding.UTF_8, root.getContentHandler()); parsed = true; } catch (IOException e) { - Log.e(cgSettings.tag, "Cannot parse .gpx file " + file.getAbsolutePath() + " as GPX " + version + ": could not read file!"); + Log.e(cgSettings.tag, "Cannot parse .gpx file as GPX " + version + ": could not read file!"); } catch (SAXException e) { - Log.e(cgSettings.tag, "Cannot parse .gpx file " + file.getAbsolutePath() + " as GPX " + version + ": could not parse XML - " + e.toString()); + Log.e(cgSettings.tag, "Cannot parse .gpx file as GPX " + version + ": could not parse XML - " + e.toString()); + } + return parsed ? search.getCurrentId() : 0l; + } + + private long parse(final File file, final Handler handlerIn) { + if (file == null) { + return 0l; + } + + FileInputStream fis = null; + long result = 0l; + try { + fis = new FileInputStream(file); + result = parse(fis, handlerIn); + } catch (FileNotFoundException e) { + Log.e(cgSettings.tag, "Cannot parse .gpx file " + file.getAbsolutePath() + " as GPX " + version + ": file not found!"); } try { - fis.close(); + if (fis != null) { + fis.close(); + } } catch (IOException e) { Log.e(cgSettings.tag, "Error after parsing .gpx file " + file.getAbsolutePath() + " as GPX " + version + ": could not close file!"); } - return parsed ? search.getCurrentId() : 0l; + return result; } protected abstract Element getCacheParent(Element waypoint); @@ -518,7 +521,7 @@ public abstract class GPXParser extends FileParser { if ("nil".equalsIgnoreCase(input)) { return ""; } - return input; + return input.trim(); } private void setType(String parsedString) { diff --git a/src/cgeo/geocaching/googlemaps/googleCacheOverlay.java b/src/cgeo/geocaching/googlemaps/googleCacheOverlay.java index 68929cf..02e8c04 100644 --- a/src/cgeo/geocaching/googlemaps/googleCacheOverlay.java +++ b/src/cgeo/geocaching/googlemaps/googleCacheOverlay.java @@ -1,10 +1,12 @@ package cgeo.geocaching.googlemaps; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + import android.content.Context; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.drawable.Drawable; - import cgeo.geocaching.cgSettings; import cgeo.geocaching.mapcommon.cgMapOverlay; import cgeo.geocaching.mapinterfaces.ItemizedOverlayImpl; @@ -22,6 +24,7 @@ import com.google.android.maps.MapView; public class googleCacheOverlay extends ItemizedOverlay<googleCacheOverlayItem> implements ItemizedOverlayImpl { private cgMapOverlay base; + private Lock lock = new ReentrantLock(); public googleCacheOverlay(cgSettings settingsIn, Context contextIn, Drawable markerIn, Boolean fromDetailIn) { super(boundCenterBottom(markerIn)); @@ -98,4 +101,14 @@ public class googleCacheOverlay extends ItemizedOverlay<googleCacheOverlayItem> // Nothing to do here... } + @Override + public void lock() { + lock.lock(); + } + + @Override + public void unlock() { + lock.unlock(); + } + } diff --git a/src/cgeo/geocaching/googlemaps/googleMapFactory.java b/src/cgeo/geocaching/googlemaps/googleMapFactory.java index 28bfca5..ba62737 100644 --- a/src/cgeo/geocaching/googlemaps/googleMapFactory.java +++ b/src/cgeo/geocaching/googlemaps/googleMapFactory.java @@ -7,8 +7,6 @@ import cgeo.geocaching.cgUser; import cgeo.geocaching.mapinterfaces.CacheOverlayItemImpl; import cgeo.geocaching.mapinterfaces.GeoPointImpl; import cgeo.geocaching.mapinterfaces.MapFactory; -import cgeo.geocaching.mapinterfaces.OverlayBase; -import cgeo.geocaching.mapinterfaces.OverlayImpl; import cgeo.geocaching.mapinterfaces.UserOverlayItemImpl; import com.google.android.maps.MapActivity; @@ -36,12 +34,6 @@ public class googleMapFactory implements MapFactory{ } @Override - public OverlayImpl getOverlayBaseWrapper(OverlayBase ovlIn) { - googleOverlay baseOvl = new googleOverlay(ovlIn); - return baseOvl; - } - - @Override public CacheOverlayItemImpl getCacheOverlayItem(cgCoord coordinate, String type) { googleCacheOverlayItem baseItem = new googleCacheOverlayItem(coordinate, type); return baseItem; diff --git a/src/cgeo/geocaching/googlemaps/googleMapView.java b/src/cgeo/geocaching/googlemaps/googleMapView.java index 737e64e..6fa1eb7 100644 --- a/src/cgeo/geocaching/googlemaps/googleMapView.java +++ b/src/cgeo/geocaching/googlemaps/googleMapView.java @@ -1,18 +1,23 @@ package cgeo.geocaching.googlemaps; +import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapcommon.cgMapMyOverlay; import cgeo.geocaching.mapcommon.cgMapOverlay; +import cgeo.geocaching.mapcommon.cgOverlayScale; import cgeo.geocaching.mapcommon.cgUsersOverlay; import cgeo.geocaching.mapinterfaces.GeoPointImpl; import cgeo.geocaching.mapinterfaces.MapControllerImpl; import cgeo.geocaching.mapinterfaces.MapProjectionImpl; import cgeo.geocaching.mapinterfaces.MapViewImpl; +import cgeo.geocaching.mapinterfaces.OverlayBase; import cgeo.geocaching.mapinterfaces.OverlayImpl; +import cgeo.geocaching.mapinterfaces.OverlayImpl.overlayType; import com.google.android.maps.GeoPoint; import com.google.android.maps.MapView; @@ -97,6 +102,24 @@ public class googleMapView extends MapView implements MapViewImpl{ } @Override + public cgMapMyOverlay createAddPositionOverlay(Activity activity, + cgSettings settingsIn) { + + googleOverlay ovl = new googleOverlay(activity, settingsIn, overlayType.PositionOverlay); + getOverlays().add(ovl); + return (cgMapMyOverlay) ovl.getBase(); + } + + @Override + public cgOverlayScale createAddScaleOverlay(Activity activity, + cgSettings settingsIn) { + + googleOverlay ovl = new googleOverlay(activity, settingsIn, overlayType.ScaleOverlay); + getOverlays().add(ovl); + return (cgOverlayScale) ovl.getBase(); + } + + @Override public int getMapZoomLevel() { return getZoomLevel(); } @@ -105,14 +128,12 @@ public class googleMapView extends MapView implements MapViewImpl{ public void setMapSource(cgSettings settings) { switch(settings.mapSource) { - case googleSat: - setSatellite(true); - break; - default: - setSatellite(false); - } - - + case googleSat: + setSatellite(true); + break; + default: + setSatellite(false); + } } @Override @@ -124,4 +145,9 @@ public class googleMapView extends MapView implements MapViewImpl{ public void setBuiltinScale(boolean b) { //Nothing to do for google maps... } + + @Override + public void repaintRequired(OverlayBase overlay) { + invalidate(); + } } diff --git a/src/cgeo/geocaching/googlemaps/googleOverlay.java b/src/cgeo/geocaching/googlemaps/googleOverlay.java index 3847d9f..8473faa 100644 --- a/src/cgeo/geocaching/googlemaps/googleOverlay.java +++ b/src/cgeo/geocaching/googlemaps/googleOverlay.java @@ -1,6 +1,13 @@ package cgeo.geocaching.googlemaps; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import android.app.Activity; import android.graphics.Canvas; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapcommon.cgMapMyOverlay; +import cgeo.geocaching.mapcommon.cgOverlayScale; import cgeo.geocaching.mapinterfaces.MapViewImpl; import cgeo.geocaching.mapinterfaces.OverlayBase; import cgeo.geocaching.mapinterfaces.OverlayImpl; @@ -10,17 +17,39 @@ import com.google.android.maps.Overlay; public class googleOverlay extends Overlay implements OverlayImpl { - private OverlayBase overlayBase; + private OverlayBase overlayBase = null; + private Lock lock = new ReentrantLock(); - public googleOverlay(OverlayBase overlayBaseIn) { - overlayBase = overlayBaseIn; + public googleOverlay(Activity activityIn, cgSettings settingsIn, overlayType ovlType) { + switch (ovlType) { + case PositionOverlay: + overlayBase = new cgMapMyOverlay(settingsIn, activityIn, this); + break; + case ScaleOverlay: + overlayBase = new cgOverlayScale(activityIn, settingsIn, this); + } } @Override public void draw(Canvas canvas, MapView mapView, boolean shadow) { super.draw(canvas, mapView, shadow); - overlayBase.draw(canvas, (MapViewImpl) mapView, shadow); + if (overlayBase != null) { + overlayBase.draw(canvas, (MapViewImpl) mapView, shadow); + } + } + + public OverlayBase getBase() { + return overlayBase; + } + + @Override + public void lock() { + lock.lock(); } + @Override + public void unlock() { + lock.unlock(); + } } diff --git a/src/cgeo/geocaching/googlemaps/googleUsersOverlay.java b/src/cgeo/geocaching/googlemaps/googleUsersOverlay.java index 5019e6e..f5f16a7 100644 --- a/src/cgeo/geocaching/googlemaps/googleUsersOverlay.java +++ b/src/cgeo/geocaching/googlemaps/googleUsersOverlay.java @@ -1,5 +1,8 @@ package cgeo.geocaching.googlemaps; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + import android.content.Context; import android.graphics.Canvas; import android.graphics.Point; @@ -15,6 +18,7 @@ import com.google.android.maps.MapView; public class googleUsersOverlay extends ItemizedOverlay<googleUsersOverlayItem> implements ItemizedOverlayImpl { private cgUsersOverlay base; + private Lock lock = new ReentrantLock(); public googleUsersOverlay(Context contextIn, Drawable markerIn) { super(boundCenter(markerIn)); @@ -91,4 +95,14 @@ public class googleUsersOverlay extends ItemizedOverlay<googleUsersOverlayItem> // Nothing to do here } -}
\ No newline at end of file + @Override + public void lock() { + lock.lock(); + } + + @Override + public void unlock() { + lock.unlock(); + } + +} diff --git a/src/cgeo/geocaching/mapcommon/ItemizedOverlayBase.java b/src/cgeo/geocaching/mapcommon/ItemizedOverlayBase.java index 41739e2..4e0379c 100644 --- a/src/cgeo/geocaching/mapcommon/ItemizedOverlayBase.java +++ b/src/cgeo/geocaching/mapcommon/ItemizedOverlayBase.java @@ -6,6 +6,8 @@ import android.graphics.drawable.Drawable; import cgeo.geocaching.mapinterfaces.ItemizedOverlayImpl; import cgeo.geocaching.mapinterfaces.MapProjectionImpl; import cgeo.geocaching.mapinterfaces.MapViewImpl; +import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.OverlayImpl; import cgeo.geocaching.mapinterfaces.OverlayItemImpl; /** @@ -14,7 +16,7 @@ import cgeo.geocaching.mapinterfaces.OverlayItemImpl; * @author rsudev * */ -public abstract class ItemizedOverlayBase { +public abstract class ItemizedOverlayBase implements OverlayBase { private ItemizedOverlayImpl ovlImpl; @@ -50,6 +52,11 @@ public abstract class ItemizedOverlayBase { MapProjectionImpl projection, byte drawZoomLevel) { ovlImpl.superDrawOverlayBitmap(canvas, drawPosition, projection, drawZoomLevel); } + + @Override + public OverlayImpl getOverlayImpl() { + return ovlImpl; + } public abstract OverlayItemImpl createItem(int index); diff --git a/src/cgeo/geocaching/mapcommon/cgMapMyOverlay.java b/src/cgeo/geocaching/mapcommon/cgMapMyOverlay.java index cbf065c..b8c8723 100644 --- a/src/cgeo/geocaching/mapcommon/cgMapMyOverlay.java +++ b/src/cgeo/geocaching/mapcommon/cgMapMyOverlay.java @@ -6,10 +6,11 @@ import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Paint.Style; import android.graphics.PaintFlagsDrawFilter; import android.graphics.Point; +import android.graphics.Paint.Style; import android.location.Location; import cgeo.geocaching.R; import cgeo.geocaching.cgBase; @@ -19,6 +20,7 @@ import cgeo.geocaching.mapinterfaces.MapFactory; import cgeo.geocaching.mapinterfaces.MapProjectionImpl; import cgeo.geocaching.mapinterfaces.MapViewImpl; import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.OverlayImpl; public class cgMapMyOverlay implements OverlayBase { private cgSettings settings = null; @@ -31,8 +33,8 @@ public class cgMapMyOverlay implements OverlayBase { private Point center = new Point(); private Point left = new Point(); private Bitmap arrow = null; - private int widthArrow = 0; - private int heightArrow = 0; + private int widthArrowHalf = 0; + private int heightArrowHalf = 0; private PaintFlagsDrawFilter setfil = null; private PaintFlagsDrawFilter remfil = null; private Location historyRecent = null; @@ -41,11 +43,13 @@ public class cgMapMyOverlay implements OverlayBase { private Point historyPointP = new Point(); private Activity activity; private MapFactory mapFactory = null; + private OverlayImpl ovlImpl = null; - public cgMapMyOverlay(cgSettings settingsIn, Activity activity) { + public cgMapMyOverlay(cgSettings settingsIn, Activity activity, OverlayImpl ovlImpl) { settings = settingsIn; this.activity = activity; this.mapFactory = settings.getMapFactory(); + this.ovlImpl = ovlImpl; } public void setCoordinates(Location coordinatesIn) { @@ -184,22 +188,29 @@ public class cgMapMyOverlay implements OverlayBase { if (arrow == null) { arrow = BitmapFactory.decodeResource(activity.getResources(), R.drawable.my_location_chevron); - widthArrow = arrow.getWidth(); - heightArrow = arrow.getHeight(); + widthArrowHalf = arrow.getWidth() / 2; + heightArrowHalf = arrow.getHeight() / 2; } int marginLeft; int marginTop; - marginLeft = center.x - (widthArrow / 2); - marginTop = center.y - (heightArrow / 2); + marginLeft = center.x - widthArrowHalf; + marginTop = center.y - heightArrowHalf; + + Matrix matrix = new Matrix(); + matrix.setRotate(heading.floatValue(), widthArrowHalf, heightArrowHalf); + matrix.postTranslate(marginLeft, marginTop); - canvas.rotate(heading.floatValue(), center.x, center.y); - canvas.drawBitmap(arrow, marginLeft, marginTop, null); - canvas.rotate(-(heading.floatValue()), center.x, center.y); + canvas.drawBitmap(arrow, matrix, null); canvas.setDrawFilter(remfil); //super.draw(canvas, mapView, shadow); } + + @Override + public OverlayImpl getOverlayImpl() { + return this.ovlImpl; + } }
\ No newline at end of file diff --git a/src/cgeo/geocaching/mapcommon/cgMapOverlay.java b/src/cgeo/geocaching/mapcommon/cgMapOverlay.java index 80eba5a..d3f7c00 100644 --- a/src/cgeo/geocaching/mapcommon/cgMapOverlay.java +++ b/src/cgeo/geocaching/mapcommon/cgMapOverlay.java @@ -10,9 +10,9 @@ import android.content.Intent; import android.graphics.Canvas; import android.graphics.DashPathEffect; import android.graphics.Paint; -import android.graphics.Paint.Style; import android.graphics.PaintFlagsDrawFilter; import android.graphics.Point; +import android.graphics.Paint.Style; import android.location.Location; import android.text.Html; import android.util.Log; @@ -74,10 +74,16 @@ public class cgMapOverlay extends ItemizedOverlayBase implements OverlayBase { item.setMarker(boundCenterBottom(item.getMarker(0))); } - items = new ArrayList<CacheOverlayItemImpl>(itemsPre); - - setLastFocusedItemIndex(-1); // to reset tap during data change - populate(); + // ensure no interference between the draw and content changing routines + getOverlayImpl().lock(); + try { + items = new ArrayList<CacheOverlayItemImpl>(itemsPre); + + setLastFocusedItemIndex(-1); // to reset tap during data change + populate(); + } finally { + getOverlayImpl().unlock(); + } } public boolean getCircles() { @@ -107,51 +113,56 @@ public class cgMapOverlay extends ItemizedOverlayBase implements OverlayBase { private void drawInternal(Canvas canvas, MapProjectionImpl projection) { - if (displayCircles) { - if (blockedCircle == null) { - blockedCircle = new Paint(); - blockedCircle.setAntiAlias(true); - blockedCircle.setStrokeWidth(1.0f); - blockedCircle.setARGB(127, 0, 0, 0); - blockedCircle.setPathEffect(new DashPathEffect(new float[] {3,2}, 0)); - } - - if (setfil == null) setfil = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG); - if (remfil == null) remfil = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0); - - canvas.setDrawFilter(setfil); - - for (CacheOverlayItemImpl item : items) { - final cgCoord itemCoord = item.getCoord(); - float[] result = new float[1]; - - Location.distanceBetween(itemCoord.latitude, itemCoord.longitude, itemCoord.latitude, itemCoord.longitude + 1, result); - final float longitudeLineDistance = result[0]; - - GeoPointImpl itemGeo = mapFactory.getGeoPointBase((int)(itemCoord.latitude * 1e6), (int)(itemCoord.longitude * 1e6)); - GeoPointImpl leftGeo = mapFactory.getGeoPointBase((int)(itemCoord.latitude * 1e6), (int)((itemCoord.longitude - 161 / longitudeLineDistance) * 1e6)); - - projection.toPixels(itemGeo, center); - projection.toPixels(leftGeo, left); - int radius = center.x - left.x; - - final String type = item.getType(); - if (type == null || "multi".equals(type) || "mystery".equals(type) || "virtual".equals(type)) { - blockedCircle.setColor(0x66000000); - blockedCircle.setStyle(Style.STROKE); - canvas.drawCircle(center.x, center.y, radius, blockedCircle); - } else { - blockedCircle.setColor(0x66BB0000); - blockedCircle.setStyle(Style.STROKE); - canvas.drawCircle(center.x, center.y, radius, blockedCircle); - - blockedCircle.setColor(0x44BB0000); - blockedCircle.setStyle(Style.FILL); - canvas.drawCircle(center.x, center.y, radius, blockedCircle); + // prevent content changes + getOverlayImpl().lock(); + try { + if (displayCircles) { + if (blockedCircle == null) { + blockedCircle = new Paint(); + blockedCircle.setAntiAlias(true); + blockedCircle.setStrokeWidth(1.0f); + blockedCircle.setARGB(127, 0, 0, 0); + blockedCircle.setPathEffect(new DashPathEffect(new float[] {3,2}, 0)); } + + if (setfil == null) setfil = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG); + if (remfil == null) remfil = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0); + + canvas.setDrawFilter(setfil); + + for (CacheOverlayItemImpl item : items) { + final cgCoord itemCoord = item.getCoord(); + float[] result = new float[1]; + + Location.distanceBetween(itemCoord.latitude, itemCoord.longitude, itemCoord.latitude, itemCoord.longitude + 1, result); + final float longitudeLineDistance = result[0]; + + GeoPointImpl itemGeo = mapFactory.getGeoPointBase((int)(itemCoord.latitude * 1e6), (int)(itemCoord.longitude * 1e6)); + GeoPointImpl leftGeo = mapFactory.getGeoPointBase((int)(itemCoord.latitude * 1e6), (int)((itemCoord.longitude - 161 / longitudeLineDistance) * 1e6)); + + projection.toPixels(itemGeo, center); + projection.toPixels(leftGeo, left); + int radius = center.x - left.x; + + final String type = item.getType(); + if (type == null || "multi".equals(type) || "mystery".equals(type) || "virtual".equals(type)) { + blockedCircle.setColor(0x66000000); + blockedCircle.setStyle(Style.STROKE); + canvas.drawCircle(center.x, center.y, radius, blockedCircle); + } else { + blockedCircle.setColor(0x66BB0000); + blockedCircle.setStyle(Style.STROKE); + canvas.drawCircle(center.x, center.y, radius, blockedCircle); + + blockedCircle.setColor(0x44BB0000); + blockedCircle.setStyle(Style.FILL); + canvas.drawCircle(center.x, center.y, radius, blockedCircle); + } + } + canvas.setDrawFilter(remfil); } - - canvas.setDrawFilter(remfil); + } finally { + getOverlayImpl().unlock(); } } @@ -170,7 +181,22 @@ public class cgMapOverlay extends ItemizedOverlayBase implements OverlayBase { } waitDialog.show(); - CacheOverlayItemImpl item = items.get(index); + CacheOverlayItemImpl item = null; + + // prevent concurrent changes + getOverlayImpl().lock(); + try { + if (index < items.size()) { + item = items.get(index); + } + } finally { + getOverlayImpl().unlock(); + } + + if (item == null) { + return false; + } + cgCoord coordinate = item.getCoord(); if (coordinate.type != null && coordinate.type.equalsIgnoreCase("cache") && coordinate.geocode != null && coordinate.geocode.length() > 0) { diff --git a/src/cgeo/geocaching/mapcommon/cgOverlayScale.java b/src/cgeo/geocaching/mapcommon/cgOverlayScale.java index 4aae3d5..b8d6f09 100644 --- a/src/cgeo/geocaching/mapcommon/cgOverlayScale.java +++ b/src/cgeo/geocaching/mapcommon/cgOverlayScale.java @@ -14,6 +14,7 @@ import cgeo.geocaching.mapinterfaces.GeoPointImpl; import cgeo.geocaching.mapinterfaces.MapProjectionImpl; import cgeo.geocaching.mapinterfaces.MapViewImpl; import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.OverlayImpl; public class cgOverlayScale implements OverlayBase { private cgSettings settings = null; @@ -26,9 +27,11 @@ public class cgOverlayScale implements OverlayBase { private double distance = 0d; private double distanceRound = 0d; private String units = null; + private OverlayImpl ovlImpl=null; - public cgOverlayScale(Activity activity, cgSettings settingsIn) { + public cgOverlayScale(Activity activity, cgSettings settingsIn, OverlayImpl overlayImpl) { settings = settingsIn; + this.ovlImpl = overlayImpl; DisplayMetrics metrics = new DisplayMetrics(); activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); @@ -38,8 +41,8 @@ public class cgOverlayScale implements OverlayBase { @Override public void drawOverlayBitmap(Canvas canvas, Point drawPosition, MapProjectionImpl projection, byte drawZoomLevel) { - // TODO Auto-generated method stub - + // Scale overlay is only necessary for google maps, so the mapsforge + // related draw method needs not to be filled. } @Override @@ -138,4 +141,9 @@ public class cgOverlayScale implements OverlayBase { canvas.drawLine(10, bottom, (int)(pixels + 10), bottom, scale); canvas.drawText(String.format("%.0f", distanceRound) + " " + units, (float)(pixels - (10 * pixelDensity)), (bottom - (10 * pixelDensity)), scale); } -}
\ No newline at end of file + + @Override + public OverlayImpl getOverlayImpl() { + return ovlImpl; + } +} diff --git a/src/cgeo/geocaching/mapcommon/cgeomap.java b/src/cgeo/geocaching/mapcommon/cgeomap.java index 474b881..8500e1c 100644 --- a/src/cgeo/geocaching/mapcommon/cgeomap.java +++ b/src/cgeo/geocaching/mapcommon/cgeomap.java @@ -28,13 +28,13 @@ import cgeo.geocaching.cgCoord; import cgeo.geocaching.cgDirection; import cgeo.geocaching.cgGeo; import cgeo.geocaching.cgSettings; -import cgeo.geocaching.cgSettings.mapSourceEnum; import cgeo.geocaching.cgUpdateDir; import cgeo.geocaching.cgUpdateLoc; import cgeo.geocaching.cgUser; import cgeo.geocaching.cgWaypoint; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; +import cgeo.geocaching.cgSettings.mapSourceEnum; import cgeo.geocaching.mapinterfaces.ActivityImpl; import cgeo.geocaching.mapinterfaces.CacheOverlayItemImpl; import cgeo.geocaching.mapinterfaces.GeoPointImpl; @@ -269,8 +269,7 @@ public class cgeomap extends MapBase { mapView.clearOverlays(); if (overlayMyLoc == null) { - overlayMyLoc = new cgMapMyOverlay(settings, activity); - mapView.addOverlay(mapFactory.getOverlayBaseWrapper(overlayMyLoc)); + overlayMyLoc = mapView.createAddPositionOverlay(activity, settings); } if (settings.publicLoc > 0 && overlayUsers == null) { @@ -282,8 +281,7 @@ public class cgeomap extends MapBase { } if (overlayScale == null && mapView.needsScaleOverlay()) { - overlayScale = new cgOverlayScale(activity, settings); - mapView.addOverlay(mapFactory.getOverlayBaseWrapper(overlayScale)); + overlayScale = mapView.createAddScaleOverlay(activity, settings); } mapView.invalidate(); @@ -747,9 +745,10 @@ public class cgeomap extends MapBase { } try { + boolean repaintRequired = false; + if (overlayMyLoc == null && mapView != null) { - overlayMyLoc = new cgMapMyOverlay(settings, activity); - mapView.addOverlay(settings.getMapFactory().getOverlayBaseWrapper(overlayMyLoc)); + overlayMyLoc = mapView.createAddPositionOverlay(activity, settings); } if (overlayMyLoc != null && geo.location != null) { @@ -760,8 +759,7 @@ public class cgeomap extends MapBase { if (followMyLocation) { myLocationInMiddle(); } else { - // move blue arrow - mapView.invalidate(); + repaintRequired = true; } } @@ -771,7 +769,13 @@ public class cgeomap extends MapBase { } else { overlayMyLoc.setHeading(Double.valueOf(0)); } + repaintRequired = true; + } + + if (repaintRequired) { + mapView.repaintRequired(overlayMyLoc); } + } catch (Exception e) { Log.w(cgSettings.tag, "Failed to update location."); } diff --git a/src/cgeo/geocaching/mapinterfaces/ItemizedOverlayImpl.java b/src/cgeo/geocaching/mapinterfaces/ItemizedOverlayImpl.java index 73eed1f..08e28ad 100644 --- a/src/cgeo/geocaching/mapinterfaces/ItemizedOverlayImpl.java +++ b/src/cgeo/geocaching/mapinterfaces/ItemizedOverlayImpl.java @@ -1,9 +1,9 @@ package cgeo.geocaching.mapinterfaces; -import cgeo.geocaching.mapcommon.ItemizedOverlayBase; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.drawable.Drawable; +import cgeo.geocaching.mapcommon.ItemizedOverlayBase; /** * Defines the common functions to access the provider-specific @@ -11,7 +11,7 @@ import android.graphics.drawable.Drawable; * @author rsudev * */ -public interface ItemizedOverlayImpl { +public interface ItemizedOverlayImpl extends OverlayImpl { ItemizedOverlayBase getBase(); diff --git a/src/cgeo/geocaching/mapinterfaces/MapFactory.java b/src/cgeo/geocaching/mapinterfaces/MapFactory.java index 620f673..4348c40 100644 --- a/src/cgeo/geocaching/mapinterfaces/MapFactory.java +++ b/src/cgeo/geocaching/mapinterfaces/MapFactory.java @@ -21,8 +21,6 @@ public interface MapFactory { public GeoPointImpl getGeoPointBase(int latE6, int lonE6); - public OverlayImpl getOverlayBaseWrapper(OverlayBase ovlIn); - CacheOverlayItemImpl getCacheOverlayItem(cgCoord coordinate, String type); public UserOverlayItemImpl getUserOverlayItemBase(Context context, diff --git a/src/cgeo/geocaching/mapinterfaces/MapViewImpl.java b/src/cgeo/geocaching/mapinterfaces/MapViewImpl.java index 5c955ed..8e99b3d 100644 --- a/src/cgeo/geocaching/mapinterfaces/MapViewImpl.java +++ b/src/cgeo/geocaching/mapinterfaces/MapViewImpl.java @@ -1,10 +1,13 @@ package cgeo.geocaching.mapinterfaces; +import android.app.Activity; +import android.content.Context; +import android.graphics.drawable.Drawable; import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapcommon.cgMapMyOverlay; import cgeo.geocaching.mapcommon.cgMapOverlay; +import cgeo.geocaching.mapcommon.cgOverlayScale; import cgeo.geocaching.mapcommon.cgUsersOverlay; -import android.content.Context; -import android.graphics.drawable.Drawable; /** * Defines common functions of the provider-specific @@ -50,6 +53,10 @@ public interface MapViewImpl { Drawable drawable, boolean fromDetailIntent); cgUsersOverlay createAddUsersOverlay(Context context, Drawable markerIn); + + cgOverlayScale createAddScaleOverlay(Activity activity, cgSettings settingsIn); + + cgMapMyOverlay createAddPositionOverlay(Activity activity, cgSettings settingsIn); boolean needsScaleOverlay(); @@ -57,4 +64,6 @@ public interface MapViewImpl { void setMapSource(cgSettings settings); + void repaintRequired(OverlayBase overlay); + } diff --git a/src/cgeo/geocaching/mapinterfaces/OverlayBase.java b/src/cgeo/geocaching/mapinterfaces/OverlayBase.java index 59afb49..17a4d85 100644 --- a/src/cgeo/geocaching/mapinterfaces/OverlayBase.java +++ b/src/cgeo/geocaching/mapinterfaces/OverlayBase.java @@ -16,4 +16,5 @@ public interface OverlayBase { void drawOverlayBitmap(Canvas canvas, Point drawPosition, MapProjectionImpl projection, byte drawZoomLevel); + OverlayImpl getOverlayImpl(); } diff --git a/src/cgeo/geocaching/mapinterfaces/OverlayImpl.java b/src/cgeo/geocaching/mapinterfaces/OverlayImpl.java index 6680b6a..ba45532 100644 --- a/src/cgeo/geocaching/mapinterfaces/OverlayImpl.java +++ b/src/cgeo/geocaching/mapinterfaces/OverlayImpl.java @@ -8,4 +8,13 @@ package cgeo.geocaching.mapinterfaces; */ public interface OverlayImpl { + public enum overlayType { + PositionOverlay, + ScaleOverlay + } + + void lock(); + + void unlock(); + } diff --git a/src/cgeo/geocaching/mapsforge/mfCacheOverlay.java b/src/cgeo/geocaching/mapsforge/mfCacheOverlay.java index 6333133..1e3b287 100644 --- a/src/cgeo/geocaching/mapsforge/mfCacheOverlay.java +++ b/src/cgeo/geocaching/mapsforge/mfCacheOverlay.java @@ -1,5 +1,8 @@ package cgeo.geocaching.mapsforge; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + import org.mapsforge.android.maps.ItemizedOverlay; import org.mapsforge.android.maps.Projection; @@ -17,6 +20,7 @@ import cgeo.geocaching.mapinterfaces.MapViewImpl; public class mfCacheOverlay extends ItemizedOverlay<mfCacheOverlayItem> implements ItemizedOverlayImpl { private cgMapOverlay base; + private Lock lock = new ReentrantLock(); public mfCacheOverlay(cgSettings settingsIn, Context contextIn, Drawable markerIn, Boolean fromDetailIn) { super(boundCenterBottom(markerIn)); @@ -94,5 +98,15 @@ public class mfCacheOverlay extends ItemizedOverlay<mfCacheOverlayItem> implemen super.drawOverlayBitmap(canvas, drawPosition, (Projection) projection.getImpl(), drawZoomLevel); } + @Override + public void lock() { + lock.lock(); + } + + @Override + public void unlock() { + lock.unlock(); + } + } diff --git a/src/cgeo/geocaching/mapsforge/mfMapController.java b/src/cgeo/geocaching/mapsforge/mfMapController.java index 45efef2..932e640 100644 --- a/src/cgeo/geocaching/mapsforge/mfMapController.java +++ b/src/cgeo/geocaching/mapsforge/mfMapController.java @@ -9,9 +9,11 @@ import cgeo.geocaching.mapinterfaces.MapControllerImpl; public class mfMapController implements MapControllerImpl { private MapController mapController; + private int maxZoomLevel; - public mfMapController(MapController mapControllerIn) { + public mfMapController(MapController mapControllerIn, int maxZoomLevelIn) { mapController = mapControllerIn; + maxZoomLevel = maxZoomLevelIn; } @Override @@ -26,7 +28,11 @@ public class mfMapController implements MapControllerImpl { @Override public void setZoom(int mapzoom) { - mapController.setZoom(mapzoom-1); + int mfzoom = mapzoom-1; + if (mfzoom > maxZoomLevel) { + mfzoom = maxZoomLevel; + } + mapController.setZoom(mfzoom); } @Override diff --git a/src/cgeo/geocaching/mapsforge/mfMapFactory.java b/src/cgeo/geocaching/mapsforge/mfMapFactory.java index fa665ec..00204dd 100644 --- a/src/cgeo/geocaching/mapsforge/mfMapFactory.java +++ b/src/cgeo/geocaching/mapsforge/mfMapFactory.java @@ -8,8 +8,6 @@ import cgeo.geocaching.cgUser; import cgeo.geocaching.mapinterfaces.CacheOverlayItemImpl; import cgeo.geocaching.mapinterfaces.GeoPointImpl; import cgeo.geocaching.mapinterfaces.MapFactory; -import cgeo.geocaching.mapinterfaces.OverlayBase; -import cgeo.geocaching.mapinterfaces.OverlayImpl; import cgeo.geocaching.mapinterfaces.UserOverlayItemImpl; public class mfMapFactory implements MapFactory{ @@ -35,12 +33,6 @@ public class mfMapFactory implements MapFactory{ } @Override - public OverlayImpl getOverlayBaseWrapper(OverlayBase ovlIn) { - mfOverlay baseOvl = new mfOverlay(ovlIn); - return baseOvl; - } - - @Override public CacheOverlayItemImpl getCacheOverlayItem(cgCoord coordinate, String type) { mfCacheOverlayItem baseItem = new mfCacheOverlayItem(coordinate, type); return baseItem; diff --git a/src/cgeo/geocaching/mapsforge/mfMapView.java b/src/cgeo/geocaching/mapsforge/mfMapView.java index de4bd1a..1e83f33 100644 --- a/src/cgeo/geocaching/mapsforge/mfMapView.java +++ b/src/cgeo/geocaching/mapsforge/mfMapView.java @@ -7,19 +7,24 @@ import org.mapsforge.android.maps.MapViewMode; import org.mapsforge.android.maps.Overlay; import org.mapsforge.android.maps.Projection; +import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapcommon.cgMapMyOverlay; import cgeo.geocaching.mapcommon.cgMapOverlay; +import cgeo.geocaching.mapcommon.cgOverlayScale; import cgeo.geocaching.mapcommon.cgUsersOverlay; import cgeo.geocaching.mapinterfaces.GeoPointImpl; import cgeo.geocaching.mapinterfaces.MapControllerImpl; import cgeo.geocaching.mapinterfaces.MapProjectionImpl; import cgeo.geocaching.mapinterfaces.MapViewImpl; +import cgeo.geocaching.mapinterfaces.OverlayBase; import cgeo.geocaching.mapinterfaces.OverlayImpl; +import cgeo.geocaching.mapinterfaces.OverlayImpl.overlayType; public class mfMapView extends MapView implements MapViewImpl { @@ -47,7 +52,7 @@ public class mfMapView extends MapView implements MapViewImpl { @Override public MapControllerImpl getMapController() { - return new mfMapController(getController()); + return new mfMapController(getController(), getMaxZoomLevel()); } @Override @@ -88,6 +93,22 @@ public class mfMapView extends MapView implements MapViewImpl { } @Override + public cgMapMyOverlay createAddPositionOverlay(Activity activity, + cgSettings settingsIn) { + mfOverlay ovl = new mfOverlay(activity, settingsIn, overlayType.PositionOverlay); + getOverlays().add(ovl); + return (cgMapMyOverlay) ovl.getBase(); + } + + @Override + public cgOverlayScale createAddScaleOverlay(Activity activity, + cgSettings settingsIn) { + mfOverlay ovl = new mfOverlay(activity, settingsIn, overlayType.ScaleOverlay); + getOverlays().add(ovl); + return (cgOverlayScale) ovl.getBase(); + } + + @Override public int getLatitudeSpan() { int span = 0; @@ -160,10 +181,28 @@ public class mfMapView extends MapView implements MapViewImpl { if (MapDatabase.isValidMapFile(settings.getMapFile())) { setMapViewMode(MapViewMode.CANVAS_RENDERER); super.setMapFile(settings.getMapFile()); + } else { + setMapViewMode(MapViewMode.MAPNIK_TILE_DOWNLOAD); } break; default: setMapViewMode(MapViewMode.MAPNIK_TILE_DOWNLOAD); } } + + @Override + public void repaintRequired(OverlayBase overlay) { + + try { + mfOverlay ovl = (mfOverlay) overlay.getOverlayImpl(); + + if (ovl != null) { + ovl.requestRedraw(); + } + + } catch (Exception e) { + Log.e(cgSettings.tag, "mfMapView.repaintRequired: " + e.toString()); + } + } + } diff --git a/src/cgeo/geocaching/mapsforge/mfOverlay.java b/src/cgeo/geocaching/mapsforge/mfOverlay.java index e015307..f764c6b 100644 --- a/src/cgeo/geocaching/mapsforge/mfOverlay.java +++ b/src/cgeo/geocaching/mapsforge/mfOverlay.java @@ -1,26 +1,56 @@ package cgeo.geocaching.mapsforge; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + import org.mapsforge.android.maps.Overlay; import org.mapsforge.android.maps.Projection; +import android.app.Activity; import android.graphics.Canvas; import android.graphics.Point; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapcommon.cgMapMyOverlay; +import cgeo.geocaching.mapcommon.cgOverlayScale; import cgeo.geocaching.mapinterfaces.OverlayBase; import cgeo.geocaching.mapinterfaces.OverlayImpl; public class mfOverlay extends Overlay implements OverlayImpl { - private OverlayBase overlayBase; + private OverlayBase overlayBase = null; + private Lock lock = new ReentrantLock(); - public mfOverlay(OverlayBase overlayBaseIn) { - overlayBase = overlayBaseIn; + public mfOverlay(Activity activityIn, cgSettings settingsIn, OverlayImpl.overlayType ovlType) { + + switch (ovlType) { + case PositionOverlay: + overlayBase = new cgMapMyOverlay(settingsIn, activityIn, this); + break; + case ScaleOverlay: + overlayBase = new cgOverlayScale(activityIn, settingsIn, this); + } } @Override protected void drawOverlayBitmap(Canvas canvas, Point drawPosition, Projection projection, byte drawZoomLevel) { - overlayBase.drawOverlayBitmap(canvas, drawPosition, new mfMapProjection(projection), drawZoomLevel); + if (overlayBase != null) { + overlayBase.drawOverlayBitmap(canvas, drawPosition, new mfMapProjection(projection), drawZoomLevel); + } } + public OverlayBase getBase() { + return overlayBase; + } + + @Override + public void lock() { + lock.lock(); + } + + @Override + public void unlock() { + lock.unlock(); + } } diff --git a/src/cgeo/geocaching/mapsforge/mfUsersOverlay.java b/src/cgeo/geocaching/mapsforge/mfUsersOverlay.java index 8f3ba04..6e2819c 100644 --- a/src/cgeo/geocaching/mapsforge/mfUsersOverlay.java +++ b/src/cgeo/geocaching/mapsforge/mfUsersOverlay.java @@ -1,5 +1,8 @@ package cgeo.geocaching.mapsforge; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + import org.mapsforge.android.maps.ItemizedOverlay; import org.mapsforge.android.maps.Projection; @@ -15,6 +18,7 @@ import cgeo.geocaching.mapinterfaces.MapViewImpl; public class mfUsersOverlay extends ItemizedOverlay<mfUsersOverlayItem> implements ItemizedOverlayImpl { private cgUsersOverlay base; + private Lock lock = new ReentrantLock(); public mfUsersOverlay(Context contextIn, Drawable markerIn) { super(boundCenter(markerIn)); @@ -94,4 +98,14 @@ public class mfUsersOverlay extends ItemizedOverlay<mfUsersOverlayItem> implemen super.drawOverlayBitmap(canvas, drawPosition, (Projection) projection.getImpl(), drawZoomLevel); } + @Override + public void lock() { + lock.lock(); + } + + @Override + public void unlock() { + lock.unlock(); + } + }
\ No newline at end of file diff --git a/src/cgeo/geocaching/sorting/FindsComparator.java b/src/cgeo/geocaching/sorting/FindsComparator.java new file mode 100644 index 0000000..8553f4b --- /dev/null +++ b/src/cgeo/geocaching/sorting/FindsComparator.java @@ -0,0 +1,40 @@ +package cgeo.geocaching.sorting; + +import cgeo.geocaching.cgBase; +import cgeo.geocaching.cgCache; +import cgeo.geocaching.cgeoapplication; + +public class FindsComparator extends AbstractCacheComparator implements + CacheComparator { + + private cgeoapplication app; + + public FindsComparator(cgeoapplication app) { + this.app = app; + } + + @Override + protected boolean canCompare(cgCache cache1, cgCache cache2) { + return cache1.logCounts != null && cache2.logCounts != null; + } + + @Override + protected int compareCaches(cgCache cache1, cgCache cache2) { + int finds1 = getFindsCount(cache1); + int finds2 = getFindsCount(cache2); + return finds2 - finds1; + } + + private int getFindsCount(cgCache cache) { + int finds = 0; + if (cache.logCounts.isEmpty()) { + cache.logCounts = app.loadLogCounts(cache.geocode); + } + Integer logged = cache.logCounts.get(cgBase.LOG_FOUND_IT); + if (logged != null) { + finds = logged; + } + return finds; + } + +} |
