diff options
Diffstat (limited to 'main/src')
199 files changed, 2958 insertions, 2493 deletions
diff --git a/main/src/cgeo/geocaching/AboutActivity.java b/main/src/cgeo/geocaching/AboutActivity.java index 9dcd099..3ab9904 100644 --- a/main/src/cgeo/geocaching/AboutActivity.java +++ b/main/src/cgeo/geocaching/AboutActivity.java @@ -120,7 +120,7 @@ public class AboutActivity extends AbstractViewPagerActivity<AboutActivity.Page> final ScrollView view = (ScrollView) getLayoutInflater().inflate(R.layout.about_version_page, null); Views.inject(this, view); version.setText(Version.getVersionName(AboutActivity.this)); - setClickListener(donateButton, "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FMLNN8GXZKJEE"); + setClickListener(donateButton, "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=AQBS7UP76CXW2"); return view; } } @@ -140,7 +140,7 @@ public class AboutActivity extends AbstractViewPagerActivity<AboutActivity.Page> } @Override - public final void onCreate(final Bundle savedInstanceState) { + public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState, R.layout.viewpager_activity); createViewPager(0, null); reinitializeViewPager(); diff --git a/main/src/cgeo/geocaching/AbstractPopupActivity.java b/main/src/cgeo/geocaching/AbstractPopupActivity.java index 03f0680..5b9b509 100644 --- a/main/src/cgeo/geocaching/AbstractPopupActivity.java +++ b/main/src/cgeo/geocaching/AbstractPopupActivity.java @@ -2,7 +2,6 @@ package cgeo.geocaching; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.ActivityMixin; -import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.gcvote.GCVote; @@ -17,12 +16,8 @@ import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; -import android.content.Intent; import android.graphics.Rect; -import android.net.Uri; import android.os.Bundle; -import android.os.Handler; -import android.os.Message; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; @@ -33,7 +28,7 @@ import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; -public abstract class AbstractPopupActivity extends AbstractActivity { +public abstract class AbstractPopupActivity extends AbstractActivity implements CacheMenuHandler.ActivityInterface { protected Geocache cache = null; protected String geocode = null; @@ -42,18 +37,6 @@ public abstract class AbstractPopupActivity extends AbstractActivity { private TextView cacheDistance = null; private final int layout; - private final Handler ratingHandler = new Handler() { - - @Override - public void handleMessage(Message msg) { - try { - details.addRating(cache); - } catch (final Exception e) { - // nothing - } - } - }; - private final GeoDirHandler geoUpdate = new GeoDirHandler() { @Override @@ -91,8 +74,8 @@ public abstract class AbstractPopupActivity extends AbstractActivity { if (!cache.supportsGCVote()) { return; } - (new Thread("Load GCVote") { + (new Thread("Load GCVote") { @Override public void run() { final GCVoteRating rating = GCVote.getRating(cache.getGuid(), geocode); @@ -102,14 +85,17 @@ public abstract class AbstractPopupActivity extends AbstractActivity { } cache.setRating(rating.getRating()); cache.setVotes(rating.getVotes()); - final Message msg = Message.obtain(); - ratingHandler.sendMessage(msg); + runOnUiThread(new Runnable() { + @Override + public void run() { + details.addRating(cache); } + }); } }).start(); } protected void init() { - cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); if (cache == null) { showToast(res.getString(R.string.err_detail_cache_find)); @@ -121,11 +107,8 @@ public abstract class AbstractPopupActivity extends AbstractActivity { geocode = cache.getGeocode(); } - private void showInBrowser() { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(cache.getUrl()))); - } - - protected abstract void navigateTo(); + @Override + public abstract void navigateTo(); @Override public void onCreate(Bundle savedInstanceState) { @@ -161,31 +144,17 @@ public abstract class AbstractPopupActivity extends AbstractActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.abstract_popup_activity, menu); + CacheMenuHandler.addMenuItems(this, menu, cache); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { - final int menuItem = item.getItemId(); - - switch (menuItem) { - case R.id.menu_default_navigation: - navigateTo(); - return true; - case R.id.menu_navigate: - showNavigationMenu(); - return true; - case R.id.menu_caches_around: - cachesAround(); - return true; - case R.id.menu_show_in_browser: - showInBrowser(); - return true; - default: - if (LoggingUI.onMenuItemSelected(item, this, cache)) { - return true; - } + if (CacheMenuHandler.onMenuItemSelected(item, this, cache)) { + return true; + } + if (LoggingUI.onMenuItemSelected(item, this, cache)) { + return true; } return true; @@ -202,12 +171,7 @@ public abstract class AbstractPopupActivity extends AbstractActivity { super.onPrepareOptionsMenu(menu); try { - final boolean visible = getCoordinates() != null; - menu.findItem(R.id.menu_default_navigation).setVisible(visible); - menu.findItem(R.id.menu_navigate).setVisible(visible); - menu.findItem(R.id.menu_caches_around).setVisible(visible); - - menu.findItem(R.id.menu_default_navigation).setTitle(NavigationAppFactory.getDefaultNavigationApplication().getName()); + CacheMenuHandler.onPrepareOptionsMenu(menu, cache); LoggingUI.onPrepareOptionsMenu(menu, cache); } catch (final RuntimeException e) { // nothing @@ -238,11 +202,13 @@ public abstract class AbstractPopupActivity extends AbstractActivity { return super.onTouchEvent(event); } - protected abstract void showNavigationMenu(); + @Override + public abstract void showNavigationMenu(); protected abstract void startDefaultNavigation2(); protected final void addCacheDetails() { + assert cache != null; // cache type final String cacheType = cache.getType().getL10n(); final String cacheSize = cache.getSize() != CacheSize.UNKNOWN ? " (" + cache.getSize().getL10n() + ")" : ""; @@ -256,6 +222,7 @@ public abstract class AbstractPopupActivity extends AbstractActivity { details.addDifficulty(cache); details.addTerrain(cache); + details.addEventDate(cache); // rating if (cache.getRating() > 0) { @@ -279,13 +246,14 @@ public abstract class AbstractPopupActivity extends AbstractActivity { }); } - private void cachesAround() { + @Override + public void cachesAround() { final Geopoint coords = getCoordinates(); if (coords == null) { showToast(res.getString(R.string.err_location_unknown)); return; } - cgeocaches.startActivityCoordinates(this, coords); + CacheListActivity.startActivityCoordinates(this, coords); finish(); } diff --git a/main/src/cgeo/geocaching/AddressListActivity.java b/main/src/cgeo/geocaching/AddressListActivity.java index c984d28..dc0239f 100644 --- a/main/src/cgeo/geocaching/AddressListActivity.java +++ b/main/src/cgeo/geocaching/AddressListActivity.java @@ -4,7 +4,7 @@ import cgeo.geocaching.activity.AbstractListActivity; import cgeo.geocaching.ui.AddressListAdapter; import cgeo.geocaching.utils.Log; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import android.app.ProgressDialog; @@ -66,7 +66,7 @@ public class AddressListActivity extends AbstractListActivity { } } else { finish(); - cgeocaches.startActivityAddress(AddressListActivity.this, null, keyword); + CacheListActivity.startActivityAddress(AddressListActivity.this, null, keyword); } } diff --git a/main/src/cgeo/geocaching/CacheCache.java b/main/src/cgeo/geocaching/CacheCache.java index e70b7a0..b3c674c 100644 --- a/main/src/cgeo/geocaching/CacheCache.java +++ b/main/src/cgeo/geocaching/CacheCache.java @@ -1,6 +1,6 @@ package cgeo.geocaching; -import cgeo.geocaching.cgData.StorageLocation; +import cgeo.geocaching.DataStore.StorageLocation; import cgeo.geocaching.connector.gc.Tile; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.geopoint.Viewport; diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java index 4b34f8d..4eddba4 100644 --- a/main/src/cgeo/geocaching/CacheDetailActivity.java +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -3,7 +3,6 @@ package cgeo.geocaching; import butterknife.InjectView; import butterknife.Views; -import cgeo.calendar.ICalendar; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.AbstractViewPagerActivity; import cgeo.geocaching.activity.Progress; @@ -16,11 +15,10 @@ import cgeo.geocaching.enumerations.CacheAttribute; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; import cgeo.geocaching.enumerations.WaypointType; -import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.geopoint.Units; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.network.Network; -import cgeo.geocaching.network.Parameters; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.AbstractCachingPageViewCreator; import cgeo.geocaching.ui.AnchorAwareLinkMovementMethod; @@ -52,7 +50,7 @@ import cgeo.geocaching.utils.TextUtils; import cgeo.geocaching.utils.TranslationUtils; import cgeo.geocaching.utils.UnknownTagsHandler; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -64,8 +62,6 @@ import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Typeface; @@ -89,7 +85,6 @@ import android.util.TypedValue; import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; -import android.view.SubMenu; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; @@ -121,25 +116,7 @@ import java.util.regex.Pattern; * * e.g. details, description, logs, waypoints, inventory... */ -public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailActivity.Page> { - - private static final int MENU_FIELD_COPY = 1; - private static final int MENU_FIELD_TRANSLATE = 2; - private static final int MENU_FIELD_TRANSLATE_EN = 3; - private static final int MENU_FIELD_SHARE = 4; - private static final int MENU_SHARE = 12; - private static final int MENU_CALENDAR = 11; - private static final int MENU_CACHES_AROUND = 10; - private static final int MENU_BROWSER = 7; - private static final int MENU_DEFAULT_NAVIGATION = 13; - - private static final int CONTEXT_MENU_WAYPOINT_EDIT = 1234; - private static final int CONTEXT_MENU_WAYPOINT_DUPLICATE = 1235; - private static final int CONTEXT_MENU_WAYPOINT_DELETE = 1236; - private static final int CONTEXT_MENU_WAYPOINT_NAVIGATE = 1238; - private static final int CONTEXT_MENU_WAYPOINT_CACHES_AROUND = 1239; - private static final int CONTEXT_MENU_WAYPOINT_DEFAULT_NAVIGATION = 1240; - private static final int CONTEXT_MENU_WAYPOINT_RESET_ORIGINAL_CACHE_COORDINATES = 1241; +public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailActivity.Page> implements CacheMenuHandler.ActivityInterface { private static final int MESSAGE_FAILED = -1; private static final int MESSAGE_SUCCEEDED = 1; @@ -173,7 +150,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc }; private CharSequence clickedItemText = null; - private int contextMenuWPIndex = -1; /** * If another activity is called and can modify the data of this activity, we refresh it on resume. @@ -184,6 +160,10 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc private TextView cacheDistanceView; protected ImagesList imagesList; + /** + * waypoint selected in context menu. This variable will be gone when the waypoint context menu is a fragment. + */ + private Waypoint selectedWaypoint; @Override public void onCreate(Bundle savedInstanceState) { @@ -360,18 +340,20 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo info) { super.onCreateContextMenu(menu, view, info); final int viewId = view.getId(); - contextMenuWPIndex = -1; switch (viewId) { case R.id.value: // coordinates, gc-code, name + assert view instanceof TextView; clickedItemText = ((TextView) view).getText(); final String itemTitle = (String) ((TextView) ((View) view.getParent()).findViewById(R.id.name)).getText(); - buildOptionsContextmenu(menu, viewId, itemTitle, true); + buildDetailsContextMenu(menu, itemTitle, true); break; case R.id.shortdesc: + assert view instanceof TextView; clickedItemText = ((TextView) view).getText(); - buildOptionsContextmenu(menu, viewId, res.getString(R.string.cache_description), false); + buildDetailsContextMenu(menu, res.getString(R.string.cache_description), false); break; case R.id.longdesc: + assert view instanceof TextView; // combine short and long description final String shortDesc = cache.getShortDescription(); if (StringUtils.isBlank(shortDesc)) { @@ -379,52 +361,38 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } else { clickedItemText = shortDesc + "\n\n" + ((TextView) view).getText(); } - buildOptionsContextmenu(menu, viewId, res.getString(R.string.cache_description), false); + buildDetailsContextMenu(menu, res.getString(R.string.cache_description), false); break; case R.id.personalnote: + assert view instanceof TextView; clickedItemText = ((TextView) view).getText(); - buildOptionsContextmenu(menu, viewId, res.getString(R.string.cache_personal_note), true); + buildDetailsContextMenu(menu, res.getString(R.string.cache_personal_note), true); break; case R.id.hint: + assert view instanceof TextView; clickedItemText = ((TextView) view).getText(); - buildOptionsContextmenu(menu, viewId, res.getString(R.string.cache_hint), false); + buildDetailsContextMenu(menu, res.getString(R.string.cache_hint), false); break; case R.id.log: + assert view instanceof TextView; clickedItemText = ((TextView) view).getText(); - buildOptionsContextmenu(menu, viewId, res.getString(R.string.cache_logs), false); + buildDetailsContextMenu(menu, res.getString(R.string.cache_logs), false); break; - case -1: - if (null != cache.getWaypoints()) { - try { - final ViewGroup parent = ((ViewGroup) view.getParent()); - for (int i = 0; i < parent.getChildCount(); i++) { - if (parent.getChildAt(i) == view) { - final List<Waypoint> sortedWaypoints = new ArrayList<Waypoint>(cache.getWaypoints()); - Collections.sort(sortedWaypoints); - final Waypoint waypoint = sortedWaypoints.get(i); - final int index = cache.getWaypoints().indexOf(waypoint); - menu.setHeaderTitle(res.getString(R.string.waypoint)); - if (waypoint.getWaypointType().equals(WaypointType.ORIGINAL)) { - menu.add(CONTEXT_MENU_WAYPOINT_RESET_ORIGINAL_CACHE_COORDINATES, index, 0, R.string.waypoint_reset_cache_coords); - } else { - menu.add(CONTEXT_MENU_WAYPOINT_EDIT, index, 0, R.string.waypoint_edit); - menu.add(CONTEXT_MENU_WAYPOINT_DUPLICATE, index, 0, R.string.waypoint_duplicate); - } - contextMenuWPIndex = index; - if (waypoint.isUserDefined() && !waypoint.getWaypointType().equals(WaypointType.ORIGINAL)) { - menu.add(CONTEXT_MENU_WAYPOINT_DELETE, index, 0, R.string.waypoint_delete); - } - if (waypoint.getCoords() != null) { - menu.add(CONTEXT_MENU_WAYPOINT_DEFAULT_NAVIGATION, index, 0, NavigationAppFactory.getDefaultNavigationApplication().getName()); - menu.add(CONTEXT_MENU_WAYPOINT_NAVIGATE, index, 0, R.string.cache_menu_navigate).setIcon(R.drawable.ic_menu_mapmode); - menu.add(CONTEXT_MENU_WAYPOINT_CACHES_AROUND, index, 0, R.string.cache_menu_around); - } - break; - } - } - } catch (final RuntimeException e) { - } - } + case R.id.waypoint: + menu.setHeaderTitle(res.getString(R.string.waypoint)); + getMenuInflater().inflate(R.menu.waypoint_options, menu); + final boolean isOriginalWaypoint = selectedWaypoint.getWaypointType().equals(WaypointType.ORIGINAL); + menu.findItem(R.id.menu_waypoint_reset_cache_coords).setVisible(isOriginalWaypoint); + menu.findItem(R.id.menu_waypoint_edit).setVisible(!isOriginalWaypoint); + menu.findItem(R.id.menu_waypoint_duplicate).setVisible(!isOriginalWaypoint); + final boolean userDefined = selectedWaypoint.isUserDefined() && !selectedWaypoint.getWaypointType().equals(WaypointType.ORIGINAL); + menu.findItem(R.id.menu_waypoint_delete).setVisible(userDefined); + final boolean hasCoords = selectedWaypoint.getCoords() != null; + final MenuItem defaultNavigationMenu = menu.findItem(R.id.menu_waypoint_navigate_default); + defaultNavigationMenu.setVisible(hasCoords); + defaultNavigationMenu.setTitle(NavigationAppFactory.getDefaultNavigationApplication().getName()); + menu.findItem(R.id.menu_waypoint_navigate).setVisible(hasCoords); + menu.findItem(R.id.menu_waypoint_caches_around).setVisible(hasCoords); break; default: if (imagesList != null) { @@ -434,171 +402,118 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } } - private void buildOptionsContextmenu(ContextMenu menu, int viewId, String fieldTitle, boolean copyOnly) { + private void buildDetailsContextMenu(ContextMenu menu, String fieldTitle, boolean copyOnly) { menu.setHeaderTitle(fieldTitle); - menu.add(viewId, MENU_FIELD_COPY, 0, res.getString(android.R.string.copy)); + getMenuInflater().inflate(R.menu.details_context, menu); + menu.findItem(R.id.menu_translate_to_sys_lang).setVisible(!copyOnly); if (!copyOnly) { - if (clickedItemText.length() > TranslationUtils.translationTextLengthToWarn) { + if (clickedItemText.length() > TranslationUtils.TRANSLATION_TEXT_LENGTH_WARN) { showToast(res.getString(R.string.translate_length_warning)); } - menu.add(viewId, MENU_FIELD_TRANSLATE, 0, res.getString(R.string.translate_to_sys_lang, Locale.getDefault().getDisplayLanguage())); - if (Settings.isUseEnglish() && !StringUtils.equals(Locale.getDefault().getLanguage(), Locale.ENGLISH.getLanguage())) { - menu.add(viewId, MENU_FIELD_TRANSLATE_EN, 0, res.getString(R.string.translate_to_english)); - } - + menu.findItem(R.id.menu_translate_to_sys_lang).setTitle(res.getString(R.string.translate_to_sys_lang, Locale.getDefault().getDisplayLanguage())); } - menu.add(viewId, MENU_FIELD_SHARE, 0, res.getString(R.string.cache_share_field)); + final boolean localeIsEnglish = StringUtils.equals(Locale.getDefault().getLanguage(), Locale.ENGLISH.getLanguage()); + menu.findItem(R.id.menu_translate_to_english).setVisible(!copyOnly && !localeIsEnglish); } @Override public boolean onContextItemSelected(MenuItem item) { - final int groupId = item.getGroupId(); - final int index = item.getItemId(); - switch (groupId) { - case R.id.value: - case R.id.shortdesc: - case R.id.longdesc: - case R.id.personalnote: - case R.id.hint: - case R.id.log: - switch (index) { - case MENU_FIELD_COPY: - ClipboardUtils.copyToClipboard(clickedItemText); - showToast(res.getString(R.string.clipboard_copy_ok)); - return true; - case MENU_FIELD_TRANSLATE: - TranslationUtils.startActivityTranslate(this, Locale.getDefault().getLanguage(), HtmlUtils.extractText(clickedItemText)); - return true; - case MENU_FIELD_TRANSLATE_EN: - TranslationUtils.startActivityTranslate(this, Locale.ENGLISH.getLanguage(), HtmlUtils.extractText(clickedItemText)); - return true; - case MENU_FIELD_SHARE: - final Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_TEXT, clickedItemText.toString()); - startActivity(Intent.createChooser(intent, res.getText(R.string.cache_share_field))); - return true; - default: - break; - } - - break; - case CONTEXT_MENU_WAYPOINT_EDIT: - final Waypoint waypointEdit = cache.getWaypoint(index); - if (waypointEdit != null) { - EditWaypointActivity.startActivityEditWaypoint(this, waypointEdit.getId()); + switch (item.getItemId()) { + // detail fields + case R.id.menu_copy: + ClipboardUtils.copyToClipboard(clickedItemText); + showToast(res.getString(R.string.clipboard_copy_ok)); + return true; + case R.id.menu_translate_to_sys_lang: + TranslationUtils.startActivityTranslate(this, Locale.getDefault().getLanguage(), HtmlUtils.extractText(clickedItemText)); + return true; + case R.id.menu_translate_to_english: + TranslationUtils.startActivityTranslate(this, Locale.ENGLISH.getLanguage(), HtmlUtils.extractText(clickedItemText)); + return true; + case R.id.menu_cache_share_field: + final Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TEXT, clickedItemText.toString()); + startActivity(Intent.createChooser(intent, res.getText(R.string.cache_share_field))); + return true; + // waypoints + case R.id.menu_waypoint_edit: + if (selectedWaypoint != null) { + EditWaypointActivity.startActivityEditWaypoint(this, cache, selectedWaypoint.getId()); refreshOnResume = true; } - break; - case CONTEXT_MENU_WAYPOINT_DUPLICATE: - final Waypoint waypointDuplicate = cache.getWaypoint(index); - if (cache.duplicateWaypoint(waypointDuplicate)) { - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); + return true; + case R.id.menu_waypoint_duplicate: + if (cache.duplicateWaypoint(selectedWaypoint)) { + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); notifyDataSetChanged(); } - break; - case CONTEXT_MENU_WAYPOINT_DELETE: - final Waypoint waypointDelete = cache.getWaypoint(index); - if (cache.deleteWaypoint(waypointDelete)) { - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); + return true; + case R.id.menu_waypoint_delete: + if (cache.deleteWaypoint(selectedWaypoint)) { + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); notifyDataSetChanged(); } - break; - case CONTEXT_MENU_WAYPOINT_DEFAULT_NAVIGATION: - final Waypoint waypointNavigation = cache.getWaypoint(index); - if (waypointNavigation != null) { - NavigationAppFactory.startDefaultNavigationApplication(1, this, waypointNavigation); + return true; + case R.id.menu_waypoint_navigate_default: + if (selectedWaypoint != null) { + NavigationAppFactory.startDefaultNavigationApplication(1, this, selectedWaypoint); } - break; - case CONTEXT_MENU_WAYPOINT_NAVIGATE: - final Waypoint waypointNav = cache.getWaypoint(contextMenuWPIndex); - if (waypointNav != null) { - NavigationAppFactory.showNavigationMenu(this, null, waypointNav, null); + return true; + case R.id.menu_waypoint_navigate: + if (selectedWaypoint != null) { + NavigationAppFactory.showNavigationMenu(this, null, selectedWaypoint, null); } - break; - case CONTEXT_MENU_WAYPOINT_CACHES_AROUND: - final Waypoint waypointAround = cache.getWaypoint(index); - if (waypointAround != null) { - cgeocaches.startActivityCoordinates(this, waypointAround.getCoords()); + return true; + case R.id.menu_waypoint_caches_around: + if (selectedWaypoint != null) { + CacheListActivity.startActivityCoordinates(this, selectedWaypoint.getCoords()); } - break; - - case CONTEXT_MENU_WAYPOINT_RESET_ORIGINAL_CACHE_COORDINATES: - final Waypoint waypointReset = cache.getWaypoint(index); + return true; + case R.id.menu_waypoint_reset_cache_coords: if (ConnectorFactory.getConnector(cache).supportsOwnCoordinates()) { - createResetCacheCoordinatesDialog(cache, waypointReset).show(); + createResetCacheCoordinatesDialog(cache, selectedWaypoint).show(); } else { final ProgressDialog progressDialog = ProgressDialog.show(this, getString(R.string.cache), getString(R.string.waypoint_reset), true); final HandlerResetCoordinates handler = new HandlerResetCoordinates(this, progressDialog, false); - new ResetCoordsThread(cache, handler, waypointReset, true, false, progressDialog).start(); + new ResetCoordsThread(cache, handler, selectedWaypoint, true, false, progressDialog).start(); } - break; - + return true; default: - if (imagesList != null && imagesList.onContextItemSelected(item)) { - return true; - } - return onOptionsItemSelected(item); + break; } - return false; + if (imagesList != null && imagesList.onContextItemSelected(item)) { + return true; + } + return onOptionsItemSelected(item); } @Override public boolean onCreateOptionsMenu(Menu menu) { - if (null != cache) { - menu.add(0, MENU_DEFAULT_NAVIGATION, 0, NavigationAppFactory.getDefaultNavigationApplication().getName()).setIcon(R.drawable.ic_menu_compass); // default navigation tool - - final SubMenu subMenu = menu.addSubMenu(0, 0, 0, res.getString(R.string.cache_menu_navigate)).setIcon(R.drawable.ic_menu_mapmode); - NavigationAppFactory.addMenuItems(subMenu, cache); - - menu.add(0, MENU_CALENDAR, 0, res.getString(R.string.cache_menu_event)).setIcon(R.drawable.ic_menu_agenda); // add event to calendar - LoggingUI.addMenuItems(this, menu, cache); - menu.add(0, MENU_CACHES_AROUND, 0, res.getString(R.string.cache_menu_around)).setIcon(R.drawable.ic_menu_rotate); // caches around - menu.add(0, MENU_BROWSER, 0, res.getString(R.string.cache_menu_browser)).setIcon(R.drawable.ic_menu_globe); // browser - menu.add(0, MENU_SHARE, 0, res.getString(R.string.cache_menu_share)).setIcon(R.drawable.ic_menu_share); // share cache - } + CacheMenuHandler.addMenuItems(this, menu, cache); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { - if (cache != null) { - menu.findItem(MENU_DEFAULT_NAVIGATION).setVisible(null != cache.getCoords()); - menu.findItem(MENU_CALENDAR).setVisible(cache.canBeAddedToCalendar()); - menu.findItem(MENU_CACHES_AROUND).setVisible(null != cache.getCoords() && cache.supportsCachesAround()); - menu.findItem(MENU_BROWSER).setVisible(cache.canOpenInBrowser()); - LoggingUI.onPrepareOptionsMenu(menu, cache); - } + CacheMenuHandler.onPrepareOptionsMenu(menu, cache); + LoggingUI.onPrepareOptionsMenu(menu, cache); return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { + if (CacheMenuHandler.onMenuItemSelected(item, this, cache)) { + return true; + } + final int menuItem = item.getItemId(); switch (menuItem) { case 0: // no menu selected, but a new sub menu shown return false; - case MENU_DEFAULT_NAVIGATION: - startDefaultNavigation(); - return true; - case MENU_BROWSER: - cache.openInBrowser(this); - return true; - case MENU_CACHES_AROUND: - cgeocaches.startActivityCoordinates(this, cache.getCoords()); - return true; - case MENU_CALENDAR: - addToCalendarWithIntent(); - return true; - case MENU_SHARE: - if (cache != null) { - cache.shareCache(this, res); - return true; - } - return false; default: if (NavigationAppFactory.onMenuItemSelected(item, this, cache)) { return true; @@ -732,80 +647,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } /** - * Indicates whether the specified action can be used as an intent. This - * method queries the package manager for installed packages that can - * respond to an intent with the specified action. If no suitable package is - * found, this method returns false. - * - * @param context - * The application's environment. - * @param action - * The Intent action to check for availability. - * @param uri - * The Intent URI to check for availability. - * - * @return True if an Intent with the specified action can be sent and - * responded to, false otherwise. - */ - private static boolean isIntentAvailable(Context context, String action, Uri uri) { - final PackageManager packageManager = context.getPackageManager(); - final Intent intent; - if (uri == null) { - intent = new Intent(action); - } else { - intent = new Intent(action, uri); - } - final List<ResolveInfo> list = packageManager.queryIntentActivities(intent, - PackageManager.MATCH_DEFAULT_ONLY); - return !list.isEmpty(); - } - - private void addToCalendarWithIntent() { - - final boolean calendarAddOnAvailable = isIntentAvailable(this, ICalendar.INTENT, Uri.parse(ICalendar.URI_SCHEME + "://" + ICalendar.URI_HOST)); - - if (calendarAddOnAvailable) { - final Parameters params = new Parameters( - ICalendar.PARAM_NAME, cache.getName(), - ICalendar.PARAM_NOTE, StringUtils.defaultString(cache.getPersonalNote()), - ICalendar.PARAM_HIDDEN_DATE, String.valueOf(cache.getHiddenDate().getTime()), - ICalendar.PARAM_URL, StringUtils.defaultString(cache.getUrl()), - ICalendar.PARAM_COORDS, cache.getCoords() == null ? "" : cache.getCoords().format(GeopointFormatter.Format.LAT_LON_DECMINUTE_RAW), - ICalendar.PARAM_LOCATION, StringUtils.defaultString(cache.getLocation()), - ICalendar.PARAM_SHORT_DESC, StringUtils.defaultString(cache.getShortDescription()), - ICalendar.PARAM_START_TIME_MINUTES, StringUtils.defaultString(cache.guessEventTimeMinutes()) - ); - - startActivity(new Intent(ICalendar.INTENT, - Uri.parse(ICalendar.URI_SCHEME + "://" + ICalendar.URI_HOST + "?" + params.toString()))); - } else { - // Inform user the calendar add-on is not installed and let them get it from Google Play - new AlertDialog.Builder(this) - .setTitle(res.getString(R.string.addon_missing_title)) - .setMessage(new StringBuilder(res.getString(R.string.helper_calendar_missing)) - .append(' ') - .append(res.getString(R.string.addon_download_prompt)) - .toString()) - .setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - final Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(ICalendar.CALENDAR_ADDON_URI)); - startActivity(intent); - } - }) - .setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }) - .create() - .show(); - } - } - - /** * Tries to navigate to the {@link Geocache} of this activity. */ private void startDefaultNavigation() { @@ -1134,7 +975,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (time > 0) { String dateString = Formatter.formatFullDate(time); if (cache.isEventCache()) { - dateString = DateUtils.formatDateTime(cgeoapplication.getInstance().getBaseContext(), time, DateUtils.FORMAT_SHOW_WEEKDAY) + ", " + dateString; + dateString = DateUtils.formatDateTime(CgeoApplication.getInstance().getBaseContext(), time, DateUtils.FORMAT_SHOW_WEEKDAY) + ", " + dateString; } details.add(cache.isEventCache() ? R.string.cache_event : R.string.cache_hidden, dateString); } @@ -1480,7 +1321,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } Settings.saveLastList(listId); - cgData.moveToList(cache, listId); + DataStore.moveToList(cache, listId); updateListBox(); } @@ -1563,7 +1404,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // update text final TextView text = (TextView) view.findViewById(R.id.list_text); - final StoredList list = cgData.getList(cache.getListId()); + final StoredList list = DataStore.getList(cache.getListId()); if (list != null) { text.setText(res.getString(R.string.cache_list_text) + " " + list.title); } else { @@ -1706,12 +1547,16 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } hintView.setVisibility(View.VISIBLE); hintView.setClickable(true); - hintView.setOnClickListener(new DecryptTextClickListener()); + hintView.setOnClickListener(new DecryptTextClickListener(hintView)); + hintBoxView.setOnClickListener(new DecryptTextClickListener(hintView)); + hintBoxView.setClickable(true); registerForContextMenu(hintView); } else { hintView.setVisibility(View.GONE); hintView.setClickable(false); hintView.setOnClickListener(null); + hintBoxView.setClickable(false); + hintBoxView.setOnClickListener(null); } final TextView spoilerlinkView = ((TextView) view.findViewById(R.id.hint_spoilerlink)); @@ -1808,6 +1653,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc private final View shortDescView; public LoadDescriptionTask(final String description, final View descriptionView, final View loadingIndicatorView, final View shortDescView) { + assert descriptionView instanceof IndexOutOfBoundsAvoidingTextView; this.descriptionString = description; this.descriptionView = (IndexOutOfBoundsAvoidingTextView) descriptionView; this.loadingIndicatorView = loadingIndicatorView; @@ -1933,122 +1779,138 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } } - private class WaypointsViewCreator extends AbstractCachingPageViewCreator<ScrollView> { + private class WaypointsViewCreator extends AbstractCachingPageViewCreator<ListView> { @Override - public ScrollView getDispatchedView() { + public ListView getDispatchedView() { if (cache == null) { // something is really wrong return null; } - view = (ScrollView) getLayoutInflater().inflate(R.layout.cachedetail_waypoints_page, null); - - final LinearLayout waypoints = (LinearLayout) view.findViewById(R.id.waypoints); - // sort waypoints: PP, Sx, FI, OWN final List<Waypoint> sortedWaypoints = new ArrayList<Waypoint>(cache.getWaypoints()); Collections.sort(sortedWaypoints); - for (final Waypoint wpt : sortedWaypoints) { - final LinearLayout waypointView = (LinearLayout) getLayoutInflater().inflate(R.layout.waypoint_item, null); + view = (ListView) getLayoutInflater().inflate(R.layout.cachedetail_waypoints_page, null); + view.setClickable(true); + View addWaypointButton = getLayoutInflater().inflate(R.layout.cachedetail_waypoints_footer, null); + view.addFooterView(addWaypointButton); + addWaypointButton.setOnClickListener(new View.OnClickListener() { - // coordinates - if (null != wpt.getCoords()) { - final TextView coordinatesView = (TextView) waypointView.findViewById(R.id.coordinates); - coordinatesView.setOnClickListener(new CoordinatesFormatSwitcher(wpt.getCoords())); - coordinatesView.setText(wpt.getCoords().toString()); - coordinatesView.setVisibility(View.VISIBLE); + @Override + public void onClick(View v) { + EditWaypointActivity.startActivityAddWaypoint(CacheDetailActivity.this, cache); + refreshOnResume = true; } + }); - // info - final String waypointInfo = Formatter.formatWaypointInfo(wpt); - if (StringUtils.isNotBlank(waypointInfo)) { - final TextView infoView = (TextView) waypointView.findViewById(R.id.info); - infoView.setText(waypointInfo); - infoView.setVisibility(View.VISIBLE); - } + view.setAdapter(new ArrayAdapter<Waypoint>(CacheDetailActivity.this, R.layout.waypoint_item, sortedWaypoints) { + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View rowView = convertView; + if (null == rowView) { + rowView = getLayoutInflater().inflate(R.layout.waypoint_item, null); + rowView.setClickable(true); + rowView.setLongClickable(true); + } + WaypointViewHolder holder = (WaypointViewHolder) rowView.getTag(); + if (null == holder) { + holder = new WaypointViewHolder(rowView); + } - // title - final TextView nameView = (TextView) waypointView.findViewById(R.id.name); - if (StringUtils.isNotBlank(wpt.getName())) { - nameView.setText(StringEscapeUtils.unescapeHtml4(wpt.getName())); - } else if (null != wpt.getCoords()) { - nameView.setText(wpt.getCoords().toString()); - } else { - nameView.setText(res.getString(R.string.waypoint)); + final Waypoint waypoint = getItem(position); + fillViewHolder(rowView, holder, waypoint); + return rowView; } - wpt.setIcon(res, nameView); + }); - // visited - if (wpt.isVisited()) { - final TypedValue a = new TypedValue(); - getTheme().resolveAttribute(R.attr.text_color_grey, a, true); - if (a.type >= TypedValue.TYPE_FIRST_COLOR_INT && a.type <= TypedValue.TYPE_LAST_COLOR_INT) { - // really should be just a color! - nameView.setTextColor(a.data); - } - } + return view; + } - // note - if (StringUtils.isNotBlank(wpt.getNote())) { - final TextView noteView = (TextView) waypointView.findViewById(R.id.note); - noteView.setVisibility(View.VISIBLE); - if (TextUtils.containsHtml(wpt.getNote())) { - noteView.setText(Html.fromHtml(wpt.getNote()), TextView.BufferType.SPANNABLE); - } - else { - noteView.setText(wpt.getNote()); - } - } + protected void fillViewHolder(View rowView, final WaypointViewHolder holder, final Waypoint wpt) { + // coordinates + if (null != wpt.getCoords()) { + final TextView coordinatesView = holder.coordinatesView; + coordinatesView.setOnClickListener(new CoordinatesFormatSwitcher(wpt.getCoords())); + coordinatesView.setText(wpt.getCoords().toString()); + coordinatesView.setVisibility(View.VISIBLE); + } - final View wpNavView = waypointView.findViewById(R.id.wpDefaultNavigation); - wpNavView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - NavigationAppFactory.startDefaultNavigationApplication(1, CacheDetailActivity.this, wpt); - } - }); - wpNavView.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - NavigationAppFactory.startDefaultNavigationApplication(2, CacheDetailActivity.this, wpt); - return true; - } - }); + // info + final String waypointInfo = Formatter.formatWaypointInfo(wpt); + if (StringUtils.isNotBlank(waypointInfo)) { + final TextView infoView = holder.infoView; + infoView.setText(waypointInfo); + infoView.setVisibility(View.VISIBLE); + } - registerForContextMenu(waypointView); - waypointView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - openContextMenu(v); - } - }); + // title + final TextView nameView = holder.nameView; + if (StringUtils.isNotBlank(wpt.getName())) { + nameView.setText(StringEscapeUtils.unescapeHtml4(wpt.getName())); + } else if (null != wpt.getCoords()) { + nameView.setText(wpt.getCoords().toString()); + } else { + nameView.setText(res.getString(R.string.waypoint)); + } + wpt.setIcon(res, nameView); - waypointView.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - EditWaypointActivity.startActivityEditWaypoint(CacheDetailActivity.this, wpt.getId()); - refreshOnResume = true; - return true; - } - }); + // visited + if (wpt.isVisited()) { + final TypedValue a = new TypedValue(); + getTheme().resolveAttribute(R.attr.text_color_grey, a, true); + if (a.type >= TypedValue.TYPE_FIRST_COLOR_INT && a.type <= TypedValue.TYPE_LAST_COLOR_INT) { + // really should be just a color! + nameView.setTextColor(a.data); + } + } - waypoints.addView(waypointView); + // note + if (StringUtils.isNotBlank(wpt.getNote())) { + final TextView noteView = holder.noteView; + noteView.setVisibility(View.VISIBLE); + if (TextUtils.containsHtml(wpt.getNote())) { + noteView.setText(Html.fromHtml(wpt.getNote()), TextView.BufferType.SPANNABLE); + } + else { + noteView.setText(wpt.getNote()); + } } - final Button addWaypoint = (Button) view.findViewById(R.id.add_waypoint); - addWaypoint.setClickable(true); - addWaypoint.setOnClickListener(new View.OnClickListener() { + final View wpNavView = holder.wpNavView; + wpNavView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + NavigationAppFactory.startDefaultNavigationApplication(1, CacheDetailActivity.this, wpt); + } + }); + wpNavView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + NavigationAppFactory.startDefaultNavigationApplication(2, CacheDetailActivity.this, wpt); + return true; + } + }); + registerForContextMenu(rowView); + rowView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - EditWaypointActivity.startActivityAddWaypoint(CacheDetailActivity.this, cache); - refreshOnResume = true; + selectedWaypoint = wpt; + openContextMenu(v); } }); - return view; + rowView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + selectedWaypoint = wpt; + EditWaypointActivity.startActivityEditWaypoint(CacheDetailActivity.this, cache, wpt.getId()); + refreshOnResume = true; + return true; + } + }); } } @@ -2197,7 +2059,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc cache.setCoords(wpt.getCoords()); cache.setUserModifiedCoords(false); cache.deleteWaypointForce(wpt); - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); handler.sendEmptyMessage(LOCAL); } @@ -2491,7 +2353,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc cache.parseWaypointsFromNote(); TextView personalNoteView = (TextView) activity.findViewById(R.id.personalnote); setPersonalNote(personalNoteView, note); - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); activity.notifyDataSetChanged(); } }; @@ -2509,4 +2371,19 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc personalNoteView.setVisibility(View.GONE); } } + + @Override + public void navigateTo() { + startDefaultNavigation(); + } + + @Override + public void showNavigationMenu() { + NavigationAppFactory.showNavigationMenu(this, cache, null, null); + } + + @Override + public void cachesAround() { + CacheListActivity.startActivityCoordinates(this, cache.getCoords()); + } } diff --git a/main/src/cgeo/geocaching/cgeocaches.java b/main/src/cgeo/geocaching/CacheListActivity.java index 4ffe7ed..59f7297 100644 --- a/main/src/cgeo/geocaching/cgeocaches.java +++ b/main/src/cgeo/geocaching/CacheListActivity.java @@ -16,6 +16,7 @@ import cgeo.geocaching.files.GPXImporter; import cgeo.geocaching.filter.FilterUserInterface; import cgeo.geocaching.filter.IFilter; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.loaders.AbstractSearchLoader; import cgeo.geocaching.loaders.AbstractSearchLoader.CacheListLoaderType; import cgeo.geocaching.loaders.AddressGeocacheListLoader; @@ -46,8 +47,10 @@ import cgeo.geocaching.utils.RunnableWithArgument; import ch.boye.httpclientandroidlib.HttpResponse; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.app.AlertDialog; import android.app.ProgressDialog; @@ -67,7 +70,6 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; -import android.view.SubMenu; import android.view.View; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.ListView; @@ -81,37 +83,9 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -public class cgeocaches extends AbstractListActivity implements FilteredActivity, LoaderManager.LoaderCallbacks<SearchResult> { +public class CacheListActivity extends AbstractListActivity implements FilteredActivity, LoaderManager.LoaderCallbacks<SearchResult> { private static final int MAX_LIST_ITEMS = 1000; - private static final int MENU_REFRESH_STORED = 2; - private static final int MENU_CACHE_DETAILS = 4; - private static final int MENU_DROP_CACHES = 5; - private static final int MENU_IMPORT_GPX = 6; - private static final int MENU_CREATE_LIST = 7; - private static final int MENU_DROP_LIST = 8; - private static final int MENU_INVERT_SELECTION = 9; - private static final int MENU_SWITCH_LIST = 17; - private static final int MENU_IMPORT_WEB = 21; - private static final int MENU_EXPORT = 22; - private static final int MENU_REMOVE_FROM_HISTORY = 23; - private static final int MENU_DROP_CACHE = 24; - private static final int MENU_MOVE_TO_LIST = 25; - private static final int MENU_REFRESH = 26; - private static final int MENU_SWITCH_SELECT_MODE = 52; - private static final int SUBMENU_SHOW_MAP = 54; - private static final int SUBMENU_MANAGE_LISTS = 55; - private static final int SUBMENU_MANAGE_OFFLINE = 56; - private static final int MENU_SORT = 57; - private static final int SUBMENU_MANAGE_HISTORY = 60; - private static final int MENU_RENAME_LIST = 64; - private static final int MENU_DROP_CACHES_AND_LIST = 65; - private static final int MENU_DEFAULT_NAVIGATION = 66; - private static final int MENU_NAVIGATION = 69; - private static final int MENU_STORE_CACHE = 73; - private static final int MENU_FILTER = 74; - private static final int MENU_DELETE_EVENTS = 75; - private static final int MENU_CLEAR_OFFLINE_LOGS = 76; private static final int MSG_DONE = -1; private static final int MSG_RESTART_GEO_AND_DIR = -2; @@ -153,7 +127,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } if (app.currentGeo().getSpeed() <= 5) { // use compass when speed is lower than 18 km/h) { - final float northHeading = DirectionProvider.getDirectionNow(cgeocaches.this, direction); + final float northHeading = DirectionProvider.getDirectionNow(CacheListActivity.this, direction); adapter.setActualHeading(northHeading); } } @@ -161,10 +135,6 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity }; private ContextMenuInfo lastMenuInfo; private String contextMenuGeocode = ""; - /** - * the navigation menu item for the cache list (not the context menu!), or <code>null</code> - */ - private MenuItem navigationMenu; // FIXME: This method has mostly been replaced by the loaders. But it still contains a license agreement check. public void handleCachesLoaded() { @@ -212,7 +182,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity setAdapterCurrentCoordinates(false); } catch (final Exception e) { showToast(res.getString(R.string.err_detail_cache_find_any)); - Log.e("cgeocaches.loadCachesHandler", e); + Log.e("CacheListActivity.loadCachesHandler", e); hideLoading(); showProgress(false); @@ -225,7 +195,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity hideLoading(); showProgress(false); } catch (final Exception e2) { - Log.e("cgeocaches.loadCachesHandler.2", e2); + Log.e("CacheListActivity.loadCachesHandler.2", e2); } adapter.setSelectMode(false); @@ -233,15 +203,15 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity private final Handler loadCachesHandler = new LoadCachesHandler(this); - private static class LoadCachesHandler extends WeakReferenceHandler<cgeocaches> { + private static class LoadCachesHandler extends WeakReferenceHandler<CacheListActivity> { - protected LoadCachesHandler(cgeocaches activity) { + protected LoadCachesHandler(CacheListActivity activity) { super(activity); } @Override public void handleMessage(Message msg) { - final cgeocaches activity = getActivity(); + final CacheListActivity activity = getActivity(); if (activity == null) { return; } @@ -399,7 +369,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity private AbstractSearchLoader currentLoader; private String newListName = StringUtils.EMPTY; - public cgeocaches() { + public CacheListActivity() { super(true); } @@ -434,7 +404,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity @Override public void onClick(View v) { - selectList(v); + selectList(); } }); @@ -470,7 +440,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity private boolean isConcreteList() { return type == CacheListType.OFFLINE && - (listId == StoredList.STANDARD_LIST_ID || listId >= cgData.customListIdOffset); + (listId == StoredList.STANDARD_LIST_ID || listId >= DataStore.customListIdOffset); } private boolean isInvokedFromAttachment() { @@ -482,7 +452,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity @Override public void run(Integer listId) { - new GPXImporter(cgeocaches.this, listId, importGpxAttachementFinishedHandler).importGPX(); + new GPXImporter(CacheListActivity.this, listId, importGpxAttachementFinishedHandler).importGPX(); switchListById(listId); } }, true, 0); @@ -504,7 +474,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); + final SearchResult newSearch = DataStore.getBatchOfStoredCaches(coords, Settings.getCacheType(), listId); if (newSearch.getTotal() != search.getTotal()) { refreshCurrentList(); } @@ -530,134 +500,83 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity @Override public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, MENU_FILTER, 0, res.getString(R.string.caches_filter)).setIcon(R.drawable.ic_menu_filter); - - if (type != CacheListType.HISTORY) { - menu.add(0, MENU_SORT, 0, res.getString(R.string.caches_sort)).setIcon(R.drawable.ic_menu_sort_alphabetically); - } - - menu.add(0, MENU_SWITCH_SELECT_MODE, 0, res.getString(R.string.caches_select_mode)).setIcon(R.drawable.ic_menu_agenda); - menu.add(0, MENU_INVERT_SELECTION, 0, res.getString(R.string.caches_select_invert)).setIcon(R.drawable.ic_menu_mark); - if (type == CacheListType.OFFLINE) { - final SubMenu subMenu = menu.addSubMenu(0, SUBMENU_MANAGE_OFFLINE, 0, res.getString(R.string.caches_manage)).setIcon(R.drawable.ic_menu_save); - subMenu.add(0, MENU_DROP_CACHES, 0, res.getString(R.string.caches_drop_all)); // delete saved caches - subMenu.add(0, MENU_DROP_CACHES_AND_LIST, 0, res.getString(R.string.caches_drop_all_and_list)); - subMenu.add(0, MENU_REFRESH_STORED, 0, res.getString(R.string.cache_offline_refresh)); // download details for all caches - subMenu.add(0, MENU_MOVE_TO_LIST, 0, res.getString(R.string.cache_menu_move_list)); - subMenu.add(0, MENU_DELETE_EVENTS, 0, res.getString(R.string.caches_delete_events)); - subMenu.add(0, MENU_CLEAR_OFFLINE_LOGS, 0, res.getString(R.string.caches_clear_offlinelogs)); - - //TODO: add submenu/AlertDialog and use R.string.gpx_import_title - subMenu.add(0, MENU_IMPORT_GPX, 0, res.getString(R.string.gpx_import_title)); - if (Settings.getWebDeviceCode() != null) { - subMenu.add(0, MENU_IMPORT_WEB, 0, res.getString(R.string.web_import_title)); - } - - subMenu.add(0, MENU_EXPORT, 0, res.getString(R.string.export)); // export caches - } else { - if (type == CacheListType.HISTORY) { - final SubMenu subMenu = menu.addSubMenu(0, SUBMENU_MANAGE_HISTORY, 0, res.getString(R.string.caches_manage)).setIcon(R.drawable.ic_menu_save); - subMenu.add(0, MENU_EXPORT, 0, res.getString(R.string.export)); // export caches - subMenu.add(0, MENU_REMOVE_FROM_HISTORY, 0, res.getString(R.string.cache_clear_history)); // remove from history - subMenu.add(0, MENU_CLEAR_OFFLINE_LOGS, 0, res.getString(R.string.caches_clear_offlinelogs)); - menu.add(0, MENU_REFRESH_STORED, 0, res.getString(R.string.cache_offline_refresh)).setIcon(R.drawable.ic_menu_set_as); - } else { - menu.add(0, MENU_REFRESH_STORED, 0, res.getString(R.string.caches_store_offline)).setIcon(R.drawable.ic_menu_set_as); // download details for all caches - } - } + getMenuInflater().inflate(R.menu.cache_list_options, menu); - navigationMenu = CacheListAppFactory.addMenuItems(menu, this, res); - - if (type == CacheListType.OFFLINE) { - final SubMenu subMenu = menu.addSubMenu(0, SUBMENU_MANAGE_LISTS, 0, res.getString(R.string.list_menu)).setIcon(R.drawable.ic_menu_more); - subMenu.add(0, MENU_CREATE_LIST, 0, res.getString(R.string.list_menu_create)); - subMenu.add(0, MENU_DROP_LIST, 0, res.getString(R.string.list_menu_drop)); - subMenu.add(0, MENU_RENAME_LIST, 0, res.getString(R.string.list_menu_rename)); - subMenu.add(0, MENU_SWITCH_LIST, 0, res.getString(R.string.list_menu_change)); - } + CacheListAppFactory.addMenuItems(menu, this, res); return true; } private static void setVisible(final Menu menu, final int itemId, final boolean visible) { - final MenuItem item = menu.findItem(itemId); - if (item != null) { - item.setVisible(visible); - } + menu.findItem(itemId).setVisible(visible); } @Override public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); + final boolean isHistory = type == CacheListType.HISTORY; + final boolean isOffline = type == CacheListType.OFFLINE; + final boolean isEmpty = cacheList.isEmpty(); + final boolean isConcrete = isConcreteList(); + try { if (adapter.isSelectMode()) { - menu.findItem(MENU_SWITCH_SELECT_MODE).setTitle(res.getString(R.string.caches_select_mode_exit)) + menu.findItem(R.id.menu_switch_select_mode).setTitle(res.getString(R.string.caches_select_mode_exit)) .setIcon(R.drawable.ic_menu_clear_playlist); - menu.findItem(MENU_INVERT_SELECTION).setVisible(true); } else { - menu.findItem(MENU_SWITCH_SELECT_MODE).setTitle(res.getString(R.string.caches_select_mode)) + menu.findItem(R.id.menu_switch_select_mode).setTitle(res.getString(R.string.caches_select_mode)) .setIcon(R.drawable.ic_menu_agenda); - menu.findItem(MENU_INVERT_SELECTION).setVisible(false); } - - final boolean isEmpty = cacheList.isEmpty(); - final boolean isConcrete = isConcreteList(); - - setVisible(menu, MENU_SWITCH_SELECT_MODE, !isEmpty); - setVisible(menu, SUBMENU_MANAGE_HISTORY, !isEmpty); - setVisible(menu, SUBMENU_SHOW_MAP, !isEmpty); - setVisible(menu, MENU_SORT, !isEmpty); - setVisible(menu, MENU_REFRESH_STORED, !isEmpty && (isConcrete || type != CacheListType.OFFLINE)); - setVisible(menu, MENU_DROP_CACHES, !isEmpty); - setVisible(menu, MENU_DROP_CACHES_AND_LIST, isConcrete && !isEmpty); - setVisible(menu, MENU_DELETE_EVENTS, isConcrete && !isEmpty && containsEvents()); - setVisible(menu, MENU_MOVE_TO_LIST, !isEmpty); - setVisible(menu, MENU_EXPORT, !isEmpty); - setVisible(menu, MENU_REMOVE_FROM_HISTORY, !isEmpty); - setVisible(menu, MENU_CLEAR_OFFLINE_LOGS, !isEmpty && containsOfflineLogs()); - - if (navigationMenu != null) { - navigationMenu.setVisible(!isEmpty); + menu.findItem(R.id.menu_invert_selection).setVisible(adapter.isSelectMode()); + + + setVisible(menu, R.id.menu_switch_select_mode, !isEmpty); + setVisible(menu, R.id.submenu_manage, (isHistory && !isEmpty) || isOffline); + setVisible(menu, R.id.submenu_manage_lists, isOffline); + + setVisible(menu, R.id.menu_sort, !isEmpty && !isHistory); + setVisible(menu, R.id.menu_refresh_stored, !isEmpty && (isConcrete || type != CacheListType.OFFLINE)); + setVisible(menu, R.id.menu_drop_caches, !isEmpty && isOffline); + setVisible(menu, R.id.menu_drop_caches_and_list, isConcrete && !isEmpty && isOffline); + setVisible(menu, R.id.menu_delete_events, isConcrete && !isEmpty && containsEvents()); + setVisible(menu, R.id.menu_move_to_list, isOffline && !isEmpty); + setVisible(menu, R.id.menu_export, !isEmpty && (isHistory || isOffline)); + setVisible(menu, R.id.menu_remove_from_history, !isEmpty && isHistory); + setVisible(menu, R.id.menu_clear_offline_logs, !isEmpty && containsOfflineLogs() && (isHistory || isOffline)); + setVisible(menu, R.id.menu_import_web, isOffline && Settings.getWebDeviceCode() != null); + setVisible(menu, R.id.menu_import_gpx, isOffline); + setVisible(menu, R.id.menu_refresh_stored_top, !isOffline && !isEmpty); + + if (!isOffline && !isHistory) { + menu.findItem(R.id.menu_refresh_stored_top).setTitle(R.string.caches_store_offline); } final boolean hasSelection = adapter != null && adapter.getCheckedCount() > 0; final boolean isNonDefaultList = isConcrete && listId != StoredList.STANDARD_LIST_ID; - if (type == CacheListType.OFFLINE || type == CacheListType.HISTORY) { // only offline list - setMenuItemLabel(menu, MENU_DROP_CACHES, R.string.caches_drop_selected, R.string.caches_drop_all); - setMenuItemLabel(menu, MENU_REFRESH_STORED, R.string.caches_refresh_selected, R.string.caches_refresh_all); - setMenuItemLabel(menu, MENU_MOVE_TO_LIST, R.string.caches_move_selected, R.string.caches_move_all); + if (isOffline || type == CacheListType.HISTORY) { // only offline list + setMenuItemLabel(menu, R.id.menu_drop_caches, R.string.caches_drop_selected, R.string.caches_drop_all); + setMenuItemLabel(menu, R.id.menu_refresh_stored, R.string.caches_refresh_selected, R.string.caches_refresh_all); + setMenuItemLabel(menu, R.id.menu_move_to_list, R.string.caches_move_selected, R.string.caches_move_all); } else { // search and global list (all other than offline and history) - setMenuItemLabel(menu, MENU_REFRESH_STORED, R.string.caches_store_selected, R.string.caches_store_offline); - } - if (type == CacheListType.OFFLINE) { - menu.findItem(MENU_DROP_CACHES_AND_LIST).setVisible(!hasSelection && isNonDefaultList && !adapter.isFiltered()); + setMenuItemLabel(menu, R.id.menu_refresh_stored, R.string.caches_store_selected, R.string.caches_store_offline); } - MenuItem item = menu.findItem(MENU_DROP_LIST); - if (item != null) { - item.setVisible(isNonDefaultList); - } - item = menu.findItem(MENU_RENAME_LIST); - if (item != null) { - item.setVisible(isNonDefaultList); - } + // make combined list deletion only possible when there are no filters, as that leads to confusion for the hidden caches + menu.findItem(R.id.menu_drop_caches_and_list).setVisible(isOffline && !hasSelection && isNonDefaultList && !adapter.isFiltered() && Settings.getCacheType() == CacheType.ALL); - final boolean multipleLists = cgData.getLists().size() >= 2; - item = menu.findItem(MENU_SWITCH_LIST); - if (item != null) { - item.setVisible(multipleLists); - } - item = menu.findItem(MENU_MOVE_TO_LIST); - if (item != null) { - item.setVisible(!isEmpty); - } + menu.findItem(R.id.menu_drop_list).setVisible(isNonDefaultList); + menu.findItem(R.id.menu_rename_list).setVisible(isNonDefaultList); - setMenuItemLabel(menu, MENU_REMOVE_FROM_HISTORY, R.string.cache_remove_from_history, R.string.cache_clear_history); - setMenuItemLabel(menu, MENU_EXPORT, R.string.export, R.string.export); + final boolean multipleLists = DataStore.getLists().size() >= 2; + menu.findItem(R.id.menu_switch_list).setVisible(multipleLists); + menu.findItem(R.id.menu_move_to_list).setVisible(!isEmpty); + + setMenuItemLabel(menu, R.id.menu_remove_from_history, R.string.cache_remove_from_history, R.string.cache_clear_history); + setMenuItemLabel(menu, R.id.menu_export, R.string.export, R.string.export); } catch (final RuntimeException e) { - Log.e("cgeocaches.onPrepareOptionsMenu", e); + Log.e("CacheListActivity.onPrepareOptionsMenu", e); } return true; @@ -696,51 +615,51 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity @Override public boolean onOptionsItemSelected(MenuItem item) { - final int itemId = item.getItemId(); - switch (itemId) { - case MENU_SWITCH_SELECT_MODE: + switch (item.getItemId()) { + case R.id.menu_switch_select_mode: adapter.switchSelectMode(); invalidateOptionsMenuCompatible(); return true; - case MENU_REFRESH_STORED: + case R.id.menu_refresh_stored_top: + case R.id.menu_refresh_stored: refreshStored(adapter.getCheckedOrAllCaches()); invalidateOptionsMenuCompatible(); return true; - case MENU_DROP_CACHES: + case R.id.menu_drop_caches: dropStored(false); invalidateOptionsMenuCompatible(); return false; - case MENU_DROP_CACHES_AND_LIST: + case R.id.menu_drop_caches_and_list: dropStored(true); invalidateOptionsMenuCompatible(); return true; - case MENU_IMPORT_GPX: + case R.id.menu_import_gpx: importGpx(); invalidateOptionsMenuCompatible(); return false; - case MENU_CREATE_LIST: + case R.id.menu_create_list: new StoredList.UserInterface(this).promptForListCreation(getListSwitchingRunnable(), newListName); invalidateOptionsMenuCompatible(); return false; - case MENU_DROP_LIST: + case R.id.menu_drop_list: removeList(true); invalidateOptionsMenuCompatible(); return false; - case MENU_RENAME_LIST: + case R.id.menu_rename_list: renameList(); return false; - case MENU_INVERT_SELECTION: + case R.id.menu_invert_selection: adapter.invertSelection(); invalidateOptionsMenuCompatible(); return false; - case MENU_SWITCH_LIST: - selectList(null); + case R.id.menu_switch_list: + selectList(); invalidateOptionsMenuCompatible(); return false; - case MENU_FILTER: + case R.id.menu_filter: showFilterMenu(null); return true; - case MENU_SORT: + case R.id.menu_sort: final CacheComparator oldComparator = adapter.getCacheComparator(); new ComparatorUserInterface(this).selectComparator(oldComparator, new RunnableWithArgument<CacheComparator>() { @Override @@ -757,25 +676,25 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } }); return true; - case MENU_IMPORT_WEB: + case R.id.menu_import_web: importWeb(); return false; - case MENU_EXPORT: + case R.id.menu_export: ExportFactory.showExportMenu(adapter.getCheckedOrAllCaches(), this); return false; - case MENU_REMOVE_FROM_HISTORY: + case R.id.menu_remove_from_history: removeFromHistoryCheck(); invalidateOptionsMenuCompatible(); return false; - case MENU_MOVE_TO_LIST: + case R.id.menu_move_to_list: moveCachesToOtherList(); invalidateOptionsMenuCompatible(); return true; - case MENU_DELETE_EVENTS: + case R.id.menu_delete_events: deletePastEvents(); invalidateOptionsMenuCompatible(); return true; - case MENU_CLEAR_OFFLINE_LOGS: + case R.id.menu_clear_offline_logs: clearOfflineLogs(); invalidateOptionsMenuCompatible(); return true; @@ -833,7 +752,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity try { adapterInfo = (AdapterContextMenuInfo) info; } catch (final Exception e) { - Log.w("cgeocaches.onCreateContextMenu", e); + Log.w("CacheListActivity.onCreateContextMenu", e); } if (adapterInfo == null || adapterInfo.position >= adapter.getCount()) { @@ -845,21 +764,21 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity contextMenuGeocode = cache.getGeocode(); - if (cache.getCoords() != null) { - menu.add(0, MENU_DEFAULT_NAVIGATION, 0, NavigationAppFactory.getDefaultNavigationApplication().getName()); - menu.add(1, MENU_NAVIGATION, 0, res.getString(R.string.cache_menu_navigate)).setIcon(R.drawable.ic_menu_mapmode); - LoggingUI.addMenuItems(this, menu, cache); - menu.add(0, MENU_CACHE_DETAILS, 0, res.getString(R.string.cache_menu_details)); - } - if (cache.isOffline()) { - menu.add(0, MENU_DROP_CACHE, 0, res.getString(R.string.cache_offline_drop)); - menu.add(0, MENU_MOVE_TO_LIST, 0, res.getString(R.string.cache_menu_move_list)); - menu.add(0, MENU_EXPORT, 0, res.getString(R.string.export)); - menu.add(0, MENU_REFRESH, 0, res.getString(R.string.cache_menu_refresh)); - } - else { - menu.add(0, MENU_STORE_CACHE, 0, res.getString(R.string.cache_offline_store)); - } + getMenuInflater().inflate(R.menu.cache_list_context, menu); + + menu.findItem(R.id.menu_default_navigation).setTitle(NavigationAppFactory.getDefaultNavigationApplication().getName()); + final boolean hasCoords = cache.getCoords() != null; + menu.findItem(R.id.menu_default_navigation).setVisible(hasCoords); + menu.findItem(R.id.menu_navigate).setVisible(hasCoords); + menu.findItem(R.id.menu_cache_details).setVisible(hasCoords); + final boolean isOffline = cache.isOffline(); + menu.findItem(R.id.menu_drop_cache).setVisible(isOffline); + menu.findItem(R.id.menu_move_to_list).setVisible(isOffline); + menu.findItem(R.id.menu_export).setVisible(isOffline); + menu.findItem(R.id.menu_refresh).setVisible(isOffline); + menu.findItem(R.id.menu_store_cache).setVisible(!isOffline); + + LoggingUI.onPrepareOptionsMenu(menu, cache); } private void moveCachesToOtherList() { @@ -867,7 +786,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity @Override public void run(Integer newListId) { - cgData.moveToList(adapter.getCheckedOrAllCaches(), newListId); + DataStore.moveToList(adapter.getCheckedOrAllCaches(), newListId); adapter.setSelectMode(false); refreshCurrentList(); @@ -890,7 +809,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity try { adapterInfo = (AdapterContextMenuInfo) info; } catch (final Exception e) { - Log.w("cgeocaches.onContextItemSelected", e); + Log.w("CacheListActivity.onContextItemSelected", e); } final Geocache cache = adapterInfo != null ? getCacheFromAdapter(adapterInfo) : null; @@ -900,18 +819,17 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity return true; } - final int id = item.getItemId(); - switch (id) { - case MENU_DEFAULT_NAVIGATION: + switch (item.getItemId()) { + case R.id.menu_default_navigation: NavigationAppFactory.startDefaultNavigationApplication(1, this, cache); break; - case MENU_NAVIGATION: + case R.id.menu_navigate: NavigationAppFactory.showNavigationMenu(this, cache, null, null); break; - case MENU_CACHE_DETAILS: + case R.id.menu_cache_details: CacheDetailActivity.startActivity(this, cache.getGeocode(), cache.getName()); break; - case MENU_DROP_CACHE: + case R.id.menu_drop_cache: cache.drop(new Handler() { @Override public void handleMessage(Message msg) { @@ -920,22 +838,22 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } }); break; - case MENU_MOVE_TO_LIST: + case R.id.menu_move_to_list: new StoredList.UserInterface(this).promptForListSelection(R.string.cache_menu_move_list, new RunnableWithArgument<Integer>() { @Override public void run(Integer newListId) { - cgData.moveToList(Collections.singletonList(cache), newListId); + DataStore.moveToList(Collections.singletonList(cache), newListId); adapter.setSelectMode(false); refreshCurrentList(); } }, true, listId, newListName); break; - case MENU_STORE_CACHE: - case MENU_REFRESH: + case R.id.menu_store_cache: + case R.id.menu_refresh: refreshStored(Collections.singletonList(cache)); break; - case MENU_EXPORT: + case R.id.menu_export: ExportFactory.showExportMenu(Collections.singletonList(cache), this); return false; default: @@ -1201,7 +1119,6 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity final private Handler handler; final private int listIdLD; private volatile boolean needToStop = false; - private long last = 0L; final private List<Geocache> caches; public LoadDetailsThread(Handler handlerIn, List<Geocache> caches, int listId) { @@ -1219,21 +1136,14 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity @Override public void run() { removeGeoAndDir(); - - final List<Geocache> cachesWithStaticMaps = new ArrayList<Geocache>(this.caches.size()); - for (final Geocache cache : this.caches) { - if (Settings.isStoreOfflineMaps() && cache.hasStaticMap()) { - cachesWithStaticMaps.add(cache); - continue; - } - if (!refreshCache(cache)) { - // in case of interruption avoid the second loop - cachesWithStaticMaps.clear(); - break; - } - } - - for (final Geocache cache : cachesWithStaticMaps) { + // First refresh caches that do not yet have static maps to get them a chance to get a copy + // before the limit expires, unless we do not want to store offline maps. + final List<Geocache> allCaches = Settings.isStoreOfflineMaps() ? + ListUtils.union(ListUtils.selectRejected(caches, Geocache.hasStaticMap), + ListUtils.select(caches, Geocache.hasStaticMap)) : + caches; + + for (final Geocache cache : allCaches) { if (!refreshCache(cache)) { break; } @@ -1256,38 +1166,16 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity if (needToStop) { throw new InterruptedException("Stopped storing process."); } - - if ((System.currentTimeMillis() - last) < 1500) { - try { - int delay = 1000 + ((Double) (Math.random() * 1000)).intValue() - (int) (System.currentTimeMillis() - last); - if (delay < 0) { - delay = 500; - } - - Log.i("Waiting for next cache " + delay + " ms"); - } catch (final Exception e) { - Log.e("cgeocaches.LoadDetailsThread.sleep", e); - } - } - - if (needToStop) { - throw new InterruptedException("Stopped storing process."); - } - detailProgress++; cache.refresh(listIdLD, null); - handler.sendEmptyMessage(cacheList.indexOf(cache)); - - yield(); } catch (final InterruptedException e) { Log.i(e.getMessage()); return false; } catch (final Exception e) { - Log.e("cgeocaches.LoadDetailsThread", e); + Log.e("CacheListActivity.LoadDetailsThread", e); } - last = System.currentTimeMillis(); return true; } } @@ -1327,7 +1215,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity if (responseFromWeb != null && responseFromWeb.getStatusLine().getStatusCode() == 200) { final String response = Network.getResponseData(responseFromWeb); - if (response.length() > 2) { + if (response != null && response.length() > 2) { delay = 1; handler.sendMessage(handler.obtainMessage(1, response)); yield(); @@ -1364,7 +1252,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity times = 0; } } catch (final InterruptedException e) { - Log.e("cgeocaches.LoadFromWebThread.sleep", e); + Log.e("CacheListActivity.LoadFromWebThread.sleep", e); } } @@ -1379,14 +1267,14 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity private final boolean removeListAfterwards; public DropDetailsTask(boolean removeListAfterwards) { - super(cgeocaches.this, null, res.getString(R.string.caches_drop_progress), true); + super(CacheListActivity.this, null, res.getString(R.string.caches_drop_progress), true); this.removeListAfterwards = removeListAfterwards; } @Override protected Void doInBackgroundInternal(Geocache[] caches) { removeGeoAndDir(); - cgData.markDropped(Arrays.asList(caches)); + DataStore.markDropped(Arrays.asList(caches)); startGeoAndDir(); return null; } @@ -1417,7 +1305,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity @Override public void run() { - cgData.clearLogsOffline(selected); + DataStore.clearLogsOffline(selected); handler.sendEmptyMessage(MSG_DONE); } } @@ -1430,7 +1318,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity showFooterLoadingCaches(); listFooter.setOnClickListener(null); - getSupportLoaderManager().restartLoader(CacheListLoaderType.NEXT_PAGE.ordinal(), null, cgeocaches.this); + getSupportLoaderManager().restartLoader(CacheListLoaderType.NEXT_PAGE.ordinal(), null, CacheListActivity.this); } } @@ -1443,17 +1331,14 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } } - /** - * @param view - * unused here but needed since this method is referenced from XML layout - */ - public void selectList(View view) { - if (type != CacheListType.OFFLINE) { + public void selectList() { + if (!type.canSwitch) { return; } new StoredList.UserInterface(this).promptForListSelection(R.string.list_title, getListSwitchingRunnable()); } + @NonNull private RunnableWithArgument<Integer> getListSwitchingRunnable() { return new RunnableWithArgument<Integer>() { @@ -1469,7 +1354,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity return; } - final StoredList list = cgData.getList(id); + final StoredList list = DataStore.getList(id); if (list == null) { return; } @@ -1481,7 +1366,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity showProgress(true); showFooterLoadingCaches(); - cgData.moveToList(adapter.getCheckedCaches(), listId); + DataStore.moveToList(adapter.getCheckedCaches(), listId); currentLoader = (OfflineGeocacheListLoader) getSupportLoaderManager().initLoader(CacheListType.OFFLINE.ordinal(), new Bundle(), this); currentLoader.reset(); @@ -1504,7 +1389,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } private void removeListInternal() { - if (cgData.removeList(listId)) { + if (DataStore.removeList(listId)) { showToast(res.getString(R.string.list_dialog_remove_ok)); switchListById(StoredList.STANDARD_LIST_ID); } else { @@ -1577,7 +1462,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } public static void startActivityOffline(final Context context) { - final Intent cachesIntent = new Intent(context, cgeocaches.class); + final Intent cachesIntent = new Intent(context, CacheListActivity.class); cachesIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.OFFLINE); context.startActivity(cachesIntent); } @@ -1586,7 +1471,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity if (!isValidUsername(context, userName)) { return; } - final Intent cachesIntent = new Intent(context, cgeocaches.class); + final Intent cachesIntent = new Intent(context, CacheListActivity.class); cachesIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.OWNER); cachesIntent.putExtra(Intents.EXTRA_USERNAME, userName); context.startActivity(cachesIntent); @@ -1594,7 +1479,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity private static boolean isValidUsername(AbstractActivity context, String username) { if (StringUtils.isBlank(username)) { - context.showToast(cgeoapplication.getInstance().getString(R.string.warn_no_username)); + context.showToast(CgeoApplication.getInstance().getString(R.string.warn_no_username)); return false; } return true; @@ -1604,7 +1489,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity if (!isValidUsername(context, userName)) { return; } - final Intent cachesIntent = new Intent(context, cgeocaches.class); + final Intent cachesIntent = new Intent(context, CacheListActivity.class); cachesIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.USERNAME); cachesIntent.putExtra(Intents.EXTRA_USERNAME, userName); context.startActivity(cachesIntent); @@ -1630,20 +1515,20 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity if (!isValidCoords(context, coordsNow)) { return; } - final Intent cachesIntent = new Intent(context, cgeocaches.class); + final Intent cachesIntent = new Intent(context, CacheListActivity.class); cachesIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.NEAREST); cachesIntent.putExtra(Intents.EXTRA_COORDS, coordsNow); context.startActivity(cachesIntent); } public static void startActivityHistory(Context context) { - final Intent cachesIntent = new Intent(context, cgeocaches.class); + final Intent cachesIntent = new Intent(context, CacheListActivity.class); cachesIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.HISTORY); context.startActivity(cachesIntent); } public static void startActivityAddress(final Context context, final Geopoint coords, final String address) { - final Intent addressIntent = new Intent(context, cgeocaches.class); + final Intent addressIntent = new Intent(context, CacheListActivity.class); addressIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.ADDRESS); addressIntent.putExtra(Intents.EXTRA_COORDS, coords); addressIntent.putExtra(Intents.EXTRA_ADDRESS, address); @@ -1654,7 +1539,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity if (!isValidCoords(context, coords)) { return; } - final Intent cachesIntent = new Intent(context, cgeocaches.class); + final Intent cachesIntent = new Intent(context, CacheListActivity.class); cachesIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.COORDINATE); cachesIntent.putExtra(Intents.EXTRA_COORDS, coords); context.startActivity(cachesIntent); @@ -1662,7 +1547,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity private static boolean isValidCoords(AbstractActivity context, Geopoint coords) { if (coords == null) { - context.showToast(cgeoapplication.getInstance().getString(R.string.warn_no_coordinates)); + context.showToast(CgeoApplication.getInstance().getString(R.string.warn_no_coordinates)); return false; } return true; @@ -1670,34 +1555,35 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity public static void startActivityKeyword(final AbstractActivity context, final String keyword) { if (keyword == null) { - context.showToast(cgeoapplication.getInstance().getString(R.string.warn_no_keyword)); + context.showToast(CgeoApplication.getInstance().getString(R.string.warn_no_keyword)); return; } - final Intent cachesIntent = new Intent(context, cgeocaches.class); + final Intent cachesIntent = new Intent(context, CacheListActivity.class); cachesIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.KEYWORD); cachesIntent.putExtra(Intents.EXTRA_KEYWORD, keyword); context.startActivity(cachesIntent); } - public static void startActivityPocket(final AbstractActivity context, final String guid, final String name) { + public static void startActivityMap(final Context context, final SearchResult search) { + final Intent cachesIntent = new Intent(context, CacheListActivity.class); + cachesIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.MAP); + cachesIntent.putExtra(Intents.EXTRA_SEARCH, search); + context.startActivity(cachesIntent); + } + + public static void startActivityPocket(final AbstractActivity context, final @NonNull PocketQueryList pq) { + final String guid = pq.getGuid(); if (guid == null) { - context.showToast(cgeoapplication.getInstance().getString(R.string.warn_pocket_query_select)); + context.showToast(CgeoApplication.getInstance().getString(R.string.warn_pocket_query_select)); return; } - final Intent cachesIntent = new Intent(context, cgeocaches.class); + final Intent cachesIntent = new Intent(context, CacheListActivity.class); cachesIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.POCKET); - cachesIntent.putExtra(Intents.EXTRA_NAME, name); + cachesIntent.putExtra(Intents.EXTRA_NAME, pq.getName()); cachesIntent.putExtra(Intents.EXTRA_POCKET_GUID, guid); context.startActivity(cachesIntent); } - public static void startActivityMap(final Context context, final SearchResult search) { - final Intent cachesIntent = new Intent(context, cgeocaches.class); - cachesIntent.putExtra(Intents.EXTRA_LIST_TYPE, CacheListType.MAP); - cachesIntent.putExtra(Intents.EXTRA_SEARCH, search); - context.startActivity(cachesIntent); - } - // Loaders @Override @@ -1714,7 +1600,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity listId = StoredList.STANDARD_LIST_ID; title = res.getString(R.string.stored_caches_button); } else { - final StoredList list = cgData.getList(listId); + final StoredList list = DataStore.getList(listId); // list.id may be different if listId was not valid listId = list.id; title = list.title; @@ -1784,6 +1670,8 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity title = pocket_name; loader = new PocketGeocacheListLoader(app, guid); break; + default: + throw new IllegalStateException(); } setTitle(title); showProgress(true); diff --git a/main/src/cgeo/geocaching/CacheMenuHandler.java b/main/src/cgeo/geocaching/CacheMenuHandler.java new file mode 100644 index 0000000..9326a73 --- /dev/null +++ b/main/src/cgeo/geocaching/CacheMenuHandler.java @@ -0,0 +1,126 @@ +package cgeo.geocaching; + +import cgeo.calendar.ICalendar; +import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; +import cgeo.geocaching.geopoint.GeopointFormatter; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.ui.AbstractUIFactory; +import cgeo.geocaching.utils.ProcessUtils; + +import org.apache.commons.lang3.StringUtils; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.view.Menu; +import android.view.MenuItem; + +/** + * Shared menu handling for all activities having menu items related to a cache. + * + */ +public class CacheMenuHandler extends AbstractUIFactory { + + /** + * Methods to be implemented by the activity to react to the cache menu selections. + * + */ + protected interface ActivityInterface { + public void navigateTo(); + + public void showNavigationMenu(); + + public void cachesAround(); + + } + + public static boolean onMenuItemSelected(MenuItem item, CacheMenuHandler.ActivityInterface activityInterface, Geocache cache) { + assert activityInterface instanceof Activity; + final Activity activity = (Activity) activityInterface; + switch (item.getItemId()) { + case R.id.menu_default_navigation: + activityInterface.navigateTo(); + return true; + case R.id.menu_navigate: + activityInterface.showNavigationMenu(); + return true; + case R.id.menu_caches_around: + activityInterface.cachesAround(); + return true; + case R.id.menu_show_in_browser: + cache.openInBrowser(activity); + return true; + case R.id.menu_share: + cache.shareCache(activity, res); + return true; + case R.id.menu_calendar: + addToCalendarWithIntent(activity, cache); + return true; + default: + return false; + } + } + + public static void onPrepareOptionsMenu(final Menu menu, final Geocache cache) { + final boolean hasCoords = cache.getCoords() != null; + menu.findItem(R.id.menu_default_navigation).setVisible(hasCoords); + menu.findItem(R.id.menu_navigate).setVisible(hasCoords); + menu.findItem(R.id.menu_caches_around).setVisible(hasCoords && cache.supportsCachesAround()); + menu.findItem(R.id.menu_calendar).setVisible(cache.canBeAddedToCalendar()); + menu.findItem(R.id.menu_show_in_browser).setVisible(cache.canOpenInBrowser()); + + menu.findItem(R.id.menu_default_navigation).setTitle(NavigationAppFactory.getDefaultNavigationApplication().getName()); + } + + public static void addMenuItems(Activity activity, Menu menu, Geocache cache) { + activity.getMenuInflater().inflate(R.menu.cache_options, menu); + onPrepareOptionsMenu(menu, cache); + } + + private static void addToCalendarWithIntent(final Activity activity, final Geocache cache) { + final boolean calendarAddOnAvailable = ProcessUtils.isIntentAvailable(ICalendar.INTENT, Uri.parse(ICalendar.URI_SCHEME + "://" + ICalendar.URI_HOST)); + + if (calendarAddOnAvailable) { + final Parameters params = new Parameters( + ICalendar.PARAM_NAME, cache.getName(), + ICalendar.PARAM_NOTE, StringUtils.defaultString(cache.getPersonalNote()), + ICalendar.PARAM_HIDDEN_DATE, String.valueOf(cache.getHiddenDate().getTime()), + ICalendar.PARAM_URL, StringUtils.defaultString(cache.getUrl()), + ICalendar.PARAM_COORDS, cache.getCoords() == null ? "" : cache.getCoords().format(GeopointFormatter.Format.LAT_LON_DECMINUTE_RAW), + ICalendar.PARAM_LOCATION, StringUtils.defaultString(cache.getLocation()), + ICalendar.PARAM_SHORT_DESC, StringUtils.defaultString(cache.getShortDescription()), + ICalendar.PARAM_START_TIME_MINUTES, StringUtils.defaultString(cache.guessEventTimeMinutes()) + ); + + activity.startActivity(new Intent(ICalendar.INTENT, + Uri.parse(ICalendar.URI_SCHEME + "://" + ICalendar.URI_HOST + "?" + params.toString()))); + } else { + // Inform user the calendar add-on is not installed and let them get it from Google Play + new AlertDialog.Builder(activity) + .setTitle(res.getString(R.string.addon_missing_title)) + .setMessage(new StringBuilder(res.getString(R.string.helper_calendar_missing)) + .append(' ') + .append(res.getString(R.string.addon_download_prompt)) + .toString()) + .setPositiveButton(activity.getString(android.R.string.yes), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(ICalendar.CALENDAR_ADDON_URI)); + activity.startActivity(intent); + } + }) + .setNegativeButton(activity.getString(android.R.string.no), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }) + .create() + .show(); + } + } + +} diff --git a/main/src/cgeo/geocaching/CachePopup.java b/main/src/cgeo/geocaching/CachePopup.java index 873801e..020c604 100644 --- a/main/src/cgeo/geocaching/CachePopup.java +++ b/main/src/cgeo/geocaching/CachePopup.java @@ -3,6 +3,7 @@ package cgeo.geocaching; import cgeo.geocaching.activity.Progress; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.network.Network; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.CacheDetailsCreator; @@ -70,7 +71,7 @@ public class CachePopup extends AbstractPopupActivity { } @Override - protected void showNavigationMenu() { + public void showNavigationMenu() { NavigationAppFactory.showNavigationMenu(this, cache, null, null); } @@ -215,7 +216,7 @@ public class CachePopup extends AbstractPopupActivity { } @Override - protected void navigateTo() { + public void navigateTo() { NavigationAppFactory.startDefaultNavigationApplication(1, this, cache); } diff --git a/main/src/cgeo/geocaching/cgeoapplication.java b/main/src/cgeo/geocaching/CgeoApplication.java index 5a793f5..2c419cf 100644 --- a/main/src/cgeo/geocaching/cgeoapplication.java +++ b/main/src/cgeo/geocaching/CgeoApplication.java @@ -9,12 +9,10 @@ import android.app.Activity; import android.app.Application; import android.app.ProgressDialog; import android.content.res.Resources; -import android.os.Handler; -import android.os.Message; import java.util.concurrent.atomic.AtomicBoolean; -public class cgeoapplication extends Application { +public class CgeoApplication extends Application { private volatile GeoDataProvider geo; private volatile DirectionProvider dir; @@ -22,17 +20,17 @@ public class cgeoapplication extends Application { public boolean showLoginToast = true; //login toast shown just once. private boolean liveMapHintShown = false; // livemap hint has been shown final private StatusUpdater statusUpdater = new StatusUpdater(); - private static cgeoapplication instance; + private static CgeoApplication instance; - public cgeoapplication() { + public CgeoApplication() { setInstance(this); } - private static void setInstance(final cgeoapplication application) { + private static void setInstance(final CgeoApplication application) { instance = application; } - public static cgeoapplication getInstance() { + public static CgeoApplication getInstance() { return instance; } @@ -46,15 +44,15 @@ public class cgeoapplication extends Application { @Override public void onLowMemory() { Log.i("Cleaning applications cache."); - cgData.removeAllFromCache(); + DataStore.removeAllFromCache(); } @Override public void onTerminate() { Log.d("Terminating c:geoโฆ"); - cgData.clean(); - cgData.closeDb(); + DataStore.clean(); + DataStore.closeDb(); super.onTerminate(); } @@ -69,24 +67,21 @@ public class cgeoapplication extends Application { final Resources res = this.getResources(); final ProgressDialog dialog = ProgressDialog.show(fromActivity, res.getString(R.string.init_dbmove_dbmove), res.getString(R.string.init_dbmove_running), true, false); final AtomicBoolean atomic = new AtomicBoolean(false); - Thread moveThread = new Thread() { - final Handler handler = new Handler() { - @Override - public void handleMessage(Message msg) { - dialog.dismiss(); - boolean success = atomic.get(); - String message = success ? res.getString(R.string.init_dbmove_success) : res.getString(R.string.init_dbmove_failed); - ActivityMixin.helpDialog(fromActivity, res.getString(R.string.init_dbmove_dbmove), message); - } - }; - + new Thread() { @Override public void run() { - atomic.set(cgData.moveDatabase()); - handler.sendMessage(handler.obtainMessage()); + atomic.set(DataStore.moveDatabase()); + fromActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + dialog.dismiss(); + boolean success = atomic.get(); + String message = success ? res.getString(R.string.init_dbmove_success) : res.getString(R.string.init_dbmove_failed); + ActivityMixin.helpDialog(fromActivity, res.getString(R.string.init_dbmove_dbmove), message); + } + }); } - }; - moveThread.start(); + }.start(); } /** diff --git a/main/src/cgeo/geocaching/CompassActivity.java b/main/src/cgeo/geocaching/CompassActivity.java index c8579e1..a176b7d 100644 --- a/main/src/cgeo/geocaching/CompassActivity.java +++ b/main/src/cgeo/geocaching/CompassActivity.java @@ -4,16 +4,20 @@ import butterknife.InjectView; import butterknife.Views; import cgeo.geocaching.activity.AbstractActivity; +import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.maps.CGeoMap; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.speech.SpeechService; import cgeo.geocaching.ui.CompassView; +import cgeo.geocaching.ui.Formatter; +import cgeo.geocaching.ui.LoggingUI; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.Nullable; import android.content.Context; import android.content.Intent; @@ -34,6 +38,8 @@ import java.util.List; public class CompassActivity extends AbstractActivity { + private static final int COORDINATES_OFFSET = 10; + @InjectView(R.id.nav_type) protected TextView navType; @InjectView(R.id.nav_accuracy) protected TextView navAccuracy; @InjectView(R.id.nav_satellites) protected TextView navSatellites; @@ -49,8 +55,11 @@ public class CompassActivity extends AbstractActivity { private static final String EXTRAS_GEOCODE = "geocode"; private static final String EXTRAS_CACHE_INFO = "cacheinfo"; private static final List<IWaypoint> coordinates = new ArrayList<IWaypoint>(); - private static final int COORDINATES_OFFSET = 10; - private static final int REQUEST_TTS_DATA_CHECK = 1; + + /** + * Destination of the compass, or null (if the compass is used for a waypoint only). + */ + private @Nullable Geocache cache = null; private Geopoint dstCoords = null; private float cacheHeading = 0; private String title = null; @@ -70,7 +79,11 @@ public class CompassActivity extends AbstractActivity { // get parameters Bundle extras = getIntent().getExtras(); if (extras != null) { - title = extras.getString(EXTRAS_GEOCODE); + final String geocode = extras.getString(EXTRAS_GEOCODE); + if (StringUtils.isNotEmpty(geocode)) { + cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + } + title = geocode; final String name = extras.getString(EXTRAS_NAME); dstCoords = extras.getParcelable(EXTRAS_COORDS); info = extras.getString(EXTRAS_CACHE_INFO); @@ -105,7 +118,7 @@ public class CompassActivity extends AbstractActivity { public void onResume() { super.onResume(); - // sensor & geolocation manager + // sensor and geolocation manager geoDirHandler.startGeoAndDir(); } @@ -134,7 +147,7 @@ public class CompassActivity extends AbstractActivity { setCacheInfo(); // Force a refresh of location and direction when data is available. - final cgeoapplication app = cgeoapplication.getInstance(); + final CgeoApplication app = CgeoApplication.getInstance(); final IGeoData geo = app.currentGeo(); if (geo != null) { geoDirHandler.update(geo); @@ -158,6 +171,9 @@ public class CompassActivity extends AbstractActivity { } else { menu.findItem(R.id.menu_select_destination).setVisible(false); } + if (cache != null) { + LoggingUI.addMenuItems(this, menu, cache); + } return true; } @@ -200,6 +216,9 @@ public class CompassActivity extends AbstractActivity { SpeechService.stopService(this); return true; default: + if (LoggingUI.onMenuItemSelected(item, this, cache)) { + return true; + } int coordinatesIndex = id - COORDINATES_OFFSET; if (coordinatesIndex >= 0 && coordinatesIndex < coordinates.size()) { final IWaypoint coordinate = coordinates.get(coordinatesIndex); @@ -326,4 +345,9 @@ public class CompassActivity extends AbstractActivity { CompassActivity.startActivity(context, geocode, displayedName, coords, coordinatesWithType, null); } + public static void startActivity(final Context context, final Geocache cache) { + startActivity(context, cache.getGeocode(), cache.getName(), cache.getCoords(), null, + Formatter.formatCacheInfoShort(cache)); + } + } diff --git a/main/src/cgeo/geocaching/cgData.java b/main/src/cgeo/geocaching/DataStore.java index a925597..7c19a83 100644 --- a/main/src/cgeo/geocaching/cgData.java +++ b/main/src/cgeo/geocaching/DataStore.java @@ -13,12 +13,15 @@ import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.files.LocalStorage; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.list.AbstractList; +import cgeo.geocaching.list.PseudoList; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.Log; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.MapUtils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNull; @@ -52,9 +55,9 @@ import java.util.Map.Entry; import java.util.Set; import java.util.regex.Pattern; -public class cgData { +public class DataStore { - private cgData() { + private DataStore() { // utility class } @@ -294,10 +297,10 @@ public class cgData { } try { - final DbHelper dbHelper = new DbHelper(new DBContext(cgeoapplication.getInstance())); + final DbHelper dbHelper = new DbHelper(new DBContext(CgeoApplication.getInstance())); database = dbHelper.getWritableDatabase(); } catch (Exception e) { - Log.e("cgData.init: unable to open database for R/W", e); + Log.e("DataStore.init: unable to open database for R/W", e); } } @@ -757,7 +760,7 @@ public class cgData { // to NPE traces. final int staleHistorySearches = db.delete(dbTableSearchDestionationHistory, "date is null", null); if (staleHistorySearches > 0) { - Log.w(String.format(Locale.getDefault(), "cgData.dbHelper.onOpen: removed %d bad search history entries", staleHistorySearches)); + Log.w(String.format(Locale.getDefault(), "DataStore.dbHelper.onOpen: removed %d bad search history entries", staleHistorySearches)); } } @@ -860,7 +863,7 @@ public class cgData { return getFirstColumn(cursor); } catch (final Exception e) { - Log.e("cgData.allDetailedThere", e); + Log.e("DataStore.allDetailedThere", e); return new String[0]; } } @@ -907,7 +910,7 @@ public class cgData { cursor.close(); } catch (final Exception e) { - Log.e("cgData.isThere", e); + Log.e("DataStore.isThere", e); } if (detailed && dataDetailed == 0) { @@ -954,7 +957,7 @@ public class cgData { } catch (SQLiteDoneException e) { // Do nothing, it only means we have no information on the cache } catch (Exception e) { - Log.e("cgData.isOffline", e); + Log.e("DataStore.isOffline", e); } return false; @@ -975,7 +978,7 @@ public class cgData { } catch (SQLiteDoneException e) { // Do nothing, it only means we have no information on the cache } catch (Exception e) { - Log.e("cgData.getGeocodeForGuid", e); + Log.e("DataStore.getGeocodeForGuid", e); } return null; @@ -996,7 +999,7 @@ public class cgData { } catch (SQLiteDoneException e) { // Do nothing, it only means we have no information on the cache } catch (Exception e) { - Log.e("cgData.getCacheidForGeocode", e); + Log.e("DataStore.getCacheidForGeocode", e); } return null; @@ -1497,7 +1500,7 @@ public class cgData { } if (remaining.size() >= 1) { - Log.d("cgData.loadCaches(" + remaining.toString() + ") returned no results"); + Log.d("DataStore.loadCaches(" + remaining.toString() + ") returned no results"); } return result; } @@ -1528,7 +1531,7 @@ public class cgData { } query.append(" WHERE ").append(dbTableCaches).append('.'); - query.append(cgData.whereGeocodeIn(geocodes)); + query.append(DataStore.whereGeocodeIn(geocodes)); Cursor cursor = database.rawQuery(query.toString(), null); try { @@ -1536,7 +1539,7 @@ public class cgData { int logIndex = -1; while (cursor.moveToNext()) { - Geocache cache = cgData.createCacheFromDatabaseContent(cursor); + Geocache cache = DataStore.createCacheFromDatabaseContent(cursor); if (loadFlags.contains(LoadFlag.LOAD_ATTRIBUTES)) { cache.setAttributes(loadAttributes(cache.getGeocode())); @@ -1717,7 +1720,7 @@ public class cgData { null, "1"); - Log.d("cgData.loadWaypoint(" + id + ")"); + Log.d("DataStore.loadWaypoint(" + id + ")"); final Waypoint waypoint = cursor.moveToFirst() ? createWaypointFromDatabaseContent(cursor) : null; @@ -2019,7 +2022,7 @@ public class cgData { reasonIndex = 1; } String listKey; - if (list == StoredList.ALL_LIST_ID) { + if (list == PseudoList.ALL_LIST.id) { sql.append(" and reason > 0"); listKey = "all_list"; } else { @@ -2033,12 +2036,12 @@ public class cgData { if (cacheType != CacheType.ALL) { compiledStmnt.bindString(1, cacheType.id); } - if (list != StoredList.ALL_LIST_ID) { + if (list != PseudoList.ALL_LIST.id) { compiledStmnt.bindLong(reasonIndex, list); } return (int) compiledStmnt.simpleQueryForLong(); } catch (Exception e) { - Log.e("cgData.loadAllStoredCachesCount", e); + Log.e("DataStore.loadAllStoredCachesCount", e); } return 0; @@ -2050,7 +2053,7 @@ public class cgData { try { return (int) PreparedStatements.getCountHistoryCaches().simpleQueryForLong(); } catch (Exception e) { - Log.e("cgData.getAllHistoricCachesCount", e); + Log.e("DataStore.getAllHistoricCachesCount", e); } return 0; @@ -2076,7 +2079,7 @@ public class cgData { final StringBuilder selection = new StringBuilder(); selection.append("reason "); - selection.append(listId != StoredList.ALL_LIST_ID ? "=" + Math.max(listId, 1) : ">= " + StoredList.STANDARD_LIST_ID); + selection.append(listId != PseudoList.ALL_LIST.id ? "=" + Math.max(listId, 1) : ">= " + StoredList.STANDARD_LIST_ID); selection.append(" and detailed = 1 "); String[] selectionArgs = null; @@ -2115,7 +2118,7 @@ public class cgData { cursor.close(); } catch (final Exception e) { - Log.e("cgData.loadBatchOfStoredGeocodes", e); + Log.e("DataStore.loadBatchOfStoredGeocodes", e); } return geocodes; @@ -2152,7 +2155,7 @@ public class cgData { } cursor.close(); } catch (Exception e) { - Log.e("cgData.loadBatchOfHistoricGeocodes", e); + Log.e("DataStore.loadBatchOfHistoricGeocodes", e); } return geocodes; @@ -2222,7 +2225,7 @@ public class cgData { cursor.close(); } catch (final Exception e) { - Log.e("cgData.loadInViewport", e); + Log.e("DataStore.loadInViewport", e); } return new SearchResult(geocodes); @@ -2286,7 +2289,7 @@ public class cgData { removeCaches(geocodes, LoadFlags.REMOVE_ALL); } } catch (final Exception e) { - Log.w("cgData.clean", e); + Log.w("DataStore.clean", e); } Log.d("Database clean: finished"); @@ -2357,11 +2360,11 @@ public class cgData { public static boolean saveLogOffline(String geocode, Date date, LogType type, String log) { if (StringUtils.isBlank(geocode)) { - Log.e("cgData.saveLogOffline: cannot log a blank geocode"); + Log.e("DataStore.saveLogOffline: cannot log a blank geocode"); return false; } if (LogType.UNKNOWN == type && StringUtils.isBlank(log)) { - Log.e("cgData.saveLogOffline: cannot log an unknown log type and no message"); + Log.e("DataStore.saveLogOffline: cannot log an unknown log type and no message"); return false; } @@ -2452,7 +2455,7 @@ public class cgData { return logCount.simpleQueryForLong() > 0; } } catch (Exception e) { - Log.e("cgData.hasLogOffline", e); + Log.e("DataStore.hasLogOffline", e); } return false; @@ -2480,11 +2483,11 @@ public class cgData { } } - public static @NonNull - List<StoredList> getLists() { + @NonNull + public static List<StoredList> getLists() { init(); - final Resources res = cgeoapplication.getInstance().getResources(); + final Resources res = CgeoApplication.getInstance().getResources(); final List<StoredList> lists = new ArrayList<StoredList>(); lists.add(new StoredList(StoredList.STANDARD_LIST_ID, res.getString(R.string.list_inbox), (int) PreparedStatements.getCountCachesOnStandardList().simpleQueryForLong())); @@ -2500,7 +2503,7 @@ public class cgData { lists.addAll(storedLists); cursor.close(); } catch (final Exception e) { - Log.e("cgData.readLists", e); + Log.e("DataStore.readLists", e); } return lists; } @@ -2536,9 +2539,9 @@ public class cgData { } } - Resources res = cgeoapplication.getInstance().getResources(); - if (id == StoredList.ALL_LIST_ID) { - return new StoredList(StoredList.ALL_LIST_ID, res.getString(R.string.list_all_lists), getAllCachesCount()); + Resources res = CgeoApplication.getInstance().getResources(); + if (id == PseudoList.ALL_LIST.id) { + return new StoredList(PseudoList.ALL_LIST.id, res.getString(R.string.list_all_lists), getAllCachesCount()); } // fall back to standard list in case of invalid list id @@ -2653,7 +2656,11 @@ public class cgData { } public static void moveToList(final List<Geocache> caches, final int listId) { - if (listId == StoredList.ALL_LIST_ID) { + final AbstractList list = AbstractList.getListById(listId); + if (list == null) { + return; + } + if (!list.isConcrete()) { return; } if (caches.isEmpty()) { @@ -2740,7 +2747,7 @@ public class cgData { } catch (SQLiteDoneException e) { // Do nothing, it only means we have no information on the cache } catch (Exception e) { - Log.e("cgData.getCacheDescription", e); + Log.e("DataStore.getCacheDescription", e); } return partial; @@ -2850,7 +2857,7 @@ public class cgData { } public static boolean saveChangedCache(Geocache cache) { - return cgData.saveCache(cache, cache.getStorageLocation().contains(StorageLocation.DATABASE) ? LoadFlags.SAVE_ALL : EnumSet.of(SaveFlag.SAVE_CACHE)); + return DataStore.saveCache(cache, cache.getStorageLocation().contains(StorageLocation.DATABASE) ? LoadFlags.SAVE_ALL : EnumSet.of(SaveFlag.SAVE_CACHE)); } private static class PreparedStatements { @@ -2912,7 +2919,7 @@ public class cgData { } private static SQLiteStatement getLogCountOfGeocode() { - return getStatement("LogCountFromGeocode", "SELECT count(_id) FROM " + cgData.dbTableLogsOffline + " WHERE geocode = ?"); + return getStatement("LogCountFromGeocode", "SELECT count(_id) FROM " + DataStore.dbTableLogsOffline + " WHERE geocode = ?"); } private static SQLiteStatement getCountCachesOnStandardList() { @@ -2962,7 +2969,7 @@ public class cgData { return null; } - return cgData.getBounds(Collections.singleton(geocode)); + return DataStore.getBounds(Collections.singleton(geocode)); } public static void clearVisitDate(String[] selected) { @@ -2970,18 +2977,18 @@ public class cgData { } public static SearchResult getBatchOfStoredCaches(Geopoint coords, CacheType cacheType, int listId) { - final Set<String> geocodes = cgData.loadBatchOfStoredGeocodes(coords, cacheType, listId); - return new SearchResult(geocodes, cgData.getAllStoredCachesCount(cacheType, listId)); + final Set<String> geocodes = DataStore.loadBatchOfStoredGeocodes(coords, cacheType, listId); + return new SearchResult(geocodes, DataStore.getAllStoredCachesCount(cacheType, listId)); } public static SearchResult getHistoryOfCaches(boolean detailedOnly, CacheType cacheType) { - final Set<String> geocodes = cgData.loadBatchOfHistoricGeocodes(detailedOnly, cacheType); - return new SearchResult(geocodes, cgData.getAllHistoryCachesCount()); + final Set<String> geocodes = DataStore.loadBatchOfHistoricGeocodes(detailedOnly, cacheType); + return new SearchResult(geocodes, DataStore.getAllHistoryCachesCount()); } public static boolean saveWaypoint(int id, String geocode, Waypoint waypoint) { - if (cgData.saveWaypointInternal(id, geocode, waypoint)) { - cgData.removeCache(geocode, EnumSet.of(RemoveFlag.REMOVE_CACHE)); + if (DataStore.saveWaypointInternal(id, geocode, waypoint)) { + DataStore.removeCache(geocode, EnumSet.of(RemoveFlag.REMOVE_CACHE)); return true; } return false; @@ -2989,7 +2996,7 @@ public class cgData { public static Set<String> getCachedMissingFromSearch(final SearchResult searchResult, final Set<Tile> tiles, final IConnector connector, final int maxZoom) { - // get cached cgeocaches + // get cached CacheListActivity final Set<String> cachedGeocodes = new HashSet<String>(); for (Tile tile : tiles) { cachedGeocodes.addAll(cacheCache.getInViewport(tile.getViewport(), CacheType.ALL)); diff --git a/main/src/cgeo/geocaching/EditWaypointActivity.java b/main/src/cgeo/geocaching/EditWaypointActivity.java index 59666e9..c31ad40 100644 --- a/main/src/cgeo/geocaching/EditWaypointActivity.java +++ b/main/src/cgeo/geocaching/EditWaypointActivity.java @@ -48,6 +48,8 @@ import java.util.List; @EActivity public class EditWaypointActivity extends AbstractActivity { + private static final ArrayList<WaypointType> POSSIBLE_WAYPOINT_TYPES = new ArrayList<WaypointType>(WaypointType.ALL_TYPES_EXCEPT_OWN_AND_ORIGINAL); + @ViewById(R.id.buttonLatitude) protected Button buttonLat; @ViewById(R.id.buttonLongitude) protected Button buttonLon; @ViewById(R.id.add_waypoint) protected Button addWaypoint; @@ -70,14 +72,21 @@ public class EditWaypointActivity extends AbstractActivity { @Extra(Intents.EXTRA_COUNT) protected int wpCount = 0; @InstanceState protected int waypointTypeSelectorPosition = -1; + private ProgressDialog waitDialog = null; private Waypoint waypoint = null; private String prefix = "OWN"; private String lookup = "---"; private boolean own = true; - ArrayList<WaypointType> wpTypes = null; ArrayList<String> distanceUnits = null; + /** + * {@code true} if the activity is newly created, {@code false} if it is restored from an instance state + */ private boolean initViews = true; + /** + * This is the cache that the waypoint belongs to. + */ + private Geocache cache; private Handler loadWaypointHandler = new Handler() { @@ -107,7 +116,7 @@ public class EditWaypointActivity extends AbstractActivity { note.setText(StringUtils.trimToEmpty(waypoint.getNote())); } } - final Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_ONLY); + final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_ONLY); setCoordsModificationVisibility(ConnectorFactory.getConnector(geocode), cache); } @@ -158,6 +167,10 @@ public class EditWaypointActivity extends AbstractActivity { initViews = false; } + if (geocode != null) { + cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + setCoordsModificationVisibility(ConnectorFactory.getConnector(geocode), cache); + } if (id > 0) { // existing waypoint waitDialog = ProgressDialog.show(this, null, res.getString(R.string.waypoint_loading), true); waitDialog.setCancelable(true); @@ -166,11 +179,6 @@ public class EditWaypointActivity extends AbstractActivity { } else { // new waypoint initializeWaypointTypeSelector(); - - if (geocode != null) { - final Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); - setCoordsModificationVisibility(ConnectorFactory.getConnector(geocode), cache); - } } initializeDistanceUnitSelector(); @@ -202,8 +210,7 @@ public class EditWaypointActivity extends AbstractActivity { } private void initializeWaypointTypeSelector() { - wpTypes = new ArrayList<WaypointType>(WaypointType.ALL_TYPES_EXCEPT_OWN_AND_ORIGINAL); - ArrayAdapter<WaypointType> wpAdapter = new ArrayAdapter<WaypointType>(this, android.R.layout.simple_spinner_item, wpTypes.toArray(new WaypointType[wpTypes.size()])); + ArrayAdapter<WaypointType> wpAdapter = new ArrayAdapter<WaypointType>(this, android.R.layout.simple_spinner_item, POSSIBLE_WAYPOINT_TYPES.toArray(new WaypointType[POSSIBLE_WAYPOINT_TYPES.size()])); wpAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); waypointTypeSelector.setAdapter(wpAdapter); @@ -218,17 +225,30 @@ public class EditWaypointActivity extends AbstractActivity { } }); - if (initViews) { - int typeIndex = -1; - if (waypoint != null) { - typeIndex = wpTypes.indexOf(waypoint.getWaypointType()); - } - waypointTypeSelector.setSelection(typeIndex >= 0 ? typeIndex : wpTypes.indexOf(WaypointType.WAYPOINT)); - } else { - waypointTypeSelector.setSelection(waypointTypeSelectorPosition); + waypointTypeSelector.setSelection(getDefaultWaypointType()); + waypointTypeSelector.setVisibility(View.VISIBLE); + } + + private int getDefaultWaypointType() { + // potentially restore saved instance state + if (waypointTypeSelectorPosition >= 0) { + return waypointTypeSelectorPosition; } - waypointTypeSelector.setVisibility(View.VISIBLE); + // when editing, use the existing type + if (waypoint != null) { + return POSSIBLE_WAYPOINT_TYPES.indexOf(waypoint.getWaypointType()); + } + + // make default for new waypoint depend on cache type + switch (cache.getType()) { + case MYSTERY: + return POSSIBLE_WAYPOINT_TYPES.indexOf(WaypointType.FINAL); + case MULTI: + return POSSIBLE_WAYPOINT_TYPES.indexOf(WaypointType.STAGE); + default: + return POSSIBLE_WAYPOINT_TYPES.indexOf(WaypointType.WAYPOINT); + } } private void initializeDistanceUnitSelector() { @@ -259,7 +279,7 @@ public class EditWaypointActivity extends AbstractActivity { @Override public void run() { try { - waypoint = cgData.loadWaypoint(id); + waypoint = DataStore.loadWaypoint(id); loadWaypointHandler.sendMessage(Message.obtain()); } catch (Exception e) { @@ -278,7 +298,7 @@ public class EditWaypointActivity extends AbstractActivity { } catch (Geopoint.ParseException e) { // button text is blank when creating new waypoint } - Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_WAYPOINTS); + Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_WAYPOINTS); CoordinatesInputDialog coordsDialog = new CoordinatesInputDialog(EditWaypointActivity.this, cache, gp, app.currentGeo()); coordsDialog.setCancelable(true); coordsDialog.setOnCoordinateUpdate(new CoordinatesInputDialog.CoordinateUpdate() { @@ -361,7 +381,7 @@ public class EditWaypointActivity extends AbstractActivity { final String noteText = note.getText().toString().trim(); final Geopoint coordsToSave = coords; final int selectedTypeIndex = waypointTypeSelector.getSelectedItemPosition(); - final WaypointType type = selectedTypeIndex >= 0 ? wpTypes.get(selectedTypeIndex) : waypoint.getWaypointType(); + final WaypointType type = selectedTypeIndex >= 0 ? POSSIBLE_WAYPOINT_TYPES.get(selectedTypeIndex) : waypoint.getWaypointType(); final boolean visited = visitedCheckBox.isChecked(); final ProgressDialog progress = ProgressDialog.show(EditWaypointActivity.this, getString(R.string.cache), getString(R.string.waypoint_being_saved), true); final Handler finishHandler = new Handler() { @@ -416,14 +436,14 @@ public class EditWaypointActivity extends AbstractActivity { waypoint.setVisited(visited); waypoint.setId(id); - Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_WAYPOINTS); + Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_WAYPOINTS); if (cache == null) { finishHandler.sendEmptyMessage(SAVE_ERROR); return null; } Waypoint oldWaypoint = cache.getWaypointById(id); if (cache.addOrChangeWaypoint(waypoint, true)) { - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); if (!StaticMapsProvider.hasAllStaticMapsForWaypoint(geocode, waypoint)) { StaticMapsProvider.removeWpStaticMaps(oldWaypoint, geocode); if (Settings.isStoreOfflineWpMaps()) { @@ -432,13 +452,13 @@ public class EditWaypointActivity extends AbstractActivity { } if (modifyLocal.isChecked() || modifyBoth.isChecked()) { if (!cache.hasUserModifiedCoords()) { - final Waypoint origWaypoint = new Waypoint(cgeoapplication.getInstance().getString(R.string.cache_coordinates_original), WaypointType.ORIGINAL, false); + final Waypoint origWaypoint = new Waypoint(CgeoApplication.getInstance().getString(R.string.cache_coordinates_original), WaypointType.ORIGINAL, false); origWaypoint.setCoords(cache.getCoords()); cache.addOrChangeWaypoint(origWaypoint, false); cache.setUserModifiedCoords(true); } cache.setCoords(waypoint.getCoords()); - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } if (modifyBoth.isChecked() && waypoint.getCoords() != null) { finishHandler.sendEmptyMessage(UPLOAD_START); @@ -468,8 +488,8 @@ public class EditWaypointActivity extends AbstractActivity { return con.supportsOwnCoordinates() && con.uploadModifiedCoordinates(cache, waypointUploaded); } - public static void startActivityEditWaypoint(final Context context, final int waypointId) { - EditWaypointActivity_.intent(context).id(waypointId).start(); + public static void startActivityEditWaypoint(final Context context, final Geocache cache, final int waypointId) { + EditWaypointActivity_.intent(context).geocode(cache.getGeocode()).id(waypointId).start(); } public static void startActivityAddWaypoint(final Context context, final Geocache cache) { diff --git a/main/src/cgeo/geocaching/GeoDataProvider.java b/main/src/cgeo/geocaching/GeoDataProvider.java index f615630..73aefce 100644 --- a/main/src/cgeo/geocaching/GeoDataProvider.java +++ b/main/src/cgeo/geocaching/GeoDataProvider.java @@ -272,6 +272,7 @@ class GeoDataProvider extends MemorySubject<IGeoData> { public void onGpsStatusChanged(final int event) { boolean changed = false; switch (event) { + case GpsStatus.GPS_EVENT_FIRST_FIX: case GpsStatus.GPS_EVENT_SATELLITE_STATUS: { final GpsStatus status = geoManager.getGpsStatus(null); int visible = 0; @@ -303,6 +304,8 @@ class GeoDataProvider extends MemorySubject<IGeoData> { changed = true; } break; + default: + throw new IllegalStateException(); } if (changed) { diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java index 7aceed4..35d6c17 100644 --- a/main/src/cgeo/geocaching/Geocache.java +++ b/main/src/cgeo/geocaching/Geocache.java @@ -1,6 +1,6 @@ package cgeo.geocaching; -import cgeo.geocaching.cgData.StorageLocation; +import cgeo.geocaching.DataStore.StorageLocation; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; @@ -21,6 +21,7 @@ import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.files.GPXParser; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.CancellableHandler; @@ -31,7 +32,8 @@ 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.collections4.CollectionUtils; +import org.apache.commons.collections4.Predicate; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; @@ -112,20 +114,20 @@ public class Geocache implements ICache, IWaypoint { private final List<String> attributes = new LazyInitializedList<String>() { @Override public List<String> call() { - return cgData.loadAttributes(geocode); + return DataStore.loadAttributes(geocode); } }; private final List<Waypoint> waypoints = new LazyInitializedList<Waypoint>() { @Override public List<Waypoint> call() { - return cgData.loadWaypoints(geocode); + return DataStore.loadWaypoints(geocode); } }; private List<Image> spoilers = null; private final List<LogEntry> logs = new LazyInitializedList<LogEntry>() { @Override public List<LogEntry> call() { - return cgData.loadLogs(geocode); + return DataStore.loadLogs(geocode); } }; private List<Trackable> inventory = null; @@ -461,7 +463,7 @@ public class Geocache implements ICache, IWaypoint { public void logOffline(final Activity fromActivity, final LogType logType) { final boolean mustIncludeSignature = StringUtils.isNotBlank(Settings.getSignature()) && Settings.isAutoInsertSignature(); - final String initial = mustIncludeSignature ? LogTemplateProvider.applyTemplates(Settings.getSignature(), new LogContext(this, true)) : ""; + final String initial = mustIncludeSignature ? LogTemplateProvider.applyTemplates(Settings.getSignature(), new LogContext(this, null, true)) : ""; logOffline(fromActivity, initial, Calendar.getInstance(), logType); } @@ -469,12 +471,12 @@ public class Geocache implements ICache, IWaypoint { if (logType == LogType.UNKNOWN) { return; } - final boolean status = cgData.saveLogOffline(geocode, date.getTime(), logType, log); + final boolean status = DataStore.saveLogOffline(geocode, date.getTime(), logType, log); final Resources res = fromActivity.getResources(); if (status) { ActivityMixin.showToast(fromActivity, res.getString(R.string.info_log_saved)); - cgData.saveVisitDate(geocode); + DataStore.saveVisitDate(geocode); logOffline = Boolean.TRUE; notifyChange(); @@ -484,44 +486,12 @@ public class Geocache implements ICache, IWaypoint { } public void clearOfflineLog() { - cgData.clearLogOffline(geocode); + DataStore.clearLogOffline(geocode); notifyChange(); } public List<LogType> getPossibleLogTypes() { - final List<LogType> logTypes = new ArrayList<LogType>(); - if (isEventCache()) { - logTypes.add(LogType.WILL_ATTEND); - logTypes.add(LogType.ATTENDED); - if (isOwner()) { - logTypes.add(LogType.ANNOUNCEMENT); - } - } else if (CacheType.WEBCAM == cacheType.getValue()) { - logTypes.add(LogType.WEBCAM_PHOTO_TAKEN); - } else { - logTypes.add(LogType.FOUND_IT); - } - if (!isEventCache()) { - logTypes.add(LogType.DIDNT_FIND_IT); - } - logTypes.add(LogType.NOTE); - if (!isEventCache()) { - logTypes.add(LogType.NEEDS_MAINTENANCE); - } - if (isOwner()) { - logTypes.add(LogType.OWNER_MAINTENANCE); - if (isDisabled()) { - logTypes.add(LogType.ENABLE_LISTING); - } - else { - logTypes.add(LogType.TEMP_DISABLE_LISTING); - } - logTypes.add(LogType.ARCHIVE); - } - if (!isArchived() && !isOwner()) { - logTypes.add(LogType.NEEDS_ARCHIVE); - } - return logTypes; + return getConnector().getPossibleLogTypes(this); } public void openInBrowser(Activity fromActivity) { @@ -664,7 +634,7 @@ public class Geocache implements ICache, IWaypoint { */ private void initializeCacheTexts() { if (description == null || shortdesc == null || hint == null || location == null) { - final Geocache partial = cgData.loadCacheTexts(this.getGeocode()); + final Geocache partial = DataStore.loadCacheTexts(this.getGeocode()); if (description == null) { setDescription(partial.getDescription()); } @@ -841,7 +811,13 @@ public class Geocache implements ICache, IWaypoint { } public boolean showSize() { - return !((isEventCache() || isVirtual()) && size == CacheSize.NOT_CHOSEN); + if (size == CacheSize.NOT_CHOSEN) { + return false; + } + if (isEventCache() || isVirtual()) { + return false; + } + return true; } public long getUpdated() { @@ -1028,7 +1004,7 @@ public class Geocache implements ICache, IWaypoint { } } } - return saveToDatabase && cgData.saveWaypoints(this); + return saveToDatabase && DataStore.saveWaypoints(this); } /** @@ -1244,7 +1220,7 @@ public class Geocache implements ICache, IWaypoint { // when waypoint was edited, finalDefined may have changed resetFinalDefined(); } - return saveToDatabase && cgData.saveWaypoint(waypoint.getId(), geocode, waypoint); + return saveToDatabase && DataStore.saveWaypoint(waypoint.getId(), geocode, waypoint); } public boolean hasWaypoints() { @@ -1295,9 +1271,9 @@ public class Geocache implements ICache, IWaypoint { final int index = getWaypointIndex(original); final Waypoint copy = new Waypoint(original); copy.setUserDefined(); - copy.setName(cgeoapplication.getInstance().getString(R.string.waypoint_copy_of) + " " + copy.getName()); + copy.setName(CgeoApplication.getInstance().getString(R.string.waypoint_copy_of) + " " + copy.getName()); waypoints.add(index + 1, copy); - return cgData.saveWaypoint(-1, geocode, copy); + return DataStore.saveWaypoint(-1, geocode, copy); } /** @@ -1317,8 +1293,8 @@ public class Geocache implements ICache, IWaypoint { if (waypoint.isUserDefined()) { final int index = getWaypointIndex(waypoint); waypoints.remove(index); - cgData.deleteWaypoint(waypoint.getId()); - cgData.removeCache(geocode, EnumSet.of(RemoveFlag.REMOVE_CACHE)); + DataStore.deleteWaypoint(waypoint.getId()); + DataStore.removeCache(geocode, EnumSet.of(RemoveFlag.REMOVE_CACHE)); // Check status if Final is defined if (waypoint.isFinalWithCoords()) { resetFinalDefined(); @@ -1337,8 +1313,8 @@ public class Geocache implements ICache, IWaypoint { public void deleteWaypointForce(Waypoint waypoint) { final int index = getWaypointIndex(waypoint); waypoints.remove(index); - cgData.deleteWaypoint(waypoint.getId()); - cgData.removeCache(geocode, EnumSet.of(RemoveFlag.REMOVE_CACHE)); + DataStore.deleteWaypoint(waypoint.getId()); + DataStore.removeCache(geocode, EnumSet.of(RemoveFlag.REMOVE_CACHE)); resetFinalDefined(); } @@ -1406,7 +1382,7 @@ public class Geocache implements ICache, IWaypoint { if (point.getLatitudeE6() != 0 && point.getLongitudeE6() != 0 && ((point.getLatitudeE6() % 1000) != 0 || (point.getLongitudeE6() % 1000) != 0) && !hasIdenticalWaypoint(point)) { - final String name = cgeoapplication.getInstance().getString(R.string.cache_personal_note) + " " + count; + final String name = CgeoApplication.getInstance().getString(R.string.cache_personal_note) + " " + count; final String potentialWaypointType = note.substring(Math.max(0, matcher.start() - 15)); final Waypoint waypoint = new Waypoint(name, parseWaypointType(potentialWaypointType), false); waypoint.setCoords(point); @@ -1511,8 +1487,8 @@ public class Geocache implements ICache, IWaypoint { public void drop(Handler handler) { try { - cgData.markDropped(Collections.singletonList(this)); - cgData.removeCache(getGeocode(), EnumSet.of(RemoveFlag.REMOVE_CACHE)); + DataStore.markDropped(Collections.singletonList(this)); + DataStore.removeCache(getGeocode(), EnumSet.of(RemoveFlag.REMOVE_CACHE)); handler.sendMessage(Message.obtain()); } catch (final Exception e) { @@ -1563,7 +1539,7 @@ public class Geocache implements ICache, IWaypoint { } public void refresh(int newListId, CancellableHandler handler) { - cgData.removeCache(geocode, EnumSet.of(RemoveFlag.REMOVE_CACHE)); + DataStore.removeCache(geocode, EnumSet.of(RemoveFlag.REMOVE_CACHE)); storeCache(null, geocode, newListId, true, handler); } @@ -1639,7 +1615,7 @@ public class Geocache implements ICache, IWaypoint { } cache.setListId(listId); - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); if (CancellableHandler.isCancelled(handler)) { return; @@ -1661,9 +1637,9 @@ public class Geocache implements ICache, IWaypoint { return null; } - if (!forceReload && listId == StoredList.TEMPORARY_LIST_ID && (cgData.isOffline(geocode, guid) || cgData.isThere(geocode, guid, true, true))) { + if (!forceReload && listId == StoredList.TEMPORARY_LIST_ID && (DataStore.isOffline(geocode, guid) || DataStore.isThere(geocode, guid, true, true))) { final SearchResult search = new SearchResult(); - final String realGeocode = StringUtils.isNotBlank(geocode) ? geocode : cgData.getGeocodeForGuid(guid); + final String realGeocode = StringUtils.isNotBlank(geocode) ? geocode : DataStore.getGeocodeForGuid(guid); search.addGeocode(realGeocode); return search; } @@ -1693,30 +1669,30 @@ public class Geocache implements ICache, IWaypoint { if (!isEventCache()) { return null; } + + final String hourLocalized = CgeoApplication.getInstance().getString(R.string.cache_time_full_hours); + ArrayList<Pattern> patterns = new ArrayList<Pattern>(); + // 12:34 - final Pattern time = Pattern.compile("\\b(\\d{1,2})\\:(\\d\\d)\\b"); - final MatcherWrapper matcher = new MatcherWrapper(time, getDescription()); - while (matcher.find()) { - try { - final int hours = Integer.valueOf(matcher.group(1)); - final int minutes = Integer.valueOf(matcher.group(2)); - if (hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60) { - return String.valueOf(hours * 60 + minutes); - } - } catch (final NumberFormatException e) { - // cannot happen, but static code analysis doesn't know - } - } - // 12 o'clock - final String hourLocalized = cgeoapplication.getInstance().getString(R.string.cache_time_full_hours); + patterns.add(Pattern.compile("\\b(\\d{1,2})\\:(\\d\\d)\\b")); if (StringUtils.isNotBlank(hourLocalized)) { - final Pattern fullHours = Pattern.compile("\\b(\\d{1,2})\\s+" + Pattern.quote(hourLocalized), Pattern.CASE_INSENSITIVE); - final MatcherWrapper matcherHours = new MatcherWrapper(fullHours, getDescription()); - if (matcherHours.find()) { + // 17 - 20 o'clock + patterns.add(Pattern.compile("\\b(\\d{1,2})(?:\\.00)?" + "\\s*-\\s*" + "(?:\\d{1,2})(?:\\.00)?" + "\\s+" + Pattern.quote(hourLocalized), Pattern.CASE_INSENSITIVE)); + // 12 o'clock, 12.00 o'clock + patterns.add(Pattern.compile("\\b(\\d{1,2})(?:\\.00)?\\s+" + Pattern.quote(hourLocalized), Pattern.CASE_INSENSITIVE)); + } + + for (Pattern pattern : patterns) { + final MatcherWrapper matcher = new MatcherWrapper(pattern, getDescription()); + while (matcher.find()) { try { - final int hours = Integer.valueOf(matcherHours.group(1)); - if (hours >= 0 && hours < 24) { - return String.valueOf(hours * 60); + final int hours = Integer.valueOf(matcher.group(1)); + int minutes = 0; + if (matcher.groupCount() >= 2) { + minutes = Integer.valueOf(matcher.group(2)); + } + if (hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60) { + return String.valueOf(hours * 60 + minutes); } } catch (final NumberFormatException e) { // cannot happen, but static code analysis doesn't know @@ -1735,7 +1711,7 @@ public class Geocache implements ICache, IWaypoint { * @return */ public boolean hasAttribute(CacheAttribute attribute, boolean yes) { - Geocache fullCache = cgData.loadCache(getGeocode(), EnumSet.of(LoadFlag.LOAD_ATTRIBUTES)); + Geocache fullCache = DataStore.loadCache(getGeocode(), EnumSet.of(LoadFlag.LOAD_ATTRIBUTES)); if (fullCache == null) { fullCache = this; } @@ -1746,6 +1722,13 @@ public class Geocache implements ICache, IWaypoint { return StaticMapsProvider.hasStaticMap(this); } + public static final Predicate<Geocache> hasStaticMap = new Predicate<Geocache>() { + @Override + public boolean evaluate(final Geocache cache) { + return cache.hasStaticMap(); + } + }; + public List<Image> getImages() { final List<Image> result = new ArrayList<Image>(); result.addAll(getSpoilers()); diff --git a/main/src/cgeo/geocaching/GpxFileListActivity.java b/main/src/cgeo/geocaching/GpxFileListActivity.java index 8b10d5b..dae52c4 100644 --- a/main/src/cgeo/geocaching/GpxFileListActivity.java +++ b/main/src/cgeo/geocaching/GpxFileListActivity.java @@ -4,6 +4,7 @@ import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector;
import cgeo.geocaching.files.AbstractFileListActivity;
import cgeo.geocaching.files.GPXImporter;
+import cgeo.geocaching.list.StoredList;
import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.ui.GPXListAdapter;
diff --git a/main/src/cgeo/geocaching/ImageSelectActivity.java b/main/src/cgeo/geocaching/ImageSelectActivity.java index e24c34a..790741f 100644 --- a/main/src/cgeo/geocaching/ImageSelectActivity.java +++ b/main/src/cgeo/geocaching/ImageSelectActivity.java @@ -9,6 +9,8 @@ import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import android.content.Intent; import android.database.Cursor; @@ -263,7 +265,8 @@ public class ImageSelectActivity extends AbstractActivity { * @param filePath * @return the scaled image path, or <tt>null</tt> if the image cannot be decoded */ - private String writeScaledImage(final String filePath) { + @Nullable + private String writeScaledImage(@NonNull final String filePath) { scaleChoiceIndex = scaleView.getSelectedItemPosition(); final int maxXY = getResources().getIntArray(R.array.log_image_scale_values)[scaleChoiceIndex]; return ImageUtils.readScaleAndWriteImage(filePath, maxXY); diff --git a/main/src/cgeo/geocaching/ImagesActivity.java b/main/src/cgeo/geocaching/ImagesActivity.java index 0b80d53..29bc8c7 100644 --- a/main/src/cgeo/geocaching/ImagesActivity.java +++ b/main/src/cgeo/geocaching/ImagesActivity.java @@ -5,7 +5,7 @@ import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.ImagesList; import cgeo.geocaching.ui.ImagesList.ImageType; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import android.content.Context; import android.content.Intent; @@ -58,7 +58,7 @@ public class ImagesActivity extends AbstractActivity { return; } - offline = cgData.isOffline(geocode, null) && (imgType == ImageType.SpoilerImages + offline = DataStore.isOffline(geocode, null) && (imgType == ImageType.SpoilerImages || Settings.isStoreLogImages()); } diff --git a/main/src/cgeo/geocaching/LogCacheActivity.java b/main/src/cgeo/geocaching/LogCacheActivity.java index c9baac1..34f036c 100644 --- a/main/src/cgeo/geocaching/LogCacheActivity.java +++ b/main/src/cgeo/geocaching/LogCacheActivity.java @@ -3,6 +3,7 @@ package cgeo.geocaching; import cgeo.geocaching.connector.ILoggingManager; import cgeo.geocaching.connector.ImageResult; import cgeo.geocaching.connector.LogResult; +import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.LogTypeTrackable; @@ -18,7 +19,7 @@ import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.LogTemplateProvider; import cgeo.geocaching.utils.LogTemplateProvider.LogContext; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import android.app.Activity; @@ -46,7 +47,6 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; -import java.util.Locale; public class LogCacheActivity extends AbstractLoggingActivity implements DateDialog.DateDialogParent { static final String EXTRAS_GEOCODE = "geocode"; @@ -79,7 +79,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia private ILoggingManager loggingManager; // Data to be saved while reconfiguring - private double rating; + private float rating; private LogType typeSelected; private Calendar date; private String imageCaption; @@ -214,13 +214,13 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia if (!postButton.isEnabled()) { return res.getString(R.string.log_post_not_possible); } - if (typeSelected != LogType.FOUND_IT || !Settings.isGCvoteLogin() || !cache.supportsGCVote()) { + if (!GCVote.isVotingPossible(cache)) { return res.getString(R.string.log_post); } - if (rating == 0) { - return res.getString(R.string.log_post_no_rate); + if (GCVote.isValidRating(rating)) { + return res.getString(R.string.log_post_rate) + " " + GCVote.getRatingText(rating) + "*"; } - return res.getString(R.string.log_post_rate) + " " + ratingTextValue(rating) + "*"; + return res.getString(R.string.log_post_no_rate); } @Override @@ -235,13 +235,13 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia } if ((StringUtils.isBlank(cacheid)) && StringUtils.isNotBlank(geocode)) { - cacheid = cgData.getCacheidForGeocode(geocode); + cacheid = DataStore.getCacheidForGeocode(geocode); } if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(cacheid)) { - geocode = cgData.getGeocodeForGuid(cacheid); + geocode = DataStore.getGeocodeForGuid(cacheid); } - cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); possibleLogTypes = cache.getPossibleLogTypes(); if (StringUtils.isNotBlank(cache.getName())) { @@ -261,7 +261,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia // Restore previous state if (savedInstanceState != null) { - rating = savedInstanceState.getDouble(SAVED_STATE_RATING); + rating = savedInstanceState.getFloat(SAVED_STATE_RATING); typeSelected = LogType.getById(savedInstanceState.getInt(SAVED_STATE_TYPE)); date.setTimeInMillis(savedInstanceState.getLong(SAVED_STATE_DATE)); imageCaption = savedInstanceState.getString(SAVED_STATE_IMAGE_CAPTION); @@ -269,7 +269,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia imageUri = Uri.parse(savedInstanceState.getString(SAVED_STATE_IMAGE_URI)); } else { // If log had been previously saved, load it now, otherwise initialize signature as needed - final LogEntry log = cgData.loadLogOffline(geocode); + final LogEntry log = DataStore.loadLogOffline(geocode); if (log != null) { typeSelected = log.type; date.setTime(new Date(log.date)); @@ -277,7 +277,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia } else if (StringUtils.isNotBlank(Settings.getSignature()) && Settings.isAutoInsertSignature() && StringUtils.isBlank(currentLogText())) { - insertIntoLog(LogTemplateProvider.applyTemplates(Settings.getSignature(), new LogContext(cache)), false); + insertIntoLog(LogTemplateProvider.applyTemplates(Settings.getSignature(), new LogContext(cache, null)), false); } } updatePostButtonText(); @@ -341,28 +341,25 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia private void setDefaultValues() { date = Calendar.getInstance(); - rating = 0.0; + rating = GCVote.NO_RATING; if (cache.isEventCache()) { final Date eventDate = cache.getHiddenDate(); - boolean expired = DateUtils.daysSince(eventDate.getTime()) > 0; + boolean expired = DateUtils.daysSince(eventDate.getTime()) >= 0; if (cache.hasOwnLog(LogType.WILL_ATTEND) || expired) { - if (cache.hasOwnLog(LogType.ATTENDED)) { - typeSelected = LogType.NOTE; - } - else { - typeSelected = LogType.ATTENDED; - } - } - else { + typeSelected = cache.hasOwnLog(LogType.ATTENDED) ? LogType.NOTE : LogType.ATTENDED; + } else { typeSelected = LogType.WILL_ATTEND; } - } - else { + // it this is an attended event log, use the event date by default instead of the current date + if (expired && typeSelected == LogType.ATTENDED) { + date.setTime(eventDate); + } + } else { if (cache.isFound()) { typeSelected = LogType.NOTE; } else { - typeSelected = LogType.FOUND_IT; + typeSelected = cache.getType() == CacheType.WEBCAM ? LogType.WEBCAM_PHOTO_TAKEN : LogType.FOUND_IT; } } text = null; @@ -424,8 +421,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - final boolean voteAvailable = Settings.isGCvoteLogin() && (typeSelected == LogType.FOUND_IT || typeSelected == LogType.ATTENDED || typeSelected == LogType.WEBCAM_PHOTO_TAKEN) && StringUtils.isNotBlank(cache.getGuid()) && cache.supportsGCVote(); - menu.findItem(SUBMENU_VOTE).setVisible(voteAvailable); + menu.findItem(SUBMENU_VOTE).setVisible(GCVote.isVotingPossible(cache)); return true; } @@ -438,9 +434,9 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia final int id = item.getItemId(); if (id >= 10 && id <= 19) { - rating = (id - 9) / 2.0; - if (rating < 1) { - rating = 0; + rating = (id - 9) / 2.0f; + if (!GCVote.isValidRating(rating)) { + rating = GCVote.NO_RATING; } updatePostButtonText(); return true; @@ -449,10 +445,6 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia return false; } - private static String ratingTextValue(final double rating) { - return String.format(Locale.getDefault(), "%.1f", rating); - } - @Override protected void onSaveInstanceState(final Bundle outState) { super.onSaveInstanceState(outState); @@ -548,13 +540,15 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia cache.setVisitedDate(new Date().getTime()); } - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); cache.clearOfflineLog(); if (typeSelected == LogType.FOUND_IT) { if (tweetCheck.isChecked() && tweetBox.getVisibility() == View.VISIBLE) { - Twitter.postTweetCache(geocode); + Twitter.postTweetCache(geocode, logNow); } + } + if (GCVote.isValidRating(rating)) { GCVote.setRating(cache, rating); } @@ -563,7 +557,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia final String uploadedImageUrl = imageResult.getImageUri(); if (StringUtils.isNotEmpty(uploadedImageUrl)) { logNow.addLogImage(new Image(uploadedImageUrl, imageCaption, imageDescription)); - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return imageResult.getPostResult(); } @@ -615,7 +609,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia @Override protected LogContext getLogContext() { - return new LogContext(cache); + return new LogContext(cache, null); } private void selectAllTrackablesAction() { diff --git a/main/src/cgeo/geocaching/LogEntry.java b/main/src/cgeo/geocaching/LogEntry.java index a01c431..b922398 100644 --- a/main/src/cgeo/geocaching/LogEntry.java +++ b/main/src/cgeo/geocaching/LogEntry.java @@ -5,7 +5,7 @@ import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.DateUtils; import cgeo.geocaching.utils.MatcherWrapper; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; @@ -89,7 +89,7 @@ public final class LogEntry { } } if (titles.isEmpty()) { - titles.add(cgeoapplication.getInstance().getString(R.string.cache_log_image_default_title)); + titles.add(CgeoApplication.getInstance().getString(R.string.cache_log_image_default_title)); } return StringUtils.join(titles, ", "); } diff --git a/main/src/cgeo/geocaching/LogTrackableActivity.java b/main/src/cgeo/geocaching/LogTrackableActivity.java index a45d584..dd3fa78 100644 --- a/main/src/cgeo/geocaching/LogTrackableActivity.java +++ b/main/src/cgeo/geocaching/LogTrackableActivity.java @@ -16,7 +16,7 @@ import cgeo.geocaching.ui.dialog.DateDialog; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.LogTemplateProvider.LogContext; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import android.app.Dialog; @@ -130,7 +130,7 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat } } - trackable = cgData.loadTrackable(geocode); + trackable = DataStore.loadTrackable(geocode); if (StringUtils.isNotBlank(trackable.getName())) { setTitle(res.getString(R.string.trackable_touch) + ": " + trackable.getName()); @@ -335,7 +335,7 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat if (status == StatusCode.NO_ERROR && Settings.isUseTwitter() && Settings.isTwitterLoginValid() && tweetCheck.isChecked() && tweetBox.getVisibility() == View.VISIBLE) { - Twitter.postTweetTrackable(geocode); + Twitter.postTweetTrackable(geocode, new LogEntry(0, typeSelected, log)); } return status; @@ -356,6 +356,6 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat @Override protected LogContext getLogContext() { - return new LogContext(trackable); + return new LogContext(trackable, null); } } diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java index 1b9fe46..0daa124 100644 --- a/main/src/cgeo/geocaching/MainActivity.java +++ b/main/src/cgeo/geocaching/MainActivity.java @@ -10,6 +10,7 @@ import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Units; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.maps.CGeoMap; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.settings.SettingsActivity; @@ -24,8 +25,9 @@ import cgeo.geocaching.utils.Version; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.app.AlertDialog; import android.app.AlertDialog.Builder; @@ -284,7 +286,7 @@ public class MainActivity extends AbstractActivity { startActivity(new Intent(this, SettingsActivity.class)); return true; case R.id.menu_history: - cgeocaches.startActivityHistory(this); + CacheListActivity.startActivityHistory(this); return true; case R.id.menu_scan: startScannerApplication(); @@ -367,7 +369,7 @@ public class MainActivity extends AbstractActivity { @Override public void run(final Integer selectedListId) { Settings.saveLastList(selectedListId); - cgeocaches.startActivityOffline(MainActivity.this); + CacheListActivity.startActivityOffline(MainActivity.this); } }); return true; @@ -469,7 +471,7 @@ public class MainActivity extends AbstractActivity { } private void checkRestore() { - if (!cgData.isNewlyCreatedDatebase() || null == DatabaseBackupUtils.getRestoreFile()) { + if (!DataStore.isNewlyCreatedDatebase() || null == DatabaseBackupUtils.getRestoreFile()) { return; } new AlertDialog.Builder(this) @@ -480,7 +482,7 @@ public class MainActivity extends AbstractActivity { @Override public void onClick(final DialogInterface dialog, final int id) { dialog.dismiss(); - cgData.resetNewlyCreatedDatabase(); + DataStore.resetNewlyCreatedDatabase(); DatabaseBackupUtils.restoreDatabase(MainActivity.this); } }) @@ -488,7 +490,7 @@ public class MainActivity extends AbstractActivity { @Override public void onClick(final DialogInterface dialog, final int id) { dialog.cancel(); - cgData.resetNewlyCreatedDatabase(); + DataStore.resetNewlyCreatedDatabase(); } }) .create() @@ -516,11 +518,14 @@ public class MainActivity extends AbstractActivity { @Override public boolean onLongClick(View v) { + if (!Settings.isPremiumMember()) { + return true; + } new PocketQueryList.UserInterface(MainActivity.this).promptForListSelection(new RunnableWithArgument<PocketQueryList>() { @Override - public void run(PocketQueryList pql) { - cgeocaches.startActivityPocket(MainActivity.this, pql.getGuid(), pql.getName()); + public void run(final @NonNull PocketQueryList pql) { + CacheListActivity.startActivityPocket(MainActivity.this, pql); } }); return true; @@ -585,7 +590,7 @@ public class MainActivity extends AbstractActivity { } nearestView.setPressed(true); - cgeocaches.startActivityNearest(this, app.currentGeo().getCoords()); + CacheListActivity.startActivityNearest(this, app.currentGeo().getCoords()); } /** @@ -594,7 +599,7 @@ public class MainActivity extends AbstractActivity { */ public void cgeoFindByOffline(final View v) { findByOffline.setPressed(true); - cgeocaches.startActivityOffline(this); + CacheListActivity.startActivityOffline(this); } /** @@ -658,7 +663,7 @@ public class MainActivity extends AbstractActivity { } int checks = 0; - while (!cgData.isInitialized()) { + while (!DataStore.isInitialized()) { try { wait(500); checks++; @@ -671,7 +676,7 @@ public class MainActivity extends AbstractActivity { } } - countBubbleCnt = cgData.getAllCachesCount(); + countBubbleCnt = DataStore.getAllCachesCount(); countBubbleHandler.sendEmptyMessage(0); } @@ -696,7 +701,7 @@ public class MainActivity extends AbstractActivity { } cleanupRunning = true; - cgData.clean(more); + DataStore.clean(more); cleanupRunning = false; if (version > 0) { diff --git a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java index 87d7ba7..d0733e5 100644 --- a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java +++ b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java @@ -1,6 +1,7 @@ package cgeo.geocaching; import butterknife.InjectView; +import butterknife.Optional; import butterknife.Views; import cgeo.geocaching.activity.AbstractActivity; @@ -43,13 +44,15 @@ import java.util.List; public class NavigateAnyPointActivity extends AbstractActivity { - @InjectView(R.id.buttonLatitude) protected Button latButton; - @InjectView(R.id.buttonLongitude) protected Button lonButton; - @InjectView(R.id.current) protected Button buttonCurrent; @InjectView(R.id.historyList) protected ListView historyListView; - @InjectView(R.id.distanceUnit) protected Spinner distanceUnitSelector; - @InjectView(R.id.bearing) protected EditText bearingEditText; - @InjectView(R.id.distance) protected EditText distanceEditText; + + // list header fields are optional, due to being expanded later than the list itself + @Optional @InjectView(R.id.buttonLatitude) protected Button latButton; + @Optional @InjectView(R.id.buttonLongitude) protected Button lonButton; + @Optional @InjectView(R.id.distance) protected EditText distanceEditText; + @Optional @InjectView(R.id.distanceUnit) protected Spinner distanceUnitSelector; + @Optional @InjectView(R.id.current) protected Button buttonCurrent; + @Optional @InjectView(R.id.bearing) protected EditText bearingEditText; private boolean changed = false; private List<Destination> historyOfSearchedLocations; @@ -214,7 +217,7 @@ public class NavigateAnyPointActivity extends AbstractActivity { private List<Destination> getHistoryOfSearchedLocations() { if (historyOfSearchedLocations == null) { // Load from database - historyOfSearchedLocations = cgData.loadHistoryOfSearchedLocations(); + historyOfSearchedLocations = DataStore.loadHistoryOfSearchedLocations(); } return historyOfSearchedLocations; @@ -377,7 +380,7 @@ public class NavigateAnyPointActivity extends AbstractActivity { getHistoryOfSearchedLocations().add(0, loc); // Save location - cgData.saveSearchedDestination(loc); + DataStore.saveSearchedDestination(loc); // Ensure to remove the footer historyListView.removeFooterView(getEmptyHistoryFooter()); @@ -396,7 +399,7 @@ public class NavigateAnyPointActivity extends AbstractActivity { getHistoryOfSearchedLocations().remove(destination); // Save - cgData.removeSearchedDestination(destination); + DataStore.removeSearchedDestination(destination); if (getHistoryOfSearchedLocations().isEmpty()) { if (historyListView.getFooterViewsCount() == 0) { @@ -415,7 +418,7 @@ public class NavigateAnyPointActivity extends AbstractActivity { getHistoryOfSearchedLocations().clear(); // Save - cgData.clearSearchedDestinations(); + DataStore.clearSearchedDestinations(); if (historyListView.getFooterViewsCount() == 0) { historyListView.addFooterView(getEmptyHistoryFooter()); @@ -443,7 +446,7 @@ public class NavigateAnyPointActivity extends AbstractActivity { return; } - cgeocaches.startActivityCoordinates(this, coords); + CacheListActivity.startActivityCoordinates(this, coords); finish(); } diff --git a/main/src/cgeo/geocaching/PocketQueryList.java b/main/src/cgeo/geocaching/PocketQueryList.java index cef4463..e1a921c 100644 --- a/main/src/cgeo/geocaching/PocketQueryList.java +++ b/main/src/cgeo/geocaching/PocketQueryList.java @@ -55,7 +55,6 @@ public final class PocketQueryList { PocketQueryList pq = pocketQueryList.get(i); items[i] = pq.name + " (" + pq.maxCaches + ")"; - } AlertDialog.Builder builder = new AlertDialog.Builder(activity); @@ -63,7 +62,9 @@ public final class PocketQueryList { builder.setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int itemId) { - runAfterwards.run(pocketQueryList.get(itemId)); + final PocketQueryList query = pocketQueryList.get(itemId); + dialogInterface.dismiss(); + runAfterwards.run(query); } }); builder.create().show(); @@ -86,13 +87,13 @@ public final class PocketQueryList { } private final Activity activity; - private final cgeoapplication app; + private final CgeoApplication app; private final Resources res; private ProgressDialog waitDialog = null; public UserInterface(final Activity activity) { this.activity = activity; - app = cgeoapplication.getInstance(); + app = CgeoApplication.getInstance(); res = app.getResources(); } diff --git a/main/src/cgeo/geocaching/SearchActivity.java b/main/src/cgeo/geocaching/SearchActivity.java index 8f58bcd..fe48fda 100644 --- a/main/src/cgeo/geocaching/SearchActivity.java +++ b/main/src/cgeo/geocaching/SearchActivity.java @@ -13,8 +13,6 @@ import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; import cgeo.geocaching.utils.EditUtils; -import cgeo.geocaching.utils.GeoDirHandler; -import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; @@ -38,26 +36,28 @@ public class SearchActivity extends AbstractActivity { @InjectView(R.id.buttonLatitude) protected Button buttonLatitude; @InjectView(R.id.buttonLongitude) protected Button buttonLongitude; - @InjectView(R.id.search_coordinates) protected Button findByCoords; - @InjectView(R.id.search_address) protected Button findByAddress; - @InjectView(R.id.geocode) protected AutoCompleteTextView geocodeEdit; - @InjectView(R.id.display_geocode) protected Button displayByGeocode; - @InjectView(R.id.search_keyword) protected Button findByKeyword; - @InjectView(R.id.search_username) protected Button findByUserName; - @InjectView(R.id.search_owner) protected Button findByOwner; - @InjectView(R.id.trackable) protected AutoCompleteTextView trackable; - @InjectView(R.id.display_trackable) protected Button displayTrackable; - @InjectView(R.id.latitude) protected EditText latEdit; - @InjectView(R.id.longitude) protected EditText lonEdit; - @InjectView(R.id.keyword) protected EditText keywordEditText; + @InjectView(R.id.search_coordinates) protected Button buttonSearchCoords; + @InjectView(R.id.address) protected EditText addressEditText; + @InjectView(R.id.search_address) protected Button buttonSearchAddress; + + @InjectView(R.id.geocode) protected AutoCompleteTextView geocodeEditText; + @InjectView(R.id.display_geocode) protected Button buttonSearchGeocode; + + @InjectView(R.id.keyword) protected EditText keywordEditText; + @InjectView(R.id.search_keyword) protected Button buttonSearchKeyword; + @InjectView(R.id.username) protected EditText userNameEditText; + @InjectView(R.id.search_username) protected Button buttonSearchUserName; + @InjectView(R.id.owner) protected EditText ownerNameEditText; - @InjectView(R.id.geocode) protected EditText geocodeEditText; - @InjectView(R.id.trackable) protected EditText trackableEditText; + @InjectView(R.id.search_owner) protected Button buttonSearchOwner; + + @InjectView(R.id.trackable) protected AutoCompleteTextView trackableEditText; + @InjectView(R.id.display_trackable) protected Button buttonSearchTrackable; @Override - public void onCreate(Bundle savedInstanceState) { + public final void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); // search query @@ -89,25 +89,18 @@ public class SearchActivity extends AbstractActivity { } @Override - public void onConfigurationChanged(Configuration newConfig) { + public final void onConfigurationChanged(final Configuration newConfig) { super.onConfigurationChanged(newConfig); init(); } @Override - public void onResume() { + public final void onResume() { super.onResume(); - geoDirHandler.startGeo(); init(); } - @Override - public void onPause() { - geoDirHandler.stopGeo(); - super.onPause(); - } - /** * Performs a search for query either as geocode, trackable code or keyword * @@ -142,7 +135,7 @@ public class SearchActivity extends AbstractActivity { } if (keywordSearch) { // keyword fallback, if desired by caller - cgeocaches.startActivityKeyword(this, query.trim()); + CacheListActivity.startActivityKeyword(this, query.trim()); return true; } @@ -150,73 +143,77 @@ public class SearchActivity extends AbstractActivity { } private void init() { - buttonLatitude.setOnClickListener(new FindByCoordsAction()); buttonLongitude.setOnClickListener(new FindByCoordsAction()); - findByCoords.setOnClickListener(new FindByCoordsListener()); - EditUtils.setActionListener((EditText) findViewById(R.id.address), new Runnable() { + buttonSearchCoords.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(final View arg0) { + findByCoordsFn(); + } + }); + + setSearchAction(addressEditText, buttonSearchAddress, new Runnable() { @Override public void run() { findByAddressFn(); } }); - findByAddress.setOnClickListener(new FindByAddressListener()); - EditUtils.setActionListener(geocodeEdit, new Runnable() { + setSearchAction(geocodeEditText, buttonSearchGeocode, new Runnable() { @Override public void run() { findByGeocodeFn(); } }); - addHistoryEntries(geocodeEdit, cgData.getRecentGeocodesForSearch()); - displayByGeocode.setOnClickListener(new FindByGeocodeListener()); + addHistoryEntries(geocodeEditText, DataStore.getRecentGeocodesForSearch()); - EditUtils.setActionListener((EditText) findViewById(R.id.keyword), new Runnable() { + setSearchAction(keywordEditText, buttonSearchKeyword, new Runnable() { @Override public void run() { findByKeywordFn(); } }); - findByKeyword.setOnClickListener(new FindByKeywordListener()); - EditUtils.setActionListener((EditText) findViewById(R.id.username), new Runnable() { + setSearchAction(userNameEditText, buttonSearchUserName, new Runnable() { @Override public void run() { findByUsernameFn(); } }); - findByUserName.setOnClickListener(new FindByUsernameListener()); - EditUtils.setActionListener((EditText) findViewById(R.id.owner), new Runnable() { + setSearchAction(ownerNameEditText, buttonSearchOwner, new Runnable() { @Override public void run() { findByOwnerFn(); } }); - findByOwner.setOnClickListener(new OnClickListener() { + + setSearchAction(trackableEditText, buttonSearchTrackable, new Runnable() { @Override - public void onClick(View arg0) { - findByOwnerFn(); + public void run() { + findTrackableFn(); } }); + addHistoryEntries(trackableEditText, DataStore.getTrackableCodes()); + disableSuggestions(trackableEditText); + } - EditUtils.setActionListener(trackable, new Runnable() { - + private static void setSearchAction(final EditText editText, final Button button, final Runnable runnable) { + EditUtils.setActionListener(editText, runnable); + button.setOnClickListener(new View.OnClickListener() { @Override - public void run() { - findTrackableFn(); + public void onClick(final View arg0) { + runnable.run(); } }); - addHistoryEntries(trackable, cgData.getTrackableCodes()); - disableSuggestions(trackable); - displayTrackable.setOnClickListener(new FindTrackableListener()); } private void addHistoryEntries(final AutoCompleteTextView textView, final String[] entries) { @@ -226,33 +223,15 @@ public class SearchActivity extends AbstractActivity { } } - private final GeoDirHandler geoDirHandler = new GeoDirHandler() { - @Override - public void updateGeoData(final IGeoData geo) { - try { - if (geo.getCoords() != null) { - if (latEdit != null) { - latEdit.setHint(geo.getCoords().format(GeopointFormatter.Format.LAT_DECMINUTE_RAW)); - } - if (lonEdit != null) { - lonEdit.setHint(geo.getCoords().format(GeopointFormatter.Format.LON_DECMINUTE_RAW)); - } - } - } catch (final RuntimeException e) { - Log.w("Failed to update location."); - } - } - }; - private class FindByCoordsAction implements OnClickListener { @Override - public void onClick(View arg0) { + public void onClick(final View arg0) { final CoordinatesInputDialog coordsDialog = new CoordinatesInputDialog(SearchActivity.this, null, null, app.currentGeo()); coordsDialog.setCancelable(true); coordsDialog.setOnCoordinateUpdate(new CoordinatesInputDialog.CoordinateUpdate() { @Override - public void update(Geopoint gp) { + public void update(final Geopoint gp) { buttonLatitude.setText(gp.format(GeopointFormatter.Format.LAT_DECMINUTE)); buttonLongitude.setText(gp.format(GeopointFormatter.Format.LON_DECMINUTE)); } @@ -261,17 +240,9 @@ public class SearchActivity extends AbstractActivity { } } - private class FindByCoordsListener implements View.OnClickListener { - - @Override - public void onClick(View arg0) { - findByCoordsFn(); - } - } - private void findByCoordsFn() { - final String latText = buttonLatitude.getText().toString(); - final String lonText = buttonLongitude.getText().toString(); + final String latText = StringUtils.trim(buttonLatitude.getText().toString()); + final String lonText = StringUtils.trim(buttonLongitude.getText().toString()); if (StringUtils.isEmpty(latText) || StringUtils.isEmpty(lonText)) { final IGeoData geo = app.currentGeo(); @@ -281,42 +252,27 @@ public class SearchActivity extends AbstractActivity { } } else { try { - cgeocaches.startActivityCoordinates(this, new Geopoint(StringUtils.trim(latText), StringUtils.trim(lonText))); + CacheListActivity.startActivityCoordinates(this, new Geopoint(latText, lonText)); } catch (final Geopoint.ParseException e) { showToast(res.getString(e.resource)); } } } - private class FindByKeywordListener implements View.OnClickListener { - - @Override - public void onClick(View arg0) { - findByKeywordFn(); - } - } - private void findByKeywordFn() { // find caches by coordinates - final String keyText = keywordEditText.getText().toString(); + final String keyText = StringUtils.trim(keywordEditText.getText().toString()); if (StringUtils.isBlank(keyText)) { helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_keyword)); return; } - cgeocaches.startActivityKeyword(this, StringUtils.trim(keyText)); - } - - private class FindByAddressListener implements View.OnClickListener { - @Override - public void onClick(View arg0) { - findByAddressFn(); - } + CacheListActivity.startActivityKeyword(this, keyText); } private void findByAddressFn() { - final String addText = addressEditText.getText().toString(); + final String addText = StringUtils.trim(addressEditText.getText().toString()); if (StringUtils.isBlank(addText)) { helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_address)); @@ -324,34 +280,26 @@ public class SearchActivity extends AbstractActivity { } final Intent addressesIntent = new Intent(this, AddressListActivity.class); - addressesIntent.putExtra(Intents.EXTRA_KEYWORD, StringUtils.trim(addText)); + addressesIntent.putExtra(Intents.EXTRA_KEYWORD, addText); startActivity(addressesIntent); } - private class FindByUsernameListener implements View.OnClickListener { - - @Override - public void onClick(View arg0) { - findByUsernameFn(); - } - } - - public void findByUsernameFn() { - final String usernameText = userNameEditText.getText().toString(); + public final void findByUsernameFn() { + final String usernameText = StringUtils.trim(userNameEditText.getText().toString()); if (StringUtils.isBlank(usernameText)) { helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_user)); return; } - cgeocaches.startActivityUserName(this, StringUtils.trim(usernameText)); + CacheListActivity.startActivityUserName(this, usernameText); } private void findByOwnerFn() { findByOwnerFn(ownerNameEditText.getText().toString()); } - private void findByOwnerFn(String userName) { + private void findByOwnerFn(final String userName) { final String usernameText = StringUtils.trimToEmpty(userName); if (StringUtils.isBlank(usernameText)) { @@ -359,38 +307,22 @@ public class SearchActivity extends AbstractActivity { return; } - cgeocaches.startActivityOwner(this, StringUtils.trim(usernameText)); - } - - private class FindByGeocodeListener implements View.OnClickListener { - - @Override - public void onClick(View arg0) { - findByGeocodeFn(); - } + CacheListActivity.startActivityOwner(this, usernameText); } private void findByGeocodeFn() { - final String geocodeText = geocodeEditText.getText().toString(); + final String geocodeText = StringUtils.trim(geocodeEditText.getText().toString()); if (StringUtils.isBlank(geocodeText) || geocodeText.equalsIgnoreCase("GC")) { helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_gccode)); return; } - CacheDetailActivity.startActivity(this, StringUtils.trim(geocodeText)); - } - - private class FindTrackableListener implements View.OnClickListener { - - @Override - public void onClick(View arg0) { - findTrackableFn(); - } + CacheDetailActivity.startActivity(this, geocodeText); } private void findTrackableFn() { - final String trackableText = trackableEditText.getText().toString(); + final String trackableText = StringUtils.trim(trackableEditText.getText().toString()); if (StringUtils.isBlank(trackableText) || trackableText.equalsIgnoreCase("TB")) { helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_tb)); @@ -398,18 +330,18 @@ public class SearchActivity extends AbstractActivity { } final Intent trackablesIntent = new Intent(this, TrackableActivity.class); - trackablesIntent.putExtra(Intents.EXTRA_GEOCODE, StringUtils.trim(trackableText).toUpperCase(Locale.US)); + trackablesIntent.putExtra(Intents.EXTRA_GEOCODE, trackableText.toUpperCase(Locale.US)); startActivity(trackablesIntent); } @Override - public boolean onCreateOptionsMenu(Menu menu) { + public final boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.search_activity_options, menu); return true; } @Override - public boolean onOptionsItemSelected(MenuItem item) { + public final boolean onOptionsItemSelected(final MenuItem item) { if (item.getItemId() == R.id.menu_search_own_caches) { findByOwnerFn(Settings.getUsername()); return true; diff --git a/main/src/cgeo/geocaching/SearchResult.java b/main/src/cgeo/geocaching/SearchResult.java index e637d1f..0bdf6c6 100644 --- a/main/src/cgeo/geocaching/SearchResult.java +++ b/main/src/cgeo/geocaching/SearchResult.java @@ -8,7 +8,7 @@ import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.gcvote.GCVote; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import android.os.Parcel; @@ -199,7 +199,7 @@ public class SearchResult implements Parcelable { SearchResult result = new SearchResult(this); result.geocodes.clear(); final ArrayList<Geocache> cachesForVote = new ArrayList<Geocache>(); - final Set<Geocache> caches = cgData.loadCaches(geocodes, LoadFlags.LOAD_CACHE_OR_DB); + final Set<Geocache> caches = DataStore.loadCaches(geocodes, LoadFlags.LOAD_CACHE_OR_DB); int excluded = 0; for (Geocache cache : caches) { // Is there any reason to exclude the cache from the list? @@ -220,11 +220,11 @@ public class SearchResult implements Parcelable { } public Geocache getFirstCacheFromResult(final EnumSet<LoadFlag> loadFlags) { - return CollectionUtils.isNotEmpty(geocodes) ? cgData.loadCache(geocodes.iterator().next(), loadFlags) : null; + return CollectionUtils.isNotEmpty(geocodes) ? DataStore.loadCache(geocodes.iterator().next(), loadFlags) : null; } public Set<Geocache> getCachesFromSearchResult(final EnumSet<LoadFlag> loadFlags) { - return cgData.loadCaches(geocodes, loadFlags); + return DataStore.loadCaches(geocodes, loadFlags); } /** Add the geocode to the search. No cache is loaded into the CacheCache */ @@ -243,7 +243,7 @@ public class SearchResult implements Parcelable { /** Add the cache geocode to the search and store the cache in the CacheCache */ public boolean addAndPutInCache(final Geocache cache) { addGeocode(cache.getGeocode()); - return cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE)); + return DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE)); } public boolean isEmpty() { @@ -252,7 +252,7 @@ public class SearchResult implements Parcelable { public boolean hasUnsavedCaches() { for (final String geocode : getGeocodes()) { - if (!cgData.isOffline(geocode, null)) { + if (!DataStore.isOffline(geocode, null)) { return true; } } diff --git a/main/src/cgeo/geocaching/SelectMapfileActivity.java b/main/src/cgeo/geocaching/SelectMapfileActivity.java index 8b50c1f..9d86fa5 100644 --- a/main/src/cgeo/geocaching/SelectMapfileActivity.java +++ b/main/src/cgeo/geocaching/SelectMapfileActivity.java @@ -1,14 +1,23 @@ package cgeo.geocaching; +import butterknife.InjectView; +import butterknife.Views; + import cgeo.geocaching.files.AbstractFileListActivity; import cgeo.geocaching.files.IFileSelectionView; import cgeo.geocaching.files.LocalStorage; +import cgeo.geocaching.files.SimpleDirChooser; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.FileSelectionListAdapter; +import org.openintents.intents.FileManagerIntents; + import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; import java.io.File; import java.util.ArrayList; @@ -20,12 +29,40 @@ public class SelectMapfileActivity extends AbstractFileListActivity<FileSelectio super("map"); } + @InjectView(R.id.select_dir) protected Button selectDirectory; + private String mapFile; + private static int REQUEST_DIRECTORY = 1; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Views.inject(this); + mapFile = Settings.getMapFile(); + + selectDirectory.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + try { + final Intent dirChooser = new Intent(FileManagerIntents.ACTION_PICK_DIRECTORY); + dirChooser.putExtra(FileManagerIntents.EXTRA_TITLE, + getString(R.string.simple_dir_chooser_title)); + dirChooser.putExtra(FileManagerIntents.EXTRA_BUTTON_TEXT, + getString(android.R.string.ok)); + startActivityForResult(dirChooser, REQUEST_DIRECTORY); + } catch (android.content.ActivityNotFoundException ex) { + // OI file manager not available + final Intent dirChooser = new Intent(SelectMapfileActivity.this, SimpleDirChooser.class); + dirChooser.putExtra(Intents.EXTRA_START_DIR, LocalStorage.getStorage().getAbsolutePath()); + startActivityForResult(dirChooser, REQUEST_DIRECTORY); + } + } + }); + selectDirectory.setText(getResources().getString(R.string.simple_dir_chooser_title)); + selectDirectory.setVisibility(View.VISIBLE); } @Override @@ -70,4 +107,22 @@ public class SelectMapfileActivity extends AbstractFileListActivity<FileSelectio return this; } + @Override + protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode != RESULT_OK) { + return; + } + + if (requestCode == REQUEST_DIRECTORY) { + final String directory = new File(data.getData().getPath()).getAbsolutePath(); + mapFile = directory; + close(); + } + } + + @Override + protected boolean requireFiles() { + return false; + } } diff --git a/main/src/cgeo/geocaching/StaticMapsActivity.java b/main/src/cgeo/geocaching/StaticMapsActivity.java index 2268df9..7811da5 100644 --- a/main/src/cgeo/geocaching/StaticMapsActivity.java +++ b/main/src/cgeo/geocaching/StaticMapsActivity.java @@ -9,7 +9,7 @@ import com.googlecode.androidannotations.annotations.Extra; import com.googlecode.androidannotations.annotations.OptionsItem; import com.googlecode.androidannotations.annotations.OptionsMenu; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import android.app.ProgressDialog; import android.content.Context; @@ -117,7 +117,7 @@ public class StaticMapsActivity extends AbstractActivity { for (int level = 1; level <= StaticMapsProvider.MAPS_LEVEL_MAX; level++) { try { if (waypointId != null) { - final Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); final Bitmap image = StaticMapsProvider.getWaypointMap(geocode, cache.getWaypointById(waypointId), level); if (image != null) { maps.add(image); @@ -151,7 +151,7 @@ public class StaticMapsActivity extends AbstractActivity { } private boolean downloadStaticMaps() { - final Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); if (waypointId == null) { showToast(res.getString(R.string.info_storing_static_maps)); StaticMapsProvider.storeCacheStaticMap(cache, true); diff --git a/main/src/cgeo/geocaching/StaticMapsProvider.java b/main/src/cgeo/geocaching/StaticMapsProvider.java index cf279c0..d5cbb13 100644 --- a/main/src/cgeo/geocaching/StaticMapsProvider.java +++ b/main/src/cgeo/geocaching/StaticMapsProvider.java @@ -11,8 +11,8 @@ import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; - import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -245,11 +245,8 @@ public final class StaticMapsProvider { * @param cache * @return <code>true</code> if at least one map file exists; <code>false</code> otherwise */ - public static boolean hasStaticMap(final Geocache cache) { - if (cache == null) { - return false; - } - final String geocode = cache.getGeocode(); + public static boolean hasStaticMap(@NonNull final Geocache cache) { + final String geocode = cache.getGeocode(); if (StringUtils.isBlank(geocode)) { return false; } diff --git a/main/src/cgeo/geocaching/StatusFragment.java b/main/src/cgeo/geocaching/StatusFragment.java index e0e714a..4f70f0e 100644 --- a/main/src/cgeo/geocaching/StatusFragment.java +++ b/main/src/cgeo/geocaching/StatusFragment.java @@ -38,12 +38,12 @@ public class StatusFragment extends Fragment { @Override public void onResume() { super.onResume(); - cgeoapplication.getInstance().getStatusUpdater().addObserver(statusHandler); + CgeoApplication.getInstance().getStatusUpdater().addObserver(statusHandler); } @Override public void onPause() { - cgeoapplication.getInstance().getStatusUpdater().deleteObserver(statusHandler); + CgeoApplication.getInstance().getStatusUpdater().deleteObserver(statusHandler); super.onPause(); } diff --git a/main/src/cgeo/geocaching/TrackableActivity.java b/main/src/cgeo/geocaching/TrackableActivity.java index 20a9ccf..69123fe 100644 --- a/main/src/cgeo/geocaching/TrackableActivity.java +++ b/main/src/cgeo/geocaching/TrackableActivity.java @@ -16,6 +16,7 @@ import cgeo.geocaching.ui.AnchorAwareLinkMovementMethod; import cgeo.geocaching.ui.CacheDetailsCreator; import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.ui.UserActionsClickListener; +import cgeo.geocaching.ui.UserNameClickListener; import cgeo.geocaching.ui.logs.TrackableLogsViewCreator; import cgeo.geocaching.utils.HtmlUtils; import cgeo.geocaching.utils.Log; @@ -233,7 +234,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi @Override public void run() { if (StringUtils.isNotEmpty(geocode)) { - trackable = cgData.loadTrackable(geocode); + trackable = DataStore.loadTrackable(geocode); if (trackable == null || trackable.isLoggable()) { // iterate over the connectors as some codes may be handled by multiple connectors @@ -418,11 +419,22 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi spotted.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { - CacheDetailActivity.startActivityGuid(TrackableActivity.this, trackable.getSpottedGuid(), trackable.getSpottedName()); + if (StringUtils.isNotBlank(trackable.getSpottedGuid())) { + CacheDetailActivity.startActivityGuid(TrackableActivity.this, trackable.getSpottedGuid(), trackable.getSpottedName()); + } + else { + // for geokrety we only know the cache geocode + final String cacheCode = trackable.getSpottedName(); + if (ConnectorFactory.canHandle(cacheCode)) { + CacheDetailActivity.startActivity(TrackableActivity.this, cacheCode); + } + } } }); } else if (Trackable.SPOTTED_USER == trackable.getSpottedType()) { - spotted.setOnClickListener(new UserActionsClickListener()); + spotted.setOnClickListener(new UserNameClickListener(Html.fromHtml(trackable.getSpottedName()).toString())); + } else if (Trackable.SPOTTED_OWNER == trackable.getSpottedType()) { + spotted.setOnClickListener(new UserNameClickListener(Html.fromHtml(trackable.getOwner()).toString())); } } diff --git a/main/src/cgeo/geocaching/UsefulAppsActivity.java b/main/src/cgeo/geocaching/UsefulAppsActivity.java index 41ea96e..08206ff 100644 --- a/main/src/cgeo/geocaching/UsefulAppsActivity.java +++ b/main/src/cgeo/geocaching/UsefulAppsActivity.java @@ -60,6 +60,7 @@ public class UsefulAppsActivity extends AbstractActivity { new HelperApp(R.string.helper_calendar_title, R.string.helper_calendar_description, R.drawable.cgeo, "cgeo.calendar"), new HelperApp(R.string.helper_pocketquery_title, R.string.helper_pocketquery_description, R.drawable.helper_pocketquery, "org.pquery"), new HelperApp(R.string.helper_locus_title, R.string.helper_locus_description, R.drawable.helper_locus, "menion.android.locus"), + new HelperApp(R.string.helper_google_translate_title, R.string.helper_google_translate_description, R.drawable.helper_google_translate, "com.google.android.apps.translate"), new HelperApp(R.string.helper_gpsstatus_title, R.string.helper_gpsstatus_description, R.drawable.helper_gpsstatus, "com.eclipsim.gpsstatus2"), new HelperApp(R.string.helper_bluetoothgps_title, R.string.helper_bluetoothgps_description, R.drawable.helper_bluetoothgps, "googoo.android.btgps"), new HelperApp(R.string.helper_barcode_title, R.string.helper_barcode_description, R.drawable.helper_barcode, "com.google.zxing.client.android"), diff --git a/main/src/cgeo/geocaching/Waypoint.java b/main/src/cgeo/geocaching/Waypoint.java index e39d26a..b204bdd 100644 --- a/main/src/cgeo/geocaching/Waypoint.java +++ b/main/src/cgeo/geocaching/Waypoint.java @@ -14,6 +14,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * Note: this class has a natural ordering that is inconsistent with equals. + */ public class Waypoint implements IWaypoint, Comparable<Waypoint> { public static final String PREFIX_OWN = "OWN"; @@ -35,7 +38,7 @@ public class Waypoint implements IWaypoint, Comparable<Waypoint> { public static void initializeScale() { // Calculate visited inset based on screen density - VISITED_INSET = (int) (6.6f * cgeoapplication.getInstance().getResources().getDisplayMetrics().density + 0.5f); + VISITED_INSET = (int) (6.6f * CgeoApplication.getInstance().getResources().getDisplayMetrics().density + 0.5f); } /** diff --git a/main/src/cgeo/geocaching/WaypointPopup.java b/main/src/cgeo/geocaching/WaypointPopup.java index a1ab36a..b1e4b92 100644 --- a/main/src/cgeo/geocaching/WaypointPopup.java +++ b/main/src/cgeo/geocaching/WaypointPopup.java @@ -56,7 +56,7 @@ public class WaypointPopup extends AbstractPopupActivity { @Override protected void init() { super.init(); - waypoint = cgData.loadWaypoint(waypointId); + waypoint = DataStore.loadWaypoint(waypointId); try { if (StringUtils.isNotBlank(waypoint.getName())) { setTitle(waypoint.getName()); @@ -78,7 +78,7 @@ public class WaypointPopup extends AbstractPopupActivity { @Override public void onClick(View arg0) { - EditWaypointActivity.startActivityEditWaypoint(WaypointPopup.this, waypoint.getId()); + EditWaypointActivity.startActivityEditWaypoint(WaypointPopup.this, cache, waypoint.getId()); finish(); } }); @@ -94,7 +94,7 @@ public class WaypointPopup extends AbstractPopupActivity { } @Override - protected void navigateTo() { + public void navigateTo() { NavigationAppFactory.startDefaultNavigationApplication(1, this, waypoint); } @@ -119,7 +119,7 @@ public class WaypointPopup extends AbstractPopupActivity { } @Override - protected void showNavigationMenu() { + public void showNavigationMenu() { NavigationAppFactory.showNavigationMenu(this, null, waypoint, null); } diff --git a/main/src/cgeo/geocaching/WaypointViewHolder.java b/main/src/cgeo/geocaching/WaypointViewHolder.java new file mode 100644 index 0000000..33b606e --- /dev/null +++ b/main/src/cgeo/geocaching/WaypointViewHolder.java @@ -0,0 +1,23 @@ +package cgeo.geocaching; + +import butterknife.InjectView; + +import cgeo.geocaching.ui.AbstractViewHolder; + +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +public class WaypointViewHolder extends AbstractViewHolder { + + @InjectView(R.id.coordinates) protected TextView coordinatesView; + @InjectView(R.id.info) protected TextView infoView; + @InjectView(R.id.name) protected TextView nameView; + @InjectView(R.id.note) protected TextView noteView; + @InjectView(R.id.wpDefaultNavigation) protected ImageView wpNavView; + + public WaypointViewHolder(View rowView) { + super(rowView); + } + +} diff --git a/main/src/cgeo/geocaching/activity/AbstractActivity.java b/main/src/cgeo/geocaching/activity/AbstractActivity.java index 28c0cdd..6e74928 100644 --- a/main/src/cgeo/geocaching/activity/AbstractActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractActivity.java @@ -2,7 +2,7 @@ package cgeo.geocaching.activity; import butterknife.Views; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.network.Cookies; import cgeo.geocaching.settings.Settings; @@ -18,7 +18,7 @@ import android.widget.EditText; public abstract class AbstractActivity extends FragmentActivity implements IAbstractActivity { - protected cgeoapplication app = null; + protected CgeoApplication app = null; protected Resources res = null; private boolean keepScreenOn = false; @@ -86,12 +86,21 @@ public abstract class AbstractActivity extends FragmentActivity implements IAbst } protected void onCreate(final Bundle savedInstanceState, final int resourceLayoutID) { + onCreate(savedInstanceState, resourceLayoutID, false); + } + + protected void onCreate(final Bundle savedInstanceState, final int resourceLayoutID, boolean useDialogTheme) { + super.onCreate(savedInstanceState); initializeCommonFields(); // non declarative part of layout - setTheme(); + if (useDialogTheme) { + setTheme(ActivityMixin.getDialogTheme()); + } else { + setTheme(); + } setContentView(resourceLayoutID); // create view variables @@ -101,7 +110,7 @@ public abstract class AbstractActivity extends FragmentActivity implements IAbst private void initializeCommonFields() { // initialize commonly used members res = this.getResources(); - app = (cgeoapplication) this.getApplication(); + app = (CgeoApplication) this.getApplication(); // only needed in some activities, but implemented in super class nonetheless Cookies.restoreCookieStore(Settings.getCookieStore()); diff --git a/main/src/cgeo/geocaching/activity/AbstractListActivity.java b/main/src/cgeo/geocaching/activity/AbstractListActivity.java index 93e9b7f..d2bc0b4 100644 --- a/main/src/cgeo/geocaching/activity/AbstractListActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractListActivity.java @@ -1,6 +1,6 @@ package cgeo.geocaching.activity; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import android.content.res.Resources; import android.graphics.drawable.Drawable; @@ -13,7 +13,7 @@ public abstract class AbstractListActivity extends FragmentListActivity implemen private boolean keepScreenOn = false; - protected cgeoapplication app = null; + protected CgeoApplication app = null; protected Resources res = null; protected AbstractListActivity() { @@ -65,7 +65,7 @@ public abstract class AbstractListActivity extends FragmentListActivity implemen private void initializeCommonFields() { // init res = this.getResources(); - app = (cgeoapplication) this.getApplication(); + app = (CgeoApplication) this.getApplication(); ActivityMixin.keepScreenOn(this, keepScreenOn); } diff --git a/main/src/cgeo/geocaching/activity/ActivityMixin.java b/main/src/cgeo/geocaching/activity/ActivityMixin.java index 699d5ab..9b1e433 100644 --- a/main/src/cgeo/geocaching/activity/ActivityMixin.java +++ b/main/src/cgeo/geocaching/activity/ActivityMixin.java @@ -2,8 +2,8 @@ package cgeo.geocaching.activity; import cgeo.geocaching.MainActivity; import cgeo.geocaching.R; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.compatibility.Compatibility; +import cgeo.geocaching.settings.Settings; import org.apache.commons.lang3.StringUtils; @@ -63,14 +63,6 @@ public final class ActivityMixin { } } - public static int getTheme() { - if (Settings.isLightSkin()) { - return R.style.light; - } - - return R.style.dark; - } - public static int getDialogTheme() { // Light theme dialogs don't work on Android Api < 11 if (Settings.isLightSkin() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { diff --git a/main/src/cgeo/geocaching/apps/AbstractApp.java b/main/src/cgeo/geocaching/apps/AbstractApp.java index 7b6b3d9..494e245 100644 --- a/main/src/cgeo/geocaching/apps/AbstractApp.java +++ b/main/src/cgeo/geocaching/apps/AbstractApp.java @@ -1,7 +1,7 @@ package cgeo.geocaching.apps; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.utils.ProcessUtils; import org.apache.commons.lang3.StringUtils; @@ -13,16 +13,21 @@ public abstract class AbstractApp implements App { private final String packageName; private final String intent; private final String name; + /** + * a unique id, defined in res/values/ids.xml + */ + private final int id; - protected AbstractApp(final String name, final String intent, + protected AbstractApp(final String name, final int id, final String intent, final String packageName) { this.name = name; + this.id = id; this.intent = intent; this.packageName = packageName; } - protected AbstractApp(final String name, final String intent) { - this(name, intent, null); + protected AbstractApp(final String name, final int id, final String intent) { + this(name, id, intent, null); } @Override @@ -38,7 +43,7 @@ public abstract class AbstractApp implements App { } @Override - public boolean isDefaultNavigationApp() { + public boolean isUsableAsDefaultNavigationApp() { return true; } @@ -49,11 +54,11 @@ public abstract class AbstractApp implements App { @Override public int getId() { - return getName().hashCode(); + return id; } protected static String getString(int ressourceId) { - return cgeoapplication.getInstance().getString(ressourceId); + return CgeoApplication.getInstance().getString(ressourceId); } @Override diff --git a/main/src/cgeo/geocaching/apps/AbstractLocusApp.java b/main/src/cgeo/geocaching/apps/AbstractLocusApp.java index 53620e4..4b524bf 100644 --- a/main/src/cgeo/geocaching/apps/AbstractLocusApp.java +++ b/main/src/cgeo/geocaching/apps/AbstractLocusApp.java @@ -1,9 +1,8 @@ package cgeo.geocaching.apps; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; -import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.WaypointType; @@ -15,11 +14,11 @@ import menion.android.locus.addon.publiclib.geoData.PointGeocachingData; import menion.android.locus.addon.publiclib.geoData.PointGeocachingDataWaypoint; import menion.android.locus.addon.publiclib.geoData.PointsData; +import org.apache.commons.lang3.time.FastDateFormat; + import android.app.Activity; -import android.content.Intent; import android.location.Location; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -30,25 +29,20 @@ import java.util.Locale; * @see <a href="http://forum.asamm.cz/viewtopic.php?f=29&t=767">Locus forum</a> */ public abstract class AbstractLocusApp extends AbstractApp { - private static final String INTENT = Intent.ACTION_VIEW; - private static final SimpleDateFormat ISO8601DATE = new SimpleDateFormat("yyyy-MM-dd'T'", Locale.US); - - protected AbstractLocusApp() { - super(getString(R.string.caches_map_locus), INTENT); - } + private static final FastDateFormat ISO8601DATE = FastDateFormat.getInstance("yyyy-MM-dd'T'", Locale.US); - protected AbstractLocusApp(final String text, final String intent) { - super(text, intent); + protected AbstractLocusApp(final String text, int id, final String intent) { + super(text, id, intent); } @Override public boolean isInstalled() { - return LocusUtils.isLocusAvailable(cgeoapplication.getInstance()); + return LocusUtils.isLocusAvailable(CgeoApplication.getInstance()); } /** * Display a list of caches / waypoints in Locus - * + * * @param objectsToShow * which caches/waypoints to show * @param withCacheWaypoints diff --git a/main/src/cgeo/geocaching/apps/App.java b/main/src/cgeo/geocaching/apps/App.java index bc99526..7e70581 100644 --- a/main/src/cgeo/geocaching/apps/App.java +++ b/main/src/cgeo/geocaching/apps/App.java @@ -5,14 +5,20 @@ import cgeo.geocaching.Geocache; public interface App { public boolean isInstalled(); - public boolean isDefaultNavigationApp(); + /** + * Whether or not an application can be used as the default navigation. + */ + public boolean isUsableAsDefaultNavigationApp(); public String getName(); + /** + * @return the unique ID of the application, defined in res/values/ids.xml + */ int getId(); /** - * whether or not the app can be used with the given cache (may depend on properties of the cache) + * Whether or not the app can be used with the given cache (may depend on properties of the cache). * * @param cache * @return diff --git a/main/src/cgeo/geocaching/apps/cache/AbstractGeneralApp.java b/main/src/cgeo/geocaching/apps/cache/AbstractGeneralApp.java index fd7d4b5..c4f2723 100644 --- a/main/src/cgeo/geocaching/apps/cache/AbstractGeneralApp.java +++ b/main/src/cgeo/geocaching/apps/cache/AbstractGeneralApp.java @@ -9,8 +9,8 @@ import android.content.Intent; abstract class AbstractGeneralApp extends AbstractApp implements CacheNavigationApp { - protected AbstractGeneralApp(String name, String packageName) { - super(name, null, packageName); + protected AbstractGeneralApp(final String name, final int id, final String packageName) { + super(name, id, null, packageName); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/CacheBeaconApp.java b/main/src/cgeo/geocaching/apps/cache/CacheBeaconApp.java index 9cfafb4..34c9074 100644 --- a/main/src/cgeo/geocaching/apps/cache/CacheBeaconApp.java +++ b/main/src/cgeo/geocaching/apps/cache/CacheBeaconApp.java @@ -7,7 +7,7 @@ import cgeo.geocaching.enumerations.CacheAttribute; public class CacheBeaconApp extends AbstractGeneralApp { public CacheBeaconApp() { - super(getString(R.string.cache_menu_cachebeacon), "de.fun2code.android.cachebeacon"); + super(getString(R.string.cache_menu_cachebeacon), R.id.cache_app_cache_beacon, "de.fun2code.android.cachebeacon"); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/GccApp.java b/main/src/cgeo/geocaching/apps/cache/GccApp.java index 0bbc2dd..4423977 100644 --- a/main/src/cgeo/geocaching/apps/cache/GccApp.java +++ b/main/src/cgeo/geocaching/apps/cache/GccApp.java @@ -10,7 +10,7 @@ public class GccApp extends AbstractGeneralApp { private static final String PACKAGE_PRO = "eisbehr.gcc.pro"; public GccApp() { - super(getString(R.string.cache_menu_gcc), null); + super(getString(R.string.cache_menu_gcc), R.id.cache_app_gcc, null); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/WhereYouGoApp.java b/main/src/cgeo/geocaching/apps/cache/WhereYouGoApp.java index 39e1963..79a5975 100644 --- a/main/src/cgeo/geocaching/apps/cache/WhereYouGoApp.java +++ b/main/src/cgeo/geocaching/apps/cache/WhereYouGoApp.java @@ -1,12 +1,12 @@ package cgeo.geocaching.apps.cache; -import cgeo.geocaching.R; import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; import cgeo.geocaching.enumerations.CacheType; public class WhereYouGoApp extends AbstractGeneralApp { public WhereYouGoApp() { - super(getString(R.string.cache_menu_whereyougo), "menion.android.whereyougo"); + super(getString(R.string.cache_menu_whereyougo), R.id.cache_app_whereyougo, "menion.android.whereyougo"); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java index a3ea57e..75ea056 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/AbstractPointNavigationApp.java @@ -1,8 +1,11 @@ package cgeo.geocaching.apps.cache.navi; import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; +import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.apps.AbstractApp; +import cgeo.geocaching.geopoint.Geopoint; import android.app.Activity; @@ -11,21 +14,32 @@ import android.app.Activity; */ abstract class AbstractPointNavigationApp extends AbstractApp implements CacheNavigationApp, WaypointNavigationApp, GeopointNavigationApp { - protected AbstractPointNavigationApp(String name, String intent) { - super(name, intent); + protected AbstractPointNavigationApp(final String name, final int id, final String intent) { + super(name, id, intent); } - protected AbstractPointNavigationApp(String name, String intent, String packageName) { - super(name, intent, packageName); + protected AbstractPointNavigationApp(final String name, final int id, final String intent, final String packageName) { + super(name, id, intent, packageName); } @Override public void navigate(Activity activity, Geocache cache) { - navigate(activity, cache.getCoords()); + final Geopoint coords = cache.getCoords(); + if (coords != null) { + navigate(activity, coords); + } else { + ActivityMixin.showToast(activity, activity.getResources().getString(R.string.err_nav_no_coordinates)); + } } @Override public void navigate(Activity activity, Waypoint waypoint) { + final Geopoint coords = waypoint.getCoords(); + if (coords != null) { + navigate(activity, coords); + } else { + ActivityMixin.showToast(activity, activity.getResources().getString(R.string.err_nav_no_coordinates)); + } navigate(activity, waypoint.getCoords()); } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/AbstractStaticMapsApp.java b/main/src/cgeo/geocaching/apps/cache/navi/AbstractStaticMapsApp.java index d898d7e..c42c2a2 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/AbstractStaticMapsApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/AbstractStaticMapsApp.java @@ -1,12 +1,12 @@ package cgeo.geocaching.apps.cache.navi; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.ILogable; import cgeo.geocaching.R; import cgeo.geocaching.StaticMapsActivity; import cgeo.geocaching.StaticMapsProvider; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgData; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.apps.AbstractApp; @@ -15,8 +15,8 @@ import org.apache.commons.lang3.StringUtils; import android.app.Activity; abstract class AbstractStaticMapsApp extends AbstractApp implements CacheNavigationApp, WaypointNavigationApp { - protected AbstractStaticMapsApp(String name) { - super(name, null); + protected AbstractStaticMapsApp(final String name, final int id) { + super(name, id, null); } @Override @@ -25,7 +25,7 @@ abstract class AbstractStaticMapsApp extends AbstractApp implements CacheNavigat } @Override - public boolean isDefaultNavigationApp() { + public boolean isUsableAsDefaultNavigationApp() { return false; } @@ -34,7 +34,7 @@ abstract class AbstractStaticMapsApp extends AbstractApp implements CacheNavigat return false; } String geocode = waypoint.getGeocode(); - if (StringUtils.isNotEmpty(geocode) && cgData.isOffline(geocode, null)) { + if (StringUtils.isNotEmpty(geocode) && DataStore.isOffline(geocode, null)) { return StaticMapsProvider.hasStaticMapForWaypoint(geocode, waypoint); } return false; diff --git a/main/src/cgeo/geocaching/apps/cache/navi/CompassApp.java b/main/src/cgeo/geocaching/apps/cache/navi/CompassApp.java index 47010df..03d2220 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/CompassApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/CompassApp.java @@ -1,18 +1,17 @@ package cgeo.geocaching.apps.cache.navi; +import cgeo.geocaching.CompassActivity; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.CompassActivity; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.ui.Formatter; import android.app.Activity; class CompassApp extends AbstractPointNavigationApp { CompassApp() { - super(getString(R.string.compass_title), null); + super(getString(R.string.compass_title), R.id.cache_app_compass, null); } @Override @@ -33,8 +32,7 @@ class CompassApp extends AbstractPointNavigationApp { @Override public void navigate(Activity activity, Geocache cache) { - CompassActivity.startActivity(activity, cache.getGeocode(), cache.getName(), cache.getCoords(), null, - Formatter.formatCacheInfoShort(cache)); + CompassActivity.startActivity(activity, cache); } }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/apps/cache/navi/DownloadStaticMapsApp.java b/main/src/cgeo/geocaching/apps/cache/navi/DownloadStaticMapsApp.java index bc422d4..19b5e02 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/DownloadStaticMapsApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/DownloadStaticMapsApp.java @@ -9,7 +9,7 @@ import android.app.Activity; class DownloadStaticMapsApp extends AbstractStaticMapsApp { DownloadStaticMapsApp() { - super(getString(R.string.cache_menu_download_map_static)); + super(getString(R.string.cache_menu_download_map_static), R.id.cache_app_download_static_maps); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsApp.java b/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsApp.java index 4cbfa00..60d6e31 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsApp.java @@ -12,7 +12,7 @@ import android.net.Uri; class GoogleMapsApp extends AbstractPointNavigationApp { GoogleMapsApp() { - super(getString(R.string.cache_menu_map_ext), null); + super(getString(R.string.cache_menu_map_ext), R.id.cache_app_google_maps, null); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java b/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java index a84b7e8..c4351bb 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java @@ -1,8 +1,8 @@ package cgeo.geocaching.apps.cache.navi; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.IGeoData; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.utils.Log; @@ -14,7 +14,7 @@ import android.net.Uri; public class GoogleMapsDirectionApp extends AbstractPointNavigationApp { protected GoogleMapsDirectionApp() { - super(getString(R.string.cache_menu_maps_directions), null); + super(getString(R.string.cache_menu_maps_directions), R.id.cache_app_google_maps_direction, null); } @Override @@ -25,7 +25,7 @@ public class GoogleMapsDirectionApp extends AbstractPointNavigationApp { @Override public void navigate(Activity activity, Geopoint coords) { try { - IGeoData geo = cgeoapplication.getInstance().currentGeo(); + IGeoData geo = CgeoApplication.getInstance().currentGeo(); final Geopoint coordsNow = geo == null ? null : geo.getCoords(); if (coordsNow != null) { diff --git a/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java index a3532a5..902eebf 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java @@ -12,8 +12,8 @@ abstract class GoogleNavigationApp extends AbstractPointNavigationApp { private final String mode; - protected GoogleNavigationApp(final int nameResourceId, final String mode) { - super(getString(nameResourceId), null); + protected GoogleNavigationApp(final int nameResourceId, final int id, final String mode) { + super(getString(nameResourceId), id, null); this.mode = mode; } @@ -36,19 +36,19 @@ abstract class GoogleNavigationApp extends AbstractPointNavigationApp { static class GoogleNavigationWalkingApp extends GoogleNavigationApp { GoogleNavigationWalkingApp() { - super(R.string.cache_menu_navigation_walk, "w"); + super(R.string.cache_menu_navigation_walk, R.id.cache_app_google_navigation_walk, "w"); } } static class GoogleNavigationDrivingApp extends GoogleNavigationApp { GoogleNavigationDrivingApp() { - super(R.string.cache_menu_navigation_drive, "d"); + super(R.string.cache_menu_navigation_drive, R.id.cache_app_google_navigation_drive, "d"); } } static class GoogleNavigationBikeApp extends GoogleNavigationApp { GoogleNavigationBikeApp() { - super(R.string.cache_menu_navigation_bike, "b"); + super(R.string.cache_menu_navigation_bike, R.id.cache_app_google_navigation_bike, "b"); } } }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/apps/cache/navi/InternalMap.java b/main/src/cgeo/geocaching/apps/cache/navi/InternalMap.java index cdf14f0..540b025 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/InternalMap.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/InternalMap.java @@ -12,7 +12,7 @@ import android.app.Activity; class InternalMap extends AbstractPointNavigationApp { InternalMap() { - super(getString(R.string.cache_menu_map), null); + super(getString(R.string.cache_menu_map), R.id.cache_app_internal_map, null); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/navi/LocusApp.java b/main/src/cgeo/geocaching/apps/cache/navi/LocusApp.java index 8b64ac8..b60d78a 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/LocusApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/LocusApp.java @@ -1,15 +1,24 @@ package cgeo.geocaching.apps.cache.navi; + import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; import cgeo.geocaching.apps.AbstractLocusApp; import android.app.Activity; +import android.content.Intent; import java.util.Collections; class LocusApp extends AbstractLocusApp implements CacheNavigationApp, WaypointNavigationApp { + private static final String INTENT = Intent.ACTION_VIEW; + + protected LocusApp() { + super(getString(R.string.caches_map_locus), R.id.cache_app_locus, INTENT); + } + @Override public boolean isEnabled(Waypoint waypoint) { return waypoint.getCoords() != null; diff --git a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java index ec6b3e1..dd02bc1 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java @@ -1,9 +1,9 @@ package cgeo.geocaching.apps.cache.navi; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.apps.AbstractAppFactory; import cgeo.geocaching.apps.App; @@ -16,10 +16,11 @@ import cgeo.geocaching.apps.cache.navi.GoogleNavigationApp.GoogleNavigationWalki import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.settings.Settings; +import org.eclipse.jdt.annotation.Nullable; + import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; -import android.view.Menu; import android.view.MenuItem; import android.widget.ArrayAdapter; @@ -214,7 +215,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { public static List<NavigationAppsEnum> getInstalledDefaultNavigationApps() { final List<NavigationAppsEnum> installedNavigationApps = new ArrayList<NavigationAppsEnum>(); for (final NavigationAppsEnum appEnum : NavigationAppsEnum.values()) { - if (appEnum.app.isInstalled() && appEnum.app.isDefaultNavigationApp()) { + if (appEnum.app.isInstalled() && appEnum.app.isUsableAsDefaultNavigationApp()) { installedNavigationApps.add(appEnum); } } @@ -222,46 +223,9 @@ public final class NavigationAppFactory extends AbstractAppFactory { } /** - * This offset is used to build unique menu ids to avoid collisions of ids in menus - */ - private static final int MENU_ITEM_OFFSET = 12345; - - - /** - * Adds the installed navigation tools to the given menu. - * Use {@link #onMenuItemSelected(MenuItem, Activity, cgeo.geocaching.Geocache)} on - * selection event to start the selected navigation tool. - * - * <b>Only use this way if {@link #showNavigationMenu(Activity, cgeo.geocaching.Geocache, cgeo.geocaching.Waypoint, Geopoint, boolean, boolean)} is - * not suitable for the given usecase.</b> - * - * @param menu - */ - public static void addMenuItems(final Menu menu, final Geocache cache) { - for (final NavigationAppsEnum navApp : getInstalledNavigationApps()) { - if (navApp.app instanceof CacheNavigationApp) { - final CacheNavigationApp cacheApp = (CacheNavigationApp) navApp.app; - if (cacheApp.isEnabled(cache) && Settings.isUseNavigationApp(navApp)) { - menu.add(0, MENU_ITEM_OFFSET + navApp.id, 0, navApp.app.getName()); - } - } - } - } - - public static void addMenuItems(final Menu menu, final Waypoint waypoint) { - for (final NavigationAppsEnum navApp : getInstalledNavigationApps()) { - if (navApp.app instanceof WaypointNavigationApp) { - final WaypointNavigationApp waypointApp = (WaypointNavigationApp) navApp.app; - if (waypointApp.isEnabled(waypoint) && Settings.isUseNavigationApp(navApp)) { - menu.add(0, MENU_ITEM_OFFSET + navApp.id, 0, navApp.app.getName()); - } - } - } - } - - /** - * Handles menu selections for menu entries created with {@link #addMenuItems(Menu, cgeo.geocaching.Geocache)}. - * + * Handles menu selections for menu entries created with + * {@link #showNavigationMenu(Activity, Geocache, Waypoint, Geopoint)}. + * * @param item * @param activity * @param cache @@ -273,20 +237,14 @@ public final class NavigationAppFactory extends AbstractAppFactory { return menuItem != null; } - private static void navigateCache(Activity activity, Geocache cache, App app) { + private static void navigateCache(Activity activity, Geocache cache, @Nullable App app) { if (app instanceof CacheNavigationApp) { final CacheNavigationApp cacheApp = (CacheNavigationApp) app; cacheApp.navigate(activity, cache); } } - public static boolean onMenuItemSelected(final MenuItem item, Activity activity, Waypoint waypoint) { - final App menuItem = getAppFromMenuItem(item); - navigateWaypoint(activity, waypoint, menuItem); - return menuItem != null; - } - - private static void navigateWaypoint(Activity activity, Waypoint waypoint, App app) { + private static void navigateWaypoint(Activity activity, Waypoint waypoint, @Nullable App app) { if (app instanceof WaypointNavigationApp) { final WaypointNavigationApp waypointApp = (WaypointNavigationApp) app; waypointApp.navigate(activity, waypoint); @@ -300,10 +258,11 @@ public final class NavigationAppFactory extends AbstractAppFactory { } } + @Nullable private static App getAppFromMenuItem(MenuItem item) { final int id = item.getItemId(); for (final NavigationAppsEnum navApp : NavigationAppsEnum.values()) { - if (MENU_ITEM_OFFSET + navApp.id == id) { + if (navApp.id == id) { return navApp.app; } } @@ -320,7 +279,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { */ public static void startDefaultNavigationApplication(int defaultNavigation, Activity activity, Geocache cache) { if (cache == null || cache.getCoords() == null) { - ActivityMixin.showToast(activity, cgeoapplication.getInstance().getString(R.string.err_location_unknown)); + ActivityMixin.showToast(activity, CgeoApplication.getInstance().getString(R.string.err_location_unknown)); return; } @@ -342,7 +301,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { */ public static void startDefaultNavigationApplication(int defaultNavigation, Activity activity, Waypoint waypoint) { if (waypoint == null || waypoint.getCoords() == null) { - ActivityMixin.showToast(activity, cgeoapplication.getInstance().getString(R.string.err_location_unknown)); + ActivityMixin.showToast(activity, CgeoApplication.getInstance().getString(R.string.err_location_unknown)); return; } navigateWaypoint(activity, waypoint, getDefaultNavigationApplication(defaultNavigation)); @@ -356,7 +315,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { */ public static void startDefaultNavigationApplication(int defaultNavigation, Activity activity, final Geopoint destination) { if (destination == null) { - ActivityMixin.showToast(activity, cgeoapplication.getInstance().getString(R.string.err_location_unknown)); + ActivityMixin.showToast(activity, CgeoApplication.getInstance().getString(R.string.err_location_unknown)); return; } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java b/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java index 7966733..da988aa 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigonApp.java @@ -13,7 +13,7 @@ class NavigonApp extends AbstractPointNavigationApp { private static final String INTENT_EXTRA_KEY_LONGITUDE = "longitude"; NavigonApp() { - super(getString(R.string.cache_menu_navigon), INTENT); + super(getString(R.string.cache_menu_navigon), R.id.cache_app_navigon, INTENT); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/navi/OruxMapsApp.java b/main/src/cgeo/geocaching/apps/cache/navi/OruxMapsApp.java index 24ef81b..5d645f7 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/OruxMapsApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/OruxMapsApp.java @@ -11,7 +11,7 @@ class OruxMapsApp extends AbstractPointNavigationApp { private static final String INTENT = "com.oruxmaps.VIEW_MAP_ONLINE"; OruxMapsApp() { - super(getString(R.string.cache_menu_oruxmaps), INTENT); + super(getString(R.string.cache_menu_oruxmaps), R.id.cache_app_orux_maps, INTENT); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/navi/RMapsApp.java b/main/src/cgeo/geocaching/apps/cache/navi/RMapsApp.java index b203aeb..82d144e 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/RMapsApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/RMapsApp.java @@ -16,7 +16,7 @@ class RMapsApp extends AbstractPointNavigationApp { private static final String INTENT = "com.robert.maps.action.SHOW_POINTS"; RMapsApp() { - super(getString(R.string.cache_menu_rmaps), INTENT); + super(getString(R.string.cache_menu_rmaps), R.id.cache_app_rmaps, INTENT); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/navi/RadarApp.java b/main/src/cgeo/geocaching/apps/cache/navi/RadarApp.java index b01539c..ffa6650 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/RadarApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/RadarApp.java @@ -12,7 +12,7 @@ class RadarApp extends AbstractPointNavigationApp { private static final String PACKAGE_NAME = "com.eclipsim.gpsstatus2"; RadarApp() { - super(getString(R.string.cache_menu_radar), INTENT, PACKAGE_NAME); + super(getString(R.string.cache_menu_radar), R.id.cache_app_radar, INTENT, PACKAGE_NAME); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/navi/StaticMapApp.java b/main/src/cgeo/geocaching/apps/cache/navi/StaticMapApp.java index 1dd57a3..9e1b3f0 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/StaticMapApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/StaticMapApp.java @@ -1,15 +1,15 @@ package cgeo.geocaching.apps.cache.navi; +import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.Geocache; import android.app.Activity; class StaticMapApp extends AbstractStaticMapsApp { StaticMapApp() { - super(getString(R.string.cache_menu_map_static)); + super(getString(R.string.cache_menu_map_static), R.id.cache_app_show_static_maps); } @Override diff --git a/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java b/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java index e2c0828..7294a40 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java @@ -1,7 +1,7 @@ package cgeo.geocaching.apps.cache.navi; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.utils.ProcessUtils; @@ -17,7 +17,7 @@ class StreetviewApp extends AbstractPointNavigationApp { private static final boolean INSTALLED = ProcessUtils.isInstalled(PACKAGE_NAME_STREET_VIEW); StreetviewApp() { - super(getString(R.string.cache_menu_streetview), null); + super(getString(R.string.cache_menu_streetview), R.id.cache_app_street_view, null); } @Override @@ -31,7 +31,7 @@ class StreetviewApp extends AbstractPointNavigationApp { activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("google.streetview:cbll=" + point.getLatitude() + "," + point.getLongitude()))); } catch (final ActivityNotFoundException e) { - ActivityMixin.showToast(activity, cgeoapplication.getInstance().getString(R.string.err_application_no)); + ActivityMixin.showToast(activity, CgeoApplication.getInstance().getString(R.string.err_application_no)); } } }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/apps/cache/navi/SygicNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/SygicNavigationApp.java index e30bfc1..76b7f0e 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/SygicNavigationApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/SygicNavigationApp.java @@ -17,7 +17,7 @@ public class SygicNavigationApp extends AbstractPointNavigationApp { private static final String PACKAGE = "com.sygic.aura"; SygicNavigationApp() { - super(getString(R.string.cache_menu_sygic), null, PACKAGE); + super(getString(R.string.cache_menu_sygic), R.id.cache_app_sygic, null, PACKAGE); } @Override diff --git a/main/src/cgeo/geocaching/apps/cachelist/LocusCacheListApp.java b/main/src/cgeo/geocaching/apps/cachelist/AbstractLocusCacheListApp.java index cd0289a..6411758 100644 --- a/main/src/cgeo/geocaching/apps/cachelist/LocusCacheListApp.java +++ b/main/src/cgeo/geocaching/apps/cachelist/AbstractLocusCacheListApp.java @@ -1,23 +1,23 @@ package cgeo.geocaching.apps.cachelist; +import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Geocache; import cgeo.geocaching.apps.AbstractLocusApp; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import android.app.Activity; import android.content.Intent; import java.util.List; -class LocusCacheListApp extends AbstractLocusApp implements CacheListApp { +abstract class AbstractLocusCacheListApp extends AbstractLocusApp implements CacheListApp { private boolean export; - public LocusCacheListApp(boolean export) { - super(getString(export ? R.string.caches_map_locus_export : R.string.caches_map_locus), Intent.ACTION_VIEW); + public AbstractLocusCacheListApp(final int id, boolean export) { + super(getString(export ? R.string.caches_map_locus_export : R.string.caches_map_locus), id, Intent.ACTION_VIEW); this.export = export; } diff --git a/main/src/cgeo/geocaching/apps/cachelist/CacheListAppFactory.java b/main/src/cgeo/geocaching/apps/cachelist/CacheListAppFactory.java index ca06c52..4df9d26 100644 --- a/main/src/cgeo/geocaching/apps/cachelist/CacheListAppFactory.java +++ b/main/src/cgeo/geocaching/apps/cachelist/CacheListAppFactory.java @@ -20,8 +20,8 @@ public final class CacheListAppFactory extends AbstractAppFactory { private static class LazyHolder { public static final CacheListApp[] apps = { new InternalCacheListMap(), - new LocusCacheListApp(false), - new LocusCacheListApp(true) + new LocusShowCacheListApp(), + new LocusExportCacheListApp() }; } @@ -29,35 +29,45 @@ public final class CacheListAppFactory extends AbstractAppFactory { * @param menu * @param activity * @param res - * @return the added menu item (also for a sub menu, then the menu item in the parent menu is returned) */ - public static MenuItem addMenuItems(final Menu menu, final Activity activity, final Resources res) { + public static void addMenuItems(final Menu menu, final Activity activity, final Resources res) { + final List<CacheListApp> activeApps = getActiveApps(); + if (activeApps.isEmpty()) { + return; + } + if (activeApps.size() == 1) { + final MenuItem subItem = menu.findItem(R.id.menu_cache_list_app); + subItem.setVisible(true); + subItem.setTitle(activeApps.get(0).getName()); + } else { + final MenuItem subItem = menu.findItem(R.id.submenu_cache_list_app); + subItem.setVisible(true); + final SubMenu subMenu = subItem.getSubMenu(); + for (final CacheListApp app : activeApps) { + subMenu.add(0, app.getId(), 0, app.getName()); + } + } + } + + private static List<CacheListApp> getActiveApps() { final List<CacheListApp> activeApps = new ArrayList<CacheListApp>(LazyHolder.apps.length); for (final CacheListApp app : LazyHolder.apps) { if (app.isInstalled()) { activeApps.add(app); } } - // use a new sub menu, if more than one app is available - switch (activeApps.size()) { - case 0: - return null; - case 1: - return menu.add(0, activeApps.get(0).getId(), 0, - activeApps.get(0).getName()).setIcon(R.drawable.ic_menu_mapmode); - default: - final SubMenu subMenu = menu.addSubMenu(0, 101, 0, - res.getString(R.string.caches_on_map)).setIcon(R.drawable.ic_menu_mapmode); - for (final CacheListApp app : activeApps) { - subMenu.add(0, app.getId(), 0, app.getName()); - } - return subMenu.getItem(); - } + return activeApps; } public static boolean onMenuItemSelected(final MenuItem item, final List<Geocache> caches, final Activity activity, final SearchResult search) { - final CacheListApp app = (CacheListApp) getAppFromMenuItem(item, LazyHolder.apps); + CacheListApp app; + if (item.getItemId() == R.id.menu_cache_list_app) { + app = getActiveApps().get(0); + } + else { + app = (CacheListApp) getAppFromMenuItem(item, LazyHolder.apps); + } if (app != null) { try { boolean result = app.invoke(caches, activity, search); diff --git a/main/src/cgeo/geocaching/apps/cachelist/InternalCacheListMap.java b/main/src/cgeo/geocaching/apps/cachelist/InternalCacheListMap.java index 38fb499..9216bc0 100644 --- a/main/src/cgeo/geocaching/apps/cachelist/InternalCacheListMap.java +++ b/main/src/cgeo/geocaching/apps/cachelist/InternalCacheListMap.java @@ -1,8 +1,8 @@ package cgeo.geocaching.apps.cachelist; +import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Geocache; import cgeo.geocaching.apps.AbstractApp; import cgeo.geocaching.maps.CGeoMap; @@ -13,7 +13,7 @@ import java.util.List; class InternalCacheListMap extends AbstractApp implements CacheListApp { InternalCacheListMap() { - super(getString(R.string.cache_menu_map), null); + super(getString(R.string.cache_menu_map), R.id.cache_list_app_map, null); } @Override diff --git a/main/src/cgeo/geocaching/apps/cachelist/LocusExportCacheListApp.java b/main/src/cgeo/geocaching/apps/cachelist/LocusExportCacheListApp.java new file mode 100644 index 0000000..adaa849 --- /dev/null +++ b/main/src/cgeo/geocaching/apps/cachelist/LocusExportCacheListApp.java @@ -0,0 +1,11 @@ +package cgeo.geocaching.apps.cachelist; + +import cgeo.geocaching.R; + +final class LocusExportCacheListApp extends AbstractLocusCacheListApp { + + public LocusExportCacheListApp() { + super(R.id.cache_list_app_locus_export, true); + } + +} diff --git a/main/src/cgeo/geocaching/apps/cachelist/LocusShowCacheListApp.java b/main/src/cgeo/geocaching/apps/cachelist/LocusShowCacheListApp.java new file mode 100644 index 0000000..32c5cb6 --- /dev/null +++ b/main/src/cgeo/geocaching/apps/cachelist/LocusShowCacheListApp.java @@ -0,0 +1,11 @@ +package cgeo.geocaching.apps.cachelist; + +import cgeo.geocaching.R; + +final class LocusShowCacheListApp extends AbstractLocusCacheListApp { + + public LocusShowCacheListApp() { + super(R.id.cache_list_app_locus_show, false); + } + +} diff --git a/main/src/cgeo/geocaching/compatibility/AndroidLevel13.java b/main/src/cgeo/geocaching/compatibility/AndroidLevel13.java index f08ac22..eb2be4b 100644 --- a/main/src/cgeo/geocaching/compatibility/AndroidLevel13.java +++ b/main/src/cgeo/geocaching/compatibility/AndroidLevel13.java @@ -1,6 +1,6 @@ package cgeo.geocaching.compatibility; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import android.annotation.TargetApi; import android.content.Context; @@ -18,7 +18,7 @@ public class AndroidLevel13 implements AndroidLevel13Interface { @Override public Point getDisplaySize() { Point dimensions = new Point(); - ((WindowManager) cgeoapplication.getInstance().getSystemService(Context.WINDOW_SERVICE)) + ((WindowManager) CgeoApplication.getInstance().getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay().getSize(dimensions); return dimensions; } diff --git a/main/src/cgeo/geocaching/compatibility/AndroidLevel13Emulation.java b/main/src/cgeo/geocaching/compatibility/AndroidLevel13Emulation.java index ded20cb..56c784f 100644 --- a/main/src/cgeo/geocaching/compatibility/AndroidLevel13Emulation.java +++ b/main/src/cgeo/geocaching/compatibility/AndroidLevel13Emulation.java @@ -1,6 +1,6 @@ package cgeo.geocaching.compatibility; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import android.content.Context; import android.graphics.Point; @@ -22,7 +22,7 @@ public class AndroidLevel13Emulation implements AndroidLevel13Interface { } private static Display getDisplay() { - return ((WindowManager) cgeoapplication.getInstance().getSystemService(Context.WINDOW_SERVICE)) + return ((WindowManager) CgeoApplication.getInstance().getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); } } diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java index b93cda0..3ec0580 100644 --- a/main/src/cgeo/geocaching/connector/AbstractConnector.java +++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java @@ -3,10 +3,15 @@ package cgeo.geocaching.connector; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.R; +import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Geopoint; import org.apache.commons.lang3.StringUtils; +import java.util.ArrayList; +import java.util.List; + public abstract class AbstractConnector implements IConnector { @Override @@ -146,4 +151,41 @@ public abstract class AbstractConnector implements IConnector { } return R.drawable.marker_other; } + + @Override + public List<LogType> getPossibleLogTypes(Geocache geocache) { + final List<LogType> logTypes = new ArrayList<LogType>(); + if (geocache.isEventCache()) { + logTypes.add(LogType.WILL_ATTEND); + logTypes.add(LogType.ATTENDED); + if (geocache.isOwner()) { + logTypes.add(LogType.ANNOUNCEMENT); + } + } else if (CacheType.WEBCAM == geocache.getType()) { + logTypes.add(LogType.WEBCAM_PHOTO_TAKEN); + } else { + logTypes.add(LogType.FOUND_IT); + } + if (!geocache.isEventCache()) { + logTypes.add(LogType.DIDNT_FIND_IT); + } + logTypes.add(LogType.NOTE); + if (!geocache.isEventCache()) { + logTypes.add(LogType.NEEDS_MAINTENANCE); + } + if (geocache.isOwner()) { + logTypes.add(LogType.OWNER_MAINTENANCE); + if (geocache.isDisabled()) { + logTypes.add(LogType.ENABLE_LISTING); + } + else { + logTypes.add(LogType.TEMP_DISABLE_LISTING); + } + logTypes.add(LogType.ARCHIVE); + } + if (!geocache.isArchived() && !geocache.isOwner()) { + logTypes.add(LogType.NEEDS_ARCHIVE); + } + return logTypes; + } } diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java index 54e3447..3fdc11b 100644 --- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java +++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java @@ -6,6 +6,7 @@ import cgeo.geocaching.SearchResult; import cgeo.geocaching.Trackable; import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByKeyword; import cgeo.geocaching.connector.capability.ISearchByViewPort; import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.connector.oc.OCApiConnector; @@ -60,6 +61,8 @@ public final class ConnectorFactory { private static final ISearchByCenter[] searchByCenterConns; + private static final ISearchByKeyword[] searchByKeywordConns; + static { final List<ISearchByViewPort> vpConns = new ArrayList<ISearchByViewPort>(); for (final IConnector conn : CONNECTORS) { @@ -77,6 +80,15 @@ public final class ConnectorFactory { } } searchByCenterConns = centerConns.toArray(new ISearchByCenter[centerConns.size()]); + + final List<ISearchByKeyword> keywordConns = new ArrayList<ISearchByKeyword>(); + for (final IConnector conn : CONNECTORS) { + // GCConnector is handled specially, omit it here! + if (conn instanceof ISearchByKeyword && !(conn instanceof GCConnector)) { + keywordConns.add((ISearchByKeyword) conn); + } + } + searchByKeywordConns = keywordConns.toArray(new ISearchByKeyword[keywordConns.size()]); } public static IConnector[] getConnectors() { @@ -87,6 +99,10 @@ public final class ConnectorFactory { return searchByCenterConns; } + public static ISearchByKeyword[] getSearchByKeywordConnectors() { + return searchByKeywordConns; + } + public static ILogin[] getActiveLiveConnectors() { final List<ILogin> liveConns = new ArrayList<ILogin>(); for (final IConnector conn : CONNECTORS) { diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java index 4ab7bde..2262e47 100644 --- a/main/src/cgeo/geocaching/connector/IConnector.java +++ b/main/src/cgeo/geocaching/connector/IConnector.java @@ -3,8 +3,11 @@ package cgeo.geocaching.connector; import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import cgeo.geocaching.LogCacheActivity; +import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Geopoint; +import java.util.List; + public interface IConnector { /** * get name for display (currently only used in links) @@ -208,4 +211,13 @@ public interface IConnector { * Whether to return the enabled or disabled marker type */ public int getCacheMapMarkerId(boolean disabled); + + /** + * Get the list of <b>potentially</b> possible log types for a cache. Those may still be filter further during the + * actual logging activity. + * + * @param geocache + * @return + */ + public List<LogType> getPossibleLogTypes(Geocache geocache); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java index 3fdd61f..91dd094 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByCenter.java @@ -1,14 +1,13 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.SearchResult; +import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.geopoint.Geopoint; /** * connector capability for online searching caches around a center coordinate, sorted by distance * */ -public interface ISearchByCenter { +public interface ISearchByCenter extends IConnector { public SearchResult searchByCenter(final Geopoint center); - - public boolean isActivated(); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java b/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java index c3d6bba..4c16049 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByGeocode.java @@ -1,12 +1,13 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.SearchResult; +import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.utils.CancellableHandler; /** * connector capability of searching online for a cache by geocode - * + * */ -public interface ISearchByGeocode { +public interface ISearchByGeocode extends IConnector { public SearchResult searchByGeocode(final String geocode, final String guid, final CancellableHandler handler); } diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByKeyword.java b/main/src/cgeo/geocaching/connector/capability/ISearchByKeyword.java new file mode 100644 index 0000000..09b2423 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByKeyword.java @@ -0,0 +1,12 @@ +package cgeo.geocaching.connector.capability; + +import cgeo.geocaching.SearchResult; +import cgeo.geocaching.connector.IConnector; + +/** + * connector capability of searching online for a cache by name + * + */ +public interface ISearchByKeyword extends IConnector { + public SearchResult searchByName(final String name); +} diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java index f1bd2ce..4954017 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java @@ -1,10 +1,9 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.SearchResult; +import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.geopoint.Viewport; -public interface ISearchByViewPort { +public interface ISearchByViewPort extends IConnector { public SearchResult searchByViewport(final Viewport viewport, final String[] tokens); - - public boolean isActivated(); } diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index 9ac9c9d..d5f7bf4 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -1,12 +1,12 @@ package cgeo.geocaching.connector.gc; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.connector.ILoggingManager; import cgeo.geocaching.connector.capability.ILogin; @@ -21,7 +21,7 @@ import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import android.content.Context; @@ -80,7 +80,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, @Override public boolean supportsPersonalNote() { - return true; + return Settings.isPremiumMember(); } @Override @@ -137,10 +137,10 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, if (StringUtils.isEmpty(page)) { final SearchResult search = new SearchResult(); - if (cgData.isThere(geocode, guid, true, false)) { + if (DataStore.isThere(geocode, guid, true, false)) { if (StringUtils.isBlank(geocode) && StringUtils.isNotBlank(guid)) { Log.i("Loading old cache from cache."); - search.addGeocode(cgData.getGeocodeForGuid(guid)); + search.addGeocode(DataStore.getGeocodeForGuid(guid)); } else { search.addGeocode(geocode); } @@ -152,6 +152,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, search.setError(StatusCode.COMMUNICATION_ERROR); return search; } + assert page != null; final SearchResult searchResult = GCParser.parseCache(page, handler); @@ -189,7 +190,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public boolean addToWatchlist(Geocache cache) { final boolean added = GCParser.addToWatchlist(cache); if (added) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return added; } @@ -198,7 +199,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public boolean removeFromWatchlist(Geocache cache) { final boolean removed = GCParser.removeFromWatchlist(cache); if (removed) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return removed; } @@ -215,7 +216,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public static boolean addToFavorites(Geocache cache) { final boolean added = GCParser.addToFavorites(cache); if (added) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return added; } @@ -232,7 +233,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public static boolean removeFromFavorites(Geocache cache) { final boolean removed = GCParser.removeFromFavorites(cache); if (removed) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return removed; } @@ -241,7 +242,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) { final boolean uploaded = GCParser.uploadModifiedCoordinates(cache, wpt); if (uploaded) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return uploaded; } @@ -250,7 +251,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public boolean deleteModifiedCoordinates(Geocache cache) { final boolean deleted = GCParser.deleteModifiedCoordinates(cache); if (deleted) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return deleted; } @@ -259,7 +260,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, public boolean uploadPersonalNote(Geocache cache) { final boolean uploaded = GCParser.uploadPersonalNote(cache); if (uploaded) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return uploaded; } @@ -299,13 +300,13 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, final StatusCode status = Login.login(); if (status == StatusCode.NO_ERROR) { - cgeoapplication.getInstance().checkLogin = false; + CgeoApplication.getInstance().checkLogin = false; Login.detectGcCustomDate(); } - if (cgeoapplication.getInstance().showLoginToast && handler != null) { + if (CgeoApplication.getInstance().showLoginToast && handler != null) { handler.sendMessage(handler.obtainMessage(0, status)); - cgeoapplication.getInstance().showLoginToast = false; + CgeoApplication.getInstance().showLoginToast = false; // invoke settings activity to insert login details if (status == StatusCode.NO_LOGIN_INFO_STORED && fromActivity != null) { diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java index 84c27fe..152b8a2 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java @@ -58,7 +58,7 @@ public final class GCConstants { public final static Pattern PATTERN_ATTRIBUTES = Pattern.compile("Attributes\\s*</h3>[^<]*<div class=\"WidgetBody\">((?:[^<]*<img src=\"[^\"]+\" alt=\"[^\"]+\"[^>]*>)+?)[^<]*<p"); /** Two groups ! */ public final static Pattern PATTERN_ATTRIBUTESINSIDE = Pattern.compile("[^<]*<img src=\"([^\"]+)\" alt=\"([^\"]+?)\""); - public final static Pattern PATTERN_SPOILER_IMAGE = Pattern.compile("<a href=\"(http://img\\.geocaching\\.com[^.]+\\.(jpg|jpeg|png|gif))\"[^>]+>([^<]+)</a>(?:<br />([^<]+)<br /><br />)?"); + public final static Pattern PATTERN_SPOILER_IMAGE = Pattern.compile("<a href=\"(http://imgcdn\\.geocaching\\.com[^.]+\\.(jpg|jpeg|png|gif))\"[^>]+>([^<]+)</a>(?:<br />([^<]+)<br /><br />)?"); public final static Pattern PATTERN_INVENTORY = Pattern.compile("<span id=\"ctl00_ContentBody_uxTravelBugList_uxInventoryLabel\">\\W*Inventory[^<]*</span>[^<]*</h3>[^<]*<div class=\"WidgetBody\">([^<]*<ul>(([^<]*<li>[^<]*<a href=\"[^\"]+\"[^>]*>[^<]*<img src=\"[^\"]+\"[^>]*>[^<]*<span>[^<]+<\\/span>[^<]*<\\/a>[^<]*<\\/li>)+)[^<]*<\\/ul>)?"); public final static Pattern PATTERN_INVENTORYINSIDE = Pattern.compile("[^<]*<li>[^<]*<a href=\"[a-z0-9\\-\\_\\.\\?\\/\\:\\@]*\\/track\\/details\\.aspx\\?guid=([0-9a-z\\-]+)[^\"]*\"[^>]*>[^<]*<img src=\"[^\"]+\"[^>]*>[^<]*<span>([^<]+)<\\/span>[^<]*<\\/a>[^<]*<\\/li>"); public final static Pattern PATTERN_WATCHLIST = Pattern.compile(Pattern.quote("watchlist.aspx") + ".{1,50}" + Pattern.quote("action=rem")); @@ -69,7 +69,7 @@ public final class GCConstants { public static final String MEMBER_STATUS_RENEW = "<a id=\"ctl00_hlRenew"; public static final String MEMBER_STATUS_PM = "Premium Member"; /** Use replaceAll("[,.]","") on the resulting string before converting to an int */ - public static final Pattern PATTERN_CACHES_FOUND = Pattern.compile("<strong[^>]*>.*?([\\d,.]+) Caches Found", Pattern.DOTALL); + public static final Pattern PATTERN_CACHES_FOUND = Pattern.compile("<strong[^>]*>.*?([\\d,.]+) Caches? Found", Pattern.DOTALL); public static final Pattern PATTERN_AVATAR_IMAGE_PROFILE_PAGE = Pattern.compile("<img src=\"(http://img.geocaching.com/user/avatar/[0-9a-f-]+\\.jpg)\"[^>]*\\salt=\"Avatar\""); public static final Pattern PATTERN_LOGIN_NAME_LOGIN_PAGE = Pattern.compile("ctl00_ContentBody_lbUsername\">.*<strong>(.*)</strong>"); public static final Pattern PATTERN_CUSTOMDATE = Pattern.compile("<option selected=\"selected\" value=\"([ /Mdy-]+)\">"); @@ -135,12 +135,12 @@ public final class GCConstants { public final static Pattern PATTERN_WPTYPE = Pattern.compile("\\/wpttypes\\/sm\\/(.+)\\.jpg"); public final static Pattern PATTERN_WPPREFIXORLOOKUPORLATLON = Pattern.compile(">([^<]*<[^>]+>)?([^<]+)(<[^>]+>[^<]*)?<\\/td>"); public final static Pattern PATTERN_WPNAME = Pattern.compile(">[^<]*<a[^>]+>([^<]*)<\\/a>"); - public final static Pattern PATTERN_WPNOTE = Pattern.compile("colspan=\"6\">(.*)<\\/td>"); + public final static Pattern PATTERN_WPNOTE = Pattern.compile("colspan=\"6\">(.*)" + Pattern.quote("</td>"), Pattern.DOTALL); /** * Patterns for different purposes */ - /** replace linebreak and paragraph tags */ + /** replace line break and paragraph tags */ public final static Pattern PATTERN_LINEBREAK = Pattern.compile("<(br|p)[^>]*>"); public final static Pattern PATTERN_TYPEBOX = Pattern.compile("<select name=\"ctl00\\$ContentBody\\$LogBookPanel1\\$ddLogType\" id=\"ctl00_ContentBody_LogBookPanel1_ddLogType\"[^>]*>" + "(([^<]*<option[^>]*>[^<]+</option>)+)[^<]*</select>", Pattern.CASE_INSENSITIVE); @@ -168,7 +168,6 @@ public final class GCConstants { public final static String STRING_PREMIUMONLY_2 = "Sorry, the owner of this listing has made it viewable to Premium Members only."; public final static String STRING_PREMIUMONLY_1 = "has chosen to make this cache listing visible to Premium Members only."; - public final static String STRING_UNPUBLISHED_OWNER = "cache has not been published yet"; public final static String STRING_UNPUBLISHED_OTHER = "you cannot view this cache listing until it has been published"; public final static String STRING_UNPUBLISHED_FROM_SEARCH = "UnpublishedCacheSearchWidget"; public final static String STRING_UNKNOWN_ERROR = "An Error Has Occurred"; diff --git a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java index dfb1f55..8bed2ea 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java @@ -17,6 +17,7 @@ import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.Nullable; import android.net.Uri; import android.os.Bundle; @@ -42,6 +43,7 @@ public class GCLoggingManager implements ILoggingManager, LoaderManager.LoaderCa this.cache = cache; } + @Nullable @Override public Loader<String> onCreateLoader(int arg0, Bundle arg1) { if (!Settings.isLogin()) { // allow offline logging diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index 27d8a77..e2c7dfa 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCMap.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -1,9 +1,9 @@ package cgeo.geocaching.connector.gc; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; @@ -19,7 +19,7 @@ import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.utils.LeastRecentlyUsedMap; import cgeo.geocaching.utils.Log; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.json.JSONArray; import org.json.JSONException; @@ -255,7 +255,7 @@ public class GCMap { * @return */ public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) { - int speed = (int) cgeoapplication.getInstance().currentGeo().getSpeed() * 60 * 60 / 1000; // in km/h + int speed = (int) CgeoApplication.getInstance().currentGeo().getSpeed() * 60 * 60 / 1000; // in km/h Strategy strategy = Settings.getLiveMapStrategy(); if (strategy == Strategy.AUTO) { strategy = speed >= 30 ? Strategy.FAST : Strategy.DETAILED; @@ -361,7 +361,7 @@ public class GCMap { // Check for vanished found caches if (tiles.iterator().next().getZoomLevel() >= Tile.ZOOMLEVEL_MIN_PERSONALIZED) { - searchResult.addFilteredGeocodes(cgData.getCachedMissingFromSearch(searchResult, tiles, GCConnector.getInstance(), Tile.ZOOMLEVEL_MIN_PERSONALIZED - 1)); + searchResult.addFilteredGeocodes(DataStore.getCachedMissingFromSearch(searchResult, tiles, GCConnector.getInstance(), Tile.ZOOMLEVEL_MIN_PERSONALIZED - 1)); } } @@ -372,7 +372,7 @@ public class GCMap { SearchResult search = GCParser.searchByCoords(center, Settings.getCacheType(), false, null); if (search != null && !search.isEmpty()) { final Set<String> geocodes = search.getGeocodes(); - lastSearchViewport = cgData.getBounds(geocodes); + lastSearchViewport = DataStore.getBounds(geocodes); searchResult.addGeocodes(geocodes); } } diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index bd20216..869d27d 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -1,5 +1,7 @@ package cgeo.geocaching.connector.gc; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.Image; import cgeo.geocaching.LogEntry; @@ -9,8 +11,6 @@ import cgeo.geocaching.SearchResult; import cgeo.geocaching.Trackable; import cgeo.geocaching.TrackableLog; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; @@ -32,15 +32,18 @@ import cgeo.geocaching.ui.DirectionImage; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; +import cgeo.geocaching.utils.SynchronizedDateFormat; import cgeo.geocaching.utils.TextUtils; import ch.boye.httpclientandroidlib.HttpResponse; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -50,7 +53,6 @@ import android.text.Html; import java.io.File; import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; @@ -62,8 +64,8 @@ import java.util.Locale; import java.util.Set; public abstract class GCParser { - private final static SimpleDateFormat dateTbIn1 = new SimpleDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 - private final static SimpleDateFormat dateTbIn2 = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 + private final static SynchronizedDateFormat dateTbIn1 = new SynchronizedDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 + private final static SynchronizedDateFormat dateTbIn2 = new SynchronizedDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 private static SearchResult parseSearch(final String url, final String pageContent, final boolean showCaptcha, RecaptchaReceiver thread) { if (StringUtils.isBlank(pageContent)) { @@ -309,14 +311,10 @@ public abstract class GCParser { final String coordinates = Network.getResponseData(Network.postRequest("http://www.geocaching.com/seek/nearest.aspx", params), false); - if (StringUtils.isNotBlank(coordinates)) { - if (coordinates.contains("You have not agreed to the license agreement. The license agreement is required before you can start downloading GPX or LOC files from Geocaching.com")) { - Log.i("User has not agreed to the license agreement. Can\'t download .loc file."); - - searchResult.setError(StatusCode.UNAPPROVED_LICENSE); - - return searchResult; - } + if (StringUtils.contains(coordinates, "You have not agreed to the license agreement. The license agreement is required before you can start downloading GPX or LOC files from Geocaching.com")) { + Log.i("User has not agreed to the license agreement. Can\'t download .loc file."); + searchResult.setError(StatusCode.UNAPPROVED_LICENSE); + return searchResult; } LocParser.parseLoc(searchResult, coordinates); @@ -346,7 +344,7 @@ public abstract class GCParser { static SearchResult parseCache(final String page, final CancellableHandler handler) { final SearchResult searchResult = parseCacheFromText(page, handler); - // attention: parseCacheFromText already stores implicitely through searchResult.addCache + // attention: parseCacheFromText already stores implicitly through searchResult.addCache if (searchResult != null && !searchResult.getGeocodes().isEmpty()) { final Geocache cache = searchResult.getFirstCacheFromResult(LoadFlags.LOAD_CACHE_OR_DB); getExtraOnlineInfo(cache, page, handler); @@ -358,7 +356,7 @@ public abstract class GCParser { // save full detailed caches CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_cache); - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); // update progress message so user knows we're still working. This is more of a place holder than // actual indication of what the program is doing @@ -377,7 +375,7 @@ public abstract class GCParser { final SearchResult searchResult = new SearchResult(); - if (pageIn.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_OWNER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) { + if (pageIn.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) { searchResult.setError(StatusCode.UNPUBLISHED_CACHE); return searchResult; } @@ -668,7 +666,7 @@ public abstract class GCParser { final String originalCoords = TextUtils.getMatch(page, GCConstants.PATTERN_LATLON_ORIG, false, null); if (null != originalCoords) { - final Waypoint waypoint = new Waypoint(cgeoapplication.getInstance().getString(R.string.cache_coordinates_original), WaypointType.ORIGINAL, false); + final Waypoint waypoint = new Waypoint(CgeoApplication.getInstance().getString(R.string.cache_coordinates_original), WaypointType.ORIGINAL, false); waypoint.setCoords(new Geopoint(originalCoords)); cache.addOrChangeWaypoint(waypoint, false); cache.setUserModifiedCoords(true); @@ -707,7 +705,7 @@ public abstract class GCParser { // waypoint name // res is null during the unit tests - final String name = TextUtils.getMatch(wp[6], GCConstants.PATTERN_WPNAME, true, 1, cgeoapplication.getInstance().getString(R.string.waypoint), true); + final String name = TextUtils.getMatch(wp[6], GCConstants.PATTERN_WPNAME, true, 1, CgeoApplication.getInstance().getString(R.string.waypoint), true); // waypoint type final String resulttype = TextUtils.getMatch(wp[3], GCConstants.PATTERN_WPTYPE, null); @@ -720,7 +718,7 @@ public abstract class GCParser { // waypoint lookup waypoint.setLookup(TextUtils.getMatch(wp[5], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getLookup(), false)); - // waypoint latitude and logitude + // waypoint latitude and longitude latlon = Html.fromHtml(TextUtils.getMatch(wp[7], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, false, 2, "", false)).toString().trim(); if (!StringUtils.startsWith(latlon, "???")) { waypoint.setLatlon(latlon); @@ -848,6 +846,7 @@ public abstract class GCParser { * @param recaptchaReceiver * @return */ + @Nullable private static SearchResult searchByAny(final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params, RecaptchaReceiver recaptchaReceiver) { insertCacheType(params, cacheType); @@ -859,6 +858,7 @@ public abstract class GCParser { Log.e("GCParser.searchByAny: No data from server"); return null; } + assert page != null; final SearchResult searchResult = parseSearch(fullUri, page, showCaptcha, recaptchaReceiver); if (searchResult == null || CollectionUtils.isEmpty(searchResult.getGeocodes())) { @@ -956,6 +956,7 @@ public abstract class GCParser { return null; } + @Nullable public static Trackable searchTrackable(final String geocode, final String guid, final String id) { if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid) && StringUtils.isBlank(id)) { Log.w("GCParser.searchTrackable: No geocode nor guid nor id given"); @@ -980,6 +981,7 @@ public abstract class GCParser { Log.e("GCParser.searchTrackable: No data from server"); return trackable; } + assert page != null; trackable = parseTrackable(page, geocode); if (trackable == null) { @@ -1019,7 +1021,9 @@ public abstract class GCParser { maxCaches = 0; Log.e("GCParser.searchPocketQueryList: Unable to parse max caches", e); } - final PocketQueryList pqList = new PocketQueryList(matcherPocket.group(2), matcherPocket.group(3), maxCaches); + final String guid = Html.fromHtml(matcherPocket.group(2)).toString(); + final String name = Html.fromHtml(matcherPocket.group(3)).toString(); + final PocketQueryList pqList = new PocketQueryList(guid, name, maxCaches); list.add(pqList); } @@ -1142,7 +1146,7 @@ public abstract class GCParser { Log.i("Log successfully posted to cache #" + cacheid); if (geocode != null) { - cgData.saveVisitDate(geocode); + DataStore.saveVisitDate(geocode); } Login.getLoginStatus(page); @@ -1184,6 +1188,7 @@ public abstract class GCParser { Log.e("GCParser.uploadLogImage: No data from server"); return new ImmutablePair<StatusCode, String>(StatusCode.UNKNOWN_ERROR, null); } + assert page != null; final String[] viewstates = Login.getViewstates(page); @@ -1341,7 +1346,8 @@ public abstract class GCParser { return !guidOnPage; // on watch list (=error) / not on watch list } - static String requestHtmlPage(final String geocode, final String guid, final String log, final String numlogs) { + @Nullable + static String requestHtmlPage(@Nullable final String geocode, @Nullable final String guid, final String log, final String numlogs) { final Parameters params = new Parameters("decrypt", "y"); if (StringUtils.isNotBlank(geocode)) { params.put("wp", geocode); @@ -1529,7 +1535,7 @@ public abstract class GCParser { Log.w("GCParser.parseTrackable: Failed to parse trackable details & image"); } if (StringUtils.isEmpty(trackable.getDetails()) && page.contains(GCConstants.ERROR_TB_NOT_ACTIVATED)) { - trackable.setDetails(cgeoapplication.getInstance().getString(R.string.trackable_not_activated)); + trackable.setDetails(CgeoApplication.getInstance().getString(R.string.trackable_not_activated)); } // trackable logs @@ -1586,8 +1592,8 @@ public abstract class GCParser { trackable.setTrackingcode(possibleTrackingcode); } - if (cgeoapplication.getInstance() != null) { - cgData.saveTrackable(trackable); + if (CgeoApplication.getInstance() != null) { + DataStore.saveTrackable(trackable); } return trackable; @@ -1649,8 +1655,17 @@ public abstract class GCParser { rawResponse = TextUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, ""); } + return parseLogs(friends, rawResponse); + } + + private static List<LogEntry> parseLogs(final boolean friends, String rawResponse) { final List<LogEntry> logs = new ArrayList<LogEntry>(); + // for non logged in users the log book is not shown + if (StringUtils.isBlank(rawResponse)) { + return logs; + } + try { final JSONObject resp = new JSONObject(rawResponse); if (!resp.getString("status").equals("success")) { @@ -1677,7 +1692,7 @@ public abstract class GCParser { // TODO: we should update our log data structure to be able to record // proper coordinates, and make them clickable. In the meantime, it is // better to integrate those coordinates into the text rather than not - // display them as all. + // display them at all. final String latLon = entry.getString("LatLonString"); final LogEntry logDone = new LogEntry( entry.getString("UserName"), @@ -1706,9 +1721,10 @@ public abstract class GCParser { return logs; } + @NonNull public static List<LogType> parseTypes(String page) { if (StringUtils.isEmpty(page)) { - return null; + return Collections.emptyList(); } final List<LogType> types = new ArrayList<LogType>(); diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java index 0d8fb05..beb49f1 100644 --- a/main/src/cgeo/geocaching/connector/gc/Login.java +++ b/main/src/cgeo/geocaching/connector/gc/Login.java @@ -1,7 +1,7 @@ package cgeo.geocaching.connector.gc; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.network.Cookies; import cgeo.geocaching.network.HtmlImage; @@ -13,10 +13,11 @@ import cgeo.geocaching.utils.MatcherWrapper; import cgeo.geocaching.utils.TextUtils; import ch.boye.httpclientandroidlib.HttpResponse; - import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import android.graphics.drawable.BitmapDrawable; @@ -76,7 +77,7 @@ public abstract class Login { return StatusCode.NO_LOGIN_INFO_STORED; } - Login.setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_working)); + Login.setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_working)); HttpResponse loginResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx"); String loginData = Network.getResponseData(loginResponse); if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && TextUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) { @@ -119,6 +120,7 @@ public abstract class Login { // FIXME: should it be CONNECTION_FAILED to match the first attempt? return StatusCode.COMMUNICATION_ERROR; // no login page } + assert loginData != null; // Caught above if (Login.getLoginStatus(loginData)) { Log.i("Successfully logged in Geocaching.com as " + login.left + " (" + Settings.getMemberStatus() + ')'); @@ -171,7 +173,7 @@ public abstract class Login { resetLoginStatus(); setActualCachesFound(-1); - setActualStatus(cgeoapplication.getInstance().getString(R.string.err_login)); + setActualStatus(CgeoApplication.getInstance().getString(R.string.err_login)); } static void setActualCachesFound(final int found) { @@ -212,13 +214,14 @@ public abstract class Login { * @param page * @return <code>true</code> if user is logged in, <code>false</code> otherwise */ - public static boolean getLoginStatus(final String page) { + public static boolean getLoginStatus(@Nullable final String page) { if (StringUtils.isBlank(page)) { Log.e("Login.checkLogin: No page given"); return false; } + assert page != null; - setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_ok)); + setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_ok)); // on every page except login page setActualLoginStatus(TextUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME)); @@ -246,7 +249,7 @@ public abstract class Login { return true; } - setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_failed)); + setActualStatus(CgeoApplication.getInstance().getString(R.string.init_login_popup_failed)); return false; } @@ -276,7 +279,8 @@ public abstract class Login { public static BitmapDrawable downloadAvatarAndGetMemberStatus() { try { - final String profile = TextUtils.replaceWhitespace(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/"))); + final String responseData = StringUtils.defaultString(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/"))); + final String profile = TextUtils.replaceWhitespace(responseData); Settings.setMemberStatus(TextUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null)); if (profile.contains(GCConstants.MEMBER_STATUS_RENEW)) { @@ -473,10 +477,13 @@ public abstract class Login { * @param params * @return */ - public static String getRequestLogged(final String uri, final Parameters params) { - final String data = Network.getResponseData(Network.getRequest(uri, params), canRemoveWhitespace(uri)); + @Nullable + public static String getRequestLogged(@NonNull final String uri, @Nullable final Parameters params) { + final HttpResponse response = Network.getRequest(uri, params); + final String data = Network.getResponseData(response, canRemoveWhitespace(uri)); - if (getLoginStatus(data)) { + // A page not found will not be found if the user logs in either + if (Network.isPageNotFound(response) || getLoginStatus(data)) { return data; } diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java index 4ed53c9..623730a 100644 --- a/main/src/cgeo/geocaching/connector/gc/Tile.java +++ b/main/src/cgeo/geocaching/connector/gc/Tile.java @@ -29,11 +29,6 @@ import java.util.Set; */ public class Tile { - public static final double LATITUDE_MIN = -85.05112878; - public static final double LATITUDE_MAX = 85.05112878; - public static final double LONGITUDE_MIN = -180; - public static final double LONGITUDE_MAX = 180; - public static final int TILE_SIZE = 256; public static final int ZOOMLEVEL_MAX = 18; public static final int ZOOMLEVEL_MIN = 0; diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGrid.java b/main/src/cgeo/geocaching/connector/gc/UTFGrid.java index 6d20eb6..89a3de8 100644 --- a/main/src/cgeo/geocaching/connector/gc/UTFGrid.java +++ b/main/src/cgeo/geocaching/connector/gc/UTFGrid.java @@ -12,22 +12,6 @@ public final class UTFGrid { public static final int GRID_MAXX = 63; public static final int GRID_MAXY = 63; - /** - * Convert a value from a JSON grid object into an id that can be used as an index - * It's not used at the moment due to optimizations. - * But maybe we need it some day... - */ - public static short getUTFGridId(final char value) { - short result = (short) value; - if (result >= 93) { - result--; - } - if (result >= 35) { - result--; - } - return (short) (result - 32); - } - /** Calculate from a list of positions (x/y) the coords */ public static UTFGridPosition getPositionInGrid(List<UTFGridPosition> positions) { int minX = GRID_MAXX; diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java index 5965fff..eff193a 100644 --- a/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java +++ b/main/src/cgeo/geocaching/connector/gc/UTFGridPosition.java @@ -15,9 +15,12 @@ public final class UTFGridPosition { private final static Pattern PATTERN_JSON_KEY = Pattern.compile("[^\\d]*" + "(\\d+),\\s*(\\d+)" + "[^\\d]*"); // (12, 34) public UTFGridPosition(final int x, final int y) { - assert x >= 0 && x <= UTFGrid.GRID_MAXX : "x outside bounds"; - assert y >= 0 && y <= UTFGrid.GRID_MAXY : "y outside bounds"; - + if (x < 0 || x > UTFGrid.GRID_MAXX) { + throw new IllegalArgumentException("x outside bounds"); + } + if (y < 0 || y > UTFGrid.GRID_MAXY) { + throw new IllegalArgumentException("y outside bounds"); + } this.x = x; this.y = y; } diff --git a/main/src/cgeo/geocaching/connector/oc/IOCAuthParams.java b/main/src/cgeo/geocaching/connector/oc/IOCAuthParams.java index 5a140a1..dacb626 100644 --- a/main/src/cgeo/geocaching/connector/oc/IOCAuthParams.java +++ b/main/src/cgeo/geocaching/connector/oc/IOCAuthParams.java @@ -3,58 +3,65 @@ package cgeo.geocaching.connector.oc; public interface IOCAuthParams { /** - * The site name: 'www.openaching...' - * + * The site name: 'www.opencaching...' + * * @return */ String getSite(); /** * ResId of the Consumer key - * + * * @return */ int getCKResId(); /** * ResId of the Consumer secret - * + * * @return */ int getCSResId(); /** - * ResId ot the Authorization title - * + * ResId of the Authorization title + * * @return */ - int getAuthTitelResId(); + int getAuthTitleResId(); /** * Preference key of the public token - * + * * @return */ int getTokenPublicPrefKey(); /** * Preference key of the secret token - * + * * @return */ int getTokenSecretPrefKey(); /** * Preference key of the temporary public token (OAuth) - * + * * @return */ int getTempTokenPublicPrefKey(); /** * Preference key of the temporary secret token (OAuth) - * + * * @return */ int getTempTokenSecretPrefKey(); + + /** + * The URI to use as a callback (OAuth) + * + * @return + */ + String getCallbackUri(); } diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java index 663bbf7..b1b9088 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java @@ -1,13 +1,14 @@ package cgeo.geocaching.connector.oc; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.ILoggingManager; import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; +import cgeo.geocaching.connector.capability.ISearchByKeyword; import cgeo.geocaching.connector.capability.ISearchByViewPort; import cgeo.geocaching.connector.oc.UserInfo.UserInfoStatus; import cgeo.geocaching.geopoint.Geopoint; @@ -20,7 +21,7 @@ import org.apache.commons.lang3.StringUtils; import android.content.Context; import android.os.Handler; -public class OCApiLiveConnector extends OCApiConnector implements ISearchByCenter, ISearchByViewPort, ILogin { +public class OCApiLiveConnector extends OCApiConnector implements ISearchByCenter, ISearchByViewPort, ILogin, ISearchByKeyword { private final String cS; private final int isActivePrefKeyId; @@ -29,9 +30,9 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente private UserInfo userInfo = new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.NOT_RETRIEVED); public OCApiLiveConnector(String name, String host, String prefix, String licenseString, int cKResId, int cSResId, int isActivePrefKeyId, int tokenPublicPrefKeyId, int tokenSecretPrefKeyId, ApiSupport apiSupport) { - super(name, host, prefix, CryptUtils.rot13(cgeoapplication.getInstance().getResources().getString(cKResId)), licenseString, apiSupport); + super(name, host, prefix, CryptUtils.rot13(CgeoApplication.getInstance().getResources().getString(cKResId)), licenseString, apiSupport); - cS = CryptUtils.rot13(cgeoapplication.getInstance().getResources().getString(cSResId)); + cS = CryptUtils.rot13(CgeoApplication.getInstance().getResources().getString(cSResId)); this.isActivePrefKeyId = isActivePrefKeyId; this.tokenPublicPrefKeyId = tokenPublicPrefKeyId; this.tokenSecretPrefKeyId = tokenSecretPrefKeyId; @@ -87,7 +88,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente final boolean added = OkapiClient.setWatchState(cache, true, this); if (added) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return added; @@ -98,7 +99,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente final boolean removed = OkapiClient.setWatchState(cache, false, this); if (removed) { - cgData.saveChangedCache(cache); + DataStore.saveChangedCache(cache); } return removed; @@ -145,11 +146,17 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente @Override public String getLoginStatusString() { - return cgeoapplication.getInstance().getString(userInfo.getStatus().resId); + return CgeoApplication.getInstance().getString(userInfo.getStatus().resId); } @Override public boolean isLoggedIn() { return userInfo.getStatus() == UserInfoStatus.SUCCESSFUL; } + + @Override + public SearchResult searchByName(final String name) { + final Geopoint currentPos = CgeoApplication.getInstance().currentGeo().getCoords(); + return new SearchResult(OkapiClient.getCachesNamed(currentPos, name, this)); + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java b/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java index 10d6a66..c082bac 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java @@ -1,13 +1,14 @@ package cgeo.geocaching.connector.oc; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.network.OAuthAuthorizationActivity; import cgeo.geocaching.settings.Settings; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.Nullable; -public class OCAuthorizationActivity extends OAuthAuthorizationActivity { +public abstract class OCAuthorizationActivity extends OAuthAuthorizationActivity { final IOCAuthParams authParams; @@ -17,8 +18,9 @@ public class OCAuthorizationActivity extends OAuthAuthorizationActivity { "/okapi/services/oauth/authorize", "/okapi/services/oauth/access_token", false, - cgeoapplication.getInstance().getResources().getString(authParams.getCKResId()), - cgeoapplication.getInstance().getResources().getString(authParams.getCSResId())); + CgeoApplication.getInstance().getResources().getString(authParams.getCKResId()), + CgeoApplication.getInstance().getResources().getString(authParams.getCSResId()), + authParams.getCallbackUri()); this.authParams = authParams; } @@ -28,12 +30,12 @@ public class OCAuthorizationActivity extends OAuthAuthorizationActivity { } @Override - protected void setTempTokens(String tokenPublic, String tokenSecret) { + protected void setTempTokens(@Nullable final String tokenPublic, @Nullable final String tokenSecret) { Settings.setTokens(authParams.getTempTokenPublicPrefKey(), tokenPublic, authParams.getTempTokenSecretPrefKey(), tokenSecret); } @Override - protected void setTokens(String tokenPublic, String tokenSecret, boolean enable) { + protected void setTokens(@Nullable final String tokenPublic, @Nullable final String tokenSecret, final boolean enable) { Settings.setTokens(authParams.getTokenPublicPrefKey(), tokenPublic, authParams.getTokenSecretPrefKey(), tokenSecret); if (tokenPublic != null) { Settings.setTokens(authParams.getTempTokenPublicPrefKey(), null, authParams.getTempTokenSecretPrefKey(), null); @@ -42,22 +44,7 @@ public class OCAuthorizationActivity extends OAuthAuthorizationActivity { @Override protected String getAuthTitle() { - return res.getString(authParams.getAuthTitelResId()); - } - - @Override - protected String getAuthAgain() { - return res.getString(R.string.auth_again_oc); - } - - @Override - protected String getErrAuthInitialize() { - return res.getString(R.string.err_auth_initialize); - } - - @Override - protected String getAuthStart() { - return res.getString(R.string.auth_start_oc); + return res.getString(authParams.getAuthTitleResId()); } @Override @@ -65,48 +52,4 @@ public class OCAuthorizationActivity extends OAuthAuthorizationActivity { return res.getString(R.string.auth_dialog_completed_oc, getAuthTitle()); } - @Override - protected String getErrAuthProcess() { - return res.getString(R.string.err_auth_process); - } - - @Override - protected String getAuthDialogWait() { - return res.getString(R.string.auth_dialog_wait_oc, getAuthTitle()); - } - - @Override - protected String getAuthDialogPinTitle() { - return res.getString(R.string.auth_dialog_pin_title_oc); - } - - @Override - protected String getAuthDialogPinMessage() { - return res.getString(R.string.auth_dialog_pin_message_oc, getAuthTitle()); - } - - @Override - protected String getAboutAuth1() { - return res.getString(R.string.about_auth_1_oc, getAuthTitle()); - } - - @Override - protected String getAboutAuth2() { - return res.getString(R.string.about_auth_2_oc, getAuthTitle(), getAuthTitle()); - } - - @Override - protected String getAuthAuthorize() { - return res.getString(R.string.auth_authorize_oc); - } - - @Override - protected String getAuthPinHint() { - return res.getString(R.string.auth_pin_hint_oc, getAuthTitle()); - } - - @Override - protected String getAuthFinish() { - return res.getString(R.string.auth_finish_oc); - } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCConnector.java b/main/src/cgeo/geocaching/connector/oc/OCConnector.java index 29cdd10..b5c62ea 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCConnector.java @@ -4,7 +4,10 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import cgeo.geocaching.R; import cgeo.geocaching.connector.AbstractConnector; +import cgeo.geocaching.enumerations.LogType; +import java.util.Arrays; +import java.util.List; import java.util.regex.Pattern; public class OCConnector extends AbstractConnector { @@ -14,6 +17,9 @@ public class OCConnector extends AbstractConnector { private final Pattern codePattern; private static final Pattern GPX_ZIP_FILE_PATTERN = Pattern.compile("oc[a-z]{2,3}\\d{5,}\\.zip", Pattern.CASE_INSENSITIVE); + private static final List<LogType> STANDARD_LOG_TYPES = Arrays.asList(LogType.FOUND_IT, LogType.DIDNT_FIND_IT, LogType.NOTE); + private static final List<LogType> EVENT_LOG_TYPES = Arrays.asList(LogType.WILL_ATTEND, LogType.ATTENDED, LogType.NOTE); + public OCConnector(final String name, final String host, final String prefix) { this.name = name; this.host = host; @@ -66,4 +72,12 @@ public class OCConnector extends AbstractConnector { return R.drawable.marker_oc; } + @Override + public final List<LogType> getPossibleLogTypes(Geocache cache) { + if (cache.isEventCache()) { + return EVENT_LOG_TYPES; + } + + return STANDARD_LOG_TYPES; + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCDEAuthParams.java b/main/src/cgeo/geocaching/connector/oc/OCDEAuthParams.java index 734b383..17c1cb8 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCDEAuthParams.java +++ b/main/src/cgeo/geocaching/connector/oc/OCDEAuthParams.java @@ -20,7 +20,7 @@ public class OCDEAuthParams implements IOCAuthParams { } @Override - public int getAuthTitelResId() { + public int getAuthTitleResId() { return R.string.auth_ocde; } @@ -43,4 +43,9 @@ public class OCDEAuthParams implements IOCAuthParams { public int getTempTokenSecretPrefKey() { return R.string.pref_temp_ocde_token_secret; } + + @Override + public String getCallbackUri() { + return "callback://www.cgeo.org/opencaching.de/"; + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCDEAuthorizationActivity.java b/main/src/cgeo/geocaching/connector/oc/OCDEAuthorizationActivity.java index caf114c..1d2aa49 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCDEAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/connector/oc/OCDEAuthorizationActivity.java @@ -1,6 +1,6 @@ package cgeo.geocaching.connector.oc; -public class OCDEAuthorizationActivity extends OCAuthorizationActivity { +public final class OCDEAuthorizationActivity extends OCAuthorizationActivity { public OCDEAuthorizationActivity() { super(new OCDEAuthParams()); diff --git a/main/src/cgeo/geocaching/connector/oc/OCPLAuthParams.java b/main/src/cgeo/geocaching/connector/oc/OCPLAuthParams.java index 117e990..dfe03e5 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCPLAuthParams.java +++ b/main/src/cgeo/geocaching/connector/oc/OCPLAuthParams.java @@ -20,7 +20,7 @@ public class OCPLAuthParams implements IOCAuthParams { } @Override - public int getAuthTitelResId() { + public int getAuthTitleResId() { return R.string.auth_ocpl; } @@ -43,4 +43,9 @@ public class OCPLAuthParams implements IOCAuthParams { public int getTempTokenSecretPrefKey() { return R.string.pref_temp_ocpl_token_secret; } + + @Override + public String getCallbackUri() { + return "callback://www.cgeo.org/opencaching.pl/"; + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCPLAuthorizationActivity.java b/main/src/cgeo/geocaching/connector/oc/OCPLAuthorizationActivity.java index 4f9ef96..30ea150 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCPLAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/connector/oc/OCPLAuthorizationActivity.java @@ -1,6 +1,6 @@ package cgeo.geocaching.connector.oc; -public class OCPLAuthorizationActivity extends OCAuthorizationActivity { +public final class OCPLAuthorizationActivity extends OCAuthorizationActivity { public OCPLAuthorizationActivity() { super(new OCPLAuthParams()); diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java index 189b6a4..686e314 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java @@ -1,12 +1,12 @@ package cgeo.geocaching.connector.oc; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.Image; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.connector.LogResult; @@ -29,9 +29,15 @@ import cgeo.geocaching.network.OAuth; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.SynchronizedDateFormat; + +import ch.boye.httpclientandroidlib.HttpResponse; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.FastDateFormat; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -39,7 +45,6 @@ import org.json.JSONObject; import android.net.Uri; import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; @@ -56,12 +61,8 @@ final class OkapiClient { private static final char SEPARATOR = '|'; private static final String SEPARATOR_STRING = Character.toString(SEPARATOR); - private static final SimpleDateFormat LOG_DATE_FORMAT; - static { - LOG_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.US); - LOG_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); - } - private static final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); + private static final FastDateFormat LOG_DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSSZ", TimeZone.getTimeZone("UTC"), Locale.US); + private static final SynchronizedDateFormat ISO8601DATEFORMAT = new SynchronizedDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); private static final String CACHE_ATTRNAMES = "attrnames"; private static final String WPT_LOCATION = "location"; @@ -81,7 +82,6 @@ final class OkapiClient { private static final String CACHE_LATEST_LOGS = "latest_logs"; private static final String CACHE_IMAGE_URL = "url"; private static final String CACHE_IMAGE_CAPTION = "caption"; - private static final String CACHE_IMAGE_IS_SPOILER = "is_spoiler"; private static final String CACHE_IMAGES = "images"; private static final String CACHE_HINT = "hint"; private static final String CACHE_DESCRIPTION = "description"; @@ -110,14 +110,15 @@ final class OkapiClient { // the several realms of possible fields for cache retrieval: // Core: for livemap requests (L3 - only with level 3 auth) // Additional: additional fields for full cache (L3 - only for level 3 auth, current - only for connectors with current api) - private static final String SERVICE_CACHE_CORE_FIELDS = "code|name|location|type|status|difficulty|terrain|size"; + private static final String SERVICE_CACHE_CORE_FIELDS = "code|name|location|type|status|difficulty|terrain|size|date_hidden"; private static final String SERVICE_CACHE_CORE_L3_FIELDS = "is_found"; - private static final String SERVICE_CACHE_ADDITIONAL_FIELDS = "owner|founds|notfounds|rating|rating_votes|recommendations|description|hint|images|latest_logs|date_hidden|alt_wpts|attrnames|req_passwd"; + private static final String SERVICE_CACHE_ADDITIONAL_FIELDS = "owner|founds|notfounds|rating|rating_votes|recommendations|description|hint|images|latest_logs|alt_wpts|attrnames|req_passwd"; private static final String SERVICE_CACHE_ADDITIONAL_CURRENT_FIELDS = "gc_code|attribution_note"; private static final String SERVICE_CACHE_ADDITIONAL_L3_FIELDS = "is_watched|my_notes"; - private static final String METHOD_SEARCH_NEAREST = "services/caches/search/nearest"; + private static final String METHOD_SEARCH_ALL = "services/caches/search/all"; private static final String METHOD_SEARCH_BBOX = "services/caches/search/bbox"; + private static final String METHOD_SEARCH_NEAREST = "services/caches/search/nearest"; private static final String METHOD_RETRIEVE_CACHES = "services/caches/geocaches"; public static Geocache getCache(final String geoCode) { @@ -132,13 +133,9 @@ final class OkapiClient { params.add("fields", getFullFields(ocapiConn)); params.add("attribution_append", "none"); - final JSONObject data = request(ocapiConn, OkapiService.SERVICE_CACHE, params); - - if (data == null) { - return null; - } + final JSONResult result = request(ocapiConn, OkapiService.SERVICE_CACHE, params); - return parseCache(data); + return result.isSuccess ? parseCache(result.data) : null; } public static List<Geocache> getCachesAround(final Geopoint center, final OCApiConnector connector) { @@ -152,12 +149,38 @@ final class OkapiClient { return requestCaches(connector, params, valueMap); } + public static List<Geocache> getCachesNamed(final Geopoint center, final String namePart, final OCApiConnector connector) { + final Map<String, String> valueMap = new LinkedHashMap<String, String>(); + final Parameters params; + + // search around current position, if there is a position + if (center != null) { + final String centerString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center) + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center); + params = new Parameters("search_method", METHOD_SEARCH_NEAREST); + valueMap.put("center", centerString); + valueMap.put("limit", "20"); + } + else { + params = new Parameters("search_method", METHOD_SEARCH_ALL); + valueMap.put("limit", "20"); + } + + // full wildcard search, maybe we need to change this after some testing and evaluation + valueMap.put("name", "*" + namePart + "*"); + return requestCaches(connector, params, valueMap); + } + private static List<Geocache> requestCaches(final OCApiConnector connector, final Parameters params, final Map<String, String> valueMap) { + // if a global type filter is set, and OKAPI does not know that type, then return an empty list instead of all caches + if (Settings.getCacheType() != CacheType.ALL && StringUtils.isBlank(getFilterFromType())) { + return Collections.emptyList(); + } + addFilterParams(valueMap, connector); params.add("search_params", new JSONObject(valueMap).toString()); addRetrieveParams(params, connector); - final JSONObject data = request(connector, OkapiService.SERVICE_SEARCH_AND_RETRIEVE, params); + final JSONObject data = request(connector, OkapiService.SERVICE_SEARCH_AND_RETRIEVE, params).data; if (data == null) { return Collections.emptyList(); @@ -166,7 +189,9 @@ final class OkapiClient { return parseCaches(data); } - // Assumes level 3 OAuth + /** + * Assumes level 3 OAuth. + */ public static List<Geocache> getCachesBBox(final Viewport viewport, final OCApiConnector connector) { if (viewport.getLatitudeSpan() == 0 || viewport.getLongitudeSpan() == 0) { @@ -188,7 +213,7 @@ final class OkapiClient { final Parameters params = new Parameters("cache_code", cache.getGeocode()); params.add("watched", watched ? "true" : "false"); - final JSONObject data = request(connector, OkapiService.SERVICE_MARK_CACHE, params); + final JSONObject data = request(connector, OkapiService.SERVICE_MARK_CACHE, params).data; if (data == null) { return false; @@ -212,7 +237,7 @@ final class OkapiClient { params.add("password", logPassword); } - final JSONObject data = request(connector, OkapiService.SERVICE_SUBMIT_LOG, params); + final JSONObject data = request(connector, OkapiService.SERVICE_SUBMIT_LOG, params).data; if (data == null) { return new LogResult(StatusCode.LOG_POST_ERROR, ""); @@ -265,7 +290,7 @@ final class OkapiClient { parseCoreCache(response, cache); - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE)); + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE)); } catch (final JSONException e) { Log.e("OkapiClient.parseSmallCache", e); } @@ -297,7 +322,7 @@ final class OkapiClient { final StringBuilder description = new StringBuilder(500); if (!response.isNull("gc_code")) { final String gccode = response.getString("gc_code"); - description.append(cgeoapplication.getInstance().getResources() + description.append(CgeoApplication.getInstance().getResources() .getString(R.string.cache_listed_on, GCConnector.getInstance().getName())) .append(": <a href=\"http://coord.info/") .append(gccode) @@ -316,17 +341,15 @@ final class OkapiClient { if (images != null) { for (int i = 0; i < images.length(); i++) { final JSONObject imageResponse = images.getJSONObject(i); - if (imageResponse.getBoolean(CACHE_IMAGE_IS_SPOILER)) { - final String title = imageResponse.getString(CACHE_IMAGE_CAPTION); - final String url = absoluteUrl(imageResponse.getString(CACHE_IMAGE_URL), cache.getGeocode()); - cache.addSpoiler(new Image(url, title)); - } + final String title = imageResponse.getString(CACHE_IMAGE_CAPTION); + final String url = absoluteUrl(imageResponse.getString(CACHE_IMAGE_URL), cache.getGeocode()); + // all images are added as spoiler images, although OKAPI has spoiler and non spoiler images + cache.addSpoiler(new Image(url, title)); } } cache.setAttributes(parseAttributes(response.getJSONArray(CACHE_ATTRNAMES))); cache.setLogs(parseLogs(response.getJSONArray(CACHE_LATEST_LOGS))); - cache.setHidden(parseDate(response.getString(CACHE_HIDDEN))); //TODO: Store license per cache //cache.setLicense(response.getString("attribution_note")); cache.setWaypoints(parseWaypoints(response.getJSONArray(CACHE_WPTS)), false); @@ -340,7 +363,7 @@ final class OkapiClient { cache.setDetailedUpdatedNow(); // save full detailed caches - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); } catch (final JSONException e) { Log.e("OkapiClient.parseCache", e); } @@ -365,6 +388,7 @@ final class OkapiClient { if (!response.isNull(CACHE_IS_FOUND)) { cache.setFound(response.getBoolean(CACHE_IS_FOUND)); } + cache.setHidden(parseDate(response.getString(CACHE_HIDDEN))); } private static String absoluteUrl(final String url, final String geocode) { @@ -599,14 +623,15 @@ final class OkapiClient { return res.toString(); } - private static JSONObject request(final OCApiConnector connector, final OkapiService service, final Parameters params) { + @NonNull + private static JSONResult request(final OCApiConnector connector, final OkapiService service, final Parameters params) { if (connector == null) { - return null; + return new JSONResult(null); } final String host = connector.getHost(); if (StringUtils.isBlank(host)) { - return null; + return new JSONResult(null); } params.add("langpref", getPreferredLanguage()); @@ -619,7 +644,7 @@ final class OkapiClient { } final String uri = "http://" + host + service.methodName; - return Network.requestJSON(uri, params); + return new JSONResult(Network.getRequest(uri, params)); } private static String getPreferredLanguage() { @@ -639,7 +664,7 @@ final class OkapiClient { valueMap.put("found_status", "notfound_only"); } if (Settings.getCacheType() != CacheType.ALL) { - valueMap.put("type", getFilterFromType(Settings.getCacheType())); + valueMap.put("type", getFilterFromType()); } } @@ -649,8 +674,8 @@ final class OkapiClient { params.add("wrap", "true"); } - private static String getFilterFromType(final CacheType cacheType) { - switch (cacheType) { + private static String getFilterFromType() { + switch (Settings.getCacheType()) { case EVENT: return "Event"; case MULTI: @@ -671,12 +696,16 @@ final class OkapiClient { public static UserInfo getUserInfo(final OCApiLiveConnector connector) { final Parameters params = new Parameters("fields", USER_INFO_FIELDS); - final JSONObject data = request(connector, OkapiService.SERVICE_USER, params); + final JSONResult result = request(connector, OkapiService.SERVICE_USER, params); - if (data == null) { - return new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.FAILED); + if (!result.isSuccess) { + final OkapiError error = new OkapiError(result.data); + Log.e("OkapiClient.getUserInfo: error getting user info: '" + error.getMessage() + "'"); + return new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.getFromOkapiError(error.getResult())); } + JSONObject data = result.data; + String name = StringUtils.EMPTY; boolean successUserName = false; @@ -704,4 +733,28 @@ final class OkapiClient { return new UserInfo(name, finds, successUserName && successFinds ? UserInfoStatus.SUCCESSFUL : UserInfoStatus.FAILED); } + /** + * Encapsulates response state and content of an HTTP-request that expects a JSON result. <code>isSuccess</code> is + * only true, if the response state was success and <code>data</code> is not null. + */ + private static class JSONResult { + + public final boolean isSuccess; + public final JSONObject data; + + public JSONResult(final @Nullable HttpResponse response) { + boolean isSuccess = Network.isSuccess(response); + final String responseData = Network.getResponseDataAlways(response); + JSONObject data = null; + if (responseData != null) { + try { + data = new JSONObject(responseData); + } catch (final JSONException e) { + Log.w("JSONResult", e); + } + } + this.data = data; + this.isSuccess = isSuccess && data != null; + } + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiError.java b/main/src/cgeo/geocaching/connector/oc/OkapiError.java new file mode 100644 index 0000000..7faf2c7 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/oc/OkapiError.java @@ -0,0 +1,87 @@ +package cgeo.geocaching.connector.oc; + +import cgeo.geocaching.utils.Log; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Handles the JSON error response from OKAPI + */ +public class OkapiError { + + /** + * List of detected errors OKAPI might return + */ + public enum OkapiErrors { + NO_ERROR, + UNSPECIFIED, + INVALID_TIMESTAMP, + INVALID_TOKEN; + } + + @NonNull private final OkapiErrors state; + @NonNull private final String message; + + public OkapiError(@Nullable JSONObject data) { + + // A null-response is by definition an error (some exception occurred somewhere in the flow) + if (data == null) { + state = OkapiErrors.UNSPECIFIED; + message = StringUtils.EMPTY; + return; + } + // Second possibility: we get an error object as return (@see http://opencaching.pl/okapi/introduction.html#errors) + if (data.has("error")) { + String localmessage = null; + OkapiErrors localstate = OkapiErrors.UNSPECIFIED; + try { + JSONObject error = data.getJSONObject("error"); + // Check reason_stack element to look for the specific oauth problems we want to report back + if (error.has("reason_stack")) { + String reason = error.getString("reason_stack"); + if (StringUtils.contains(reason, "invalid_oauth_request")) { + if (StringUtils.contains(reason, "invalid_timestamp")) { + localstate = OkapiErrors.INVALID_TIMESTAMP; + } else if (StringUtils.contains(reason, "invalid_token")) { + localstate = OkapiErrors.INVALID_TOKEN; + } + } + } + // Check if we can extract a message as well + if (error.has("developer_message")) { + localmessage = error.getString("developer_message"); + assert localmessage != null; // by virtue of defaultString + } + } catch (JSONException ex) { + Log.d("OkapiError: Failed to parse JSON", ex); + localstate = OkapiErrors.UNSPECIFIED; + } + state = localstate; + message = StringUtils.defaultString(localmessage); + return; + } + + // Third possibility: some other response, everything is fine! + state = OkapiErrors.NO_ERROR; + message = StringUtils.EMPTY; + } + + public boolean isError() { + return state != OkapiErrors.NO_ERROR; + } + + @NonNull + public OkapiErrors getResult() { + return state; + } + + @NonNull + public String getMessage() { + return message; + } + +} diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java index 9ffe7a2..c6be3cb 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java @@ -11,7 +11,6 @@ import cgeo.geocaching.enumerations.StatusCode; import android.net.Uri; -import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.List; @@ -22,48 +21,42 @@ public class OkapiLoggingManager implements ILoggingManager { private final Geocache cache; private LogCacheActivity activity; - private final static List<LogType> standardLogTypes = Arrays.asList(LogType.FOUND_IT, LogType.DIDNT_FIND_IT, LogType.NOTE); - private final static List<LogType> eventLogTypes = Arrays.asList(LogType.WILL_ATTEND, LogType.ATTENDED, LogType.NOTE); - - public OkapiLoggingManager(final LogCacheActivity activity, OCApiLiveConnector connector, Geocache cache) { + public OkapiLoggingManager(final LogCacheActivity activity, final OCApiLiveConnector connector, final Geocache cache) { this.connector = connector; this.cache = cache; this.activity = activity; } @Override - public void init() { + public final void init() { activity.onLoadFinished(); } @Override - public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, String logPassword, List<TrackableLog> trackableLogs) { + public final LogResult postLog(final Geocache cache, final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { final LogResult result = OkapiClient.postLog(cache, logType, date, log, logPassword, connector); connector.login(null, null); return result; } @Override - public ImageResult postLogImage(String logId, String imageCaption, String imageDescription, Uri imageUri) { + public final ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { return new ImageResult(StatusCode.LOG_POST_ERROR, ""); } @Override - public boolean hasLoaderError() { + public final boolean hasLoaderError() { return false; } @Override - public List<TrackableLog> getTrackables() { + public final List<TrackableLog> getTrackables() { return Collections.emptyList(); } @Override public List<LogType> getPossibleLogTypes() { - if (cache.isEventCache()) { - return eventLogTypes; - } - - return standardLogTypes; + return connector.getPossibleLogTypes(cache); } + } diff --git a/main/src/cgeo/geocaching/connector/oc/UserInfo.java b/main/src/cgeo/geocaching/connector/oc/UserInfo.java index 0dc0440..c8b37cd 100644 --- a/main/src/cgeo/geocaching/connector/oc/UserInfo.java +++ b/main/src/cgeo/geocaching/connector/oc/UserInfo.java @@ -1,6 +1,7 @@ package cgeo.geocaching.connector.oc; import cgeo.geocaching.R; +import cgeo.geocaching.connector.oc.OkapiError.OkapiErrors; public class UserInfo { @@ -8,13 +9,28 @@ public class UserInfo { NOT_RETRIEVED(R.string.init_login_popup_working), SUCCESSFUL(R.string.init_login_popup_ok), FAILED(R.string.init_login_popup_failed), - NOT_SUPPORTED(R.string.init_login_popup_not_authorized); + NOT_SUPPORTED(R.string.init_login_popup_not_authorized), + INVALID_TIMESTAMP(R.string.init_login_popup_invalid_timestamp), + INVALID_TOKEN(R.string.init_login_popup_invalid_token); public final int resId; UserInfoStatus(int resId) { this.resId = resId; } + + public static UserInfoStatus getFromOkapiError(OkapiErrors result) { + switch (result) { + case NO_ERROR: + return SUCCESSFUL; + case INVALID_TIMESTAMP: + return INVALID_TIMESTAMP; + case INVALID_TOKEN: + return INVALID_TOKEN; + default: + return FAILED; + } + } } private final String name; diff --git a/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java b/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java index f25e289..9d1dfc7 100644 --- a/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java +++ b/main/src/cgeo/geocaching/connector/ox/OpenCachingApi.java @@ -1,9 +1,9 @@ package cgeo.geocaching.connector.ox; import cgeo.geocaching.Geocache; -import cgeo.geocaching.StoredList; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.utils.CryptUtils; @@ -11,7 +11,7 @@ import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import java.util.Collection; import java.util.Collections; diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java index 66ca5f7..67180b3 100644 --- a/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java +++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java @@ -1,8 +1,8 @@ package cgeo.geocaching.connector.trackable; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.Trackable; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.utils.Log; import org.xml.sax.Attributes; @@ -35,15 +35,23 @@ public class GeokretyParser { @Override public void start(Attributes attributes) { try { - if (attributes.getIndex("id") > -1) { + final int indexId = attributes.getIndex("id"); + if (indexId > -1) { trackable.setGeocode(geocode(Integer.valueOf(attributes.getValue("id")))); } - if (attributes.getIndex("dist") > -1) { + final int indexDist = attributes.getIndex("dist"); + if (indexDist > -1) { trackable.setDistance(Float.valueOf(attributes.getValue("dist"))); } - if (attributes.getIndex("type") > -1) { + final int indexType = attributes.getIndex("type"); + if (indexType > -1) { trackable.setType(getType(Integer.valueOf(attributes.getValue("type")))); } + final int indexWaypoint = attributes.getIndex("waypoint"); + if (indexWaypoint > -1) { + trackable.setSpottedName(attributes.getValue(indexWaypoint)); + trackable.setSpottedType(Trackable.SPOTTED_CACHE); + } } catch (final NumberFormatException e) { Log.e("Parsing geokret", e); } @@ -63,15 +71,15 @@ public class GeokretyParser { protected static String getType(int type) { switch (type) { case 0: - return cgeoapplication.getInstance().getString(R.string.geokret_type_traditional); + return CgeoApplication.getInstance().getString(R.string.geokret_type_traditional); case 1: - return cgeoapplication.getInstance().getString(R.string.geokret_type_book_or_media); + return CgeoApplication.getInstance().getString(R.string.geokret_type_book_or_media); case 2: - return cgeoapplication.getInstance().getString(R.string.geokret_type_human); + return CgeoApplication.getInstance().getString(R.string.geokret_type_human); case 3: - return cgeoapplication.getInstance().getString(R.string.geokret_type_coin); + return CgeoApplication.getInstance().getString(R.string.geokret_type_coin); case 4: - return cgeoapplication.getInstance().getString(R.string.geokret_type_post); + return CgeoApplication.getInstance().getString(R.string.geokret_type_post); } return null; } diff --git a/main/src/cgeo/geocaching/enumerations/CacheAttribute.java b/main/src/cgeo/geocaching/enumerations/CacheAttribute.java index 243f63d..1ddfeaf 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheAttribute.java +++ b/main/src/cgeo/geocaching/enumerations/CacheAttribute.java @@ -1,7 +1,7 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import org.apache.commons.lang3.StringUtils; @@ -143,7 +143,7 @@ public enum CacheAttribute { * @return the localized text */ public String getL10n(final boolean enabled) { - return cgeoapplication.getInstance().getResources().getString( + return CgeoApplication.getInstance().getResources().getString( enabled ? stringIdYes : stringIdNo); } diff --git a/main/src/cgeo/geocaching/enumerations/CacheListType.java b/main/src/cgeo/geocaching/enumerations/CacheListType.java index 5c326a3..f482d5b 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheListType.java +++ b/main/src/cgeo/geocaching/enumerations/CacheListType.java @@ -1,14 +1,23 @@ package cgeo.geocaching.enumerations; public enum CacheListType { - OFFLINE, - POCKET, - HISTORY, - NEAREST, - COORDINATE, - KEYWORD, - ADDRESS, - USERNAME, - OWNER, - MAP; + OFFLINE(true), + POCKET(false), + HISTORY(true), + NEAREST(false), + COORDINATE(false), + KEYWORD(false), + ADDRESS(false), + USERNAME(false), + OWNER(false), + MAP(false); + + /** + * whether or not this list allows switching to another list + */ + public final boolean canSwitch; + + private CacheListType(final boolean canSwitch) { + this.canSwitch = canSwitch; + } } diff --git a/main/src/cgeo/geocaching/enumerations/CacheSize.java b/main/src/cgeo/geocaching/enumerations/CacheSize.java index 726ebe2..a6f8df3 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheSize.java +++ b/main/src/cgeo/geocaching/enumerations/CacheSize.java @@ -1,7 +1,7 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import java.util.Collections; import java.util.HashMap; @@ -60,7 +60,7 @@ public enum CacheSize { } public final String getL10n() { - return cgeoapplication.getInstance().getBaseContext().getResources().getString(stringId); + return CgeoApplication.getInstance().getBaseContext().getResources().getString(stringId); } } diff --git a/main/src/cgeo/geocaching/enumerations/CacheType.java b/main/src/cgeo/geocaching/enumerations/CacheType.java index c7a9130..c952ba0 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheType.java +++ b/main/src/cgeo/geocaching/enumerations/CacheType.java @@ -2,7 +2,7 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.ICache; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import java.util.Collections; import java.util.HashMap; @@ -85,7 +85,7 @@ public enum CacheType { } public final String getL10n() { - return cgeoapplication.getInstance().getBaseContext().getResources().getString(stringId); + return CgeoApplication.getInstance().getBaseContext().getResources().getString(stringId); } public boolean isEvent() { @@ -114,7 +114,7 @@ public enum CacheType { } public boolean applyDistanceRule() { - return !isVirtual() && !isEvent(); + return TRADITIONAL == this || PROJECT_APE == this || GCHQ == this; } public boolean isVirtual() { diff --git a/main/src/cgeo/geocaching/enumerations/LiveMapStrategy.java b/main/src/cgeo/geocaching/enumerations/LiveMapStrategy.java index 5f5b2c9..710c3ba 100644 --- a/main/src/cgeo/geocaching/enumerations/LiveMapStrategy.java +++ b/main/src/cgeo/geocaching/enumerations/LiveMapStrategy.java @@ -1,7 +1,7 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import java.util.EnumSet; @@ -42,7 +42,7 @@ public interface LiveMapStrategy { } public final String getL10n() { - return cgeoapplication.getInstance().getBaseContext().getResources().getString(stringId); + return CgeoApplication.getInstance().getBaseContext().getResources().getString(stringId); } } } diff --git a/main/src/cgeo/geocaching/enumerations/LogType.java b/main/src/cgeo/geocaching/enumerations/LogType.java index 9902d3f..543f83d 100644 --- a/main/src/cgeo/geocaching/enumerations/LogType.java +++ b/main/src/cgeo/geocaching/enumerations/LogType.java @@ -1,7 +1,7 @@ package cgeo.geocaching.enumerations; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import java.util.Collections; import java.util.HashMap; @@ -102,6 +102,6 @@ public enum LogType { } public final String getL10n() { - return cgeoapplication.getInstance().getBaseContext().getResources().getString(stringId); + return CgeoApplication.getInstance().getBaseContext().getResources().getString(stringId); } } diff --git a/main/src/cgeo/geocaching/enumerations/WaypointType.java b/main/src/cgeo/geocaching/enumerations/WaypointType.java index 79c8106..272b2f2 100644 --- a/main/src/cgeo/geocaching/enumerations/WaypointType.java +++ b/main/src/cgeo/geocaching/enumerations/WaypointType.java @@ -1,7 +1,7 @@ package cgeo.geocaching.enumerations; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import java.util.Collections; import java.util.HashMap; @@ -65,7 +65,7 @@ public enum WaypointType { } public final String getL10n() { - return cgeoapplication.getInstance().getBaseContext().getResources().getString(stringId); + return CgeoApplication.getInstance().getBaseContext().getResources().getString(stringId); } @Override diff --git a/main/src/cgeo/geocaching/export/AbstractExport.java b/main/src/cgeo/geocaching/export/AbstractExport.java index e4ba5f0..5d15ecb 100644 --- a/main/src/cgeo/geocaching/export/AbstractExport.java +++ b/main/src/cgeo/geocaching/export/AbstractExport.java @@ -1,7 +1,7 @@ package cgeo.geocaching.export; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; abstract class AbstractExport implements Export { private final String name; @@ -23,7 +23,7 @@ abstract class AbstractExport implements Export { * @return localized string */ protected static String getString(int resourceId) { - return cgeoapplication.getInstance().getString(resourceId); + return CgeoApplication.getInstance().getString(resourceId); } /** @@ -36,7 +36,7 @@ abstract class AbstractExport implements Export { * @return localized string */ protected static String getString(int resourceId, Object... params) { - return cgeoapplication.getInstance().getString(resourceId, params); + return CgeoApplication.getInstance().getString(resourceId, params); } @Override diff --git a/main/src/cgeo/geocaching/export/ExportFactory.java b/main/src/cgeo/geocaching/export/ExportFactory.java index a3ecb0c..e743eb2 100644 --- a/main/src/cgeo/geocaching/export/ExportFactory.java +++ b/main/src/cgeo/geocaching/export/ExportFactory.java @@ -56,6 +56,7 @@ public abstract class ExportFactory { builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { + dialog.dismiss(); final Export selectedExport = adapter.getItem(item); selectedExport.export(caches, activity); } diff --git a/main/src/cgeo/geocaching/export/FieldnoteExport.java b/main/src/cgeo/geocaching/export/FieldnoteExport.java index d0040a9..1ae97f3 100644 --- a/main/src/cgeo/geocaching/export/FieldnoteExport.java +++ b/main/src/cgeo/geocaching/export/FieldnoteExport.java @@ -1,9 +1,9 @@ package cgeo.geocaching.export; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; -import cgeo.geocaching.cgData; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.connector.gc.Login; import cgeo.geocaching.enumerations.StatusCode; @@ -13,11 +13,12 @@ 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; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.FastDateFormat; import android.app.Activity; import android.app.AlertDialog; @@ -42,7 +43,7 @@ import java.util.Locale; import java.util.TimeZone; /** - * Exports offline-logs in the Groundspeak Field Note format.<br> + * Exports offline logs in the Groundspeak Field Note format.<br> * <br> * * Field Notes are simple plain text files, but poorly documented. Syntax:<br> @@ -50,10 +51,7 @@ import java.util.TimeZone; */ class FieldnoteExport extends AbstractExport { private static final File exportLocation = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/field-notes"); - private static final SimpleDateFormat fieldNoteDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); - static { - fieldNoteDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - } + private static final FastDateFormat fieldNoteDateFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"), Locale.US); protected FieldnoteExport() { super(getString(R.string.export_fieldnotes)); @@ -80,22 +78,24 @@ class FieldnoteExport extends AbstractExport { builder.setView(layout); final CheckBox uploadOption = (CheckBox) layout.findViewById(R.id.upload); + uploadOption.setChecked(Settings.getFieldNoteExportUpload()); final CheckBox onlyNewOption = (CheckBox) layout.findViewById(R.id.onlynew); + onlyNewOption.setChecked(Settings.getFieldNoteExportOnlyNew()); if (Settings.getFieldnoteExportDate() > 0) { - onlyNewOption.setText(getString(R.string.export_fieldnotes_onlynew) + "\n(" + Formatter.formatShortDateTime(Settings.getFieldnoteExportDate()) + ')'); + onlyNewOption.setText(getString(R.string.export_fieldnotes_onlynew) + " (" + Formatter.formatDateTime(Settings.getFieldnoteExportDate()) + ')'); } builder.setPositiveButton(R.string.export, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { + final boolean upload = uploadOption.isChecked(); + final boolean onlyNew = onlyNewOption.isChecked(); + Settings.setFieldNoteExportUpload(upload); + Settings.setFieldNoteExportOnlyNew(onlyNew); dialog.dismiss(); - new ExportTask( - activity, - uploadOption.isChecked(), - onlyNewOption.isChecked()) - .execute(caches); + new ExportTask(activity, upload, onlyNew).execute(caches); } }); @@ -134,7 +134,7 @@ class FieldnoteExport extends AbstractExport { int i = 0; for (final Geocache cache : caches) { if (cache.isLogOffline()) { - final LogEntry log = cgData.loadLogOffline(cache.getGeocode()); + final LogEntry log = DataStore.loadLogOffline(cache.getGeocode()); if (!onlyNew || log.date > Settings.getFieldnoteExportDate()) { appendFieldNote(fieldNoteBuffer, cache, log); } diff --git a/main/src/cgeo/geocaching/export/GpxExport.java b/main/src/cgeo/geocaching/export/GpxExport.java index 36e43a0..a2e0f93 100644 --- a/main/src/cgeo/geocaching/export/GpxExport.java +++ b/main/src/cgeo/geocaching/export/GpxExport.java @@ -2,7 +2,7 @@ package cgeo.geocaching.export; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.AsyncTaskWithProgress; @@ -125,7 +125,7 @@ class GpxExport extends AbstractExport { final List<String> allGeocodes = new ArrayList<String>(Arrays.asList(geocodes)); - setMessage(cgeoapplication.getInstance().getResources().getQuantityString(R.plurals.cache_counts, allGeocodes.size(), allGeocodes.size())); + setMessage(CgeoApplication.getInstance().getResources().getQuantityString(R.plurals.cache_counts, allGeocodes.size(), allGeocodes.size())); final File exportFile = getExportFile(); BufferedWriter writer = null; diff --git a/main/src/cgeo/geocaching/export/GpxSerializer.java b/main/src/cgeo/geocaching/export/GpxSerializer.java index 1e39be4..da179da 100644 --- a/main/src/cgeo/geocaching/export/GpxSerializer.java +++ b/main/src/cgeo/geocaching/export/GpxSerializer.java @@ -1,9 +1,9 @@ package cgeo.geocaching.export; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogEntry; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgData; import cgeo.geocaching.enumerations.CacheAttribute; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.geopoint.Geopoint; @@ -13,11 +13,11 @@ import cgeo.org.kxml2.io.KXmlSerializer; import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.FastDateFormat; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.Writer; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -27,7 +27,7 @@ import java.util.Set; public final class GpxSerializer { - private static final SimpleDateFormat dateFormatZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); + private static final FastDateFormat dateFormatZ = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); public static final String PREFIX_XSI = "http://www.w3.org/2001/XMLSchema-instance"; public static final String PREFIX_GPX = "http://www.topografix.com/GPX/1/0"; public static final String PREFIX_GROUNDSPEAK = "http://www.groundspeak.com/cache/1/0"; @@ -81,7 +81,7 @@ public final class GpxSerializer { } private void exportBatch(final XmlSerializer gpx, Collection<String> geocodesOfBatch) throws IOException { - final Set<Geocache> caches = cgData.loadCaches(geocodesOfBatch, LoadFlags.LOAD_ALL_DB_ONLY); + final Set<Geocache> caches = DataStore.loadCaches(geocodesOfBatch, LoadFlags.LOAD_ALL_DB_ONLY); for (final Geocache cache : caches) { gpx.startTag(PREFIX_GPX, "wpt"); gpx.attribute("", "lat", Double.toString(cache.getCoords().getLatitude())); diff --git a/main/src/cgeo/geocaching/files/AbstractFileListActivity.java b/main/src/cgeo/geocaching/files/AbstractFileListActivity.java index b0aba58..35e6265 100644 --- a/main/src/cgeo/geocaching/files/AbstractFileListActivity.java +++ b/main/src/cgeo/geocaching/files/AbstractFileListActivity.java @@ -2,12 +2,12 @@ package cgeo.geocaching.files; import cgeo.geocaching.Intents; import cgeo.geocaching.R; -import cgeo.geocaching.StoredList; import cgeo.geocaching.activity.AbstractListActivity; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.utils.FileUtils; import cgeo.geocaching.utils.Log; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import android.app.ProgressDialog; @@ -66,7 +66,7 @@ public abstract class AbstractFileListActivity<T extends ArrayAdapter<File>> ext if (waitDialog != null) { waitDialog.dismiss(); } - if (CollectionUtils.isEmpty(files)) { + if (CollectionUtils.isEmpty(files) && requireFiles()) { showToast(res.getString(R.string.file_list_no_files)); finish(); } else if (adapter != null) { @@ -104,7 +104,7 @@ public abstract class AbstractFileListActivity<T extends ArrayAdapter<File>> ext if (searchingThread != null && searchingThread.isAlive()) { searchingThread.notifyEnd(); } - if (files.isEmpty()) { + if (files.isEmpty() && requireFiles()) { finish(); } } @@ -121,6 +121,10 @@ public abstract class AbstractFileListActivity<T extends ArrayAdapter<File>> ext } + protected boolean requireFiles() { + return true; + } + protected abstract T getAdapter(List<File> files); private void setAdapter() { diff --git a/main/src/cgeo/geocaching/files/FileParser.java b/main/src/cgeo/geocaching/files/FileParser.java index f979d74..396a589 100644 --- a/main/src/cgeo/geocaching/files/FileParser.java +++ b/main/src/cgeo/geocaching/files/FileParser.java @@ -2,7 +2,8 @@ package cgeo.geocaching.files; import cgeo.geocaching.Geocache; import cgeo.geocaching.utils.CancellableHandler; -import cgeo.geocaching.utils.IOUtils; + +import org.apache.commons.io.IOUtils; import java.io.BufferedInputStream; import java.io.BufferedReader; @@ -51,7 +52,7 @@ public abstract class FileParser { protected static StringBuilder readStream(InputStream is, CancellableHandler progressHandler) throws IOException { final StringBuilder buffer = new StringBuilder(); ProgressInputStream progressInputStream = new ProgressInputStream(is); - final BufferedReader input = new BufferedReader(new InputStreamReader(progressInputStream)); + final BufferedReader input = new BufferedReader(new InputStreamReader(progressInputStream, "UTF-8")); try { String line; diff --git a/main/src/cgeo/geocaching/files/GPXImporter.java b/main/src/cgeo/geocaching/files/GPXImporter.java index 3f11c26..bf0aa72 100644 --- a/main/src/cgeo/geocaching/files/GPXImporter.java +++ b/main/src/cgeo/geocaching/files/GPXImporter.java @@ -4,7 +4,7 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; import cgeo.geocaching.StaticMapsProvider; -import cgeo.geocaching.cgData; +import cgeo.geocaching.DataStore; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.activity.Progress; import cgeo.geocaching.enumerations.LoadFlags; @@ -175,7 +175,7 @@ public class GPXImporter { private boolean importStaticMaps(final SearchResult importedCaches) { int storedCacheMaps = 0; for (final String geocode : importedCaches.getGeocodes()) { - final Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_WAYPOINTS); + final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_WAYPOINTS); if (cache != null) { Log.d("GPXImporter.ImportThread.importStaticMaps start downloadMaps for cache " + geocode); StaticMapsProvider.downloadMaps(cache); diff --git a/main/src/cgeo/geocaching/files/GPXParser.java b/main/src/cgeo/geocaching/files/GPXParser.java index 3358759..e01c191 100644 --- a/main/src/cgeo/geocaching/files/GPXParser.java +++ b/main/src/cgeo/geocaching/files/GPXParser.java @@ -1,13 +1,12 @@ package cgeo.geocaching.files; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; -import cgeo.geocaching.StoredList; import cgeo.geocaching.Trackable; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; @@ -17,9 +16,11 @@ import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; +import cgeo.geocaching.utils.SynchronizedDateFormat; import org.apache.commons.lang3.StringUtils; import org.xml.sax.Attributes; @@ -35,7 +36,6 @@ import android.util.Xml; import java.io.IOException; import java.io.InputStream; import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -47,9 +47,9 @@ import java.util.regex.Pattern; public abstract class GPXParser extends FileParser { - private static final SimpleDateFormat formatSimple = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US); // 2010-04-20T07:00:00 - private static final SimpleDateFormat formatSimpleZ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); // 2010-04-20T07:00:00Z - private static final SimpleDateFormat formatTimezone = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US); // 2010-04-20T01:01:03-04:00 + private static final SynchronizedDateFormat formatSimple = new SynchronizedDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US); // 2010-04-20T07:00:00 + private static final SynchronizedDateFormat formatSimpleZ = new SynchronizedDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); // 2010-04-20T07:00:00Z + private static final SynchronizedDateFormat formatTimezone = new SynchronizedDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US); // 2010-04-20T01:01:03-04:00 /** * Attention: case sensitive geocode pattern to avoid matching normal words in the name or description of the cache. @@ -205,7 +205,7 @@ public abstract class GPXParser extends FileParser { // get text for string String stringName; try { - stringName = cgeoapplication.getInstance().getResources().getResourceName(stringId); + stringName = CgeoApplication.getInstance().getResources().getResourceName(stringId); } catch (final NullPointerException e) { return null; } @@ -312,10 +312,10 @@ public abstract class GPXParser extends FileParser { // finally store the cache in the database result.add(geocode); - cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); + DataStore.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); // avoid the cachecache using lots of memory for caches which the user did not actually look at - cgData.removeAllFromCache(); + DataStore.removeAllFromCache(); showProgressMessage(progressHandler, progressStream.getProgress()); } else if (StringUtils.isNotBlank(cache.getName()) && StringUtils.containsIgnoreCase(type, "waypoint")) { @@ -331,7 +331,7 @@ public abstract class GPXParser extends FileParser { if (cache.getName().length() > 2) { final String cacheGeocodeForWaypoint = "GC" + cache.getName().substring(2).toUpperCase(Locale.US); // lookup cache for waypoint in already parsed caches - final Geocache cacheForWaypoint = cgData.loadCache(cacheGeocodeForWaypoint, LoadFlags.LOAD_CACHE_OR_DB); + final Geocache cacheForWaypoint = DataStore.loadCache(cacheGeocodeForWaypoint, LoadFlags.LOAD_CACHE_OR_DB); if (cacheForWaypoint != null) { final Waypoint waypoint = new Waypoint(cache.getShortDescription(), convertWaypointSym2Type(sym), false); waypoint.setId(-1); @@ -349,7 +349,7 @@ public abstract class GPXParser extends FileParser { newPoints.add(waypoint); Waypoint.mergeWayPoints(newPoints, mergedWayPoints, true); cacheForWaypoint.setWaypoints(newPoints, false); - cgData.saveCache(cacheForWaypoint, EnumSet.of(SaveFlag.SAVE_DB)); + DataStore.saveCache(cacheForWaypoint, EnumSet.of(SaveFlag.SAVE_DB)); showProgressMessage(progressHandler, progressStream.getProgress()); } } @@ -785,9 +785,8 @@ public abstract class GPXParser extends FileParser { try { progressStream = new ProgressInputStream(stream); Xml.parse(progressStream, Xml.Encoding.UTF_8, root.getContentHandler()); - return cgData.loadCaches(result, EnumSet.of(LoadFlag.LOAD_DB_MINIMAL)); + return DataStore.loadCaches(result, EnumSet.of(LoadFlag.LOAD_DB_MINIMAL)); } catch (final SAXException e) { - Log.w("Cannot parse .gpx file as GPX " + version + ": could not parse XML - ", e); throw new ParserException("Cannot parse .gpx file as GPX " + version + ": could not parse XML", e); } } diff --git a/main/src/cgeo/geocaching/files/LocParser.java b/main/src/cgeo/geocaching/files/LocParser.java index 1cfb2a3..3d01c1b 100644 --- a/main/src/cgeo/geocaching/files/LocParser.java +++ b/main/src/cgeo/geocaching/files/LocParser.java @@ -1,8 +1,8 @@ package cgeo.geocaching.files; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.cgData; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; @@ -59,7 +59,7 @@ public final class LocParser extends FileParser { contained.add(geocode); } } - Set<Geocache> caches = cgData.loadCaches(contained, LoadFlags.LOAD_CACHE_OR_DB); + Set<Geocache> caches = DataStore.loadCaches(contained, LoadFlags.LOAD_CACHE_OR_DB); for (Geocache cache : caches) { Geocache coord = cidCoords.get(cache.getGeocode()); copyCoordToCache(coord, cache); diff --git a/main/src/cgeo/geocaching/files/LocalStorage.java b/main/src/cgeo/geocaching/files/LocalStorage.java index fc82409..edbecf6 100644 --- a/main/src/cgeo/geocaching/files/LocalStorage.java +++ b/main/src/cgeo/geocaching/files/LocalStorage.java @@ -1,15 +1,16 @@ package cgeo.geocaching.files; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.utils.CryptUtils; import cgeo.geocaching.utils.FileUtils; -import cgeo.geocaching.utils.IOUtils; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.Header; import ch.boye.httpclientandroidlib.HttpResponse; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.Nullable; import android.os.Environment; @@ -21,11 +22,13 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; @@ -35,6 +38,7 @@ import java.util.List; */ public final class LocalStorage { + private static final String FILE_SYSTEM_TABLE_PATH = "/system/etc/vold.fstab"; public static final String HEADER_LAST_MODIFIED = "last-modified"; public static final String HEADER_ETAG = "etag"; @@ -86,7 +90,7 @@ public final class LocalStorage { private static File getInternalStorageBase() { if (internalStorageBase == null) { // A race condition will do no harm as the operation is idempotent. No need to synchronize. - internalStorageBase = cgeoapplication.getInstance().getApplicationContext().getFilesDir().getParentFile(); + internalStorageBase = CgeoApplication.getInstance().getApplicationContext().getFilesDir().getParentFile(); } return internalStorageBase; } @@ -99,7 +103,14 @@ public final class LocalStorage { * @return the file extension, including the leading dot, or the empty string if none could be determined */ static String getExtension(final String url) { - final String urlExt = StringUtils.substringAfterLast(url, "."); + String urlExt; + if (url.startsWith("data:")) { + // "data:image/png;base64,i53โฆ" -> ".png" + urlExt = StringUtils.substringAfter(StringUtils.substringBefore(url, ";"), "/"); + } else { + // "http://example.com/foo/bar.png" -> ".png" + urlExt = StringUtils.substringAfterLast(url, "."); + } return urlExt.length() >= 1 && urlExt.length() <= 4 ? "." + urlExt : ""; } @@ -198,13 +209,19 @@ public final class LocalStorage { return false; } - private static void saveHeader(final String name, final HttpResponse response, final File baseFile) { + private static void saveHeader(final String name, @Nullable final HttpResponse response, final File baseFile) { final Header header = response != null ? response.getFirstHeader(name) : null; final File file = filenameForHeader(baseFile, name); if (header == null) { FileUtils.deleteIgnoringFailure(file); } else { - saveToFile(new ByteArrayInputStream(header.getValue().getBytes()), file); + try { + saveToFile(new ByteArrayInputStream(header.getValue().getBytes("UTF-8")), file); + } catch (final UnsupportedEncodingException e) { + // Do not try to display the header in the log message, as our default encoding is + // likely to be UTF-8 and it will fail as well. + Log.e("LocalStorage.saveHeader: unable to decode header", e); + } } } @@ -219,12 +236,13 @@ public final class LocalStorage { * the name of the cached resource * @param name * the name of the header ("etag" or "last-modified") - * @return null if no value has been cached, the value otherwise + * @return the cached value, or <tt>null</tt> if none has been cached */ + @Nullable public static String getSavedHeader(final File baseFile, final String name) { try { final File file = filenameForHeader(baseFile, name); - final FileReader f = new FileReader(file); + final Reader f = new InputStreamReader(new FileInputStream(file), "UTF-8"); try { // No header will be more than 256 bytes final char[] value = new char[256]; @@ -408,12 +426,12 @@ public final class LocalStorage { String extStorage = Environment.getExternalStorageDirectory().getAbsolutePath(); List<File> storages = new ArrayList<File>(); storages.add(new File(extStorage)); - File file = new File("/system/etc/vold.fstab"); + File file = new File(FILE_SYSTEM_TABLE_PATH); if (file.canRead()) { - FileReader fr = null; + Reader fr = null; BufferedReader br = null; try { - fr = new FileReader(file); + fr = new InputStreamReader(new FileInputStream(file), "UTF-8"); br = new BufferedReader(fr); String s = br.readLine(); while (s != null) { diff --git a/main/src/cgeo/geocaching/files/ProgressInputStream.java b/main/src/cgeo/geocaching/files/ProgressInputStream.java index 593949b..552aee0 100644 --- a/main/src/cgeo/geocaching/files/ProgressInputStream.java +++ b/main/src/cgeo/geocaching/files/ProgressInputStream.java @@ -4,6 +4,14 @@ import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +/** + * Stream to measure progress of reading automatically. + * <p> + * The method @link ProgressInputStream#read(byte[]) does not need to be overridden as it delegates to @link + * ProgressInputStream#read(byte[], int, int) anyway. + * </p> + * + */ public class ProgressInputStream extends FilterInputStream { private int progress = 0; @@ -15,17 +23,13 @@ public class ProgressInputStream extends FilterInputStream { @Override public int read() throws IOException { final int read = super.read(); - progress += read; + if (read >= 0) { + progress++; + } return read; } @Override - public int read(byte[] buffer) throws IOException { - return super.read(buffer); - // don't increment here, this calls another read implementation which we already measure - } - - @Override public int read(byte[] buffer, int offset, int count) throws IOException { final int read = super.read(buffer, offset, count); progress += read; diff --git a/main/src/cgeo/geocaching/files/SimpleDirChooser.java b/main/src/cgeo/geocaching/files/SimpleDirChooser.java index 8f69b88..e59287d 100644 --- a/main/src/cgeo/geocaching/files/SimpleDirChooser.java +++ b/main/src/cgeo/geocaching/files/SimpleDirChooser.java @@ -32,17 +32,20 @@ import java.util.List; * Dialog for choosing a file or directory. */ public class SimpleDirChooser extends AbstractListActivity { + public static final String EXTRA_CHOOSE_FOR_WRITING = "chooseForWriting"; private static final String PARENT_DIR = ".. "; private File currentDir; private FileArrayAdapter adapter; private Button okButton = null; private int lastPosition = -1; + private boolean chooseForWriting = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Bundle extras = getIntent().getExtras(); currentDir = dirContaining(extras.getString(Intents.EXTRA_START_DIR)); + chooseForWriting = extras.getBoolean(SimpleDirChooser.EXTRA_CHOOSE_FOR_WRITING, false); ActivityMixin.setTheme(this); setContentView(R.layout.simple_dir_chooser); @@ -85,19 +88,20 @@ public class SimpleDirChooser extends AbstractListActivity { } private void fill(File dir) { + lastPosition = -1; EditText path = (EditText) findViewById(R.id.simple_dir_chooser_path); path.setText(this.getResources().getString(R.string.simple_dir_chooser_current_path) + " " + dir.getAbsolutePath()); final File[] dirs = dir.listFiles(new DirOnlyFilenameFilter()); List<Option> listDirs = new ArrayList<Option>(); try { for (File currentDir : dirs) { - listDirs.add(new Option(currentDir.getName(), currentDir.getAbsolutePath())); + listDirs.add(new Option(currentDir.getName(), currentDir.getAbsolutePath(), currentDir.canWrite())); } } catch (RuntimeException e) { } Collections.sort(listDirs); if (dir.getParent() != null) { - listDirs.add(0, new Option(PARENT_DIR, dir.getParent())); + listDirs.add(0, new Option(PARENT_DIR, dir.getParent(), false)); } this.adapter = new FileArrayAdapter(this, R.layout.simple_dir_item, listDirs); this.setListAdapter(adapter); @@ -138,8 +142,13 @@ public class SimpleDirChooser extends AbstractListActivity { } CheckBox check = (CheckBox) v.findViewById(R.id.CheckBox); if (check != null) { - check.setOnClickListener(new OnCheckBoxClickListener(position)); - check.setChecked(option.isChecked()); + if (!chooseForWriting || option.isWriteable()) { + check.setOnClickListener(new OnCheckBoxClickListener(position)); + check.setChecked(option.isChecked()); + check.setEnabled(true); + } else { + check.setEnabled(false); + } } } return v; @@ -196,14 +205,19 @@ public class SimpleDirChooser extends AbstractListActivity { } } + /** + * Note: this class has a natural ordering that is inconsistent with equals. + */ public static class Option implements Comparable<Option> { private final String name; private final String path; private boolean checked = false; + private boolean writeable = false; - public Option(String name, String path) { + public Option(String name, String path, boolean writeable) { this.name = name; this.path = path; + this.writeable = writeable; } public String getName() { @@ -222,6 +236,10 @@ public class SimpleDirChooser extends AbstractListActivity { this.checked = checked; } + public boolean isWriteable() { + return writeable; + } + @Override public int compareTo(Option other) { if (other != null && this.name != null) { @@ -236,7 +254,7 @@ public class SimpleDirChooser extends AbstractListActivity { @Override public boolean accept(File dir, String filename) { File file = new File(dir, filename); - return file.isDirectory() && file.canWrite(); + return file.isDirectory() && file.canRead(); } } diff --git a/main/src/cgeo/geocaching/filter/AbstractRangeFilter.java b/main/src/cgeo/geocaching/filter/AbstractRangeFilter.java index 96d256a..cafb92b 100644 --- a/main/src/cgeo/geocaching/filter/AbstractRangeFilter.java +++ b/main/src/cgeo/geocaching/filter/AbstractRangeFilter.java @@ -1,6 +1,6 @@ package cgeo.geocaching.filter; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; abstract class AbstractRangeFilter extends AbstractFilter { @@ -9,7 +9,7 @@ abstract class AbstractRangeFilter extends AbstractFilter { protected final float rangeMax; protected AbstractRangeFilter(final int ressourceId, final int range) { - super(cgeoapplication.getInstance().getResources().getString(ressourceId) + ' ' + (range == 5 ? '5' : range + " + " + String.format("%.1f", range + 0.5))); + super(CgeoApplication.getInstance().getResources().getString(ressourceId) + ' ' + (range == 5 ? '5' : range + " + " + String.format("%.1f", range + 0.5))); this.rangeMin = range; rangeMax = rangeMin + 1f; } diff --git a/main/src/cgeo/geocaching/filter/AttributeFilter.java b/main/src/cgeo/geocaching/filter/AttributeFilter.java index cadcf49..6f0d5da 100644 --- a/main/src/cgeo/geocaching/filter/AttributeFilter.java +++ b/main/src/cgeo/geocaching/filter/AttributeFilter.java @@ -1,9 +1,9 @@ package cgeo.geocaching.filter; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.LoadFlags.LoadFlag; import android.content.res.Resources; @@ -29,7 +29,7 @@ class AttributeFilter extends AbstractFilter { @Override public boolean accepts(final Geocache cache) { - Geocache fullCache = cgData.loadCache(cache.getGeocode(), EnumSet.of(LoadFlag.LOAD_ATTRIBUTES)); + Geocache fullCache = DataStore.loadCache(cache.getGeocode(), EnumSet.of(LoadFlag.LOAD_ATTRIBUTES)); if (fullCache == null) { fullCache = cache; } @@ -40,8 +40,8 @@ class AttributeFilter extends AbstractFilter { @Override public List<IFilter> getFilters() { - final String packageName = cgeoapplication.getInstance().getBaseContext().getPackageName(); - final Resources res = cgeoapplication.getInstance().getResources(); + final String packageName = CgeoApplication.getInstance().getBaseContext().getPackageName(); + final Resources res = CgeoApplication.getInstance().getResources(); final List<IFilter> filters = new LinkedList<IFilter>(); for (final String id: res.getStringArray(R.array.attribute_ids)) { diff --git a/main/src/cgeo/geocaching/filter/DistanceFilter.java b/main/src/cgeo/geocaching/filter/DistanceFilter.java index 4d00b65..54225d2 100644 --- a/main/src/cgeo/geocaching/filter/DistanceFilter.java +++ b/main/src/cgeo/geocaching/filter/DistanceFilter.java @@ -3,7 +3,7 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; import cgeo.geocaching.IGeoData; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.geopoint.Geopoint; import java.util.ArrayList; @@ -18,7 +18,7 @@ class DistanceFilter extends AbstractFilter { super(name); this.minDistance = minDistance; this.maxDistance = maxDistance; - geo = cgeoapplication.getInstance().currentGeo(); + geo = CgeoApplication.getInstance().currentGeo(); } @Override @@ -51,7 +51,7 @@ class DistanceFilter extends AbstractFilter { maxRange = Integer.MAX_VALUE; } final String range = maxRange == Integer.MAX_VALUE ? "> " + minRange : minRange + " - " + maxRange; - final String name = cgeoapplication.getInstance().getResources().getQuantityString(R.plurals.tts_kilometers, maxRange, range); + final String name = CgeoApplication.getInstance().getResources().getQuantityString(R.plurals.tts_kilometers, maxRange, range); filters.add(new DistanceFilter(name, minRange, maxRange)); } return filters; diff --git a/main/src/cgeo/geocaching/filter/FilterUserInterface.java b/main/src/cgeo/geocaching/filter/FilterUserInterface.java index 2404f44..8ff700a 100644 --- a/main/src/cgeo/geocaching/filter/FilterUserInterface.java +++ b/main/src/cgeo/geocaching/filter/FilterUserInterface.java @@ -1,7 +1,7 @@ package cgeo.geocaching.filter; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; @@ -41,7 +41,7 @@ public final class FilterUserInterface { public FilterUserInterface(final Activity activity) { this.activity = activity; - this.res = cgeoapplication.getInstance().getResources(); + this.res = CgeoApplication.getInstance().getResources(); registry = new ArrayList<FactoryEntry>(); if (Settings.getCacheType() == CacheType.ALL) { diff --git a/main/src/cgeo/geocaching/filter/ModifiedFilter.java b/main/src/cgeo/geocaching/filter/ModifiedFilter.java index 74befda..d976b69 100644 --- a/main/src/cgeo/geocaching/filter/ModifiedFilter.java +++ b/main/src/cgeo/geocaching/filter/ModifiedFilter.java @@ -2,7 +2,7 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import java.util.Collections; import java.util.List; @@ -10,7 +10,7 @@ import java.util.List; class ModifiedFilter extends AbstractFilter implements IFilterFactory { public ModifiedFilter() { - super(cgeoapplication.getInstance().getString(R.string.caches_filter_modified)); + super(CgeoApplication.getInstance().getString(R.string.caches_filter_modified)); } @Override diff --git a/main/src/cgeo/geocaching/filter/StateFilter.java b/main/src/cgeo/geocaching/filter/StateFilter.java index 98589ab..d5b3027 100644 --- a/main/src/cgeo/geocaching/filter/StateFilter.java +++ b/main/src/cgeo/geocaching/filter/StateFilter.java @@ -1,8 +1,8 @@ package cgeo.geocaching.filter; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import android.content.res.Resources; @@ -13,7 +13,7 @@ import java.util.List; abstract class StateFilter extends AbstractFilter { - static final Resources res = cgeoapplication.getInstance().getResources(); + static final Resources res = CgeoApplication.getInstance().getResources(); protected StateFilter(final String name) { super(name); diff --git a/main/src/cgeo/geocaching/filter/TrackablesFilter.java b/main/src/cgeo/geocaching/filter/TrackablesFilter.java index 74f43be..d836a0f 100644 --- a/main/src/cgeo/geocaching/filter/TrackablesFilter.java +++ b/main/src/cgeo/geocaching/filter/TrackablesFilter.java @@ -1,15 +1,15 @@ package cgeo.geocaching.filter; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import java.util.Collections; import java.util.List; class TrackablesFilter extends AbstractFilter implements IFilterFactory { public TrackablesFilter() { - super(cgeoapplication.getInstance().getString(R.string.caches_filter_track)); + super(CgeoApplication.getInstance().getString(R.string.caches_filter_track)); } @Override diff --git a/main/src/cgeo/geocaching/gcvote/GCVote.java b/main/src/cgeo/geocaching/gcvote/GCVote.java index c3c7b8d..b245aa9 100644 --- a/main/src/cgeo/geocaching/gcvote/GCVote.java +++ b/main/src/cgeo/geocaching/gcvote/GCVote.java @@ -8,7 +8,7 @@ import cgeo.geocaching.utils.LeastRecentlyUsedMap; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; -import org.apache.commons.collections.MapUtils; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -16,10 +16,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.regex.Pattern; public final class GCVote { + public static final float NO_RATING = 0; private static final Pattern PATTERN_LOG_IN = Pattern.compile("loggedIn='([^']+)'", Pattern.CASE_INSENSITIVE); private static final Pattern PATTERN_GUID = Pattern.compile("cacheId='([^']+)'", Pattern.CASE_INSENSITIVE); private static final Pattern PATTERN_WAYPOINT = Pattern.compile("waypoint='([^']+)'", Pattern.CASE_INSENSITIVE); @@ -30,6 +32,12 @@ public final class GCVote { private static final int MAX_CACHED_RATINGS = 1000; private static final LeastRecentlyUsedMap<String, GCVoteRating> RATINGS_CACHE = new LeastRecentlyUsedMap.LruCache<String, GCVoteRating>(MAX_CACHED_RATINGS); + private static final float MIN_RATING = 1; + private static final float MAX_RATING = 5; + + private GCVote() { + // utility class + } /** * Get user rating for a given guid or geocode. For a guid first the ratings cache is checked @@ -39,7 +47,7 @@ public final class GCVote { * @param geocode * @return */ - public static GCVoteRating getRating(String guid, String geocode) { + public static GCVoteRating getRating(final String guid, final String geocode) { if (StringUtils.isNotBlank(guid) && RATINGS_CACHE.containsKey(guid)) { return RATINGS_CACHE.get(guid); } @@ -59,7 +67,7 @@ public final class GCVote { * @param geocodes * @return */ - public static Map<String, GCVoteRating> getRating(List<String> guids, List<String> geocodes) { + public static Map<String, GCVoteRating> getRating(final List<String> guids, final List<String> geocodes) { if (guids == null && geocodes == null) { return null; } @@ -128,7 +136,7 @@ public final class GCVote { } } - float rating = 0; + float rating = NO_RATING; try { final MatcherWrapper matcherRating = new MatcherWrapper(PATTERN_RATING, voteData); if (matcherRating.find()) { @@ -137,7 +145,7 @@ public final class GCVote { } catch (NumberFormatException e) { Log.w("GCVote.getRating: Failed to parse rating"); } - if (rating <= 0) { + if (!isValidRating(rating)) { continue; } @@ -154,7 +162,7 @@ public final class GCVote { continue; } - float myVote = 0; + float myVote = NO_RATING; if (loggedIn) { try { final MatcherWrapper matcherVote = new MatcherWrapper(PATTERN_VOTE, voteData); @@ -186,18 +194,11 @@ public final class GCVote { * @param vote * @return {@code true} if the rating was submitted successfully */ - public static boolean setRating(Geocache cache, double vote) { - if (!Settings.isGCvoteLogin()) { - return false; - } - if (!cache.supportsGCVote()) { + public static boolean setRating(final Geocache cache, final float vote) { + if (!isVotingPossible(cache)) { return false; } - String guid = cache.getGuid(); - if (StringUtils.isBlank(guid)) { - return false; - } - if (vote <= 0.0 || vote > 5.0) { + if (!isValidRating(vote)) { return false; } @@ -209,16 +210,16 @@ public final class GCVote { final Parameters params = new Parameters( "userName", login.left, "password", login.right, - "cacheId", guid, + "cacheId", cache.getGuid(), "voteUser", String.format("%.1f", vote).replace(',', '.'), "version", "cgeo"); final String result = Network.getResponseData(Network.getRequest("http://gcvote.com/setVote.php", params)); - return result.trim().equalsIgnoreCase("ok"); + return result != null && result.trim().equalsIgnoreCase("ok"); } - public static void loadRatings(ArrayList<Geocache> caches) { + public static void loadRatings(final ArrayList<Geocache> caches) { if (!Settings.isRatingWanted()) { return; } @@ -254,4 +255,17 @@ public final class GCVote { Log.e("GCvote.loadRatings", e); } } + + public static boolean isValidRating(final float rating) { + return rating >= MIN_RATING && rating <= MAX_RATING; + } + + public static String getRatingText(final float rating) { + return String.format(Locale.getDefault(), "%.1f", rating); + } + + public static boolean isVotingPossible(final Geocache cache) { + return Settings.isGCvoteLogin() && StringUtils.isNotBlank(cache.getGuid()) && cache.supportsGCVote(); + } + } diff --git a/main/src/cgeo/geocaching/gcvote/GCVoteRating.java b/main/src/cgeo/geocaching/gcvote/GCVoteRating.java index f6813a2..1e65589 100644 --- a/main/src/cgeo/geocaching/gcvote/GCVoteRating.java +++ b/main/src/cgeo/geocaching/gcvote/GCVoteRating.java @@ -5,7 +5,7 @@ public final class GCVoteRating { private final int votes; private final float myVote; - public GCVoteRating(float rating, int votes, float myVote) { + public GCVoteRating(final float rating, final int votes, final float myVote) { this.rating = rating; this.votes = votes; this.myVote = myVote; diff --git a/main/src/cgeo/geocaching/geopoint/Geopoint.java b/main/src/cgeo/geocaching/geopoint/Geopoint.java index 547ad29..f21df01 100644 --- a/main/src/cgeo/geocaching/geopoint/Geopoint.java +++ b/main/src/cgeo/geocaching/geopoint/Geopoint.java @@ -300,7 +300,7 @@ public final class Geopoint implements ICoordinates, Parcelable { /** * Returns formatted coordinates with default format. * Default format is decimalminutes, e.g. N 52ยฐ 36.123 E 010ยฐ 03.456 - * + * * @return formatted coordinates */ @Override @@ -364,8 +364,8 @@ public final class Geopoint implements ICoordinates, Parcelable { } /** - * Get longitude chararcter (E or W). - * + * Get longitude character (E or W). + * * @return */ public char getLonDir() { diff --git a/main/src/cgeo/geocaching/geopoint/GeopointParser.java b/main/src/cgeo/geocaching/geopoint/GeopointParser.java index ba86e70..c043d6f 100644 --- a/main/src/cgeo/geocaching/geopoint/GeopointParser.java +++ b/main/src/cgeo/geocaching/geopoint/GeopointParser.java @@ -25,8 +25,8 @@ class GeopointParser { } // ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) - private static final Pattern PATTERN_LAT = Pattern.compile("\\b([NS])\\s*(\\d+)ยฐ?(?:\\s*(\\d+)(?:[.,](\\d+)|'?\\s*(\\d+(?:[.,]\\d+)?)(?:''|\")?)?)?", Pattern.CASE_INSENSITIVE); - private static final Pattern PATTERN_LON = Pattern.compile("\\b([WE])\\s*(\\d+)ยฐ?(?:\\s*(\\d+)(?:[.,](\\d+)|'?\\s*(\\d+(?:[.,]\\d+)?)(?:''|\")?)?)?", Pattern.CASE_INSENSITIVE); + private static final Pattern PATTERN_LAT = Pattern.compile("\\b([NS]|)\\s*(\\d+)ยฐ?(?:\\s*(\\d+)(?:[.,](\\d+)|'?\\s*(\\d+(?:[.,]\\d+)?)(?:''|\")?)?)?", Pattern.CASE_INSENSITIVE); + private static final Pattern PATTERN_LON = Pattern.compile("\\b([WE]|)\\s*(\\d+)ยฐ?(?:\\s*(\\d+)(?:[.,](\\d+)|'?\\s*(\\d+(?:[.,]\\d+)?)(?:''|\")?)?)?", Pattern.CASE_INSENSITIVE); private static final Pattern PATTERN_BAD_BLANK = Pattern.compile("(\\d)[,.] (\\d{2,})"); @@ -110,6 +110,12 @@ class GeopointParser { final Pattern pattern = LatLon.LAT == latlon ? PATTERN_LAT : PATTERN_LON; matcher = new MatcherWrapper(pattern, replaceSpaceAfterComma); + try { + return new ResultWrapper(Double.valueOf(replaceSpaceAfterComma), 0, text.length()); + } catch (NumberFormatException e1) { + // fall through to advanced parsing + } + if (matcher.find()) { final double sign = matcher.group(1).equalsIgnoreCase("S") || matcher.group(1).equalsIgnoreCase("W") ? -1.0 : 1.0; final double degree = Integer.valueOf(matcher.group(2)).doubleValue(); @@ -131,13 +137,14 @@ class GeopointParser { } - // Nothing found with "N 52...", try to match string as decimaldegree + // Nothing found with "N 52...", try to match string as decimal degree parts (i.e. multiple doubles) try { final String[] items = StringUtils.split(text.trim()); - if (items.length > 0) { + if (items.length > 0 && items.length <= 2) { final int index = (latlon == LatLon.LON ? items.length - 1 : 0); - final int pos = (latlon == LatLon.LON ? text.lastIndexOf(items[index]) : text.indexOf(items[index])); - return new ResultWrapper(Double.parseDouble(items[index]), pos, items[index].length()); + final String textPart = items[index]; + final int pos = (latlon == LatLon.LON ? text.lastIndexOf(textPart) : text.indexOf(textPart)); + return new ResultWrapper(Double.parseDouble(textPart), pos, textPart.length()); } } catch (NumberFormatException e) { // The right exception will be raised below. diff --git a/main/src/cgeo/geocaching/list/AbstractList.java b/main/src/cgeo/geocaching/list/AbstractList.java new file mode 100644 index 0000000..ec783eb --- /dev/null +++ b/main/src/cgeo/geocaching/list/AbstractList.java @@ -0,0 +1,28 @@ +package cgeo.geocaching.list; + +import org.eclipse.jdt.annotation.Nullable; + +import android.util.SparseArray; + +public abstract class AbstractList { + + public final int id; + public final String title; + private static SparseArray<AbstractList> LISTS = new SparseArray<AbstractList>(); + + public AbstractList(final int id, final String title) { + this.id = id; + this.title = title; + LISTS.put(id, this); + } + + public abstract String getTitleAndCount(); + + public abstract boolean isConcrete(); + + @Nullable + public static AbstractList getListById(int listId) { + return LISTS.get(listId); + } + +} diff --git a/main/src/cgeo/geocaching/list/PseudoList.java b/main/src/cgeo/geocaching/list/PseudoList.java new file mode 100644 index 0000000..365d6fd --- /dev/null +++ b/main/src/cgeo/geocaching/list/PseudoList.java @@ -0,0 +1,37 @@ +package cgeo.geocaching.list; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.R; + +public class PseudoList extends AbstractList { + + private static final int ALL_LIST_ID = 2; + /** + * list entry to show all caches + */ + public static final PseudoList ALL_LIST = new PseudoList(ALL_LIST_ID, R.string.list_all_lists); + + private static final int NEW_LIST_ID = 3; + /** + * list entry to create a new list + */ + public static final AbstractList NEW_LIST = new PseudoList(NEW_LIST_ID, R.string.list_menu_create); + + /** + * private constructor to have all instances as constants in the class + */ + private PseudoList(int id, final int titleResourceId) { + super(id, CgeoApplication.getInstance().getResources().getString(titleResourceId)); + } + + @Override + public String getTitleAndCount() { + return "<" + title + ">"; + } + + @Override + public boolean isConcrete() { + return false; + } + +} diff --git a/main/src/cgeo/geocaching/StoredList.java b/main/src/cgeo/geocaching/list/StoredList.java index e080af9..d3729c0 100644 --- a/main/src/cgeo/geocaching/StoredList.java +++ b/main/src/cgeo/geocaching/list/StoredList.java @@ -1,5 +1,8 @@ -package cgeo.geocaching; +package cgeo.geocaching.list; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; +import cgeo.geocaching.R; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.utils.RunnableWithArgument; @@ -19,21 +22,18 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -public final class StoredList { +public final class StoredList extends AbstractList { public static final int TEMPORARY_LIST_ID = 0; + public static final StoredList TEMPORARY_LIST = new StoredList(TEMPORARY_LIST_ID, "<temporary>", 0); // Never displayed public static final int STANDARD_LIST_ID = 1; - public static final int ALL_LIST_ID = 2; - - public final int id; - public final String title; private final int count; // this value is only valid as long as the list is not changed by other database operations public StoredList(int id, String title, int count) { - this.id = id; - this.title = title; + super(id, title); this.count = count; } + @Override public String getTitleAndCount() { return title + " [" + count + "]"; } @@ -59,41 +59,43 @@ public final class StoredList { public static class UserInterface { private final Activity activity; - private final cgeoapplication app; + private final CgeoApplication app; private final Resources res; public UserInterface(final Activity activity) { this.activity = activity; - app = cgeoapplication.getInstance(); + app = CgeoApplication.getInstance(); res = app.getResources(); } - public void promptForListSelection(final int titleId, final RunnableWithArgument<Integer> runAfterwards) { + public void promptForListSelection(final int titleId, @NonNull final RunnableWithArgument<Integer> runAfterwards) { promptForListSelection(titleId, runAfterwards, false, -1); } - public void promptForListSelection(final int titleId, final RunnableWithArgument<Integer> runAfterwards, final boolean onlyConcreteLists, final int exceptListId) { + public void promptForListSelection(final int titleId, @NonNull 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(); + public void promptForListSelection(final int titleId, @NonNull final RunnableWithArgument<Integer> runAfterwards, final boolean onlyConcreteLists, final int exceptListId, final String newListName) { + final List<AbstractList> lists = new ArrayList<AbstractList>(); + lists.addAll(getSortedLists()); if (exceptListId > StoredList.TEMPORARY_LIST_ID) { - StoredList exceptList = cgData.getList(exceptListId); + StoredList exceptList = DataStore.getList(exceptListId); if (exceptList != null) { lists.remove(exceptList); } } + if (!onlyConcreteLists) { + lists.add(PseudoList.ALL_LIST); + } + lists.add(PseudoList.NEW_LIST); + final List<CharSequence> listsTitle = new ArrayList<CharSequence>(); - for (StoredList list : lists) { + for (AbstractList list : lists) { listsTitle.add(list.getTitleAndCount()); } - if (!onlyConcreteLists) { - listsTitle.add("<" + res.getString(R.string.list_menu_all_lists) + ">"); - } - listsTitle.add("<" + res.getString(R.string.list_menu_create) + ">"); final CharSequence[] items = new CharSequence[listsTitle.size()]; @@ -102,27 +104,23 @@ public final class StoredList { builder.setItems(listsTitle.toArray(items), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int itemId) { - if (itemId == lists.size() && !onlyConcreteLists) { - // all lists - runAfterwards.run(StoredList.ALL_LIST_ID); - } else if (itemId >= lists.size()) { + final AbstractList list = lists.get(itemId); + if (list == PseudoList.NEW_LIST) { // create new list on the fly promptForListCreation(runAfterwards, newListName); } else { - if (runAfterwards != null) { - runAfterwards.run(lists.get(itemId).id); - } + runAfterwards.run(lists.get(itemId).id); } } }); builder.create().show(); } - private static @NonNull - List<StoredList> getSortedLists() { + @NonNull + private static List<StoredList> getSortedLists() { final Collator collator = Collator.getInstance(); - final List<StoredList> lists = cgData.getLists(); + final List<StoredList> lists = DataStore.getLists(); Collections.sort(lists, new Comparator<StoredList>() { @Override @@ -141,18 +139,17 @@ public final class StoredList { return lists; } - public void promptForListCreation(final RunnableWithArgument<Integer> runAfterwards, String newListName) { + public void promptForListCreation(@NonNull 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) { - final int newId = cgData.createList(listName); + final int newId = DataStore.createList(listName); + new StoredList(newId, listName, 0); - if (newId >= cgData.customListIdOffset) { + if (newId >= DataStore.customListIdOffset) { ActivityMixin.showToast(activity, res.getString(R.string.list_dialog_create_ok)); - if (runAfterwards != null) { - runAfterwards.run(newId); - } + runAfterwards.run(newId); } else { ActivityMixin.showToast(activity, res.getString(R.string.list_dialog_create_err)); } @@ -188,16 +185,14 @@ public final class StoredList { alert.show(); } - public void promptForListRename(final int listId, final Runnable runAfterRename) { - final StoredList list = cgData.getList(listId); + public void promptForListRename(final int listId, @NonNull final Runnable runAfterRename) { + final StoredList list = DataStore.getList(listId); handleListNameInput(list.title, R.string.list_dialog_rename_title, R.string.list_dialog_rename, new RunnableWithArgument<String>() { @Override public void run(final String listName) { - cgData.renameList(listId, listName); - if (runAfterRename != null) { - runAfterRename.run(); - } + DataStore.renameList(listId, listName); + runAfterRename.run(); } }); } @@ -216,9 +211,15 @@ public final class StoredList { * Return the given list, if it is a concrete list. Return the default list otherwise. */ public static int getConcreteList(int listId) { - if (listId == ALL_LIST_ID || listId == TEMPORARY_LIST_ID) { + if (listId == PseudoList.ALL_LIST.id || listId == TEMPORARY_LIST_ID) { return STANDARD_LIST_ID; } return listId; } + + @Override + public boolean isConcrete() { + return true; + } + } diff --git a/main/src/cgeo/geocaching/loaders/HistoryGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/HistoryGeocacheListLoader.java index 722f9f5..605f461 100644 --- a/main/src/cgeo/geocaching/loaders/HistoryGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/HistoryGeocacheListLoader.java @@ -1,8 +1,8 @@ package cgeo.geocaching.loaders; +import cgeo.geocaching.DataStore; import cgeo.geocaching.SearchResult; import cgeo.geocaching.settings.Settings; -import cgeo.geocaching.cgData; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.geopoint.Geopoint; @@ -18,7 +18,7 @@ public class HistoryGeocacheListLoader extends AbstractSearchLoader { @Override public SearchResult runSearch() { - return cgData.getHistoryOfCaches(true, coords != null ? Settings.getCacheType() : CacheType.ALL); + return DataStore.getHistoryOfCaches(true, coords != null ? Settings.getCacheType() : CacheType.ALL); } } diff --git a/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java index adfc423..c8132e7 100644 --- a/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java @@ -1,8 +1,10 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.connector.ConnectorFactory; +import cgeo.geocaching.connector.capability.ISearchByKeyword; import cgeo.geocaching.connector.gc.GCParser; +import cgeo.geocaching.settings.Settings; import android.content.Context; @@ -17,7 +19,18 @@ public class KeywordGeocacheListLoader extends AbstractSearchLoader { @Override public SearchResult runSearch() { - return GCParser.searchByKeyword(keyword, Settings.getCacheType(), Settings.isShowCaptcha(), this); + SearchResult searchResult = new SearchResult(); + if (Settings.isGCConnectorActive()) { + searchResult = GCParser.searchByKeyword(keyword, Settings.getCacheType(), Settings.isShowCaptcha(), this); + } + + for (ISearchByKeyword connector : ConnectorFactory.getSearchByKeywordConnectors()) { + if (connector.isActivated()) { + searchResult.addSearchResult(connector.searchByName(keyword)); + } + } + + return searchResult; } } diff --git a/main/src/cgeo/geocaching/loaders/OfflineGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/OfflineGeocacheListLoader.java index ab8ba6a..5088484 100644 --- a/main/src/cgeo/geocaching/loaders/OfflineGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/OfflineGeocacheListLoader.java @@ -2,7 +2,7 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; import cgeo.geocaching.settings.Settings; -import cgeo.geocaching.cgData; +import cgeo.geocaching.DataStore; import cgeo.geocaching.geopoint.Geopoint; import android.content.Context; @@ -20,7 +20,7 @@ public class OfflineGeocacheListLoader extends AbstractSearchLoader { @Override public SearchResult runSearch() { - return cgData.getBatchOfStoredCaches(searchCenter, Settings.getCacheType(), listId); + return DataStore.getBatchOfStoredCaches(searchCenter, Settings.getCacheType(), listId); } public void setListId(int listId) { diff --git a/main/src/cgeo/geocaching/loaders/RemoveFromHistoryLoader.java b/main/src/cgeo/geocaching/loaders/RemoveFromHistoryLoader.java index 2229afe..dc1a5df 100644 --- a/main/src/cgeo/geocaching/loaders/RemoveFromHistoryLoader.java +++ b/main/src/cgeo/geocaching/loaders/RemoveFromHistoryLoader.java @@ -1,10 +1,10 @@ package cgeo.geocaching.loaders; +import cgeo.geocaching.DataStore; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.settings.Settings; -import cgeo.geocaching.cgData; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.settings.Settings; import android.content.Context; @@ -15,14 +15,14 @@ public class RemoveFromHistoryLoader extends AbstractSearchLoader { public RemoveFromHistoryLoader(Context context, String[] selected, Geopoint coords) { super(context); - this.selected = selected; + this.selected = selected.clone(); this.coords = coords; } @Override public SearchResult runSearch() { - cgData.clearVisitDate(selected); - return cgData.getHistoryOfCaches(true, coords != null ? Settings.getCacheType() : CacheType.ALL); + DataStore.clearVisitDate(selected); + return DataStore.getHistoryOfCaches(true, coords != null ? Settings.getCacheType() : CacheType.ALL); } } diff --git a/main/src/cgeo/geocaching/maps/AbstractItemizedOverlay.java b/main/src/cgeo/geocaching/maps/AbstractItemizedOverlay.java index 464c2b2..747618b 100644 --- a/main/src/cgeo/geocaching/maps/AbstractItemizedOverlay.java +++ b/main/src/cgeo/geocaching/maps/AbstractItemizedOverlay.java @@ -31,10 +31,6 @@ public abstract class AbstractItemizedOverlay implements GeneralOverlay { return ovlImpl.superOnTap(index); } - Drawable boundCenter(Drawable markerIn) { - return ovlImpl.superBoundCenter(markerIn); - } - Drawable boundCenterBottom(Drawable markerIn) { return ovlImpl.superBoundCenterBottom(markerIn); } diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index 0703dd8..28e1b71 100644 --- a/main/src/cgeo/geocaching/maps/CGeoMap.java +++ b/main/src/cgeo/geocaching/maps/CGeoMap.java @@ -1,15 +1,14 @@ package cgeo.geocaching.maps; +import cgeo.geocaching.CacheListActivity; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.DirectionProvider; import cgeo.geocaching.Geocache; import cgeo.geocaching.IGeoData; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.StoredList; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; -import cgeo.geocaching.cgeocaches; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.gc.Login; @@ -22,6 +21,7 @@ import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.maps.interfaces.CachesOverlayItemImpl; import cgeo.geocaching.maps.interfaces.GeoPointImpl; import cgeo.geocaching.maps.interfaces.MapActivityImpl; @@ -40,7 +40,7 @@ import cgeo.geocaching.utils.LeastRecentlyUsedSet; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.RunnableWithArgument; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -128,7 +128,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto private MapItemFactory mapItemFactory = null; private Activity activity = null; private MapViewImpl mapView = null; - private cgeoapplication app = null; + private CgeoApplication app = null; final private GeoDirHandler geoDirUpdate = new UpdateLoc(); private SearchResult searchIntent = null; private String geocodeIntent = null; @@ -359,9 +359,9 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto // class init res = this.getResources(); activity = this.getActivity(); - app = (cgeoapplication) activity.getApplication(); + app = (CgeoApplication) activity.getApplication(); - int countBubbleCnt = cgData.getAllCachesCount(); + int countBubbleCnt = DataStore.getAllCachesCount(); caches = new LeastRecentlyUsedSet<Geocache>(MAX_CACHES + countBubbleCnt); final MapProvider mapProvider = Settings.getMapProvider(); @@ -491,7 +491,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto if (!CollectionUtils.isEmpty(dirtyCaches)) { for (String geocode : dirtyCaches) { - Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_WAYPOINTS); + Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_WAYPOINTS); if (cache != null) { // new collection type needs to remove first caches.remove(cache); @@ -592,7 +592,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto item = menu.findItem(R.id.menu_theme_mode); // show theme selection item.setVisible(mapView.hasMapThemes()); - menu.findItem(R.id.menu_as_list).setEnabled(isLiveEnabled && !isLoading()); + menu.findItem(R.id.menu_as_list).setEnabled(!isLoading()); menu.findItem(R.id.submenu_strategy).setEnabled(isLiveEnabled); @@ -641,7 +641,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto final List<String> geocodes = new ArrayList<String>(); for (final String geocode : geocodesInViewport) { - if (!cgData.isOffline(geocode, null)) { + if (!DataStore.isOffline(geocode, null)) { geocodes.add(geocode); } } @@ -690,7 +690,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto selectMapTheme(); return true; case R.id.menu_as_list: { - cgeocaches.startActivityMap(activity, new SearchResult(getGeocodesForCachesInViewport())); + CacheListActivity.startActivityMap(activity, new SearchResult(getGeocodesForCachesInViewport())); return true; } case R.id.menu_strategy_fastest: { @@ -1090,7 +1090,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto SearchResult searchResult; if (mapMode == MapMode.LIVE) { - searchResult = isLiveEnabled ? new SearchResult() : new SearchResult(cgData.loadStoredInViewport(viewport, Settings.getCacheType())); + searchResult = isLiveEnabled ? new SearchResult() : new SearchResult(DataStore.loadStoredInViewport(viewport, Settings.getCacheType())); } else { // map started from another activity searchResult = searchIntent != null ? new SearchResult(searchIntent) : new SearchResult(); @@ -1100,7 +1100,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } // live mode search result if (isLiveEnabled) { - searchResult.addSearchResult(cgData.loadCachedInViewport(viewport, Settings.getCacheType())); + searchResult.addSearchResult(DataStore.loadCachedInViewport(viewport, Settings.getCacheType())); } downloaded = true; @@ -1123,7 +1123,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto || mapMode == MapMode.COORDS) { //All visible waypoints CacheType type = Settings.getCacheType(); - Set<Waypoint> waypointsInViewport = cgData.loadWaypoints(viewport, excludeMine, excludeDisabled, type); + Set<Waypoint> waypointsInViewport = DataStore.loadWaypoints(viewport, excludeMine, excludeDisabled, type); waypoints.addAll(waypointsInViewport); } else { @@ -1196,8 +1196,8 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto // 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)); + caches.removeAll(DataStore.loadCaches(filteredCodes, LoadFlags.LOAD_CACHE_ONLY)); + DataStore.removeCaches(filteredCodes, EnumSet.of(RemoveFlag.REMOVE_CACHE)); // new collection type needs to remove first to refresh caches.removeAll(result); caches.addAll(result); @@ -1383,7 +1383,6 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto final private CancellableHandler handler; final private List<String> geocodes; final private int listId; - private long last = 0L; public LoadDetails(final CancellableHandler handler, final List<String> geocodes, final int listId) { this.handler = handler; @@ -1409,26 +1408,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto break; } - if (!cgData.isOffline(geocode, null)) { - if ((System.currentTimeMillis() - last) < 1500) { - try { - int delay = 1000 + (int) (Math.random() * 1000.0) - (int) (System.currentTimeMillis() - last); - if (delay < 0) { - delay = 500; - } - - sleep(delay); - } catch (InterruptedException e) { - // nothing - } - } - - if (handler.isCancelled()) { - Log.i("Stopped storing process."); - - break; - } - + if (!DataStore.isOffline(geocode, null)) { Geocache.storeCache(null, geocode, listId, false, handler); } } catch (Exception e) { @@ -1441,8 +1421,6 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto // FIXME: what does this yield() do here? yield(); - - last = System.currentTimeMillis(); } // we're done @@ -1512,9 +1490,9 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto Viewport viewport = null; if (geocodeCenter != null) { - viewport = cgData.getBounds(geocodeCenter); + viewport = DataStore.getBounds(geocodeCenter); } else if (searchCenter != null) { - viewport = cgData.getBounds(searchCenter.getGeocodes()); + viewport = DataStore.getBounds(searchCenter.getGeocodes()); } if (viewport == null) { diff --git a/main/src/cgeo/geocaching/maps/CachesOverlay.java b/main/src/cgeo/geocaching/maps/CachesOverlay.java index 8fbb3db..cd2ff75 100644 --- a/main/src/cgeo/geocaching/maps/CachesOverlay.java +++ b/main/src/cgeo/geocaching/maps/CachesOverlay.java @@ -5,7 +5,7 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.IWaypoint; import cgeo.geocaching.R; import cgeo.geocaching.WaypointPopup; -import cgeo.geocaching.cgData; +import cgeo.geocaching.DataStore; import cgeo.geocaching.activity.Progress; import cgeo.geocaching.connector.gc.GCMap; import cgeo.geocaching.enumerations.CacheType; @@ -228,7 +228,7 @@ public class CachesOverlay extends AbstractItemizedOverlay { final IWaypoint coordinate = item.getCoord(); if (StringUtils.isNotBlank(coordinate.getCoordType()) && coordinate.getCoordType().equalsIgnoreCase("cache") && StringUtils.isNotBlank(coordinate.getGeocode())) { - Geocache cache = cgData.loadCache(coordinate.getGeocode(), LoadFlags.LOAD_CACHE_OR_DB); + Geocache cache = DataStore.loadCache(coordinate.getGeocode(), LoadFlags.LOAD_CACHE_OR_DB); RequestDetailsThread requestDetailsThread = new RequestDetailsThread(cache); if (!requestDetailsThread.requestRequired()) { // don't show popup if we have enough details diff --git a/main/src/cgeo/geocaching/maps/MapProviderFactory.java b/main/src/cgeo/geocaching/maps/MapProviderFactory.java index a03fc47..2e43e19 100644 --- a/main/src/cgeo/geocaching/maps/MapProviderFactory.java +++ b/main/src/cgeo/geocaching/maps/MapProviderFactory.java @@ -1,7 +1,7 @@ package cgeo.geocaching.maps; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.maps.google.GoogleMapProvider; import cgeo.geocaching.maps.interfaces.MapProvider; import cgeo.geocaching.maps.interfaces.MapSource; @@ -31,7 +31,7 @@ public class MapProviderFactory { public static boolean isGoogleMapsInstalled() { // Check if API key is available - if (StringUtils.isBlank(cgeoapplication.getInstance().getString(R.string.maps_api_key))) { + if (StringUtils.isBlank(CgeoApplication.getInstance().getString(R.string.maps_api_key))) { Log.w("No Google API key available."); return false; } diff --git a/main/src/cgeo/geocaching/maps/google/GoogleMapProvider.java b/main/src/cgeo/geocaching/maps/google/GoogleMapProvider.java index 1fa38ad..cb95b2c 100644 --- a/main/src/cgeo/geocaching/maps/google/GoogleMapProvider.java +++ b/main/src/cgeo/geocaching/maps/google/GoogleMapProvider.java @@ -1,7 +1,7 @@ package cgeo.geocaching.maps.google; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.maps.AbstractMapProvider; import cgeo.geocaching.maps.AbstractMapSource; import cgeo.geocaching.maps.interfaces.MapItemFactory; @@ -20,7 +20,7 @@ public final class GoogleMapProvider extends AbstractMapProvider { private final MapItemFactory mapItemFactory; private GoogleMapProvider() { - final Resources resources = cgeoapplication.getInstance().getResources(); + final Resources resources = CgeoApplication.getInstance().getResources(); registerMapSource(new GoogleMapSource(this, resources.getString(R.string.map_source_google_map))); registerMapSource(new GoogleSatelliteSource(this, resources.getString(R.string.map_source_google_satellite))); diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapProvider.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapProvider.java index 7c6865d..c5ef40c 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapProvider.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapProvider.java @@ -1,8 +1,7 @@ package cgeo.geocaching.maps.mapsforge; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.settings.Settings; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.maps.AbstractMapProvider; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.maps.interfaces.MapItemFactory; @@ -10,6 +9,7 @@ import cgeo.geocaching.maps.interfaces.MapProvider; import cgeo.geocaching.maps.interfaces.MapSource; import cgeo.geocaching.maps.mapsforge.v024.MapsforgeMapActivity024; import cgeo.geocaching.maps.mapsforge.v024.MapsforgeMapItemFactory024; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; @@ -33,7 +33,7 @@ public final class MapsforgeMapProvider extends AbstractMapProvider { private MapItemFactory mapItemFactory = new MapsforgeMapItemFactory(); private MapsforgeMapProvider() { - final Resources resources = cgeoapplication.getInstance().getResources(); + final Resources resources = CgeoApplication.getInstance().getResources(); registerMapSource(new MapsforgeMapSource(MAPSFORGE_MAPNIK_ID, this, resources.getString(R.string.map_source_osm_mapnik), MapGeneratorInternal.MAPNIK)); registerMapSource(new MapsforgeMapSource(MAPSFORGE_CYCLEMAP_ID, this, resources.getString(R.string.map_source_osm_cyclemap), MapGeneratorInternal.OPENCYCLEMAP)); @@ -50,25 +50,27 @@ public final class MapsforgeMapProvider extends AbstractMapProvider { } public static List<String> getOfflineMaps() { - final String mapFile = Settings.getMapFile(); - if (StringUtils.isEmpty(mapFile)) { + final String directoryPath = Settings.getMapFileDirectory(); + if (StringUtils.isBlank(directoryPath)) { return Collections.emptyList(); } - try { - File directory = new File(mapFile).getParentFile(); - ArrayList<String> mapFileList = new ArrayList<String>(); - for (File file : directory.listFiles()) { - if (file.getName().endsWith(".map")) { - if (MapsforgeMapProvider.isValidMapFile(file.getAbsolutePath())) { - mapFileList.add(file.getAbsolutePath()); + File directory = new File(directoryPath); + if (directory.isDirectory()) { + try { + ArrayList<String> mapFileList = new ArrayList<String>(); + for (File file : directory.listFiles()) { + if (file.getName().endsWith(".map")) { + if (MapsforgeMapProvider.isValidMapFile(file.getAbsolutePath())) { + mapFileList.add(file.getAbsolutePath()); + } } } + Collections.sort(mapFileList, String.CASE_INSENSITIVE_ORDER); + return mapFileList; + } catch (Exception e) { + Log.e("MapsforgeMapProvider.getOfflineMaps: ", e); } - Collections.sort(mapFileList, String.CASE_INSENSITIVE_ORDER); - return mapFileList; - } catch (Exception e) { - Log.e("MapsforgeMapProvider.getOfflineMaps: ", e); } return Collections.emptyList(); } @@ -163,15 +165,11 @@ public final class MapsforgeMapProvider extends AbstractMapProvider { public void updateOfflineMaps() { MapProviderFactory.deleteOfflineMapSources(); - final Resources resources = cgeoapplication.getInstance().getResources(); + final Resources resources = CgeoApplication.getInstance().getResources(); final List<String> offlineMaps = getOfflineMaps(); for (String mapFile : offlineMaps) { final String mapName = StringUtils.capitalize(StringUtils.substringBeforeLast(new File(mapFile).getName(), ".")); registerMapSource(new OfflineMapSource(mapFile, this, mapName + " (" + resources.getString(R.string.map_source_osm_offline) + ")", MapGeneratorInternal.DATABASE_RENDERER)); } - // have a default entry, if no map files are available. otherwise we cannot select "offline" in the settings - if (offlineMaps.isEmpty()) { - registerMapSource(new OfflineMapSource("", this, resources.getString(R.string.map_source_osm_offline), MapGeneratorInternal.DATABASE_RENDERER)); - } } } diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java index aa11405..dc4e82c 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java @@ -1,7 +1,6 @@ package cgeo.geocaching.maps.mapsforge; import cgeo.geocaching.R; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.maps.CachesOverlay; import cgeo.geocaching.maps.PositionOverlay; @@ -15,6 +14,7 @@ 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.settings.Settings; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; @@ -46,6 +46,9 @@ public class MapsforgeMapView extends MapView implements MapViewImpl { public MapsforgeMapView(Context context, AttributeSet attrs) { super(context, attrs); gestureDetector = new GestureDetector(context, new GestureListener()); + if (Settings.isScaleMapsforgeText()) { + this.setTextScale(getResources().getDisplayMetrics().density); + } } @Override diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java index a94b988..f61e523 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeOverlay.java @@ -31,7 +31,7 @@ public class MapsforgeOverlay extends Overlay implements OverlayImpl { overlayBase = new ScaleOverlay(activityIn, this); break; default: - throw new IllegalArgumentException(); + throw new IllegalStateException(); } } diff --git a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java index bdaac98..8c9e0c3 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeOverlay.java @@ -29,6 +29,9 @@ public class MapsforgeOverlay extends Overlay implements OverlayImpl { break; case ScaleOverlay: overlayBase = new ScaleOverlay(activityIn, this); + break; + default: + throw new IllegalStateException(); } } diff --git a/main/src/cgeo/geocaching/network/HtmlImage.java b/main/src/cgeo/geocaching/network/HtmlImage.java index 797e67d..0daa588 100644 --- a/main/src/cgeo/geocaching/network/HtmlImage.java +++ b/main/src/cgeo/geocaching/network/HtmlImage.java @@ -1,18 +1,18 @@ package cgeo.geocaching.network; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.StoredList; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.files.LocalStorage; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.utils.FileUtils; -import cgeo.geocaching.utils.IOUtils; import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; - +import ch.boye.httpclientandroidlib.androidextra.Base64; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import android.content.res.Resources; @@ -27,6 +27,9 @@ import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.util.Date; public class HtmlImage implements Html.ImageGetter { @@ -74,7 +77,7 @@ public class HtmlImage implements Html.ImageGetter { Point displaySize = Compatibility.getDisplaySize(); this.maxWidth = displaySize.x - 25; this.maxHeight = displaySize.y - 25; - this.resources = cgeoapplication.getInstance().getResources(); + this.resources = CgeoApplication.getInstance().getResources(); } @Override @@ -91,24 +94,45 @@ public class HtmlImage implements Html.ImageGetter { // Download image and save it to the cache if (imagePre == null) { - final String absoluteURL = makeAbsoluteURL(url); - - if (absoluteURL != null) { - try { - final File file = LocalStorage.getStorageFile(pseudoGeocode, url, true, true); - final HttpResponse httpResponse = Network.getRequest(absoluteURL, null, file); - if (httpResponse != null) { - final int statusCode = httpResponse.getStatusLine().getStatusCode(); - if (statusCode == 200) { - LocalStorage.saveEntityToFile(httpResponse, file); - } else if (statusCode == 304) { - if (!file.setLastModified(System.currentTimeMillis())) { - makeFreshCopy(file); + final File file = LocalStorage.getStorageFile(pseudoGeocode, url, true, true); + if (url.startsWith("data:image/")) { + if (url.contains(";base64,")) { + // TODO: when we use SDK level 8 or above, we can use the streaming version of the base64 + // Android utilities. + byte[] decoded = Base64.decode(StringUtils.substringAfter(url, ";base64,"), Base64.DEFAULT); + OutputStream out = null; + try { + out = new FileOutputStream(file); + out.write(decoded); + } catch (final IOException e) { + Log.e("HtmlImage.getDrawable: cannot write file for decoded inline image", e); + return null; + } finally { + IOUtils.closeQuietly(out); + } + } else { + Log.e("HtmlImage.getDrawable: unable to decode non-base64 inline image"); + return null; + } + } else { + final String absoluteURL = makeAbsoluteURL(url); + + if (absoluteURL != null) { + try { + final HttpResponse httpResponse = Network.getRequest(absoluteURL, null, file); + if (httpResponse != null) { + final int statusCode = httpResponse.getStatusLine().getStatusCode(); + if (statusCode == 200) { + LocalStorage.saveEntityToFile(httpResponse, file); + } else if (statusCode == 304) { + if (!file.setLastModified(System.currentTimeMillis())) { + makeFreshCopy(file); + } } } + } catch (Exception e) { + Log.e("HtmlImage.getDrawable (downloading from web)", e); } - } catch (Exception e) { - Log.e("HtmlImage.getDrawable (downloading from web)", e); } } } diff --git a/main/src/cgeo/geocaching/network/Network.java b/main/src/cgeo/geocaching/network/Network.java index 2d2ab0b..e891d3b 100644 --- a/main/src/cgeo/geocaching/network/Network.java +++ b/main/src/cgeo/geocaching/network/Network.java @@ -37,6 +37,7 @@ import ch.boye.httpclientandroidlib.util.EntityUtils; import org.apache.commons.lang3.CharEncoding; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.Nullable; import org.json.JSONException; import org.json.JSONObject; @@ -54,8 +55,6 @@ import java.nio.charset.Charset; public abstract class Network { - private static final int NB_DOWNLOAD_RETRIES = 4; - /** User agent id */ private final static String PC_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:9.0.1) Gecko/20100101 Firefox/9.0.1"; /** Native user agent, taken from a Android 2.2 Nexus **/ @@ -75,7 +74,7 @@ public abstract class Network { static { Network.clientParams.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, CharEncoding.UTF_8); Network.clientParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 30000); - Network.clientParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, 30000); + Network.clientParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, 90000); Network.clientParams.setParameter(ClientPNames.HANDLE_REDIRECTS, true); } @@ -150,6 +149,7 @@ public abstract class Network { * @param params the parameters to add to the POST request * @return the HTTP response, or null in case of an encoding error params */ + @Nullable public static HttpResponse postRequest(final String uri, final Parameters params) { return request("POST", uri, params, null, null); } @@ -162,6 +162,7 @@ public abstract class Network { * @params headers the headers to add to the request * @return the HTTP response, or null in case of an encoding error params */ + @Nullable public static HttpResponse postRequest(final String uri, final Parameters params, final Parameters headers) { return request("POST", uri, params, headers, null); } @@ -173,18 +174,19 @@ public abstract class Network { * @param json the json object to add to the POST request * @return the HTTP response, or null in case of an encoding error params */ + @Nullable public static HttpResponse postJsonRequest(final String uri, final JSONObject json) { HttpPost request = new HttpPost(uri); request.addHeader("Content-Type", "application/json; charset=utf-8"); if (json != null) { try { - request.setEntity(new StringEntity(json.toString())); + request.setEntity(new StringEntity(json.toString(), CharEncoding.UTF_8)); } catch (UnsupportedEncodingException e) { Log.e("postJsonRequest:JSON Entity: UnsupportedEncodingException"); return null; } } - return doRepeatedRequests(request); + return doLogRequest(request); } /** @@ -197,6 +199,7 @@ public abstract class Network { * @param file the file to include in the request * @return the HTTP response, or null in case of an encoding error param */ + @Nullable public static HttpResponse postRequest(final String uri, final Parameters params, final String fileFieldName, final String fileContentType, final File file) { final MultipartEntity entity = new MultipartEntity(); @@ -214,7 +217,7 @@ public abstract class Network { request.setEntity(entity); addHeaders(request, null, null); - return doRepeatedRequests(request); + return doLogRequest(request); } /** @@ -232,7 +235,9 @@ public abstract class Network { * the cache file used to cache this query * @return the HTTP response, or null in case of an encoding error in a POST request arguments */ - private static HttpResponse request(final String method, final String uri, final Parameters params, final Parameters headers, final File cacheFile) { + @Nullable + private static HttpResponse request(final String method, final String uri, + @Nullable final Parameters params, @Nullable final Parameters headers, @Nullable final File cacheFile) { HttpRequestBase request; if (method.equals("GET")) { final String fullUri = params == null ? uri : Uri.parse(uri).buildUpon().encodedQuery(params.toString()).build().toString(); @@ -251,7 +256,7 @@ public abstract class Network { addHeaders(request, headers, cacheFile); - return doRepeatedRequests(request); + return doLogRequest(request); } /** @@ -263,7 +268,7 @@ public abstract class Network { * @param cacheFile * if non-null, the file to take ETag and If-Modified-Since information from */ - private static void addHeaders(final HttpRequestBase request, final Parameters headers, final File cacheFile) { + private static void addHeaders(final HttpRequestBase request, @Nullable final Parameters headers, @Nullable final File cacheFile) { for (final NameValuePair header : Parameters.extend(Parameters.merge(headers, cacheHeaders(cacheFile)), "Accept-Charset", "utf-8,iso-8859-1;q=0.8,utf-16;q=0.8,*;q=0.7", "Accept-Language", "en-US,*;q=0.9", @@ -275,44 +280,39 @@ public abstract class Network { } /** - * Retry a request for a few times. + * Perform an HTTP request and log it. * * @param request * the request to try * @return * the response, or null if there has been a failure */ - private static HttpResponse doRepeatedRequests(final HttpRequestBase request) { + @Nullable + private static HttpResponse doLogRequest(final HttpRequestBase request) { final String reqLogStr = request.getMethod() + " " + Network.hidePassword(request.getURI().toString()); Log.d(reqLogStr); final HttpClient client = Network.getHttpClient(); - for (int i = 0; i <= Network.NB_DOWNLOAD_RETRIES; i++) { - final long before = System.currentTimeMillis(); - try { - final HttpResponse response = client.execute(request); - int status = response.getStatusLine().getStatusCode(); - if (status == 200) { - Log.d(status + Network.formatTimeSpan(before) + reqLogStr); - } else { - Log.w(status + " [" + response.getStatusLine().getReasonPhrase() + "]" + Network.formatTimeSpan(before) + reqLogStr); - } - return response; - } catch (IOException e) { - final String timeSpan = Network.formatTimeSpan(before); - final String tries = (i + 1) + "/" + (Network.NB_DOWNLOAD_RETRIES + 1); - if (i == Network.NB_DOWNLOAD_RETRIES) { - Log.w("Failure " + tries + timeSpan + reqLogStr + " (" + e.toString() + ")"); - } else { - Log.w("Failure " + tries + " (" + e.toString() + ")" + timeSpan + "- retrying " + reqLogStr); - } + final long before = System.currentTimeMillis(); + try { + final HttpResponse response = client.execute(request); + int status = response.getStatusLine().getStatusCode(); + if (status == 200) { + Log.d(status + Network.formatTimeSpan(before) + reqLogStr); + } else { + Log.w(status + " [" + response.getStatusLine().getReasonPhrase() + "]" + Network.formatTimeSpan(before) + reqLogStr); } + return response; + } catch (final IOException e) { + final String timeSpan = Network.formatTimeSpan(before); + Log.w("Failure" + timeSpan + reqLogStr + " (" + e.toString() + ")"); } return null; } - private static Parameters cacheHeaders(final File cacheFile) { + @Nullable + private static Parameters cacheHeaders(@Nullable final File cacheFile) { if (cacheFile == null || !cacheFile.exists()) { return null; } @@ -343,7 +343,8 @@ public abstract class Network { * the name of the file storing the cached resource, or null not to use one * @return the HTTP response */ - public static HttpResponse getRequest(final String uri, final Parameters params, final File cacheFile) { + @Nullable + public static HttpResponse getRequest(final String uri, @Nullable final Parameters params, @Nullable final File cacheFile) { return request("GET", uri, params, null, cacheFile); } @@ -357,7 +358,8 @@ public abstract class Network { * the parameters to add the the GET request * @return the HTTP response */ - public static HttpResponse getRequest(final String uri, final Parameters params) { + @Nullable + public static HttpResponse getRequest(final String uri, @Nullable final Parameters params) { return request("GET", uri, params, null, null); } @@ -372,7 +374,8 @@ public abstract class Network { * the headers to add to the GET request * @return the HTTP response */ - public static HttpResponse getRequest(final String uri, final Parameters params, final Parameters headers) { + @Nullable + public static HttpResponse getRequest(final String uri, @Nullable final Parameters params, @Nullable final Parameters headers) { return request("GET", uri, params, headers, null); } @@ -383,6 +386,7 @@ public abstract class Network { * the URI to request * @return the HTTP response */ + @Nullable public static HttpResponse getRequest(final String uri) { return request("GET", uri, null, null, null); } @@ -392,10 +396,14 @@ public abstract class Network { return " (" + (System.currentTimeMillis() - before) + " ms) "; } - static public boolean isSuccess(final HttpResponse response) { + static public boolean isSuccess(@Nullable final HttpResponse response) { return response != null && response.getStatusLine().getStatusCode() == 200; } + static public boolean isPageNotFound(@Nullable final HttpResponse response) { + return response != null && response.getStatusLine().getStatusCode() == 404; + } + /** * Get the result of a GET HTTP request returning a JSON body. * @@ -403,7 +411,8 @@ public abstract class Network { * @param params the query parameters, or <code>null</code> if there are none * @return a JSON object if the request was successful and the body could be decoded, <code>null</code> otherwise */ - public static JSONObject requestJSON(final String uri, final Parameters params) { + @Nullable + public static JSONObject requestJSON(final String uri, @Nullable final Parameters params) { final HttpResponse response = request("GET", uri, params, new Parameters("Accept", "application/json, text/javascript, */*; q=0.01"), null); final String responseData = Network.getResponseData(response, false); if (responseData != null) { @@ -417,6 +426,7 @@ public abstract class Network { return null; } + @Nullable private static String getResponseDataNoError(final HttpResponse response, boolean replaceWhitespace) { try { String data = EntityUtils.toString(response.getEntity(), CharEncoding.UTF_8); @@ -435,10 +445,16 @@ public abstract class Network { * @param response a HTTP response, which can be null * @return the body if the response comes from a successful HTTP request, <code>null</code> otherwise */ - public static String getResponseData(final HttpResponse response) { + @Nullable + public static String getResponseData(@Nullable final HttpResponse response) { return Network.getResponseData(response, true); } + @Nullable + public static String getResponseDataAlways(@Nullable final HttpResponse response) { + return response != null ? getResponseDataNoError(response, false) : null; + } + /** * Get the body of a HTTP response. * @@ -447,17 +463,22 @@ public abstract class Network { * should be called on the body * @return the body if the response comes from a successful HTTP request, <code>null</code> otherwise */ - public static String getResponseData(final HttpResponse response, boolean replaceWhitespace) { + @Nullable + public static String getResponseData(@Nullable final HttpResponse response, boolean replaceWhitespace) { if (!isSuccess(response)) { return null; } + assert response != null; // Caught above return getResponseDataNoError(response, replaceWhitespace); } + @Nullable public static String rfc3986URLEncode(String text) { - return StringUtils.replace(Network.encode(text).replace("+", "%20"), "%7E", "~"); + final String encoded = Network.encode(text); + return encoded != null ? StringUtils.replace(encoded.replace("+", "%20"), "%7E", "~") : null; } + @Nullable public static String decode(final String text) { try { return URLDecoder.decode(text, CharEncoding.UTF_8); @@ -467,6 +488,7 @@ public abstract class Network { return null; } + @Nullable public static String encode(final String text) { try { return URLEncoder.encode(text, CharEncoding.UTF_8); diff --git a/main/src/cgeo/geocaching/network/OAuth.java b/main/src/cgeo/geocaching/network/OAuth.java index 6740096..c033660 100644 --- a/main/src/cgeo/geocaching/network/OAuth.java +++ b/main/src/cgeo/geocaching/network/OAuth.java @@ -5,6 +5,8 @@ import cgeo.geocaching.utils.CryptUtils; import ch.boye.httpclientandroidlib.NameValuePair; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import java.util.ArrayList; import java.util.Date; @@ -16,8 +18,8 @@ public class OAuth { final String method, final boolean https, final Parameters params, - final String token, - final String tokenSecret, + @Nullable final String token, + @Nullable final String tokenSecret, final String consumerKey, final String consumerSecret) { params.put( @@ -31,11 +33,21 @@ public class OAuth { final List<String> paramsEncoded = new ArrayList<String>(); for (final NameValuePair nameValue : params) { - paramsEncoded.add(nameValue.getName() + "=" + Network.rfc3986URLEncode(nameValue.getValue())); + paramsEncoded.add(nameValue.getName() + "=" + OAuth.percentEncode(nameValue.getValue())); } final String keysPacked = consumerSecret + "&" + StringUtils.defaultString(tokenSecret); // both even if empty some of them! - final String requestPacked = method + "&" + Network.rfc3986URLEncode((https ? "https" : "http") + "://" + host + path) + "&" + Network.rfc3986URLEncode(StringUtils.join(paramsEncoded.toArray(), '&')); + final String requestPacked = method + "&" + OAuth.percentEncode((https ? "https" : "http") + "://" + host + path) + "&" + OAuth.percentEncode(StringUtils.join(paramsEncoded.toArray(), '&')); params.put("oauth_signature", CryptUtils.base64Encode(CryptUtils.hashHmac(requestPacked, keysPacked))); } + + /** + * percent encode following http://tools.ietf.org/html/rfc5849#section-3.6 + * + * @param url + * @return + */ + static String percentEncode(@NonNull String url) { + return StringUtils.replace(Network.rfc3986URLEncode(url), "*", "%2A"); + } } diff --git a/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java b/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java index 221147c..888cf77 100644 --- a/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java @@ -1,5 +1,7 @@ package cgeo.geocaching.network; +import butterknife.InjectView; + import cgeo.geocaching.R; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.utils.Log; @@ -11,6 +13,8 @@ import ch.boye.httpclientandroidlib.util.EntityUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import android.app.ProgressDialog; import android.content.Intent; @@ -20,7 +24,6 @@ import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; -import android.widget.EditText; import android.widget.TextView; import java.io.IOException; @@ -28,20 +31,24 @@ import java.util.regex.Pattern; public abstract class OAuthAuthorizationActivity extends AbstractActivity { - private String host; - private String pathRequest; - private String pathAuthorize; - private String pathAccess; - private boolean https; - private String consumerKey; - private String consumerSecret; + public static final int NOT_AUTHENTICATED = 0; + public static final int AUTHENTICATED = 1; + + @NonNull final private String host; + @NonNull final private String pathRequest; + @NonNull final private String pathAuthorize; + @NonNull final private String pathAccess; + private final boolean https; + @NonNull final private String consumerKey; + @NonNull final private String consumerSecret; + @NonNull final private String callback; private String OAtoken = null; private String OAtokenSecret = null; private final Pattern paramsPattern1 = Pattern.compile("oauth_token=([a-zA-Z0-9\\-\\_.]+)"); private final Pattern paramsPattern2 = Pattern.compile("oauth_token_secret=([a-zA-Z0-9\\-\\_.]+)"); - private Button startButton = null; - private EditText pinEntry = null; - private Button pinEntryButton = null; + @InjectView(R.id.start) protected Button startButton; + @InjectView(R.id.auth_1) protected TextView auth_1; + @InjectView(R.id.auth_2) protected TextView auth_2; private ProgressDialog requestTokenDialog = null; private ProgressDialog changeTokensDialog = null; private Handler requestTokenHandler = new Handler() { @@ -57,10 +64,6 @@ public abstract class OAuthAuthorizationActivity extends AbstractActivity { if (msg.what == 1) { startButton.setText(getAuthAgain()); - - pinEntry.setVisibility(View.VISIBLE); - pinEntryButton.setVisibility(View.VISIBLE); - pinEntryButton.setOnClickListener(new ConfirmPINListener()); } else { showToast(getErrAuthInitialize()); startButton.setText(getAuthStart()); @@ -76,33 +79,26 @@ public abstract class OAuthAuthorizationActivity extends AbstractActivity { changeTokensDialog.dismiss(); } - pinEntryButton.setOnClickListener(new ConfirmPINListener()); - pinEntryButton.setEnabled(true); - - if (msg.what == 1) { + if (msg.what == AUTHENTICATED) { showToast(getAuthDialogCompleted()); - - pinEntryButton.setVisibility(View.GONE); - setResult(RESULT_OK); finish(); } else { showToast(getErrAuthProcess()); - - pinEntry.setVisibility(View.GONE); - pinEntryButton.setVisibility(View.GONE); startButton.setText(getAuthStart()); } } }; - public OAuthAuthorizationActivity(String host, - String pathRequest, - String pathAuthorize, - String pathAccess, - boolean https, - String consumerKey, - String consumerSecret) { + public OAuthAuthorizationActivity + (@NonNull String host, + @NonNull String pathRequest, + @NonNull String pathAuthorize, + @NonNull String pathAccess, + boolean https, + @NonNull String consumerKey, + @NonNull String consumerSecret, + @NonNull String callback) { this.host = host; this.pathRequest = pathRequest; this.pathAuthorize = pathAuthorize; @@ -110,34 +106,23 @@ public abstract class OAuthAuthorizationActivity extends AbstractActivity { this.https = https; this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; + this.callback = callback; } @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState, R.layout.authorization_activity); + super.onCreate(savedInstanceState, R.layout.authorization_activity, true); setTitle(getAuthTitle()); - init(); - } - - private void init() { - startButton = (Button) findViewById(R.id.start); - pinEntry = (EditText) findViewById(R.id.pin); - pinEntryButton = (Button) findViewById(R.id.pin_button); - - TextView auth = (TextView) findViewById(R.id.auth_1); - auth.setText(getAboutAuth1()); - auth = (TextView) findViewById(R.id.auth_2); - auth.setText(getAboutAuth2()); + auth_1.setText(getAuthExplainShort()); + auth_2.setText(getAuthExplainLong()); ImmutablePair<String, String> tempToken = getTempTokens(); OAtoken = tempToken.left; OAtokenSecret = tempToken.right; startButton.setText(getAuthAuthorize()); - pinEntryButton.setText(getAuthFinish()); - startButton.setEnabled(true); startButton.setOnClickListener(new StartListener()); @@ -147,24 +132,40 @@ public abstract class OAuthAuthorizationActivity extends AbstractActivity { } else { // already have temporary tokens, continue from pin startButton.setText(getAuthAgain()); + } + } + + @Override + public void onNewIntent(final Intent intent) { + setIntent(intent); + } - pinEntry.setHint(getAuthPinHint()); - pinEntry.setVisibility(View.VISIBLE); - pinEntryButton.setVisibility(View.VISIBLE); - pinEntryButton.setOnClickListener(new ConfirmPINListener()); + @Override + public void onResume() { + super.onResume(); + final Uri uri = getIntent().getData(); + if (uri != null) { + final String verifier = uri.getQueryParameter("oauth_verifier"); + if (StringUtils.isNotBlank(verifier)) { + exchangeTokens(verifier); + } else { + // We can shortcut the whole verification process if we do not have a token at all. + changeTokensHandler.sendEmptyMessage(NOT_AUTHENTICATED); + } } } private void requestToken() { - int status = 0; final Parameters params = new Parameters(); - params.put("oauth_callback", "oob"); + params.put("oauth_callback", callback); final String method = "GET"; OAuth.signOAuth(host, pathRequest, method, https, params, null, null, consumerKey, consumerSecret); final String line = Network.getResponseData(Network.getRequest(getUrlPrefix() + host + pathRequest, params)); + int status = 0; if (StringUtils.isNotBlank(line)) { + assert line != null; final MatcherWrapper paramsMatcher1 = new MatcherWrapper(paramsPattern1, line); if (paramsMatcher1.find()) { OAtoken = paramsMatcher1.group(1); @@ -193,12 +194,12 @@ public abstract class OAuthAuthorizationActivity extends AbstractActivity { requestTokenHandler.sendEmptyMessage(status); } - private void changeToken() { + private void changeToken(final String verifier) { - int status = 0; + int status = NOT_AUTHENTICATED; try { - final Parameters params = new Parameters("oauth_verifier", pinEntry.getText().toString()); + final Parameters params = new Parameters("oauth_verifier", verifier); final String method = "POST"; OAuth.signOAuth(host, pathAccess, method, https, params, OAtoken, OAtokenSecret, consumerKey, consumerSecret); @@ -222,7 +223,7 @@ public abstract class OAuthAuthorizationActivity extends AbstractActivity { setTokens(null, null, false); } else { setTokens(OAtoken, OAtokenSecret, true); - status = 1; + status = AUTHENTICATED; } } catch (Exception e) { Log.e("OAuthAuthorizationActivity.changeToken", e); @@ -260,68 +261,64 @@ public abstract class OAuthAuthorizationActivity extends AbstractActivity { } } - private class ConfirmPINListener implements View.OnClickListener { + private void exchangeTokens(final String verifier) { + if (changeTokensDialog == null) { + changeTokensDialog = new ProgressDialog(this); + changeTokensDialog.setCancelable(false); + changeTokensDialog.setMessage(getAuthDialogWait()); + } + changeTokensDialog.show(); - @Override - public void onClick(View arg0) { - if (StringUtils.isEmpty(((EditText) findViewById(R.id.pin)).getText().toString())) { - helpDialog(getAuthDialogPinTitle(), getAuthDialogPinMessage()); - return; - } + (new Thread() { - if (changeTokensDialog == null) { - changeTokensDialog = new ProgressDialog(OAuthAuthorizationActivity.this); - changeTokensDialog.setCancelable(false); - changeTokensDialog.setMessage(getAuthDialogWait()); + @Override + public void run() { + changeToken(verifier); } - changeTokensDialog.show(); - pinEntryButton.setEnabled(false); - pinEntryButton.setOnTouchListener(null); - pinEntryButton.setOnClickListener(null); - - (new Thread() { - - @Override - public void run() { - changeToken(); - } - }).start(); - } + }).start(); } protected abstract ImmutablePair<String, String> getTempTokens(); - protected abstract void setTempTokens(String tokenPublic, String tokenSecret); + protected abstract void setTempTokens(@Nullable String tokenPublic, @Nullable String tokenSecret); - protected abstract void setTokens(String tokenPublic, String tokenSecret, boolean enable); + protected abstract void setTokens(@Nullable String tokenPublic, @Nullable String tokenSecret, boolean enable); // get resources from derived class protected abstract String getAuthTitle(); - protected abstract String getAuthAgain(); + protected String getAuthAgain() { + return getString(R.string.auth_again); + } - protected abstract String getErrAuthInitialize(); + protected String getErrAuthInitialize() { + return getString(R.string.err_auth_initialize); + } - protected abstract String getAuthStart(); + protected String getAuthStart() { + return getString(R.string.auth_start); + } protected abstract String getAuthDialogCompleted(); - protected abstract String getErrAuthProcess(); - - protected abstract String getAuthDialogWait(); - - protected abstract String getAuthDialogPinTitle(); - - protected abstract String getAuthDialogPinMessage(); - - protected abstract String getAboutAuth1(); + protected String getErrAuthProcess() { + return res.getString(R.string.err_auth_process); + } - protected abstract String getAboutAuth2(); + protected String getAuthDialogWait() { + return res.getString(R.string.auth_dialog_waiting, getAuthTitle()); + } - protected abstract String getAuthAuthorize(); + protected String getAuthExplainShort() { + return res.getString(R.string.auth_explain_short, getAuthTitle()); + } - protected abstract String getAuthPinHint(); + protected String getAuthExplainLong() { + return res.getString(R.string.auth_explain_long, getAuthTitle()); + } - protected abstract String getAuthFinish(); + protected String getAuthAuthorize() { + return res.getString(R.string.auth_authorize, getAuthTitle()); + } } diff --git a/main/src/cgeo/geocaching/network/Parameters.java b/main/src/cgeo/geocaching/network/Parameters.java index f035c4a..9cb0da5 100644 --- a/main/src/cgeo/geocaching/network/Parameters.java +++ b/main/src/cgeo/geocaching/network/Parameters.java @@ -3,8 +3,9 @@ package cgeo.geocaching.network; import ch.boye.httpclientandroidlib.NameValuePair; import ch.boye.httpclientandroidlib.client.utils.URLEncodedUtils; import ch.boye.httpclientandroidlib.message.BasicNameValuePair; - import org.apache.commons.lang3.CharEncoding; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import java.security.InvalidParameterException; import java.util.ArrayList; @@ -81,7 +82,8 @@ public class Parameters extends ArrayList<NameValuePair> { * if the number of key/values is unbalanced * @return the object itself if it is non-null, a new one otherwise */ - public static Parameters extend(final Parameters params, final String... keyValues) { + @NonNull + public static Parameters extend(@Nullable final Parameters params, final String... keyValues) { return params == null ? new Parameters(keyValues) : params.put(keyValues); } @@ -94,7 +96,8 @@ public class Parameters extends ArrayList<NameValuePair> { * the object to merge from if non-null * @return params with extra data if params was non-null, extra otherwise */ - public static Parameters merge(final Parameters params, final Parameters extra) { + @Nullable + public static Parameters merge(@Nullable final Parameters params, @Nullable final Parameters extra) { if (params == null) { return extra; } diff --git a/main/src/cgeo/geocaching/network/StatusUpdater.java b/main/src/cgeo/geocaching/network/StatusUpdater.java index ee9bc31..cb4c7f4 100644 --- a/main/src/cgeo/geocaching/network/StatusUpdater.java +++ b/main/src/cgeo/geocaching/network/StatusUpdater.java @@ -1,6 +1,6 @@ package cgeo.geocaching.network; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.utils.MemorySubject; import cgeo.geocaching.utils.PeriodicHandler; import cgeo.geocaching.utils.PeriodicHandler.PeriodicHandlerListener; @@ -42,8 +42,8 @@ public class StatusUpdater extends MemorySubject<StatusUpdater.Status> implement public void onPeriodic() { final JSONObject response = Network.requestJSON("http://status.cgeo.org/api/status.json", - new Parameters("version_code", String.valueOf(Version.getVersionCode(cgeoapplication.getInstance())), - "version_name", Version.getVersionName(cgeoapplication.getInstance()), + new Parameters("version_code", String.valueOf(Version.getVersionCode(CgeoApplication.getInstance())), + "version_name", Version.getVersionName(CgeoApplication.getInstance()), "locale", Locale.getDefault().toString())); if (response != null) { notifyObservers(new Status(get(response, "message"), get(response, "message_id"), get(response, "icon"), get(response, "url"))); diff --git a/main/src/cgeo/geocaching/settings/AuthorizeTwitterPreference.java b/main/src/cgeo/geocaching/settings/AuthorizeTwitterPreference.java deleted file mode 100644 index 1fdd0de..0000000 --- a/main/src/cgeo/geocaching/settings/AuthorizeTwitterPreference.java +++ /dev/null @@ -1,46 +0,0 @@ -package cgeo.geocaching.settings; - -import cgeo.geocaching.R; -import cgeo.geocaching.twitter.TwitterAuthorizationActivity; - -import android.content.Context; -import android.content.Intent; -import android.preference.Preference; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; - -public class AuthorizeTwitterPreference extends Preference { - - public AuthorizeTwitterPreference(Context context) { - super(context); - } - - public AuthorizeTwitterPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public AuthorizeTwitterPreference(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected View onCreateView(ViewGroup parent) { - final SettingsActivity activity = (SettingsActivity) getContext(); - - setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - Intent authIntent = new Intent(preference.getContext(), - TwitterAuthorizationActivity.class); - activity.startActivityForResult(authIntent, - R.string.pref_fakekey_twitter_authorization); - - return false; // no shared preference has to be changed - } - }); - - activity.setTwitterAuthTitle(); - return super.onCreateView(parent); - } -} diff --git a/main/src/cgeo/geocaching/settings/LogSignaturePreference.java b/main/src/cgeo/geocaching/settings/LogSignaturePreference.java deleted file mode 100644 index d0c9739..0000000 --- a/main/src/cgeo/geocaching/settings/LogSignaturePreference.java +++ /dev/null @@ -1,60 +0,0 @@ -package cgeo.geocaching.settings; - -import cgeo.geocaching.R; - -import android.content.Context; -import android.preference.DialogPreference; -import android.util.AttributeSet; -import android.view.View; -import android.widget.Button; -import android.widget.EditText; - -public class LogSignaturePreference extends DialogPreference { - - private SettingsActivity settingsActivity; - private EditText editText; - - public LogSignaturePreference(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public LogSignaturePreference(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - private void init() { - setDialogLayoutResource(R.layout.log_signature_preference_dialog); - } - - @Override - protected void onBindDialogView(View view) { - settingsActivity = (SettingsActivity) this.getContext(); - - editText = (EditText) view.findViewById(R.id.signature_dialog_text); - editText.setText(getPersistedString("")); - settingsActivity.setSignatureTextView(editText); - - Button templates = (Button) view.findViewById(R.id.signature_templates); - settingsActivity.registerForContextMenu(templates); - templates.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View templates) { - settingsActivity.openContextMenu(templates); - } - }); - - super.onBindDialogView(view); - } - - @Override - protected void onDialogClosed(boolean positiveResult) { - if (positiveResult) { - String text = editText.getText().toString(); - persistString(text); - callChangeListener(text); - } - super.onDialogClosed(positiveResult); - } -} diff --git a/main/src/cgeo/geocaching/settings/AuthorizeOcPreference.java b/main/src/cgeo/geocaching/settings/OAuthPreference.java index f5d6a57..3550947 100644 --- a/main/src/cgeo/geocaching/settings/AuthorizeOcPreference.java +++ b/main/src/cgeo/geocaching/settings/OAuthPreference.java @@ -1,9 +1,10 @@ package cgeo.geocaching.settings; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.oc.OCDEAuthorizationActivity; import cgeo.geocaching.connector.oc.OCPLAuthorizationActivity; +import cgeo.geocaching.twitter.TwitterAuthorizationActivity; import android.content.Context; import android.content.Intent; @@ -12,49 +13,50 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; -public class AuthorizeOcPreference extends Preference { +public class OAuthPreference extends Preference { private static final int NO_KEY = -1; - private enum OCAuthorizations { + private enum OAuthActivityMapping { NONE(NO_KEY, null), OCDE(R.string.pref_fakekey_ocde_authorization, OCDEAuthorizationActivity.class), - OCPL(R.string.pref_fakekey_ocpl_authorization, OCPLAuthorizationActivity.class); + OCPL(R.string.pref_fakekey_ocpl_authorization, OCPLAuthorizationActivity.class), + TWITTER(R.string.pref_fakekey_twitter_authorization, TwitterAuthorizationActivity.class); public int prefKeyId; public Class<?> authActivity; - OCAuthorizations(int prefKeyId, Class<?> clazz) { + OAuthActivityMapping(int prefKeyId, Class<?> clazz) { this.prefKeyId = prefKeyId; this.authActivity = clazz; } } - private final OCAuthorizations ocAuth; + private final OAuthActivityMapping oAuthMapping; - private OCAuthorizations getAuthorization() { + private OAuthActivityMapping getAuthorization() { final String prefKey = getKey(); - for (OCAuthorizations auth : OCAuthorizations.values()) { - if (auth.prefKeyId != NO_KEY && prefKey.equals(cgeoapplication.getInstance().getString(auth.prefKeyId))) { + for (OAuthActivityMapping auth : OAuthActivityMapping.values()) { + if (auth.prefKeyId != NO_KEY && prefKey.equals(CgeoApplication.getInstance().getString(auth.prefKeyId))) { return auth; } } - return OCAuthorizations.NONE; + return OAuthActivityMapping.NONE; } - public AuthorizeOcPreference(Context context) { + public OAuthPreference(Context context) { super(context); - this.ocAuth = getAuthorization(); + this.oAuthMapping = getAuthorization(); } - public AuthorizeOcPreference(Context context, AttributeSet attrs) { + public OAuthPreference(Context context, AttributeSet attrs) { super(context, attrs); - this.ocAuth = getAuthorization(); + this.oAuthMapping = getAuthorization(); } - public AuthorizeOcPreference(Context context, AttributeSet attrs, int defStyle) { + public OAuthPreference(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - this.ocAuth = getAuthorization(); + this.oAuthMapping = getAuthorization(); } @Override @@ -64,17 +66,17 @@ public class AuthorizeOcPreference extends Preference { setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - if (ocAuth.authActivity != null) { - Intent authIntent = new Intent(preference.getContext(), - ocAuth.authActivity); - activity.startActivityForResult(authIntent, - ocAuth.prefKeyId); + if (oAuthMapping.authActivity != null) { + Intent authIntent = new Intent(preference.getContext(), + oAuthMapping.authActivity); + activity.startActivityForResult(authIntent, + oAuthMapping.prefKeyId); } return false; // no shared preference has to be changed } }); - activity.setOcAuthTitle(ocAuth.prefKeyId); + activity.setOcAuthTitle(oAuthMapping.prefKeyId); return super.onCreateView(parent); } } diff --git a/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java b/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java index a019c4a..fbf08fa 100644 --- a/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java +++ b/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java @@ -99,7 +99,7 @@ public class RegisterSend2CgeoPreference extends Preference { if (response != null && response.getStatusLine().getStatusCode() == 200) { //response was OK - String[] strings = Network.getResponseData(response).split(","); + String[] strings = StringUtils.split(Network.getResponseData(response), ','); try { pin = Integer.parseInt(strings[1].trim()); } catch (Exception e) { diff --git a/main/src/cgeo/geocaching/settings/Settings.java b/main/src/cgeo/geocaching/settings/Settings.java index 3597cd0..146182a 100644 --- a/main/src/cgeo/geocaching/settings/Settings.java +++ b/main/src/cgeo/geocaching/settings/Settings.java @@ -1,8 +1,7 @@ package cgeo.geocaching.settings; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.StoredList; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory.NavigationAppsEnum; import cgeo.geocaching.connector.gc.GCConstants; import cgeo.geocaching.connector.gc.Login; @@ -10,6 +9,7 @@ import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.maps.google.GoogleMapProvider; import cgeo.geocaching.maps.interfaces.GeoPointImpl; @@ -24,6 +24,7 @@ import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.Nullable; import android.content.Context; import android.content.SharedPreferences; @@ -41,7 +42,7 @@ import java.util.Locale; /** * General c:geo preferences/settings set by the user */ -public final class Settings { +public class Settings { public static final int SHOW_WP_THRESHOLD_DEFAULT = 10; public static final int SHOW_WP_THRESHOLD_MAX = 50; @@ -69,7 +70,7 @@ public final class Settings { } private static final SharedPreferences sharedPrefs = PreferenceManager - .getDefaultSharedPreferences(cgeoapplication.getInstance().getBaseContext()); + .getDefaultSharedPreferences(CgeoApplication.getInstance().getBaseContext()); static { migrateSettings(); Log.setDebug(sharedPrefs.getBoolean(getKey(R.string.pref_debug), false)); @@ -81,8 +82,8 @@ public final class Settings { */ private static MapSource mapSource; - private Settings() { - // this class is not to be instantiated; + protected Settings() { + throw new InstantiationError(); } private static void migrateSettings() { @@ -90,7 +91,7 @@ public final class Settings { int oldVersion = getInt(R.string.pref_settingsversion, 0); if (oldVersion < 1) { final String oldPreferencesName = "cgeo.pref"; - final SharedPreferences old = cgeoapplication.getInstance().getSharedPreferences(oldPreferencesName, Context.MODE_PRIVATE); + final SharedPreferences old = CgeoApplication.getInstance().getSharedPreferences(oldPreferencesName, Context.MODE_PRIVATE); final Editor e = sharedPrefs.edit(); e.putString(getKey(R.string.pref_temp_twitter_token_secret), old.getString(getKey(R.string.pref_temp_twitter_token_secret), null)); @@ -191,7 +192,7 @@ public final class Settings { } private static String getKey(final int prefKeyId) { - return cgeoapplication.getInstance().getString(prefKeyId); + return CgeoApplication.getInstance().getString(prefKeyId); } static String getString(final int prefKeyId, final String defaultValue) { @@ -214,13 +215,13 @@ public final class Settings { return sharedPrefs.getFloat(getKey(prefKeyId), defaultValue); } - static boolean putString(final int prefKeyId, final String value) { + protected static boolean putString(final int prefKeyId, final String value) { final SharedPreferences.Editor edit = sharedPrefs.edit(); edit.putString(getKey(prefKeyId), value); return edit.commit(); } - private static boolean putBoolean(final int prefKeyId, final boolean value) { + protected static boolean putBoolean(final int prefKeyId, final boolean value) { final SharedPreferences.Editor edit = sharedPrefs.edit(); edit.putBoolean(getKey(prefKeyId), value); return edit.commit(); @@ -257,7 +258,7 @@ public final class Settings { public static void setLanguage(boolean useEnglish) { final Configuration config = new Configuration(); config.locale = useEnglish ? Locale.ENGLISH : Locale.getDefault(); - final Resources resources = cgeoapplication.getInstance().getResources(); + final Resources resources = CgeoApplication.getInstance().getResources(); resources.updateConfiguration(config, resources.getDisplayMetrics()); } @@ -295,11 +296,7 @@ public final class Settings { public static boolean isPremiumMember() { // Basic Member, Premium Member, ??? - String memberStatus = Settings.getMemberStatus(); - if (memberStatus == null) { - return false; - } - return GCConstants.MEMBER_STATUS_PM.equalsIgnoreCase(memberStatus); + return GCConstants.MEMBER_STATUS_PM.equalsIgnoreCase(Settings.getMemberStatus()); } public static String getMemberStatus() { @@ -317,7 +314,7 @@ public final class Settings { return new ImmutablePair<String, String>(getString(tokenPublicPrefKey, null), getString(tokenSecretPrefKey, null)); } - public static void setTokens(final int tokenPublicPrefKey, final String tokenPublic, final int tokenSecretPrefKey, final String tokenSecret) { + public static void setTokens(final int tokenPublicPrefKey, @Nullable final String tokenPublic, final int tokenSecretPrefKey, @Nullable final String tokenSecret) { if (tokenPublic == null) { remove(tokenPublicPrefKey); } else { @@ -441,6 +438,10 @@ public final class Settings { return MapsforgeMapProvider.isValidMapFile(mapFileIn); } + public static boolean isScaleMapsforgeText() { + return getBoolean(R.string.pref_mapsforge_scale_text, true); + } + public static CoordInputFormatEnum getCoordInputFormat() { return CoordInputFormatEnum.fromInt(getInt(R.string.pref_coordinputformat, 0)); } @@ -449,10 +450,6 @@ public final class Settings { putInt(R.string.pref_coordinputformat, format.ordinal()); } - static void setLogOffline(final boolean offline) { - putBoolean(R.string.pref_log_offline, offline); - } - public static boolean getLogOffline() { return getBoolean(R.string.pref_log_offline, false); } @@ -481,11 +478,6 @@ public final class Settings { return getBoolean(R.string.pref_excludemine, false); } - public static void setUseEnglish(final boolean english) { - putBoolean(R.string.pref_useenglish, english); - setLanguage(english); - } - public static boolean isUseEnglish() { return getBoolean(R.string.pref_useenglish, false); } @@ -657,7 +649,7 @@ public final class Settings { public static void setAnyCoordinates(final Geopoint coords) { if (null != coords) { putFloat(R.string.pref_anylatitude, (float) coords.getLatitude()); - putFloat(R.string.pref_anylatitude, (float) coords.getLongitude()); + putFloat(R.string.pref_anylongitude, (float) coords.getLongitude()); } else { remove(R.string.pref_anylatitude); remove(R.string.pref_anylongitude); @@ -747,8 +739,8 @@ public final class Settings { && StringUtils.isNotBlank(getTokenSecret()); } - public static void setTwitterTokens(final String tokenPublic, - final String tokenSecret, boolean enableTwitter) { + public static void setTwitterTokens(@Nullable final String tokenPublic, + @Nullable final String tokenSecret, boolean enableTwitter) { putString(R.string.pref_twitter_token_public, tokenPublic); putString(R.string.pref_twitter_token_secret, tokenSecret); if (tokenPublic != null) { @@ -758,8 +750,8 @@ public final class Settings { setUseTwitter(enableTwitter); } - public static void setTwitterTempTokens(final String tokenPublic, - final String tokenSecret) { + public static void setTwitterTempTokens(@Nullable final String tokenPublic, + @Nullable final String tokenSecret) { putString(R.string.pref_temp_twitter_token_public, tokenPublic); putString(R.string.pref_temp_twitter_token_secret, tokenSecret); } @@ -796,22 +788,12 @@ public final class Settings { String.valueOf(NavigationAppsEnum.COMPASS.id))); } - public static void setDefaultNavigationTool(final int defaultNavigationTool) { - putString(R.string.pref_defaultNavigationTool, - String.valueOf(defaultNavigationTool)); - } - public static int getDefaultNavigationTool2() { return Integer.parseInt(getString( R.string.pref_defaultNavigationTool2, String.valueOf(NavigationAppsEnum.INTERNAL_MAP.id))); } - public static void setDefaultNavigationTool2(final int defaultNavigationTool) { - putString(R.string.pref_defaultNavigationTool2, - String.valueOf(defaultNavigationTool)); - } - public static Strategy getLiveMapStrategy() { return Strategy.getById(getInt(R.string.pref_livemapstrategy, Strategy.AUTO.id)); } @@ -924,13 +906,11 @@ public final class Settings { } public static String getCacheTwitterMessage() { - // TODO make customizable from UI - return "I found [NAME] ([URL])"; + return getString(R.string.pref_twitter_cache_message, "I found [NAME] ([URL])."); } public static String getTrackableTwitterMessage() { - // TODO make customizable from UI - return "I touched [NAME] ([URL])!"; + return getString(R.string.pref_twitter_trackable_message, "I touched [NAME] ([URL])."); } public static int getLogImageScale() { @@ -941,11 +921,6 @@ public final class Settings { putInt(R.string.pref_logImageScale, scale); } - // Only for tests! - static void setExcludeDisabledCaches(final boolean exclude) { - putBoolean(R.string.pref_excludedisabled, exclude); - } - public static void setExcludeMine(final boolean exclude) { putBoolean(R.string.pref_excludemine, exclude); } @@ -964,28 +939,47 @@ public final class Settings { return a && b; } - static void setStoreOfflineMaps(final boolean offlineMaps) { - putBoolean(R.string.pref_offlinemaps, offlineMaps); + public static long getFieldnoteExportDate() { + return getLong(R.string.pref_fieldNoteExportDate, 0); } - static void setStoreOfflineWpMaps(final boolean offlineWpMaps) { - putBoolean(R.string.pref_offlinewpmaps, offlineWpMaps); + /** + * remember date of last field note export + * + * @param date + */ + public static void setFieldnoteExportDate(final long date) { + putLong(R.string.pref_fieldNoteExportDate, date); } - static void setUseImperialUnits(final boolean imperial) { - putBoolean(R.string.pref_units, imperial); + public static boolean isUseNavigationApp(NavigationAppsEnum navApp) { + return getBoolean(navApp.preferenceKey, true); } - public static long getFieldnoteExportDate() { - return getLong(R.string.pref_fieldnoteExportDate, 0); + /** + * remember the state of the "Upload" checkbox in the field notes export dialog + * + * @param upload + */ + public static void setFieldNoteExportUpload(boolean upload) { + putBoolean(R.string.pref_fieldNoteExportUpload, upload); } - public static void setFieldnoteExportDate(final long date) { - putLong(R.string.pref_fieldnoteExportDate, date); + public static boolean getFieldNoteExportUpload() { + return getBoolean(R.string.pref_fieldNoteExportUpload, true); } - public static boolean isUseNavigationApp(NavigationAppsEnum navApp) { - return getBoolean(navApp.preferenceKey, true); + /** + * remember the state of the "Only new" checkbox in the field notes export dialog + * + * @param onlyNew + */ + public static void setFieldNoteExportOnlyNew(boolean onlyNew) { + putBoolean(R.string.pref_fieldNoteExportOnlyNew, onlyNew); + } + + public static boolean getFieldNoteExportOnlyNew() { + return getBoolean(R.string.pref_fieldNoteExportOnlyNew, false); } } diff --git a/main/src/cgeo/geocaching/settings/SettingsActivity.java b/main/src/cgeo/geocaching/settings/SettingsActivity.java index 90b6145..7b3f7fa 100644 --- a/main/src/cgeo/geocaching/settings/SettingsActivity.java +++ b/main/src/cgeo/geocaching/settings/SettingsActivity.java @@ -1,9 +1,9 @@ package cgeo.geocaching.settings; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Intents; import cgeo.geocaching.R; import cgeo.geocaching.SelectMapfileActivity; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory.NavigationAppsEnum; @@ -15,8 +15,6 @@ import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.maps.interfaces.MapSource; import cgeo.geocaching.utils.DatabaseBackupUtils; import cgeo.geocaching.utils.Log; -import cgeo.geocaching.utils.LogTemplateProvider; -import cgeo.geocaching.utils.LogTemplateProvider.LogTemplate; import org.apache.commons.lang3.StringUtils; import org.openintents.intents.FileManagerIntents; @@ -34,17 +32,10 @@ import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.MenuItem; -import android.view.MenuItem.OnMenuItemClickListener; -import android.view.View; import android.widget.BaseAdapter; -import android.widget.EditText; import android.widget.ListAdapter; import java.io.File; -import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -64,8 +55,6 @@ public class SettingsActivity extends PreferenceActivity { private static final String INTENT_GOTO = "GOTO"; private static final int INTENT_GOTO_SERVICES = 1; - private EditText signatureText; - /** * Enumeration for directory choosers. This is how we can retrieve information about the * directory and preference key in onActivityResult() easily just by knowing @@ -73,18 +62,20 @@ public class SettingsActivity extends PreferenceActivity { */ private enum DirChooserType { GPX_IMPORT_DIR(1, R.string.pref_gpxImportDir, - Environment.getExternalStorageDirectory().getPath() + "/gpx"), + Environment.getExternalStorageDirectory().getPath() + "/gpx", false), GPX_EXPORT_DIR(2, R.string.pref_gpxExportDir, - Environment.getExternalStorageDirectory().getPath() + "/gpx"), - THEMES_DIR(3, R.string.pref_renderthemepath, ""); + Environment.getExternalStorageDirectory().getPath() + "/gpx", true), + THEMES_DIR(3, R.string.pref_renderthemepath, "", false); public final int requestCode; public final int keyId; public final String defaultValue; + public final boolean writeMode; - DirChooserType(final int requestCode, final int keyId, final String defaultValue) { + DirChooserType(final int requestCode, final int keyId, final String defaultValue, final boolean writeMode) { this.requestCode = requestCode; this.keyId = keyId; this.defaultValue = defaultValue; + this.writeMode = writeMode; } } @@ -102,8 +93,10 @@ public class SettingsActivity extends PreferenceActivity { if (gotoPage == INTENT_GOTO_SERVICES) { // start with services screen PreferenceScreen main = (PreferenceScreen) getPreference(R.string.pref_fakekey_main_screen); - int index = getPreference(R.string.pref_fakekey_services_screen).getOrder(); - main.onItemClick(null, null, index, 0); + if (main != null) { + int index = getPreference(R.string.pref_fakekey_services_screen).getOrder(); + main.onItemClick(null, null, index, 0); + } } } @@ -131,7 +124,7 @@ public class SettingsActivity extends PreferenceActivity { R.string.pref_gpxExportDir, R.string.pref_gpxImportDir, R.string.pref_mapDirectory, R.string.pref_defaultNavigationTool, R.string.pref_defaultNavigationTool2, R.string.pref_webDeviceName, - R.string.pref_fakekey_preference_backup_info, }) { + R.string.pref_fakekey_preference_backup_info, R.string.pref_twitter_cache_message, R.string.pref_twitter_trackable_message }) { bindSummaryToStringValue(k); } getPreference(R.string.pref_units).setDefaultValue(Settings.getImperialUnitsDefault()); @@ -171,51 +164,13 @@ public class SettingsActivity extends PreferenceActivity { } private static String getKey(final int prefKeyId) { - return cgeoapplication.getInstance().getString(prefKeyId); + return CgeoApplication.getInstance().getString(prefKeyId); } private Preference getPreference(final int keyId) { return SettingsActivity.findPreference(this, getKey(keyId)); } - // workaround, because OnContextItemSelected nor onMenuItemSelected is never called - OnMenuItemClickListener TEMPLATE_CLICK = new OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(final MenuItem item) { - LogTemplate template = LogTemplateProvider.getTemplate(item.getItemId()); - if (template != null) { - insertSignatureTemplate(template); - return true; - } - return false; - } - }; - - // workaround, because OnContextItemSelected and onMenuItemSelected are never called - void setSignatureTextView(final EditText view) { - this.signatureText = view; - } - - @Override - public void onCreateContextMenu(final ContextMenu menu, final View v, - final ContextMenuInfo menuInfo) { - // context menu for signature templates - if (v.getId() == R.id.signature_templates) { - menu.setHeaderTitle(R.string.init_signature_template_button); - ArrayList<LogTemplate> templates = LogTemplateProvider.getTemplates(); - for (int i = 0; i < templates.size(); ++i) { - menu.add(0, templates.get(i).getItemId(), 0, templates.get(i).getResourceId()); - menu.getItem(i).setOnMenuItemClickListener(TEMPLATE_CLICK); - } - } - super.onCreateContextMenu(menu, v, menuInfo); - } - - private void insertSignatureTemplate(final LogTemplate template) { - String insertText = "[" + template.getTemplateString() + "]"; - ActivityMixin.insertAtPosition(signatureText, insertText, true); - } - /** * Fill the choice list for map sources. */ @@ -306,6 +261,7 @@ public class SettingsActivity extends PreferenceActivity { // OI file manager not available final Intent dirChooser = new Intent(this, SimpleDirChooser.class); dirChooser.putExtra(Intents.EXTRA_START_DIR, startDirectory); + dirChooser.putExtra(SimpleDirChooser.EXTRA_CHOOSE_FOR_WRITING, dct.writeMode); startActivityForResult(dirChooser, dct.requestCode); } } @@ -354,7 +310,7 @@ public class SettingsActivity extends PreferenceActivity { @Override public boolean onPreferenceClick(final Preference preference) { boolean oldValue = Settings.isDbOnSDCard(); - ((cgeoapplication) SettingsActivity.this.getApplication()) + ((CgeoApplication) SettingsActivity.this.getApplication()) .moveDatabase(SettingsActivity.this); return oldValue != Settings.isDbOnSDCard(); } @@ -407,6 +363,9 @@ public class SettingsActivity extends PreferenceActivity { case R.string.pref_fakekey_ocpl_authorization: setOCPLAuthTitle(); break; + case R.string.pref_fakekey_twitter_authorization: + setTwitterAuthTitle(); + break; default: Log.e(String.format(Locale.ENGLISH, "Invalid key %d in SettingsActivity.setTitle()", prefKeyId)); } @@ -415,22 +374,22 @@ public class SettingsActivity extends PreferenceActivity { void setOCDEAuthTitle() { getPreference(R.string.pref_fakekey_ocde_authorization) .setTitle(getString(Settings.hasOCAuthorization(R.string.pref_ocde_tokenpublic, R.string.pref_ocde_tokensecret) - ? R.string.init_reregister_oc_de - : R.string.init_register_oc_de)); + ? R.string.settings_reauthorize + : R.string.settings_authorize)); } void setOCPLAuthTitle() { getPreference(R.string.pref_fakekey_ocpl_authorization) .setTitle(getString(Settings.hasOCAuthorization(R.string.pref_ocpl_tokenpublic, R.string.pref_ocpl_tokensecret) - ? R.string.init_reregister_oc_pl - : R.string.init_register_oc_pl)); + ? R.string.settings_reauthorize + : R.string.settings_authorize)); } void setTwitterAuthTitle() { getPreference(R.string.pref_fakekey_twitter_authorization) .setTitle(getString(Settings.hasTwitterAuthorization() - ? R.string.init_twitter_reauthorize - : R.string.init_twitter_authorize)); + ? R.string.settings_reauthorize + : R.string.settings_authorize)); } public static void jumpToServicesPage(final Context fromActivity) { @@ -457,14 +416,25 @@ public class SettingsActivity extends PreferenceActivity { case R.string.pref_mapDirectory: if (data.hasExtra(Intents.EXTRA_MAP_FILE)) { final String mapFile = data.getStringExtra(Intents.EXTRA_MAP_FILE); - Settings.setMapFile(mapFile); - if (!Settings.isValidMapFile(Settings.getMapFile())) { - ActivityMixin.showToast(this, R.string.warn_invalid_mapfile); + File file = new File(mapFile); + if (!file.isDirectory()) { + Settings.setMapFile(mapFile); + if (!Settings.isValidMapFile(Settings.getMapFile())) { + ActivityMixin.showToast(this, R.string.warn_invalid_mapfile); + } else { + // Ensure map source preference is updated accordingly. + // TODO: There should be a better way to find and select the map source for a map file + Integer mapSourceId = mapFile.hashCode(); + ListPreference mapSource = (ListPreference) getPreference(R.string.pref_mapsource); + mapSource.setValue(mapSourceId.toString()); + VALUE_CHANGE_LISTENER.onPreferenceChange(mapSource, mapSourceId); + } + } else { + Settings.setMapFileDirectory(mapFile); } } initMapSourcePreference(); - getPreference(R.string.pref_mapDirectory).setSummary( - Settings.getMapFileDirectory()); + getPreference(R.string.pref_mapDirectory).setSummary(StringUtils.defaultString(Settings.getMapFileDirectory())); break; case R.string.pref_fakekey_ocde_authorization: setOCDEAuthTitle(); @@ -523,7 +493,7 @@ public class SettingsActivity extends PreferenceActivity { 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; + CgeoApplication.getInstance().checkLogin = true; } else if (preference instanceof ListPreference) { // For list preferences, look up the correct display value in // the preference's 'entries' list. @@ -554,7 +524,7 @@ public class SettingsActivity extends PreferenceActivity { if (Login.isActualLoginStatus()) { Login.logout(); } - cgeoapplication.getInstance().checkLogin = true; + CgeoApplication.getInstance().checkLogin = true; } return true; } diff --git a/main/src/cgeo/geocaching/settings/TemplateTextPreference.java b/main/src/cgeo/geocaching/settings/TemplateTextPreference.java new file mode 100644 index 0000000..9eaaa67 --- /dev/null +++ b/main/src/cgeo/geocaching/settings/TemplateTextPreference.java @@ -0,0 +1,111 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.R; +import cgeo.geocaching.activity.ActivityMixin; +import cgeo.geocaching.utils.LogTemplateProvider; +import cgeo.geocaching.utils.LogTemplateProvider.LogTemplate; + +import org.apache.commons.lang3.StringUtils; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.TypedArray; +import android.preference.DialogPreference; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +import java.util.ArrayList; + +public class TemplateTextPreference extends DialogPreference { + + /** + * default value, if none is given in the preference XML. + */ + private static final String DEFAULT_VALUE = StringUtils.EMPTY; + private SettingsActivity settingsActivity; + private EditText editText; + private String initialValue; + + public TemplateTextPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public TemplateTextPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + setDialogLayoutResource(R.layout.template_preference_dialog); + } + + @Override + protected void onBindDialogView(View view) { + settingsActivity = (SettingsActivity) this.getContext(); + + editText = (EditText) view.findViewById(R.id.signature_dialog_text); + editText.setText(getPersistedString(initialValue != null ? initialValue.toString() : StringUtils.EMPTY)); + + Button button = (Button) view.findViewById(R.id.signature_templates); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View button) { + AlertDialog.Builder alert = new AlertDialog.Builder(TemplateTextPreference.this.getContext()); + alert.setTitle(R.string.init_signature_template_button); + final ArrayList<LogTemplate> templates = LogTemplateProvider.getTemplates(); + String[] items = new String[templates.size()]; + for (int i = 0; i < templates.size(); i++) { + items[i] = settingsActivity.getResources().getString(templates.get(i).getResourceId()); + } + alert.setItems(items, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int position) { + dialog.dismiss(); + final LogTemplate template = templates.get(position); + insertSignatureTemplate(template); + } + }); + alert.create().show(); + } + }); + + super.onBindDialogView(view); + } + + private void insertSignatureTemplate(final LogTemplate template) { + String insertText = "[" + template.getTemplateString() + "]"; + ActivityMixin.insertAtPosition(editText, insertText, true); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + if (positiveResult) { + String text = editText.getText().toString(); + persistString(text); + callChangeListener(text); + } + super.onDialogClosed(positiveResult); + } + + @Override + protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { + if (restorePersistedValue) { + // Restore existing state + initialValue = this.getPersistedString(DEFAULT_VALUE); + } else { + // Set default state from the XML attribute + initialValue = defaultValue.toString(); + persistString(initialValue); + } + } + + @Override + protected Object onGetDefaultValue(TypedArray array, int index) { + return array.getString(index); + } +} diff --git a/main/src/cgeo/geocaching/sorting/ComparatorUserInterface.java b/main/src/cgeo/geocaching/sorting/ComparatorUserInterface.java index 3cdd393..99a535a 100644 --- a/main/src/cgeo/geocaching/sorting/ComparatorUserInterface.java +++ b/main/src/cgeo/geocaching/sorting/ComparatorUserInterface.java @@ -47,6 +47,7 @@ public class ComparatorUserInterface { register(R.string.caches_sort_inventory, InventoryComparator.class); register(R.string.caches_sort_name, NameComparator.class); register(R.string.caches_sort_favorites, PopularityComparator.class); + register(R.string.caches_sort_favorites_ratio, PopularityRatioComparator.class); register(R.string.caches_sort_rating, RatingComparator.class); register(R.string.caches_sort_size, SizeComparator.class); register(R.string.caches_sort_state, StateComparator.class); diff --git a/main/src/cgeo/geocaching/sorting/DateComparator.java b/main/src/cgeo/geocaching/sorting/DateComparator.java index 3464103..091f6a4 100644 --- a/main/src/cgeo/geocaching/sorting/DateComparator.java +++ b/main/src/cgeo/geocaching/sorting/DateComparator.java @@ -1,7 +1,7 @@ package cgeo.geocaching.sorting; import cgeo.geocaching.Geocache; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import java.util.ArrayList; import java.util.Date; @@ -27,7 +27,7 @@ public class DateComparator extends AbstractCacheComparator { final ArrayList<Geocache> list = new ArrayList<Geocache>(); list.add(cache1); list.add(cache2); - final DistanceComparator distanceComparator = new DistanceComparator(cgeoapplication.getInstance().currentGeo().getCoords(), list); + final DistanceComparator distanceComparator = new DistanceComparator(CgeoApplication.getInstance().currentGeo().getCoords(), list); return distanceComparator.compare(cache1, cache2); } return dateDifference; diff --git a/main/src/cgeo/geocaching/sorting/FindsComparator.java b/main/src/cgeo/geocaching/sorting/FindsComparator.java index ba929b8..b147fad 100644 --- a/main/src/cgeo/geocaching/sorting/FindsComparator.java +++ b/main/src/cgeo/geocaching/sorting/FindsComparator.java @@ -1,7 +1,7 @@ package cgeo.geocaching.sorting; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; -import cgeo.geocaching.cgData; import cgeo.geocaching.enumerations.LogType; public class FindsComparator extends AbstractCacheComparator { @@ -20,7 +20,7 @@ public class FindsComparator extends AbstractCacheComparator { private static int getFindsCount(Geocache cache) { if (cache.getLogCounts().isEmpty()) { - cache.setLogCounts(cgData.loadLogCounts(cache.getGeocode())); + cache.setLogCounts(DataStore.loadLogCounts(cache.getGeocode())); } Integer logged = cache.getLogCounts().get(LogType.FOUND_IT); if (logged != null) { diff --git a/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java b/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java new file mode 100644 index 0000000..fab7bb1 --- /dev/null +++ b/main/src/cgeo/geocaching/sorting/PopularityRatioComparator.java @@ -0,0 +1,56 @@ +/** + * + */ +package cgeo.geocaching.sorting; + +import cgeo.geocaching.DataStore; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.enumerations.LogType; + +/** + * sorts caches by popularity ratio (favorites per find in %). + * only caches with 10 finds and more are counted to obtain meaningful statistics + */ +public class PopularityRatioComparator extends AbstractCacheComparator { + + @Override + protected boolean canCompare(final Geocache cache1, final Geocache cache2) { + return true; + } + + @Override + protected int compareCaches(final Geocache cache1, final Geocache cache2) { + + float ratio1 = 0.0f; + float ratio2 = 0.0f; + + int finds1 = getFindsCount(cache1); + int finds2 = getFindsCount(cache2); + + if (finds1 != 0 && finds1 > 9) { + ratio1 = (((float) cache1.getFavoritePoints()) / ((float) finds1)); + } + if (finds2 != 0 && finds2 > 9) { + ratio2 = (((float) cache2.getFavoritePoints()) / ((float) finds2)); + } + + if ((ratio2 - ratio1) > 0.0f) { + return 1; + } else if ((ratio2 - ratio1) < 0.0f) { + return -1; + } + + return 0; + } + + private static int getFindsCount(Geocache cache) { + if (cache.getLogCounts().isEmpty()) { + cache.setLogCounts(DataStore.loadLogCounts(cache.getGeocode())); + } + Integer logged = cache.getLogCounts().get(LogType.FOUND_IT); + if (logged != null) { + return logged; + } + return 0; + } +} diff --git a/main/src/cgeo/geocaching/speech/SpeechService.java b/main/src/cgeo/geocaching/speech/SpeechService.java index 1907bfc..2a72bbf 100644 --- a/main/src/cgeo/geocaching/speech/SpeechService.java +++ b/main/src/cgeo/geocaching/speech/SpeechService.java @@ -1,8 +1,8 @@ package cgeo.geocaching.speech; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.DirectionProvider; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.settings.Settings; @@ -48,7 +48,7 @@ public class SpeechService extends Service implements OnInitListener { GeoDirHandler geoHandler = new GeoDirHandler() { @Override protected void updateDirection(float newDirection) { - if (cgeoapplication.getInstance().currentGeo().getSpeed() <= 5) { + if (CgeoApplication.getInstance().currentGeo().getSpeed() <= 5) { direction = DirectionProvider.getDirectionNow(startingActivity, newDirection); directionInitialized = true; updateCompass(); @@ -177,7 +177,7 @@ public class SpeechService extends Service implements OnInitListener { if (intent != null) { target = intent.getParcelableExtra(EXTRA_TARGET_COORDS); } - return START_NOT_STICKY; + return START_NOT_STICKY; // service can be stopped by system, if under memory pressure } private void speak(final String text) { diff --git a/main/src/cgeo/geocaching/speech/TextFactory.java b/main/src/cgeo/geocaching/speech/TextFactory.java index 81a74fb..2a3b6d7 100644 --- a/main/src/cgeo/geocaching/speech/TextFactory.java +++ b/main/src/cgeo/geocaching/speech/TextFactory.java @@ -1,8 +1,8 @@ package cgeo.geocaching.speech; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.settings.Settings; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.IConversion; import cgeo.geocaching.utils.AngleUtils; @@ -12,6 +12,13 @@ import java.util.Locale; /** * Creates the output to be read by TTS. * + * Note: some languages need to read "one hour" as "a hour" (indefinite article). Also, other languages + * use the <tt>quantity="1"</tt> plurals rule for other values than 1, such as Slovenian, so it is not + * possible to store the literal value to use for 1 in this rule. For this reason, we need to have one + * string for the unit quantity ("one meter") and a plurals rule for everything else. + * + * See http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html for rules + * on unit expressions. */ public class TextFactory { public static String getText(Geopoint position, Geopoint target, float direction) { @@ -78,11 +85,11 @@ public class TextFactory { } private static String getString(int resourceId, Object... formatArgs) { - return cgeoapplication.getInstance().getString(resourceId, formatArgs); + return CgeoApplication.getInstance().getString(resourceId, formatArgs); } private static String getQuantityString(int resourceId, int quantity, Object... formatArgs) { - return cgeoapplication.getInstance().getResources().getQuantityString(resourceId, quantity, formatArgs); + return CgeoApplication.getInstance().getResources().getQuantityString(resourceId, quantity, formatArgs); } private static String getDirection(Geopoint position, Geopoint target, float direction) { diff --git a/main/src/cgeo/geocaching/twitter/Twitter.java b/main/src/cgeo/geocaching/twitter/Twitter.java index 7233764..7213789 100644 --- a/main/src/cgeo/geocaching/twitter/Twitter.java +++ b/main/src/cgeo/geocaching/twitter/Twitter.java @@ -1,9 +1,10 @@ package cgeo.geocaching.twitter; +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; +import cgeo.geocaching.LogEntry; import cgeo.geocaching.Trackable; -import cgeo.geocaching.cgData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter.Format; @@ -12,31 +13,42 @@ import cgeo.geocaching.network.OAuth; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.LogTemplateProvider; +import cgeo.geocaching.utils.LogTemplateProvider.LogContext; import ch.boye.httpclientandroidlib.HttpResponse; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; public final class Twitter { private static final String HASH_PREFIX_WITH_BLANK = " #"; private static final int MAX_TWEET_SIZE = 140; - public static void postTweetCache(String geocode) { - final Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); - postTweet(cgeoapplication.getInstance(), getStatusMessage(cache), null); + public static void postTweetCache(String geocode, final @Nullable LogEntry logEntry) { + final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + if (cache == null) { + return; + } + postTweet(CgeoApplication.getInstance(), getStatusMessage(cache, logEntry), null); } - public static void postTweetTrackable(String geocode) { - final Trackable trackable = cgData.loadTrackable(geocode); - postTweet(cgeoapplication.getInstance(), getStatusMessage(trackable), null); + public static void postTweetTrackable(String geocode, final @Nullable LogEntry logEntry) { + final Trackable trackable = DataStore.loadTrackable(geocode); + if (trackable == null) { + return; + } + postTweet(CgeoApplication.getInstance(), getStatusMessage(trackable, logEntry), null); } - private static void postTweet(final cgeoapplication app, final String status, final Geopoint coords) { + private static void postTweet(final CgeoApplication app, final String statusIn, final Geopoint coords) { if (app == null || !Settings.isUseTwitter() || !Settings.isTwitterLoginValid()) { return; } try { + final String status = shortenToMaxSize(statusIn); Parameters parameters = new Parameters("status", status); if (coords != null) { parameters.put( @@ -61,38 +73,33 @@ public final class Twitter { } } + private static String shortenToMaxSize(final String status) { + String result = StringUtils.trim(status); + if (StringUtils.length(result) > MAX_TWEET_SIZE) { + return StringUtils.substring(result, 0, MAX_TWEET_SIZE - 1) + 'โฆ'; + } + return result; + } + private static void appendHashTag(final StringBuilder status, final String tag) { if (status.length() + HASH_PREFIX_WITH_BLANK.length() + tag.length() <= MAX_TWEET_SIZE) { final String tagWithPrefix = HASH_PREFIX_WITH_BLANK + tag; - if (status.indexOf(tagWithPrefix, 0) == -1) { + if (!StringUtils.contains(status, tagWithPrefix)) { status.append(tagWithPrefix); } } } - static String getStatusMessage(Geocache cache) { - String name = cache.getName(); - if (name.length() > 100) { - name = name.substring(0, 100) + 'โฆ'; - } - final String url = StringUtils.defaultString(cache.getUrl()); - return fillTemplate(Settings.getCacheTwitterMessage(), name, url); + static String getStatusMessage(final @NonNull Geocache cache, final @Nullable LogEntry logEntry) { + return appendHashTags(LogTemplateProvider.applyTemplates(Settings.getCacheTwitterMessage(), new LogContext(cache, logEntry))); } - static String getStatusMessage(Trackable trackable) { - String name = trackable.getName(); - if (name.length() > 82) { - name = name.substring(0, 81) + 'โฆ'; - } - String url = StringUtils.defaultString(trackable.getUrl()); - String status = Settings.getTrackableTwitterMessage(); - return fillTemplate(status, name, url); + static String getStatusMessage(final @NonNull Trackable trackable, final @Nullable LogEntry logEntry) { + return appendHashTags(LogTemplateProvider.applyTemplates(Settings.getTrackableTwitterMessage(), new LogContext(trackable, logEntry))); } - private static String fillTemplate(String template, String name, final String url) { - String result = StringUtils.replace(template, "[NAME]", name); - result = StringUtils.replace(result, "[URL]", url); - StringBuilder builder = new StringBuilder(result); + private static String appendHashTags(final String status) { + StringBuilder builder = new StringBuilder(status); appendHashTag(builder, "cgeo"); appendHashTag(builder, "geocaching"); return builder.toString(); diff --git a/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java b/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java index 4d9a1f2..b813389 100644 --- a/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java @@ -1,10 +1,11 @@ package cgeo.geocaching.twitter; import cgeo.geocaching.R; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.network.OAuthAuthorizationActivity; +import cgeo.geocaching.settings.Settings; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.Nullable; public class TwitterAuthorizationActivity extends OAuthAuthorizationActivity { @@ -15,92 +16,33 @@ public class TwitterAuthorizationActivity extends OAuthAuthorizationActivity { "/oauth/access_token", true, Settings.getKeyConsumerPublic(), - Settings.getKeyConsumerSecret()); + Settings.getKeyConsumerSecret(), + "callback://www.cgeo.org/twitter/"); } @Override - protected ImmutablePair<String, String> getTempTokens() { + protected final ImmutablePair<String, String> getTempTokens() { return Settings.getTempToken(); } @Override - protected void setTempTokens(String tokenPublic, String tokenSecret) { + protected final void setTempTokens(@Nullable final String tokenPublic, @Nullable final String tokenSecret) { Settings.setTwitterTempTokens(tokenPublic, tokenSecret); } @Override - protected void setTokens(String tokenPublic, String tokenSecret, boolean enable) { + protected final void setTokens(@Nullable final String tokenPublic, @Nullable final String tokenSecret, final boolean enable) { Settings.setTwitterTokens(tokenPublic, tokenSecret, enable); } @Override - protected String getAuthTitle() { + protected final String getAuthTitle() { return res.getString(R.string.auth_twitter); } @Override - protected String getAuthAgain() { - return res.getString(R.string.auth_again); - } - - @Override - protected String getErrAuthInitialize() { - return res.getString(R.string.err_auth_initialize); - } - - @Override - protected String getAuthStart() { - return res.getString(R.string.auth_start); - } - - @Override - protected String getAuthDialogCompleted() { - return res.getString(R.string.auth_dialog_completed); - } - - @Override - protected String getErrAuthProcess() { - return res.getString(R.string.err_auth_process); - } - - @Override - protected String getAuthDialogWait() { - return res.getString(R.string.auth_dialog_wait); - } - - @Override - protected String getAuthDialogPinTitle() { - return res.getString(R.string.auth_dialog_pin_title); - } - - @Override - protected String getAuthDialogPinMessage() { - return res.getString(R.string.auth_dialog_pin_message); - } - - @Override - protected String getAboutAuth1() { - return res.getString(R.string.about_auth_1); - } - - @Override - protected String getAboutAuth2() { - return res.getString(R.string.about_auth_2); - } - - @Override - protected String getAuthAuthorize() { - return res.getString(R.string.auth_authorize); - } - - @Override - protected String getAuthPinHint() { - return res.getString(R.string.auth_pin_hint); - } - - @Override - protected String getAuthFinish() { - return res.getString(R.string.auth_finish); + protected final String getAuthDialogCompleted() { + return res.getString(R.string.auth_dialog_completed_twitter); } } diff --git a/main/src/cgeo/geocaching/ui/AbstractUIFactory.java b/main/src/cgeo/geocaching/ui/AbstractUIFactory.java index 2351383..0d447f2 100644 --- a/main/src/cgeo/geocaching/ui/AbstractUIFactory.java +++ b/main/src/cgeo/geocaching/ui/AbstractUIFactory.java @@ -1,9 +1,9 @@ package cgeo.geocaching.ui; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import android.content.res.Resources; public class AbstractUIFactory { - protected final static Resources res = cgeoapplication.getInstance().getResources(); + protected final static Resources res = CgeoApplication.getInstance().getResources(); } diff --git a/main/src/cgeo/geocaching/ui/AbstractUserClickListener.java b/main/src/cgeo/geocaching/ui/AbstractUserClickListener.java index b717568..f8cfb8a 100644 --- a/main/src/cgeo/geocaching/ui/AbstractUserClickListener.java +++ b/main/src/cgeo/geocaching/ui/AbstractUserClickListener.java @@ -1,7 +1,7 @@ package cgeo.geocaching.ui; import cgeo.geocaching.R; -import cgeo.geocaching.cgeocaches; +import cgeo.geocaching.CacheListActivity; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.network.Network; @@ -53,10 +53,10 @@ abstract class AbstractUserClickListener implements View.OnClickListener { public void onClick(DialogInterface dialog, int item) { switch (item) { case 0: - cgeocaches.startActivityOwner(context, name.toString()); + CacheListActivity.startActivityOwner(context, name.toString()); return; case 1: - cgeocaches.startActivityUserName(context, name.toString()); + CacheListActivity.startActivityUserName(context, name.toString()); return; case 2: context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/profile/?u=" + Network.encode(name.toString())))); diff --git a/main/src/cgeo/geocaching/ui/AddressListAdapter.java b/main/src/cgeo/geocaching/ui/AddressListAdapter.java index 736c036..0d5fba7 100644 --- a/main/src/cgeo/geocaching/ui/AddressListAdapter.java +++ b/main/src/cgeo/geocaching/ui/AddressListAdapter.java @@ -2,9 +2,9 @@ package cgeo.geocaching.ui; import butterknife.InjectView; +import cgeo.geocaching.CacheListActivity; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; -import cgeo.geocaching.cgeocaches; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Units; @@ -38,7 +38,7 @@ public class AddressListAdapter extends ArrayAdapter<Address> { public AddressListAdapter(final Context context) { super(context, 0); inflater = ((Activity) context).getLayoutInflater(); - location = cgeoapplication.getInstance().currentGeo().getCoords(); + location = CgeoApplication.getInstance().currentGeo().getCoords(); } @Override @@ -61,7 +61,7 @@ public class AddressListAdapter extends ArrayAdapter<Address> { @Override public void onClick(final View v) { final Activity activity = (Activity) v.getContext(); - cgeocaches.startActivityAddress(activity, new Geopoint(address.getLatitude(), address.getLongitude()), StringUtils.defaultString(address.getAddressLine(0))); + CacheListActivity.startActivityAddress(activity, new Geopoint(address.getLatitude(), address.getLongitude()), StringUtils.defaultString(address.getAddressLine(0))); activity.finish(); } }); diff --git a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java index 9059a6b..5db562e 100644 --- a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java +++ b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java @@ -1,16 +1,18 @@ package cgeo.geocaching.ui; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Units; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.app.Activity; import android.content.res.Resources; +import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -134,7 +136,7 @@ public final class CacheDetailsCreator { public void addDistance(final Geocache cache, final TextView cacheDistanceView) { Float distance = null; if (cache.getCoords() != null) { - final Geopoint currentCoords = cgeoapplication.getInstance().currentGeo().getCoords(); + final Geopoint currentCoords = CgeoApplication.getInstance().currentGeo().getCoords(); if (currentCoords != null) { distance = currentCoords.distanceTo(cache); } @@ -159,7 +161,7 @@ public final class CacheDetailsCreator { public void addDistance(final Waypoint wpt, final TextView waypointDistanceView) { Float distance = null; if (wpt.getCoords() != null) { - final Geopoint currentCoords = cgeoapplication.getInstance().currentGeo().getCoords(); + final Geopoint currentCoords = CgeoApplication.getInstance().currentGeo().getCoords(); if (currentCoords != null) { distance = currentCoords.distanceTo(wpt); } @@ -175,4 +177,14 @@ public final class CacheDetailsCreator { } add(R.string.cache_distance, text); } + + public void addEventDate(@NonNull Geocache cache) { + if (cache.isEventCache() && cache.getHiddenDate() != null) { + final long time = cache.getHiddenDate().getTime(); + if (time > 0) { + final String dateString = DateUtils.formatDateTime(CgeoApplication.getInstance().getBaseContext(), time, DateUtils.FORMAT_SHOW_WEEKDAY) + ", " + Formatter.formatFullDate(time); + add(R.string.cache_event, dateString); + } + } + } } diff --git a/main/src/cgeo/geocaching/ui/CacheListAdapter.java b/main/src/cgeo/geocaching/ui/CacheListAdapter.java index c6aeaa6..56cc60a 100644 --- a/main/src/cgeo/geocaching/ui/CacheListAdapter.java +++ b/main/src/cgeo/geocaching/ui/CacheListAdapter.java @@ -3,10 +3,10 @@ package cgeo.geocaching.ui; import butterknife.InjectView; import cgeo.geocaching.CacheDetailActivity; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.IGeoData; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheListType; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.filter.IFilter; @@ -18,9 +18,10 @@ import cgeo.geocaching.sorting.EventDateComparator; import cgeo.geocaching.sorting.InverseComparator; import cgeo.geocaching.sorting.VisitComparator; import cgeo.geocaching.utils.AngleUtils; +import cgeo.geocaching.utils.DateUtils; import cgeo.geocaching.utils.Log; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -112,7 +113,7 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> { public CacheListAdapter(final Activity activity, final List<Geocache> list, CacheListType cacheListType) { super(activity, 0, list); - final IGeoData currentGeo = cgeoapplication.getInstance().currentGeo(); + final IGeoData currentGeo = CgeoApplication.getInstance().currentGeo(); if (currentGeo != null) { coords = currentGeo.getCoords(); } @@ -328,7 +329,7 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> { } public void setActualHeading(final float direction) { - if (Math.abs(AngleUtils.difference(azimuth, direction)) < 10) { + if (Math.abs(AngleUtils.difference(azimuth, direction)) < 5) { return; } @@ -399,7 +400,7 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> { } Spannable spannable = null; - if (cache.isDisabled() || cache.isArchived()) { // strike + if (cache.isDisabled() || cache.isArchived() || isPastEvent(cache)) { // strike spannable = Spannable.Factory.getInstance().newSpannable(cache.getName()); spannable.setSpan(new StrikethroughSpan(), 0, spannable.toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } @@ -488,6 +489,10 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> { return v; } + private static boolean isPastEvent(final Geocache cache) { + return cache.isEventCache() && DateUtils.daysSince(cache.getHiddenDate().getTime()) > 0; + } + private static Drawable getCacheIcon(Geocache cache) { int hashCode = getIconHashCode(cache.getType(), cache.hasUserModifiedCoords() || cache.hasFinalDefined()); final Drawable drawable = gcIconDrawables.get(hashCode); @@ -517,6 +522,7 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> { @Override public void onClick(View view) { + assert view instanceof CheckBox; final boolean checkNow = ((CheckBox) view).isChecked(); cache.setStatusChecked(checkNow); } diff --git a/main/src/cgeo/geocaching/ui/CompassMiniView.java b/main/src/cgeo/geocaching/ui/CompassMiniView.java index 92280dc..260c990 100644 --- a/main/src/cgeo/geocaching/ui/CompassMiniView.java +++ b/main/src/cgeo/geocaching/ui/CompassMiniView.java @@ -1,8 +1,8 @@ package cgeo.geocaching.ui; import cgeo.geocaching.R; -import cgeo.geocaching.settings.Settings; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.AngleUtils; import android.content.Context; @@ -49,7 +49,6 @@ final public class CompassMiniView extends View { */ private static final int ARROW_BITMAP_SIZE = 21; private static final PaintFlagsDrawFilter FILTER_SET = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG); - private static final PaintFlagsDrawFilter FILTER_REMOVE = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0); private static final float MINIMUM_ROTATION_DEGREES_FOR_REPAINT = 5; public CompassMiniView(Context context) { @@ -84,6 +83,7 @@ final public class CompassMiniView extends View { @Override public void onDetachedFromWindow() { + super.onDetachedFromWindow(); instances--; if (instances == 0) { if (compassArrow != null) { @@ -144,19 +144,15 @@ final public class CompassMiniView extends View { lastDrawingAzimuth = azimuthRelative; // compass margins - canvas.setDrawFilter(FILTER_SET); - final int canvasCenterX = getWidth() / 2; final int canvasCenterY = getHeight() / 2; final int marginLeft = (getWidth() - compassArrowWidth) / 2; final int marginTop = (getHeight() - compassArrowHeight) / 2; + canvas.setDrawFilter(FILTER_SET); canvas.rotate(-azimuthRelative, canvasCenterX, canvasCenterY); canvas.drawBitmap(compassArrow, marginLeft, marginTop, null); - canvas.rotate(azimuthRelative, canvasCenterX, canvasCenterY); - - canvas.setDrawFilter(FILTER_REMOVE); } private float calculateAzimuthRelative() { diff --git a/main/src/cgeo/geocaching/ui/CompassView.java b/main/src/cgeo/geocaching/ui/CompassView.java index b73a2a9..3b4ed36 100644 --- a/main/src/cgeo/geocaching/ui/CompassView.java +++ b/main/src/cgeo/geocaching/ui/CompassView.java @@ -87,6 +87,7 @@ public class CompassView extends View implements PeriodicHandlerListener { @Override public void onDetachedFromWindow() { redrawHandler.stop(); + super.onDetachedFromWindow(); if (compassUnderlay != null) { compassUnderlay.recycle(); diff --git a/main/src/cgeo/geocaching/ui/CoordinatesFormatSwitcher.java b/main/src/cgeo/geocaching/ui/CoordinatesFormatSwitcher.java index afadb33..299256c 100644 --- a/main/src/cgeo/geocaching/ui/CoordinatesFormatSwitcher.java +++ b/main/src/cgeo/geocaching/ui/CoordinatesFormatSwitcher.java @@ -9,7 +9,7 @@ import android.widget.TextView; /** * view click listener to automatically switch different coordinate formats - * + * */ public class CoordinatesFormatSwitcher implements OnClickListener { @@ -29,6 +29,7 @@ public class CoordinatesFormatSwitcher implements OnClickListener { @Override public void onClick(View view) { + assert view instanceof TextView; position = (position + 1) % availableFormats.length; TextView textView = (TextView) view; // rotate coordinate formats on click diff --git a/main/src/cgeo/geocaching/ui/DecryptTextClickListener.java b/main/src/cgeo/geocaching/ui/DecryptTextClickListener.java index 56c7f2f..e2e587e 100644 --- a/main/src/cgeo/geocaching/ui/DecryptTextClickListener.java +++ b/main/src/cgeo/geocaching/ui/DecryptTextClickListener.java @@ -2,34 +2,36 @@ package cgeo.geocaching.ui; import cgeo.geocaching.utils.CryptUtils;
+import org.eclipse.jdt.annotation.NonNull;
+
import android.text.Spannable;
import android.view.View;
import android.widget.TextView;
public class DecryptTextClickListener implements View.OnClickListener {
- @Override
- public void onClick(View view) {
- if (view == null) {
- return;
- }
+ @NonNull private final TextView targetView;
- try {
- final TextView logView = (TextView) view;
+ public DecryptTextClickListener(@NonNull final TextView targetView) {
+ this.targetView = targetView;
+ }
+ @Override
+ public final void onClick(final View view) {
+ try {
// do not run the click listener if a link was clicked
- if (logView.getSelectionStart() != -1 || logView.getSelectionEnd() != -1) {
+ if (targetView.getSelectionStart() != -1 || targetView.getSelectionEnd() != -1) {
return;
}
- CharSequence text = logView.getText();
+ CharSequence text = targetView.getText();
if (text instanceof Spannable) {
Spannable span = (Spannable) text;
- logView.setText(CryptUtils.rot13(span));
+ targetView.setText(CryptUtils.rot13(span));
}
else {
String string = (String) text;
- logView.setText(CryptUtils.rot13(string));
+ targetView.setText(CryptUtils.rot13(string));
}
} catch (RuntimeException e) {
// nothing
diff --git a/main/src/cgeo/geocaching/ui/DirectionImage.java b/main/src/cgeo/geocaching/ui/DirectionImage.java index 9de360c..cd7695e 100644 --- a/main/src/cgeo/geocaching/ui/DirectionImage.java +++ b/main/src/cgeo/geocaching/ui/DirectionImage.java @@ -1,6 +1,6 @@ package cgeo.geocaching.ui; -import cgeo.geocaching.StoredList; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.network.HtmlImage; import org.apache.commons.lang3.StringUtils; diff --git a/main/src/cgeo/geocaching/ui/Formatter.java b/main/src/cgeo/geocaching/ui/Formatter.java index ecae9ea..49c7a50 100644 --- a/main/src/cgeo/geocaching/ui/Formatter.java +++ b/main/src/cgeo/geocaching/ui/Formatter.java @@ -1,9 +1,9 @@ package cgeo.geocaching.ui; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheListType; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.WaypointType; @@ -22,7 +22,7 @@ public abstract class Formatter { /** Text separator used for formatting texts */ public static final String SEPARATOR = " ยท "; - private static final Context context = cgeoapplication.getInstance().getBaseContext(); + private static final Context context = CgeoApplication.getInstance().getBaseContext(); /** * Generate a time string according to system-wide settings (locale, 12/24 hour) @@ -87,9 +87,9 @@ public abstract class Formatter { int diff = cgeo.geocaching.utils.DateUtils.daysSince(date); switch (diff) { case 0: - return cgeoapplication.getInstance().getString(R.string.log_today); + return CgeoApplication.getInstance().getString(R.string.log_today); case 1: - return cgeoapplication.getInstance().getString(R.string.log_yesterday); + return CgeoApplication.getInstance().getString(R.string.log_yesterday); default: return formatShortDate(date); } @@ -107,6 +107,18 @@ public abstract class Formatter { return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL); } + /** + * Generate a numeric date and time string according to system-wide settings (locale, + * date format) such as "7 september at 12:35". + * + * @param date + * milliseconds since the epoch + * @return the formatted string + */ + public static String formatDateTime(long date) { + return DateUtils.formatDateTime(context, date, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME); + } + public static String formatCacheInfoLong(Geocache cache, CacheListType cacheListType) { final ArrayList<String> infos = new ArrayList<String>(); if (StringUtils.isNotBlank(cache.getGeocode())) { @@ -116,10 +128,10 @@ public abstract class Formatter { addShortInfos(cache, infos); if (cache.isPremiumMembersOnly()) { - infos.add(cgeoapplication.getInstance().getString(R.string.cache_premium)); + infos.add(CgeoApplication.getInstance().getString(R.string.cache_premium)); } if (cacheListType != CacheListType.OFFLINE && cacheListType != CacheListType.HISTORY && cache.getListId() > 0) { - infos.add(cgeoapplication.getInstance().getString(R.string.cache_offline)); + infos.add(CgeoApplication.getInstance().getString(R.string.cache_offline)); } return StringUtils.join(infos, Formatter.SEPARATOR); } @@ -161,7 +173,7 @@ public abstract class Formatter { infos.add(waypointType.getL10n()); } if (Waypoint.PREFIX_OWN.equalsIgnoreCase(waypoint.getPrefix())) { - infos.add(cgeoapplication.getInstance().getString(R.string.waypoint_custom)); + infos.add(CgeoApplication.getInstance().getString(R.string.waypoint_custom)); } else { if (StringUtils.isNotBlank(waypoint.getPrefix())) { infos.add(waypoint.getPrefix()); diff --git a/main/src/cgeo/geocaching/ui/ImagesList.java b/main/src/cgeo/geocaching/ui/ImagesList.java index 0f860c4..4eaf06d 100644 --- a/main/src/cgeo/geocaching/ui/ImagesList.java +++ b/main/src/cgeo/geocaching/ui/ImagesList.java @@ -2,12 +2,12 @@ package cgeo.geocaching.ui; import cgeo.geocaching.Image; import cgeo.geocaching.R; -import cgeo.geocaching.StoredList; import cgeo.geocaching.files.LocalStorage; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.network.HtmlImage; -import cgeo.geocaching.utils.IOUtils; import cgeo.geocaching.utils.Log; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import android.app.Activity; @@ -156,6 +156,7 @@ public class ImagesList { } public void onCreateContextMenu(ContextMenu menu, View v) { + assert v instanceof ImageView; activity.getMenuInflater().inflate(R.menu.images_list_context, menu); final Resources res = activity.getResources(); menu.setHeaderTitle(res.getString(R.string.cache_image)); diff --git a/main/src/cgeo/geocaching/ui/LoggingUI.java b/main/src/cgeo/geocaching/ui/LoggingUI.java index c2e2d24..30c719e 100644 --- a/main/src/cgeo/geocaching/ui/LoggingUI.java +++ b/main/src/cgeo/geocaching/ui/LoggingUI.java @@ -1,9 +1,9 @@ package cgeo.geocaching.ui; +import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; -import cgeo.geocaching.cgData; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.settings.Settings; @@ -74,7 +74,7 @@ public class LoggingUI extends AbstractUIFactory { } private static void showOfflineMenu(final Geocache cache, final Activity activity) { - final LogEntry currentLog = cgData.loadLogOffline(cache.getGeocode()); + final LogEntry currentLog = DataStore.loadLogOffline(cache.getGeocode()); final LogType currentLogType = currentLog == null ? null : currentLog.type; final List<LogType> logTypes = cache.getPossibleLogTypes(); @@ -105,6 +105,8 @@ public class LoggingUI extends AbstractUIFactory { case CLEAR_LOG: cache.clearOfflineLog(); break; + default: + throw new IllegalStateException(); } } else { cache.logOffline(activity, logTypeEntry.logType); diff --git a/main/src/cgeo/geocaching/ui/UserNameClickListener.java b/main/src/cgeo/geocaching/ui/UserNameClickListener.java new file mode 100644 index 0000000..190dfde --- /dev/null +++ b/main/src/cgeo/geocaching/ui/UserNameClickListener.java @@ -0,0 +1,18 @@ +package cgeo.geocaching.ui; + +import android.view.View; + +public class UserNameClickListener extends AbstractUserClickListener { + + final private String name; + + public UserNameClickListener(final String name) { + super(true); + this.name = name; + } + + @Override + protected CharSequence getUserName(final View view) { + return name; + } +} diff --git a/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java b/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java index 91ce7e3..00d4c1a 100644 --- a/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java +++ b/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java @@ -3,13 +3,13 @@ package cgeo.geocaching.ui.dialog; 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.activity.AbstractActivity; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.settings.Settings.CoordInputFormatEnum; import org.apache.commons.lang3.StringUtils; @@ -216,6 +216,8 @@ public class CoordinatesInputDialog extends NoTitleDialog { eLonSec.setText(addZeros(gp.getLonSec(), 2)); eLonSub.setText(addZeros(gp.getLonSecFrac(), 3)); break; + default: + throw new IllegalStateException(); } } @@ -227,6 +229,7 @@ public class CoordinatesInputDialog extends NoTitleDialog { @Override public void onClick(View view) { + assert view instanceof Button; final Button button = (Button) view; final CharSequence text = button.getText(); if (StringUtils.isBlank(text)) { diff --git a/main/src/cgeo/geocaching/ui/dialog/LiveMapInfoDialogBuilder.java b/main/src/cgeo/geocaching/ui/dialog/LiveMapInfoDialogBuilder.java index 2c4f38d..6ad59ec 100644 --- a/main/src/cgeo/geocaching/ui/dialog/LiveMapInfoDialogBuilder.java +++ b/main/src/cgeo/geocaching/ui/dialog/LiveMapInfoDialogBuilder.java @@ -2,7 +2,7 @@ package cgeo.geocaching.ui.dialog; import cgeo.geocaching.R; import cgeo.geocaching.settings.Settings; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import android.app.Activity; import android.app.AlertDialog; @@ -33,7 +33,7 @@ public class LiveMapInfoDialogBuilder { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); - cgeoapplication.getInstance().setLiveMapHintShown(); + CgeoApplication.getInstance().setLiveMapHintShown(); if (checkBoxHide.getVisibility() == View.VISIBLE && checkBoxHide.isChecked()) { Settings.setHideLiveHint(true); } diff --git a/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java b/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java index 8da711e..8fe3866 100644 --- a/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java +++ b/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java @@ -4,7 +4,7 @@ import cgeo.geocaching.CacheDetailActivity; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.ui.UserActionsClickListener; @@ -23,7 +23,7 @@ import java.util.Map.Entry; public class CacheLogsViewCreator extends LogsViewCreator { private final boolean allLogs; - private final Resources res = cgeoapplication.getInstance().getResources(); + private final Resources res = CgeoApplication.getInstance().getResources(); public CacheLogsViewCreator(CacheDetailActivity cacheDetailActivity, boolean allLogs) { super(cacheDetailActivity); diff --git a/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java b/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java index ee2713a..15634d3 100644 --- a/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java +++ b/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java @@ -4,9 +4,9 @@ import cgeo.geocaching.Image; import cgeo.geocaching.ImagesActivity; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; -import cgeo.geocaching.StoredList; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.Progress; +import cgeo.geocaching.list.StoredList; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.ui.AbstractCachingPageViewCreator; import cgeo.geocaching.ui.AnchorAwareLinkMovementMethod; @@ -129,7 +129,7 @@ public abstract class LogsViewCreator extends AbstractCachingPageViewCreator<Lis if (null == convertView) { holder.author.setOnClickListener(createUserActionsListener()); holder.text.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); - holder.text.setOnClickListener(new DecryptTextClickListener()); + holder.text.setOnClickListener(new DecryptTextClickListener(holder.text)); activity.registerForContextMenu(holder.text); } } diff --git a/main/src/cgeo/geocaching/utils/ApplicationSettings.java b/main/src/cgeo/geocaching/utils/ApplicationSettings.java index 99d3142..78fa4f6 100644 --- a/main/src/cgeo/geocaching/utils/ApplicationSettings.java +++ b/main/src/cgeo/geocaching/utils/ApplicationSettings.java @@ -17,7 +17,7 @@ public class ApplicationSettings { */ public static String getPreferencesName() { // There is currently no Android API to get the file name of the shared preferences. Let's hardcode - // it without needing a cgeoapplication instance. + // it without needing a CgeoApplication instance. return "cgeo.geocaching_preferences"; } diff --git a/main/src/cgeo/geocaching/utils/CancellableHandler.java b/main/src/cgeo/geocaching/utils/CancellableHandler.java index 8cf8f28..cb4b9db 100644 --- a/main/src/cgeo/geocaching/utils/CancellableHandler.java +++ b/main/src/cgeo/geocaching/utils/CancellableHandler.java @@ -1,6 +1,6 @@ package cgeo.geocaching.utils; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import android.os.Handler; import android.os.Message; @@ -114,7 +114,7 @@ public abstract class CancellableHandler extends Handler { public static void sendLoadProgressDetail(final Handler handler, final int resourceId) { if (null != handler) { - handler.obtainMessage(UPDATE_LOAD_PROGRESS_DETAIL, cgeoapplication.getInstance().getString(resourceId)).sendToTarget(); + handler.obtainMessage(UPDATE_LOAD_PROGRESS_DETAIL, CgeoApplication.getInstance().getString(resourceId)).sendToTarget(); } } } diff --git a/main/src/cgeo/geocaching/utils/ClipboardUtils.java b/main/src/cgeo/geocaching/utils/ClipboardUtils.java index 67069b2..77250f3 100644 --- a/main/src/cgeo/geocaching/utils/ClipboardUtils.java +++ b/main/src/cgeo/geocaching/utils/ClipboardUtils.java @@ -1,6 +1,6 @@ package cgeo.geocaching.utils; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import android.content.Context; @@ -24,7 +24,7 @@ public final class ClipboardUtils { */ public static void copyToClipboard(final CharSequence text) { // fully qualified name used here to avoid buggy deprecation warning (of javac) on the import statement - final android.text.ClipboardManager clipboard = (android.text.ClipboardManager) cgeoapplication.getInstance().getSystemService(Context.CLIPBOARD_SERVICE); + final android.text.ClipboardManager clipboard = (android.text.ClipboardManager) CgeoApplication.getInstance().getSystemService(Context.CLIPBOARD_SERVICE); clipboard.setText(text); } diff --git a/main/src/cgeo/geocaching/utils/CryptUtils.java b/main/src/cgeo/geocaching/utils/CryptUtils.java index d98585a..5273fa5 100644 --- a/main/src/cgeo/geocaching/utils/CryptUtils.java +++ b/main/src/cgeo/geocaching/utils/CryptUtils.java @@ -95,20 +95,6 @@ public final class CryptUtils { return StringUtils.EMPTY; } - public static String sha1(String text) { - try { - final MessageDigest digest = MessageDigest.getInstance("SHA-1"); - digest.update(text.getBytes(CharEncoding.UTF_8), 0, text.length()); - return new BigInteger(1, digest.digest()).toString(16); - } catch (NoSuchAlgorithmException e) { - Log.e("CryptUtils.sha1", e); - } catch (UnsupportedEncodingException e) { - Log.e("CryptUtils.sha1", e); - } - - return StringUtils.EMPTY; - } - public static byte[] hashHmac(String text, String salt) { byte[] macBytes = {}; diff --git a/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java b/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java index 8aa605f..b291a8a 100644 --- a/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java +++ b/main/src/cgeo/geocaching/utils/DatabaseBackupUtils.java @@ -1,8 +1,8 @@ package cgeo.geocaching.utils; +import cgeo.geocaching.DataStore; import cgeo.geocaching.MainActivity; import cgeo.geocaching.R; -import cgeo.geocaching.cgData; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.ui.Formatter; @@ -12,8 +12,6 @@ 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; @@ -34,34 +32,31 @@ public class DatabaseBackupUtils { 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(); - } - } - }; - + new Thread() { @Override public void run() { - restoreSuccessful.set(cgData.restoreDatabaseInternal()); - handler.sendMessage(handler.obtainMessage()); + restoreSuccessful.set(DataStore.restoreDatabaseInternal()); + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + 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(); + } + } + }); } - }; - restoreThread.start(); + }.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) { + if (DataStore.getAllCachesCount() == 0) { ActivityMixin.helpDialog(activity, context.getString(R.string.init_backup), context.getString(R.string.init_backup_unnecessary)); @@ -74,7 +69,7 @@ public class DatabaseBackupUtils { new Thread() { @Override public void run() { - final String backupFileName = cgData.backupDatabaseInternal(); + final String backupFileName = DataStore.backupDatabaseInternal(); activity.runOnUiThread(new Runnable() { @Override public void run() { @@ -96,7 +91,7 @@ public class DatabaseBackupUtils { } public static File getRestoreFile() { - final File fileSourceFile = cgData.getBackupFileInternal(); + final File fileSourceFile = DataStore.getBackupFileInternal(); return fileSourceFile.exists() && fileSourceFile.length() > 0 ? fileSourceFile : null; } diff --git a/main/src/cgeo/geocaching/utils/GeoDirHandler.java b/main/src/cgeo/geocaching/utils/GeoDirHandler.java index a143730..c85648b 100644 --- a/main/src/cgeo/geocaching/utils/GeoDirHandler.java +++ b/main/src/cgeo/geocaching/utils/GeoDirHandler.java @@ -1,7 +1,7 @@ package cgeo.geocaching.utils; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.IGeoData; -import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.settings.Settings; import android.os.Handler; @@ -29,7 +29,7 @@ public abstract class GeoDirHandler extends Handler implements IObserver<Object> private static final int STOP_GEO = 1 << 4; private static final int STOP_DIR = 1 << 5; - private static final cgeoapplication app = cgeoapplication.getInstance(); + private static final CgeoApplication app = CgeoApplication.getInstance(); @Override final public void handleMessage(final Message message) { diff --git a/main/src/cgeo/geocaching/utils/HtmlUtils.java b/main/src/cgeo/geocaching/utils/HtmlUtils.java index 5717a37..37e20ec 100644 --- a/main/src/cgeo/geocaching/utils/HtmlUtils.java +++ b/main/src/cgeo/geocaching/utils/HtmlUtils.java @@ -3,6 +3,7 @@ package cgeo.geocaching.utils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import android.text.Html; import android.text.Spanned; import android.text.style.ImageSpan; @@ -56,7 +57,7 @@ public final class HtmlUtils { } } - // some line breaks are still in the text, source is unknown - return StringUtils.replace(result, "<br />", "\n").trim(); + // now that images are gone, do a normal html to text conversion + return Html.fromHtml(result).toString().trim(); } } diff --git a/main/src/cgeo/geocaching/utils/IOUtils.java b/main/src/cgeo/geocaching/utils/IOUtils.java deleted file mode 100644 index df90da3..0000000 --- a/main/src/cgeo/geocaching/utils/IOUtils.java +++ /dev/null @@ -1,22 +0,0 @@ -package cgeo.geocaching.utils; - -import java.io.Closeable; -import java.io.IOException; - -final public class IOUtils { - - private IOUtils() { - // utility class - } - - public static void closeQuietly(final Closeable closeable) { - if (closeable != null) { - try { - closeable.close(); - } catch (final IOException e) { - Log.w("closeQuietly: unable to close " + closeable, e); - } - } - } - -} diff --git a/main/src/cgeo/geocaching/utils/ImageUtils.java b/main/src/cgeo/geocaching/utils/ImageUtils.java index 34bfa1c..ea4498b 100644 --- a/main/src/cgeo/geocaching/utils/ImageUtils.java +++ b/main/src/cgeo/geocaching/utils/ImageUtils.java @@ -1,8 +1,11 @@ package cgeo.geocaching.utils; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.compatibility.Compatibility; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Point; @@ -46,7 +49,7 @@ public final class ImageUtils { * @return BitmapDrawable The scaled image */ public static BitmapDrawable scaleBitmapTo(final Bitmap image, final int maxWidth, final int maxHeight) { - final cgeoapplication app = cgeoapplication.getInstance(); + final CgeoApplication app = CgeoApplication.getInstance(); Bitmap result = image; int width = image.getWidth(); int height = image.getHeight(); @@ -94,9 +97,10 @@ public final class ImageUtils { * Image to read * @param maxXY * boundings - * @return String filename and path, NULL if something fails + * @return filename and path, <tt>null</tt> if something fails */ - public static String readScaleAndWriteImage(final String filePath, final int maxXY) { + @Nullable + public static String readScaleAndWriteImage(@NonNull final String filePath, final int maxXY) { if (maxXY <= 0) { return filePath; } @@ -117,12 +121,22 @@ public final class ImageUtils { return null; } final BitmapDrawable scaledImage = scaleBitmapTo(image, maxXY, maxXY); - final String uploadFilename = ImageUtils.getOutputImageFile().getPath(); + final File tempImageFile = ImageUtils.getOutputImageFile(); + if (tempImageFile == null) { + Log.e("ImageUtils.readScaleAndWriteImage: unable to write scaled image"); + return null; + } + final String uploadFilename = tempImageFile.getPath(); storeBitmap(scaledImage.getBitmap(), Bitmap.CompressFormat.JPEG, 75, uploadFilename); return uploadFilename; } - /** Create a File for saving an image or video */ + /** Create a File for saving an image or video + * + * @return the temporary image file to use, or <tt>null</tt> if the media directory could + * not be created. + * */ + @Nullable public static File getOutputImageFile() { // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. @@ -134,6 +148,7 @@ public final class ImageUtils { // Create the storage directory if it does not exist if (!mediaStorageDir.exists()) { if (!FileUtils.mkdirs(mediaStorageDir)) { + Log.e("ImageUtils.getOutputImageFile: cannot create media storage directory"); return null; } } @@ -143,6 +158,7 @@ public final class ImageUtils { return new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg"); } + @Nullable public static Uri getOutputImageFileUri() { final File file = getOutputImageFile(); if (file == null) { diff --git a/main/src/cgeo/geocaching/utils/Log.java b/main/src/cgeo/geocaching/utils/Log.java index 1ade2f9..8f96f10 100644 --- a/main/src/cgeo/geocaching/utils/Log.java +++ b/main/src/cgeo/geocaching/utils/Log.java @@ -1,5 +1,6 @@ package cgeo.geocaching.utils; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.CharEncoding; import android.os.Environment; diff --git a/main/src/cgeo/geocaching/utils/LogTemplateProvider.java b/main/src/cgeo/geocaching/utils/LogTemplateProvider.java index 98201b5..76fa0f7 100644 --- a/main/src/cgeo/geocaching/utils/LogTemplateProvider.java +++ b/main/src/cgeo/geocaching/utils/LogTemplateProvider.java @@ -1,6 +1,7 @@ package cgeo.geocaching.utils; import cgeo.geocaching.Geocache; +import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.Trackable; import cgeo.geocaching.connector.ConnectorFactory; @@ -35,22 +36,25 @@ public final class LogTemplateProvider { private Geocache cache; private Trackable trackable; private boolean offline = false; + private LogEntry logEntry; - public LogContext(final Geocache cache) { - this(cache, false); + public LogContext(final Geocache cache, LogEntry logEntry) { + this(cache, logEntry, false); } - public LogContext(final Trackable trackable) { + public LogContext(final Trackable trackable, final LogEntry logEntry) { this.trackable = trackable; + this.logEntry = logEntry; } public LogContext(final boolean offline) { - this(null, offline); + this(null, null, offline); } - public LogContext(final Geocache cache, final boolean offline) { + public LogContext(final Geocache cache, LogEntry logEntry, final boolean offline) { this.cache = cache; this.offline = offline; + this.logEntry = logEntry; } public final Geocache getCache() { @@ -64,6 +68,10 @@ public final class LogTemplateProvider { public final boolean isOffline() { return offline; } + + public final LogEntry getLogEntry() { + return logEntry; + } } public abstract static class LogTemplate { @@ -178,6 +186,45 @@ public final class LogTemplateProvider { return StringUtils.EMPTY; } }); + templates.add(new LogTemplate("NAME", R.string.init_signature_template_name) { + @Override + public String getValue(LogContext context) { + Trackable trackable = context.getTrackable(); + if (trackable != null) { + return trackable.getName(); + } + Geocache cache = context.getCache(); + if (cache != null) { + return cache.getName(); + } + return StringUtils.EMPTY; + } + }); + templates.add(new LogTemplate("URL", R.string.init_signature_template_url) { + + @Override + public String getValue(LogContext context) { + Trackable trackable = context.getTrackable(); + if (trackable != null) { + return trackable.getUrl(); + } + Geocache cache = context.getCache(); + if (cache != null) { + return cache.getUrl(); + } + return StringUtils.EMPTY; + } + }); + templates.add(new LogTemplate("LOG", R.string.init_signature_template_log) { + @Override + public String getValue(LogContext context) { + LogEntry logEntry = context.getLogEntry(); + if (logEntry != null) { + return logEntry.getDisplayText(); + } + return StringUtils.EMPTY; + } + }); return templates; } diff --git a/main/src/cgeo/geocaching/utils/ProcessUtils.java b/main/src/cgeo/geocaching/utils/ProcessUtils.java index 3345ff1..d80674b 100644 --- a/main/src/cgeo/geocaching/utils/ProcessUtils.java +++ b/main/src/cgeo/geocaching/utils/ProcessUtils.java @@ -1,13 +1,14 @@ package cgeo.geocaching.utils; -import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.CgeoApplication; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.net.Uri; import java.util.List; @@ -43,7 +44,7 @@ public final class ProcessUtils { * This will find installed applications even without launch intent (e.g. the streetview plugin). */ private static boolean hasPackageInstalled(final String packageName) { - final List<PackageInfo> packs = cgeoapplication.getInstance().getPackageManager().getInstalledPackages(0); + final List<PackageInfo> packs = CgeoApplication.getInstance().getPackageManager().getInstalledPackages(0); for (final PackageInfo packageInfo : packs) { if (packageName.equals(packageInfo.packageName)) { return true; @@ -59,7 +60,7 @@ public final class ProcessUtils { if (packageName == null) { return null; } - final PackageManager packageManager = cgeoapplication.getInstance().getPackageManager(); + final PackageManager packageManager = CgeoApplication.getInstance().getPackageManager(); try { // This can throw an exception where the exception type is only defined on API Level > 3 // therefore surround with try-catch @@ -70,10 +71,34 @@ public final class ProcessUtils { } public static boolean isIntentAvailable(final String intent) { - final PackageManager packageManager = cgeoapplication.getInstance().getPackageManager(); - final List<ResolveInfo> list = packageManager.queryIntentActivities( - new Intent(intent), PackageManager.MATCH_DEFAULT_ONLY); + return isIntentAvailable(intent, null); + } + /** + * Indicates whether the specified action can be used as an intent. This + * method queries the package manager for installed packages that can + * respond to an intent with the specified action. If no suitable package is + * found, this method returns false. + * + * @param action + * The Intent action to check for availability. + * @param uri + * The Intent URI to check for availability. + * + * @return True if an Intent with the specified action can be sent and + * responded to, false otherwise. + */ + public static boolean isIntentAvailable(final String action, final Uri uri) { + final PackageManager packageManager = CgeoApplication.getInstance().getPackageManager(); + final Intent intent; + if (uri == null) { + intent = new Intent(action); + } else { + intent = new Intent(action, uri); + } + final List<ResolveInfo> list = packageManager.queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY); return CollectionUtils.isNotEmpty(list); } + } diff --git a/main/src/cgeo/geocaching/utils/SynchronizedDateFormat.java b/main/src/cgeo/geocaching/utils/SynchronizedDateFormat.java new file mode 100644 index 0000000..2368469 --- /dev/null +++ b/main/src/cgeo/geocaching/utils/SynchronizedDateFormat.java @@ -0,0 +1,18 @@ +package cgeo.geocaching.utils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public class SynchronizedDateFormat { + private final SimpleDateFormat format; + + public SynchronizedDateFormat(final String pattern, final Locale locale) { + format = new SimpleDateFormat(pattern, locale); + } + + public synchronized Date parse(final String input) throws ParseException { + return format.parse(input); + } +} diff --git a/main/src/cgeo/geocaching/utils/TextUtils.java b/main/src/cgeo/geocaching/utils/TextUtils.java index c9d4958..302a65d 100644 --- a/main/src/cgeo/geocaching/utils/TextUtils.java +++ b/main/src/cgeo/geocaching/utils/TextUtils.java @@ -3,6 +3,8 @@ */ package cgeo.geocaching.utils; +import org.eclipse.jdt.annotation.Nullable; + import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -34,7 +36,7 @@ public final class TextUtils { * Find the last occurring value * @return defaultValue or the n-th group if the pattern matches (trimmed if wanted) */ - public static String getMatch(final String data, final Pattern p, final boolean trim, final int group, final String defaultValue, final boolean last) { + public static String getMatch(@Nullable final String data, final Pattern p, final boolean trim, final int group, final String defaultValue, final boolean last) { if (data != null) { String result = null; @@ -87,7 +89,7 @@ public final class TextUtils { * Value to return if the pattern is not found * @return defaultValue or the first group if the pattern matches (trimmed) */ - public static String getMatch(final String data, final Pattern p, final String defaultValue) { + public static String getMatch(@Nullable final String data, final Pattern p, final String defaultValue) { return TextUtils.getMatch(data, p, true, 1, defaultValue, false); } diff --git a/main/src/cgeo/geocaching/utils/TranslationUtils.java b/main/src/cgeo/geocaching/utils/TranslationUtils.java index 1224f7e..619db08 100644 --- a/main/src/cgeo/geocaching/utils/TranslationUtils.java +++ b/main/src/cgeo/geocaching/utils/TranslationUtils.java @@ -3,6 +3,8 @@ package cgeo.geocaching.utils; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.network.Network; +import org.apache.commons.lang3.StringUtils; + import android.content.Intent; import android.net.Uri; @@ -11,12 +13,12 @@ import android.net.Uri; */ public final class TranslationUtils { - private static final String translationWebsite = "http://translate.google.com/"; - private static final String translationForceClassicMode = "?vi=c"; - private static final String translationAutoSelect = "#auto"; - private static final String translationFieldSeparator = "|"; + private static final String TRANSLATION_WEBSITE = "http://translate.google.com/"; + private static final String TRANSLATION_FORCE_CLASSIC_MODE = "?vi=c"; + private static final String TRANSLATION_AUTO_SELECT = "#auto"; + private static final String TRANSLATION_FIELD_SEPARATOR = "|"; - public static final int translationTextLengthToWarn = 500; + public static final int TRANSLATION_TEXT_LENGTH_WARN = 500; private static final String TRANSLATION_APP = "com.google.android.apps.translate"; private TranslationUtils() { @@ -24,8 +26,8 @@ public final class TranslationUtils { } /** - * Build a URI for Google Translate - * + * Build a URI for Google Translate. + * * @param toLang * The two-letter lowercase ISO language codes as defined by ISO 639-1 * @param text @@ -36,13 +38,13 @@ public final class TranslationUtils { String content = Network.encode(text); // the app works better without the "+", the website works better with "+", therefore assume using the app if installed if (ProcessUtils.isLaunchable(TRANSLATION_APP)) { - content = content.replace("+", "%20"); + content = StringUtils.replace(content, "+", "%20"); } - return translationWebsite + translationForceClassicMode + translationAutoSelect + translationFieldSeparator + toLang + translationFieldSeparator + content; + return TRANSLATION_WEBSITE + TRANSLATION_FORCE_CLASSIC_MODE + TRANSLATION_AUTO_SELECT + TRANSLATION_FIELD_SEPARATOR + toLang + TRANSLATION_FIELD_SEPARATOR + content; } /** - * Send Intent for Google Translate. Can be caught by Google Translate App or browser + * Send Intent for Google Translate. Can be caught by Google Translate App or browser. * * @param toLang * The two-letter lowercase ISO language codes as defined by ISO 639-1 |
