diff options
Diffstat (limited to 'main/src')
58 files changed, 903 insertions, 574 deletions
diff --git a/main/src/cgeo/geocaching/AboutActivity.java b/main/src/cgeo/geocaching/AboutActivity.java index ef59cee..14786ec 100644 --- a/main/src/cgeo/geocaching/AboutActivity.java +++ b/main/src/cgeo/geocaching/AboutActivity.java @@ -22,8 +22,8 @@ public class AboutActivity extends AbstractActivity { @ViewById(R.id.about_version_string) protected TextView version; @ViewById(R.id.contributors) protected TextView contributors; @ViewById(R.id.changelog_master) protected TextView changeLogMaster; - @ViewById(R.id.changelog_release) protected TextView changeLogRelease; + @ViewById(R.id.changelog_release) protected TextView changeLogRelease; @Override public void onCreate(Bundle savedInstanceState) { // TODO remove this after the theme has been fixed @@ -80,4 +80,9 @@ public class AboutActivity extends AbstractActivity { marketIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); startActivity(marketIntent); } + + @Click(R.id.license) + public void license() { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.apache.org/licenses/LICENSE-2.0.html"))); + } } diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java index 45a1dd2..f43de4e 100644 --- a/main/src/cgeo/geocaching/CacheDetailActivity.java +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -243,7 +243,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return; } } else if (uriHost.contains("coord.info")) { - if (uriPath != null && uriPath.startsWith("/gc")) { + if (StringUtils.startsWith(uriPath, "/gc")) { geocode = uriPath.substring(1).toUpperCase(Locale.US); } else { showToast(res.getString(R.string.err_detail_open)); @@ -251,7 +251,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return; } } else if (uriHost.contains("opencaching.de")) { - if (uriPath != null && uriPath.startsWith("/oc")) { + if (StringUtils.startsWith(uriPath, "/oc")) { geocode = uriPath.substring(1).toUpperCase(Locale.US); } else { geocode = uri.getQueryParameter("wp"); diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java index 3c69197..96fbc06 100644 --- a/main/src/cgeo/geocaching/Geocache.java +++ b/main/src/cgeo/geocaching/Geocache.java @@ -30,8 +30,10 @@ import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.LogTemplateProvider; import cgeo.geocaching.utils.LogTemplateProvider.LogContext; import cgeo.geocaching.utils.MatcherWrapper; +import cgeo.geocaching.utils.UncertainProperty; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import android.app.Activity; @@ -66,7 +68,7 @@ public class Geocache implements ICache, IWaypoint { private String geocode = ""; private String cacheId = ""; private String guid = ""; - private CacheType cacheType = CacheType.UNKNOWN; + private UncertainProperty<CacheType> cacheType = new UncertainProperty<CacheType>(CacheType.UNKNOWN, Tile.ZOOMLEVEL_MIN - 1); private String name = ""; private String ownerDisplayName = ""; private String ownerUserId = ""; @@ -84,7 +86,7 @@ public class Geocache implements ICache, IWaypoint { * lazy initialized */ private String location = null; - private Geopoint coords = null; + private UncertainProperty<Geopoint> coords = new UncertainProperty<Geopoint>(null); private boolean reliableLatLon = false; private String personalNote = null; /** @@ -95,17 +97,18 @@ public class Geocache implements ICache, IWaypoint { * lazy initialized */ private String description = null; - private boolean disabled = false; - private boolean archived = false; - private boolean premiumMembersOnly = false; - private boolean found = false; - private boolean favorite = false; + private Boolean disabled = null; + private Boolean archived = null; + private Boolean premiumMembersOnly = null; + private Boolean found = null; + private Boolean favorite = null; + private Boolean onWatchlist = null; + private Boolean logOffline = null; private int favoritePoints = 0; private float rating = 0; // valid ratings are larger than zero private int votes = 0; private float myVote = 0; // valid ratings are larger than zero private int inventoryItems = 0; - private boolean onWatchlist = false; private final List<String> attributes = new LazyInitializedList<String>() { @Override public List<String> call() { @@ -127,7 +130,6 @@ public class Geocache implements ICache, IWaypoint { }; private List<Trackable> inventory = null; private Map<LogType, Integer> logCounts = new HashMap<LogType, Integer>(); - private boolean logOffline = false; private boolean userModifiedCoords = false; // temporary values private boolean statusChecked = false; @@ -136,7 +138,7 @@ public class Geocache implements ICache, IWaypoint { private final EnumSet<StorageLocation> storageLocation = EnumSet.of(StorageLocation.HEAP); private boolean finalDefined = false; private boolean logPasswordRequired = false; - private int zoomlevel = Tile.ZOOMLEVEL_MAX + 1; + // private int zoomlevel = Tile.ZOOMLEVEL_MIN - 1; private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+"); @@ -192,38 +194,35 @@ public class Geocache implements ICache, IWaypoint { updated = System.currentTimeMillis(); // if parsed cache is not yet detailed and stored is, the information of // the parsed cache will be overwritten - if (!detailed && (other.detailed || zoomlevel < other.zoomlevel)) { + if (!detailed && other.detailed) { detailed = other.detailed; detailedUpdate = other.detailedUpdate; - coords = other.coords; - cacheType = other.cacheType; - zoomlevel = other.zoomlevel; // boolean values must be enumerated here. Other types are assigned outside this if-statement - // TODO: check whether a search or a live map systematically returns those, in which case - // we want to keep the most recent one instead of getting information from the previously - // stored data. This is the case for "archived" for example which has been taken out of this - // list. - premiumMembersOnly = other.premiumMembersOnly; reliableLatLon = other.reliableLatLon; + finalDefined = other.finalDefined; + } + + if (premiumMembersOnly == null) { + premiumMembersOnly = other.premiumMembersOnly; + } + if (found == null) { found = other.found; + } + if (disabled == null) { disabled = other.disabled; + } + if (favorite == null) { favorite = other.favorite; + } + if (archived == null) { + archived = other.archived; + } + if (onWatchlist == null) { onWatchlist = other.onWatchlist; + } + if (logOffline == null) { logOffline = other.logOffline; - finalDefined = other.finalDefined; - archived = other.archived; } - - /* - * No gathering for boolean members if other cache is not-detailed - * and does not have information with higher reliability (denoted by zoomlevel) - * - found - * - own - * - disabled - * - favorite - * - onWatchlist - * - logOffline - */ if (visitedDate == 0) { visitedDate = other.visitedDate; } @@ -239,9 +238,7 @@ public class Geocache implements ICache, IWaypoint { if (StringUtils.isBlank(guid)) { guid = other.guid; } - if (null == cacheType || CacheType.UNKNOWN == cacheType) { - cacheType = other.cacheType; - } + cacheType = UncertainProperty.getMergedProperty(cacheType, other.cacheType); if (StringUtils.isBlank(name)) { name = other.name; } @@ -275,9 +272,7 @@ public class Geocache implements ICache, IWaypoint { if (StringUtils.isBlank(getLocation())) { location = other.getLocation(); } - if (coords == null) { - coords = other.coords; - } + coords = UncertainProperty.getMergedProperty(coords, other.coords); // don't use StringUtils.isBlank here. Otherwise we cannot recognize a note which was deleted on GC if (personalNote == null) { personalNote = other.personalNote; @@ -354,9 +349,6 @@ public class Geocache implements ICache, IWaypoint { if (!reliableLatLon) { reliableLatLon = other.reliableLatLon; } - if (zoomlevel == -1) { - zoomlevel = other.zoomlevel; - } return isEqualTo(other); } @@ -374,14 +366,14 @@ public class Geocache implements ICache, IWaypoint { StringUtils.equalsIgnoreCase(name, other.name) && cacheType == other.cacheType && size == other.size && - found == other.found && - premiumMembersOnly == other.premiumMembersOnly && + ObjectUtils.equals(found, other.found) && + ObjectUtils.equals(premiumMembersOnly, other.premiumMembersOnly) && difficulty == other.difficulty && terrain == other.terrain && (coords != null ? coords.equals(other.coords) : null == other.coords) && reliableLatLon == other.reliableLatLon && - disabled == other.disabled && - archived == other.archived && + ObjectUtils.equals(disabled, other.disabled) && + ObjectUtils.equals(archived, other.archived) && listId == other.listId && StringUtils.equalsIgnoreCase(ownerDisplayName, other.ownerDisplayName) && StringUtils.equalsIgnoreCase(ownerUserId, other.ownerUserId) && @@ -389,9 +381,9 @@ public class Geocache implements ICache, IWaypoint { StringUtils.equalsIgnoreCase(personalNote, other.personalNote) && StringUtils.equalsIgnoreCase(getShortDescription(), other.getShortDescription()) && StringUtils.equalsIgnoreCase(getLocation(), other.getLocation()) && - favorite == other.favorite && + ObjectUtils.equals(favorite, other.favorite) && favoritePoints == other.favoritePoints && - onWatchlist == other.onWatchlist && + ObjectUtils.equals(onWatchlist, other.onWatchlist) && (hidden != null ? hidden.equals(other.hidden) : null == other.hidden) && StringUtils.equalsIgnoreCase(guid, other.guid) && StringUtils.equalsIgnoreCase(getHint(), other.getHint()) && @@ -408,7 +400,7 @@ public class Geocache implements ICache, IWaypoint { logs == other.logs && inventory == other.inventory && logCounts == other.logCounts && - logOffline == other.logOffline && + ObjectUtils.equals(logOffline, other.logOffline) && finalDefined == other.finalDefined; } @@ -452,7 +444,7 @@ public class Geocache implements ICache, IWaypoint { } public boolean isEventCache() { - return cacheType.isEvent(); + return cacheType.getValue().isEvent(); } public void logVisit(final IAbstractActivity fromActivity) { @@ -483,7 +475,7 @@ public class Geocache implements ICache, IWaypoint { if (status) { ActivityMixin.showToast(fromActivity, res.getString(R.string.info_log_saved)); cgData.saveVisitDate(geocode); - logOffline = true; + logOffline = Boolean.TRUE; notifyChange(); } else { @@ -504,7 +496,7 @@ public class Geocache implements ICache, IWaypoint { if (isOwner()) { logTypes.add(LogType.ANNOUNCEMENT); } - } else if (CacheType.WEBCAM == cacheType) { + } else if (CacheType.WEBCAM == cacheType.getValue()) { logTypes.add(LogType.WEBCAM_PHOTO_TAKEN); } else { logTypes.add(LogType.FOUND_IT); @@ -610,21 +602,21 @@ public class Geocache implements ICache, IWaypoint { @Override public boolean isArchived() { - return archived; + return (archived != null && archived.booleanValue()); } @Override public boolean isDisabled() { - return disabled; + return (disabled != null && disabled.booleanValue()); } @Override public boolean isPremiumMembersOnly() { - return premiumMembersOnly; + return (premiumMembersOnly != null && premiumMembersOnly.booleanValue()); } public void setPremiumMembersOnly(boolean members) { - this.premiumMembersOnly = members; + this.premiumMembersOnly = Boolean.valueOf(members); } @Override @@ -775,16 +767,16 @@ public class Geocache implements ICache, IWaypoint { @Override public boolean isFound() { - return found; + return (found != null && found.booleanValue()); } @Override public boolean isFavorite() { - return favorite; + return (favorite != null && favorite.booleanValue()); } public void setFavorite(boolean favorite) { - this.favorite = favorite; + this.favorite = Boolean.valueOf(favorite); } @Override @@ -830,19 +822,22 @@ public class Geocache implements ICache, IWaypoint { @Override public String getNameForSorting() { if (null == nameForSorting) { - final MatcherWrapper matcher = new MatcherWrapper(NUMBER_PATTERN, name); - if (matcher.find()) { - nameForSorting = name.replace(matcher.group(), StringUtils.leftPad(matcher.group(), 6, '0')); - } - else { - nameForSorting = name; + nameForSorting = name; + // pad each number part to a fixed size of 6 digits, so that numerical sorting becomes equivalent to string sorting + MatcherWrapper matcher = new MatcherWrapper(NUMBER_PATTERN, nameForSorting); + int start = 0; + while (matcher.find(start)) { + final String number = matcher.group(); + nameForSorting = StringUtils.substring(nameForSorting, 0, matcher.start()) + StringUtils.leftPad(number, 6, '0') + StringUtils.substring(nameForSorting, matcher.start() + number.length()); + start = matcher.start() + Math.max(6, number.length()); + matcher = new MatcherWrapper(NUMBER_PATTERN, nameForSorting); } } return nameForSorting; } public boolean isVirtual() { - return cacheType.isVirtual(); + return cacheType.getValue().isVirtual(); } public boolean showSize() { @@ -916,11 +911,30 @@ public class Geocache implements ICache, IWaypoint { @Override public Geopoint getCoords() { - return coords; + return coords.getValue(); + } + + public int getCoordZoomLevel() { + return coords.getCertaintyLevel(); } + /** + * Set reliable coordinates + * + * @param coords + */ public void setCoords(Geopoint coords) { - this.coords = coords; + this.coords = new UncertainProperty<Geopoint>(coords); + } + + /** + * Set unreliable coordinates from a certain map zoom level + * + * @param coords + * @param zoomlevel + */ + public void setCoords(Geopoint coords, int zoomlevel) { + this.coords = new UncertainProperty<Geopoint>(coords, zoomlevel); } /** @@ -976,11 +990,11 @@ public class Geocache implements ICache, IWaypoint { @Override public boolean isOnWatchlist() { - return onWatchlist; + return (onWatchlist != null && onWatchlist.booleanValue()); } public void setOnWatchlist(boolean onWatchlist) { - this.onWatchlist = onWatchlist; + this.onWatchlist = Boolean.valueOf(onWatchlist); } /** @@ -1049,11 +1063,11 @@ public class Geocache implements ICache, IWaypoint { } public boolean isLogOffline() { - return logOffline; + return (logOffline != null && logOffline.booleanValue()); } public void setLogOffline(boolean logOffline) { - this.logOffline = logOffline; + this.logOffline = Boolean.valueOf(logOffline); } public boolean isStatusChecked() { @@ -1126,15 +1140,15 @@ public class Geocache implements ICache, IWaypoint { } public void setDisabled(boolean disabled) { - this.disabled = disabled; + this.disabled = Boolean.valueOf(disabled); } public void setArchived(boolean archived) { - this.archived = archived; + this.archived = Boolean.valueOf(archived); } public void setFound(boolean found) { - this.found = found; + this.found = Boolean.valueOf(found); } public void setAttributes(List<String> attributes) { @@ -1165,14 +1179,21 @@ public class Geocache implements ICache, IWaypoint { */ @Override public CacheType getType() { - return cacheType; + return cacheType.getValue(); } public void setType(CacheType cacheType) { if (cacheType == null || CacheType.ALL == cacheType) { throw new IllegalArgumentException("Illegal cache type"); } - this.cacheType = cacheType; + this.cacheType = new UncertainProperty<CacheType>(cacheType); + } + + public void setType(CacheType cacheType, final int zoomlevel) { + if (cacheType == null || CacheType.ALL == cacheType) { + throw new IllegalArgumentException("Illegal cache type"); + } + this.cacheType = new UncertainProperty<CacheType>(cacheType, zoomlevel); } public boolean hasDifficulty() { @@ -1473,14 +1494,6 @@ public class Geocache implements ICache, IWaypoint { storeCache(this, null, newListId, false, handler); } - public int getZoomLevel() { - return this.zoomlevel; - } - - public void setZoomlevel(int zoomlevel) { - this.zoomlevel = zoomlevel; - } - @Override public int getId() { return 0; diff --git a/main/src/cgeo/geocaching/ImageSelectActivity.java b/main/src/cgeo/geocaching/ImageSelectActivity.java index 52f9a7e..738b9a7 100644 --- a/main/src/cgeo/geocaching/ImageSelectActivity.java +++ b/main/src/cgeo/geocaching/ImageSelectActivity.java @@ -6,6 +6,7 @@ import butterknife.Views; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.Log; @@ -328,8 +329,7 @@ public class ImageSelectActivity extends AbstractActivity { // Create the storage directory if it does not exist if (!mediaStorageDir.exists()) { - if (!mediaStorageDir.mkdirs()) { - Log.w("Failed to create directory"); + if (!FileUtils.mkdirs(mediaStorageDir)) { return null; } } diff --git a/main/src/cgeo/geocaching/LogCacheActivity.java b/main/src/cgeo/geocaching/LogCacheActivity.java index e11af0f..207dce5 100644 --- a/main/src/cgeo/geocaching/LogCacheActivity.java +++ b/main/src/cgeo/geocaching/LogCacheActivity.java @@ -545,6 +545,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia if (typeSelected == LogType.FOUND_IT || typeSelected == LogType.ATTENDED) { cache.setFound(true); + cache.setVisitedDate(new Date().getTime()); } cgData.saveChangedCache(cache); diff --git a/main/src/cgeo/geocaching/LogTrackableActivity.java b/main/src/cgeo/geocaching/LogTrackableActivity.java index f0dd7f9..a45d584 100644 --- a/main/src/cgeo/geocaching/LogTrackableActivity.java +++ b/main/src/cgeo/geocaching/LogTrackableActivity.java @@ -183,7 +183,6 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat public void init() { registerForContextMenu(typeButton); - typeButton.setText(typeSelected.getL10n()); typeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -191,10 +190,11 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat } }); + setType(typeSelected); dateButton.setOnClickListener(new DateListener()); setDate(date); - tweetCheck.setChecked(true); + initTwitter(); if (CollectionUtils.isEmpty(possibleLogTypes)) { possibleLogTypes = Trackable.getPossibleLogTypes(); @@ -223,7 +223,10 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat public void setType(LogType type) { typeSelected = type; typeButton.setText(typeSelected.getL10n()); + } + private void initTwitter() { + tweetCheck.setChecked(true); if (Settings.isUseTwitter() && Settings.isTwitterLoginValid()) { tweetBox.setVisibility(View.VISIBLE); } else { diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java index 2d488d6..a3daf97 100644 --- a/main/src/cgeo/geocaching/MainActivity.java +++ b/main/src/cgeo/geocaching/MainActivity.java @@ -14,6 +14,7 @@ import cgeo.geocaching.maps.CGeoMap; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.ui.Formatter; +import cgeo.geocaching.utils.DatabaseBackupUtils; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.ProcessUtils; @@ -462,12 +463,12 @@ public class MainActivity extends AbstractActivity { builder.create().show(); } - void updateCacheCounter() { + public void updateCacheCounter() { (new CountBubbleUpdateThread()).start(); } private void checkRestore() { - if (!cgData.isNewlyCreatedDatebase() || null == cgData.getRestoreFile()) { + if (!cgData.isNewlyCreatedDatebase() || null == DatabaseBackupUtils.getRestoreFile()) { return; } new AlertDialog.Builder(this) @@ -479,7 +480,7 @@ public class MainActivity extends AbstractActivity { public void onClick(final DialogInterface dialog, final int id) { dialog.dismiss(); cgData.resetNewlyCreatedDatabase(); - app.restoreDatabase(MainActivity.this); + DatabaseBackupUtils.restoreDatabase(MainActivity.this); } }) .setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { diff --git a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java index be4ef05..741414b 100644 --- a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java +++ b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java @@ -100,13 +100,13 @@ public class NavigateAnyPointActivity extends AbstractActivity { return rowView; } - private void fillViewHolder(ViewHolder viewHolder, Destination loc) { + private static void fillViewHolder(ViewHolder viewHolder, Destination loc) { String lonString = loc.getCoords().format(GeopointFormatter.Format.LON_DECMINUTE); String latString = loc.getCoords().format(GeopointFormatter.Format.LAT_DECMINUTE); viewHolder.longitude.setText(lonString); viewHolder.latitude.setText(latString); - viewHolder.date.setText(Formatter.formatShortDateTime(getContext(), loc.getDate())); + viewHolder.date.setText(Formatter.formatShortDateTime(loc.getDate())); } private LayoutInflater getInflater() { @@ -490,6 +490,7 @@ public class NavigateAnyPointActivity extends AbstractActivity { return null; } + // get base coordinates Geopoint coords; if (StringUtils.isNotBlank(latText) && StringUtils.isNotBlank(lonText)) { try { @@ -507,8 +508,8 @@ public class NavigateAnyPointActivity extends AbstractActivity { coords = app.currentGeo().getCoords(); } - Geopoint result; - if (StringUtils.isNotBlank(bearingText) && StringUtils.isNotBlank(distanceText)) { + // apply projection + if (coords != null && StringUtils.isNotBlank(bearingText) && StringUtils.isNotBlank(distanceText)) { // bearing & distance double bearing; try { @@ -527,23 +528,14 @@ public class NavigateAnyPointActivity extends AbstractActivity { return null; } - final Geopoint coordsDst = coords.project(bearing, distance); - - if (coordsDst == null) { - showToast(res.getString(R.string.err_point_location_error)); - return null; - } - - result = coordsDst; - } else if (coords != null) { - result = coords; - } else { - return null; + coords = coords.project(bearing, distance); } - saveCoords(result); + if (coords != null) { + saveCoords(coords); + } - return result; + return coords; } private void saveCoords(final Geopoint coords) { diff --git a/main/src/cgeo/geocaching/SearchActivity.java b/main/src/cgeo/geocaching/SearchActivity.java index ccf3edf..57b391f 100644 --- a/main/src/cgeo/geocaching/SearchActivity.java +++ b/main/src/cgeo/geocaching/SearchActivity.java @@ -63,6 +63,7 @@ public class SearchActivity extends AbstractActivity { // search query final Intent intent = getIntent(); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + hideKeyboard(); final String query = intent.getStringExtra(SearchManager.QUERY); final boolean keywordSearch = intent.getBooleanExtra(Intents.EXTRA_KEYWORD_SEARCH, true); diff --git a/main/src/cgeo/geocaching/SelectMapfileActivity.java b/main/src/cgeo/geocaching/SelectMapfileActivity.java index 8f82288..8b50c1f 100644 --- a/main/src/cgeo/geocaching/SelectMapfileActivity.java +++ b/main/src/cgeo/geocaching/SelectMapfileActivity.java @@ -47,7 +47,7 @@ public class SelectMapfileActivity extends AbstractFileListActivity<FileSelectio @Override protected List<File> getBaseFolders() { List<File> folders = new ArrayList<File>(); - for (File dir : getStorages()) { + for (File dir : LocalStorage.getStorages()) { folders.add(new File(dir, "mfmaps")); folders.add(new File(new File(dir, "Locus"), "mapsVector")); folders.add(new File(dir, LocalStorage.cache)); diff --git a/main/src/cgeo/geocaching/StaticMapsProvider.java b/main/src/cgeo/geocaching/StaticMapsProvider.java index 2555b80..eb59bcb 100644 --- a/main/src/cgeo/geocaching/StaticMapsProvider.java +++ b/main/src/cgeo/geocaching/StaticMapsProvider.java @@ -7,6 +7,7 @@ import cgeo.geocaching.geopoint.GeopointFormatter.Format; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; @@ -82,7 +83,7 @@ public final class StaticMapsProvider { // Delete image if it has no contents final long fileSize = file.length(); if (fileSize < MIN_MAP_IMAGE_BYTES) { - file.delete(); + FileUtils.deleteIgnoringFailure(file); } } } @@ -229,10 +230,9 @@ public final class StaticMapsProvider { int waypointId = waypoint.getId(); int waypointMapHash = waypoint.getStaticMapsHashcode(); for (int level = 1; level <= MAPS_LEVEL_MAX; level++) { - try { - StaticMapsProvider.getMapFile(geocode, WAYPOINT_PREFIX + waypointId + "_" + waypointMapHash + '_' + level, false).delete(); - } catch (Exception e) { - Log.e("StaticMapsProvider.removeWpStaticMaps", e); + final File mapFile = StaticMapsProvider.getMapFile(geocode, WAYPOINT_PREFIX + waypointId + "_" + waypointMapHash + '_' + level, false); + if (!FileUtils.delete(mapFile)) { + Log.e("StaticMapsProvider.removeWpStaticMaps failed for " + mapFile.getAbsolutePath()); } } } diff --git a/main/src/cgeo/geocaching/StoredList.java b/main/src/cgeo/geocaching/StoredList.java index 55a155c..ec99d0a 100644 --- a/main/src/cgeo/geocaching/StoredList.java +++ b/main/src/cgeo/geocaching/StoredList.java @@ -72,6 +72,10 @@ public final class StoredList { } public void promptForListSelection(final int titleId, final RunnableWithArgument<Integer> runAfterwards, final boolean onlyConcreteLists, final int exceptListId) { + promptForListSelection(titleId, runAfterwards, onlyConcreteLists, exceptListId, StringUtils.EMPTY); + } + + public void promptForListSelection(final int titleId, final RunnableWithArgument<Integer> runAfterwards, final boolean onlyConcreteLists, final int exceptListId, final String newListName) { final List<StoredList> lists = getSortedLists(); if (lists == null) { @@ -106,7 +110,7 @@ public final class StoredList { runAfterwards.run(StoredList.ALL_LIST_ID); } else if (itemId >= lists.size()) { // create new list on the fly - promptForListCreation(runAfterwards); + promptForListCreation(runAfterwards, newListName); } else { if (runAfterwards != null) { @@ -131,8 +135,8 @@ public final class StoredList { return lists; } - public void promptForListCreation(final RunnableWithArgument<Integer> runAfterwards) { - handleListNameInput("", R.string.list_dialog_create_title, R.string.list_dialog_create, new RunnableWithArgument<String>() { + public void promptForListCreation(final RunnableWithArgument<Integer> runAfterwards, String newListName) { + handleListNameInput(newListName, R.string.list_dialog_create_title, R.string.list_dialog_create, new RunnableWithArgument<String>() { @Override public void run(final String listName) { diff --git a/main/src/cgeo/geocaching/TrackableActivity.java b/main/src/cgeo/geocaching/TrackableActivity.java index d1f323c..35ffdc6 100644 --- a/main/src/cgeo/geocaching/TrackableActivity.java +++ b/main/src/cgeo/geocaching/TrackableActivity.java @@ -154,7 +154,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi } } else if (uriHost.contains("coord.info")) { final String uriPath = uri.getPath().toLowerCase(Locale.US); - if (uriPath != null && uriPath.startsWith("/tb")) { + if (StringUtils.startsWith(uriPath, "/tb")) { geocode = uriPath.substring(1).toUpperCase(Locale.US); guid = null; id = null; diff --git a/main/src/cgeo/geocaching/activity/AbstractActivity.java b/main/src/cgeo/geocaching/activity/AbstractActivity.java index 9aca35e..28c0cdd 100644 --- a/main/src/cgeo/geocaching/activity/AbstractActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractActivity.java @@ -2,16 +2,18 @@ package cgeo.geocaching.activity; import butterknife.Views; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.network.Cookies; +import cgeo.geocaching.settings.Settings; +import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.View; +import android.view.inputmethod.InputMethodManager; import android.widget.EditText; public abstract class AbstractActivity extends FragmentActivity implements IAbstractActivity { @@ -113,4 +115,8 @@ public abstract class AbstractActivity extends FragmentActivity implements IAbst // initialize the action bar title with the activity title for single source ActivityMixin.setTitle(this, getTitle()); } + + protected void hideKeyboard() { + ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0); + } } diff --git a/main/src/cgeo/geocaching/apps/cache/GccApp.java b/main/src/cgeo/geocaching/apps/cache/GccApp.java index b129b45..0bbc2dd 100644 --- a/main/src/cgeo/geocaching/apps/cache/GccApp.java +++ b/main/src/cgeo/geocaching/apps/cache/GccApp.java @@ -1,9 +1,28 @@ package cgeo.geocaching.apps.cache; import cgeo.geocaching.R; +import cgeo.geocaching.utils.ProcessUtils; + +import android.content.Intent; public class GccApp extends AbstractGeneralApp { + private static final String PACKAGE = "eisbehr.gcc"; + private static final String PACKAGE_PRO = "eisbehr.gcc.pro"; + public GccApp() { - super(getString(R.string.cache_menu_gcc), "eisbehr.gcc"); + super(getString(R.string.cache_menu_gcc), null); + } + + @Override + public boolean isInstalled() { + return ProcessUtils.isLaunchable(PACKAGE) || ProcessUtils.isLaunchable(PACKAGE_PRO); + } + + @Override + protected Intent getLaunchIntent() { + if (ProcessUtils.isLaunchable(PACKAGE_PRO)) { + return ProcessUtils.getLaunchIntent(PACKAGE_PRO); + } + return ProcessUtils.getLaunchIntent(PACKAGE); } } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java index 4db889d..ec6b3e1 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java @@ -1,7 +1,6 @@ package cgeo.geocaching.apps.cache.navi; import cgeo.geocaching.Geocache; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; import cgeo.geocaching.cgeoapplication; @@ -15,6 +14,7 @@ import cgeo.geocaching.apps.cache.navi.GoogleNavigationApp.GoogleNavigationBikeA import cgeo.geocaching.apps.cache.navi.GoogleNavigationApp.GoogleNavigationDrivingApp; import cgeo.geocaching.apps.cache.navi.GoogleNavigationApp.GoogleNavigationWalkingApp; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.settings.Settings; import android.app.Activity; import android.app.AlertDialog; @@ -30,51 +30,52 @@ public final class NavigationAppFactory extends AbstractAppFactory { public enum NavigationAppsEnum { /** The internal compass activity */ - COMPASS(new CompassApp(), 0), + COMPASS(new CompassApp(), 0, R.string.pref_navigation_menu_compass), /** The external radar app */ - RADAR(new RadarApp(), 1), + RADAR(new RadarApp(), 1, R.string.pref_navigation_menu_radar), /** The selected map */ - INTERNAL_MAP(new InternalMap(), 2), - /** The internal static map activity */ - STATIC_MAP(new StaticMapApp(), 3), - /** The external Locus app */ - DOWNLOAD_STATIC_MAPS(new DownloadStaticMapsApp(), 20), + INTERNAL_MAP(new InternalMap(), 2, R.string.pref_navigation_menu_internal_map), + /** The internal static map activity, when stored */ + STATIC_MAP(new StaticMapApp(), 3, R.string.pref_navigation_menu_static_map), + /** The internal static map activity, when not yet stored */ + DOWNLOAD_STATIC_MAPS(new DownloadStaticMapsApp(), 20, R.string.pref_navigation_menu_static_map_download), /** The external Locus app */ - LOCUS(new LocusApp(), 4), + LOCUS(new LocusApp(), 4, R.string.pref_navigation_menu_locus), /** The external RMaps app */ - RMAPS(new RMapsApp(), 5), + RMAPS(new RMapsApp(), 5, R.string.pref_navigation_menu_rmaps), /** Google Maps */ - GOOGLE_MAPS(new GoogleMapsApp(), 6), + GOOGLE_MAPS(new GoogleMapsApp(), 6, R.string.pref_navigation_menu_google_maps), /** Google Navigation */ - GOOGLE_NAVIGATION(new GoogleNavigationDrivingApp(), 7), + GOOGLE_NAVIGATION(new GoogleNavigationDrivingApp(), 7, R.string.pref_navigation_menu_google_navigation), /** Google Streetview */ - GOOGLE_STREETVIEW(new StreetviewApp(), 8), + GOOGLE_STREETVIEW(new StreetviewApp(), 8, R.string.pref_navigation_menu_google_streetview), /** The external OruxMaps app */ - ORUX_MAPS(new OruxMapsApp(), 9), + ORUX_MAPS(new OruxMapsApp(), 9, R.string.pref_navigation_menu_oruxmaps), /** The external navigon app */ - NAVIGON(new NavigonApp(), 10), + NAVIGON(new NavigonApp(), 10, R.string.pref_navigation_menu_navigon), /** The external Sygic app */ - SYGIC(new SygicNavigationApp(), 11), + SYGIC(new SygicNavigationApp(), 11, R.string.pref_navigation_menu_sygic), /** * Google Navigation in walking mode */ - GOOGLE_NAVIGATION_WALK(new GoogleNavigationWalkingApp(), 12), + GOOGLE_NAVIGATION_WALK(new GoogleNavigationWalkingApp(), 12, R.string.pref_navigation_menu_google_walk), /** * Google Navigation in walking mode */ - GOOGLE_NAVIGATION_BIKE(new GoogleNavigationBikeApp(), 21), + GOOGLE_NAVIGATION_BIKE(new GoogleNavigationBikeApp(), 21, R.string.pref_navigation_menu_google_bike), /** * Google Maps Directions */ - GOOGLE_MAPS_DIRECTIONS(new GoogleMapsDirectionApp(), 13), + GOOGLE_MAPS_DIRECTIONS(new GoogleMapsDirectionApp(), 13, R.string.pref_navigation_menu_google_maps_directions), - CACHE_BEACON(new CacheBeaconApp(), 14), - GCC(new GccApp(), 15), - WHERE_YOU_GO(new WhereYouGoApp(), 16); + CACHE_BEACON(new CacheBeaconApp(), 14, R.string.pref_navigation_menu_cache_beacon), + GCC(new GccApp(), 15, R.string.pref_navigation_menu_gcc), + WHERE_YOU_GO(new WhereYouGoApp(), 16, R.string.pref_navigation_menu_where_you_go); - NavigationAppsEnum(App app, int id) { + NavigationAppsEnum(final App app, final int id, final int preferenceKey) { this.app = app; this.id = id; + this.preferenceKey = preferenceKey; } /** @@ -86,6 +87,11 @@ public final class NavigationAppFactory extends AbstractAppFactory { */ public final int id; + /** + * key of the related preference in the navigation menu preference screen, used for disabling the preference UI + */ + public final int preferenceKey; + /* * display app name in array adapter * @@ -142,18 +148,20 @@ public final class NavigationAppFactory extends AbstractAppFactory { for (final NavigationAppsEnum navApp : getInstalledNavigationApps()) { if ((showInternalMap || !(navApp.app instanceof InternalMap)) && (showDefaultNavigation || defaultNavigationTool != navApp.id)) { - boolean add = false; - if (cache != null && navApp.app instanceof CacheNavigationApp && navApp.app.isEnabled(cache)) { - add = true; - } - if (waypoint != null && navApp.app instanceof WaypointNavigationApp && ((WaypointNavigationApp) navApp.app).isEnabled(waypoint)) { - add = true; - } - if (destination != null && navApp.app instanceof GeopointNavigationApp) { - add = true; - } - if (add) { - items.add(navApp); + if (Settings.isUseNavigationApp(navApp)) { + boolean add = false; + if (cache != null && navApp.app instanceof CacheNavigationApp && navApp.app.isEnabled(cache)) { + add = true; + } + if (waypoint != null && navApp.app instanceof WaypointNavigationApp && ((WaypointNavigationApp) navApp.app).isEnabled(waypoint)) { + add = true; + } + if (destination != null && navApp.app instanceof GeopointNavigationApp) { + add = true; + } + if (add) { + items.add(navApp); + } } } } @@ -233,7 +241,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { for (final NavigationAppsEnum navApp : getInstalledNavigationApps()) { if (navApp.app instanceof CacheNavigationApp) { final CacheNavigationApp cacheApp = (CacheNavigationApp) navApp.app; - if (cacheApp.isEnabled(cache)) { + if (cacheApp.isEnabled(cache) && Settings.isUseNavigationApp(navApp)) { menu.add(0, MENU_ITEM_OFFSET + navApp.id, 0, navApp.app.getName()); } } @@ -244,7 +252,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { for (final NavigationAppsEnum navApp : getInstalledNavigationApps()) { if (navApp.app instanceof WaypointNavigationApp) { final WaypointNavigationApp waypointApp = (WaypointNavigationApp) navApp.app; - if (waypointApp.isEnabled(waypoint)) { + if (waypointApp.isEnabled(waypoint) && Settings.isUseNavigationApp(navApp)) { menu.add(0, MENU_ITEM_OFFSET + navApp.id, 0, navApp.app.getName()); } } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java b/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java index 7ea86fb..7966733 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java @@ -1,5 +1,6 @@ package cgeo.geocaching.apps.cache.navi; +import cgeo.geocaching.R; import cgeo.geocaching.geopoint.Geopoint; import android.app.Activity; @@ -12,7 +13,7 @@ class NavigonApp extends AbstractPointNavigationApp { private static final String INTENT_EXTRA_KEY_LONGITUDE = "longitude"; NavigonApp() { - super("Navigon", INTENT); + super(getString(R.string.cache_menu_navigon), INTENT); } @Override diff --git a/main/src/cgeo/geocaching/cgData.java b/main/src/cgeo/geocaching/cgData.java index 87710fb..9d636f9 100644 --- a/main/src/cgeo/geocaching/cgData.java +++ b/main/src/cgeo/geocaching/cgData.java @@ -14,6 +14,7 @@ import cgeo.geocaching.files.LocalStorage; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.Log; import org.apache.commons.collections.CollectionUtils; @@ -310,17 +311,17 @@ public class cgData { database = null; } - private static File getBackupFile() { + public static File getBackupFileInternal() { return new File(LocalStorage.getStorage(), "cgeo.sqlite"); } - public static String backupDatabase() { + public static String backupDatabaseInternal() { if (!LocalStorage.isExternalStorageAvailable()) { Log.w("Database wasn't backed up: no external memory"); return null; } - final File target = getBackupFile(); + final File target = getBackupFileInternal(); closeDb(); final boolean backupDone = LocalStorage.copy(databasePath(), target); init(); @@ -351,7 +352,9 @@ public class cgData { return false; } - source.delete(); + if (!FileUtils.delete(source)) { + Log.e("Original database could not be deleted during move"); + } Settings.setDbOnSDCard(!Settings.isDbOnSDCard()); Log.i("Database was moved to " + target); init(); @@ -370,18 +373,13 @@ public class cgData { return databasePath(Settings.isDbOnSDCard()); } - public static File getRestoreFile() { - final File fileSourceFile = getBackupFile(); - return fileSourceFile.exists() ? fileSourceFile : null; - } - - public static boolean restoreDatabase() { + public static boolean restoreDatabaseInternal() { if (!LocalStorage.isExternalStorageAvailable()) { Log.w("Database wasn't restored: no external memory"); return false; } - final File sourceFile = getBackupFile(); + final File sourceFile = getBackupFileInternal(); closeDb(); final boolean restoreDone = LocalStorage.copy(sourceFile, databasePath()); init(); @@ -409,7 +407,7 @@ public class cgData { public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) { final File file = new File(name); - file.getParentFile().mkdirs(); + FileUtils.mkdirs(file.getParentFile()); return SQLiteDatabase.openOrCreateDatabase(file, factory); } @@ -778,7 +776,7 @@ public class cgData { final File[] wrongFiles = dir.listFiles(filter); if (wrongFiles != null) { for (final File wrongFile : wrongFiles) { - wrongFile.delete(); + FileUtils.deleteIgnoringFailure(wrongFile); } } } @@ -828,7 +826,7 @@ public class cgData { for (final File file : LocalStorage.getStorageSec().listFiles()) { if (file.isDirectory()) { // This will silently fail if the directory is not empty. - file.delete(); + FileUtils.deleteIgnoringFailure(file); } } } @@ -2748,8 +2746,6 @@ public class cgData { /** * checks if this is a newly created database - * - * @return */ public static boolean isNewlyCreatedDatebase() { return newlyCreatedDatabase; @@ -2762,6 +2758,10 @@ public class cgData { newlyCreatedDatabase = false; } + /** + * Creates the WHERE clause for matching multiple geocodes. This automatically converts all given codes to + * UPPERCASE. + */ private static StringBuilder whereGeocodeIn(Set<String> geocodes) { final StringBuilder where = new StringBuilder(); @@ -2771,7 +2771,7 @@ public class cgData { if (all.length() > 0) { all.append(','); } - all.append(DatabaseUtils.sqlEscapeString(geocode)); + all.append(DatabaseUtils.sqlEscapeString(StringUtils.upperCase(geocode))); } where.append("geocode in (").append(all).append(')'); @@ -3000,7 +3000,7 @@ public class cgData { for (String geocode : cachedGeocodes) { if (connector.canHandle(geocode)) { Geocache geocache = cacheCache.getCacheFromCache(geocode); - if (geocache.getZoomLevel() <= maxZoom) { + if (geocache.getCoordZoomLevel() <= maxZoom) { boolean found = false; for (Tile tile : tiles) { if (tile.containsPoint(geocache)) { diff --git a/main/src/cgeo/geocaching/cgeoapplication.java b/main/src/cgeo/geocaching/cgeoapplication.java index fd5f714..5a793f5 100644 --- a/main/src/cgeo/geocaching/cgeoapplication.java +++ b/main/src/cgeo/geocaching/cgeoapplication.java @@ -25,7 +25,11 @@ public class cgeoapplication extends Application { private static cgeoapplication instance; public cgeoapplication() { - instance = this; + setInstance(this); + } + + private static void setInstance(final cgeoapplication application) { + instance = application; } public static cgeoapplication getInstance() { @@ -86,39 +90,6 @@ public class cgeoapplication extends Application { } /** - * restore the database in a new thread, showing a progress window - * - * @param fromActivity - * calling activity - */ - public void restoreDatabase(final Activity fromActivity) { - final Resources res = this.getResources(); - final ProgressDialog dialog = ProgressDialog.show(fromActivity, res.getString(R.string.init_backup_restore), res.getString(R.string.init_restore_running), true, false); - final AtomicBoolean atomic = new AtomicBoolean(false); - Thread restoreThread = new Thread() { - final Handler handler = new Handler() { - @Override - public void handleMessage(Message msg) { - dialog.dismiss(); - boolean restored = atomic.get(); - String message = restored ? res.getString(R.string.init_restore_success) : res.getString(R.string.init_restore_failed); - ActivityMixin.helpDialog(fromActivity, res.getString(R.string.init_backup_restore), message); - if (fromActivity instanceof MainActivity) { - ((MainActivity) fromActivity).updateCacheCounter(); - } - } - }; - - @Override - public void run() { - atomic.set(cgData.restoreDatabase()); - handler.sendMessage(handler.obtainMessage()); - } - }; - restoreThread.start(); - } - - /** * Register an observer to receive GeoData information. * <br/> * If there is a chance that no observers are registered before this diff --git a/main/src/cgeo/geocaching/cgeocaches.java b/main/src/cgeo/geocaching/cgeocaches.java index 5d7841d..304aabe 100644 --- a/main/src/cgeo/geocaching/cgeocaches.java +++ b/main/src/cgeo/geocaching/cgeocaches.java @@ -396,6 +396,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } }; private AbstractSearchLoader currentLoader; + private String newListName = StringUtils.EMPTY; public cgeocaches() { super(true); @@ -503,7 +504,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity // refresh standard list if it has changed (new caches downloaded) if (type == CacheListType.OFFLINE && listId >= StoredList.STANDARD_LIST_ID && search != null) { final SearchResult newSearch = cgData.getBatchOfStoredCaches(coords, Settings.getCacheType(), listId); - if (newSearch != null && newSearch.getTotal() != search.getTotal()) { + if (newSearch.getTotal() != search.getTotal()) { refreshCurrentList(); } } @@ -717,7 +718,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity invalidateOptionsMenuCompatible(); return false; case MENU_CREATE_LIST: - new StoredList.UserInterface(this).promptForListCreation(null); + new StoredList.UserInterface(this).promptForListCreation(null, newListName); invalidateOptionsMenuCompatible(); return false; case MENU_DROP_LIST: @@ -927,7 +928,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity adapter.setSelectMode(false); refreshCurrentList(); } - }, true, listId); + }, true, listId, newListName); break; case MENU_STORE_CACHE: case MENU_REFRESH: @@ -1082,7 +1083,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity public void run(final Integer selectedListId) { refreshStored(caches, selectedListId); } - }, true, StoredList.TEMPORARY_LIST_ID); + }, true, StoredList.TEMPORARY_LIST_ID, newListName); } else { refreshStored(caches, this.listId); } @@ -1481,9 +1482,9 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity currentLoader.reset(); ((OfflineGeocacheListLoader) currentLoader).setListId(listId); ((OfflineGeocacheListLoader) currentLoader).setSearchCenter(coords); + adapter.setComparator(null); // delete current sorting currentLoader.startLoading(); - invalidateOptionsMenuCompatible(); } @@ -1719,13 +1720,13 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity break; case KEYWORD: final String keyword = extras.getString(Intents.EXTRA_KEYWORD); - title = keyword; + rememberTerm(keyword); loader = new KeywordGeocacheListLoader(app, keyword); break; case ADDRESS: final String address = extras.getString(Intents.EXTRA_ADDRESS); if (StringUtils.isNotBlank(address)) { - title = address; + rememberTerm(address); } else { title = coords.toString(); } @@ -1738,12 +1739,12 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity break; case USERNAME: final String username = extras.getString(Intents.EXTRA_USERNAME); - title = username; + rememberTerm(username); loader = new UsernameGeocacheListLoader(app, username); break; case OWNER: final String ownerName = extras.getString(Intents.EXTRA_USERNAME); - title = ownerName; + rememberTerm(ownerName); loader = new OwnerGeocacheListLoader(app, ownerName); break; case MAP: @@ -1771,6 +1772,13 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity return loader; } + private void rememberTerm(String term) { + // set the title of the activity + title = term; + // and remember this term for potential use in list creation + newListName = term; + } + @Override public void onLoadFinished(Loader<SearchResult> arg0, SearchResult searchIn) { // The database search was moved into the UI call intentionally. If this is done before the runOnUIThread, diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java index c2aeffd..e93df14 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java @@ -127,7 +127,7 @@ public final class GCConstants { public final static Pattern PATTERN_SEARCH_TOTALCOUNT = Pattern.compile("<span>Total Records\\D*(\\d+)<"); public final static Pattern PATTERN_SEARCH_RECAPTCHA = Pattern.compile("<script[^>]*src=\"[^\"]*/recaptcha/api/challenge\\?k=([^\"]+)\"[^>]*>"); public final static Pattern PATTERN_SEARCH_RECAPTCHACHALLENGE = Pattern.compile("challenge : '([^']+)'"); - public final static Pattern PATTERN_SEARCH_HIDDEN_DATE = Pattern.compile("<span class=\"small\">([\\d-/]{6,10})</span>"); + public final static Pattern PATTERN_SEARCH_HIDDEN_DATE = Pattern.compile("<td valign=\"top\"[^<]+<span class=\"small\">([^<]+)</span>"); /** * Patterns for waypoints diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index d243306..4fdde56 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCMap.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -209,16 +209,15 @@ public class GCMap { cache.setReliableLatLon(false); cache.setGeocode(id); cache.setName(nameCache.get(id)); - cache.setZoomlevel(tile.getZoomlevel()); - cache.setCoords(tile.getCoord(xy)); + cache.setCoords(tile.getCoord(xy), tile.getZoomLevel()); if (strategy.flags.contains(StrategyFlag.PARSE_TILES) && bitmap != null) { for (UTFGridPosition singlePos : singlePositions.get(id)) { - if (IconDecoder.parseMapPNG(cache, bitmap, singlePos, tile.getZoomlevel())) { + if (IconDecoder.parseMapPNG(cache, bitmap, singlePos, tile.getZoomLevel())) { break; // cache parsed } } } else { - cache.setType(CacheType.UNKNOWN); + cache.setType(CacheType.UNKNOWN, tile.getZoomLevel()); } boolean exclude = false; @@ -296,7 +295,7 @@ public class GCMap { final Set<Tile> tiles = Tile.getTilesForViewport(viewport); if (Settings.isDebug()) { - searchResult.setUrl(new StringBuilder().append(tiles.iterator().next().getZoomlevel()).append(Formatter.SEPARATOR).append(searchResult.getUrl()).toString()); + searchResult.setUrl(new StringBuilder().append(tiles.iterator().next().getZoomLevel()).append(Formatter.SEPARATOR).append(searchResult.getUrl()).toString()); } for (Tile tile : tiles) { @@ -305,7 +304,7 @@ public class GCMap { final Parameters params = new Parameters( "x", String.valueOf(tile.getX()), "y", String.valueOf(tile.getY()), - "z", String.valueOf(tile.getZoomlevel()), + "z", String.valueOf(tile.getZoomLevel()), "ep", "1", "app", "cgeo"); if (tokens != null) { @@ -321,7 +320,7 @@ public class GCMap { } else if (Settings.getCacheType() == CacheType.MYSTERY) { params.put("ect", "9,5,3,6,453,13,1304,137,11,4,2,1858"); } - if (tile.getZoomlevel() != 14) { + if (tile.getZoomLevel() != 14) { params.put("_", String.valueOf(System.currentTimeMillis())); } // TODO: other types t.b.d @@ -341,7 +340,7 @@ public class GCMap { Log.w("GCMap.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")"); } else { final SearchResult search = GCMap.parseMapJSON(data, tile, bitmap, strategy); - if (search == null || CollectionUtils.isEmpty(search.getGeocodes())) { + if (CollectionUtils.isEmpty(search.getGeocodes())) { Log.e("GCMap.searchByViewport: No cache parsed for viewport " + viewport); } else { @@ -359,7 +358,7 @@ public class GCMap { } // Check for vanished found caches - if (tiles.iterator().next().getZoomlevel() >= Tile.ZOOMLEVEL_MIN_PERSONALIZED) { + if (tiles.iterator().next().getZoomLevel() >= Tile.ZOOMLEVEL_MIN_PERSONALIZED) { searchResult.addFilteredGeocodes(cgData.getCachedMissingFromSearch(searchResult, tiles, GCConnector.getInstance(), Tile.ZOOMLEVEL_MIN_PERSONALIZED - 1)); } } diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index 7dc048a..0dc4242 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -529,9 +529,7 @@ public abstract class GCParser { if (result != null) { // replace linebreak and paragraph tags final String hint = GCConstants.PATTERN_LINEBREAK.matcher(result).replaceAll("\n"); - if (hint != null) { - cache.setHint(StringUtils.replace(hint, "</p>", "").trim()); - } + cache.setHint(StringUtils.replace(hint, "</p>", "").trim()); } cache.checkFields(); @@ -766,7 +764,7 @@ public abstract class GCParser { public static SearchResult searchByNextPage(final SearchResult search, boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) { if (search == null) { - return search; + return null; } final String[] viewstates = search.getViewstates(); @@ -1131,9 +1129,9 @@ public abstract class GCParser { * @return status code to indicate success or failure */ public static ImmutablePair<StatusCode, String> uploadLogImage(final String logId, final String caption, final String description, final Uri imageUri) { - final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/upload.aspx").build().toString(); + final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/upload.aspx").encodedQuery("LID=" + logId).build().toString(); - final String page = Login.getRequestLogged(uri, new Parameters("LID=", logId)); + final String page = Login.getRequestLogged(uri, null); if (StringUtils.isBlank(page)) { Log.e("GCParser.uploadLogImage: No data from server"); return new ImmutablePair<StatusCode, String>(StatusCode.UNKNOWN_ERROR, null); @@ -1717,7 +1715,7 @@ public abstract class GCParser { try { final Integer ctl = Integer.valueOf(trackableMatcher.group(3)); final Integer id = Integer.valueOf(trackableMatcher.group(5)); - if (trackCode != null && name != null && ctl != null && id != null) { + if (trackCode != null && ctl != null && id != null) { final TrackableLog entry = new TrackableLog(trackCode, name, id, ctl); Log.i("Trackable in inventory (#" + entry.ctl + "/" + entry.id + "): " + entry.trackCode + " - " + entry.name); diff --git a/main/src/cgeo/geocaching/connector/gc/IconDecoder.java b/main/src/cgeo/geocaching/connector/gc/IconDecoder.java index ed44392..c7b470a 100644 --- a/main/src/cgeo/geocaching/connector/gc/IconDecoder.java +++ b/main/src/cgeo/geocaching/connector/gc/IconDecoder.java @@ -1,8 +1,8 @@ package cgeo.geocaching.connector.gc; import cgeo.geocaching.Geocache; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.settings.Settings; import android.graphics.Bitmap; @@ -87,19 +87,19 @@ public abstract class IconDecoder { if (count > 1) { // 2 pixels need to detect same type and we say good to go switch (type) { case CT_TRADITIONAL: - cache.setType(CacheType.TRADITIONAL); + cache.setType(CacheType.TRADITIONAL, zoomlevel); return true; case CT_MULTI: - cache.setType(CacheType.MULTI); + cache.setType(CacheType.MULTI, zoomlevel); return true; case CT_MYSTERY: - cache.setType(CacheType.MYSTERY); + cache.setType(CacheType.MYSTERY, zoomlevel); return true; case CT_EVENT: - cache.setType(CacheType.EVENT); + cache.setType(CacheType.EVENT, zoomlevel); return true; case CT_EARTH: - cache.setType(CacheType.EARTH); + cache.setType(CacheType.EARTH, zoomlevel); return true; case CT_FOUND: cache.setFound(true); @@ -108,22 +108,22 @@ public abstract class IconDecoder { cache.setOwnerUserId(Settings.getUsername()); return true; case CT_MEGAEVENT: - cache.setType(CacheType.MEGA_EVENT); + cache.setType(CacheType.MEGA_EVENT, zoomlevel); return true; case CT_CITO: - cache.setType(CacheType.CITO); + cache.setType(CacheType.CITO, zoomlevel); return true; case CT_WEBCAM: - cache.setType(CacheType.WEBCAM); + cache.setType(CacheType.WEBCAM, zoomlevel); return true; case CT_WHERIGO: - cache.setType(CacheType.WHERIGO); + cache.setType(CacheType.WHERIGO, zoomlevel); return true; case CT_VIRTUAL: - cache.setType(CacheType.VIRTUAL); + cache.setType(CacheType.VIRTUAL, zoomlevel); return true; case CT_LETTERBOX: - cache.setType(CacheType.LETTERBOX); + cache.setType(CacheType.LETTERBOX, zoomlevel); return true; } } diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java index 2629339..0d8fb05 100644 --- a/main/src/cgeo/geocaching/connector/gc/Login.java +++ b/main/src/cgeo/geocaching/connector/gc/Login.java @@ -30,20 +30,22 @@ import java.util.Map; public abstract class Login { + private static final String DEFAULT_CUSTOM_DATE_FORMAT = "MM/dd/yyyy"; + private final static String ENGLISH = "<a href=\"#\">English▼</a>"; // false = not logged in private static boolean actualLoginStatus = false; - private static String actualUserName = ""; + private static String actualUserName = StringUtils.EMPTY; private static int actualCachesFound = -1; - private static String actualStatus = ""; + private static String actualStatus = StringUtils.EMPTY; - private final static Map<String, SimpleDateFormat> gcCustomDateFormats; + private final static Map<String, SimpleDateFormat> GC_CUSTOM_DATE_FORMATS; public static final String LANGUAGE_CHANGE_URI = "http://www.geocaching.com/my/souvenirs.aspx"; static { final String[] formats = new String[] { - "MM/dd/yyyy", + DEFAULT_CUSTOM_DATE_FORMAT, "yyyy-MM-dd", "yyyy/MM/dd", "dd/MMM/yyyy", @@ -58,7 +60,7 @@ public abstract class Login { map.put(format, new SimpleDateFormat(format, Locale.ENGLISH)); } - gcCustomDateFormats = Collections.unmodifiableMap(map); + GC_CUSTOM_DATE_FORMATS = Collections.unmodifiableMap(map); } public static StatusCode login() { @@ -321,14 +323,14 @@ public abstract class Login { final String trimmed = input.trim(); - if (gcCustomDateFormats.containsKey(format)) { + if (GC_CUSTOM_DATE_FORMATS.containsKey(format)) { try { - return gcCustomDateFormats.get(format).parse(trimmed); + return GC_CUSTOM_DATE_FORMATS.get(format).parse(trimmed); } catch (final ParseException e) { } } - for (final SimpleDateFormat sdf : gcCustomDateFormats.values()) { + for (final SimpleDateFormat sdf : GC_CUSTOM_DATE_FORMATS.values()) { try { return sdf.parse(trimmed); } catch (final ParseException e) { @@ -344,11 +346,11 @@ public abstract class Login { public static SimpleDateFormat getCustomGcDateFormat() { final String format = Settings.getGcCustomDate(); - if (gcCustomDateFormats.containsKey(format)) { - return gcCustomDateFormats.get(format); + if (GC_CUSTOM_DATE_FORMATS.containsKey(format)) { + return GC_CUSTOM_DATE_FORMATS.get(format); } - return gcCustomDateFormats.get("MM/dd/yyyy"); + return GC_CUSTOM_DATE_FORMATS.get(DEFAULT_CUSTOM_DATE_FORMAT); } /** diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java index 3177f2c..4ed53c9 100644 --- a/main/src/cgeo/geocaching/connector/gc/Tile.java +++ b/main/src/cgeo/geocaching/connector/gc/Tile.java @@ -50,7 +50,7 @@ public class Tile { private final int tileX; private final int tileY; - private final int zoomlevel; + private final int zoomLevel; private final Viewport viewPort; public Tile(Geopoint origin, int zoomlevel) { @@ -59,7 +59,7 @@ public class Tile { private Tile(int tileX, int tileY, int zoomlevel) { - this.zoomlevel = clippedZoomlevel(zoomlevel); + this.zoomLevel = clippedZoomlevel(zoomlevel); this.tileX = tileX; this.tileY = tileY; @@ -67,8 +67,8 @@ public class Tile { viewPort = new Viewport(getCoord(new UTFGridPosition(0, 0)), getCoord(new UTFGridPosition(63, 63))); } - public int getZoomlevel() { - return zoomlevel; + public int getZoomLevel() { + return zoomLevel; } private static int clippedZoomlevel(int zoomlevel) { @@ -120,14 +120,14 @@ public class Tile { double pixX = tileX * TILE_SIZE + pos.x * 4; double pixY = tileY * TILE_SIZE + pos.y * 4; - double lonDeg = ((360.0 * pixX) / NUMBER_OF_PIXELS[this.zoomlevel]) - 180.0; - double latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * pixY / NUMBER_OF_PIXELS[this.zoomlevel]))); + double lonDeg = ((360.0 * pixX) / NUMBER_OF_PIXELS[this.zoomLevel]) - 180.0; + double latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * pixY / NUMBER_OF_PIXELS[this.zoomLevel]))); return new Geopoint(Math.toDegrees(latRad), lonDeg); } @Override public String toString() { - return String.format(Locale.US, "(%d/%d), zoom=%d", tileX, tileY, zoomlevel); + return String.format(Locale.US, "(%d/%d), zoom=%d", tileX, tileY, zoomLevel); } /** @@ -225,7 +225,7 @@ public class Tile { } return (this.tileX == ((Tile) o).tileX) && (this.tileY == ((Tile) o).tileY) - && (this.zoomlevel == ((Tile) o).zoomlevel); + && (this.zoomLevel == ((Tile) o).zoomLevel); } @Override diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java index be7b3a4..139a48e 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java @@ -247,9 +247,7 @@ final class OkapiClient { while (keys.hasNext()) { final String key = keys.next(); final Geocache cache = parseSmallCache(cachesResponse.getJSONObject(key)); - if (cache != null) { - caches.add(cache); - } + caches.add(cache); } return caches; } diff --git a/main/src/cgeo/geocaching/export/FieldnoteExport.java b/main/src/cgeo/geocaching/export/FieldnoteExport.java index 38cd43e..d0040a9 100644 --- a/main/src/cgeo/geocaching/export/FieldnoteExport.java +++ b/main/src/cgeo/geocaching/export/FieldnoteExport.java @@ -12,6 +12,7 @@ import cgeo.geocaching.network.Parameters; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.utils.AsyncTaskWithProgress; +import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.IOUtils; import cgeo.geocaching.utils.Log; @@ -82,7 +83,7 @@ class FieldnoteExport extends AbstractExport { final CheckBox onlyNewOption = (CheckBox) layout.findViewById(R.id.onlynew); if (Settings.getFieldnoteExportDate() > 0) { - onlyNewOption.setText(getString(R.string.export_fieldnotes_onlynew) + "\n(" + Formatter.formatShortDateTime(activity, Settings.getFieldnoteExportDate()) + ')'); + onlyNewOption.setText(getString(R.string.export_fieldnotes_onlynew) + "\n(" + Formatter.formatShortDateTime(Settings.getFieldnoteExportDate()) + ')'); } builder.setPositiveButton(R.string.export, new DialogInterface.OnClickListener() { @@ -134,7 +135,7 @@ class FieldnoteExport extends AbstractExport { for (final Geocache cache : caches) { if (cache.isLogOffline()) { final LogEntry log = cgData.loadLogOffline(cache.getGeocode()); - if (!onlyNew || onlyNew && log.date > Settings.getFieldnoteExportDate()) { + if (!onlyNew || log.date > Settings.getFieldnoteExportDate()) { appendFieldNote(fieldNoteBuffer, cache, log); } } @@ -151,7 +152,7 @@ class FieldnoteExport extends AbstractExport { return false; } - exportLocation.mkdirs(); + FileUtils.mkdirs(exportLocation); final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US); exportFile = new File(exportLocation.toString() + '/' + fileNameDateFormat.format(new Date()) + ".txt"); diff --git a/main/src/cgeo/geocaching/export/GpxExport.java b/main/src/cgeo/geocaching/export/GpxExport.java index 61be3c5..821a3f6 100644 --- a/main/src/cgeo/geocaching/export/GpxExport.java +++ b/main/src/cgeo/geocaching/export/GpxExport.java @@ -1,13 +1,16 @@ package cgeo.geocaching.export; import cgeo.geocaching.Geocache; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.R; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.AsyncTaskWithProgress; +import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.Log; +import org.apache.commons.lang3.CharEncoding; + import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -22,8 +25,9 @@ import android.widget.TextView; import java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStreamWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -106,6 +110,12 @@ class GpxExport extends AbstractExport { this.activity = activity; } + private File getExportFile() { + final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + final Date now = new Date(); + return FileUtils.getUniqueNamedFile(Settings.getGpxExportDir() + File.separatorChar + "export_" + fileNameDateFormat.format(now) + ".gpx"); + } + @Override protected File doInBackgroundInternal(String[] geocodes) { // quick check for being able to write the GPX file @@ -117,18 +127,17 @@ class GpxExport extends AbstractExport { setMessage(cgeoapplication.getInstance().getResources().getQuantityString(R.plurals.cache_counts, allGeocodes.size(), allGeocodes.size())); - final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US); - final File exportFile = new File(Settings.getGpxExportDir() + File.separatorChar + "export_" + fileNameDateFormat.format(new Date()) + ".gpx"); + final File exportFile = getExportFile(); BufferedWriter writer = null; try { final File exportLocation = new File(Settings.getGpxExportDir()); - exportLocation.mkdirs(); + FileUtils.mkdirs(exportLocation); - writer = new BufferedWriter(new FileWriter(exportFile)); + writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(exportFile), CharEncoding.UTF_8)); new GpxSerializer().writeGPX(allGeocodes, writer, new GpxSerializer.ProgressListener() { @Override - public void publishProgress(int countExported) { + public void publishProgress(final int countExported) { ExportTask.this.publishProgress(countExported); } }); @@ -142,9 +151,9 @@ class GpxExport extends AbstractExport { // Ignore double error } } - // delete partial gpx file on error + // delete partial GPX file on error if (exportFile.exists()) { - exportFile.delete(); + FileUtils.deleteIgnoringFailure(exportFile); } return null; diff --git a/main/src/cgeo/geocaching/export/GpxSerializer.java b/main/src/cgeo/geocaching/export/GpxSerializer.java index 2d25296..1e39be4 100644 --- a/main/src/cgeo/geocaching/export/GpxSerializer.java +++ b/main/src/cgeo/geocaching/export/GpxSerializer.java @@ -11,6 +11,7 @@ import cgeo.geocaching.utils.TextUtils; import cgeo.geocaching.utils.XmlUtils; import cgeo.org.kxml2.io.KXmlSerializer; +import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; import org.xmlpull.v1.XmlSerializer; @@ -56,7 +57,7 @@ public final class GpxSerializer { this.progressListener = progressListener; gpx.setOutput(writer); - gpx.startDocument("UTF-8", true); + gpx.startDocument(CharEncoding.UTF_8, true); gpx.setPrefix("", PREFIX_GPX); gpx.setPrefix("xsi", PREFIX_XSI); gpx.setPrefix("groundspeak", PREFIX_GROUNDSPEAK); diff --git a/main/src/cgeo/geocaching/files/AbstractFileListActivity.java b/main/src/cgeo/geocaching/files/AbstractFileListActivity.java index 8b02eeb..b0aba58 100644 --- a/main/src/cgeo/geocaching/files/AbstractFileListActivity.java +++ b/main/src/cgeo/geocaching/files/AbstractFileListActivity.java @@ -5,7 +5,6 @@ import cgeo.geocaching.R; import cgeo.geocaching.StoredList; import cgeo.geocaching.activity.AbstractListActivity; import cgeo.geocaching.utils.FileUtils; -import cgeo.geocaching.utils.IOUtils; import cgeo.geocaching.utils.Log; import org.apache.commons.collections.CollectionUtils; @@ -19,10 +18,7 @@ import android.os.Handler; import android.os.Message; import android.widget.ArrayAdapter; -import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -55,15 +51,11 @@ public abstract class AbstractFileListActivity<T extends ArrayAdapter<File>> ext } private String getDefaultFolders() { - StringBuilder sb = new StringBuilder(); - for (File f : getBaseFolders()) { - String fName = f.getPath(); - if (sb.length() > 0) { - sb.append('\n'); - } - sb.append(fName); + final ArrayList<String> names = new ArrayList<String>(); + for (File dir : getExistingBaseFolders()) { + names.add(dir.getPath()); } - return sb.toString(); + return StringUtils.join(names, '\n'); } }; @@ -160,19 +152,16 @@ public abstract class AbstractFileListActivity<T extends ArrayAdapter<File>> ext try { if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { boolean loaded = false; - for (final File dir : getBaseFolders()) - { - if (dir.exists() && dir.isDirectory()) { - FileUtils.listDir(list, dir,selector,changeWaitDialogHandler); - if (!list.isEmpty()) { - loaded = true; - break; - } + for (final File dir : getExistingBaseFolders()) { + FileUtils.listDir(list, dir, selector, changeWaitDialogHandler); + if (!list.isEmpty()) { + loaded = true; + break; } } if (!loaded) { changeWaitDialogHandler.sendMessage(Message.obtain(changeWaitDialogHandler, MSG_SEARCH_WHOLE_SD_CARD, Environment.getExternalStorageDirectory().getName())); - listDirs(list, getStorages(), selector, changeWaitDialogHandler); + listDirs(list, LocalStorage.getStorages(), selector, changeWaitDialogHandler); } } else { Log.w("No external media mounted."); @@ -202,49 +191,6 @@ public abstract class AbstractFileListActivity<T extends ArrayAdapter<File>> ext } } - /* - * Get all storages available on the device. - * Will include paths like /mnt/sdcard /mnt/usbdisk /mnt/ext_card /mnt/sdcard/ext_card - */ - protected static List<File> getStorages() { - - String extStorage = Environment.getExternalStorageDirectory().getAbsolutePath(); - List<File> storages = new ArrayList<File>(); - storages.add(new File(extStorage)); - File file = new File("/system/etc/vold.fstab"); - if (file.canRead()) { - FileReader fr = null; - BufferedReader br = null; - try { - fr = new FileReader(file); - br = new BufferedReader(fr); - String s = br.readLine(); - while (s != null) { - if (s.startsWith("dev_mount")) { - String[] tokens = StringUtils.split(s); - if (tokens.length >= 3) { - String path = tokens[2]; // mountpoint - if (!extStorage.equals(path)) { - File directory = new File(path); - if (directory.exists() && directory.isDirectory()) { - storages.add(directory); - } - } - } - } - s = br.readLine(); - } - } catch (IOException e) { - Log.e("Could not get additional mount points for user content. " + - "Proceeding with external storage only (" + extStorage + ")"); - } finally { - IOUtils.closeQuietly(fr); - IOUtils.closeQuietly(br); - } - } - return storages; - } - /** * Check if a filename belongs to the AbstractFileListActivity. This implementation checks for file extensions. * Subclasses may override this method to filter out specific files. @@ -261,6 +207,16 @@ public abstract class AbstractFileListActivity<T extends ArrayAdapter<File>> ext return false; } + protected List<File> getExistingBaseFolders() { + ArrayList<File> result = new ArrayList<File>(); + for (final File dir : getBaseFolders()) { + if (dir.exists() && dir.isDirectory()) { + result.add(dir); + } + } + return result; + } + protected AbstractFileListActivity(final String extension) { setExtensions(new String[] { extension }); } diff --git a/main/src/cgeo/geocaching/files/LocalStorage.java b/main/src/cgeo/geocaching/files/LocalStorage.java index ec09433..57f586e 100644 --- a/main/src/cgeo/geocaching/files/LocalStorage.java +++ b/main/src/cgeo/geocaching/files/LocalStorage.java @@ -2,6 +2,7 @@ package cgeo.geocaching.files; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.utils.CryptUtils; +import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.IOUtils; import cgeo.geocaching.utils.Log; @@ -14,6 +15,7 @@ import android.os.Environment; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; @@ -24,12 +26,14 @@ import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; /** * Handle local storage issues on phone and SD card. * */ -public class LocalStorage { +public final class LocalStorage { public static final String HEADER_LAST_MODIFIED = "last-modified"; public static final String HEADER_ETAG = "etag"; @@ -39,6 +43,10 @@ public class LocalStorage { private static File internalStorageBase; + private LocalStorage() { + // utility class + } + /** * Return the primary storage cache root (external media if mounted, phone otherwise). * @@ -159,7 +167,7 @@ public class LocalStorage { private static File buildFile(final File base, final String fileName, final boolean isUrl, final boolean createDirs) { if (createDirs) { - base.mkdirs(); + FileUtils.mkdirs(base); } return new File(base, isUrl ? CryptUtils.md5(fileName) + getExtension(fileName) : fileName); } @@ -194,7 +202,7 @@ public class LocalStorage { final Header header = response != null ? response.getFirstHeader(name) : null; final File file = filenameForHeader(baseFile, name); if (header == null) { - file.delete(); + FileUtils.deleteIgnoringFailure(file); } else { saveToFile(new ByteArrayInputStream(header.getValue().getBytes()), file); } @@ -256,15 +264,15 @@ public class LocalStorage { final boolean written = copy(inputStream, fos); fos.close(); if (!written) { - targetFile.delete(); + FileUtils.deleteIgnoringFailure(targetFile); } return written; } finally { - inputStream.close(); + IOUtils.closeQuietly(inputStream); } } catch (IOException e) { Log.e("LocalStorage.saveToFile", e); - targetFile.delete(); + FileUtils.deleteIgnoringFailure(targetFile); } return false; } @@ -279,7 +287,7 @@ public class LocalStorage { * @return true if the copy happened without error, false otherwise */ public static boolean copy(final File source, final File destination) { - destination.getParentFile().mkdirs(); + FileUtils.mkdirs(destination.getParentFile()); InputStream input = null; OutputStream output = null; @@ -288,9 +296,10 @@ public class LocalStorage { output = new BufferedOutputStream(new FileOutputStream(destination)); } catch (FileNotFoundException e) { Log.e("LocalStorage.copy: could not open file", e); + return false; + } finally { IOUtils.closeQuietly(input); IOUtils.closeQuietly(output); - return false; } boolean copyDone = copy(input, output); @@ -338,12 +347,12 @@ public class LocalStorage { if (file.isDirectory()) { deleteDirectory(file); } else { - file.delete(); + FileUtils.delete(file); } } } - return path.delete(); + return FileUtils.delete(path); } /** @@ -361,7 +370,7 @@ public class LocalStorage { } for (final File file : filesToDelete) { try { - if (!file.delete()) { + if (!FileUtils.delete(file)) { Log.w("LocalStorage.deleteFilesPrefix: Can't delete file " + file.getName()); } } catch (Exception e) { @@ -389,4 +398,47 @@ public class LocalStorage { }; return LocalStorage.getStorageDir(geocode).listFiles(filter); } + + /** + * Get all storages available on the device. + * Will include paths like /mnt/sdcard /mnt/usbdisk /mnt/ext_card /mnt/sdcard/ext_card + */ + public static List<File> getStorages() { + + String extStorage = Environment.getExternalStorageDirectory().getAbsolutePath(); + List<File> storages = new ArrayList<File>(); + storages.add(new File(extStorage)); + File file = new File("/system/etc/vold.fstab"); + if (file.canRead()) { + FileReader fr = null; + BufferedReader br = null; + try { + fr = new FileReader(file); + br = new BufferedReader(fr); + String s = br.readLine(); + while (s != null) { + if (s.startsWith("dev_mount")) { + String[] tokens = StringUtils.split(s); + if (tokens.length >= 3) { + String path = tokens[2]; // mountpoint + if (!extStorage.equals(path)) { + File directory = new File(path); + if (directory.exists() && directory.isDirectory()) { + storages.add(directory); + } + } + } + } + s = br.readLine(); + } + } catch (IOException e) { + Log.e("Could not get additional mount points for user content. " + + "Proceeding with external storage only (" + extStorage + ")"); + } finally { + IOUtils.closeQuietly(fr); + IOUtils.closeQuietly(br); + } + } + return storages; + } } diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index f892622..0817170 100644 --- a/main/src/cgeo/geocaching/maps/CGeoMap.java +++ b/main/src/cgeo/geocaching/maps/CGeoMap.java @@ -156,12 +156,12 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto private ScaleOverlay overlayScale = null; private PositionOverlay overlayPosition = null; // data for overlays - private static final int[][] INSET_RELIABLE = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; // center, 33x40 / 45x51 - private static final int[][] INSET_TYPE = { { 5, 8, 6, 10 }, { 4, 4, 5, 11 } }; // center, 22x22 / 36x36 - private static final int[][] INSET_OWN = { { 21, 0, 0, 26 }, { 25, 0, 0, 35 } }; // top right, 12x12 / 16x16 - private static final int[][] INSET_FOUND = { { 0, 0, 21, 28 }, { 0, 0, 25, 35 } }; // top left, 12x12 / 16x16 - private static final int[][] INSET_USERMODIFIEDCOORDS = { { 21, 28, 0, 0 }, { 19, 25, 0, 0 } }; // bottom right, 12x12 / 26x26 - private static final int[][] INSET_PERSONALNOTE = { { 0, 28, 21, 0 }, { 0, 25, 19, 0 } }; // bottom left, 12x12 / 26x26 + private static final int[][] INSET_RELIABLE = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; // center, 33x40 / 45x51 / 60x68 + private static final int[][] INSET_TYPE = { { 5, 8, 6, 10 }, { 4, 4, 5, 11 }, { 4, 4, 5, 11 } }; // center, 22x22 / 36x36 + private static final int[][] INSET_OWN = { { 21, 0, 0, 26 }, { 25, 0, 0, 35 }, { 40, 0, 0, 48 } }; // top right, 12x12 / 16x16 / 20x20 + private static final int[][] INSET_FOUND = { { 0, 0, 21, 28 }, { 0, 0, 25, 35 }, { 0, 0, 40, 48 } }; // top left, 12x12 / 16x16 / 20x20 + private static final int[][] INSET_USERMODIFIEDCOORDS = { { 21, 28, 0, 0 }, { 19, 25, 0, 0 }, { 25, 33, 0, 0 } }; // bottom right, 12x12 / 26x26 / 35x35 + private static final int[][] INSET_PERSONALNOTE = { { 0, 28, 21, 0 }, { 0, 25, 19, 0 }, { 0, 33, 25, 0 } }; // bottom left, 12x12 / 26x26 / 35x35 private SparseArray<LayerDrawable> overlaysCache = new SparseArray<LayerDrawable>(); /** Count of caches currently visible */ @@ -1179,33 +1179,29 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } searchResult = ConnectorFactory.searchByViewport(viewport.resize(0.8), tokens); - if (searchResult != null) { - downloaded = true; - if (searchResult.getError() == StatusCode.NOT_LOGGED_IN && Settings.isGCConnectorActive()) { - Login.login(); - tokens = null; - } else { - break; - } + downloaded = true; + if (searchResult.getError() == StatusCode.NOT_LOGGED_IN && Settings.isGCConnectorActive()) { + Login.login(); + tokens = null; + } else { + break; } count++; } while (count < 2); - if (searchResult != null) { - Set<Geocache> result = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB); - CGeoMap.filter(result); - // update the caches - // first remove filtered out - final Set<String> filteredCodes = searchResult.getFilteredGeocodes(); - Log.d("Filtering out " + filteredCodes.size() + " caches: " + filteredCodes.toString()); - caches.removeAll(cgData.loadCaches(filteredCodes, LoadFlags.LOAD_CACHE_ONLY)); - cgData.removeCaches(filteredCodes, EnumSet.of(RemoveFlag.REMOVE_CACHE)); - // new collection type needs to remove first to refresh - caches.removeAll(result); - caches.addAll(result); - lastSearchResult = searchResult; - } + Set<Geocache> result = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB); + CGeoMap.filter(result); + // update the caches + // first remove filtered out + final Set<String> filteredCodes = searchResult.getFilteredGeocodes(); + Log.d("Filtering out " + filteredCodes.size() + " caches: " + filteredCodes.toString()); + caches.removeAll(cgData.loadCaches(filteredCodes, LoadFlags.LOAD_CACHE_ONLY)); + cgData.removeCaches(filteredCodes, EnumSet.of(RemoveFlag.REMOVE_CACHE)); + // new collection type needs to remove first to refresh + caches.removeAll(result); + caches.addAll(result); + lastSearchResult = searchResult; //render displayExecutor.execute(new DisplayRunnable(viewport)); @@ -1677,7 +1673,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto // background: disabled or not final Drawable marker = getResources().getDrawable(cache.getMapMarkerId()); layers.add(marker); - final int resolution = marker.getIntrinsicWidth() > 40 ? 1 : 0; + final int resolution = marker.getIntrinsicWidth() > 40 ? (marker.getIntrinsicWidth() > 50 ? 2 : 1) : 0; // reliable or not if (!cache.isReliableLatLon()) { insets.add(INSET_RELIABLE[resolution]); diff --git a/main/src/cgeo/geocaching/maps/PositionHistory.java b/main/src/cgeo/geocaching/maps/PositionHistory.java new file mode 100644 index 0000000..9b090fc --- /dev/null +++ b/main/src/cgeo/geocaching/maps/PositionHistory.java @@ -0,0 +1,63 @@ +package cgeo.geocaching.maps; + +import android.location.Location; + +import java.util.ArrayList; + +/** + * Map trail history + */ +public class PositionHistory { + + /** + * minimum distance between two recorded points of the trail + */ + private static final double MINIMUM_DISTANCE_METERS = 10.0; + + /** + * maximum number of positions to remember + */ + private static final int MAX_POSITIONS = 700; + + private ArrayList<Location> history = new ArrayList<Location>(); + + /** + * Adds the current position to the trail history to be able to show the trail on the map. + */ + void rememberTrailPosition(Location coordinates) { + if (coordinates.getAccuracy() >= 50f) { + return; + } + if (coordinates.getLatitude() == 0.0 && coordinates.getLatitude() == 0.0) { + return; + } + if (history.isEmpty()) { + history.add(coordinates); + return; + } + + Location historyRecent = history.get(history.size() - 1); + if (historyRecent.distanceTo(coordinates) <= MINIMUM_DISTANCE_METERS) { + return; + } + + history.add(coordinates); + + // avoid running out of memory + final int itemsToRemove = getHistory().size() - MAX_POSITIONS; + if (itemsToRemove > 0) { + for (int i = 0; i < itemsToRemove; i++) { + getHistory().remove(0); + } + } + } + + public ArrayList<Location> getHistory() { + return history; + } + + public void setHistory(ArrayList<Location> history) { + this.history = history; + } + +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/maps/PositionOverlay.java b/main/src/cgeo/geocaching/maps/PositionOverlay.java index c3a0834..39f4987 100644 --- a/main/src/cgeo/geocaching/maps/PositionOverlay.java +++ b/main/src/cgeo/geocaching/maps/PositionOverlay.java @@ -1,7 +1,6 @@ package cgeo.geocaching.maps; import cgeo.geocaching.R; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.GeoPointImpl; @@ -9,6 +8,7 @@ import cgeo.geocaching.maps.interfaces.MapItemFactory; import cgeo.geocaching.maps.interfaces.MapProjectionImpl; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OverlayImpl; +import cgeo.geocaching.settings.Settings; import android.app.Activity; import android.graphics.Bitmap; @@ -37,10 +37,7 @@ public class PositionOverlay implements GeneralOverlay { private int heightArrowHalf = 0; private PaintFlagsDrawFilter setfil = null; private PaintFlagsDrawFilter remfil = null; - private Location historyRecent = null; - private ArrayList<Location> history = new ArrayList<Location>(); - private Point historyPointN = new Point(); - private Point historyPointP = new Point(); + private PositionHistory positionHistory = new PositionHistory(); private Activity activity; private MapItemFactory mapItemFactory = null; private OverlayImpl ovlImpl = null; @@ -139,67 +136,40 @@ public class PositionOverlay implements GeneralOverlay { accuracyCircle.setStyle(Style.FILL); canvas.drawCircle(center.x, center.y, radius, accuracyCircle); - if (coordinates.getAccuracy() < 50f && ((historyRecent != null && historyRecent.distanceTo(coordinates) > 5.0) || historyRecent == null)) { - if (historyRecent != null) { - history.add(historyRecent); - } - historyRecent = coordinates; - - int toRemove = history.size() - 700; - - if (toRemove > 0) { - for (int cnt = 0; cnt < toRemove; cnt++) { - history.remove(cnt); - } - } - } + positionHistory.rememberTrailPosition(coordinates); if (Settings.isMapTrail()) { - int size = history.size(); + int size = positionHistory.getHistory().size(); if (size > 1) { int alphaCnt = size - 201; if (alphaCnt < 1) { alphaCnt = 1; } + Point pointNow = new Point(); + Point pointPrevious = new Point(); + Location prev = positionHistory.getHistory().get(0); + projection.toPixels(mapItemFactory.getGeoPointBase(new Geopoint(prev)), pointPrevious); + for (int cnt = 1; cnt < size; cnt++) { - Location prev = history.get(cnt - 1); - Location now = history.get(cnt); - - if (prev != null && now != null) { - projection.toPixels(mapItemFactory.getGeoPointBase(new Geopoint(prev)), historyPointP); - projection.toPixels(mapItemFactory.getGeoPointBase(new Geopoint(now)), historyPointN); - - int alpha; - if ((alphaCnt - cnt) > 0) { - alpha = 255 / (alphaCnt - cnt); - } - else { - alpha = 255; - } - - historyLineShadow.setAlpha(alpha); - historyLine.setAlpha(alpha); - - canvas.drawLine(historyPointP.x, historyPointP.y, historyPointN.x, historyPointN.y, historyLineShadow); - canvas.drawLine(historyPointP.x, historyPointP.y, historyPointN.x, historyPointN.y, historyLine); - } - } - } + Location now = positionHistory.getHistory().get(cnt); + projection.toPixels(mapItemFactory.getGeoPointBase(new Geopoint(now)), pointNow); - if (size > 0) { - Location prev = history.get(size - 1); - Location now = coordinates; + int alpha; + if ((alphaCnt - cnt) > 0) { + alpha = 255 / (alphaCnt - cnt); + } + else { + alpha = 255; + } - if (prev != null && now != null) { - projection.toPixels(mapItemFactory.getGeoPointBase(new Geopoint(prev)), historyPointP); - projection.toPixels(mapItemFactory.getGeoPointBase(new Geopoint(now)), historyPointN); + historyLineShadow.setAlpha(alpha); + historyLine.setAlpha(alpha); - historyLineShadow.setAlpha(255); - historyLine.setAlpha(255); + canvas.drawLine(pointPrevious.x, pointPrevious.y, pointNow.x, pointNow.y, historyLineShadow); + canvas.drawLine(pointPrevious.x, pointPrevious.y, pointNow.x, pointNow.y, historyLine); - canvas.drawLine(historyPointP.x, historyPointP.y, historyPointN.x, historyPointN.y, historyLineShadow); - canvas.drawLine(historyPointP.x, historyPointP.y, historyPointN.x, historyPointN.y, historyLine); + pointPrevious.set(pointNow.x, pointNow.y); } } } @@ -230,10 +200,10 @@ public class PositionOverlay implements GeneralOverlay { } public ArrayList<Location> getHistory() { - return history; + return positionHistory.getHistory(); } - public void setHistory(ArrayList<Location> inHistory) { - history = inHistory; + public void setHistory(ArrayList<Location> history) { + positionHistory.setHistory(history); } } diff --git a/main/src/cgeo/geocaching/maps/google/GoogleMapView.java b/main/src/cgeo/geocaching/maps/google/GoogleMapView.java index 6e5406e..3cf258e 100644 --- a/main/src/cgeo/geocaching/maps/google/GoogleMapView.java +++ b/main/src/cgeo/geocaching/maps/google/GoogleMapView.java @@ -14,7 +14,7 @@ import cgeo.geocaching.maps.interfaces.MapProjectionImpl; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OnMapDragListener; import cgeo.geocaching.maps.interfaces.OverlayImpl; -import cgeo.geocaching.maps.interfaces.OverlayImpl.overlayType; +import cgeo.geocaching.maps.interfaces.OverlayImpl.OverlayType; import cgeo.geocaching.utils.Log; import com.google.android.maps.GeoPoint; @@ -121,7 +121,7 @@ public class GoogleMapView extends MapView implements MapViewImpl { @Override public PositionOverlay createAddPositionOverlay(Activity activity) { - GoogleOverlay ovl = new GoogleOverlay(activity, overlayType.PositionOverlay); + GoogleOverlay ovl = new GoogleOverlay(activity, OverlayType.PositionOverlay); getOverlays().add(ovl); return (PositionOverlay) ovl.getBase(); } @@ -129,7 +129,7 @@ public class GoogleMapView extends MapView implements MapViewImpl { @Override public ScaleOverlay createAddScaleOverlay(Activity activity) { - GoogleOverlay ovl = new GoogleOverlay(activity, overlayType.ScaleOverlay); + GoogleOverlay ovl = new GoogleOverlay(activity, OverlayType.ScaleOverlay); getOverlays().add(ovl); return (ScaleOverlay) ovl.getBase(); } diff --git a/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java b/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java index 773f9ff..bf4f606 100644 --- a/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java +++ b/main/src/cgeo/geocaching/maps/google/GoogleOverlay.java @@ -20,7 +20,7 @@ public class GoogleOverlay extends Overlay implements OverlayImpl { private GeneralOverlay overlayBase = null; private Lock lock = new ReentrantLock(); - public GoogleOverlay(Activity activityIn, overlayType ovlType) { + public GoogleOverlay(Activity activityIn, OverlayType ovlType) { switch (ovlType) { case PositionOverlay: overlayBase = new PositionOverlay(activityIn, this); diff --git a/main/src/cgeo/geocaching/maps/interfaces/OverlayImpl.java b/main/src/cgeo/geocaching/maps/interfaces/OverlayImpl.java index 115b692..a17b5fb 100644 --- a/main/src/cgeo/geocaching/maps/interfaces/OverlayImpl.java +++ b/main/src/cgeo/geocaching/maps/interfaces/OverlayImpl.java @@ -6,7 +6,7 @@ package cgeo.geocaching.maps.interfaces; */ public interface OverlayImpl { - public enum overlayType { + public enum OverlayType { PositionOverlay, ScaleOverlay } diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java index 2b0c1f7..aa11405 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java @@ -14,7 +14,7 @@ import cgeo.geocaching.maps.interfaces.MapSource; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OnMapDragListener; import cgeo.geocaching.maps.interfaces.OverlayImpl; -import cgeo.geocaching.maps.interfaces.OverlayImpl.overlayType; +import cgeo.geocaching.maps.interfaces.OverlayImpl.OverlayType; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; @@ -109,14 +109,14 @@ public class MapsforgeMapView extends MapView implements MapViewImpl { @Override public PositionOverlay createAddPositionOverlay(Activity activity) { - MapsforgeOverlay ovl = new MapsforgeOverlay(activity, overlayType.PositionOverlay); + MapsforgeOverlay ovl = new MapsforgeOverlay(activity, OverlayType.PositionOverlay); getOverlays().add(ovl); return (PositionOverlay) ovl.getBase(); } @Override public ScaleOverlay createAddScaleOverlay(Activity activity) { - MapsforgeOverlay ovl = new MapsforgeOverlay(activity, overlayType.ScaleOverlay); + MapsforgeOverlay ovl = new MapsforgeOverlay(activity, OverlayType.ScaleOverlay); getOverlays().add(ovl); return (ScaleOverlay) ovl.getBase(); } diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java index dd7fb75..a94b988 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java @@ -21,7 +21,7 @@ public class MapsforgeOverlay extends Overlay implements OverlayImpl { private GeneralOverlay overlayBase = null; private Lock lock = new ReentrantLock(); - public MapsforgeOverlay(Activity activityIn, OverlayImpl.overlayType ovlType) { + public MapsforgeOverlay(Activity activityIn, OverlayImpl.OverlayType ovlType) { switch (ovlType) { case PositionOverlay: diff --git a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java index 8e3a4d8..a074e70 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java @@ -13,7 +13,7 @@ import cgeo.geocaching.maps.interfaces.MapProjectionImpl; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OnMapDragListener; import cgeo.geocaching.maps.interfaces.OverlayImpl; -import cgeo.geocaching.maps.interfaces.OverlayImpl.overlayType; +import cgeo.geocaching.maps.interfaces.OverlayImpl.OverlayType; import cgeo.geocaching.utils.Log; import org.mapsforge.android.mapsold.GeoPoint; @@ -103,14 +103,14 @@ public class MapsforgeMapView024 extends MapView implements MapViewImpl { @Override public PositionOverlay createAddPositionOverlay(Activity activity) { - MapsforgeOverlay ovl = new MapsforgeOverlay(activity, overlayType.PositionOverlay); + MapsforgeOverlay ovl = new MapsforgeOverlay(activity, OverlayType.PositionOverlay); getOverlays().add(ovl); return (PositionOverlay) ovl.getBase(); } @Override public ScaleOverlay createAddScaleOverlay(Activity activity) { - MapsforgeOverlay ovl = new MapsforgeOverlay(activity, overlayType.ScaleOverlay); + MapsforgeOverlay ovl = new MapsforgeOverlay(activity, OverlayType.ScaleOverlay); getOverlays().add(ovl); return (ScaleOverlay) ovl.getBase(); } diff --git a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java index d40b539..bdaac98 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java @@ -21,7 +21,7 @@ public class MapsforgeOverlay extends Overlay implements OverlayImpl { private GeneralOverlay overlayBase = null; private Lock lock = new ReentrantLock(); - public MapsforgeOverlay(Activity activityIn, OverlayImpl.overlayType ovlType) { + public MapsforgeOverlay(Activity activityIn, OverlayImpl.OverlayType ovlType) { switch (ovlType) { case PositionOverlay: diff --git a/main/src/cgeo/geocaching/network/HtmlImage.java b/main/src/cgeo/geocaching/network/HtmlImage.java index 0649e12..797e67d 100644 --- a/main/src/cgeo/geocaching/network/HtmlImage.java +++ b/main/src/cgeo/geocaching/network/HtmlImage.java @@ -6,6 +6,7 @@ import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.files.LocalStorage; +import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.IOUtils; import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.Log; @@ -146,9 +147,13 @@ public class HtmlImage implements Html.ImageGetter { */ private static void makeFreshCopy(final File file) { final File tempFile = new File(file.getParentFile(), file.getName() + "-temp"); - file.renameTo(tempFile); - LocalStorage.copy(tempFile, file); - tempFile.delete(); + if (file.renameTo(tempFile)) { + LocalStorage.copy(tempFile, file); + FileUtils.deleteIgnoringFailure(tempFile); + } + else { + Log.e("Could not reset timestamp of file " + file.getAbsolutePath()); + } } private Bitmap getTransparent1x1Image() { diff --git a/main/src/cgeo/geocaching/settings/EditPasswordPreference.java b/main/src/cgeo/geocaching/settings/EditPasswordPreference.java index d89f128..20d0250 100644 --- a/main/src/cgeo/geocaching/settings/EditPasswordPreference.java +++ b/main/src/cgeo/geocaching/settings/EditPasswordPreference.java @@ -10,7 +10,6 @@ import android.util.AttributeSet; * Use it exactly as an EditTextPreference * * @see SettingsActivity - search for EditPasswordPreference - * @author koem */ public class EditPasswordPreference extends EditTextPreference { diff --git a/main/src/cgeo/geocaching/settings/Settings.java b/main/src/cgeo/geocaching/settings/Settings.java index a4b2563..0e6158f 100644 --- a/main/src/cgeo/geocaching/settings/Settings.java +++ b/main/src/cgeo/geocaching/settings/Settings.java @@ -43,7 +43,7 @@ import java.util.Locale; */ public final class Settings { - public static final int SHOW_WP_THRESHOLD_DEFAULT = 5; + public static final int SHOW_WP_THRESHOLD_DEFAULT = 10; public static final int SHOW_WP_THRESHOLD_MAX = 50; private static final int MAP_SOURCE_DEFAULT = GoogleMapProvider.GOOGLE_MAP_ID.hashCode(); @@ -53,14 +53,14 @@ public final class Settings { private final static String keyConsumerPublic = CryptUtils.rot13("ESnsCvAv3kEupF1GCR3jGj"); private final static String keyConsumerSecret = CryptUtils.rot13("7vQWceACV9umEjJucmlpFe9FCMZSeqIqfkQ2BnhV9x"); - public enum coordInputFormatEnum { + public enum CoordInputFormatEnum { Plain, Deg, Min, Sec; - public static coordInputFormatEnum fromInt(int id) { - final coordInputFormatEnum[] values = coordInputFormatEnum.values(); + public static CoordInputFormatEnum fromInt(int id) { + final CoordInputFormatEnum[] values = CoordInputFormatEnum.values(); if (id < 0 || id >= values.length) { return Min; } @@ -75,8 +75,11 @@ public final class Settings { Log.setDebug(sharedPrefs.getBoolean(getKey(R.string.pref_debug), false)); } - // maps - private static MapProvider mapProvider = null; + /** + * Cache the mapsource locally. If that is an offline map source, each request would potentially access the + * underlying map file, leading to delays. + */ + private static MapSource mapSource; private Settings() { // this class is not to be instantiated; @@ -132,10 +135,10 @@ public final class Settings { e.putString(getKey(R.string.pref_memberstatus), old.getString(getKey(R.string.pref_memberstatus), "")); e.putInt(getKey(R.string.pref_coordinputformat), old.getInt(getKey(R.string.pref_coordinputformat), 0)); e.putBoolean(getKey(R.string.pref_log_offline), old.getBoolean(getKey(R.string.pref_log_offline), false)); - e.putBoolean(getKey(R.string.pref_choose_list), old.getBoolean(getKey(R.string.pref_choose_list), false)); + e.putBoolean(getKey(R.string.pref_choose_list), old.getBoolean(getKey(R.string.pref_choose_list), true)); e.putBoolean(getKey(R.string.pref_loaddirectionimg), old.getBoolean(getKey(R.string.pref_loaddirectionimg), true)); e.putString(getKey(R.string.pref_gccustomdate), old.getString(getKey(R.string.pref_gccustomdate), null)); - e.putInt(getKey(R.string.pref_gcshowwaypointsthreshold), old.getInt(getKey(R.string.pref_gcshowwaypointsthreshold), 0)); + e.putInt(getKey(R.string.pref_showwaypointsthreshold), old.getInt(getKey(R.string.pref_showwaypointsthreshold), SHOW_WP_THRESHOLD_DEFAULT)); e.putString(getKey(R.string.pref_cookiestore), old.getString(getKey(R.string.pref_cookiestore), null)); e.putBoolean(getKey(R.string.pref_opendetailslastpage), old.getBoolean(getKey(R.string.pref_opendetailslastpage), false)); e.putInt(getKey(R.string.pref_lastdetailspage), old.getInt(getKey(R.string.pref_lastdetailspage), 1)); @@ -163,7 +166,7 @@ public final class Settings { } else if (wpThreshold > SHOW_WP_THRESHOLD_MAX) { wpThreshold = SHOW_WP_THRESHOLD_MAX; } - e.putInt(getKey(R.string.pref_gcshowwaypointsthreshold), wpThreshold); + e.putInt(getKey(R.string.pref_showwaypointsthreshold), wpThreshold); // KEY_MAP_SOURCE must be string, because it is the key for a ListPreference now int ms = sharedPrefs.getInt(getKey(R.string.pref_mapsource), MAP_SOURCE_DEFAULT); @@ -397,10 +400,7 @@ public final class Settings { } public static MapProvider getMapProvider() { - if (mapProvider == null) { - mapProvider = getMapSource().getMapProvider(); - } - return mapProvider; + return getMapSource().getMapProvider(); } public static String getMapFile() { @@ -441,11 +441,11 @@ public final class Settings { return MapsforgeMapProvider.isValidMapFile(mapFileIn); } - public static coordInputFormatEnum getCoordInputFormat() { - return coordInputFormatEnum.fromInt(getInt(R.string.pref_coordinputformat, 0)); + public static CoordInputFormatEnum getCoordInputFormat() { + return CoordInputFormatEnum.fromInt(getInt(R.string.pref_coordinputformat, 0)); } - public static void setCoordInputFormat(final coordInputFormatEnum format) { + public static void setCoordInputFormat(final CoordInputFormatEnum format) { putInt(R.string.pref_coordinputformat, format.ordinal()); } @@ -543,7 +543,21 @@ public final class Settings { } public static boolean isUseImperialUnits() { - return getBoolean(R.string.pref_units, false); + return getBoolean(R.string.pref_units, getImperialUnitsDefault()); + } + + static boolean getImperialUnitsDefault() { + final String countryCode = Locale.getDefault().getCountry(); + if ("US".equals(countryCode)) { + return true; // USA + } + if ("LR".equals(countryCode)) { + return true; // Liberia + } + if ("MM".equals(countryCode)) { + return true; // Burma + } + return false; } public static boolean isLiveMap() { @@ -582,12 +596,15 @@ public final class Settings { } public static MapSource getMapSource() { + if (mapSource != null) { + return mapSource; + } final int id = getConvertedMapId(); - final MapSource map = MapProviderFactory.getMapSource(id); - if (map != null) { + mapSource = MapProviderFactory.getMapSource(id); + if (mapSource != null) { // don't use offline maps if the map file is not valid - if ((!(map instanceof OfflineMapSource)) || (isValidMapFile())) { - return map; + if ((!(mapSource instanceof OfflineMapSource)) || (isValidMapFile())) { + return mapSource; } } // fallback to first available map @@ -636,13 +653,12 @@ public final class Settings { } public static void setMapSource(final MapSource newMapSource) { - if (!MapProviderFactory.isSameActivity(getMapSource(), newMapSource)) { - mapProvider = null; - } putString(R.string.pref_mapsource, String.valueOf(newMapSource.getNumericalId())); if (newMapSource instanceof OfflineMapSource) { setMapFile(((OfflineMapSource) newMapSource).getFileName()); } + // cache the value + mapSource = newMapSource; } public static void setAnyCoordinates(final Geopoint coords) { @@ -704,11 +720,11 @@ public final class Settings { * The Threshold for the showing of child waypoints */ public static int getWayPointsThreshold() { - return getInt(R.string.pref_gcshowwaypointsthreshold, SHOW_WP_THRESHOLD_DEFAULT); + return getInt(R.string.pref_showwaypointsthreshold, SHOW_WP_THRESHOLD_DEFAULT); } public static void setShowWaypointsThreshold(final int threshold) { - putInt(R.string.pref_gcshowwaypointsthreshold, threshold); + putInt(R.string.pref_showwaypointsthreshold, threshold); } public static boolean isUseTwitter() { @@ -974,4 +990,9 @@ public final class Settings { public static void setFieldnoteExportDate(final long date) { putLong(R.string.pref_fieldnoteExportDate, date); } + + public static boolean isUseNavigationApp(NavigationAppsEnum navApp) { + return getBoolean(navApp.preferenceKey, true); + } + } diff --git a/main/src/cgeo/geocaching/settings/SettingsActivity.java b/main/src/cgeo/geocaching/settings/SettingsActivity.java index daef9af..42f6074 100644 --- a/main/src/cgeo/geocaching/settings/SettingsActivity.java +++ b/main/src/cgeo/geocaching/settings/SettingsActivity.java @@ -3,7 +3,6 @@ package cgeo.geocaching.settings; import cgeo.geocaching.Intents; import cgeo.geocaching.R; import cgeo.geocaching.SelectMapfileActivity; -import cgeo.geocaching.cgData; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; @@ -13,7 +12,7 @@ import cgeo.geocaching.connector.gc.Login; import cgeo.geocaching.files.SimpleDirChooser; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.maps.interfaces.MapSource; -import cgeo.geocaching.ui.Formatter; +import cgeo.geocaching.utils.DatabaseBackupUtils; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.LogTemplateProvider; import cgeo.geocaching.utils.LogTemplateProvider.LogTemplate; @@ -21,7 +20,6 @@ import cgeo.geocaching.utils.LogTemplateProvider.LogTemplate; import org.apache.commons.lang3.StringUtils; import org.openintents.intents.FileManagerIntents; -import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -58,7 +56,6 @@ import java.util.Locale; * guidelines and the <a href="http://developer.android.com/guide/topics/ui/settings.html">Settings API Guide</a> for * more information on developing a Settings UI. * - * @author koem (initial author) */ public class SettingsActivity extends PreferenceActivity { @@ -123,6 +120,7 @@ public class SettingsActivity extends PreferenceActivity { initBasicMemberPreferences(); initSend2CgeoPreferences(); initServicePreferences(); + initNavigationMenuPreferences(); for (int k : new int[] { R.string.pref_username, R.string.pref_password, R.string.pref_pass_vote, R.string.pref_signature, @@ -133,6 +131,18 @@ public class SettingsActivity extends PreferenceActivity { R.string.pref_fakekey_preference_backup_info, }) { bindSummaryToStringValue(k); } + getPreference(R.string.pref_units).setDefaultValue(Settings.getImperialUnitsDefault()); + } + + private void initNavigationMenuPreferences() { + for (NavigationAppsEnum appEnum : NavigationAppsEnum.values()) { + if (appEnum.app.isInstalled()) { + getPreference(appEnum.preferenceKey).setEnabled(true); + } + } + getPreference(R.string.pref_fakekey_basicmembers_screen) + .setEnabled(!Settings.isPremiumMember()); + redrawScreen(R.string.pref_fakekey_navigation_menu_screen); } private void initServicePreferences() { @@ -298,39 +308,13 @@ public class SettingsActivity extends PreferenceActivity { backup.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(final Preference preference) { - final Context context = preference.getContext(); - // avoid overwriting an existing backup with an empty database - // (can happen directly after reinstalling the app) - if (cgData.getAllCachesCount() == 0) { - ActivityMixin.helpDialog(SettingsActivity.this, - context.getString(R.string.init_backup), - context.getString(R.string.init_backup_unnecessary)); - return false; - } + return DatabaseBackupUtils.createBackup(SettingsActivity.this, new Runnable() { - final ProgressDialog dialog = ProgressDialog.show(context, - context.getString(R.string.init_backup), - context.getString(R.string.init_backup_running), true, false); - new Thread() { @Override public void run() { - final String backupFileName = cgData.backupDatabase(); - runOnUiThread(new Runnable() { - @Override - public void run() { - dialog.dismiss(); - ActivityMixin.helpDialog(SettingsActivity.this, - context.getString(R.string.init_backup_backup), - backupFileName != null - ? context.getString(R.string.init_backup_success) - + "\n" + backupFileName - : context.getString(R.string.init_backup_failed)); - VALUE_CHANGE_LISTENER.onPreferenceChange(SettingsActivity.this.getPreference(R.string.pref_fakekey_preference_backup_info), ""); - } - }); + VALUE_CHANGE_LISTENER.onPreferenceChange(SettingsActivity.this.getPreference(R.string.pref_fakekey_preference_backup_info), ""); } - }.start(); - return true; + }); } }); @@ -338,8 +322,7 @@ public class SettingsActivity extends PreferenceActivity { restore.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(final Preference preference) { - ((cgeoapplication) SettingsActivity.this.getApplication()) - .restoreDatabase(SettingsActivity.this); + DatabaseBackupUtils.restoreDatabase(SettingsActivity.this); return true; } }); @@ -492,10 +475,19 @@ public class SettingsActivity extends PreferenceActivity { if (preference instanceof EditPasswordPreference) { if (StringUtils.isBlank((String) value)) { - preference.setSummary(""); + preference.setSummary(StringUtils.EMPTY); } else { - preference.setSummary("\u2022 \u2022 \u2022 \u2022 \u2022 \u2022 \u2022 \u2022 \u2022 \u2022"); + preference.setSummary(StringUtils.repeat("\u2022 ", 10)); } + } else if (isPreference(preference, R.string.pref_mapsource)) { + // reset the cached map source + int mapSourceId = Integer.valueOf(stringValue); + final MapSource mapSource = MapProviderFactory.getMapSource(mapSourceId); + Settings.setMapSource(mapSource); + preference.setSummary(mapSource.getName()); + } else if (isPreference(preference, R.string.pref_connectorOCActive) || isPreference(preference, R.string.pref_connectorOCPLActive) || isPreference(preference, R.string.pref_connectorGCActive)) { + // // reset log-in status if connector activation was changed + cgeoapplication.getInstance().checkLogin = true; } else if (preference instanceof ListPreference) { // For list preferences, look up the correct display value in // the preference's 'entries' list. @@ -507,13 +499,11 @@ public class SettingsActivity extends PreferenceActivity { index >= 0 ? listPreference.getEntries()[index] : null); - } else if (getKey(R.string.pref_fakekey_preference_backup_info).equals(preference.getKey())) { - File lastBackupFile = cgData.getRestoreFile(); - String text; - if (lastBackupFile != null) { + } else if (isPreference(preference, R.string.pref_fakekey_preference_backup_info)) { + final String text; + if (DatabaseBackupUtils.hasBackup()) { text = preference.getContext().getString(R.string.init_backup_last) + " " - + Formatter.formatTime(lastBackupFile.lastModified()) - + ", " + Formatter.formatDate(lastBackupFile.lastModified()); + + DatabaseBackupUtils.getBackupDateTime(); } else { text = preference.getContext().getString(R.string.init_backup_last_no); } @@ -523,17 +513,13 @@ public class SettingsActivity extends PreferenceActivity { // simple string representation. preference.setSummary(stringValue); } - // reset log-in if gc user or password is changed - if (isPreference(preference, R.string.pref_username) || isPreference(preference, R.string.pref_password)) { + if ((isPreference(preference, R.string.pref_username) && !stringValue.equals(Settings.getUsername())) || (isPreference(preference, R.string.pref_password) && !stringValue.equals(Settings.getGcLogin().getRight()))) { + // reset log-in if gc user or password is changed if (Login.isActualLoginStatus()) { Login.logout(); } cgeoapplication.getInstance().checkLogin = true; } - // reset log-in status if connector activation was changed - if (isPreference(preference, R.string.pref_connectorOCActive) || isPreference(preference, R.string.pref_connectorOCPLActive) || isPreference(preference, R.string.pref_connectorGCActive)) { - cgeoapplication.getInstance().checkLogin = true; - } return true; } }; diff --git a/main/src/cgeo/geocaching/speech/SpeechService.java b/main/src/cgeo/geocaching/speech/SpeechService.java index 634f1c4..ae289ed 100644 --- a/main/src/cgeo/geocaching/speech/SpeechService.java +++ b/main/src/cgeo/geocaching/speech/SpeechService.java @@ -1,8 +1,9 @@ package cgeo.geocaching.speech; import cgeo.geocaching.DirectionProvider; -import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; @@ -44,16 +45,18 @@ public class SpeechService extends Service implements OnInitListener { GeoDirHandler geoHandler = new GeoDirHandler() { @Override protected void updateDirection(float newDirection) { - direction = DirectionProvider.getDirectionNow(startingActivity, newDirection); - directionInitialized = true; - updateCompass(); + if (cgeoapplication.getInstance().currentGeo().getSpeed() <= 5) { + direction = DirectionProvider.getDirectionNow(startingActivity, newDirection); + directionInitialized = true; + updateCompass(); + } } @Override protected void updateGeoData(cgeo.geocaching.IGeoData newGeo) { position = newGeo.getCoords(); positionInitialized = true; - if (newGeo.getSpeed() > 5) { + if (!Settings.isUseCompass() || newGeo.getSpeed() > 5) { direction = newGeo.getBearing(); directionInitialized = true; } @@ -151,7 +154,11 @@ public class SpeechService extends Service implements OnInitListener { initialized = true; - geoHandler.startGeoAndDir(); + if (Settings.isUseCompass()) { + geoHandler.startGeoAndDir(); + } else { + geoHandler.startGeo(); + } } @Override diff --git a/main/src/cgeo/geocaching/ui/CacheListAdapter.java b/main/src/cgeo/geocaching/ui/CacheListAdapter.java index 3179857..c6aeaa6 100644 --- a/main/src/cgeo/geocaching/ui/CacheListAdapter.java +++ b/main/src/cgeo/geocaching/ui/CacheListAdapter.java @@ -6,12 +6,12 @@ import cgeo.geocaching.CacheDetailActivity; import cgeo.geocaching.Geocache; import cgeo.geocaching.IGeoData; import cgeo.geocaching.R; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheListType; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.filter.IFilter; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.sorting.CacheComparator; import cgeo.geocaching.sorting.DistanceComparator; import cgeo.geocaching.sorting.EventDateComparator; @@ -645,6 +645,10 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> { } public void setInitialComparator() { + // will be called repeatedly when coming back to the list, therefore check first for an already existing sorting + if (cacheComparator != null) { + return; + } CacheComparator comparator = null; // a null comparator will automatically sort by distance if (cacheListType == CacheListType.HISTORY) { comparator = new VisitComparator(); diff --git a/main/src/cgeo/geocaching/ui/EditNoteDialog.java b/main/src/cgeo/geocaching/ui/EditNoteDialog.java index 50cf57a..2af1cb8 100644 --- a/main/src/cgeo/geocaching/ui/EditNoteDialog.java +++ b/main/src/cgeo/geocaching/ui/EditNoteDialog.java @@ -7,7 +7,7 @@ import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; +import android.view.ContextThemeWrapper; import android.view.View; import android.widget.EditText; @@ -35,13 +35,12 @@ public class EditNoteDialog extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - LayoutInflater inflater = getActivity().getLayoutInflater(); - View view = inflater.inflate(R.layout.fragment_edit_note, null); + View view = View.inflate(new ContextThemeWrapper(getActivity(), R.style.dark), R.layout.fragment_edit_note, null); mEditText = (EditText) view.findViewById(R.id.note); String initialNote = getArguments().getString(ARGUMENT_INITIAL_NOTE); if (initialNote != null) { mEditText.setText(initialNote); - initialNote = null; + getArguments().remove(ARGUMENT_INITIAL_NOTE); } AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); diff --git a/main/src/cgeo/geocaching/ui/Formatter.java b/main/src/cgeo/geocaching/ui/Formatter.java index 92a0defc..ecae9ea 100644 --- a/main/src/cgeo/geocaching/ui/Formatter.java +++ b/main/src/cgeo/geocaching/ui/Formatter.java @@ -99,13 +99,11 @@ public abstract class Formatter { * Generate a numeric date and time string according to system-wide settings (locale, * date format) such as "7 sept. at 12:35". * - * @param context - * a Context * @param date * milliseconds since the epoch * @return the formatted string */ - public static String formatShortDateTime(Context context, long date) { + public static String formatShortDateTime(long date) { return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL); } diff --git a/main/src/cgeo/geocaching/ui/GPXListAdapter.java b/main/src/cgeo/geocaching/ui/GPXListAdapter.java index 7f3c33f..988bb81 100644 --- a/main/src/cgeo/geocaching/ui/GPXListAdapter.java +++ b/main/src/cgeo/geocaching/ui/GPXListAdapter.java @@ -5,6 +5,7 @@ import butterknife.InjectView; import cgeo.geocaching.GpxFileListActivity; import cgeo.geocaching.R; import cgeo.geocaching.files.GPXImporter; +import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.Log; import android.app.Activity; @@ -80,7 +81,7 @@ public class GPXListAdapter extends ArrayAdapter<File> { .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { - file.delete(); + FileUtils.deleteIgnoringFailure(file); GPXListAdapter.this.remove(file); } }) diff --git a/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java b/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java index 959cb14..91ce7e3 100644 --- a/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java +++ b/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java @@ -4,7 +4,7 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.IGeoData; import cgeo.geocaching.R; import cgeo.geocaching.settings.Settings; -import cgeo.geocaching.settings.Settings.coordInputFormatEnum; +import cgeo.geocaching.settings.Settings.CoordInputFormatEnum; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.compatibility.Compatibility; @@ -41,7 +41,7 @@ public class CoordinatesInputDialog extends NoTitleDialog { private CoordinateUpdate cuListener; - private coordInputFormatEnum currentFormat = null; + private CoordInputFormatEnum currentFormat = null; public CoordinatesInputDialog(final AbstractActivity context, final Geocache cache, final Geopoint gp, final IGeoData geo) { super(context, ActivityMixin.getDialogTheme()); @@ -272,7 +272,7 @@ public class CoordinatesInputDialog extends NoTitleDialog { * formatSec 2/3 2 2 3 */ - if (currentFormat == coordInputFormatEnum.Plain) { + if (currentFormat == CoordInputFormatEnum.Plain) { return; } @@ -325,7 +325,7 @@ public class CoordinatesInputDialog extends NoTitleDialog { } private boolean calc(final boolean signalError) { - if (currentFormat == coordInputFormatEnum.Plain) { + if (currentFormat == CoordInputFormatEnum.Plain) { try { gp = new Geopoint(eLat.getText().toString(), eLon.getText().toString()); } catch (final Geopoint.ParseException e) { @@ -375,10 +375,10 @@ public class CoordinatesInputDialog extends NoTitleDialog { if (editText == eLonDeg || editText == eLatSub || editText == eLonSub) { return 3; } - if ((editText == eLatMin || editText == eLonMin) && currentFormat == coordInputFormatEnum.Deg) { + if ((editText == eLatMin || editText == eLonMin) && currentFormat == CoordInputFormatEnum.Deg) { return 5; } - if ((editText == eLatSec || editText == eLonSec) && currentFormat == coordInputFormatEnum.Min) { + if ((editText == eLatSec || editText == eLonSec) && currentFormat == CoordInputFormatEnum.Min) { return 3; } return 2; @@ -402,7 +402,7 @@ public class CoordinatesInputDialog extends NoTitleDialog { } } - currentFormat = coordInputFormatEnum.fromInt(pos); + currentFormat = CoordInputFormatEnum.fromInt(pos); Settings.setCoordInputFormat(currentFormat); updateGUI(); } diff --git a/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java b/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java new file mode 100644 index 0000000..24f375d --- /dev/null +++ b/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java @@ -0,0 +1,115 @@ +package cgeo.geocaching.utils; + +import cgeo.geocaching.MainActivity; +import cgeo.geocaching.R; +import cgeo.geocaching.cgData; +import cgeo.geocaching.activity.ActivityMixin; +import cgeo.geocaching.ui.Formatter; + +import org.apache.commons.lang3.StringUtils; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.res.Resources; +import android.os.Handler; +import android.os.Message; + +import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; + +public class DatabaseBackupUtils { + + private DatabaseBackupUtils() { + // utility class + } + + /** + * restore the database in a new thread, showing a progress window + * + * @param activity + * calling activity + */ + public static void restoreDatabase(final Activity activity) { + final Resources res = activity.getResources(); + final ProgressDialog dialog = ProgressDialog.show(activity, res.getString(R.string.init_backup_restore), res.getString(R.string.init_restore_running), true, false); + final AtomicBoolean restoreSuccessful = new AtomicBoolean(false); + Thread restoreThread = new Thread() { + final Handler handler = new Handler() { + @Override + public void handleMessage(Message msg) { + dialog.dismiss(); + boolean restored = restoreSuccessful.get(); + String message = restored ? res.getString(R.string.init_restore_success) : res.getString(R.string.init_restore_failed); + ActivityMixin.helpDialog(activity, res.getString(R.string.init_backup_restore), message); + if (activity instanceof MainActivity) { + ((MainActivity) activity).updateCacheCounter(); + } + } + }; + + @Override + public void run() { + restoreSuccessful.set(cgData.restoreDatabaseInternal()); + handler.sendMessage(handler.obtainMessage()); + } + }; + restoreThread.start(); + } + + public static boolean createBackup(final Activity activity, final Runnable runAfterwards) { + final Context context = activity; + // avoid overwriting an existing backup with an empty database + // (can happen directly after reinstalling the app) + if (cgData.getAllCachesCount() == 0) { + ActivityMixin.helpDialog(activity, + context.getString(R.string.init_backup), + context.getString(R.string.init_backup_unnecessary)); + return false; + } + + final ProgressDialog dialog = ProgressDialog.show(context, + context.getString(R.string.init_backup), + context.getString(R.string.init_backup_running), true, false); + new Thread() { + @Override + public void run() { + final String backupFileName = cgData.backupDatabaseInternal(); + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + dialog.dismiss(); + ActivityMixin.helpDialog(activity, + context.getString(R.string.init_backup_backup), + backupFileName != null + ? context.getString(R.string.init_backup_success) + + "\n" + backupFileName + : context.getString(R.string.init_backup_failed)); + if (runAfterwards != null) { + runAfterwards.run(); + } + } + }); + } + }.start(); + return true; + } + + public static File getRestoreFile() { + final File fileSourceFile = cgData.getBackupFileInternal(); + return fileSourceFile.exists() ? fileSourceFile : null; + } + + public static boolean hasBackup() { + return getRestoreFile() != null; + } + + public static String getBackupDateTime() { + final File restoreFile = getRestoreFile(); + if (restoreFile == null) { + return StringUtils.EMPTY; + } + return Formatter.formatShortDateTime(restoreFile.lastModified()); + } + +} diff --git a/main/src/cgeo/geocaching/utils/FileUtils.java b/main/src/cgeo/geocaching/utils/FileUtils.java index 5ab8fcc..edb8102 100644 --- a/main/src/cgeo/geocaching/utils/FileUtils.java +++ b/main/src/cgeo/geocaching/utils/FileUtils.java @@ -1,6 +1,7 @@ package cgeo.geocaching.utils; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; import android.os.Handler; import android.os.Message; @@ -11,8 +12,6 @@ import java.util.List; /** * Utility class for files * - * @author rsudev - * */ public final class FileUtils { @@ -65,4 +64,65 @@ public final class FileUtils { public abstract boolean shouldEnd(); } + + /** + * Create a unique non existing file named like the given file name. If a file with the given name already exists, + * add a number as suffix to the file name.<br> + * Example: For the file name "file.ext" this will return the first file of the list + * <ul> + * <li>file.ext</li> + * <li>file_2.ext</li> + * <li>file_3.ext</li> + * </ul> + * which does not yet exist. + */ + public static File getUniqueNamedFile(final String baseNameAndPath) { + String extension = StringUtils.substringAfterLast(baseNameAndPath, "."); + String pathName = StringUtils.substringBeforeLast(baseNameAndPath, "."); + int number = 1; + while (new File(getNumberedFileName(pathName, extension, number)).exists()) { + number++; + } + return new File(getNumberedFileName(pathName, extension, number)); + } + + private static String getNumberedFileName(String pathName, String extension, int number) { + return pathName + (number > 1 ? "_" + Integer.toString(number) : "") + "." + extension; + } + + /** + * This usage of this method indicates that the return value of File.delete() can safely be ignored. + */ + public static void deleteIgnoringFailure(final File file) { + if (!file.delete()) { + Log.i("Could not delete " + file.getAbsolutePath()); + } + } + + /** + * Deletes a file and logs deletion failures. + * + * @return <code> true</code> if this file was deleted, <code>false</code> otherwise. + */ + public static boolean delete(final File file) { + final boolean success = file.delete(); + if (!success) { + Log.e("Could not delete " + file.getAbsolutePath()); + } + return success; + } + + /** + * Creates the directory named by the given file, creating any missing parent directories in the process. + * + * @return <code>true</code> if the directory was created, <code>false</code> on failure or if the directory already + * existed. + */ + public static boolean mkdirs(File file) { + final boolean success = file.mkdirs(); + if (!success) { + Log.e("Could not make directories " + file.getAbsolutePath()); + } + return success; + } } diff --git a/main/src/cgeo/geocaching/utils/Log.java b/main/src/cgeo/geocaching/utils/Log.java index f7f33d9..aa8dbd1 100644 --- a/main/src/cgeo/geocaching/utils/Log.java +++ b/main/src/cgeo/geocaching/utils/Log.java @@ -8,26 +8,30 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Writer; -final public class Log { +public final class Log { private static final String TAG = "cgeo"; /** - * the debug flag is cached here so that we don't need to access the settings every time we have to evaluate it + * The debug flag is cached here so that we don't need to access the settings every time we have to evaluate it. */ private static boolean isDebug = true; private static boolean first = true; + private Log() { + // utility class + } + public static boolean isDebug() { return isDebug; } /** - * save a copy of the debug flag from the settings for performance reasons - * + * Save a copy of the debug flag from the settings for performance reasons. + * * @param isDebug */ - public static void setDebug(boolean isDebug) { + public static void setDebug(final boolean isDebug) { Log.isDebug = isDebug; } @@ -95,11 +99,11 @@ final public class Log { * * @param msg the message to log, or to add to the log if other messages have been stored in the same run */ - public synchronized static void logToFile(final String msg) { + public static synchronized void logToFile(final String msg) { final File file = new File(Environment.getExternalStorageDirectory(), "cgeo-debug.log"); if (first) { first = false; - file.delete(); + FileUtils.delete(file); } Writer writer = null; try { diff --git a/main/src/cgeo/geocaching/utils/MatcherWrapper.java b/main/src/cgeo/geocaching/utils/MatcherWrapper.java index c3c1663..78b1170 100644 --- a/main/src/cgeo/geocaching/utils/MatcherWrapper.java +++ b/main/src/cgeo/geocaching/utils/MatcherWrapper.java @@ -22,6 +22,10 @@ public class MatcherWrapper { return matcher.find(); } + public boolean find(int start) { + return matcher.find(start); + } + /** * see {@link Matcher#group(int)} */ diff --git a/main/src/cgeo/geocaching/utils/SimpleCancellableHandler.java b/main/src/cgeo/geocaching/utils/SimpleCancellableHandler.java index 75c10ab..9207c74 100644 --- a/main/src/cgeo/geocaching/utils/SimpleCancellableHandler.java +++ b/main/src/cgeo/geocaching/utils/SimpleCancellableHandler.java @@ -20,7 +20,7 @@ public class SimpleCancellableHandler extends CancellableHandler { } @Override - public void handleRegularMessage(final Message msg) { + protected void handleRegularMessage(final Message msg) { AbstractActivity activity = activityRef.get(); if (activity != null && msg.getData() != null && msg.getData().getString(MESSAGE_TEXT) != null) { activity.showToast(msg.getData().getString(MESSAGE_TEXT)); @@ -30,7 +30,7 @@ public class SimpleCancellableHandler extends CancellableHandler { } @Override - public void handleCancel(final Object extra) { + protected void handleCancel(final Object extra) { AbstractActivity activity = activityRef.get(); if (activity != null) { activity.showToast((String) extra); @@ -38,7 +38,7 @@ public class SimpleCancellableHandler extends CancellableHandler { dismissProgress(); } - public final void showToast(int resId) { + protected final void showToast(int resId) { AbstractActivity activity = activityRef.get(); if (activity != null) { Resources res = activity.getResources(); @@ -46,7 +46,7 @@ public class SimpleCancellableHandler extends CancellableHandler { } } - public final void dismissProgress() { + protected final void dismissProgress() { Progress progressDialog = progressDialogRef.get(); if (progressDialog != null) { progressDialog.dismiss(); diff --git a/main/src/cgeo/geocaching/utils/UncertainProperty.java b/main/src/cgeo/geocaching/utils/UncertainProperty.java new file mode 100644 index 0000000..5f86662 --- /dev/null +++ b/main/src/cgeo/geocaching/utils/UncertainProperty.java @@ -0,0 +1,48 @@ +package cgeo.geocaching.utils; + +import cgeo.geocaching.connector.gc.Tile; + +public class UncertainProperty<T> { + + private final T value; + private final int certaintyLevel; + + public UncertainProperty(T value) { + this(value, Tile.ZOOMLEVEL_MAX + 1); + } + + public UncertainProperty(T value, int certaintyLevel) { + this.value = value; + this.certaintyLevel = certaintyLevel; + } + + public T getValue() { + return value; + } + + public int getCertaintyLevel() { + return certaintyLevel; + } + + public UncertainProperty<T> getMergedProperty(final UncertainProperty<T> other) { + if (null == other || null == other.value) { + return this; + } + if (null == this.value) { + return other; + } + if (other.certaintyLevel > certaintyLevel) { + return other; + } + + return this; + } + + public static <T> UncertainProperty<T> getMergedProperty(UncertainProperty<T> property, UncertainProperty<T> otherProperty) { + if (null == property) { + return otherProperty; + } + return property.getMergedProperty(otherProperty); + } + +} |
