diff options
Diffstat (limited to 'main/src/cgeo/geocaching')
77 files changed, 2128 insertions, 1469 deletions
diff --git a/main/src/cgeo/geocaching/AbstractLoggingActivity.java b/main/src/cgeo/geocaching/AbstractLoggingActivity.java index 78da757..1524834 100644 --- a/main/src/cgeo/geocaching/AbstractLoggingActivity.java +++ b/main/src/cgeo/geocaching/AbstractLoggingActivity.java @@ -5,6 +5,7 @@ import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.connector.gc.GCSmiliesProvider; import cgeo.geocaching.connector.gc.GCSmiliesProvider.Smiley; +import cgeo.geocaching.connector.trackable.TravelBugConnector; import cgeo.geocaching.utils.LogTemplateProvider; import cgeo.geocaching.utils.LogTemplateProvider.LogContext; import cgeo.geocaching.utils.LogTemplateProvider.LogTemplate; @@ -23,12 +24,12 @@ public abstract class AbstractLoggingActivity extends AbstractActivity { getMenuInflater().inflate(R.menu.abstract_logging_activity, menu); final SubMenu menuLog = menu.findItem(R.id.menu_templates).getSubMenu(); - for (LogTemplate template : LogTemplateProvider.getTemplates()) { + for (final LogTemplate template : LogTemplateProvider.getTemplates()) { menuLog.add(0, template.getItemId(), 0, template.getResourceId()); } final SubMenu menuSmilies = menu.findItem(R.id.menu_smilies).getSubMenu(); - for (Smiley smiley : GCSmiliesProvider.getSmilies()) { + for (final Smiley smiley : GCSmiliesProvider.getSmilies()) { menuSmilies.add(0, smiley.getItemId(), 0, smiley.text); } @@ -46,7 +47,7 @@ public abstract class AbstractLoggingActivity extends AbstractActivity { smileyVisible = true; } final Trackable trackable = getLogContext().getTrackable(); - if (trackable != null && ConnectorFactory.getConnector(trackable).equals(GCConnector.getInstance())) { + if (trackable != null && ConnectorFactory.getConnector(trackable).equals(TravelBugConnector.getInstance())) { smileyVisible = true; } diff --git a/main/src/cgeo/geocaching/AbstractPopupActivity.java b/main/src/cgeo/geocaching/AbstractPopupActivity.java index 73dc86d..64e5539 100644 --- a/main/src/cgeo/geocaching/AbstractPopupActivity.java +++ b/main/src/cgeo/geocaching/AbstractPopupActivity.java @@ -2,6 +2,7 @@ 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; @@ -46,7 +47,7 @@ public abstract class AbstractPopupActivity extends AbstractActivity { public void handleMessage(Message msg) { try { details.addRating(cache); - } catch (Exception e) { + } catch (final Exception e) { // nothing } } @@ -62,7 +63,7 @@ public abstract class AbstractPopupActivity extends AbstractActivity { cacheDistance.bringToFront(); } onUpdateGeoData(geo); - } catch (Exception e) { + } catch (final Exception e) { Log.w("Failed to UpdateLocation location."); } } @@ -205,8 +206,9 @@ public abstract class AbstractPopupActivity extends AbstractActivity { 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()); LoggingUI.onPrepareOptionsMenu(menu, cache); - } catch (Exception e) { + } catch (final Exception e) { // nothing } diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java index 7acb4ae..6ec034d 100644 --- a/main/src/cgeo/geocaching/CacheDetailActivity.java +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -14,7 +14,6 @@ import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.enumerations.CacheAttribute; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; -import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.geopoint.Units; @@ -29,19 +28,22 @@ import cgeo.geocaching.ui.DecryptTextClickListener; import cgeo.geocaching.ui.EditNoteDialog; import cgeo.geocaching.ui.EditNoteDialog.EditNoteDialogListener; import cgeo.geocaching.ui.Formatter; +import cgeo.geocaching.ui.HtmlImageCounter; import cgeo.geocaching.ui.ImagesList; import cgeo.geocaching.ui.LoggingUI; +import cgeo.geocaching.ui.OwnerActionsClickListener; import cgeo.geocaching.ui.WeakReferenceHandler; -import cgeo.geocaching.utils.BaseUtils; +import cgeo.geocaching.ui.logs.CacheLogsViewCreator; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.ClipboardUtils; import cgeo.geocaching.utils.CryptUtils; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.HtmlUtils; -import cgeo.geocaching.utils.ImageHelper; +import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; import cgeo.geocaching.utils.RunnableWithArgument; +import cgeo.geocaching.utils.TextUtils; import cgeo.geocaching.utils.TranslationUtils; import cgeo.geocaching.utils.UnknownTagsHandler; @@ -103,12 +105,9 @@ import android.widget.TextView.BufferType; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; import java.util.regex.Pattern; /** @@ -175,7 +174,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc cacheDistanceView.setText(dist.toString()); cacheDistanceView.bringToFront(); - } catch (Exception e) { + } catch (final Exception e) { Log.w("Failed to update location."); } } @@ -192,7 +191,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // some views that must be available from everywhere // TODO: Reference can block GC? private TextView cacheDistanceView; - private Handler cacheChangeNotificationHandler = new Handler() { + private final Handler cacheChangeNotificationHandler = new Handler() { @Override public void handleMessage(Message msg) { notifyDataSetChanged(); @@ -232,9 +231,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // try to get data from URI if (geocode == null && guid == null && uri != null) { - String uriHost = uri.getHost().toLowerCase(Locale.US); - String uriPath = uri.getPath().toLowerCase(Locale.US); - String uriQuery = uri.getQuery(); + final String uriHost = uri.getHost().toLowerCase(Locale.US); + final String uriPath = uri.getPath().toLowerCase(Locale.US); + final String uriQuery = uri.getQuery(); if (uriQuery != null) { Log.i("Opening URI: " + uriHost + uriPath + "?" + uriQuery); @@ -302,11 +301,11 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc title = geocode; } progress.show(this, title, res.getString(R.string.cache_dialog_loading_details), true, loadCacheHandler.cancelMessage()); - } catch (Exception e) { + } catch (final Exception e) { // nothing, we lost the window } - ImageView defaultNavigationImageView = (ImageView) findViewById(R.id.defaultNavigation); + final ImageView defaultNavigationImageView = (ImageView) findViewById(R.id.defaultNavigation); defaultNavigationImageView.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { @@ -375,7 +374,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc switch (viewId) { case R.id.value: // coordinates, gc-code, name clickedItemText = ((TextView) view).getText(); - String itemTitle = (String) ((TextView) ((View) view.getParent()).findViewById(R.id.name)).getText(); + final String itemTitle = (String) ((TextView) ((View) view.getParent()).findViewById(R.id.name)).getText(); buildOptionsContextmenu(menu, viewId, itemTitle, true); break; case R.id.shortdesc: @@ -384,7 +383,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc break; case R.id.longdesc: // combine short and long description - String shortDesc = cache.getShortDescription(); + final String shortDesc = cache.getShortDescription(); if (StringUtils.isBlank(shortDesc)) { clickedItemText = ((TextView) view).getText(); } else { @@ -433,7 +432,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc break; } } - } catch (Exception e) { + } catch (final Exception e) { } } break; @@ -579,8 +578,8 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc 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); } - LoggingUI.onPrepareOptionsMenu(menu, cache); return super.onPrepareOptionsMenu(menu); } @@ -788,7 +787,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc .setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { - Intent intent = new Intent(Intent.ACTION_VIEW); + final Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(ICalendar.CALENDAR_ADDON_URI)); startActivity(intent); } @@ -832,95 +831,15 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc NavigationAppFactory.showNavigationMenu(this, cache, null, null, true, true); } - /** - * Listener for clicks on username - */ - private class UserActionsClickListener implements View.OnClickListener { - - @Override - public void onClick(View view) { - if (view == null) { - return; - } - if (!cache.supportsUserActions()) { - return; - } - - clickedItemText = ((TextView) view).getText().toString(); - showUserActionsDialog(clickedItemText); - } - } - - /** - * Listener for clicks on owner name - */ - private class OwnerActionsClickListener implements View.OnClickListener { - - @Override - public void onClick(View view) { - if (view == null) { - return; - } - if (!cache.supportsUserActions()) { - return; - } - - // Use real owner name vice the one owner chose to display - if (StringUtils.isNotBlank(cache.getOwnerUserId())) { - clickedItemText = cache.getOwnerUserId(); - } else { - clickedItemText = ((TextView) view).getText().toString(); - } - showUserActionsDialog(clickedItemText); - } - } - - /** - * Opens a dialog to do actions on an username - */ - private void showUserActionsDialog(final CharSequence name) { - final CharSequence[] items = { res.getString(R.string.user_menu_view_hidden), - res.getString(R.string.user_menu_view_found), - res.getString(R.string.user_menu_open_browser), - res.getString(R.string.user_menu_send_message) - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(res.getString(R.string.user_menu_title) + " " + name); - builder.setItems(items, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int item) { - switch (item) { - case 0: - cgeocaches.startActivityOwner(CacheDetailActivity.this, name.toString()); - return; - case 1: - cgeocaches.startActivityUserName(CacheDetailActivity.this, name.toString()); - return; - case 2: - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/profile/?u=" + Network.encode(name.toString())))); - return; - case 3: - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/email/?u=" + Network.encode(name.toString())))); - return; - default: - break; - } - } - }); - AlertDialog alert = builder.create(); - alert.show(); - } - private void loadCacheImages() { if (imagesList != null) { return; } - PageViewCreator creator = getViewCreator(Page.IMAGES); + final PageViewCreator creator = getViewCreator(Page.IMAGES); if (creator == null) { return; } - View imageView = creator.getView(); + final View imageView = creator.getView(); if (imageView == null) { return; } @@ -1062,12 +981,12 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc noAttributeIconsFound = true; - for (String attributeName : cache.getAttributes()) { + for (final String attributeName : cache.getAttributes()) { // check if another attribute icon fits in this row attributeRow.measure(0, 0); - int rowWidth = attributeRow.getMeasuredWidth(); - FrameLayout fl = (FrameLayout) getLayoutInflater().inflate(R.layout.attribute_image, null); - ImageView iv = (ImageView) fl.getChildAt(0); + final int rowWidth = attributeRow.getMeasuredWidth(); + final FrameLayout fl = (FrameLayout) getLayoutInflater().inflate(R.layout.attribute_image, null); + final ImageView iv = (ImageView) fl.getChildAt(0); if ((parentWidth - rowWidth) < iv.getLayoutParams().width) { // make a new row attributeRow = newAttributeIconsRow(); @@ -1083,14 +1002,14 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // strike through? if (strikethru) { // generate strikethru image with same properties as attribute image - ImageView strikethruImage = new ImageView(CacheDetailActivity.this); + final ImageView strikethruImage = new ImageView(CacheDetailActivity.this); strikethruImage.setLayoutParams(iv.getLayoutParams()); d = res.getDrawable(R.drawable.attribute__strikethru); strikethruImage.setImageDrawable(d); fl.addView(strikethruImage); } } else { - Drawable d = res.getDrawable(R.drawable.attribute_unknown); + final Drawable d = res.getDrawable(R.drawable.attribute_unknown); iv.setImageDrawable(d); } @@ -1101,7 +1020,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } private LinearLayout newAttributeIconsRow() { - LinearLayout rowLayout = new LinearLayout(CacheDetailActivity.this); + final LinearLayout rowLayout = new LinearLayout(CacheDetailActivity.this); rowLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); rowLayout.setOrientation(LinearLayout.HORIZONTAL); @@ -1166,7 +1085,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc final CacheDetailsCreator details = new CacheDetailsCreator(CacheDetailActivity.this, detailsList); // cache name (full name) - Spannable span = (new Spannable.Factory()).newSpannable(Html.fromHtml(cache.getName()).toString()); + final Spannable span = (new Spannable.Factory()).newSpannable(Html.fromHtml(cache.getName()).toString()); if (cache.isDisabled() || cache.isArchived()) { // strike span.setSpan(new StrikethroughSpan(), 0, span.toString().length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } @@ -1199,18 +1118,18 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // cache author if (StringUtils.isNotBlank(cache.getOwnerDisplayName()) || StringUtils.isNotBlank(cache.getOwnerUserId())) { - TextView ownerView = details.add(R.string.cache_owner, ""); + final TextView ownerView = details.add(R.string.cache_owner, ""); if (StringUtils.isNotBlank(cache.getOwnerDisplayName())) { ownerView.setText(cache.getOwnerDisplayName(), TextView.BufferType.SPANNABLE); } else { // OwnerReal guaranteed to be not blank based on above ownerView.setText(cache.getOwnerUserId(), TextView.BufferType.SPANNABLE); } - ownerView.setOnClickListener(new OwnerActionsClickListener()); + ownerView.setOnClickListener(new OwnerActionsClickListener(cache)); } // cache hidden if (cache.getHiddenDate() != null) { - long time = cache.getHiddenDate().getTime(); + final long time = cache.getHiddenDate().getTime(); if (time > 0) { String dateString = Formatter.formatFullDate(time); if (cache.isEventCache()) { @@ -1227,7 +1146,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // cache coordinates if (cache.getCoords() != null) { - TextView valueView = details.add(R.string.cache_coordinates, cache.getCoords().toString()); + final TextView valueView = details.add(R.string.cache_coordinates, cache.getCoords().toString()); valueView.setOnClickListener(new CoordinatesFormatSwitcher(cache.getCoords())); registerForContextMenu(valueView); } @@ -1241,31 +1160,31 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc updateOfflineBox(view, cache, res, new RefreshCacheClickListener(), new DropCacheClickListener(), new StoreCacheClickListener()); // watchlist - Button buttonWatchlistAdd = (Button) view.findViewById(R.id.add_to_watchlist); - Button buttonWatchlistRemove = (Button) view.findViewById(R.id.remove_from_watchlist); + final Button buttonWatchlistAdd = (Button) view.findViewById(R.id.add_to_watchlist); + final Button buttonWatchlistRemove = (Button) view.findViewById(R.id.remove_from_watchlist); buttonWatchlistAdd.setOnClickListener(new AddToWatchlistClickListener()); buttonWatchlistRemove.setOnClickListener(new RemoveFromWatchlistClickListener()); updateWatchlistBox(); // favorite points - Button buttonFavPointAdd = (Button) view.findViewById(R.id.add_to_favpoint); - Button buttonFavPointRemove = (Button) view.findViewById(R.id.remove_from_favpoint); + final Button buttonFavPointAdd = (Button) view.findViewById(R.id.add_to_favpoint); + final Button buttonFavPointRemove = (Button) view.findViewById(R.id.remove_from_favpoint); buttonFavPointAdd.setOnClickListener(new FavoriteAddClickListener()); buttonFavPointRemove.setOnClickListener(new FavoriteRemoveClickListener()); updateFavPointBox(); // list - Button buttonChangeList = (Button) view.findViewById(R.id.change_list); + final Button buttonChangeList = (Button) view.findViewById(R.id.change_list); buttonChangeList.setOnClickListener(new ChangeListClickListener()); updateListBox(); // data license - IConnector connector = ConnectorFactory.getConnector(cache); + final IConnector connector = ConnectorFactory.getConnector(cache); if (connector != null) { - String license = connector.getLicenseText(cache); + final String license = connector.getLicenseText(cache); if (StringUtils.isNotBlank(license)) { view.findViewById(R.id.license_box).setVisibility(View.VISIBLE); - TextView licenseView = ((TextView) view.findViewById(R.id.license)); + final TextView licenseView = ((TextView) view.findViewById(R.id.license)); licenseView.setText(Html.fromHtml(license), BufferType.SPANNABLE); licenseView.setClickable(true); licenseView.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); @@ -1613,15 +1532,15 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc * shows/hides buttons, sets text in watchlist box */ private void updateWatchlistBox() { - LinearLayout layout = (LinearLayout) view.findViewById(R.id.watchlist_box); - boolean supportsWatchList = cache.supportsWatchList(); + final LinearLayout layout = (LinearLayout) view.findViewById(R.id.watchlist_box); + final boolean supportsWatchList = cache.supportsWatchList(); layout.setVisibility(supportsWatchList ? View.VISIBLE : View.GONE); if (!supportsWatchList) { return; } - Button buttonAdd = (Button) view.findViewById(R.id.add_to_watchlist); - Button buttonRemove = (Button) view.findViewById(R.id.remove_from_watchlist); - TextView text = (TextView) view.findViewById(R.id.watchlist_text); + final Button buttonAdd = (Button) view.findViewById(R.id.add_to_watchlist); + final Button buttonRemove = (Button) view.findViewById(R.id.remove_from_watchlist); + final TextView text = (TextView) view.findViewById(R.id.watchlist_text); if (cache.isOnWatchlist() || cache.isOwner()) { buttonAdd.setVisibility(View.GONE); @@ -1647,15 +1566,15 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc * shows/hides buttons, sets text in watchlist box */ private void updateFavPointBox() { - LinearLayout layout = (LinearLayout) view.findViewById(R.id.favpoint_box); - boolean supportsFavoritePoints = cache.supportsFavoritePoints(); + final LinearLayout layout = (LinearLayout) view.findViewById(R.id.favpoint_box); + final boolean supportsFavoritePoints = cache.supportsFavoritePoints(); layout.setVisibility(supportsFavoritePoints ? View.VISIBLE : View.GONE); if (!supportsFavoritePoints || cache.isOwner() || !Settings.isPremiumMember()) { return; } - Button buttonAdd = (Button) view.findViewById(R.id.add_to_favpoint); - Button buttonRemove = (Button) view.findViewById(R.id.remove_from_favpoint); - TextView text = (TextView) view.findViewById(R.id.favpoint_text); + final Button buttonAdd = (Button) view.findViewById(R.id.add_to_favpoint); + final Button buttonRemove = (Button) view.findViewById(R.id.remove_from_favpoint); + final TextView text = (TextView) view.findViewById(R.id.favpoint_text); if (cache.isFavorite()) { buttonAdd.setVisibility(View.GONE); @@ -1680,15 +1599,15 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc * shows/hides/updates list box */ private void updateListBox() { - View box = view.findViewById(R.id.list_box); + final View box = view.findViewById(R.id.list_box); if (cache.isOffline()) { // show box box.setVisibility(View.VISIBLE); // update text - TextView text = (TextView) view.findViewById(R.id.list_text); - StoredList list = cgData.getList(cache.getListId()); + final TextView text = (TextView) view.findViewById(R.id.list_text); + final StoredList list = cgData.getList(cache.getListId()); if (list != null) { text.setText(res.getString(R.string.cache_list_text) + " " + list.title); } else { @@ -1732,8 +1651,8 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } } - return ImageHelper.scaleBitmapToFitDisplay(image); - } catch (Exception e) { + return ImageUtils.scaleBitmapToFitDisplay(image); + } catch (final Exception e) { Log.w("CacheDetailActivity.PreviewMapTask", e); return null; } @@ -1757,12 +1676,11 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc ((ImageView) view.findViewById(R.id.map_preview)).setImageDrawable(image); view.findViewById(R.id.map_preview_box).setVisibility(View.VISIBLE); - } catch (Exception e) { + } catch (final Exception e) { Log.e("CacheDetailActivity.PreviewMapTask", e); } } } - } protected class DescriptionViewCreator extends AbstractCachingPageViewCreator<ScrollView> { @@ -1789,7 +1707,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (Settings.isAutoLoadDescription()) { loadLongDescription(); } else { - Button showDesc = (Button) view.findViewById(R.id.show_description); + final Button showDesc = (Button) view.findViewById(R.id.show_description); showDesc.setVisibility(View.VISIBLE); showDesc.setOnClickListener(new View.OnClickListener() { @Override @@ -1838,7 +1756,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc final TextView hintView = ((TextView) view.findViewById(R.id.hint)); if (StringUtils.isNotBlank(cache.getHint())) { - if (BaseUtils.containsHtml(cache.getHint())) { + if (TextUtils.containsHtml(cache.getHint())) { hintView.setText(Html.fromHtml(cache.getHint(), new HtmlImage(cache.getGeocode(), false, cache.getListId(), false), null), TextView.BufferType.SPANNABLE); hintView.setText(CryptUtils.rot13((Spannable) hintView.getText())); } @@ -1923,7 +1841,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } private void loadLongDescription() { - Button showDesc = (Button) view.findViewById(R.id.show_description); + final Button showDesc = (Button) view.findViewById(R.id.show_description); showDesc.setVisibility(View.GONE); showDesc.setOnClickListener(null); view.findViewById(R.id.loading).setVisibility(View.VISIBLE); @@ -1965,21 +1883,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc editNoteDialogListener.onFinishEditNoteDialog(note); } - private static class HtmlImageCounter implements Html.ImageGetter { - - private int imageCount = 0; - - @Override - public Drawable getDrawable(String url) { - imageCount++; - return null; - } - - public int getImageCount() { - return imageCount; - } - } - /** * Loads the description in background. <br /> * <br /> @@ -2008,7 +1911,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc protected Void doInBackground(Object... params) { try { // Fast preview: parse only HTML without loading any images - HtmlImageCounter imageCounter = new HtmlImageCounter(); + final HtmlImageCounter imageCounter = new HtmlImageCounter(); final UnknownTagsHandler unknownTagsHandler = new UnknownTagsHandler(); description = Html.fromHtml(descriptionString, imageCounter, unknownTagsHandler); publishProgress(); @@ -2035,7 +1938,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (needsRefresh) { publishProgress(); } - } catch (Exception e) { + } catch (final Exception e) { Log.e("LoadDescriptionTask: ", e); } return null; @@ -2050,7 +1953,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (StringUtils.isNotBlank(descriptionString)) { try { descriptionView.setText(description, TextView.BufferType.SPANNABLE); - } catch (Exception e) { + } catch (final Exception e) { // On 4.1, there is sometimes a crash on measuring the layout: https://code.google.com/p/android/issues/detail?id=35412 Log.e("Android bug setting text: ", e); // remove the formatting by converting to a simple string @@ -2072,7 +1975,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (shortDescView != null) { final String shortDescription = cache.getShortDescription(); if (StringUtils.isNotBlank(shortDescription)) { - int index = descriptionString.indexOf(shortDescription); + final int index = descriptionString.indexOf(shortDescription); if (index >= 0 && index < 200) { shortDescView.setVisibility(View.GONE); } @@ -2101,7 +2004,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (Settings.isLightSkin()) { backcolor = color.white; - for (Pattern pattern : LIGHT_COLOR_PATTERNS) { + for (final Pattern pattern : LIGHT_COLOR_PATTERNS) { final MatcherWrapper matcher = new MatcherWrapper(pattern, text); if (matcher.find()) { view.setBackgroundResource(color.darker_gray); @@ -2111,7 +2014,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } else { backcolor = color.black; - for (Pattern pattern : DARK_COLOR_PATTERNS) { + for (final Pattern pattern : DARK_COLOR_PATTERNS) { final MatcherWrapper matcher = new MatcherWrapper(pattern, text); if (matcher.find()) { view.setBackgroundResource(color.darker_gray); @@ -2123,176 +2026,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } } - private class LogsViewCreator extends AbstractCachingPageViewCreator<ListView> { - private final boolean allLogs; - - LogsViewCreator(boolean allLogs) { - this.allLogs = allLogs; - } - - @Override - public ListView getDispatchedView() { - if (cache == null) { - // something is really wrong - return null; - } - - view = (ListView) getLayoutInflater().inflate(R.layout.cachedetail_logs_page, null); - - // log count - final Map<LogType, Integer> logCounts = cache.getLogCounts(); - if (logCounts != null) { - final List<Entry<LogType, Integer>> sortedLogCounts = new ArrayList<Entry<LogType, Integer>>(logCounts.size()); - for (Entry<LogType, Integer> entry : logCounts.entrySet()) { - // it may happen that the label is unknown -> then avoid any output for this type - if (entry.getKey() != LogType.PUBLISH_LISTING && entry.getKey().getL10n() != null) { - sortedLogCounts.add(entry); - } - } - - if (!sortedLogCounts.isEmpty()) { - // sort the log counts by type id ascending. that way the FOUND, DNF log types are the first and most visible ones - Collections.sort(sortedLogCounts, new Comparator<Entry<LogType, Integer>>() { - - @Override - public int compare(Entry<LogType, Integer> logCountItem1, Entry<LogType, Integer> logCountItem2) { - return logCountItem1.getKey().compareTo(logCountItem2.getKey()); - } - }); - - ArrayList<String> labels = new ArrayList<String>(sortedLogCounts.size()); - for (Entry<LogType, Integer> pair : sortedLogCounts) { - labels.add(pair.getValue() + "× " + pair.getKey().getL10n()); - } - - final TextView countView = new TextView(CacheDetailActivity.this); - countView.setText(res.getString(R.string.cache_log_types) + ": " + StringUtils.join(labels, ", ")); - view.addHeaderView(countView, null, false); - } - } - - final List<LogEntry> logs = allLogs ? cache.getLogs() : cache.getFriendsLogs(); - view.setAdapter(new ArrayAdapter<LogEntry>(CacheDetailActivity.this, R.layout.logs_item, logs) { - final UserActionsClickListener userActionsClickListener = new UserActionsClickListener(); - final DecryptTextClickListener decryptTextClickListener = new DecryptTextClickListener(); - - @Override - public View getView(final int position, final View convertView, final ViewGroup parent) { - View rowView = convertView; - if (null == rowView) { - rowView = getLayoutInflater().inflate(R.layout.logs_item, null); - } - LogViewHolder holder = (LogViewHolder) rowView.getTag(); - if (null == holder) { - holder = new LogViewHolder(rowView); - } - holder.setPosition(position); - - final LogEntry log = getItem(position); - - if (log.date > 0) { - holder.date.setText(Formatter.formatShortDateVerbally(log.date)); - holder.date.setVisibility(View.VISIBLE); - } else { - holder.date.setVisibility(View.GONE); - } - - holder.type.setText(log.type.getL10n()); - holder.author.setText(StringEscapeUtils.unescapeHtml4(log.author)); - - // finds count - holder.countOrLocation.setVisibility(View.VISIBLE); - if (log.found == -1) { - holder.countOrLocation.setVisibility(View.GONE); - } else { - holder.countOrLocation.setText(res.getQuantityString(R.plurals.cache_counts, log.found, log.found)); - } - - // logtext, avoid parsing HTML if not necessary - String logText = log.log; - if (BaseUtils.containsHtml(logText)) { - logText = log.getDisplayText(); - // Fast preview: parse only HTML without loading any images - HtmlImageCounter imageCounter = new HtmlImageCounter(); - final UnknownTagsHandler unknownTagsHandler = new UnknownTagsHandler(); - holder.text.setText(Html.fromHtml(logText, imageCounter, unknownTagsHandler), TextView.BufferType.SPANNABLE); - if (imageCounter.getImageCount() > 0) { - // Complete view: parse again with loading images - if necessary ! If there are any images causing problems the user can see at least the preview - LogImageLoader loader = new LogImageLoader(holder); - loader.execute(logText); - } - } - else { - holder.text.setText(logText, TextView.BufferType.SPANNABLE); - } - - // images - if (log.hasLogImages()) { - holder.images.setText(log.getImageTitles()); - holder.images.setVisibility(View.VISIBLE); - holder.images.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - ImagesActivity.startActivityLogImages(CacheDetailActivity.this, cache.getGeocode(), new ArrayList<Image>(log.getLogImages())); - } - }); - } else { - holder.images.setVisibility(View.GONE); - } - - // colored marker - int marker = log.type.markerId; - if (marker != 0) { - holder.marker.setVisibility(View.VISIBLE); - holder.marker.setImageResource(marker); - } - else { - holder.marker.setVisibility(View.GONE); - } - - if (null == convertView) { - // if convertView != null then this listeners are already set - holder.author.setOnClickListener(userActionsClickListener); - holder.text.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); - holder.text.setOnClickListener(decryptTextClickListener); - registerForContextMenu(holder.text); - } - - return rowView; - } - }); - - return view; - } - - /** Loads the Log Images outside the ui thread. */ - - private class LogImageLoader extends AsyncTask<String, Progress, Spanned> { - final private LogViewHolder holder; - final private int position; - - public LogImageLoader(LogViewHolder holder) { - this.holder = holder; - this.position = holder.getPosition(); - } - - @Override - protected Spanned doInBackground(String... logtext) { - return Html.fromHtml(logtext[0], new HtmlImage(cache.getGeocode(), false, cache.getListId(), false), null); //, TextView.BufferType.SPANNABLE) - } - - @Override - protected void onPostExecute(Spanned result) { - // Ensure that this holder and its view still references the right item before updating the text. - if (position == holder.getPosition()) { - holder.text.setText(result); - } - } - - } - - } - private class WaypointsViewCreator extends AbstractCachingPageViewCreator<ScrollView> { @Override @@ -2342,7 +2075,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // visited if (wpt.isVisited()) { - TypedValue a = new TypedValue(); + 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! @@ -2354,7 +2087,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (StringUtils.isNotBlank(wpt.getNote())) { final TextView noteView = (TextView) waypointView.findViewById(R.id.note); noteView.setVisibility(View.VISIBLE); - if (BaseUtils.containsHtml(wpt.getNote())) { + if (TextUtils.containsHtml(wpt.getNote())) { noteView.setText(Html.fromHtml(wpt.getNote()), TextView.BufferType.SPANNABLE); } else { @@ -2429,9 +2162,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc view.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) { - Object selection = arg0.getItemAtPosition(arg2); + final Object selection = arg0.getItemAtPosition(arg2); if (selection instanceof Trackable) { - Trackable trackable = (Trackable) selection; + final Trackable trackable = (Trackable) selection; TrackableActivity.startActivity(CacheDetailActivity.this, trackable.getGuid(), trackable.getGeocode(), trackable.getName()); } } @@ -2476,10 +2209,10 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc */ private AlertDialog createResetCacheCoordinatesDialog(final Geocache cache, final Waypoint wpt) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); + final AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.waypoint_reset_cache_coords); - String[] items = new String[] { res.getString(R.string.waypoint_localy_reset_cache_coords), res.getString(R.string.waypoint_reset_local_and_remote_cache_coords) }; + final String[] items = new String[] { res.getString(R.string.waypoint_localy_reset_cache_coords), res.getString(R.string.waypoint_reset_local_and_remote_cache_coords) }; builder.setSingleChoiceItems(items, 0, new DialogInterface.OnClickListener() { @Override @@ -2531,7 +2264,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc private final boolean local; private final boolean remote; private final Waypoint wpt; - private ProgressDialog progress; + private final ProgressDialog progress; public static final int LOCAL = 0; public static final int ON_WEBSITE = 1; @@ -2561,7 +2294,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc handler.sendEmptyMessage(LOCAL); } - IConnector con = ConnectorFactory.getConnector(cache); + final IConnector con = ConnectorFactory.getConnector(cache); if (remote && con.supportsOwnCoordinates()) { runOnUiThread(new Runnable() { @Override @@ -2675,10 +2408,10 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return new DescriptionViewCreator(); case LOGS: - return new LogsViewCreator(true); + return new CacheLogsViewCreator(this, cache, true); case LOGSFRIENDS: - return new LogsViewCreator(false); + return new CacheLogsViewCreator(this, cache, false); case WAYPOINTS: return new WaypointsViewCreator(); @@ -2704,7 +2437,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc final Button offlineStore = (Button) view.findViewById(R.id.offline_store); if (cache.isOffline()) { - long diff = (System.currentTimeMillis() / (60 * 1000)) - (cache.getDetailedUpdate() / (60 * 1000)); // minutes + final long diff = (System.currentTimeMillis() / (60 * 1000)) - (cache.getDetailedUpdate() / (60 * 1000)); // minutes String ago; if (diff < 15) { diff --git a/main/src/cgeo/geocaching/CompassActivity.java b/main/src/cgeo/geocaching/CompassActivity.java index 4f8af08..059f664 100644 --- a/main/src/cgeo/geocaching/CompassActivity.java +++ b/main/src/cgeo/geocaching/CompassActivity.java @@ -18,6 +18,7 @@ import android.content.Context; import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorManager; +import android.media.AudioManager; import android.os.Bundle; import android.speech.tts.TextToSpeech.Engine; import android.view.Menu; @@ -94,6 +95,9 @@ public class CompassActivity extends AbstractActivity { setCacheInfo(); Views.inject(this); + + // make sure we can control the TTS volume + setVolumeControlStream(AudioManager.STREAM_MUSIC); } @Override @@ -127,8 +131,7 @@ public class CompassActivity extends AbstractActivity { final IWaypoint coordinate = coordinates.get(i); subMenu.add(0, COORDINATES_OFFSET + i, 0, coordinate.getName() + " (" + coordinate.getCoordType() + ")"); } - } - else { + } else { menu.findItem(R.id.menu_select_destination).setVisible(false); } return true; @@ -199,8 +202,7 @@ public class CompassActivity extends AbstractActivity { protected void onActivityResult(int request, int result, Intent data) { if (request == REQUEST_TTS_DATA_CHECK && result == Engine.CHECK_VOICE_DATA_PASS) { SpeechService.startService(this, dstCoords); - } - else { + } else { Log.i("TTS failed to start. Request: " + request + " result: " + result); startActivity(new Intent(Engine.ACTION_INSTALL_TTS_DATA)); } @@ -248,8 +250,7 @@ public class CompassActivity extends AbstractActivity { if (geo.getCoords() != null) { if (geo.getSatellitesVisible() >= 0) { navSatellites.setText(res.getString(R.string.loc_sat) + ": " + geo.getSatellitesFixed() + "/" + geo.getSatellitesVisible()); - } - else { + } else { navSatellites.setText(""); } navType.setText(res.getString(geo.getLocationProvider().resourceId)); diff --git a/main/src/cgeo/geocaching/DirectionProvider.java b/main/src/cgeo/geocaching/DirectionProvider.java index 37b184a..b4fb86c 100644 --- a/main/src/cgeo/geocaching/DirectionProvider.java +++ b/main/src/cgeo/geocaching/DirectionProvider.java @@ -27,6 +27,8 @@ public class DirectionProvider extends MemorySubject<Float> implements SensorEve @Override protected void onFirstObserver() { + @SuppressWarnings("deprecation") + // This will be removed when using a new location service. Until then, it is okay to be used. final Sensor defaultSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); sensorManager.registerListener(this, defaultSensor, SensorManager.SENSOR_DELAY_NORMAL); } diff --git a/main/src/cgeo/geocaching/EditWaypointActivity.java b/main/src/cgeo/geocaching/EditWaypointActivity.java index ab35641..dce49a3 100644 --- a/main/src/cgeo/geocaching/EditWaypointActivity.java +++ b/main/src/cgeo/geocaching/EditWaypointActivity.java @@ -11,7 +11,7 @@ import cgeo.geocaching.geopoint.DistanceParser; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; -import cgeo.geocaching.utils.BaseUtils; +import cgeo.geocaching.utils.TextUtils; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; @@ -99,7 +99,7 @@ public class EditWaypointActivity extends AbstractActivity { buttonLon.setText(waypoint.getCoords().format(GeopointFormatter.Format.LON_DECMINUTE)); } waypointName.setText(Html.fromHtml(StringUtils.trimToEmpty(waypoint.getName())).toString()); - if (BaseUtils.containsHtml(waypoint.getNote())) { + if (TextUtils.containsHtml(waypoint.getNote())) { note.setText(Html.fromHtml(StringUtils.trimToEmpty(waypoint.getNote())).toString()); } else { diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java index 00f1a1f..9649f5f 100644 --- a/main/src/cgeo/geocaching/Geocache.java +++ b/main/src/cgeo/geocaching/Geocache.java @@ -106,20 +106,20 @@ public class Geocache implements ICache, IWaypoint { private float myVote = 0; // valid ratings are larger than zero private int inventoryItems = 0; private boolean onWatchlist = false; - private List<String> attributes = new LazyInitializedList<String>() { + private final List<String> attributes = new LazyInitializedList<String>() { @Override public List<String> call() { return cgData.loadAttributes(geocode); } }; - private List<Waypoint> waypoints = new LazyInitializedList<Waypoint>() { + private final List<Waypoint> waypoints = new LazyInitializedList<Waypoint>() { @Override public List<Waypoint> call() { return cgData.loadWaypoints(geocode); } }; private List<Image> spoilers = null; - private List<LogEntry> logs = new LazyInitializedList<LogEntry>() { + private final List<LogEntry> logs = new LazyInitializedList<LogEntry>() { @Override public List<LogEntry> call() { return cgData.loadLogs(geocode); @@ -135,6 +135,7 @@ public class Geocache implements ICache, IWaypoint { private String nameForSorting; private final EnumSet<StorageLocation> storageLocation = EnumSet.of(StorageLocation.HEAP); private boolean finalDefined = false; + private boolean logPasswordRequired = false; private int zoomlevel = Tile.ZOOMLEVEL_MAX + 1; private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d+"); @@ -319,7 +320,7 @@ public class Geocache implements ICache, IWaypoint { this.setWaypoints(other.waypoints, false); } else { - ArrayList<Waypoint> newPoints = new ArrayList<Waypoint>(waypoints); + final ArrayList<Waypoint> newPoints = new ArrayList<Waypoint>(waypoints); Waypoint.mergeWayPoints(newPoints, other.waypoints, false); this.setWaypoints(newPoints, false); } @@ -346,12 +347,10 @@ public class Geocache implements ICache, IWaypoint { // if cache has ORIGINAL type waypoint ... it is considered that it has modified coordinates, otherwise not userModifiedCoords = false; - if (waypoints != null) { - for (Waypoint wpt : waypoints) { - if (wpt.getWaypointType() == WaypointType.ORIGINAL) { - userModifiedCoords = true; - break; - } + for (final Waypoint wpt : waypoints) { + if (wpt.getWaypointType() == WaypointType.ORIGINAL) { + userModifiedCoords = true; + break; } } @@ -465,7 +464,7 @@ public class Geocache implements ICache, IWaypoint { fromActivity.showToast(((Activity) fromActivity).getResources().getString(R.string.err_cannot_log_visit)); return; } - Intent logVisitIntent = new Intent((Activity) fromActivity, LogCacheActivity.class); + final Intent logVisitIntent = new Intent((Activity) fromActivity, LogCacheActivity.class); logVisitIntent.putExtra(LogCacheActivity.EXTRAS_ID, cacheId); logVisitIntent.putExtra(LogCacheActivity.EXTRAS_GEOCODE, geocode); @@ -484,7 +483,7 @@ public class Geocache implements ICache, IWaypoint { } final boolean status = cgData.saveLogOffline(geocode, date.getTime(), logType, log); - Resources res = fromActivity.getResources(); + final Resources res = fromActivity.getResources(); if (status) { ActivityMixin.showToast(fromActivity, res.getString(R.string.info_log_saved)); cgData.saveVisitDate(geocode); @@ -677,7 +676,7 @@ public class Geocache implements ICache, IWaypoint { */ private void initializeCacheTexts() { if (description == null || shortdesc == null || hint == null || location == null) { - Geocache partial = cgData.loadCacheTexts(this.getGeocode()); + final Geocache partial = cgData.loadCacheTexts(this.getGeocode()); if (description == null) { setDescription(partial.getDescription()); } @@ -752,7 +751,7 @@ public class Geocache implements ICache, IWaypoint { return; } - StringBuilder subject = new StringBuilder("Geocache "); + final StringBuilder subject = new StringBuilder("Geocache "); subject.append(geocode); if (StringUtils.isNotBlank(name)) { subject.append(" - ").append(name); @@ -1024,7 +1023,7 @@ public class Geocache implements ICache, IWaypoint { } finalDefined = false; if (waypoints != null) { - for (Waypoint waypoint : waypoints) { + for (final Waypoint waypoint : waypoints) { waypoint.setGeocode(geocode); if (waypoint.isFinalWithCoords()) { finalDefined = true; @@ -1045,8 +1044,8 @@ public class Geocache implements ICache, IWaypoint { * @return only the logs of friends, never <code>null</code> */ public List<LogEntry> getFriendsLogs() { - ArrayList<LogEntry> friendLogs = new ArrayList<LogEntry>(); - for (LogEntry log : logs) { + final ArrayList<LogEntry> friendLogs = new ArrayList<LogEntry>(); + for (final LogEntry log : logs) { if (log.friend) { friendLogs.add(log); } @@ -1261,7 +1260,7 @@ public class Geocache implements ICache, IWaypoint { */ private void resetFinalDefined() { finalDefined = false; - for (Waypoint wp : waypoints) { + for (final Waypoint wp : waypoints) { if (wp.isFinalWithCoords()) { finalDefined = true; break; @@ -1409,14 +1408,14 @@ public class Geocache implements ICache, IWaypoint { addOrChangeWaypoint(waypoint, false); count++; } - } catch (Geopoint.ParseException e) { + } catch (final Geopoint.ParseException e) { // ignore } note = note.substring(matcher.start() + 1); matcher = new MatcherWrapper(coordPattern, note); } - } catch (Exception e) { + } catch (final Exception e) { Log.e("Geocache.parseWaypointsFromNote", e); } } @@ -1426,7 +1425,7 @@ public class Geocache implements ICache, IWaypoint { */ private static WaypointType parseWaypointType(final String input) { final String lowerInput = StringUtils.substring(input, 0, 20).toLowerCase(Locale.getDefault()); - for (WaypointType wpType : WaypointType.values()) { + for (final WaypointType wpType : WaypointType.values()) { if (lowerInput.contains(wpType.getL10n().toLowerCase(Locale.getDefault()))) { return wpType; } @@ -1484,7 +1483,7 @@ public class Geocache implements ICache, IWaypoint { } public void store(final int listId, CancellableHandler handler) { - int newListId = listId < StoredList.STANDARD_LIST_ID + final int newListId = listId < StoredList.STANDARD_LIST_ID ? Math.max(getListId(), StoredList.STANDARD_LIST_ID) : listId; storeCache(this, null, newListId, false, handler); @@ -1515,7 +1514,7 @@ public class Geocache implements ICache, IWaypoint { cgData.removeCache(getGeocode(), EnumSet.of(RemoveFlag.REMOVE_CACHE)); handler.sendMessage(Message.obtain()); - } catch (Exception e) { + } catch (final Exception e) { Log.e("cache.drop: ", e); } } @@ -1614,7 +1613,7 @@ public class Geocache implements ICache, IWaypoint { // store spoilers if (CollectionUtils.isNotEmpty(cache.getSpoilers())) { - for (Image oneSpoiler : cache.getSpoilers()) { + for (final Image oneSpoiler : cache.getSpoilers()) { imgGetter.getDrawable(oneSpoiler.getUrl()); } } @@ -1625,9 +1624,9 @@ public class Geocache implements ICache, IWaypoint { // store images from logs if (Settings.isStoreLogImages()) { - for (LogEntry log : cache.getLogs()) { + for (final LogEntry log : cache.getLogs()) { if (log.hasLogImages()) { - for (Image oneLogImg : log.getLogImages()) { + for (final Image oneLogImg : log.getLogImages()) { imgGetter.getDrawable(oneLogImg.getUrl()); } } @@ -1650,7 +1649,7 @@ public class Geocache implements ICache, IWaypoint { if (handler != null) { handler.sendMessage(Message.obtain()); } - } catch (Exception e) { + } catch (final Exception e) { Log.e("Geocache.storeCache", e); } } @@ -1703,7 +1702,7 @@ public class Geocache implements ICache, IWaypoint { if (hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60) { return String.valueOf(hours * 60 + minutes); } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { // cannot happen, but static code analysis doesn't know } } @@ -1718,7 +1717,7 @@ public class Geocache implements ICache, IWaypoint { if (hours >= 0 && hours < 24) { return String.valueOf(hours * 60); } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { // cannot happen, but static code analysis doesn't know } } @@ -1747,9 +1746,9 @@ public class Geocache implements ICache, IWaypoint { } public List<Image> getImages() { - List<Image> result = new ArrayList<Image>(); + final List<Image> result = new ArrayList<Image>(); result.addAll(getSpoilers()); - for (LogEntry log : getLogs()) { + for (final LogEntry log : getLogs()) { result.addAll(log.getLogImages()); } return result; @@ -1767,7 +1766,7 @@ public class Geocache implements ICache, IWaypoint { * the cache, so the result might be wrong. */ public boolean hasOwnLog(LogType logType) { - for (LogEntry logEntry : getLogs()) { + for (final LogEntry logEntry : getLogs()) { if (logEntry.type == logType && logEntry.isOwn()) { return true; } @@ -1778,4 +1777,12 @@ public class Geocache implements ICache, IWaypoint { public int getMapMarkerId() { return getConnector().getCacheMapMarkerId(isDisabled() || isArchived()); } + + public boolean isLogPasswordRequired() { + return logPasswordRequired; + } + + public void setLogPasswordRequired(boolean required) { + logPasswordRequired = required; + } } diff --git a/main/src/cgeo/geocaching/ImageSelectActivity.java b/main/src/cgeo/geocaching/ImageSelectActivity.java index 26dd85a..39824a9 100644 --- a/main/src/cgeo/geocaching/ImageSelectActivity.java +++ b/main/src/cgeo/geocaching/ImageSelectActivity.java @@ -5,7 +5,7 @@ import butterknife.Views; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.compatibility.Compatibility; -import cgeo.geocaching.utils.ImageHelper; +import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; @@ -282,10 +282,10 @@ public class ImageSelectActivity extends AbstractActivity { } else { image = BitmapFactory.decodeFile(filePath); } - final BitmapDrawable scaledImage = ImageHelper.scaleBitmapTo(image, maxXY, maxXY); + final BitmapDrawable scaledImage = ImageUtils.scaleBitmapTo(image, maxXY, maxXY); image = null; final String uploadFilename = getOutputImageFile().getPath(); - ImageHelper.storeBitmap(scaledImage.getBitmap(), Bitmap.CompressFormat.JPEG, 75, uploadFilename); + ImageUtils.storeBitmap(scaledImage.getBitmap(), Bitmap.CompressFormat.JPEG, 75, uploadFilename); return uploadFilename; } diff --git a/main/src/cgeo/geocaching/LogCacheActivity.java b/main/src/cgeo/geocaching/LogCacheActivity.java index 62c94ce..b3b6c09 100644 --- a/main/src/cgeo/geocaching/LogCacheActivity.java +++ b/main/src/cgeo/geocaching/LogCacheActivity.java @@ -71,6 +71,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia private Button postButton = null; private CheckBox tweetCheck = null; private LinearLayout tweetBox = null; + private LinearLayout logPasswordBox = null; private boolean tbChanged = false; private SparseArray<TrackableLog> actionButtons; @@ -252,6 +253,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia postButton = (Button) findViewById(R.id.post); tweetBox = (LinearLayout) findViewById(R.id.tweet_box); tweetCheck = (CheckBox) findViewById(R.id.tweet); + logPasswordBox = (LinearLayout) findViewById(R.id.log_password_box); // initialize with default values setDefaultValues(); @@ -302,6 +304,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia tweetCheck.setChecked(true); updateTweetBox(typeSelected); + updateLogPasswordBox(typeSelected); final Button imageButton = (Button) findViewById(R.id.image_btn); imageButton.setOnClickListener(new View.OnClickListener() { @@ -377,6 +380,8 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia final EditText logView = (EditText) findViewById(R.id.log); logView.setText(StringUtils.EMPTY); + final EditText logPasswordView = (EditText) findViewById(R.id.log_password); + logPasswordView.setText(StringUtils.EMPTY); updateImageButton(); @@ -491,6 +496,14 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia } } + private void updateLogPasswordBox(LogType type) { + if (type == LogType.FOUND_IT && cache.isLogPasswordRequired()) { + logPasswordBox.setVisibility(View.VISIBLE); + } else { + logPasswordBox.setVisibility(View.GONE); + } + } + private class DateListener implements View.OnClickListener { @Override @@ -507,7 +520,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia final String message = res.getString(StringUtils.isBlank(imageUri.getPath()) ? R.string.log_saving : R.string.log_saving_and_uploading); - new Poster(LogCacheActivity.this, message).execute(currentLogText()); + new Poster(LogCacheActivity.this, message).execute(currentLogText(), currentLogPassword()); } } @@ -520,8 +533,9 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia @Override protected StatusCode doInBackgroundInternal(final String[] logTexts) { final String log = logTexts[0]; + final String logPwd = logTexts.length > 1 ? logTexts[1] : null; try { - final LogResult logResult = loggingManager.postLog(cache, typeSelected, date, log, trackables); + final LogResult logResult = loggingManager.postLog(cache, typeSelected, date, log, logPwd, trackables); if (logResult.getPostLogResult() == StatusCode.NO_ERROR) { final LogEntry logNow = new LogEntry(date, typeSelected, log); @@ -593,6 +607,10 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia return ((EditText) findViewById(R.id.log)).getText().toString(); } + private String currentLogPassword() { + return ((EditText) findViewById(R.id.log_password)).getText().toString(); + } + @Override protected LogContext getLogContext() { return new LogContext(cache); diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java index ff24322..5898cba 100644 --- a/main/src/cgeo/geocaching/MainActivity.java +++ b/main/src/cgeo/geocaching/MainActivity.java @@ -5,9 +5,7 @@ import butterknife.Views; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.connector.ConnectorFactory; -import cgeo.geocaching.connector.IConnector; -import cgeo.geocaching.connector.gc.Login; -import cgeo.geocaching.connector.oc.OCApiLiveConnector; +import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.geopoint.Geopoint; @@ -38,11 +36,13 @@ import android.location.Geocoder; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import java.util.ArrayList; @@ -53,8 +53,6 @@ import java.util.List; import java.util.Locale; public class MainActivity extends AbstractActivity { - @InjectView(R.id.user_info_gc) protected TextView userInfoViewGc; - @InjectView(R.id.user_info_ocde) protected TextView userInfoViewOcDe; @InjectView(R.id.nav_satellites) protected TextView navSatellites; @InjectView(R.id.filter_button_title)protected TextView filterTitle; @InjectView(R.id.map) protected ImageView findOnMap; @@ -67,6 +65,7 @@ public class MainActivity extends AbstractActivity { @InjectView(R.id.nav_accuracy) protected TextView navAccuracy ; @InjectView(R.id.nav_location) protected TextView navLocation ; @InjectView(R.id.offline_count) protected TextView countBubble ; + @InjectView(R.id.info_area) protected LinearLayout infoArea; private static final String SCAN_INTENT = "com.google.zxing.client.android.SCAN"; public static final int SEARCH_REQUEST_CODE = 2; @@ -86,46 +85,30 @@ public class MainActivity extends AbstractActivity { @Override public void handleMessage(Message msg) { - if (Settings.isGCConnectorActive()) { - StringBuilder userInfo = new StringBuilder("geocaching.com").append(Formatter.SEPARATOR); - if (Login.isActualLoginStatus()) { - userInfo.append(Login.getActualUserName()); - if (Login.getActualCachesFound() >= 0) { - userInfo.append(" (").append(String.valueOf(Login.getActualCachesFound())).append(')'); - } - userInfo.append(Formatter.SEPARATOR); - } - userInfo.append(Login.getActualStatus()); + // Get active connectors with login status + ILogin[] loginConns = ConnectorFactory.getActiveLiveConnectors(); - userInfoViewGc.setText(userInfo.toString()); - userInfoViewGc.setVisibility(View.VISIBLE); - } - else { - userInfoViewGc.setVisibility(View.GONE); - } + // Update UI + infoArea.removeAllViews(); + LayoutInflater inflater = getLayoutInflater(); - if (Settings.isOCConnectorActive()) { - StringBuilder userInfo = new StringBuilder("opencaching.de").append(Formatter.SEPARATOR); - IConnector conn = ConnectorFactory.getConnector("OCXXXX"); - if (conn instanceof OCApiLiveConnector) { - OCApiLiveConnector ocapiConn = (OCApiLiveConnector) conn; - if (ocapiConn.supportsPersonalization()) { - userInfo.append(ocapiConn.getUserName()); - int count = ocapiConn.getCachesFound(); - if (count >= 0) { - userInfo.append(" (").append(String.valueOf(count)).append(')'); - } - } else { - userInfo.append("Anonymous"); + for (ILogin conn : loginConns) { + + TextView connectorInfo = (TextView) inflater.inflate(R.layout.main_activity_connectorstatus, null); + infoArea.addView(connectorInfo); + + StringBuilder userInfo = new StringBuilder(conn.getName()).append(Formatter.SEPARATOR); + if (conn.isLoggedIn()) { + userInfo.append(conn.getUserName()); + if (conn.getCachesFound() >= 0) { + userInfo.append(" (").append(String.valueOf(conn.getCachesFound())).append(')'); } + userInfo.append(Formatter.SEPARATOR); } - userInfoViewOcDe.setText(userInfo.toString()); - userInfoViewOcDe.setVisibility(View.VISIBLE); - } - else { - userInfoViewOcDe.setVisibility(View.GONE); - } + userInfo.append(conn.getLoginStatusString()); + connectorInfo.setText(userInfo); + } } }; @@ -727,35 +710,11 @@ public class MainActivity extends AbstractActivity { return; } - if (Settings.isGCConnectorActive()) { - // login - final StatusCode status = Login.login(); - - if (status == StatusCode.NO_ERROR) { - app.firstRun = false; - Login.detectGcCustomDate(); - updateUserInfoHandler.sendEmptyMessage(-1); - } - - if (app.showLoginToast) { - firstLoginHandler.sendMessage(firstLoginHandler.obtainMessage(0, status)); - app.showLoginToast = false; + ILogin[] conns = ConnectorFactory.getActiveLiveConnectors(); - // invoke settings activity to insert login details - if (status == StatusCode.NO_LOGIN_INFO_STORED) { - SettingsActivity.startActivity(MainActivity.this); - } - } - } - if (Settings.isOCConnectorActive()) { - IConnector conn = ConnectorFactory.getConnector("OCXXXX"); - if (conn instanceof OCApiLiveConnector) { - OCApiLiveConnector ocapiConn = (OCApiLiveConnector) conn; - if (ocapiConn.supportsPersonalization()) { - ocapiConn.retrieveUserInfo(); - } - updateUserInfoHandler.sendEmptyMessage(-1); - } + for (ILogin conn : conns) { + conn.login(firstLoginHandler, MainActivity.this); + updateUserInfoHandler.sendEmptyMessage(-1); } } } diff --git a/main/src/cgeo/geocaching/SearchActivity.java b/main/src/cgeo/geocaching/SearchActivity.java index 0c31abf..c2a7b6d 100644 --- a/main/src/cgeo/geocaching/SearchActivity.java +++ b/main/src/cgeo/geocaching/SearchActivity.java @@ -7,11 +7,10 @@ import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.connector.capability.ISearchByGeocode; -import cgeo.geocaching.connector.gc.GCConstants; +import cgeo.geocaching.connector.trackable.TrackableConnector; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; -import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.EditUtils; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; @@ -61,7 +60,7 @@ public class SearchActivity extends AbstractActivity { super.onCreate(savedInstanceState); // search query - Intent intent = getIntent(); + final Intent intent = getIntent(); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { final String query = intent.getStringExtra(SearchManager.QUERY); final boolean keywordSearch = intent.getBooleanExtra(Intents.EXTRA_KEYWORD_SEARCH, true); @@ -122,7 +121,7 @@ public class SearchActivity extends AbstractActivity { // otherwise see if this is a pure geocode if (StringUtils.isEmpty(geocode)) { - geocode = StringUtils.trim(query); + geocode = StringUtils.upperCase(StringUtils.trim(query)); } final IConnector connector = ConnectorFactory.getConnector(geocode); @@ -134,10 +133,10 @@ public class SearchActivity extends AbstractActivity { } // Check if the query is a TB code - final String trackable = BaseUtils.getMatch(query, GCConstants.PATTERN_TB_CODE, true, 0, "", false); - if (StringUtils.isNotBlank(trackable)) { + final TrackableConnector trackableConnector = ConnectorFactory.getTrackableConnector(geocode); + if (trackableConnector != ConnectorFactory.UNKNOWN_TRACKABLE_CONNECTOR) { final Intent trackablesIntent = new Intent(this, TrackableActivity.class); - trackablesIntent.putExtra(Intents.EXTRA_GEOCODE, trackable.toUpperCase(Locale.US)); + trackablesIntent.putExtra(Intents.EXTRA_GEOCODE, geocode.toUpperCase(Locale.US)); startActivity(trackablesIntent); return true; } @@ -240,7 +239,7 @@ public class SearchActivity extends AbstractActivity { lonEdit.setHint(geo.getCoords().format(GeopointFormatter.Format.LON_DECMINUTE_RAW)); } } - } catch (Exception e) { + } catch (final Exception e) { Log.w("Failed to update location."); } } @@ -250,7 +249,7 @@ public class SearchActivity extends AbstractActivity { @Override public void onClick(View arg0) { - CoordinatesInputDialog coordsDialog = new CoordinatesInputDialog(SearchActivity.this, null, null, app.currentGeo()); + final CoordinatesInputDialog coordsDialog = new CoordinatesInputDialog(SearchActivity.this, null, null, app.currentGeo()); coordsDialog.setCancelable(true); coordsDialog.setOnCoordinateUpdate(new CoordinatesInputDialog.CoordinateUpdate() { @Override @@ -284,7 +283,7 @@ public class SearchActivity extends AbstractActivity { } else { try { cgeocaches.startActivityCoordinates(this, new Geopoint(StringUtils.trim(latText), StringUtils.trim(lonText))); - } catch (Geopoint.ParseException e) { + } catch (final Geopoint.ParseException e) { showToast(res.getString(e.resource)); } } @@ -300,7 +299,7 @@ public class SearchActivity extends AbstractActivity { private void findByKeywordFn() { // find caches by coordinates - String keyText = keywordEditText.getText().toString(); + final String keyText = keywordEditText.getText().toString(); if (StringUtils.isBlank(keyText)) { helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_keyword)); diff --git a/main/src/cgeo/geocaching/StatusFragment.java b/main/src/cgeo/geocaching/StatusFragment.java index 5a9a5b4..e0e714a 100644 --- a/main/src/cgeo/geocaching/StatusFragment.java +++ b/main/src/cgeo/geocaching/StatusFragment.java @@ -57,7 +57,7 @@ public class StatusFragment extends Fragment { @Override public void handleMessage(final Message msg) { final Status data = (Status) msg.obj; - updateDisplay(data != null && data.message != null ? data : null); + updateDisplay(data != null && data.message != null ? data : Status.defaultStatus()); } private void updateDisplay(final Status data) { diff --git a/main/src/cgeo/geocaching/StoredList.java b/main/src/cgeo/geocaching/StoredList.java index b92ce9a..55a155c 100644 --- a/main/src/cgeo/geocaching/StoredList.java +++ b/main/src/cgeo/geocaching/StoredList.java @@ -71,7 +71,7 @@ public final class StoredList { promptForListSelection(titleId, runAfterwards, false, -1); } - public void promptForListSelection(final int titleId, final RunnableWithArgument<Integer> runAfterwards, final boolean onlyMoveTargets, final int exceptListId) { + public void promptForListSelection(final int titleId, final RunnableWithArgument<Integer> runAfterwards, final boolean onlyConcreteLists, final int exceptListId) { final List<StoredList> lists = getSortedLists(); if (lists == null) { @@ -89,7 +89,7 @@ public final class StoredList { for (StoredList list : lists) { listsTitle.add(list.getTitleAndCount()); } - if (!onlyMoveTargets) { + if (!onlyConcreteLists) { listsTitle.add("<" + res.getString(R.string.list_menu_all_lists) + ">"); } listsTitle.add("<" + res.getString(R.string.list_menu_create) + ">"); @@ -101,7 +101,7 @@ public final class StoredList { builder.setItems(listsTitle.toArray(items), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int itemId) { - if (itemId == lists.size() && !onlyMoveTargets) { + if (itemId == lists.size() && !onlyConcreteLists) { // all lists runAfterwards.run(StoredList.ALL_LIST_ID); } else if (itemId >= lists.size()) { diff --git a/main/src/cgeo/geocaching/Trackable.java b/main/src/cgeo/geocaching/Trackable.java index f777351..d532cda 100644 --- a/main/src/cgeo/geocaching/Trackable.java +++ b/main/src/cgeo/geocaching/Trackable.java @@ -1,7 +1,8 @@ package cgeo.geocaching; +import cgeo.geocaching.connector.ConnectorFactory; +import cgeo.geocaching.connector.trackable.TrackableConnector; import cgeo.geocaching.enumerations.LogType; -import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; @@ -38,17 +39,11 @@ public class Trackable implements ILogable { private String trackingcode = null; public String getUrl() { - if (StringUtils.startsWithIgnoreCase(getGeocode(), "GK")) { - String hex = getGeocode().substring(3); - try { - int id = Integer.parseInt(hex, 16); - return "http://geokrety.org/konkret.php?id=" + id; - } catch (NumberFormatException e) { - Log.e("Trackable.getUrl", e); - return null; - } - } - return "http://www.geocaching.com//track/details.aspx?tracker=" + geocode; + return getConnector().getUrl(this); + } + + private TrackableConnector getConnector() { + return ConnectorFactory.getConnector(this); } public String getGuid() { @@ -208,7 +203,7 @@ public class Trackable implements ILogable { } public boolean isLoggable() { - return !StringUtils.startsWithIgnoreCase(getGeocode(), "GK"); + return getConnector().isLoggable(); } public String getTrackingcode() { @@ -220,7 +215,7 @@ public class Trackable implements ILogable { } static public List<LogType> getPossibleLogTypes() { - List<LogType> logTypes = new ArrayList<LogType>(); + final List<LogType> logTypes = new ArrayList<LogType>(); logTypes.add(LogType.RETRIEVED_IT); logTypes.add(LogType.GRABBED_IT); logTypes.add(LogType.NOTE); diff --git a/main/src/cgeo/geocaching/TrackableActivity.java b/main/src/cgeo/geocaching/TrackableActivity.java index 9822946..d1f323c 100644 --- a/main/src/cgeo/geocaching/TrackableActivity.java +++ b/main/src/cgeo/geocaching/TrackableActivity.java @@ -5,16 +5,18 @@ import butterknife.Views; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.AbstractViewPagerActivity; -import cgeo.geocaching.connector.gc.GCParser; +import cgeo.geocaching.connector.ConnectorFactory; +import cgeo.geocaching.connector.trackable.TrackableConnector; +import cgeo.geocaching.connector.trackable.TravelBugConnector; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.network.HtmlImage; -import cgeo.geocaching.network.Network; import cgeo.geocaching.ui.AbstractCachingPageViewCreator; import cgeo.geocaching.ui.AnchorAwareLinkMovementMethod; import cgeo.geocaching.ui.CacheDetailsCreator; import cgeo.geocaching.ui.Formatter; -import cgeo.geocaching.utils.BaseUtils; +import cgeo.geocaching.ui.UserActionsClickListener; +import cgeo.geocaching.ui.logs.TrackableLogsViewCreator; import cgeo.geocaching.utils.HtmlUtils; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.UnknownTagsHandler; @@ -31,16 +33,12 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.Html; -import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.RelativeLayout; import android.widget.ScrollView; import android.widget.TextView; @@ -60,15 +58,15 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi this.resId = resId; } } + private Trackable trackable = null; private String geocode = null; private String name = null; private String guid = null; private String id = null; - private String contextMenuUser = null; private LayoutInflater inflater = null; private ProgressDialog waitDialog = null; - private Handler loadTrackableHandler = new Handler() { + private final Handler loadTrackableHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -100,7 +98,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi invalidateOptionsMenuCompatible(); reinitializeViewPager(); - } catch (Exception e) { + } catch (final Exception e) { Log.e("TrackableActivity.loadTrackableHandler: ", e); } @@ -118,8 +116,8 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi setTitle(res.getString(R.string.trackable)); // get parameters - Bundle extras = getIntent().getExtras(); - Uri uri = getIntent().getData(); + final Bundle extras = getIntent().getExtras(); + final Uri uri = getIntent().getData(); // try to get data from extras if (extras != null) { @@ -131,7 +129,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi // try to get data from URI if (geocode == null && guid == null && id == null && uri != null) { - String uriHost = uri.getHost().toLowerCase(Locale.US); + final String uriHost = uri.getHost().toLowerCase(Locale.US); if (uriHost.contains("geocaching.com")) { geocode = uri.getQueryParameter("tracker"); guid = uri.getQueryParameter("guid"); @@ -155,7 +153,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi return; } } else if (uriHost.contains("coord.info")) { - String uriPath = uri.getPath().toLowerCase(Locale.US); + final String uriPath = uri.getPath().toLowerCase(Locale.US); if (uriPath != null && uriPath.startsWith("/tb")) { geocode = uriPath.substring(1).toUpperCase(Locale.US); guid = null; @@ -186,57 +184,11 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi waitDialog = ProgressDialog.show(this, message, res.getString(R.string.trackable_details_loading), true, true); createViewPager(0, null); - LoadTrackableThread thread = new LoadTrackableThread(loadTrackableHandler, geocode, guid, id); + final LoadTrackableThread thread = new LoadTrackableThread(loadTrackableHandler, geocode, guid, id); thread.start(); } @Override - public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo info) { - super.onCreateContextMenu(menu, view, info); - final int viewId = view.getId(); - - if (viewId == R.id.author) { // Log item author - contextMenuUser = ((TextView) view).getText().toString(); - } else { // Trackable owner, and user holding trackable now - RelativeLayout itemLayout = (RelativeLayout) view.getParent(); - TextView itemName = (TextView) itemLayout.findViewById(R.id.name); - - String selectedName = itemName.getText().toString(); - if (selectedName.equals(res.getString(R.string.trackable_owner))) { - contextMenuUser = trackable.getOwner(); - } else if (selectedName.equals(res.getString(R.string.trackable_spotted))) { - contextMenuUser = trackable.getSpottedName(); - } - } - - menu.setHeaderTitle(res.getString(R.string.user_menu_title) + " " + contextMenuUser); - menu.add(viewId, 1, 0, res.getString(R.string.user_menu_view_hidden)); - menu.add(viewId, 2, 0, res.getString(R.string.user_menu_view_found)); - menu.add(viewId, 3, 0, res.getString(R.string.user_menu_open_browser)); - menu.add(viewId, 4, 0, res.getString(R.string.user_menu_send_message)); - } - - @Override - public boolean onContextItemSelected(final MenuItem item) { - switch (item.getItemId()) { - case 1: - cgeocaches.startActivityOwner(this, contextMenuUser); - return true; - case 2: - cgeocaches.startActivityUserName(this, contextMenuUser); - return true; - case 3: - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/profile/?u=" + Network.encode(contextMenuUser)))); - return true; - case 4: - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/email/?u=" + Network.encode(contextMenuUser)))); - return true; - default: - return false; - } - } - - @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.trackable_activity, menu); return true; @@ -280,29 +232,26 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi @Override public void run() { - trackable = cgData.loadTrackable(geocode); - - if ((trackable == null || trackable.isLoggable()) && !StringUtils.startsWithIgnoreCase(geocode, "GK")) { - trackable = GCParser.searchTrackable(geocode, guid, id); - } - handler.sendMessage(Message.obtain()); - } - } - - private class UserActionsListener implements View.OnClickListener { - - @Override - public void onClick(View view) { - if (view == null) { - return; + if (StringUtils.isNotEmpty(geocode)) { + trackable = cgData.loadTrackable(geocode); + + if (trackable == null || trackable.isLoggable()) { + // iterate over the connectors as some codes may be handled by multiple connectors + for (final TrackableConnector trackableConnector : ConnectorFactory.getTrackableConnectors()) { + if (trackableConnector.canHandleTrackable(geocode)) { + trackable = trackableConnector.searchTrackable(geocode, guid, id); + if (trackable != null) { + break; + } + } + } + } } - - try { - registerForContextMenu(view); - openContextMenu(view); - } catch (Exception e) { - Log.e("TrackableActivity.UserActionsListener.onClick ", e); + // fall back to GC search by GUID + if (trackable == null) { + trackable = TravelBugConnector.getInstance().searchTrackable(geocode, guid, id); } + handler.sendMessage(Message.obtain()); } } @@ -322,12 +271,12 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi } try { - HtmlImage imgGetter = new HtmlImage(trackable.getGeocode(), false, 0, false); + final HtmlImage imgGetter = new HtmlImage(trackable.getGeocode(), false, 0, false); - BitmapDrawable image = imgGetter.getDrawable(url); - Message message = handler.obtainMessage(0, image); + final BitmapDrawable image = imgGetter.getDrawable(url); + final Message message = handler.obtainMessage(0, image); handler.sendMessage(message); - } catch (Exception e) { + } catch (final Exception e) { Log.e("TrackableActivity.TrackableIconThread.run: ", e); } } @@ -365,7 +314,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi case DETAILS: return new DetailsViewCreator(); case LOGS: - return new LogsViewCreator(); + return new TrackableLogsViewCreator(this, trackable); default: throw new IllegalArgumentException(); } @@ -378,7 +327,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi @Override protected Pair<List<? extends Page>, Integer> getOrderedPages() { - List<Page> pages = new ArrayList<TrackableActivity.Page>(); + final List<Page> pages = new ArrayList<TrackableActivity.Page>(); pages.add(Page.DETAILS); if (!trackable.getLogs().isEmpty()) { pages.add(Page.LOGS); @@ -386,98 +335,6 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi return new ImmutablePair<List<? extends Page>, Integer>(pages, 0); } - public class LogsViewCreator extends AbstractCachingPageViewCreator<ListView> { - - @Override - public ListView getDispatchedView() { - view = (ListView) getLayoutInflater().inflate(R.layout.trackable_logs_view, null); - - if (trackable != null && trackable.getLogs() != null) { - view.setAdapter(new ArrayAdapter<LogEntry>(TrackableActivity.this, R.layout.logs_item, trackable.getLogs()) { - @Override - public View getView(int position, View convertView, android.view.ViewGroup parent) { - View rowView = convertView; - if (null == rowView) { - rowView = getLayoutInflater().inflate(R.layout.logs_item, null); - } - LogViewHolder holder = (LogViewHolder) rowView.getTag(); - if (null == holder) { - holder = new LogViewHolder(rowView); - } - - final LogEntry log = getItem(position); - fillViewHolder(holder, log); - return rowView; - } - }); - } - return view; - } - - protected void fillViewHolder(LogViewHolder holder, final LogEntry log) { - if (log.date > 0) { - holder.date.setText(Formatter.formatShortDateVerbally(log.date)); - } - - holder.type.setText(log.type.getL10n()); - holder.author.setText(Html.fromHtml(log.author), TextView.BufferType.SPANNABLE); - - if (StringUtils.isBlank(log.cacheName)) { - holder.countOrLocation.setVisibility(View.GONE); - } else { - holder.countOrLocation.setText(Html.fromHtml(log.cacheName)); - final String cacheGuid = log.cacheGuid; - final String cacheName = log.cacheName; - holder.countOrLocation.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View arg0) { - CacheDetailActivity.startActivityGuid(TrackableActivity.this, cacheGuid, Html.fromHtml(cacheName).toString()); - } - }); - } - - TextView logView = holder.text; - logView.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); - - String logText = log.log; - if (BaseUtils.containsHtml(logText)) { - logText = log.getDisplayText(); - logView.setText(Html.fromHtml(logText, new HtmlImage(null, false, StoredList.TEMPORARY_LIST_ID, false), null), TextView.BufferType.SPANNABLE); - } - else { - logView.setText(logText); - } - - ImageView statusMarker = holder.marker; - // colored marker - int marker = log.type.markerId; - if (marker != 0) { - statusMarker.setVisibility(View.VISIBLE); - statusMarker.setImageResource(marker); - } - else { - statusMarker.setVisibility(View.GONE); - } - - // images - if (log.hasLogImages()) { - holder.images.setText(log.getImageTitles()); - holder.images.setVisibility(View.VISIBLE); - holder.images.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - ImagesActivity.startActivityLogImages(TrackableActivity.this, trackable.getGeocode(), new ArrayList<Image>(log.getLogImages())); - } - }); - } else { - holder.images.setVisibility(View.GONE); - } - - holder.author.setOnClickListener(new UserActionsListener()); - } - - } - public class DetailsViewCreator extends AbstractCachingPageViewCreator<ScrollView> { @InjectView(R.id.goal_box) protected LinearLayout goalBox; @@ -518,10 +375,10 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi details.add(R.string.trackable_code, trackable.getGeocode()); // trackable owner - TextView owner = details.add(R.string.trackable_owner, res.getString(R.string.trackable_unknown)); + final TextView owner = details.add(R.string.trackable_owner, res.getString(R.string.trackable_unknown)); if (StringUtils.isNotBlank(trackable.getOwner())) { owner.setText(Html.fromHtml(trackable.getOwner()), TextView.BufferType.SPANNABLE); - owner.setOnClickListener(new UserActionsListener()); + owner.setOnClickListener(new UserActionsClickListener()); } // trackable spotted @@ -546,7 +403,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi // days since last spotting if (showTimeSpan && trackable.getLogs() != null) { - for (LogEntry log : trackable.getLogs()) { + for (final LogEntry log : trackable.getLogs()) { if (log.type == LogType.RETRIEVED_IT || log.type == LogType.GRABBED_IT || log.type == LogType.DISCOVERED_IT || log.type == LogType.PLACED_IT) { final int days = log.daysSinceLog(); text.append(" (").append(res.getQuantityString(R.plurals.days_ago, days, days)).append(')'); @@ -565,13 +422,13 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi } }); } else if (Trackable.SPOTTED_USER == trackable.getSpottedType()) { - spotted.setOnClickListener(new UserActionsListener()); + spotted.setOnClickListener(new UserActionsClickListener()); } } // trackable origin if (StringUtils.isNotBlank(trackable.getOrigin())) { - TextView origin = details.add(R.string.trackable_origin, ""); + final TextView origin = details.add(R.string.trackable_origin, ""); origin.setText(Html.fromHtml(trackable.getOrigin()), TextView.BufferType.SPANNABLE); } @@ -621,7 +478,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi @Override public void handleMessage(Message message) { - BitmapDrawable image = (BitmapDrawable) message.obj; + final BitmapDrawable image = (BitmapDrawable) message.obj; if (image != null) { trackableImage.setImageDrawable((BitmapDrawable) message.obj); } @@ -633,12 +490,12 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi @Override public void run() { try { - HtmlImage imgGetter = new HtmlImage(geocode, true, 0, false); + final HtmlImage imgGetter = new HtmlImage(geocode, true, 0, false); - BitmapDrawable image = imgGetter.getDrawable(trackable.getImage()); - Message message = handler.obtainMessage(0, image); + final BitmapDrawable image = imgGetter.getDrawable(trackable.getImage()); + final Message message = handler.obtainMessage(0, image); handler.sendMessage(message); - } catch (Exception e) { + } catch (final Exception e) { Log.e("TrackableActivity.DetailsViewCreator.ImageGetterThread: ", e); } } diff --git a/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java b/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java index e0c304a..0345633 100644 --- a/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java @@ -208,6 +208,7 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends pageOrder.add(null); } } + viewPagerAdapter.notifyDataSetChanged(); viewPager.setCurrentItem(startPageIndex, false); } @@ -233,15 +234,19 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends final Pair<List<? extends Page>, Integer> pagesAndIndex = getOrderedPages(); pageOrder.addAll(pagesAndIndex.getLeft()); + // Since we just added pages notifyDataSetChanged needs to be called before we possibly setCurrentItem below. + // But, calling it will reset current item and we won't be able to tell if we would have been out of bounds + final int currentItem = getCurrentItem(); + + // notify the adapter that the data has changed + viewPagerAdapter.notifyDataSetChanged(); + // switch to details page, if we're out of bounds final int defaultPage = pagesAndIndex.getRight(); - if (getCurrentItem() < 0 || getCurrentItem() >= viewPagerAdapter.getCount()) { + if (currentItem < 0 || currentItem >= viewPagerAdapter.getCount()) { viewPager.setCurrentItem(defaultPage, false); } - // notify the adapter that the data has changed - viewPagerAdapter.notifyDataSetChanged(); - // notify the indicator that the data has changed titleIndicator.notifyDataSetChanged(); } diff --git a/main/src/cgeo/geocaching/apps/AbstractApp.java b/main/src/cgeo/geocaching/apps/AbstractApp.java index ef56f87..4d7abde 100644 --- a/main/src/cgeo/geocaching/apps/AbstractApp.java +++ b/main/src/cgeo/geocaching/apps/AbstractApp.java @@ -5,6 +5,8 @@ import cgeo.geocaching.MainActivity; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.utils.ProcessUtils; +import org.apache.commons.lang3.StringUtils; + import android.content.Intent; public abstract class AbstractApp implements App { @@ -26,7 +28,7 @@ public abstract class AbstractApp implements App { @Override public boolean isInstalled() { - if (ProcessUtils.isInstalled(packageName)) { + if (StringUtils.isNotEmpty(packageName) && ProcessUtils.isLaunchable(packageName)) { return true; } return MainActivity.isIntentAvailable(intent); diff --git a/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java b/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java index 1019b0f..a3532a5 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/GoogleNavigationApp.java @@ -27,22 +27,28 @@ abstract class GoogleNavigationApp extends AbstractPointNavigationApp { try { activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri .parse("google.navigation:ll=" + coords.getLatitude() + "," - + coords.getLongitude() + mode))); + + coords.getLongitude() + "&mode=" + mode))); - } catch (Exception e) { + } catch (final Exception e) { Log.i("GoogleNavigationApp.navigate: No navigation application available.", e); } } static class GoogleNavigationWalkingApp extends GoogleNavigationApp { GoogleNavigationWalkingApp() { - super(R.string.cache_menu_navigation_walk, "&mode=w"); + super(R.string.cache_menu_navigation_walk, "w"); } } static class GoogleNavigationDrivingApp extends GoogleNavigationApp { GoogleNavigationDrivingApp() { - super(R.string.cache_menu_navigation_drive, "&mode=d"); + super(R.string.cache_menu_navigation_drive, "d"); + } + } + + static class GoogleNavigationBikeApp extends GoogleNavigationApp { + GoogleNavigationBikeApp() { + super(R.string.cache_menu_navigation_bike, "b"); } } }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java index 5545936..7316d4c 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java @@ -11,6 +11,7 @@ import cgeo.geocaching.apps.App; import cgeo.geocaching.apps.cache.CacheBeaconApp; import cgeo.geocaching.apps.cache.GccApp; import cgeo.geocaching.apps.cache.WhereYouGoApp; +import cgeo.geocaching.apps.cache.navi.GoogleNavigationApp.GoogleNavigationBikeApp; import cgeo.geocaching.apps.cache.navi.GoogleNavigationApp.GoogleNavigationDrivingApp; import cgeo.geocaching.apps.cache.navi.GoogleNavigationApp.GoogleNavigationWalkingApp; import cgeo.geocaching.geopoint.Geopoint; @@ -59,6 +60,10 @@ public final class NavigationAppFactory extends AbstractAppFactory { */ GOOGLE_NAVIGATION_WALK(new GoogleNavigationWalkingApp(), 12), /** + * Google Navigation in walking mode + */ + GOOGLE_NAVIGATION_BIKE(new GoogleNavigationBikeApp(), 21), + /** * Google Maps Directions */ GOOGLE_MAPS_DIRECTIONS(new GoogleMapsDirectionApp(), 13), @@ -134,7 +139,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { builder.setTitle(R.string.cache_menu_navigate); final List<NavigationAppsEnum> items = new ArrayList<NavigationAppFactory.NavigationAppsEnum>(); final int defaultNavigationTool = Settings.getDefaultNavigationTool(); - for (NavigationAppsEnum navApp : getInstalledNavigationApps()) { + for (final NavigationAppsEnum navApp : getInstalledNavigationApps()) { if ((showInternalMap || !(navApp.app instanceof InternalMap)) && (showDefaultNavigation || defaultNavigationTool != navApp.id)) { boolean add = false; @@ -161,8 +166,8 @@ public final class NavigationAppFactory extends AbstractAppFactory { builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { - NavigationAppsEnum selectedItem = adapter.getItem(item); - App app = selectedItem.app; + final NavigationAppsEnum selectedItem = adapter.getItem(item); + final App app = selectedItem.app; if (cache != null) { navigateCache(activity, cache, app); } @@ -185,7 +190,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { */ public static List<NavigationAppsEnum> getInstalledNavigationApps() { final List<NavigationAppsEnum> installedNavigationApps = new ArrayList<NavigationAppsEnum>(); - for (NavigationAppsEnum appEnum : NavigationAppsEnum.values()) { + for (final NavigationAppsEnum appEnum : NavigationAppsEnum.values()) { if (appEnum.app.isInstalled()) { installedNavigationApps.add(appEnum); } @@ -200,7 +205,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { */ public static List<NavigationAppsEnum> getInstalledDefaultNavigationApps() { final List<NavigationAppsEnum> installedNavigationApps = new ArrayList<NavigationAppsEnum>(); - for (NavigationAppsEnum appEnum : NavigationAppsEnum.values()) { + for (final NavigationAppsEnum appEnum : NavigationAppsEnum.values()) { if (appEnum.app.isInstalled() && appEnum.app.isDefaultNavigationApp()) { installedNavigationApps.add(appEnum); } @@ -225,9 +230,9 @@ public final class NavigationAppFactory extends AbstractAppFactory { * @param menu */ public static void addMenuItems(final Menu menu, final Geocache cache) { - for (NavigationAppsEnum navApp : getInstalledNavigationApps()) { + for (final NavigationAppsEnum navApp : getInstalledNavigationApps()) { if (navApp.app instanceof CacheNavigationApp) { - CacheNavigationApp cacheApp = (CacheNavigationApp) navApp.app; + final CacheNavigationApp cacheApp = (CacheNavigationApp) navApp.app; if (cacheApp.isEnabled(cache)) { menu.add(0, MENU_ITEM_OFFSET + navApp.id, 0, navApp.app.getName()); } @@ -236,9 +241,9 @@ public final class NavigationAppFactory extends AbstractAppFactory { } public static void addMenuItems(final Menu menu, final Waypoint waypoint) { - for (NavigationAppsEnum navApp : getInstalledNavigationApps()) { + for (final NavigationAppsEnum navApp : getInstalledNavigationApps()) { if (navApp.app instanceof WaypointNavigationApp) { - WaypointNavigationApp waypointApp = (WaypointNavigationApp) navApp.app; + final WaypointNavigationApp waypointApp = (WaypointNavigationApp) navApp.app; if (waypointApp.isEnabled(waypoint)) { menu.add(0, MENU_ITEM_OFFSET + navApp.id, 0, navApp.app.getName()); } @@ -262,7 +267,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { private static void navigateCache(Activity activity, Geocache cache, App app) { if (app instanceof CacheNavigationApp) { - CacheNavigationApp cacheApp = (CacheNavigationApp) app; + final CacheNavigationApp cacheApp = (CacheNavigationApp) app; cacheApp.navigate(activity, cache); } } @@ -275,21 +280,21 @@ public final class NavigationAppFactory extends AbstractAppFactory { private static void navigateWaypoint(Activity activity, Waypoint waypoint, App app) { if (app instanceof WaypointNavigationApp) { - WaypointNavigationApp waypointApp = (WaypointNavigationApp) app; + final WaypointNavigationApp waypointApp = (WaypointNavigationApp) app; waypointApp.navigate(activity, waypoint); } } private static void navigateGeopoint(Activity activity, Geopoint destination, App app) { if (app instanceof GeopointNavigationApp) { - GeopointNavigationApp geopointApp = (GeopointNavigationApp) app; + final GeopointNavigationApp geopointApp = (GeopointNavigationApp) app; geopointApp.navigate(activity, destination); } } private static App getAppFromMenuItem(MenuItem item) { final int id = item.getItemId(); - for (NavigationAppsEnum navApp : NavigationAppsEnum.values()) { + for (final NavigationAppsEnum navApp : NavigationAppsEnum.values()) { if (MENU_ITEM_OFFSET + navApp.id == id) { return navApp.app; } @@ -362,7 +367,7 @@ public final class NavigationAppFactory extends AbstractAppFactory { private static App getNavigationAppForId(final int navigationAppId) { final List<NavigationAppsEnum> installedNavigationApps = getInstalledNavigationApps(); - for (NavigationAppsEnum navigationApp : installedNavigationApps) { + for (final NavigationAppsEnum navigationApp : installedNavigationApps) { if (navigationApp.id == navigationAppId) { return navigationApp.app; } diff --git a/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java b/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java index 012b94f..e2c0828 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/StreetviewApp.java @@ -4,6 +4,7 @@ import cgeo.geocaching.R; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.utils.ProcessUtils; import android.app.Activity; import android.content.ActivityNotFoundException; @@ -12,13 +13,16 @@ import android.net.Uri; class StreetviewApp extends AbstractPointNavigationApp { + private static final String PACKAGE_NAME_STREET_VIEW = "com.google.android.street"; + private static final boolean INSTALLED = ProcessUtils.isInstalled(PACKAGE_NAME_STREET_VIEW); + StreetviewApp() { super(getString(R.string.cache_menu_streetview), null); } @Override public boolean isInstalled() { - return true; + return INSTALLED; } @Override @@ -26,7 +30,7 @@ class StreetviewApp extends AbstractPointNavigationApp { try { activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("google.streetview:cbll=" + point.getLatitude() + "," + point.getLongitude()))); - } catch (ActivityNotFoundException e) { + } catch (final ActivityNotFoundException e) { ActivityMixin.showToast(activity, cgeoapplication.getInstance().getString(R.string.err_application_no)); } } diff --git a/main/src/cgeo/geocaching/cgData.java b/main/src/cgeo/geocaching/cgData.java index 2ef5b27..6f535dc 100644 --- a/main/src/cgeo/geocaching/cgData.java +++ b/main/src/cgeo/geocaching/cgData.java @@ -67,8 +67,8 @@ public class cgData { "difficulty", "direction", "distance", "terrain", "latlon", "location", "elevation", "personal_note", "shortdesc", // 24 25 26 27 28 29 30 31 32 "favourite_cnt", "rating", "votes", "myvote", "disabled", "archived", "members", "found", "favourite", - // 33 34 35 36 37 38 39 40 41 42 - "inventoryunknown", "onWatchlist", "reliable_latlon", "coordsChanged", "latitude", "longitude", "finalDefined", "_id", "inventorycoins", "inventorytags" + // 33 34 35 36 37 38 39 40 41 42 43 + "inventoryunknown", "onWatchlist", "reliable_latlon", "coordsChanged", "latitude", "longitude", "finalDefined", "_id", "inventorycoins", "inventorytags", "logPasswordRequired" // reason is replaced by listId in Geocache }; @@ -86,7 +86,7 @@ public class cgData { private static int[] cacheColumnIndex; private static CacheCache cacheCache = new CacheCache(); private static SQLiteDatabase database = null; - private static final int dbVersion = 67; + private static final int dbVersion = 68; public static final int customListIdOffset = 10; private static final String dbName = "data"; private static final String dbTableCaches = "cg_caches"; @@ -146,6 +146,7 @@ public class cgData { + "onWatchlist integer default 0, " + "coordsChanged integer default 0, " + "finalDefined integer default 0" + + "logPasswordRequired integer default 0" + "); "; private static final String dbCreateLists = "" + "create table " + dbTableLists + " (" @@ -685,6 +686,15 @@ public class cgData { } } + // Introduces logPasswordRequired on caches + if (oldVersion < 68) { + try { + db.execSQL("alter table " + dbTableCaches + " add column logPasswordRequired integer default 0"); + } catch (Exception e) { + Log.e("Failed to upgrade to ver. 68", e); + + } + } } db.setTransactionSuccessful(); @@ -1052,6 +1062,7 @@ public class cgData { values.put("onWatchlist", cache.isOnWatchlist() ? 1 : 0); values.put("coordsChanged", cache.hasUserModifiedCoords() ? 1 : 0); values.put("finalDefined", cache.hasFinalDefined() ? 1 : 0); + values.put("logPasswordRequired", cache.isLogPasswordRequired() ? 1 : 0); init(); @@ -1643,6 +1654,7 @@ public class cgData { cache.setReliableLatLon(cursor.getInt(cacheColumnIndex[35]) > 0); cache.setUserModifiedCoords(cursor.getInt(cacheColumnIndex[36]) > 0); cache.setFinalDefined(cursor.getInt(cacheColumnIndex[39]) > 0); + cache.setLogPasswordRequired(cursor.getInt(cacheColumnIndex[43]) > 0); Log.d("Loading " + cache.toString() + " (" + cache.getListId() + ") from DB"); diff --git a/main/src/cgeo/geocaching/cgeocaches.java b/main/src/cgeo/geocaching/cgeocaches.java index 40dddd4..e321d59 100644 --- a/main/src/cgeo/geocaching/cgeocaches.java +++ b/main/src/cgeo/geocaching/cgeocaches.java @@ -177,7 +177,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity showFooterMoreCaches(); if (search != null && search.getError() == StatusCode.UNAPPROVED_LICENSE) { - AlertDialog.Builder dialog = new AlertDialog.Builder(this); + final AlertDialog.Builder dialog = new AlertDialog.Builder(this); dialog.setTitle(res.getString(R.string.license)); dialog.setMessage(res.getString(R.string.err_license)); dialog.setCancelable(true); @@ -198,7 +198,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } }); - AlertDialog alert = dialog.create(); + final AlertDialog alert = dialog.create(); alert.show(); } else if (search != null && search.getError() != null) { showToast(res.getString(R.string.err_download_fail) + ' ' + search.getError().getErrorString(res) + '.'); @@ -211,7 +211,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } setAdapterCurrentCoordinates(false); - } catch (Exception e) { + } catch (final Exception e) { showToast(res.getString(R.string.err_detail_cache_find_any)); Log.e("cgeocaches.loadCachesHandler", e); @@ -225,14 +225,14 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity try { hideLoading(); showProgress(false); - } catch (Exception e2) { + } catch (final Exception e2) { Log.e("cgeocaches.loadCachesHandler.2", e2); } adapter.setSelectMode(false); } - private Handler loadCachesHandler = new LoadCachesHandler(this); + private final Handler loadCachesHandler = new LoadCachesHandler(this); private static class LoadCachesHandler extends WeakReferenceHandler<cgeocaches> { @@ -277,7 +277,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } protected void updateTitle() { - ArrayList<Integer> numbers = new ArrayList<Integer>(); + final ArrayList<Integer> numbers = new ArrayList<Integer>(); if (adapter.isFiltered()) { numbers.add(adapter.getCount()); } @@ -292,7 +292,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } } - private Handler loadDetailsHandler = new Handler() { + private final Handler loadDetailsHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -303,8 +303,8 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity adapter.notifyDataSetChanged(); - int secondsElapsed = (int) ((System.currentTimeMillis() - detailProgressTime) / 1000); - int minutesRemaining = ((detailTotal - detailProgress) * secondsElapsed / ((detailProgress > 0) ? detailProgress : 1) / 60); + final int secondsElapsed = (int) ((System.currentTimeMillis() - detailProgressTime) / 1000); + final int minutesRemaining = ((detailTotal - detailProgress) * secondsElapsed / ((detailProgress > 0) ? detailProgress : 1) / 60); progress.setProgress(detailProgress); if (minutesRemaining < 1) { @@ -340,7 +340,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity /** * TODO Possibly parts should be a Thread not a Handler */ - private Handler downloadFromWebHandler = new Handler() { + private final Handler downloadFromWebHandler = new Handler() { @Override public void handleMessage(Message msg) { setAdapter(); @@ -375,7 +375,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } } }; - private Handler clearOfflineLogsHandler = new Handler() { + private final Handler clearOfflineLogsHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -391,7 +391,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } }; - private Handler importGpxAttachementFinishedHandler = new Handler() { + private final Handler importGpxAttachementFinishedHandler = new Handler() { @Override public void handleMessage(Message msg) { refreshCurrentList(); @@ -413,7 +413,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity // get parameters Bundle extras = getIntent().getExtras(); if (extras != null) { - Object typeObject = extras.get(Intents.EXTRA_LIST_TYPE); + final Object typeObject = extras.get(Intents.EXTRA_LIST_TYPE); type = (typeObject instanceof CacheListType) ? (CacheListType) typeObject : CacheListType.OFFLINE; coords = extras.getParcelable(Intents.EXTRA_COORDS); } @@ -478,24 +478,14 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } private void importGpxAttachement() { - new AlertDialog.Builder(this) - .setTitle(res.getString(R.string.gpx_import_title)) - .setMessage(res.getString(R.string.gpx_import_confirm)) - .setCancelable(false) - .setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - new GPXImporter(cgeocaches.this, listId, importGpxAttachementFinishedHandler).importGPX(); - } - }) - .setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }) - .create() - .show(); + new StoredList.UserInterface(this).promptForListSelection(R.string.gpx_import_select_list_title, new RunnableWithArgument<Integer>() { + + @Override + public void run(Integer listId) { + new GPXImporter(cgeocaches.this, listId, importGpxAttachementFinishedHandler).importGPX(); + switchListById(listId); + } + }, true, 0); } @Override @@ -514,7 +504,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity // refresh standard list if it has changed (new caches downloaded) if (type == CacheListType.OFFLINE && listId >= StoredList.STANDARD_LIST_ID && search != null) { - SearchResult newSearch = cgData.getBatchOfStoredCaches(coords, Settings.getCacheType(), listId); + final SearchResult newSearch = cgData.getBatchOfStoredCaches(coords, Settings.getCacheType(), listId); if (newSearch != null && newSearch.getTotal() != search.getTotal()) { refreshCurrentList(); } @@ -549,7 +539,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity 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) { - SubMenu subMenu = menu.addSubMenu(0, SUBMENU_MANAGE_OFFLINE, 0, res.getString(R.string.caches_manage)).setIcon(R.drawable.ic_menu_save); + 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 @@ -566,7 +556,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity subMenu.add(0, MENU_EXPORT, 0, res.getString(R.string.export)); // export caches } else { if (type == CacheListType.HISTORY) { - SubMenu subMenu = menu.addSubMenu(0, SUBMENU_MANAGE_HISTORY, 0, res.getString(R.string.caches_manage)).setIcon(R.drawable.ic_menu_save); + 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_REMOVE_FROM_HISTORY, 0, res.getString(R.string.cache_clear_history)); // remove from history subMenu.add(0, MENU_EXPORT, 0, res.getString(R.string.export)); // export caches subMenu.add(0, MENU_CLEAR_OFFLINE_LOGS, 0, res.getString(R.string.caches_clear_offlinelogs)); @@ -579,7 +569,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity navigationMenu = CacheListAppFactory.addMenuItems(menu, this, res); if (type == CacheListType.OFFLINE) { - SubMenu subMenu = menu.addSubMenu(0, SUBMENU_MANAGE_LISTS, 0, res.getString(R.string.list_menu)).setIcon(R.drawable.ic_menu_more); + 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)); @@ -664,7 +654,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity 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); - } catch (Exception e) { + } catch (final Exception e) { Log.e("cgeocaches.onPrepareOptionsMenu", e); } @@ -672,7 +662,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } private boolean containsEvents() { - for (Geocache cache : adapter.getCheckedOrAllCaches()) { + for (final Geocache cache : adapter.getCheckedOrAllCaches()) { if (cache.isEventCache()) { return true; } @@ -681,7 +671,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } private boolean containsOfflineLogs() { - for (Geocache cache : adapter.getCheckedOrAllCaches()) { + for (final Geocache cache : adapter.getCheckedOrAllCaches()) { if (cache.isLogOffline()) { return true; } @@ -694,7 +684,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity if (menuItem == null) { return; } - boolean hasSelection = adapter != null && adapter.getCheckedCount() > 0; + final boolean hasSelection = adapter != null && adapter.getCheckedCount() > 0; if (hasSelection) { menuItem.setTitle(res.getString(resIdSelection) + " (" + adapter.getCheckedCount() + ")"); } else { @@ -704,7 +694,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity @Override public boolean onOptionsItemSelected(MenuItem item) { - int itemId = item.getItemId(); + final int itemId = item.getItemId(); switch (itemId) { case MENU_SWITCH_SELECT_MODE: adapter.switchSelectMode(); @@ -794,7 +784,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity public void deletePastEvents() { final List<Geocache> deletion = new ArrayList<Geocache>(); - for (Geocache cache : adapter.getCheckedOrAllCaches()) { + for (final Geocache cache : adapter.getCheckedOrAllCaches()) { if (cache.isEventCache()) { final Date eventDate = cache.getHiddenDate(); if (DateUtils.daysSince(eventDate.getTime()) > 0) { @@ -840,7 +830,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity AdapterContextMenuInfo adapterInfo = null; try { adapterInfo = (AdapterContextMenuInfo) info; - } catch (Exception e) { + } catch (final Exception e) { Log.w("cgeocaches.onCreateContextMenu", e); } @@ -897,7 +887,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity AdapterContextMenuInfo adapterInfo = null; try { adapterInfo = (AdapterContextMenuInfo) info; - } catch (Exception e) { + } catch (final Exception e) { Log.w("cgeocaches.onContextItemSelected", e); } @@ -1106,7 +1096,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity showProgress(false); - int etaTime = ((detailTotal * 25) / 60); + final int etaTime = ((detailTotal * 25) / 60); String message; if (etaTime < 1) { message = res.getString(R.string.caches_downloading) + " " + res.getString(R.string.caches_eta_ltm); @@ -1124,7 +1114,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } public void removeFromHistoryCheck() { - AlertDialog.Builder dialog = new AlertDialog.Builder(this); + final AlertDialog.Builder dialog = new AlertDialog.Builder(this); dialog.setCancelable(true); dialog.setTitle(res.getString(R.string.caches_removing_from_history)); dialog.setMessage((adapter != null && adapter.getCheckedCount() > 0) ? res.getString(R.string.cache_remove_from_history) @@ -1143,7 +1133,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } }); - AlertDialog alert = dialog.create(); + final AlertDialog alert = dialog.create(); alert.show(); } @@ -1153,7 +1143,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity for (int i = 0; i < geocodes.length; i++) { geocodes[i] = caches.get(i).getGeocode(); } - Bundle b = new Bundle(); + final Bundle b = new Bundle(); b.putStringArray(Intents.EXTRA_CACHELIST, geocodes); getSupportLoaderManager().initLoader(CacheListLoaderType.REMOVE_FROM_HISTORY.ordinal(), b, this); } @@ -1169,7 +1159,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } public void dropStored(final boolean removeListAfterwards) { - AlertDialog.Builder dialog = new AlertDialog.Builder(this); + final AlertDialog.Builder dialog = new AlertDialog.Builder(this); dialog.setCancelable(true); dialog.setTitle(res.getString(R.string.caches_drop_stored)); @@ -1194,7 +1184,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } }); - AlertDialog alert = dialog.create(); + final AlertDialog alert = dialog.create(); alert.show(); } @@ -1232,7 +1222,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity removeGeoAndDir(); final List<Geocache> cachesWithStaticMaps = new ArrayList<Geocache>(this.caches.size()); - for (Geocache cache : this.caches) { + for (final Geocache cache : this.caches) { if (Settings.isStoreOfflineMaps() && cache.hasStaticMap()) { cachesWithStaticMaps.add(cache); continue; @@ -1244,7 +1234,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } } - for (Geocache cache : cachesWithStaticMaps) { + for (final Geocache cache : cachesWithStaticMaps) { if (!refreshCache(cache)) { break; } @@ -1276,7 +1266,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } Log.i("Waiting for next cache " + delay + " ms"); - } catch (Exception e) { + } catch (final Exception e) { Log.e("cgeocaches.LoadDetailsThread.sleep", e); } } @@ -1291,10 +1281,10 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity handler.sendEmptyMessage(cacheList.indexOf(cache)); yield(); - } catch (InterruptedException e) { + } catch (final InterruptedException e) { Log.i(e.getMessage()); return false; - } catch (Exception e) { + } catch (final Exception e) { Log.e("cgeocaches.LoadDetailsThread", e); } @@ -1334,7 +1324,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity deviceCode = ""; } final Parameters params = new Parameters("code", deviceCode); - HttpResponse responseFromWeb = Network.getRequest("http://send2.cgeo.org/read.html", params); + final HttpResponse responseFromWeb = Network.getRequest("http://send2.cgeo.org/read.html", params); if (responseFromWeb != null && responseFromWeb.getStatusLine().getStatusCode() == 200) { final String response = Network.getResponseData(responseFromWeb); @@ -1374,7 +1364,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity sleep(500); //Cache was loaded 0.5s times = 0; } - } catch (InterruptedException e) { + } catch (final InterruptedException e) { Log.e("cgeocaches.LoadFromWebThread.sleep", e); } } @@ -1476,7 +1466,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity return; } - StoredList list = cgData.getList(id); + final StoredList list = cgData.getList(id); if (list == null) { return; } @@ -1565,8 +1555,8 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } // apply filter settings (if there's a filter) - Set<String> geocodes = new HashSet<String>(); - for (Geocache cache : adapter.getFilteredList()) { + final Set<String> geocodes = new HashSet<String>(); + for (final Geocache cache : adapter.getFilteredList()) { geocodes.add(cache.getGeocode()); } @@ -1641,7 +1631,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity private void setDateComparatorForEventList() { if (CollectionUtils.isNotEmpty(cacheList)) { boolean eventsOnly = true; - for (Geocache cache : cacheList) { + for (final Geocache cache : cacheList) { if (!cache.isEventCache()) { eventsOnly = false; break; @@ -1726,7 +1716,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity if (type >= CacheListLoaderType.values().length) { throw new IllegalArgumentException("invalid loader type " + type); } - CacheListLoaderType enumType = CacheListLoaderType.values()[type]; + final CacheListLoaderType enumType = CacheListLoaderType.values()[type]; AbstractSearchLoader loader = null; switch (enumType) { case OFFLINE: diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java index 3319fe4..c5a083c 100644 --- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java +++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java @@ -4,6 +4,7 @@ import cgeo.geocaching.ICache; import cgeo.geocaching.R; 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.ISearchByViewPort; import cgeo.geocaching.connector.gc.GCConnector; @@ -12,6 +13,10 @@ import cgeo.geocaching.connector.oc.OCApiConnector.ApiSupport; import cgeo.geocaching.connector.oc.OCApiLiveConnector; import cgeo.geocaching.connector.oc.OCConnector; import cgeo.geocaching.connector.ox.OXConnector; +import cgeo.geocaching.connector.trackable.GeokretyConnector; +import cgeo.geocaching.connector.trackable.TrackableConnector; +import cgeo.geocaching.connector.trackable.TravelBugConnector; +import cgeo.geocaching.connector.trackable.UnknownTrackableConnector; import cgeo.geocaching.geopoint.Viewport; import org.apache.commons.lang3.StringUtils; @@ -21,9 +26,9 @@ import java.util.List; public final class ConnectorFactory { private static final UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector(); - private static final IConnector[] connectors = new IConnector[] { + private static final IConnector[] CONNECTORS = new IConnector[] { GCConnector.getInstance(), - new OCApiLiveConnector("Opencaching.de", "www.opencaching.de", "OC", R.string.oc_de_okapi_consumer_key, R.string.oc_de_okapi_consumer_secret, ApiSupport.current), + new OCApiLiveConnector("opencaching.de", "www.opencaching.de", "OC", R.string.oc_de_okapi_consumer_key, R.string.oc_de_okapi_consumer_secret, ApiSupport.current), new OCConnector("OpenCaching.CZ", "www.opencaching.cz", "OZ"), new OCApiConnector("OpenCaching.CO.UK", "www.opencaching.org.uk", "OK", "arU4okouc4GEjMniE2fq", ApiSupport.oldapi), new OCConnector("OpenCaching.ES", "www.opencachingspain.es", "OC"), @@ -40,21 +45,28 @@ public final class ConnectorFactory { UNKNOWN_CONNECTOR // the unknown connector MUST be the last one }; + public static final UnknownTrackableConnector UNKNOWN_TRACKABLE_CONNECTOR = new UnknownTrackableConnector(); + private static final TrackableConnector[] TRACKABLE_CONNECTORS = new TrackableConnector[] { + new GeokretyConnector(), // GK must be first, as it overlaps with the secret codes of travel bugs + TravelBugConnector.getInstance(), + UNKNOWN_TRACKABLE_CONNECTOR // must be last + }; + private static final ISearchByViewPort[] searchByViewPortConns; private static final ISearchByCenter[] searchByCenterConns; static { - List<ISearchByViewPort> vpConns = new ArrayList<ISearchByViewPort>(); - for (IConnector conn : connectors) { + final List<ISearchByViewPort> vpConns = new ArrayList<ISearchByViewPort>(); + for (final IConnector conn : CONNECTORS) { if (conn instanceof ISearchByViewPort) { vpConns.add((ISearchByViewPort) conn); } } searchByViewPortConns = vpConns.toArray(new ISearchByViewPort[vpConns.size()]); - List<ISearchByCenter> centerConns = new ArrayList<ISearchByCenter>(); - for (IConnector conn : connectors) { + final List<ISearchByCenter> centerConns = new ArrayList<ISearchByCenter>(); + for (final IConnector conn : CONNECTORS) { // GCConnector is handled specially, omit it here! if (conn instanceof ISearchByCenter && !(conn instanceof GCConnector)) { centerConns.add((ISearchByCenter) conn); @@ -64,18 +76,28 @@ public final class ConnectorFactory { } public static IConnector[] getConnectors() { - return connectors; + return CONNECTORS; } public static ISearchByCenter[] getSearchByCenterConnectors() { return searchByCenterConns; } + public static ILogin[] getActiveLiveConnectors() { + final List<ILogin> liveConns = new ArrayList<ILogin>(); + for (final IConnector conn : CONNECTORS) { + if (conn instanceof ILogin && conn.isActivated()) { + liveConns.add((ILogin) conn); + } + } + return liveConns.toArray(new ILogin[liveConns.size()]); + } + public static boolean canHandle(final String geocode) { if (isInvalidGeocode(geocode)) { return false; } - for (IConnector connector : connectors) { + for (final IConnector connector : CONNECTORS) { if (connector.canHandle(geocode)) { return true; } @@ -87,8 +109,17 @@ public final class ConnectorFactory { return getConnector(cache.getGeocode()); } - public static IConnector getConnector(Trackable trackable) { - return getConnector(trackable.getGeocode()); + public static TrackableConnector getConnector(Trackable trackable) { + return getTrackableConnector(trackable.getGeocode()); + } + + public static TrackableConnector getTrackableConnector(String geocode) { + for (final TrackableConnector connector : TRACKABLE_CONNECTORS) { + if (connector.canHandleTrackable(geocode)) { + return connector; + } + } + return UNKNOWN_TRACKABLE_CONNECTOR; // avoid null checks by returning a non implementing connector } public static IConnector getConnector(final String geocodeInput) { @@ -97,12 +128,12 @@ public final class ConnectorFactory { if (isInvalidGeocode(geocode)) { return UNKNOWN_CONNECTOR; } - for (IConnector connector : connectors) { + for (final IConnector connector : CONNECTORS) { if (connector.canHandle(geocode)) { return connector; } } - // in case of errors, take UNKNOWN + // in case of errors, take UNKNOWN to avoid null checks everywhere return UNKNOWN_CONNECTOR; } @@ -113,10 +144,10 @@ public final class ConnectorFactory { /** @see ISearchByViewPort#searchByViewport */ public static SearchResult searchByViewport(final Viewport viewport, final String[] tokens) { - SearchResult result = new SearchResult(); - for (ISearchByViewPort vpconn : searchByViewPortConns) { + final SearchResult result = new SearchResult(); + for (final ISearchByViewPort vpconn : searchByViewPortConns) { if (vpconn.isActivated()) { - SearchResult temp = vpconn.searchByViewport(viewport, tokens); + final SearchResult temp = vpconn.searchByViewport(viewport, tokens); if (temp != null) { result.addGeocodes(temp.getGeocodes()); } @@ -126,8 +157,8 @@ public final class ConnectorFactory { } public static String getGeocodeFromURL(final String url) { - for (IConnector connector : connectors) { - String geocode = connector.getGeocodeFromUrl(url); + for (final IConnector connector : CONNECTORS) { + final String geocode = connector.getGeocodeFromUrl(url); if (StringUtils.isNotBlank(geocode)) { return geocode; } @@ -135,4 +166,8 @@ public final class ConnectorFactory { return null; } + public static TrackableConnector[] getTrackableConnectors() { + return TRACKABLE_CONNECTORS; + } + } diff --git a/main/src/cgeo/geocaching/connector/ILoggingManager.java b/main/src/cgeo/geocaching/connector/ILoggingManager.java index f0029f9..c5586b3 100644 --- a/main/src/cgeo/geocaching/connector/ILoggingManager.java +++ b/main/src/cgeo/geocaching/connector/ILoggingManager.java @@ -11,10 +11,23 @@ import java.util.List; public interface ILoggingManager { + /** + * Post a log for a cache online + * + * @param cache + * @param logType + * @param date + * @param log + * @param logPassword + * optional, maybe null + * @param trackableLogs + * @return + */ LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, + String logPassword, List<TrackableLog> trackableLogs); ImageResult postLogImage(String logId, diff --git a/main/src/cgeo/geocaching/connector/NoLoggingManager.java b/main/src/cgeo/geocaching/connector/NoLoggingManager.java index bfea4ca..04a73c1 100644 --- a/main/src/cgeo/geocaching/connector/NoLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/NoLoggingManager.java @@ -19,7 +19,7 @@ public class NoLoggingManager implements ILoggingManager { } @Override - public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, List<TrackableLog> trackableLogs) { + public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, String logPassword, List<TrackableLog> trackableLogs) { return new LogResult(StatusCode.LOG_POST_ERROR, ""); } diff --git a/main/src/cgeo/geocaching/connector/capability/ILogin.java b/main/src/cgeo/geocaching/connector/capability/ILogin.java new file mode 100644 index 0000000..4a839c8 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/capability/ILogin.java @@ -0,0 +1,57 @@ +package cgeo.geocaching.connector.capability; + +import cgeo.geocaching.connector.IConnector; + +import android.content.Context; +import android.os.Handler; + +public interface ILogin extends IConnector { + + + /** + * Contacts the server the connector belongs to + * and verifies/establishes authentication and + * retrieves information about the current user + * (Name, found caches) if applicable. + * + * @param handler + * Handler to receive status feedback + * @param fromActivity + * Calling activity context + * @return true in case of success, false in case of failure + */ + boolean login(Handler handler, Context fromActivity); + + /** + * Returns the status of the last {@link}login() request + * + * @return + */ + boolean isLoggedIn(); + + /** + * User-centered string describing the current login/connection status + * + * @return + */ + String getLoginStatusString(); + + /** + * Name the user has in this connector or empty string if not applicable + * It might be necessary to execute login before this information is valid. + * + * @return + */ + String getUserName(); + + /** + * Number of caches the user has found in this connector + * Normally retrieved/updated with (@see login). + * Might be out dated as changes on the connectors site + * are generally not notified. + * + * @return + */ + int getCachesFound(); + +} diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index 9b2a84a..33bb1ce 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -5,9 +5,12 @@ import cgeo.geocaching.ICache; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; import cgeo.geocaching.Settings; +import cgeo.geocaching.SettingsActivity; import cgeo.geocaching.cgData; +import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.connector.ILoggingManager; +import cgeo.geocaching.connector.capability.ILogin; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.capability.ISearchByGeocode; import cgeo.geocaching.connector.capability.ISearchByViewPort; @@ -21,10 +24,12 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import android.app.Activity; +import android.content.Context; +import android.os.Handler; import java.util.regex.Pattern; -public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort { +public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ILogin { private static final String CACHE_URL_SHORT = "http://coord.info/"; // Double slash is used to force open in browser @@ -35,6 +40,11 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, */ private static final Pattern gpxZipFilePattern = Pattern.compile("((\\d{7,})|(pocketquery))" + "(_.+)?" + "\\.zip", Pattern.CASE_INSENSITIVE); + /** + * Pattern for GC codes + */ + private final static Pattern PATTERN_GC_CODE = Pattern.compile("GC[0-9A-Z]+", Pattern.CASE_INSENSITIVE); + private GCConnector() { // singleton } @@ -55,7 +65,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, if (geocode == null) { return false; } - return GCConstants.PATTERN_GC_CODE.matcher(geocode).matches() || GCConstants.PATTERN_TB_CODE.matcher(geocode).matches(); + return GCConnector.PATTERN_GC_CODE.matcher(geocode).matches(); } @Override @@ -105,7 +115,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, @Override public String getName() { - return "GeoCaching.com"; + return "geocaching.com"; } @Override @@ -282,4 +292,46 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } return R.drawable.marker; } + + @Override + public boolean login(Handler handler, Context fromActivity) { + // login + final StatusCode status = Login.login(); + + if (status == StatusCode.NO_ERROR) { + cgeoapplication.getInstance().firstRun = false; + Login.detectGcCustomDate(); + } + + if (cgeoapplication.getInstance().showLoginToast && handler != null) { + handler.sendMessage(handler.obtainMessage(0, status)); + cgeoapplication.getInstance().showLoginToast = false; + + // invoke settings activity to insert login details + if (status == StatusCode.NO_LOGIN_INFO_STORED && fromActivity != null) { + SettingsActivity.startActivity(fromActivity); + } + } + return status == StatusCode.NO_ERROR; + } + + @Override + public String getUserName() { + return Login.getActualUserName(); + } + + @Override + public int getCachesFound() { + return Login.getActualCachesFound(); + } + + @Override + public String getLoginStatusString() { + return Login.getActualStatus(); + } + + @Override + public boolean isLoggedIn() { + return Login.isActualLoginStatus(); + } } diff --git a/main/src/cgeo/geocaching/connector/gc/GCConstants.java b/main/src/cgeo/geocaching/connector/gc/GCConstants.java index b4f5845..f2e2e69 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java @@ -84,6 +84,7 @@ public final class GCConstants { public final static String ERROR_TB_DOES_NOT_EXIST = "does not exist in the system"; public final static String ERROR_TB_ELEMENT_EXCEPTION = "ElementNotFound Exception"; public final static String ERROR_TB_ARITHMETIC_OVERFLOW = "operation resulted in an overflow"; + public final static String ERROR_TB_NOT_ACTIVATED = "hasn't been activated"; /** * some parts of the webpage don't correctly encode the name, therefore this pattern must be checked with a * trackable name that needs HTML encoding @@ -157,12 +158,6 @@ public final class GCConstants { public final static Pattern PATTERN_VIEWSTATES = Pattern.compile("id=\"__VIEWSTATE(\\d*)\"[^(value)]+value=\"([^\"]+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); public final static Pattern PATTERN_USERTOKEN = Pattern.compile("userToken\\s*=\\s*'([^']+)'"); - /** - * Patterns for GC and TB codes - */ - public final static Pattern PATTERN_GC_CODE = Pattern.compile("GC[0-9A-Z]+", Pattern.CASE_INSENSITIVE); - public final static Pattern PATTERN_TB_CODE = Pattern.compile("TB[0-9A-Z]+", Pattern.CASE_INSENSITIVE); - /** Live Map since 14.02.2012 */ public final static Pattern PATTERN_USERSESSION = Pattern.compile("UserSession\\('([^']+)'"); public final static Pattern PATTERN_SESSIONTOKEN = Pattern.compile("sessionToken:'([^']+)'"); @@ -194,7 +189,7 @@ public final class GCConstants { */ public static long gccodeToGCId(final String gccode) { long base = GC_BASE31; - String geocodeWO = gccode.substring(2).toUpperCase(Locale.US); + final String geocodeWO = gccode.substring(2).toUpperCase(Locale.US); if ((geocodeWO.length() < 4) || (geocodeWO.length() == 4 && SEQUENCE_GCID.indexOf(geocodeWO.charAt(0)) < 16)) { base = GC_BASE16; diff --git a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java index 4f2f8c4..dd150de 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java @@ -1,10 +1,10 @@ package cgeo.geocaching.connector.gc; import cgeo.geocaching.Geocache; +import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.R; import cgeo.geocaching.Settings; import cgeo.geocaching.TrackableLog; -import cgeo.geocaching.LogCacheActivity; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.connector.ILoggingManager; import cgeo.geocaching.connector.ImageResult; @@ -80,7 +80,7 @@ public class GCLoggingManager implements ILoggingManager, LoaderManager.LoaderCa } @Override - public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, List<TrackableLog> trackableLogs) { + public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, String logPassword, List<TrackableLog> trackableLogs) { try { final ImmutablePair<StatusCode, String> postResult = GCParser.postLog(cache.getGeocode(), cache.getCacheId(), viewstates, logType, diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index 15958ba..3e26eb2 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -28,10 +28,10 @@ import cgeo.geocaching.loaders.RecaptchaReceiver; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.ui.DirectionImage; -import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; +import cgeo.geocaching.utils.TextUtils; import ch.boye.httpclientandroidlib.HttpResponse; @@ -79,14 +79,14 @@ public abstract class GCParser { // recaptcha String recaptchaChallenge = null; if (showCaptcha) { - String recaptchaJsParam = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_RECAPTCHA, false, null); + final String recaptchaJsParam = TextUtils.getMatch(page, GCConstants.PATTERN_SEARCH_RECAPTCHA, false, null); if (recaptchaJsParam != null) { final Parameters params = new Parameters("k", recaptchaJsParam.trim()); final String recaptchaJs = Network.getResponseData(Network.getRequest("http://www.google.com/recaptcha/api/challenge", params)); if (StringUtils.isNotBlank(recaptchaJs)) { - recaptchaChallenge = BaseUtils.getMatch(recaptchaJs, GCConstants.PATTERN_SEARCH_RECAPTCHACHALLENGE, true, 1, null, true); + recaptchaChallenge = TextUtils.getMatch(recaptchaJs, GCConstants.PATTERN_SEARCH_RECAPTCHACHALLENGE, true, 1, null, true); } } if (thread != null && StringUtils.isNotBlank(recaptchaChallenge)) { @@ -109,7 +109,7 @@ public abstract class GCParser { page = page.substring(startPos); // cut on <table startPos = page.indexOf('>'); - int endPos = page.indexOf("ctl00_ContentBody_UnitTxt"); + final int endPos = page.indexOf("ctl00_ContentBody_UnitTxt"); if (startPos == -1 || endPos == -1) { Log.e("GCParser.parseSearch: ID \"ctl00_ContentBody_UnitTxt\" not found on page"); return null; @@ -122,7 +122,7 @@ public abstract class GCParser { for (int z = 1; z < rows_count; z++) { final Geocache cache = new Geocache(); - String row = rows[z]; + final String row = rows[z]; // check for cache type presence if (!row.contains("images/wpttypes")) { @@ -150,7 +150,7 @@ public abstract class GCParser { } } } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse GUID and/or Disabled Log.w("GCParser.parseSearch: Failed to parse GUID and/or Disabled data"); } @@ -160,21 +160,21 @@ public abstract class GCParser { continue; } - cache.setGeocode(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_GEOCODE, true, 1, cache.getGeocode(), true)); + cache.setGeocode(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_GEOCODE, true, 1, cache.getGeocode(), true)); // cache type - cache.setType(CacheType.getByPattern(BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_TYPE, true, 1, null, true))); + cache.setType(CacheType.getByPattern(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_TYPE, true, 1, null, true))); // cache direction - image if (Settings.getLoadDirImg()) { - final String direction = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 1, null, false); + final String direction = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 1, null, false); if (direction != null) { cache.setDirectionImg(direction); } } // cache distance - estimated distance for basic members - final String distance = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 2, null, false); + final String distance = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_DIRECTION_DISTANCE, false, 2, null, false); if (distance != null) { cache.setDistance(DistanceParser.parseDistance(distance, Settings.isUseMetricUnits())); } @@ -193,7 +193,7 @@ public abstract class GCParser { } // size - final String container = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_CONTAINER, false, 1, null, false); + final String container = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_CONTAINER, false, 1, null, false); cache.setSize(CacheSize.getById(container)); // cache inventory @@ -203,7 +203,7 @@ public abstract class GCParser { if (matcherTbs.groupCount() > 0) { try { cache.setInventoryItems(Integer.parseInt(matcherTbs.group(1))); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("Error parsing trackables count", e); } inventoryPre = matcherTbs.group(2); @@ -229,7 +229,7 @@ public abstract class GCParser { cache.setFound(row.contains("/images/icons/16/found.png")); // id - String result = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_ID, null); + String result = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_ID, null); if (null != result) { cache.setCacheId(result); cids.add(cache.getCacheId()); @@ -237,11 +237,11 @@ public abstract class GCParser { // favorite count try { - result = BaseUtils.getMatch(row, GCConstants.PATTERN_SEARCH_FAVORITE, false, 1, null, true); + result = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_FAVORITE, false, 1, null, true); if (null != result) { cache.setFavoritePoints(Integer.parseInt(result)); } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.w("GCParser.parseSearch: Failed to parse favorite count"); } @@ -250,11 +250,11 @@ public abstract class GCParser { // total caches found try { - String result = BaseUtils.getMatch(page, GCConstants.PATTERN_SEARCH_TOTALCOUNT, false, 1, null, true); + final String result = TextUtils.getMatch(page, GCConstants.PATTERN_SEARCH_TOTALCOUNT, false, 1, null, true); if (null != result) { searchResult.setTotal(Integer.parseInt(result)); } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.w("GCParser.parseSearch: Failed to parse cache count"); } @@ -284,7 +284,7 @@ public abstract class GCParser { params.put("__VIEWSTATEFIELDCOUNT", String.valueOf(searchResult.viewstates.length)); } } - for (String cid : cids) { + for (final String cid : cids) { params.put("CID", cid); } @@ -308,7 +308,7 @@ public abstract class GCParser { LocParser.parseLoc(searchResult, coordinates); - } catch (Exception e) { + } catch (final Exception e) { Log.e("GCParser.parseSearch.CIDs", e); } } @@ -316,7 +316,7 @@ public abstract class GCParser { // get direction images if (Settings.getLoadDirImg()) { final Set<Geocache> caches = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB); - for (Geocache cache : caches) { + for (final Geocache cache : caches) { if (cache.getCoords() == null && StringUtils.isNotEmpty(cache.getDirectionImg())) { DirectionImage.getDrawable(cache.getDirectionImg()); } @@ -327,7 +327,7 @@ public abstract class GCParser { } private static Float parseStars(final String value) { - float floatValue = Float.parseFloat(StringUtils.replaceChars(value, ',', '.')); + final float floatValue = Float.parseFloat(StringUtils.replaceChars(value, ',', '.')); return floatValue >= 0.5 && floatValue <= 5.0 ? floatValue : null; } @@ -374,7 +374,7 @@ public abstract class GCParser { return searchResult; } - final String cacheName = Html.fromHtml(BaseUtils.getMatch(page, GCConstants.PATTERN_NAME, true, "")).toString(); + final String cacheName = Html.fromHtml(TextUtils.getMatch(page, GCConstants.PATTERN_NAME, true, "")).toString(); if (GCConstants.STRING_UNKNOWN_ERROR.equalsIgnoreCase(cacheName)) { searchResult.setError(StatusCode.UNKNOWN_ERROR); return searchResult; @@ -385,30 +385,30 @@ public abstract class GCParser { cache.setArchived(page.contains(GCConstants.STRING_ARCHIVED)); - cache.setPremiumMembersOnly(BaseUtils.matches(page, GCConstants.PATTERN_PREMIUMMEMBERS)); + cache.setPremiumMembersOnly(TextUtils.matches(page, GCConstants.PATTERN_PREMIUMMEMBERS)); - cache.setFavorite(BaseUtils.matches(page, GCConstants.PATTERN_FAVORITE)); + cache.setFavorite(TextUtils.matches(page, GCConstants.PATTERN_FAVORITE)); // cache geocode - cache.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_GEOCODE, true, cache.getGeocode())); + cache.setGeocode(TextUtils.getMatch(page, GCConstants.PATTERN_GEOCODE, true, cache.getGeocode())); // cache id - cache.setCacheId(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHEID, true, cache.getCacheId())); + cache.setCacheId(TextUtils.getMatch(page, GCConstants.PATTERN_CACHEID, true, cache.getCacheId())); // cache guid - cache.setGuid(BaseUtils.getMatch(page, GCConstants.PATTERN_GUID, true, cache.getGuid())); + cache.setGuid(TextUtils.getMatch(page, GCConstants.PATTERN_GUID, true, cache.getGuid())); // name cache.setName(cacheName); // owner real name - cache.setOwnerUserId(Network.decode(BaseUtils.getMatch(page, GCConstants.PATTERN_OWNER_USERID, true, cache.getOwnerUserId()))); + cache.setOwnerUserId(Network.decode(TextUtils.getMatch(page, GCConstants.PATTERN_OWNER_USERID, true, cache.getOwnerUserId()))); cache.setUserModifiedCoords(false); String tableInside = page; - int pos = tableInside.indexOf(GCConstants.STRING_CACHEDETAILS); + final int pos = tableInside.indexOf(GCConstants.STRING_CACHEDETAILS); if (pos == -1) { Log.e("GCParser.parseCache: ID \"cacheDetails\" not found on page"); return null; @@ -418,96 +418,96 @@ public abstract class GCParser { if (StringUtils.isNotBlank(tableInside)) { // cache terrain - String result = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_TERRAIN, true, null); + String result = TextUtils.getMatch(tableInside, GCConstants.PATTERN_TERRAIN, true, null); if (result != null) { try { cache.setTerrain(Float.parseFloat(StringUtils.replaceChars(result, '_', '.'))); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("Error parsing terrain value", e); } } // cache difficulty - result = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_DIFFICULTY, true, null); + result = TextUtils.getMatch(tableInside, GCConstants.PATTERN_DIFFICULTY, true, null); if (result != null) { try { cache.setDifficulty(Float.parseFloat(StringUtils.replaceChars(result, '_', '.'))); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("Error parsing difficulty value", e); } } // owner - cache.setOwnerDisplayName(StringEscapeUtils.unescapeHtml4(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_OWNER_DISPLAYNAME, true, cache.getOwnerDisplayName()))); + cache.setOwnerDisplayName(StringEscapeUtils.unescapeHtml4(TextUtils.getMatch(tableInside, GCConstants.PATTERN_OWNER_DISPLAYNAME, true, cache.getOwnerDisplayName()))); // hidden try { - String hiddenString = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDEN, true, null); + String hiddenString = TextUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDEN, true, null); if (StringUtils.isNotBlank(hiddenString)) { cache.setHidden(Login.parseGcCustomDate(hiddenString)); } if (cache.getHiddenDate() == null) { // event date - hiddenString = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDENEVENT, true, null); + hiddenString = TextUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDENEVENT, true, null); if (StringUtils.isNotBlank(hiddenString)) { cache.setHidden(Login.parseGcCustomDate(hiddenString)); } } - } catch (ParseException e) { + } catch (final ParseException e) { // failed to parse cache hidden date Log.w("GCParser.parseCache: Failed to parse cache hidden (event) date"); } // favorite try { - cache.setFavoritePoints(Integer.parseInt(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_FAVORITECOUNT, true, "0"))); - } catch (NumberFormatException e) { + cache.setFavoritePoints(Integer.parseInt(TextUtils.getMatch(tableInside, GCConstants.PATTERN_FAVORITECOUNT, true, "0"))); + } catch (final NumberFormatException e) { Log.e("Error parsing favorite count", e); } // cache size - cache.setSize(CacheSize.getById(BaseUtils.getMatch(tableInside, GCConstants.PATTERN_SIZE, true, CacheSize.NOT_CHOSEN.id))); + cache.setSize(CacheSize.getById(TextUtils.getMatch(tableInside, GCConstants.PATTERN_SIZE, true, CacheSize.NOT_CHOSEN.id))); } // cache found - cache.setFound(BaseUtils.matches(page, GCConstants.PATTERN_FOUND) || BaseUtils.matches(page, GCConstants.PATTERN_FOUND_ALTERNATIVE)); + cache.setFound(TextUtils.matches(page, GCConstants.PATTERN_FOUND) || TextUtils.matches(page, GCConstants.PATTERN_FOUND_ALTERNATIVE)); // cache found date try { - final String foundDateString = BaseUtils.getMatch(page, GCConstants.PATTERN_FOUND_DATE, true, null); + final String foundDateString = TextUtils.getMatch(page, GCConstants.PATTERN_FOUND_DATE, true, null); if (StringUtils.isNotBlank(foundDateString)) { cache.setVisitedDate(Login.parseGcCustomDate(foundDateString).getTime()); } - } catch (ParseException e) { + } catch (final ParseException e) { // failed to parse cache found date Log.w("GCParser.parseCache: Failed to parse cache found date"); } // cache type - cache.setType(CacheType.getByPattern(BaseUtils.getMatch(page, GCConstants.PATTERN_TYPE, true, cache.getType().id))); + cache.setType(CacheType.getByPattern(TextUtils.getMatch(page, GCConstants.PATTERN_TYPE, true, cache.getType().id))); // on watchlist - cache.setOnWatchlist(BaseUtils.matches(page, GCConstants.PATTERN_WATCHLIST)); + cache.setOnWatchlist(TextUtils.matches(page, GCConstants.PATTERN_WATCHLIST)); // latitude and longitude. Can only be retrieved if user is logged in - String latlon = BaseUtils.getMatch(page, GCConstants.PATTERN_LATLON, true, ""); + String latlon = TextUtils.getMatch(page, GCConstants.PATTERN_LATLON, true, ""); if (StringUtils.isNotEmpty(latlon)) { try { cache.setCoords(new Geopoint(latlon)); cache.setReliableLatLon(true); - } catch (Geopoint.GeopointException e) { + } catch (final Geopoint.GeopointException e) { Log.w("GCParser.parseCache: Failed to parse cache coordinates", e); } } // cache location - cache.setLocation(BaseUtils.getMatch(page, GCConstants.PATTERN_LOCATION, true, "")); + cache.setLocation(TextUtils.getMatch(page, GCConstants.PATTERN_LOCATION, true, "")); // cache hint - String result = BaseUtils.getMatch(page, GCConstants.PATTERN_HINT, false, null); + final String result = TextUtils.getMatch(page, GCConstants.PATTERN_HINT, false, null); if (result != null) { // replace linebreak and paragraph tags - String hint = GCConstants.PATTERN_LINEBREAK.matcher(result).replaceAll("\n"); + final String hint = GCConstants.PATTERN_LINEBREAK.matcher(result).replaceAll("\n"); if (hint != null) { cache.setHint(StringUtils.replace(hint, "</p>", "").trim()); } @@ -516,17 +516,17 @@ public abstract class GCParser { cache.checkFields(); // cache personal note - cache.setPersonalNote(BaseUtils.getMatch(page, GCConstants.PATTERN_PERSONALNOTE, true, cache.getPersonalNote())); + cache.setPersonalNote(TextUtils.getMatch(page, GCConstants.PATTERN_PERSONALNOTE, true, cache.getPersonalNote())); // cache short description - cache.setShortDescription(BaseUtils.getMatch(page, GCConstants.PATTERN_SHORTDESC, true, "")); + cache.setShortDescription(TextUtils.getMatch(page, GCConstants.PATTERN_SHORTDESC, true, "")); // cache description - cache.setDescription(BaseUtils.getMatch(page, GCConstants.PATTERN_DESC, true, "")); + cache.setDescription(TextUtils.getMatch(page, GCConstants.PATTERN_DESC, true, "")); // cache attributes try { - final String attributesPre = BaseUtils.getMatch(page, GCConstants.PATTERN_ATTRIBUTES, true, null); + final String attributesPre = TextUtils.getMatch(page, GCConstants.PATTERN_ATTRIBUTES, true, null); if (null != attributesPre) { final MatcherWrapper matcherAttributesInside = new MatcherWrapper(GCConstants.PATTERN_ATTRIBUTESINSIDE, attributesPre); @@ -539,8 +539,8 @@ public abstract class GCParser { // if the image name can be recognized, use the image name as attribute final String imageName = matcherAttributesInside.group(1).trim(); if (StringUtils.isNotEmpty(imageName)) { - int start = imageName.lastIndexOf('/'); - int end = imageName.lastIndexOf('.'); + final int start = imageName.lastIndexOf('/'); + final int end = imageName.lastIndexOf('.'); if (start >= 0 && end >= 0) { attribute = imageName.substring(start + 1, end).replace('-', '_').toLowerCase(Locale.US); } @@ -550,7 +550,7 @@ public abstract class GCParser { } cache.setAttributes(attributes); } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse cache attributes Log.w("GCParser.parseCache: Failed to parse cache attributes"); } @@ -567,7 +567,7 @@ public abstract class GCParser { while (matcherSpoilersInside.find()) { // the original spoiler URL (include .../display/... contains a low-resolution image // if we shorten the URL we get the original-resolution image - String url = matcherSpoilersInside.group(1).replace("/display", ""); + final String url = matcherSpoilersInside.group(1).replace("/display", ""); String title = null; if (matcherSpoilersInside.group(3) != null) { @@ -579,7 +579,7 @@ public abstract class GCParser { } cache.addSpoiler(new Image(url, title, description)); } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse cache spoilers Log.w("GCParser.parseCache: Failed to parse cache spoilers"); } @@ -613,20 +613,20 @@ public abstract class GCParser { } } } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse cache inventory Log.w("GCParser.parseCache: Failed to parse cache inventory (2)"); } // cache logs counts try { - final String countlogs = BaseUtils.getMatch(page, GCConstants.PATTERN_COUNTLOGS, true, null); + final String countlogs = TextUtils.getMatch(page, GCConstants.PATTERN_COUNTLOGS, true, null); if (null != countlogs) { final MatcherWrapper matcherLog = new MatcherWrapper(GCConstants.PATTERN_COUNTLOG, countlogs); while (matcherLog.find()) { - String typeStr = matcherLog.group(1); - String countStr = matcherLog.group(2).replaceAll("[.,]", ""); + final String typeStr = matcherLog.group(1); + final String countStr = matcherLog.group(2).replaceAll("[.,]", ""); if (StringUtils.isNotBlank(typeStr) && LogType.UNKNOWN != LogType.getByIconName(typeStr) @@ -635,7 +635,7 @@ public abstract class GCParser { } } } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse logs Log.w("GCParser.parseCache: Failed to parse cache log count"); } @@ -645,7 +645,7 @@ public abstract class GCParser { // add waypoint for original coordinates in case of user-modified listing-coordinates try { - final String originalCoords = BaseUtils.getMatch(page, GCConstants.PATTERN_LATLON_ORIG, false, null); + 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); @@ -653,7 +653,7 @@ public abstract class GCParser { cache.addOrChangeWaypoint(waypoint, false); cache.setUserModifiedCoords(true); } - } catch (Geopoint.GeopointException e) { + } catch (final Geopoint.GeopointException e) { } int wpBegin = page.indexOf("<table class=\"Table\" id=\"ctl00_ContentBody_Waypoints\">"); @@ -687,21 +687,21 @@ public abstract class GCParser { // waypoint name // res is null during the unit tests - final String name = BaseUtils.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 = BaseUtils.getMatch(wp[3], GCConstants.PATTERN_WPTYPE, null); + final String resulttype = TextUtils.getMatch(wp[3], GCConstants.PATTERN_WPTYPE, null); final Waypoint waypoint = new Waypoint(name, WaypointType.findById(resulttype), false); // waypoint prefix - waypoint.setPrefix(BaseUtils.getMatch(wp[4], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getPrefix(), false)); + waypoint.setPrefix(TextUtils.getMatch(wp[4], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getPrefix(), false)); // waypoint lookup - waypoint.setLookup(BaseUtils.getMatch(wp[5], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getLookup(), false)); + waypoint.setLookup(TextUtils.getMatch(wp[5], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, true, 2, waypoint.getLookup(), false)); // waypoint latitude and logitude - latlon = Html.fromHtml(BaseUtils.getMatch(wp[7], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, false, 2, "", false)).toString().trim(); + latlon = Html.fromHtml(TextUtils.getMatch(wp[7], GCConstants.PATTERN_WPPREFIXORLOOKUPORLATLON, false, 2, "", false)).toString().trim(); if (!StringUtils.startsWith(latlon, "???")) { waypoint.setLatlon(latlon); waypoint.setCoords(new Geopoint(latlon)); @@ -713,7 +713,7 @@ public abstract class GCParser { } // waypoint note - waypoint.setNote(BaseUtils.getMatch(wp[3], GCConstants.PATTERN_WPNOTE, waypoint.getNote())); + waypoint.setNote(TextUtils.getMatch(wp[3], GCConstants.PATTERN_WPNOTE, waypoint.getNote())); cache.addOrChangeWaypoint(waypoint, false); } @@ -785,7 +785,7 @@ public abstract class GCParser { // save to application search.setError(searchResult.getError()); search.setViewstates(searchResult.viewstates); - for (String geocode : searchResult.getGeocodes()) { + for (final String geocode : searchResult.getGeocodes()) { search.addGeocode(geocode); } return search; @@ -896,7 +896,7 @@ public abstract class GCParser { return null; } try { - JSONObject response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address)); + final JSONObject response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address)); if (response == null) { return null; } @@ -906,12 +906,12 @@ public abstract class GCParser { if (!response.has("data")) { return null; } - JSONObject data = response.getJSONObject("data"); + final JSONObject data = response.getJSONObject("data"); if (data == null) { return null; } return searchByCoords(new Geopoint(data.getDouble("lat"), data.getDouble("lng")), cacheType, showCaptcha, recaptchaReceiver); - } catch (JSONException e) { + } catch (final JSONException e) { Log.w("GCParser.searchByAddress", e); } @@ -1008,7 +1008,7 @@ public abstract class GCParser { final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/log.aspx").encodedQuery("ID=" + cacheid).build().toString(); String page = Login.postRequestLogged(uri, params); if (!Login.getLoginStatus(page)) { - Log.e("GCParser.postLogTrackable: Can not log in geocaching"); + Log.e("GCParser.postLog: Cannot log in geocaching"); return new ImmutablePair<StatusCode, String>(StatusCode.NOT_LOGGED_IN, ""); } @@ -1036,7 +1036,7 @@ public abstract class GCParser { if (trackables != null && !trackables.isEmpty()) { // we have some trackables to proceed final StringBuilder hdnSelected = new StringBuilder(); - for (TrackableLog tb : trackables) { + for (final TrackableLog tb : trackables) { final String action = Integer.toString(tb.id) + tb.action.action; final StringBuilder paramText = new StringBuilder("ctl00$ContentBody$LogBookPanel1$uxTrackables$repTravelBugs$ctl"); @@ -1057,7 +1057,7 @@ public abstract class GCParser { page = Network.getResponseData(Network.postRequest(uri, params)); } - } catch (Exception e) { + } catch (final Exception e) { Log.e("GCParser.postLog.confim", e); } @@ -1077,11 +1077,11 @@ public abstract class GCParser { Login.setActualCachesFound(Login.getActualCachesFound() + 1); } - final String logID = BaseUtils.getMatch(page, GCConstants.PATTERN_LOG_IMAGE_UPLOAD, ""); + final String logID = TextUtils.getMatch(page, GCConstants.PATTERN_LOG_IMAGE_UPLOAD, ""); return new ImmutablePair<StatusCode, String>(StatusCode.NO_ERROR, logID); } - } catch (Exception e) { + } catch (final Exception e) { Log.e("GCParser.postLog.check", e); } @@ -1103,19 +1103,12 @@ public abstract class GCParser { * @return status code to indicate success or failure */ public static ImmutablePair<StatusCode, String> uploadLogImage(final String logId, final String caption, final String description, final Uri imageUri) { - final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/upload.aspx").encodedQuery("LID=" + logId).build().toString(); - - String page = Network.getResponseData(Network.getRequest(uri)); + final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/upload.aspx").build().toString(); - if (!Login.getLoginStatus(page)) { - // Login.isActualLoginStatus() was wrong, we are not logged in - final StatusCode loginState = Login.login(); - if (loginState == StatusCode.NO_ERROR) { - page = Network.getResponseData(Network.getRequest(uri)); - } else { - Log.e("Image upload: No login (error: " + loginState + ')'); - return ImmutablePair.of(StatusCode.NOT_LOGGED_IN, null); - } + final String page = Login.getRequestLogged(uri, new Parameters("LID=", logId)); + if (StringUtils.isBlank(page)) { + Log.e("GCParser.uploadLogImage: No data from server"); + return new ImmutablePair<StatusCode, String>(StatusCode.UNKNOWN_ERROR, null); } final String[] viewstates = Login.getViewstates(page); @@ -1131,7 +1124,7 @@ public abstract class GCParser { final File image = new File(imageUri.getPath()); final String response = Network.getResponseData(Network.postRequest(uri, uploadParams, "ctl00$ContentBody$ImageUploadControl1$uxFileUpload", "image/jpeg", image)); - MatcherWrapper matcherUrl = new MatcherWrapper(GCConstants.PATTERN_IMAGE_UPLOAD_URL, response); + final MatcherWrapper matcherUrl = new MatcherWrapper(GCConstants.PATTERN_IMAGE_UPLOAD_URL, response); if (matcherUrl.find()) { Log.i("Logimage successfully uploaded."); @@ -1195,7 +1188,7 @@ public abstract class GCParser { final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/track/log.aspx").encodedQuery("wid=" + tbid).build().toString(); final String page = Login.postRequestLogged(uri, params); if (!Login.getLoginStatus(page)) { - Log.e("GCParser.postLogTrackable: Can not log in geocaching"); + Log.e("GCParser.postLogTrackable: Cannot log in geocaching"); return StatusCode.NOT_LOGGED_IN; } @@ -1206,7 +1199,7 @@ public abstract class GCParser { Log.i("Log successfully posted to trackable #" + trackingCode); return StatusCode.NO_ERROR; } - } catch (Exception e) { + } catch (final Exception e) { Log.e("GCParser.postLogTrackable.check", e); } @@ -1223,14 +1216,14 @@ public abstract class GCParser { */ static boolean addToWatchlist(final Geocache cache) { final String uri = "http://www.geocaching.com/my/watchlist.aspx?w=" + cache.getCacheId(); - String page = Login.postRequestLogged(uri, null); + final String page = Login.postRequestLogged(uri, null); if (StringUtils.isBlank(page)) { Log.e("GCParser.addToWatchlist: No data from server"); return false; // error } - boolean guidOnPage = cache.isGuidContainedInPage(page); + final boolean guidOnPage = cache.isGuidContainedInPage(page); if (guidOnPage) { Log.i("GCParser.addToWatchlist: cache is on watchlist"); cache.setOnWatchlist(true); @@ -1264,7 +1257,7 @@ public abstract class GCParser { Login.transferViewstates(page, params); page = Network.getResponseData(Network.postRequest(uri, params)); - boolean guidOnPage = cache.isGuidContainedInPage(page); + final boolean guidOnPage = cache.isGuidContainedInPage(page); if (!guidOnPage) { Log.i("GCParser.removeFromWatchlist: cache removed from watchlist"); cache.setOnWatchlist(false); @@ -1302,14 +1295,14 @@ public abstract class GCParser { private static boolean changeFavorite(final Geocache cache, final boolean add) { final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0"); - final String userToken = BaseUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); + final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); if (StringUtils.isEmpty(userToken)) { return false; } final String uri = "http://www.geocaching.com/datastore/favorites.svc/update?u=" + userToken + "&f=" + Boolean.toString(add); - HttpResponse response = Network.postRequest(uri, null); + final HttpResponse response = Network.postRequest(uri, null); if (response != null && response.getStatusLine().getStatusCode() == 200) { Log.i("GCParser.changeFavorite: cache added/removed to/from favorites"); @@ -1338,7 +1331,7 @@ public abstract class GCParser { * Parse a trackable HTML description into a Trackable object * * @param page - * the HTML page to parse, already processed through {@link BaseUtils#replaceWhitespace} + * the HTML page to parse, already processed through {@link TextUtils#replaceWhitespace} * @return the parsed trackable, or null if none could be parsed */ static Trackable parseTrackable(final String page, final String possibleTrackingcode) { @@ -1354,20 +1347,20 @@ public abstract class GCParser { final Trackable trackable = new Trackable(); // trackable geocode - trackable.setGeocode(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GEOCODE, true, trackable.getGeocode())); + trackable.setGeocode(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GEOCODE, true, StringUtils.upperCase(possibleTrackingcode))); // trackable id - trackable.setGuid(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GUID, true, trackable.getGuid())); + trackable.setGuid(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GUID, true, trackable.getGuid())); // trackable icon - trackable.setIconUrl(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ICON, true, trackable.getIconUrl())); + trackable.setIconUrl(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ICON, true, trackable.getIconUrl())); // trackable name - trackable.setName(Html.fromHtml(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_NAME, true, "")).toString()); + trackable.setName(Html.fromHtml(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_NAME, true, "")).toString()); // trackable type if (StringUtils.isNotBlank(trackable.getName())) { - trackable.setType(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_TYPE, true, trackable.getType())); + trackable.setType(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_TYPE, true, trackable.getType())); } // trackable owner name @@ -1377,13 +1370,13 @@ public abstract class GCParser { trackable.setOwnerGuid(matcherOwner.group(1)); trackable.setOwner(matcherOwner.group(2).trim()); } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse trackable owner name Log.w("GCParser.parseTrackable: Failed to parse trackable owner name"); } // trackable origin - trackable.setOrigin(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ORIGIN, true, trackable.getOrigin())); + trackable.setOrigin(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_ORIGIN, true, trackable.getOrigin())); // trackable spotted try { @@ -1401,43 +1394,43 @@ public abstract class GCParser { trackable.setSpottedType(Trackable.SPOTTED_USER); } - if (BaseUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDUNKNOWN)) { + if (TextUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDUNKNOWN)) { trackable.setSpottedType(Trackable.SPOTTED_UNKNOWN); } - if (BaseUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDOWNER)) { + if (TextUtils.matches(page, GCConstants.PATTERN_TRACKABLE_SPOTTEDOWNER)) { trackable.setSpottedType(Trackable.SPOTTED_OWNER); } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse trackable last known place Log.w("GCParser.parseTrackable: Failed to parse trackable last known place"); } // released date - can be missing on the page try { - String releaseString = BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_RELEASES, false, null); + final String releaseString = TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_RELEASES, false, null); if (releaseString != null) { trackable.setReleased(dateTbIn1.parse(releaseString)); if (trackable.getReleased() == null) { trackable.setReleased(dateTbIn2.parse(releaseString)); } } - } catch (ParseException e1) { + } catch (final ParseException e1) { trackable.setReleased(null); } // trackable distance - final String distance = BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_DISTANCE, false, null); + final String distance = TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_DISTANCE, false, null); if (null != distance) { try { trackable.setDistance(DistanceParser.parseDistance(distance, Settings.isUseMetricUnits())); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("GCParser.parseTrackable: Failed to parse distance", e); } } // trackable goal - trackable.setGoal(convertLinks(BaseUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GOAL, true, trackable.getGoal()))); + trackable.setGoal(convertLinks(TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_GOAL, true, trackable.getGoal()))); // trackable details & image try { @@ -1453,10 +1446,13 @@ public abstract class GCParser { trackable.setDetails(convertLinks(details)); } } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse trackable details & image 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 logs try { @@ -1474,7 +1470,7 @@ public abstract class GCParser { long date = 0; try { date = Login.parseGcCustomDate(matcherLogs.group(2)).getTime(); - } catch (ParseException e) { + } catch (final ParseException e) { } final LogEntry logDone = new LogEntry( @@ -1502,7 +1498,7 @@ public abstract class GCParser { trackable.getLogs().add(logDone); } - } catch (Exception e) { + } catch (final Exception e) { // failed to parse logs Log.w("GCParser.parseCache: Failed to parse cache logs", e); } @@ -1572,10 +1568,10 @@ public abstract class GCParser { } } else { // extract embedded JSON data from page - rawResponse = BaseUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, ""); + rawResponse = TextUtils.getMatch(page, GCConstants.PATTERN_LOGBOOK, ""); } - List<LogEntry> logs = new ArrayList<LogEntry>(); + final List<LogEntry> logs = new ArrayList<LogEntry>(); try { final JSONObject resp = new JSONObject(rawResponse); @@ -1596,7 +1592,7 @@ public abstract class GCParser { long date = 0; try { date = Login.parseGcCustomDate(entry.getString("Visited")).getTime(); - } catch (ParseException e) { + } catch (final ParseException e) { Log.e("GCParser.loadLogsFromDetails: failed to parse log date."); } @@ -1624,7 +1620,7 @@ public abstract class GCParser { logs.add(logDone); } - } catch (JSONException e) { + } catch (final JSONException e) { // failed to parse logs Log.w("GCParser.loadLogsFromDetails: Failed to parse cache logs", e); } @@ -1641,16 +1637,16 @@ public abstract class GCParser { final MatcherWrapper typeBoxMatcher = new MatcherWrapper(GCConstants.PATTERN_TYPEBOX, page); if (typeBoxMatcher.find() && typeBoxMatcher.groupCount() > 0) { - String typesText = typeBoxMatcher.group(1); + final String typesText = typeBoxMatcher.group(1); final MatcherWrapper typeMatcher = new MatcherWrapper(GCConstants.PATTERN_TYPE2, typesText); while (typeMatcher.find()) { if (typeMatcher.groupCount() > 1) { try { - int type = Integer.parseInt(typeMatcher.group(2)); + final int type = Integer.parseInt(typeMatcher.group(2)); if (type > 0) { types.add(LogType.getById(type)); } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("Error parsing log types", e); } } @@ -1698,7 +1694,7 @@ public abstract class GCParser { Log.i("Trackable in inventory (#" + entry.ctl + "/" + entry.id + "): " + entry.trackCode + " - " + entry.name); trackableLogs.add(entry); } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("GCParser.parseTrackableLog", e); } } @@ -1727,10 +1723,10 @@ public abstract class GCParser { //cache.setLogs(loadLogsFromDetails(page, cache, false)); if (Settings.isFriendLogsWanted()) { CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_logs); - List<LogEntry> allLogs = cache.getLogs(); - List<LogEntry> friendLogs = loadLogsFromDetails(page, cache, true, false); + final List<LogEntry> allLogs = cache.getLogs(); + final List<LogEntry> friendLogs = loadLogsFromDetails(page, cache, true, false); if (friendLogs != null) { - for (LogEntry log : friendLogs) { + for (final LogEntry log : friendLogs) { if (allLogs.contains(log)) { allLogs.get(allLogs.indexOf(log)).friend = true; } else { @@ -1774,7 +1770,7 @@ public abstract class GCParser { public static boolean editModifiedCoordinates(Geocache cache, Geopoint wpt) { final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0"); - final String userToken = BaseUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); + final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); if (StringUtils.isEmpty(userToken)) { return false; } @@ -1793,7 +1789,7 @@ public abstract class GCParser { final String uriSuffix = wpt != null ? "SetUserCoordinate" : "ResetUserCoordinate"; final String uriPrefix = "http://www.geocaching.com/seek/cache_details.aspx/"; - HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo); + final HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo); Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString()); if (response != null && response.getStatusLine().getStatusCode() == 200) { @@ -1801,7 +1797,7 @@ public abstract class GCParser { return true; } - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("Unknown exception with json wrap code", e); } Log.e("GCParser.deleteModifiedCoordinates - cannot delete modified coords"); @@ -1810,13 +1806,13 @@ public abstract class GCParser { public static boolean uploadPersonalNote(Geocache cache) { final String page = requestHtmlPage(cache.getGeocode(), null, "n", "0"); - final String userToken = BaseUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); + final String userToken = TextUtils.getMatch(page, GCConstants.PATTERN_USERTOKEN, ""); if (StringUtils.isEmpty(userToken)) { return false; } try { - JSONObject jo = new JSONObject() + final JSONObject jo = new JSONObject() .put("dto", (new JSONObject() .put("et", cache.getPersonalNote()) .put("ut", userToken))); @@ -1824,7 +1820,7 @@ public abstract class GCParser { final String uriSuffix = "SetUserCacheNote"; final String uriPrefix = "http://www.geocaching.com/seek/cache_details.aspx/"; - HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo); + final HttpResponse response = Network.postJsonRequest(uriPrefix + uriSuffix, jo); Log.i("Sending to " + uriPrefix + uriSuffix + " :" + jo.toString()); if (response != null && response.getStatusLine().getStatusCode() == 200) { @@ -1832,7 +1828,7 @@ public abstract class GCParser { return true; } - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("Unknown exception with json wrap code", e); } Log.e("GCParser.uploadPersonalNote - cannot upload personal note"); diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java index 7351311..3146712 100644 --- a/main/src/cgeo/geocaching/connector/gc/Login.java +++ b/main/src/cgeo/geocaching/connector/gc/Login.java @@ -8,9 +8,9 @@ import cgeo.geocaching.network.Cookies; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; -import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; +import cgeo.geocaching.utils.TextUtils; import ch.boye.httpclientandroidlib.HttpResponse; @@ -52,9 +52,9 @@ public abstract class Login { "dd/MM/yyyy" }; - Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>(); + final Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>(); - for (String format : formats) { + for (final String format : formats) { map.put(format, new SimpleDateFormat(format, Locale.ENGLISH)); } @@ -77,7 +77,7 @@ public abstract class Login { 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 && BaseUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) { + if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && TextUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) { return StatusCode.MAINTENANCE; } @@ -147,9 +147,9 @@ public abstract class Login { } public static StatusCode logout() { - HttpResponse logoutResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f"); - String logoutData = Network.getResponseData(logoutResponse); - if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) { + final HttpResponse logoutResponse = Network.getRequest("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f"); + final String logoutData = Network.getResponseData(logoutResponse); + if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && TextUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) { return StatusCode.MAINTENANCE; } @@ -205,17 +205,17 @@ public abstract class Login { setActualStatus(cgeoapplication.getInstance().getString(R.string.init_login_popup_ok)); // on every page except login page - setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME)); + setActualLoginStatus(TextUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME)); if (isActualLoginStatus()) { - setActualUserName(BaseUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???")); + setActualUserName(TextUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???")); int cachesCount = 0; try { - cachesCount = Integer.parseInt(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", "")); - } catch (NumberFormatException e) { + cachesCount = Integer.parseInt(TextUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", "")); + } catch (final NumberFormatException e) { Log.e("getLoginStatus: bad cache count", e); } setActualCachesFound(cachesCount); - Settings.setMemberStatus(BaseUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, null)); + Settings.setMemberStatus(TextUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, null)); if ( page.contains(GCConstants.MEMBER_STATUS_RENEW) ) { Settings.setMemberStatus(GCConstants.MEMBER_STATUS_PM); } @@ -223,7 +223,7 @@ public abstract class Login { } // login page - setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME_LOGIN_PAGE)); + setActualLoginStatus(TextUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME_LOGIN_PAGE)); if (isActualLoginStatus()) { setActualUserName(Settings.getUsername()); // number of caches found is not part of this page @@ -260,23 +260,23 @@ public abstract class Login { public static BitmapDrawable downloadAvatarAndGetMemberStatus() { try { - final String profile = BaseUtils.replaceWhitespace(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/"))); + final String profile = TextUtils.replaceWhitespace(Network.getResponseData(Network.getRequest("http://www.geocaching.com/my/"))); - Settings.setMemberStatus(BaseUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null)); + Settings.setMemberStatus(TextUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null)); if (profile.contains(GCConstants.MEMBER_STATUS_RENEW)) { Settings.setMemberStatus(GCConstants.MEMBER_STATUS_PM); } - setActualCachesFound(Integer.parseInt(BaseUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", ""))); + setActualCachesFound(Integer.parseInt(TextUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", ""))); - final String avatarURL = BaseUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null); + final String avatarURL = TextUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null); if (null != avatarURL) { final HtmlImage imgGetter = new HtmlImage("", false, 0, false); return imgGetter.getDrawable(avatarURL); } // No match? There may be no avatar set by user. Log.d("No avatar set for user"); - } catch (Exception e) { + } catch (final Exception e) { Log.w("Error when retrieving user avatar", e); } return null; @@ -294,7 +294,7 @@ public abstract class Login { return; } - String customDate = BaseUtils.getMatch(result, GCConstants.PATTERN_CUSTOMDATE, true, null); + final String customDate = TextUtils.getMatch(result, GCConstants.PATTERN_CUSTOMDATE, true, null); if (null != customDate) { Settings.setGcCustomDate(customDate); } @@ -310,14 +310,14 @@ public abstract class Login { if (gcCustomDateFormats.containsKey(format)) { try { return gcCustomDateFormats.get(format).parse(trimmed); - } catch (ParseException e) { + } catch (final ParseException e) { } } - for (SimpleDateFormat sdf : gcCustomDateFormats.values()) { + for (final SimpleDateFormat sdf : gcCustomDateFormats.values()) { try { return sdf.parse(trimmed); - } catch (ParseException e) { + } catch (final ParseException e) { } } @@ -347,7 +347,7 @@ public abstract class Login { return true; } - for (String s : a) { + for (final String s : a) { if (StringUtils.isNotEmpty(s)) { return false; } @@ -373,24 +373,24 @@ public abstract class Login { if (matcherViewstateCount.find()) { try { count = Integer.parseInt(matcherViewstateCount.group(1)); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("getViewStates", e); } } - String[] viewstates = new String[count]; + final String[] viewstates = new String[count]; // Get the viewstates final MatcherWrapper matcherViewstates = new MatcherWrapper(GCConstants.PATTERN_VIEWSTATES, page); while (matcherViewstates.find()) { - String sno = matcherViewstates.group(1); // number of viewstate + final String sno = matcherViewstates.group(1); // number of viewstate int no; if (StringUtils.isEmpty(sno)) { no = 0; } else { try { no = Integer.parseInt(sno); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("getViewStates", e); no = 0; } @@ -436,17 +436,17 @@ public abstract class Login { * @return */ public static String postRequestLogged(final String uri, final Parameters params) { - HttpResponse response = Network.postRequest(uri, params); - String data = Network.getResponseData(response); + final String data = Network.getResponseData(Network.postRequest(uri, params)); - if (!getLoginStatus(data)) { - if (login() == StatusCode.NO_ERROR) { - response = Network.postRequest(uri, params); - data = Network.getResponseData(response); - } else { - Log.i("Working as guest."); - } + if (getLoginStatus(data)) { + return data; } + + if (login() == StatusCode.NO_ERROR) { + return Network.getResponseData(Network.postRequest(uri, params)); + } + + Log.i("Working as guest."); return data; } @@ -476,8 +476,8 @@ public abstract class Login { public static String[] getMapTokens() { final HttpResponse response = Network.getRequest(GCConstants.URL_LIVE_MAP); final String data = Network.getResponseData(response); - final String userSession = BaseUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, ""); - final String sessionToken = BaseUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, ""); + final String userSession = TextUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, ""); + final String sessionToken = TextUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, ""); return new String[] { userSession, sessionToken }; } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java index 4c6db97..5f30934 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java @@ -6,9 +6,10 @@ import cgeo.geocaching.Settings; 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.ISearchByViewPort; -import cgeo.geocaching.connector.oc.OkapiClient.UserInfo; +import cgeo.geocaching.connector.oc.UserInfo.UserInfoStatus; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.utils.CryptUtils; @@ -16,11 +17,13 @@ import cgeo.geocaching.utils.CryptUtils; import org.apache.commons.lang3.StringUtils; import android.app.Activity; +import android.content.Context; +import android.os.Handler; -public class OCApiLiveConnector extends OCApiConnector implements ISearchByCenter, ISearchByViewPort { +public class OCApiLiveConnector extends OCApiConnector implements ISearchByCenter, ISearchByViewPort, ILogin { private String cS; - private UserInfo userInfo = new UserInfo(StringUtils.EMPTY, 0, false); + private UserInfo userInfo = new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.NOT_RETRIEVED); public OCApiLiveConnector(String name, String host, String prefix, int cKResId, int cSResId, ApiSupport apiSupport) { super(name, host, prefix, CryptUtils.rot13(cgeoapplication.getInstance().getResources().getString(cKResId)), apiSupport); @@ -104,16 +107,33 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente return getSupportedAuthLevel() == OAuthLevel.Level3; } - public boolean retrieveUserInfo() { - userInfo = OkapiClient.getUserInfo(this); - return userInfo.isRetrieveSuccessful(); + @Override + public boolean login(Handler handler, Context fromActivity) { + if (supportsPersonalization()) { + userInfo = OkapiClient.getUserInfo(this); + } else { + userInfo = new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.NOT_SUPPORTED); + } + return userInfo.getStatus() == UserInfoStatus.SUCCESSFUL; } - public Object getUserName() { + @Override + public String getUserName() { return userInfo.getName(); } + @Override public int getCachesFound() { return userInfo.getFinds(); } + + @Override + public String getLoginStatusString() { + return cgeoapplication.getInstance().getString(userInfo.getStatus().resId); + } + + @Override + public boolean isLoggedIn() { + return userInfo.getStatus() == UserInfoStatus.SUCCESSFUL; + } } diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java index 3c99bc9..b6f9711 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java @@ -14,6 +14,7 @@ import cgeo.geocaching.connector.LogResult; import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.connector.oc.OCApiConnector.ApiSupport; import cgeo.geocaching.connector.oc.OCApiConnector.OAuthLevel; +import cgeo.geocaching.connector.oc.UserInfo.UserInfoStatus; import cgeo.geocaching.enumerations.CacheAttribute; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; @@ -93,6 +94,7 @@ final public class OkapiClient { private static final String CACHE_LOCATION = "location"; private static final String CACHE_NAME = "name"; private static final String CACHE_CODE = "code"; + private static final String CACHE_REQ_PASSWORD = "req_passwd"; private static final String LOG_TYPE = "type"; private static final String LOG_COMMENT = "comment"; @@ -108,7 +110,7 @@ final public class OkapiClient { // 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_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"; + 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_CURRENT_FIELDS = "gc_code|attribution_note"; private static final String SERVICE_CACHE_ADDITIONAL_L3_FIELDS = "is_watched"; @@ -118,12 +120,12 @@ final public class OkapiClient { public static Geocache getCache(final String geoCode) { final Parameters params = new Parameters("cache_code", geoCode); - IConnector connector = ConnectorFactory.getConnector(geoCode); + final IConnector connector = ConnectorFactory.getConnector(geoCode); if (!(connector instanceof OCApiConnector)) { return null; } - OCApiConnector ocapiConn = (OCApiConnector) connector; + final OCApiConnector ocapiConn = (OCApiConnector) connector; params.add("fields", getFullFields(ocapiConn)); params.add("attribution_append", "none"); @@ -138,16 +140,18 @@ final public class OkapiClient { } public static List<Geocache> getCachesAround(final Geopoint center, OCApiConnector connector) { - String centerString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center) + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center); + final String centerString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, center) + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, center); final Parameters params = new Parameters("search_method", METHOD_SEARCH_NEAREST); final Map<String, String> valueMap = new LinkedHashMap<String, String>(); valueMap.put("center", centerString); valueMap.put("limit", "20"); - addFilterParams(valueMap, connector); + return requestCaches(connector, params, valueMap); + } + private static List<Geocache> requestCaches(OCApiConnector connector, final Parameters params, final Map<String, String> valueMap) { + 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); @@ -166,7 +170,7 @@ final public class OkapiClient { return Collections.emptyList(); } - String bboxString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, viewport.bottomLeft) + final String bboxString = GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, viewport.bottomLeft) + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, viewport.bottomLeft) + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LAT_DECDEGREE_RAW, viewport.topRight) + SEPARATOR + GeopointFormatter.format(GeopointFormatter.Format.LON_DECDEGREE_RAW, viewport.topRight); @@ -174,19 +178,7 @@ final public class OkapiClient { final Map<String, String> valueMap = new LinkedHashMap<String, String>(); valueMap.put("bbox", bboxString); - 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); - - if (data == null) { - return Collections.emptyList(); - } - - return parseCaches(data); + return requestCaches(connector, params, valueMap); } public static boolean setWatchState(final Geocache cache, final boolean watched, OCApiConnector connector) { @@ -204,7 +196,7 @@ final public class OkapiClient { return true; } - public static LogResult postLog(final Geocache cache, LogType logType, Calendar date, String log, OCApiConnector connector) { + public static LogResult postLog(final Geocache cache, LogType logType, Calendar date, String log, String logPassword, OCApiConnector connector) { final Parameters params = new Parameters("cache_code", cache.getGeocode()); params.add("logtype", logType.oc_type); params.add("comment", log); @@ -213,6 +205,9 @@ final public class OkapiClient { if (logType.equals(LogType.NEEDS_MAINTENANCE)) { params.add("needs_maintenance", "true"); } + if (logPassword != null) { + params.add("password", logPassword); + } final JSONObject data = request(connector, OkapiService.SERVICE_SUBMIT_LOG, params); @@ -226,7 +221,7 @@ final public class OkapiClient { } return new LogResult(StatusCode.LOG_POST_ERROR, ""); - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.postLog", e); } return new LogResult(StatusCode.LOG_POST_ERROR, ""); @@ -243,19 +238,20 @@ final public class OkapiClient { // Get and iterate result list final JSONObject cachesResponse = response.getJSONObject("results"); if (cachesResponse != null) { - List<Geocache> caches = new ArrayList<Geocache>(cachesResponse.length()); + final List<Geocache> caches = new ArrayList<Geocache>(cachesResponse.length()); @SuppressWarnings("unchecked") + final Iterator<String> keys = cachesResponse.keys(); while (keys.hasNext()) { - String key = keys.next(); - Geocache cache = parseSmallCache(cachesResponse.getJSONObject(key)); + final String key = keys.next(); + final Geocache cache = parseSmallCache(cachesResponse.getJSONObject(key)); if (cache != null) { caches.add(cache); } } return caches; } - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.parseCachesResult", e); } return Collections.emptyList(); @@ -269,7 +265,7 @@ final public class OkapiClient { parseCoreCache(response, cache); cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_CACHE)); - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.parseSmallCache", e); } return cache; @@ -297,9 +293,9 @@ final public class OkapiClient { cache.setFavoritePoints(response.getInt(CACHE_RECOMMENDATIONS)); // not used: req_password // Prepend gc-link to description if available - StringBuilder description = new StringBuilder(500); + final StringBuilder description = new StringBuilder(500); if (!response.isNull("gc_code")) { - String gccode = response.getString("gc_code"); + final String gccode = response.getString("gc_code"); description.append(cgeoapplication.getInstance().getResources() .getString(R.string.cache_listed_on, GCConnector.getInstance().getName())) .append(": <a href=\"http://coord.info/") @@ -318,7 +314,7 @@ final public class OkapiClient { final JSONArray images = response.getJSONArray(CACHE_IMAGES); if (images != null) { for (int i = 0; i < images.length(); i++) { - JSONObject imageResponse = images.getJSONObject(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()); @@ -336,11 +332,12 @@ final public class OkapiClient { if (!response.isNull(CACHE_IS_WATCHED)) { cache.setOnWatchlist(response.getBoolean(CACHE_IS_WATCHED)); } + cache.setLogPasswordRequired(response.getBoolean(CACHE_REQ_PASSWORD)); cache.setDetailedUpdatedNow(); // save full detailed caches cgData.saveCache(cache, EnumSet.of(SaveFlag.SAVE_DB)); - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.parseCache", e); } return cache; @@ -387,8 +384,8 @@ final public class OkapiClient { List<LogEntry> result = null; for (int i = 0; i < logsJSON.length(); i++) { try { - JSONObject logResponse = logsJSON.getJSONObject(i); - LogEntry log = new LogEntry( + final JSONObject logResponse = logsJSON.getJSONObject(i); + final LogEntry log = new LogEntry( parseUser(logResponse.getJSONObject(LOG_USER)), parseDate(logResponse.getString(LOG_DATE)).getTime(), parseLogType(logResponse.getString(LOG_TYPE)), @@ -397,7 +394,7 @@ final public class OkapiClient { result = new ArrayList<LogEntry>(); } result.add(log); - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.parseLogs", e); } } @@ -408,12 +405,12 @@ final public class OkapiClient { List<Waypoint> result = null; for (int i = 0; i < wptsJson.length(); i++) { try { - JSONObject wptResponse = wptsJson.getJSONObject(i); - Waypoint wpt = new Waypoint(wptResponse.getString(WPT_NAME), + final JSONObject wptResponse = wptsJson.getJSONObject(i); + final Waypoint wpt = new Waypoint(wptResponse.getString(WPT_NAME), parseWptType(wptResponse.getString(WPT_TYPE)), false); wpt.setNote(wptResponse.getString(WPT_DESCRIPTION)); - Geopoint pt = parseCoords(wptResponse.getString(WPT_LOCATION)); + final Geopoint pt = parseCoords(wptResponse.getString(WPT_LOCATION)); if (pt != null) { wpt.setCoords(pt); } @@ -421,7 +418,7 @@ final public class OkapiClient { result = new ArrayList<Waypoint>(); } result.add(wpt); - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.parseWaypoints", e); } } @@ -468,7 +465,7 @@ final public class OkapiClient { final String strippedDate = date.replaceAll("\\+0([0-9]){1}\\:00", "+0$100"); try { return ISO8601DATEFORMAT.parse(strippedDate); - } catch (ParseException e) { + } catch (final ParseException e) { Log.e("OkapiClient.parseDate", e); } return null; @@ -486,17 +483,17 @@ final public class OkapiClient { private static List<String> parseAttributes(JSONArray nameList) { - List<String> result = new ArrayList<String>(); + final List<String> result = new ArrayList<String>(); for (int i = 0; i < nameList.length(); i++) { try { - String name = nameList.getString(i); - CacheAttribute attr = CacheAttribute.getByOcId(AttributeParser.getOcDeId(name)); + final String name = nameList.getString(i); + final CacheAttribute attr = CacheAttribute.getByOcId(AttributeParser.getOcDeId(name)); if (attr != null) { result.add(attr.rawName); } - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.parseAttributes", e); } } @@ -517,7 +514,7 @@ final public class OkapiClient { double size = 0; try { size = response.getDouble(CACHE_SIZE); - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.getCacheSize", e); } switch ((int) Math.round(size)) { @@ -584,7 +581,7 @@ final public class OkapiClient { return StringUtils.EMPTY; } - StringBuilder res = new StringBuilder(500); + final StringBuilder res = new StringBuilder(500); res.append(SERVICE_CACHE_CORE_FIELDS); res.append(SEPARATOR).append(SERVICE_CACHE_ADDITIONAL_FIELDS); @@ -673,7 +670,7 @@ final public class OkapiClient { final JSONObject data = request(connector, OkapiService.SERVICE_USER, params); if (data == null) { - return new UserInfo(StringUtils.EMPTY, 0, false); + return new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.FAILED); } String name = StringUtils.EMPTY; @@ -683,7 +680,7 @@ final public class OkapiClient { if (!data.isNull(USER_USERNAME)) { try { name = data.getString(USER_USERNAME); - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.getUserInfo - name", e); success = false; } @@ -694,7 +691,7 @@ final public class OkapiClient { if (!data.isNull(USER_CACHES_FOUND)) { try { finds = data.getInt(USER_CACHES_FOUND); - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.getUserInfo - finds", e); success = false; } @@ -702,32 +699,7 @@ final public class OkapiClient { success = false; } - return new UserInfo(name, finds, success); - } - - public static class UserInfo { - - private final String name; - private final int finds; - private final boolean retrieveSuccessful; - - UserInfo(String name, int finds, boolean retrieveSuccessful) { - this.name = name; - this.finds = finds; - this.retrieveSuccessful = retrieveSuccessful; - } - - public String getName() { - return name; - } - - public int getFinds() { - return finds; - } - - public boolean isRetrieveSuccessful() { - return retrieveSuccessful; - } + return new UserInfo(name, finds, success ? UserInfoStatus.SUCCESSFUL : UserInfoStatus.FAILED); } } diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java index 8a94218..c4bf91f 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java @@ -1,8 +1,8 @@ package cgeo.geocaching.connector.oc; import cgeo.geocaching.Geocache; -import cgeo.geocaching.TrackableLog; import cgeo.geocaching.LogCacheActivity; +import cgeo.geocaching.TrackableLog; import cgeo.geocaching.connector.ILoggingManager; import cgeo.geocaching.connector.ImageResult; import cgeo.geocaching.connector.LogResult; @@ -38,8 +38,8 @@ public class OkapiLoggingManager implements ILoggingManager { } @Override - public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, List<TrackableLog> trackableLogs) { - return OkapiClient.postLog(cache, logType, date, log, connector); + public LogResult postLog(Geocache cache, LogType logType, Calendar date, String log, String logPassword, List<TrackableLog> trackableLogs) { + return OkapiClient.postLog(cache, logType, date, log, logPassword, connector); } @Override diff --git a/main/src/cgeo/geocaching/connector/oc/UserInfo.java b/main/src/cgeo/geocaching/connector/oc/UserInfo.java new file mode 100644 index 0000000..0dc0440 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/oc/UserInfo.java @@ -0,0 +1,41 @@ +package cgeo.geocaching.connector.oc; + +import cgeo.geocaching.R; + +public class UserInfo { + + public enum UserInfoStatus { + 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); + + public final int resId; + + UserInfoStatus(int resId) { + this.resId = resId; + } + } + + private final String name; + private final int finds; + private final UserInfoStatus status; + + UserInfo(String name, int finds, UserInfoStatus status) { + this.name = name; + this.finds = finds; + this.status = status; + } + + public String getName() { + return name; + } + + public int getFinds() { + return finds; + } + + public UserInfoStatus getStatus() { + return status; + } +} diff --git a/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java new file mode 100644 index 0000000..cd32d87 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java @@ -0,0 +1,10 @@ +package cgeo.geocaching.connector.trackable; + + +public abstract class AbstractTrackableConnector implements TrackableConnector { + + @Override + public boolean isLoggable() { + return false; + } +} diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java new file mode 100644 index 0000000..8387076 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java @@ -0,0 +1,42 @@ +package cgeo.geocaching.connector.trackable; + +import cgeo.geocaching.Trackable; +import cgeo.geocaching.network.Network; +import cgeo.geocaching.utils.Log; + +import java.util.regex.Pattern; + +public class GeokretyConnector extends AbstractTrackableConnector { + + private static final Pattern PATTERN_GK_CODE = Pattern.compile("GK[0-9A-F]{4,}"); + + @Override + public boolean canHandleTrackable(String geocode) { + return geocode != null && PATTERN_GK_CODE.matcher(geocode).matches(); + } + + @Override + public String getUrl(Trackable trackable) { + return "http://geokrety.org/konkret.php?id=" + getId(trackable.getGeocode()); + } + + @Override + public Trackable searchTrackable(String geocode, String guid, String id) { + final String page = Network.getResponseData(Network.getRequest("http://geokrety.org/export2.php?gkid=" + getId(geocode))); + if (page == null) { + return null; + } + return GeokretyParser.parse(page); + } + + private static int getId(String geocode) { + try { + final String hex = geocode.substring(2); + return Integer.parseInt(hex, 16); + } catch (final NumberFormatException e) { + Log.e("Trackable.getUrl", e); + } + return -1; + } + +} diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java new file mode 100644 index 0000000..66ca5f7 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyParser.java @@ -0,0 +1,82 @@ +package cgeo.geocaching.connector.trackable; + +import cgeo.geocaching.R; +import cgeo.geocaching.Trackable; +import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.utils.Log; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; + +import android.sax.Element; +import android.sax.EndTextElementListener; +import android.sax.RootElement; +import android.sax.StartElementListener; +import android.util.Xml; + +public class GeokretyParser { + + public static Trackable parse(final String page) { + final Trackable trackable = new Trackable(); + + final RootElement root = new RootElement("gkxml"); + final Element geokret = root.getChild("geokrety").getChild("geokret"); + + geokret.setEndTextElementListener(new EndTextElementListener() { + + @Override + public void end(String name) { + trackable.setName(name); + } + }); + + geokret.setStartElementListener(new StartElementListener() { + + @Override + public void start(Attributes attributes) { + try { + if (attributes.getIndex("id") > -1) { + trackable.setGeocode(geocode(Integer.valueOf(attributes.getValue("id")))); + } + if (attributes.getIndex("dist") > -1) { + trackable.setDistance(Float.valueOf(attributes.getValue("dist"))); + } + if (attributes.getIndex("type") > -1) { + trackable.setType(getType(Integer.valueOf(attributes.getValue("type")))); + } + } catch (final NumberFormatException e) { + Log.e("Parsing geokret", e); + } + } + }); + + try { + Xml.parse(page, root.getContentHandler()); + return trackable; + } catch (final SAXException e) { + Log.w("Cannot parse geokrety", e); + } + + return null; + } + + protected static String getType(int type) { + switch (type) { + case 0: + return cgeoapplication.getInstance().getString(R.string.geokret_type_traditional); + case 1: + return cgeoapplication.getInstance().getString(R.string.geokret_type_book_or_media); + case 2: + return cgeoapplication.getInstance().getString(R.string.geokret_type_human); + case 3: + return cgeoapplication.getInstance().getString(R.string.geokret_type_coin); + case 4: + return cgeoapplication.getInstance().getString(R.string.geokret_type_post); + } + return null; + } + + protected static String geocode(final int id) { + return String.format("GK%04X", id); + } +} diff --git a/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java new file mode 100644 index 0000000..c09dc07 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java @@ -0,0 +1,19 @@ +package cgeo.geocaching.connector.trackable; + +import cgeo.geocaching.Trackable; + +/** + * Methods to be implemented by any connector for handling trackables + * + */ +public interface TrackableConnector { + + public boolean canHandleTrackable(final String geocode); + + public String getUrl(final Trackable trackable); + + public boolean isLoggable(); + + public Trackable searchTrackable(String geocode, String guid, String id); + +} diff --git a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java new file mode 100644 index 0000000..0dac6cc --- /dev/null +++ b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java @@ -0,0 +1,50 @@ +package cgeo.geocaching.connector.trackable; + +import cgeo.geocaching.Trackable; +import cgeo.geocaching.connector.gc.GCParser; + +import java.util.regex.Pattern; + +public class TravelBugConnector extends AbstractTrackableConnector { + + /** + * TB codes really start with TB1, there is no padding or minimum length + */ + private final static Pattern PATTERN_TB_CODE = Pattern.compile("(TB[0-9A-Z]+)|([0-9A-Z]{6})", Pattern.CASE_INSENSITIVE); + + @Override + public boolean canHandleTrackable(String geocode) { + return TravelBugConnector.PATTERN_TB_CODE.matcher(geocode).matches(); + } + + @Override + public String getUrl(Trackable trackable) { + return "http://www.geocaching.com//track/details.aspx?tracker=" + trackable.getGeocode(); + } + + @Override + public boolean isLoggable() { + return true; + } + + @Override + public Trackable searchTrackable(String geocode, String guid, String id) { + return GCParser.searchTrackable(geocode, guid, id); + } + + /** + * initialization on demand holder pattern + */ + private static class Holder { + private static final TravelBugConnector INSTANCE = new TravelBugConnector(); + } + + private TravelBugConnector() { + // singleton + } + + public static TravelBugConnector getInstance() { + return Holder.INSTANCE; + } + +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java new file mode 100644 index 0000000..0295927 --- /dev/null +++ b/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java @@ -0,0 +1,24 @@ +package cgeo.geocaching.connector.trackable; + +import cgeo.geocaching.Trackable; + +import org.apache.commons.lang3.StringUtils; + +public class UnknownTrackableConnector extends AbstractTrackableConnector { + + @Override + public boolean canHandleTrackable(String geocode) { + return false; + } + + @Override + public String getUrl(Trackable trackable) { + return StringUtils.EMPTY; + } + + @Override + public Trackable searchTrackable(String geocode, String guid, String id) { + return null; + } + +} diff --git a/main/src/cgeo/geocaching/export/FieldnoteExport.java b/main/src/cgeo/geocaching/export/FieldnoteExport.java index a42a48a..38b603c 100644 --- a/main/src/cgeo/geocaching/export/FieldnoteExport.java +++ b/main/src/cgeo/geocaching/export/FieldnoteExport.java @@ -70,10 +70,10 @@ class FieldnoteExport extends AbstractExport { } private Dialog getExportOptionsDialog(final Geocache[] caches, final Activity activity) { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); // AlertDialog has always dark style, so we have to apply it as well always - View layout = View.inflate(new ContextThemeWrapper(activity, R.style.dark), R.layout.fieldnote_export_dialog, null); + final View layout = View.inflate(new ContextThemeWrapper(activity, R.style.dark), R.layout.fieldnote_export_dialog, null); builder.setView(layout); final CheckBox uploadOption = (CheckBox) layout.findViewById(R.id.upload); @@ -132,13 +132,13 @@ class FieldnoteExport extends AbstractExport { final StringBuilder fieldNoteBuffer = new StringBuilder(); try { int i = 0; - for (Geocache cache : caches) { + for (final Geocache cache : caches) { if (cache.isLogOffline()) { appendFieldNote(fieldNoteBuffer, cache, cgData.loadLogOffline(cache.getGeocode())); publishProgress(++i); } } - } catch (Exception e) { + } catch (final Exception e) { Log.e("FieldnoteExport.ExportTask generation", e); return false; } @@ -151,17 +151,17 @@ class FieldnoteExport extends AbstractExport { exportLocation.mkdirs(); - SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US); + final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US); exportFile = new File(exportLocation.toString() + '/' + fileNameDateFormat.format(new Date()) + ".txt"); Writer fileWriter = null; BufferedOutputStream buffer = null; try { - OutputStream os = new FileOutputStream(exportFile); + final OutputStream os = new FileOutputStream(exportFile); buffer = new BufferedOutputStream(os); fileWriter = new OutputStreamWriter(buffer, CharEncoding.UTF_16); fileWriter.write(fieldNoteBuffer.toString()); - } catch (IOException e) { + } catch (final IOException e) { Log.e("FieldnoteExport.ExportTask export", e); return false; } finally { @@ -181,17 +181,11 @@ class FieldnoteExport extends AbstractExport { } final String uri = "http://www.geocaching.com/my/uploadfieldnotes.aspx"; - String page = Network.getResponseData(Network.getRequest(uri)); + final String page = Login.getRequestLogged(uri, null); - if (!Login.getLoginStatus(page)) { - // Login.isActualLoginStatus() was wrong, we are not logged in - final StatusCode loginState = Login.login(); - if (loginState == StatusCode.NO_ERROR) { - page = Network.getResponseData(Network.getRequest(uri)); - } else { - Log.e("FieldnoteExport.ExportTask upload: No login (error: " + loginState + ')'); - return false; - } + if (StringUtils.isBlank(page)) { + Log.e("FieldnoteExport.ExportTask get page: No data from server"); + return false; } final String[] viewstates = Login.getViewstates(page); diff --git a/main/src/cgeo/geocaching/export/GpxExport.java b/main/src/cgeo/geocaching/export/GpxExport.java index 66345da..ab2e281 100644 --- a/main/src/cgeo/geocaching/export/GpxExport.java +++ b/main/src/cgeo/geocaching/export/GpxExport.java @@ -1,24 +1,12 @@ package cgeo.geocaching.export; import cgeo.geocaching.Geocache; -import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.Settings; -import cgeo.geocaching.Waypoint; -import cgeo.geocaching.cgData; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; -import cgeo.geocaching.enumerations.CacheAttribute; -import cgeo.geocaching.enumerations.LoadFlags; -import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.utils.AsyncTaskWithProgress; -import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.Log; -import cgeo.geocaching.utils.XmlUtils; -import cgeo.org.kxml2.io.KXmlSerializer; - -import org.apache.commons.lang3.StringUtils; -import org.xmlpull.v1.XmlSerializer; import android.app.Activity; import android.app.AlertDialog; @@ -39,22 +27,11 @@ import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Locale; -import java.util.Set; class GpxExport extends AbstractExport { - private static final SimpleDateFormat dateFormatZ = new SimpleDateFormat("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"; - - /** - * During the export, only this number of geocaches is fully loaded into memory. - */ - public static final int CACHES_PER_BATCH = 100; protected GpxExport() { super(getString(R.string.export_gpx)); @@ -62,7 +39,7 @@ class GpxExport extends AbstractExport { @Override public void export(final List<Geocache> caches, final Activity activity) { - String[] geocodes = getGeocodes(caches); + final String[] geocodes = getGeocodes(caches); if (null == activity) { // No activity given, so no user interaction possible. // Start export with default parameters. @@ -75,10 +52,10 @@ class GpxExport extends AbstractExport { } private Dialog getExportDialog(final String[] geocodes, final Activity activity) { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); // AlertDialog has always dark style, so we have to apply it as well always - View layout = View.inflate(new ContextThemeWrapper(activity, R.style.dark), R.layout.gpx_export_dialog, null); + final View layout = View.inflate(new ContextThemeWrapper(activity, R.style.dark), R.layout.gpx_export_dialog, null); builder.setView(layout); final TextView text = (TextView) layout.findViewById(R.id.info); @@ -108,7 +85,7 @@ class GpxExport extends AbstractExport { } private static String[] getGeocodes(final List<Geocache> caches) { - ArrayList<String> allGeocodes = new ArrayList<String>(caches.size()); + final ArrayList<String> allGeocodes = new ArrayList<String>(caches.size()); for (final Geocache geocache : caches) { allGeocodes.add(geocache.getGeocode()); } @@ -117,7 +94,6 @@ class GpxExport extends AbstractExport { protected class ExportTask extends AsyncTaskWithProgress<String, File> { private final Activity activity; - private int countExported = 0; /** * Instantiates and configures the task for exporting field notes. @@ -137,7 +113,7 @@ class GpxExport extends AbstractExport { return null; } - List<String> allGeocodes = new ArrayList<String>(Arrays.asList(geocodes)); + final List<String> allGeocodes = new ArrayList<String>(Arrays.asList(geocodes)); setMessage(cgeoapplication.getInstance().getResources().getQuantityString(R.plurals.cache_counts, allGeocodes.size(), allGeocodes.size())); @@ -148,38 +124,21 @@ class GpxExport extends AbstractExport { final File exportLocation = new File(Settings.getGpxExportDir()); exportLocation.mkdirs(); - final XmlSerializer gpx = new KXmlSerializer(); writer = new BufferedWriter(new FileWriter(exportFile)); - gpx.setOutput(writer); + new GpxSerializer().writeGPX(allGeocodes, writer, new GpxSerializer.ProgressListener() { - gpx.startDocument("UTF-8", true); - gpx.setPrefix("", PREFIX_GPX); - gpx.setPrefix("xsi", PREFIX_XSI); - gpx.setPrefix("groundspeak", PREFIX_GROUNDSPEAK); - gpx.startTag(PREFIX_GPX, "gpx"); - gpx.attribute("", "version", "1.0"); - gpx.attribute("", "creator", "c:geo - http://www.cgeo.org/"); - gpx.attribute(PREFIX_XSI, "schemaLocation", - PREFIX_GPX + " http://www.topografix.com/GPX/1/0/gpx.xsd " + - PREFIX_GROUNDSPEAK + " http://www.groundspeak.com/cache/1/0/1/cache.xsd"); - - // Split the overall set of geocodes into small chunks. That is a compromise between memory efficiency (because - // we don't load all caches fully into memory) and speed (because we don't query each cache separately). - while (!allGeocodes.isEmpty()) { - final List<String> batch = allGeocodes.subList(0, Math.min(CACHES_PER_BATCH, allGeocodes.size())); - exportBatch(gpx, batch); - batch.clear(); - } - - gpx.endTag(PREFIX_GPX, "gpx"); - gpx.endDocument(); + @Override + public void publishProgress(int countExported) { + ExportTask.this.publishProgress(countExported); + } + }); } catch (final Exception e) { Log.e("GpxExport.ExportTask export", e); if (writer != null) { try { writer.close(); - } catch (IOException e1) { + } catch (final IOException e1) { // Ignore double error } } @@ -194,184 +153,13 @@ class GpxExport extends AbstractExport { return exportFile; } - private void exportBatch(final XmlSerializer gpx, Collection<String> geocodesOfBatch) throws IOException { - Set<Geocache> caches = cgData.loadCaches(geocodesOfBatch, LoadFlags.LOAD_ALL_DB_ONLY); - for (Geocache cache : caches) { - gpx.startTag(PREFIX_GPX, "wpt"); - gpx.attribute("", "lat", Double.toString(cache.getCoords().getLatitude())); - gpx.attribute("", "lon", Double.toString(cache.getCoords().getLongitude())); - - final Date hiddenDate = cache.getHiddenDate(); - if (hiddenDate != null) { - XmlUtils.simpleText(gpx, PREFIX_GPX, "time", dateFormatZ.format(hiddenDate)); - } - - XmlUtils.multipleTexts(gpx, PREFIX_GPX, - "name", cache.getGeocode(), - "desc", cache.getName(), - "url", cache.getUrl(), - "urlname", cache.getName(), - "sym", cache.isFound() ? "Geocache Found" : "Geocache", - "type", "Geocache|" + cache.getType().pattern); - - gpx.startTag(PREFIX_GROUNDSPEAK, "cache"); - gpx.attribute("", "id", cache.getCacheId()); - gpx.attribute("", "available", !cache.isDisabled() ? "True" : "False"); - gpx.attribute("", "archives", cache.isArchived() ? "True" : "False"); - - XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK, - "name", cache.getName(), - "placed_by", cache.getOwnerDisplayName(), - "owner", cache.getOwnerUserId(), - "type", cache.getType().pattern, - "container", cache.getSize().id, - "difficulty", Float.toString(cache.getDifficulty()), - "terrain", Float.toString(cache.getTerrain()), - "country", cache.getLocation(), - "state", "", - "encoded_hints", cache.getHint()); - - writeAttributes(gpx, cache); - - gpx.startTag(PREFIX_GROUNDSPEAK, "short_description"); - gpx.attribute("", "html", BaseUtils.containsHtml(cache.getShortDescription()) ? "True" : "False"); - gpx.text(cache.getShortDescription()); - gpx.endTag(PREFIX_GROUNDSPEAK, "short_description"); - - gpx.startTag(PREFIX_GROUNDSPEAK, "long_description"); - gpx.attribute("", "html", BaseUtils.containsHtml(cache.getDescription()) ? "True" : "False"); - gpx.text(cache.getDescription()); - gpx.endTag(PREFIX_GROUNDSPEAK, "long_description"); - - writeLogs(gpx, cache); - - gpx.endTag(PREFIX_GROUNDSPEAK, "cache"); - gpx.endTag(PREFIX_GPX, "wpt"); - - writeWaypoints(gpx, cache); - - countExported++; - publishProgress(countExported); - } - } - - private void writeWaypoints(final XmlSerializer gpx, final Geocache cache) throws IOException { - List<Waypoint> waypoints = cache.getWaypoints(); - List<Waypoint> ownWaypoints = new ArrayList<Waypoint>(waypoints.size()); - List<Waypoint> originWaypoints = new ArrayList<Waypoint>(waypoints.size()); - for (Waypoint wp : cache.getWaypoints()) { - if (wp.isUserDefined()) { - ownWaypoints.add(wp); - } else { - originWaypoints.add(wp); - } - } - int maxPrefix = 0; - for (Waypoint wp : originWaypoints) { - String prefix = wp.getPrefix(); - try { - final int numericPrefix = Integer.parseInt(prefix); - maxPrefix = Math.max(numericPrefix, maxPrefix); - } catch (NumberFormatException ex) { - // ignore non numeric prefix, as it should be unique in the list of non-own waypoints already - } - writeCacheWaypoint(gpx, wp, prefix); - } - // Prefixes must be unique. There use numeric strings as prefixes in OWN waypoints - for (Waypoint wp : ownWaypoints) { - maxPrefix++; - String prefix = StringUtils.leftPad(String.valueOf(maxPrefix), 2, '0'); - writeCacheWaypoint(gpx, wp, prefix); - } - } - - /** - * Writes one waypoint entry for cache waypoint. - * - * @param cache - * The - * @param wp - * @param prefix - * @throws IOException - */ - private void writeCacheWaypoint(final XmlSerializer gpx, final Waypoint wp, final String prefix) throws IOException { - final Geopoint coords = wp.getCoords(); - // TODO: create some extension to GPX to include waypoint without coords - if (coords != null) { - gpx.startTag(PREFIX_GPX, "wpt"); - gpx.attribute("", "lat", Double.toString(coords.getLatitude())); - gpx.attribute("", "lon", Double.toString(coords.getLongitude())); - XmlUtils.multipleTexts(gpx, PREFIX_GPX, - "name", prefix + wp.getGeocode().substring(2), - "cmt", wp.getNote(), - "desc", wp.getName(), - "sym", wp.getWaypointType().toString(), //TODO: Correct identifier string - "type", "Waypoint|" + wp.getWaypointType().toString()); //TODO: Correct identifier string - gpx.endTag(PREFIX_GPX, "wpt"); - } - } - - private void writeLogs(final XmlSerializer gpx, final Geocache cache) throws IOException { - if (cache.getLogs().isEmpty()) { - return; - } - gpx.startTag(PREFIX_GROUNDSPEAK, "logs"); - - for (LogEntry log : cache.getLogs()) { - gpx.startTag(PREFIX_GROUNDSPEAK, "log"); - gpx.attribute("", "id", Integer.toString(log.id)); - - XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK, - "date", dateFormatZ.format(new Date(log.date)), - "type", log.type.type); - - gpx.startTag(PREFIX_GROUNDSPEAK, "finder"); - gpx.attribute("", "id", ""); - gpx.text(log.author); - gpx.endTag(PREFIX_GROUNDSPEAK, "finder"); - - gpx.startTag(PREFIX_GROUNDSPEAK, "text"); - gpx.attribute("", "encoded", "False"); - gpx.text(log.log); - gpx.endTag(PREFIX_GROUNDSPEAK, "text"); - - gpx.endTag(PREFIX_GROUNDSPEAK, "log"); - } - - gpx.endTag(PREFIX_GROUNDSPEAK, "logs"); - } - - private void writeAttributes(final XmlSerializer gpx, final Geocache cache) throws IOException { - if (cache.getAttributes().isEmpty()) { - return; - } - //TODO: Attribute conversion required: English verbose name, gpx-id - gpx.startTag(PREFIX_GROUNDSPEAK, "attributes"); - - for (String attribute : cache.getAttributes()) { - final CacheAttribute attr = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attribute)); - if (attr == null) { - continue; - } - final boolean enabled = CacheAttribute.isEnabled(attribute); - - gpx.startTag(PREFIX_GROUNDSPEAK, "attribute"); - gpx.attribute("", "id", Integer.toString(attr.gcid)); - gpx.attribute("", "inc", enabled ? "1" : "0"); - gpx.text(attr.getL10n(enabled)); - gpx.endTag(PREFIX_GROUNDSPEAK, "attribute"); - } - - gpx.endTag(PREFIX_GROUNDSPEAK, "attributes"); - } - @Override protected void onPostExecuteInternal(final File exportFile) { if (null != activity) { if (exportFile != null) { ActivityMixin.showToast(activity, getName() + ' ' + getString(R.string.export_exportedto) + ": " + exportFile.toString()); if (Settings.getShareAfterExport()) { - Intent shareIntent = new Intent(); + final Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(exportFile)); shareIntent.setType("application/xml"); diff --git a/main/src/cgeo/geocaching/export/GpxSerializer.java b/main/src/cgeo/geocaching/export/GpxSerializer.java new file mode 100644 index 0000000..2d25296 --- /dev/null +++ b/main/src/cgeo/geocaching/export/GpxSerializer.java @@ -0,0 +1,255 @@ +package cgeo.geocaching.export; + +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; +import cgeo.geocaching.utils.TextUtils; +import cgeo.geocaching.utils.XmlUtils; +import cgeo.org.kxml2.io.KXmlSerializer; + +import org.apache.commons.lang3.StringUtils; +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; +import java.util.List; +import java.util.Locale; +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); + 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"; + + /** + * During the export, only this number of geocaches is fully loaded into memory. + */ + public static final int CACHES_PER_BATCH = 100; + + /** + * counter for exported caches, used for progress reporting + */ + private int countExported; + private ProgressListener progressListener; + private final XmlSerializer gpx = new KXmlSerializer(); + + protected static interface ProgressListener { + + void publishProgress(int countExported); + + } + + public void writeGPX(List<String> allGeocodesIn, Writer writer, final ProgressListener progressListener) throws IOException { + // create a copy of the geocode list, as we need to modify it, but it might be immutable + final ArrayList<String> allGeocodes = new ArrayList<String>(allGeocodesIn); + + this.progressListener = progressListener; + gpx.setOutput(writer); + + gpx.startDocument("UTF-8", true); + gpx.setPrefix("", PREFIX_GPX); + gpx.setPrefix("xsi", PREFIX_XSI); + gpx.setPrefix("groundspeak", PREFIX_GROUNDSPEAK); + gpx.startTag(PREFIX_GPX, "gpx"); + gpx.attribute("", "version", "1.0"); + gpx.attribute("", "creator", "c:geo - http://www.cgeo.org/"); + gpx.attribute(PREFIX_XSI, "schemaLocation", + PREFIX_GPX + " http://www.topografix.com/GPX/1/0/gpx.xsd " + + PREFIX_GROUNDSPEAK + " http://www.groundspeak.com/cache/1/0/1/cache.xsd"); + + // Split the overall set of geocodes into small chunks. That is a compromise between memory efficiency (because + // we don't load all caches fully into memory) and speed (because we don't query each cache separately). + while (!allGeocodes.isEmpty()) { + final List<String> batch = allGeocodes.subList(0, Math.min(CACHES_PER_BATCH, allGeocodes.size())); + exportBatch(gpx, batch); + batch.clear(); + } + + gpx.endTag(PREFIX_GPX, "gpx"); + gpx.endDocument(); + } + + private void exportBatch(final XmlSerializer gpx, Collection<String> geocodesOfBatch) throws IOException { + final Set<Geocache> caches = cgData.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())); + gpx.attribute("", "lon", Double.toString(cache.getCoords().getLongitude())); + + final Date hiddenDate = cache.getHiddenDate(); + if (hiddenDate != null) { + XmlUtils.simpleText(gpx, PREFIX_GPX, "time", dateFormatZ.format(hiddenDate)); + } + + XmlUtils.multipleTexts(gpx, PREFIX_GPX, + "name", cache.getGeocode(), + "desc", cache.getName(), + "url", cache.getUrl(), + "urlname", cache.getName(), + "sym", cache.isFound() ? "Geocache Found" : "Geocache", + "type", "Geocache|" + cache.getType().pattern); + + gpx.startTag(PREFIX_GROUNDSPEAK, "cache"); + gpx.attribute("", "id", cache.getCacheId()); + gpx.attribute("", "available", !cache.isDisabled() ? "True" : "False"); + gpx.attribute("", "archives", cache.isArchived() ? "True" : "False"); + + XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK, + "name", cache.getName(), + "placed_by", cache.getOwnerDisplayName(), + "owner", cache.getOwnerUserId(), + "type", cache.getType().pattern, + "container", cache.getSize().id, + "difficulty", Float.toString(cache.getDifficulty()), + "terrain", Float.toString(cache.getTerrain()), + "country", cache.getLocation(), + "state", "", + "encoded_hints", cache.getHint()); + + writeAttributes(cache); + + gpx.startTag(PREFIX_GROUNDSPEAK, "short_description"); + gpx.attribute("", "html", TextUtils.containsHtml(cache.getShortDescription()) ? "True" : "False"); + gpx.text(cache.getShortDescription()); + gpx.endTag(PREFIX_GROUNDSPEAK, "short_description"); + + gpx.startTag(PREFIX_GROUNDSPEAK, "long_description"); + gpx.attribute("", "html", TextUtils.containsHtml(cache.getDescription()) ? "True" : "False"); + gpx.text(cache.getDescription()); + gpx.endTag(PREFIX_GROUNDSPEAK, "long_description"); + + writeLogs(cache); + + gpx.endTag(PREFIX_GROUNDSPEAK, "cache"); + gpx.endTag(PREFIX_GPX, "wpt"); + + writeWaypoints(cache); + + countExported++; + if (progressListener != null) { + progressListener.publishProgress(countExported); + } + } + } + + private void writeWaypoints(final Geocache cache) throws IOException { + final List<Waypoint> waypoints = cache.getWaypoints(); + final List<Waypoint> ownWaypoints = new ArrayList<Waypoint>(waypoints.size()); + final List<Waypoint> originWaypoints = new ArrayList<Waypoint>(waypoints.size()); + for (final Waypoint wp : cache.getWaypoints()) { + if (wp.isUserDefined()) { + ownWaypoints.add(wp); + } else { + originWaypoints.add(wp); + } + } + int maxPrefix = 0; + for (final Waypoint wp : originWaypoints) { + final String prefix = wp.getPrefix(); + try { + final int numericPrefix = Integer.parseInt(prefix); + maxPrefix = Math.max(numericPrefix, maxPrefix); + } catch (final NumberFormatException ex) { + // ignore non numeric prefix, as it should be unique in the list of non-own waypoints already + } + writeCacheWaypoint(wp, prefix); + } + // Prefixes must be unique. There use numeric strings as prefixes in OWN waypoints + for (final Waypoint wp : ownWaypoints) { + maxPrefix++; + final String prefix = StringUtils.leftPad(String.valueOf(maxPrefix), 2, '0'); + writeCacheWaypoint(wp, prefix); + } + } + + /** + * Writes one waypoint entry for cache waypoint. + * + * @param cache + * The + * @param wp + * @param prefix + * @throws IOException + */ + private void writeCacheWaypoint(final Waypoint wp, final String prefix) throws IOException { + final Geopoint coords = wp.getCoords(); + // TODO: create some extension to GPX to include waypoint without coords + if (coords != null) { + gpx.startTag(PREFIX_GPX, "wpt"); + gpx.attribute("", "lat", Double.toString(coords.getLatitude())); + gpx.attribute("", "lon", Double.toString(coords.getLongitude())); + XmlUtils.multipleTexts(gpx, PREFIX_GPX, + "name", prefix + wp.getGeocode().substring(2), + "cmt", wp.getNote(), + "desc", wp.getName(), + "sym", wp.getWaypointType().toString(), //TODO: Correct identifier string + "type", "Waypoint|" + wp.getWaypointType().toString()); //TODO: Correct identifier string + gpx.endTag(PREFIX_GPX, "wpt"); + } + } + + private void writeLogs(final Geocache cache) throws IOException { + if (cache.getLogs().isEmpty()) { + return; + } + gpx.startTag(PREFIX_GROUNDSPEAK, "logs"); + + for (final LogEntry log : cache.getLogs()) { + gpx.startTag(PREFIX_GROUNDSPEAK, "log"); + gpx.attribute("", "id", Integer.toString(log.id)); + + XmlUtils.multipleTexts(gpx, PREFIX_GROUNDSPEAK, + "date", dateFormatZ.format(new Date(log.date)), + "type", log.type.type); + + gpx.startTag(PREFIX_GROUNDSPEAK, "finder"); + gpx.attribute("", "id", ""); + gpx.text(log.author); + gpx.endTag(PREFIX_GROUNDSPEAK, "finder"); + + gpx.startTag(PREFIX_GROUNDSPEAK, "text"); + gpx.attribute("", "encoded", "False"); + gpx.text(log.log); + gpx.endTag(PREFIX_GROUNDSPEAK, "text"); + + gpx.endTag(PREFIX_GROUNDSPEAK, "log"); + } + + gpx.endTag(PREFIX_GROUNDSPEAK, "logs"); + } + + private void writeAttributes(final Geocache cache) throws IOException { + if (cache.getAttributes().isEmpty()) { + return; + } + //TODO: Attribute conversion required: English verbose name, gpx-id + gpx.startTag(PREFIX_GROUNDSPEAK, "attributes"); + + for (final String attribute : cache.getAttributes()) { + final CacheAttribute attr = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attribute)); + if (attr == null) { + continue; + } + final boolean enabled = CacheAttribute.isEnabled(attribute); + + gpx.startTag(PREFIX_GROUNDSPEAK, "attribute"); + gpx.attribute("", "id", Integer.toString(attr.gcid)); + gpx.attribute("", "inc", enabled ? "1" : "0"); + gpx.text(attr.getL10n(enabled)); + gpx.endTag(PREFIX_GROUNDSPEAK, "attribute"); + } + + gpx.endTag(PREFIX_GROUNDSPEAK, "attributes"); + } + +} diff --git a/main/src/cgeo/geocaching/files/GPXImporter.java b/main/src/cgeo/geocaching/files/GPXImporter.java index 10d791b..0b2e49f 100644 --- a/main/src/cgeo/geocaching/files/GPXImporter.java +++ b/main/src/cgeo/geocaching/files/GPXImporter.java @@ -57,12 +57,12 @@ public class GPXImporter { private static final List<String> GPX_MIME_TYPES = Arrays.asList("text/xml", "application/xml"); private static final List<String> ZIP_MIME_TYPES = Arrays.asList("application/zip", "application/x-compressed", "application/x-zip-compressed", "application/x-zip", "application/octet-stream"); - private Progress progress = new Progress(true); + private final Progress progress = new Progress(true); - private Resources res; - private int listId; - private IAbstractActivity fromActivity; - private Handler importFinishedHandler; + private final Resources res; + private final int listId; + private final IAbstractActivity fromActivity; + private final Handler importFinishedHandler; public GPXImporter(final IAbstractActivity fromActivity, final int listId, final Handler importFinishedHandler) { this.listId = listId; @@ -143,14 +143,12 @@ public class GPXImporter { final Collection<Geocache> caches = doImport(); Log.i("Imported successfully " + caches.size() + " caches."); - final SearchResult search = new SearchResult(); - for (Geocache cache : caches) { - search.addAndPutInCache(cache); - } + final SearchResult search = new SearchResult(caches); + // Do not put imported caches into the cachecache. That would consume lots of memory for no benefit. if (Settings.isStoreOfflineMaps() || Settings.isStoreOfflineWpMaps()) { importStepHandler.sendMessage(importStepHandler.obtainMessage(IMPORT_STEP_STORE_STATIC_MAPS, R.string.gpx_import_store_static_maps, search.getCount())); - boolean finishedWithoutCancel = importStaticMaps(search); + final boolean finishedWithoutCancel = importStaticMaps(search); // Skip last message if static maps where canceled if (!finishedWithoutCancel) { return; @@ -158,16 +156,16 @@ public class GPXImporter { } importStepHandler.sendMessage(importStepHandler.obtainMessage(IMPORT_STEP_FINISHED, search.getCount(), 0, search)); - } catch (IOException e) { + } catch (final IOException e) { Log.i("Importing caches failed - error reading data: ", e); importStepHandler.sendMessage(importStepHandler.obtainMessage(IMPORT_STEP_FINISHED_WITH_ERROR, R.string.gpx_import_error_io, 0, e.getLocalizedMessage())); - } catch (ParserException e) { + } catch (final ParserException e) { Log.i("Importing caches failed - data format error", e); importStepHandler.sendMessage(importStepHandler.obtainMessage(IMPORT_STEP_FINISHED_WITH_ERROR, R.string.gpx_import_error_parser, 0, e.getLocalizedMessage())); - } catch (CancellationException e) { + } catch (final CancellationException e) { Log.i("Importing caches canceled"); importStepHandler.sendMessage(importStepHandler.obtainMessage(IMPORT_STEP_CANCELED)); - } catch (Exception e) { + } catch (final Exception e) { Log.e("Importing caches failed - unknown error: ", e); importStepHandler.sendMessage(importStepHandler.obtainMessage(IMPORT_STEP_FINISHED_WITH_ERROR, R.string.gpx_import_error_unexpected, 0, e.getLocalizedMessage())); } @@ -207,14 +205,14 @@ public class GPXImporter { protected Collection<Geocache> doImport() throws IOException, ParserException { Log.i("Import LOC file: " + file.getAbsolutePath()); importStepHandler.sendMessage(importStepHandler.obtainMessage(IMPORT_STEP_READ_FILE, R.string.gpx_import_loading_caches, (int) file.length())); - LocParser parser = new LocParser(listId); + final LocParser parser = new LocParser(listId); return parser.parse(file, progressHandler); } } static class ImportLocAttachmentThread extends ImportThread { private final Uri uri; - private ContentResolver contentResolver; + private final ContentResolver contentResolver; public ImportLocAttachmentThread(Uri uri, ContentResolver contentResolver, int listId, Handler importStepHandler, CancellableHandler progressHandler) { super(listId, importStepHandler, progressHandler); @@ -226,8 +224,8 @@ public class GPXImporter { protected Collection<Geocache> doImport() throws IOException, ParserException { Log.i("Import LOC from uri: " + uri); importStepHandler.sendMessage(importStepHandler.obtainMessage(IMPORT_STEP_READ_FILE, R.string.gpx_import_loading_caches, -1)); - InputStream is = contentResolver.openInputStream(uri); - LocParser parser = new LocParser(listId); + final InputStream is = contentResolver.openInputStream(uri); + final LocParser parser = new LocParser(listId); try { return parser.parse(is, progressHandler); } finally { @@ -247,7 +245,7 @@ public class GPXImporter { try { // try to parse cache file as GPX 10 return doImport(new GPX10Parser(listId)); - } catch (ParserException pe) { + } catch (final ParserException pe) { // didn't work -> lets try GPX11 return doImport(new GPX11Parser(listId)); } @@ -285,7 +283,7 @@ public class GPXImporter { static class ImportGpxAttachmentThread extends ImportGpxThread { private final Uri uri; - private ContentResolver contentResolver; + private final ContentResolver contentResolver; public ImportGpxAttachmentThread(Uri uri, ContentResolver contentResolver, int listId, Handler importStepHandler, CancellableHandler progressHandler) { super(listId, importStepHandler, progressHandler); @@ -297,7 +295,7 @@ public class GPXImporter { protected Collection<Geocache> doImport(GPXParser parser) throws IOException, ParserException { Log.i("Import GPX from uri: " + uri); importStepHandler.sendMessage(importStepHandler.obtainMessage(IMPORT_STEP_READ_FILE, R.string.gpx_import_loading_caches, -1)); - InputStream is = contentResolver.openInputStream(uri); + final InputStream is = contentResolver.openInputStream(uri); try { return parser.parse(is, progressHandler); } finally { @@ -371,7 +369,7 @@ public class GPXImporter { static class ImportGpxZipAttachmentThread extends ImportGpxZipThread { private final Uri uri; - private ContentResolver contentResolver; + private final ContentResolver contentResolver; public ImportGpxZipAttachmentThread(Uri uri, ContentResolver contentResolver, int listId, Handler importStepHandler, CancellableHandler progressHandler) { super(listId, importStepHandler, progressHandler); @@ -394,13 +392,11 @@ public class GPXImporter { }; final private Handler importStepHandler = new Handler() { - private boolean showProgressAfterCancel = false; - @Override public void handleMessage(Message msg) { switch (msg.what) { case IMPORT_STEP_START: - Message cancelMessage = importStepHandler.obtainMessage(IMPORT_STEP_CANCEL); + final Message cancelMessage = importStepHandler.obtainMessage(IMPORT_STEP_CANCEL); progress.show((Context) fromActivity, res.getString(R.string.gpx_import_title_reading_file), res.getString(R.string.gpx_import_loading_caches), ProgressDialog.STYLE_HORIZONTAL, cancelMessage); break; @@ -412,7 +408,7 @@ public class GPXImporter { case IMPORT_STEP_STORE_STATIC_MAPS: progress.dismiss(); - Message skipMessage = importStepHandler.obtainMessage(IMPORT_STEP_STATIC_MAPS_SKIPPED, msg.arg2, 0); + final Message skipMessage = importStepHandler.obtainMessage(IMPORT_STEP_STATIC_MAPS_SKIPPED, msg.arg2, 0); progress.show((Context) fromActivity, res.getString(R.string.gpx_import_title_static_maps), res.getString(R.string.gpx_import_store_static_maps), ProgressDialog.STYLE_HORIZONTAL, skipMessage); progress.setMaxProgressAndReset(msg.arg2); break; @@ -420,7 +416,7 @@ public class GPXImporter { case IMPORT_STEP_STATIC_MAPS_SKIPPED: progress.dismiss(); progressHandler.cancel(); - StringBuilder bufferSkipped = new StringBuilder(20); + final StringBuilder bufferSkipped = new StringBuilder(20); bufferSkipped.append(res.getString(R.string.gpx_import_static_maps_skipped)).append(", ").append(msg.arg1).append(' ').append(res.getString(R.string.gpx_import_caches_imported)); fromActivity.helpDialog(res.getString(R.string.gpx_import_title_caches_imported), bufferSkipped.toString()); importFinished(); @@ -444,11 +440,8 @@ public class GPXImporter { break; case IMPORT_STEP_CANCELED: - StringBuilder bufferCanceled = new StringBuilder(20); + final StringBuilder bufferCanceled = new StringBuilder(20); bufferCanceled.append(res.getString(R.string.gpx_import_canceled)); - if (showProgressAfterCancel) { - bufferCanceled.append(", ").append(progress.getProgress()).append(' ').append(res.getString(R.string.gpx_import_caches_imported)); - } fromActivity.showShortToast(bufferCanceled.toString()); importFinished(); break; @@ -469,13 +462,13 @@ public class GPXImporter { return null; } final String gpxFileName = gpxfile.getName(); - File dir = gpxfile.getParentFile(); - String[] filenameList = dir.list(); - for (String filename : filenameList) { + final File dir = gpxfile.getParentFile(); + final String[] filenameList = dir.list(); + for (final String filename : filenameList) { if (!StringUtils.containsIgnoreCase(filename, WAYPOINTS_FILE_SUFFIX)) { continue; } - String expectedGpxFileName = StringUtils.substringBeforeLast(filename, WAYPOINTS_FILE_SUFFIX) + final String expectedGpxFileName = StringUtils.substringBeforeLast(filename, WAYPOINTS_FILE_SUFFIX) + StringUtils.substringAfterLast(filename, WAYPOINTS_FILE_SUFFIX); if (gpxFileName.equals(expectedGpxFileName)) { return filename; diff --git a/main/src/cgeo/geocaching/files/GPXParser.java b/main/src/cgeo/geocaching/files/GPXParser.java index 8412207..f5ae381 100644 --- a/main/src/cgeo/geocaching/files/GPXParser.java +++ b/main/src/cgeo/geocaching/files/GPXParser.java @@ -206,7 +206,7 @@ public abstract class GPXParser extends FileParser { String stringName; try { stringName = cgeoapplication.getInstance().getResources().getResourceName(stringId); - } catch (NullPointerException e) { + } catch (final NullPointerException e) { return null; } if (stringName == null) { @@ -272,7 +272,7 @@ public abstract class GPXParser extends FileParser { Double.valueOf(longitude))); } } - } catch (Exception e) { + } catch (final Exception e) { Log.w("Failed to parse waypoint's latitude and/or longitude."); } } @@ -313,6 +313,8 @@ public abstract class GPXParser extends FileParser { // finally store the cache in the database result.add(geocode); cgData.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(); showProgressMessage(progressHandler, progressStream.getProgress()); } else if (StringUtils.isNotBlank(cache.getName()) @@ -361,7 +363,7 @@ public abstract class GPXParser extends FileParser { public void end(String body) { try { cache.setHidden(parseDate(body)); - } catch (Exception e) { + } catch (final Exception e) { Log.w("Failed to parse cache date", e); } } @@ -441,7 +443,7 @@ public abstract class GPXParser extends FileParser { } final MatcherWrapper matcherCode = new MatcherWrapper(patternUrlGeocode, url); if (matcherCode.matches()) { - String geocode = matcherCode.group(1); + final String geocode = matcherCode.group(1); cache.setGeocode(geocode); } } @@ -463,7 +465,7 @@ public abstract class GPXParser extends FileParser { final Element cacheParent = getCacheParent(waypoint); // GSAK extensions - for (String gsakNamespace : GSAK_NS) { + for (final String gsakNamespace : GSAK_NS) { final Element gsak = cacheParent.getChild(gsakNamespace, "wptExtension"); gsak.getChild(gsakNamespace, "Watch").setEndTextElementListener(new EndTextElementListener() { @@ -481,7 +483,7 @@ public abstract class GPXParser extends FileParser { } // 3 different versions of the GC schema - for (String nsGC : nsGCList) { + for (final String nsGC : nsGCList) { // waypoints.cache final Element gcCache = cacheParent.getChild(nsGC, "cache"); @@ -499,7 +501,7 @@ public abstract class GPXParser extends FileParser { if (attrs.getIndex("available") > -1) { cache.setDisabled(!attrs.getValue("available").equalsIgnoreCase("true")); } - } catch (Exception e) { + } catch (final Exception e) { Log.w("Failed to parse cache attributes."); } } @@ -568,14 +570,14 @@ public abstract class GPXParser extends FileParser { public void start(Attributes attrs) { try { if (attrs.getIndex("id") > -1 && attrs.getIndex("inc") > -1) { - int attributeId = Integer.parseInt(attrs.getValue("id")); - boolean attributeActive = Integer.parseInt(attrs.getValue("inc")) != 0; - String internalId = CacheAttributeTranslator.getInternalId(attributeId, attributeActive); + final int attributeId = Integer.parseInt(attrs.getValue("id")); + final boolean attributeActive = Integer.parseInt(attrs.getValue("inc")) != 0; + final String internalId = CacheAttributeTranslator.getInternalId(attributeId, attributeActive); if (internalId != null) { cache.getAttributes().add(internalId); } } - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { // nothing } } @@ -588,7 +590,7 @@ public abstract class GPXParser extends FileParser { public void end(String body) { try { cache.setDifficulty(Float.parseFloat(body)); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.w("Failed to parse difficulty", e); } } @@ -601,7 +603,7 @@ public abstract class GPXParser extends FileParser { public void end(String body) { try { cache.setTerrain(Float.parseFloat(body)); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.w("Failed to parse terrain", e); } } @@ -625,7 +627,7 @@ public abstract class GPXParser extends FileParser { @Override public void end(String state) { - String trimmedState = state.trim(); + final String trimmedState = state.trim(); if (StringUtils.isNotEmpty(trimmedState)) { // state can be completely empty if (StringUtils.isBlank(cache.getLocation())) { cache.setLocation(validate(state)); @@ -678,7 +680,7 @@ public abstract class GPXParser extends FileParser { if (attrs.getIndex("ref") > -1) { trackable.setGeocode(attrs.getValue("ref")); } - } catch (Exception e) { + } catch (final Exception e) { // nothing } } @@ -722,7 +724,7 @@ public abstract class GPXParser extends FileParser { if (attrs.getIndex("id") > -1) { log.id = Integer.parseInt(attrs.getValue("id")); } - } catch (Exception e) { + } catch (final Exception e) { // nothing } } @@ -745,7 +747,7 @@ public abstract class GPXParser extends FileParser { public void end(String body) { try { log.date = parseDate(body).getTime(); - } catch (Exception e) { + } catch (final Exception e) { Log.w("Failed to parse log date", e); } } @@ -784,7 +786,7 @@ public abstract class GPXParser extends FileParser { progressStream = new ProgressInputStream(stream); Xml.parse(progressStream, Xml.Encoding.UTF_8, root.getContentHandler()); return cgData.loadCaches(result, EnumSet.of(LoadFlag.LOAD_DB_MINIMAL)); - } catch (SAXException e) { + } 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); } @@ -831,7 +833,7 @@ public abstract class GPXParser extends FileParser { return WaypointType.FINAL; } // this is not fully correct, but lets also look for localized waypoint types - for (WaypointType waypointType : WaypointType.ALL_TYPES_EXCEPT_OWN_AND_ORIGINAL) { + for (final WaypointType waypointType : WaypointType.ALL_TYPES_EXCEPT_OWN_AND_ORIGINAL) { final String localized = waypointType.getL10n(); if (StringUtils.isNotEmpty(localized)) { if (localized.equalsIgnoreCase(sym)) { diff --git a/main/src/cgeo/geocaching/network/HtmlImage.java b/main/src/cgeo/geocaching/network/HtmlImage.java index 616a951..0649e12 100644 --- a/main/src/cgeo/geocaching/network/HtmlImage.java +++ b/main/src/cgeo/geocaching/network/HtmlImage.java @@ -7,7 +7,7 @@ import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.files.LocalStorage; import cgeo.geocaching.utils.IOUtils; -import cgeo.geocaching.utils.ImageHelper; +import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; @@ -132,7 +132,7 @@ public class HtmlImage implements Html.ImageGetter { } } - return imagePre != null ? ImageHelper.scaleBitmapToFitDisplay(imagePre) : null; + return imagePre != null ? ImageUtils.scaleBitmapToFitDisplay(imagePre) : null; } /** diff --git a/main/src/cgeo/geocaching/network/Network.java b/main/src/cgeo/geocaching/network/Network.java index 5a8cbb2..57196c5 100644 --- a/main/src/cgeo/geocaching/network/Network.java +++ b/main/src/cgeo/geocaching/network/Network.java @@ -2,7 +2,7 @@ package cgeo.geocaching.network; import cgeo.geocaching.Settings; import cgeo.geocaching.files.LocalStorage; -import cgeo.geocaching.utils.BaseUtils; +import cgeo.geocaching.utils.TextUtils; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.Header; @@ -418,7 +418,7 @@ public abstract class Network { private static String getResponseDataNoError(final HttpResponse response, boolean replaceWhitespace) { try { String data = EntityUtils.toString(response.getEntity(), CharEncoding.UTF_8); - return replaceWhitespace ? BaseUtils.replaceWhitespace(data) : data; + return replaceWhitespace ? TextUtils.replaceWhitespace(data) : data; } catch (Exception e) { Log.e("getResponseData", e); return null; @@ -428,7 +428,7 @@ public abstract class Network { /** * Get the body of a HTTP response. * - * {@link BaseUtils#replaceWhitespace(String)} will be called on the result + * {@link TextUtils#replaceWhitespace(String)} will be called on the result * * @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 @@ -441,7 +441,7 @@ public abstract class Network { * Get the body of a HTTP response. * * @param response a HTTP response, which can be null - * @param replaceWhitespace <code>true</code> if {@link BaseUtils#replaceWhitespace(String)} + * @param replaceWhitespace <code>true</code> if {@link TextUtils#replaceWhitespace(String)} * should be called on the body * @return the body if the response comes from a successful HTTP request, <code>null</code> otherwise */ diff --git a/main/src/cgeo/geocaching/network/StatusUpdater.java b/main/src/cgeo/geocaching/network/StatusUpdater.java index bfc77ba..d54f706 100644 --- a/main/src/cgeo/geocaching/network/StatusUpdater.java +++ b/main/src/cgeo/geocaching/network/StatusUpdater.java @@ -9,6 +9,8 @@ import cgeo.geocaching.utils.Version; import org.json.JSONException; import org.json.JSONObject; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.os.Looper; import java.util.Locale; @@ -27,6 +29,13 @@ public class StatusUpdater extends MemorySubject<StatusUpdater.Status> implement this.icon = icon; this.url = url; } + + final static public Status closeoutStatus = + new Status("", "status_closeout_warning", "attribute_abandonedbuilding", "http://www.cgeo.org/closeout/"); + + final static public Status defaultStatus() { + return VERSION.SDK_INT < VERSION_CODES.ECLAIR_MR1 ? closeoutStatus : null; + } } @Override diff --git a/main/src/cgeo/geocaching/speech/SpeechService.java b/main/src/cgeo/geocaching/speech/SpeechService.java index 7226014..7bc948d 100644 --- a/main/src/cgeo/geocaching/speech/SpeechService.java +++ b/main/src/cgeo/geocaching/speech/SpeechService.java @@ -1,6 +1,7 @@ package cgeo.geocaching.speech; import cgeo.geocaching.DirectionProvider; +import cgeo.geocaching.Settings; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; @@ -37,8 +38,8 @@ public class SpeechService extends Service implements OnInitListener { private boolean initialized = false; protected float direction; protected Geopoint position; - protected boolean directionInitialized; - protected boolean positionInitialized; + protected boolean directionInitialized = !Settings.isUseCompass(); // don't wait for magnetometer, if it shall not be used + protected boolean positionInitialized = false; GeoDirHandler geoHandler = new GeoDirHandler() { @Override diff --git a/main/src/cgeo/geocaching/speech/TextFactory.java b/main/src/cgeo/geocaching/speech/TextFactory.java index 0e13564..e367bb1 100644 --- a/main/src/cgeo/geocaching/speech/TextFactory.java +++ b/main/src/cgeo/geocaching/speech/TextFactory.java @@ -22,50 +22,80 @@ public class TextFactory { } private static String getDistance(Geopoint position, Geopoint target) { - float kilometers = position.distanceTo(target); + final float kilometers = position.distanceTo(target); if (Settings.isUseMetricUnits()) { - if (kilometers >= 5.0) { - return getString(R.string.tts_kilometers, String.valueOf(Math.round(kilometers))); - } - if (kilometers >= 1.0) { - String digits = String.format(Locale.getDefault(), "%.1f", kilometers); - return getString(R.string.tts_kilometers, digits); - } - int meters = (int) (kilometers * 1000.0); - if (meters > 50) { - return getString(R.string.tts_meters, String.valueOf(Math.round(meters / 10.0) * 10)); - } - return getString(R.string.tts_meters, String.valueOf(meters)); + return getDistance(kilometers, (int) (kilometers * 1000.0), + 5.0f, 1.0f, 50, + R.plurals.tts_kilometers, R.string.tts_one_kilometer, + R.plurals.tts_meters, R.string.tts_one_meter); } + return getDistance(kilometers / IConversion.MILES_TO_KILOMETER, + (int) (kilometers * 1000.0 * IConversion.METERS_TO_FEET), + 3.0f, 0.2f, 300, + R.plurals.tts_miles, R.string.tts_one_mile, + R.plurals.tts_feet, R.string.tts_one_foot); + } - float miles = kilometers / IConversion.MILES_TO_KILOMETER; - if (miles >= 3.0) { - return getString(R.string.tts_miles, String.valueOf(Math.round(miles))); + private static String getDistance(float farDistance, int nearDistance, + float farFarAway, float farNearAway, int nearFarAway, + int farId, int farOneId, int nearId, int nearOneId) { + if (farDistance >= farFarAway) { + // example: "5 kilometers" - always without decimal digits + final int quantity = Math.round(farDistance); + if (quantity == 1) { + return getString(farOneId, quantity, String.valueOf(quantity)); + } + return getQuantityString(farId, quantity, String.valueOf(quantity)); } - if (miles >= 0.2) { // approx 1000 ft - String digits = String.format(Locale.getDefault(), "%.1f", miles); - return getString(R.string.tts_miles, digits); + if (farDistance >= farNearAway) { + // example: "2.2 kilometers" - decimals if necessary + final float precision1 = Math.round(farDistance * 10.0f) / 10.0f; + final float precision0 = Math.round(farDistance); + if (Math.abs(precision1 - precision0) < 0.0001) { + // this is an int - e.g. 2 kilometers + final int quantity = (int) precision0; + if (quantity == 1) { + return getString(farOneId, quantity, String.valueOf(quantity)); + } + return getQuantityString(farId, quantity, String.valueOf(quantity)); + } + // this is no int - e.g. 1.7 kilometers + final String digits = String.format(Locale.getDefault(), "%.1f", farDistance); + // always use the plural (9 leads to plural) + return getQuantityString(farId, 9, digits); + } + // example: "34 meters" + int quantity = nearDistance; + if (quantity > nearFarAway) { + // example: "120 meters" - rounded to 10 meters + quantity = (int) Math.round(quantity / 10.0) * 10; } - int feet = (int) (kilometers * 1000.0 * IConversion.METERS_TO_FEET); - if (feet > 300) { - return getString(R.string.tts_feet, String.valueOf(Math.round(feet / 10.0) * 10)); + if (quantity == 1) { + return getString(nearOneId, quantity, String.valueOf(quantity)); } - return getString(R.string.tts_feet, String.valueOf(feet)); + return getQuantityString(nearId, quantity, String.valueOf(quantity)); } private static String getString(int resourceId, Object... 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); + } + private static String getDirection(Geopoint position, Geopoint target, float direction) { final int bearing = (int) position.bearingTo(target); - int degrees = (int) AngleUtils.normalize(bearing - direction); + final int degrees = (int) AngleUtils.normalize(bearing - direction); int hours = (degrees + 15) / 30; if (hours == 0) { hours = 12; } + if (hours == 1) { + return getString(R.string.tts_one_oclock, String.valueOf(hours)); + } return getString(R.string.tts_oclock, String.valueOf(hours)); } } diff --git a/main/src/cgeo/geocaching/ui/AbstractUserClickListener.java b/main/src/cgeo/geocaching/ui/AbstractUserClickListener.java new file mode 100644 index 0000000..b717568 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/AbstractUserClickListener.java @@ -0,0 +1,76 @@ +package cgeo.geocaching.ui; + +import cgeo.geocaching.R; +import cgeo.geocaching.cgeocaches; +import cgeo.geocaching.activity.AbstractActivity; +import cgeo.geocaching.network.Network; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Resources; +import android.net.Uri; +import android.view.View; + +abstract class AbstractUserClickListener implements View.OnClickListener { + + private final boolean enabled; + + public AbstractUserClickListener(final boolean enabled) { + this.enabled = enabled; + } + + @Override + public void onClick(View view) { + if (view == null) { + return; + } + if (!enabled) { + return; + } + + showUserActionsDialog(getUserName(view), view); + } + + protected abstract CharSequence getUserName(View view); + + /** + * Opens a dialog to do actions on an user name + */ + protected static void showUserActionsDialog(final CharSequence name, final View view) { + final AbstractActivity context = (AbstractActivity) view.getContext(); + final Resources res = context.getResources(); + final CharSequence[] items = { res.getString(R.string.user_menu_view_hidden), + res.getString(R.string.user_menu_view_found), + res.getString(R.string.user_menu_open_browser), + res.getString(R.string.user_menu_send_message) + }; + + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(res.getString(R.string.user_menu_title) + " " + name); + builder.setItems(items, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int item) { + switch (item) { + case 0: + cgeocaches.startActivityOwner(context, name.toString()); + return; + case 1: + cgeocaches.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())))); + return; + case 3: + context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/email/?u=" + Network.encode(name.toString())))); + return; + default: + break; + } + } + }); + final AlertDialog alert = builder.create(); + alert.show(); + } + +} diff --git a/main/src/cgeo/geocaching/ui/CompassView.java b/main/src/cgeo/geocaching/ui/CompassView.java index c926057..b73a2a9 100644 --- a/main/src/cgeo/geocaching/ui/CompassView.java +++ b/main/src/cgeo/geocaching/ui/CompassView.java @@ -171,12 +171,12 @@ public class CompassView extends View implements PeriodicHandlerListener { headingDrawn = cacheHeadingShown; } - float azimuthTemp = azimuthDrawn; + final float azimuthTemp = azimuthDrawn; final float azimuthRelative = AngleUtils.normalize(azimuthTemp - headingDrawn); // compass margins - int canvasCenterX = (compassRoseWidth / 2) + ((getWidth() - compassRoseWidth) / 2); - int canvasCenterY = (compassRoseHeight / 2) + ((getHeight() - compassRoseHeight) / 2); + final int canvasCenterX = (compassRoseWidth / 2) + ((getWidth() - compassRoseWidth) / 2); + final int canvasCenterY = (compassRoseHeight / 2) + ((getHeight() - compassRoseHeight) / 2); super.onDraw(canvas); @@ -217,38 +217,36 @@ public class CompassView extends View implements PeriodicHandlerListener { } private int measureWidth(int measureSpec) { - int result; - int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); + final int specMode = MeasureSpec.getMode(measureSpec); + final int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { - result = specSize; - } else { - result = compassArrow.getWidth() + getPaddingLeft() + getPaddingRight(); + return specSize; + } - if (specMode == MeasureSpec.AT_MOST) { - result = Math.min(result, specSize); - } + final int desired = compassArrow.getWidth() + getPaddingLeft() + getPaddingRight(); + if (specMode == MeasureSpec.AT_MOST) { + return Math.min(desired, specSize); } - return result; + return desired; } private int measureHeight(int measureSpec) { - int result; - int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); + // The duplicated code in measureHeight and measureWidth cannot be avoided. + // Those methods must be efficient, therefore we cannot extract the code differences and unify the remainder. + final int specMode = MeasureSpec.getMode(measureSpec); + final int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { - result = specSize; - } else { - result = compassArrow.getHeight() + getPaddingTop() + getPaddingBottom(); + return specSize; + } - if (specMode == MeasureSpec.AT_MOST) { - result = Math.min(result, specSize); - } + final int desired = compassArrow.getHeight() + getPaddingTop() + getPaddingBottom(); + if (specMode == MeasureSpec.AT_MOST) { + return Math.min(desired, specSize); } - return result; + return desired; } } diff --git a/main/src/cgeo/geocaching/ui/HtmlImageCounter.java b/main/src/cgeo/geocaching/ui/HtmlImageCounter.java new file mode 100644 index 0000000..24b70ea --- /dev/null +++ b/main/src/cgeo/geocaching/ui/HtmlImageCounter.java @@ -0,0 +1,19 @@ +package cgeo.geocaching.ui; + +import android.graphics.drawable.Drawable; +import android.text.Html; + +public class HtmlImageCounter implements Html.ImageGetter { + + private int imageCount = 0; + + @Override + public Drawable getDrawable(String url) { + imageCount++; + return null; + } + + public int getImageCount() { + return imageCount; + } +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/ui/OwnerActionsClickListener.java b/main/src/cgeo/geocaching/ui/OwnerActionsClickListener.java new file mode 100644 index 0000000..45ce237 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/OwnerActionsClickListener.java @@ -0,0 +1,31 @@ +package cgeo.geocaching.ui; + +import cgeo.geocaching.Geocache; + +import org.apache.commons.lang3.StringUtils; + +import android.view.View; +import android.widget.TextView; + +/** + * Listener for clicks on owner name + */ +public class OwnerActionsClickListener extends AbstractUserClickListener { + + private final Geocache cache; + + public OwnerActionsClickListener(Geocache cache) { + super(cache.supportsUserActions()); + this.cache = cache; + } + + @Override + protected String getUserName(View view) { + // Use real owner name vice the one owner chose to display + if (StringUtils.isNotBlank(cache.getOwnerUserId())) { + return cache.getOwnerUserId(); + } + return ((TextView) view).getText().toString(); + } +} + diff --git a/main/src/cgeo/geocaching/ui/UserActionsClickListener.java b/main/src/cgeo/geocaching/ui/UserActionsClickListener.java new file mode 100644 index 0000000..292074e --- /dev/null +++ b/main/src/cgeo/geocaching/ui/UserActionsClickListener.java @@ -0,0 +1,26 @@ +package cgeo.geocaching.ui; + +import cgeo.geocaching.Geocache; + +import android.view.View; +import android.widget.TextView; + +/** + * Listener for clicks on user name + */ +public class UserActionsClickListener extends AbstractUserClickListener { + + public UserActionsClickListener(Geocache cache) { + super(cache.supportsUserActions()); + } + + public UserActionsClickListener() { + super(true); + } + + @Override + protected CharSequence getUserName(View view) { + return ((TextView) view).getText().toString(); + } +} + diff --git a/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java b/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java index 4ab9af0..60116f9 100644 --- a/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java +++ b/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java @@ -13,13 +13,10 @@ import cgeo.geocaching.geopoint.GeopointFormatter; import org.apache.commons.lang3.StringUtils; -import android.app.Dialog; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.View; -import android.view.ViewGroup.LayoutParams; -import android.view.Window; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; @@ -28,7 +25,7 @@ import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; -public class CoordinatesInputDialog extends Dialog { +public class CoordinatesInputDialog extends NoTitleDialog { final private AbstractActivity context; final private IGeoData geo; @@ -65,13 +62,6 @@ public class CoordinatesInputDialog extends Dialog { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - try { - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - } catch (Exception e) { - // nothing - } - setContentView(R.layout.coordinatesinput_dialog); final Spinner spinner = (Spinner) findViewById(R.id.spinnerCoordinateFormats); @@ -338,7 +328,7 @@ public class CoordinatesInputDialog extends Dialog { if (currentFormat == coordInputFormatEnum.Plain) { try { gp = new Geopoint(eLat.getText().toString(), eLon.getText().toString()); - } catch (Geopoint.ParseException e) { + } catch (final Geopoint.ParseException e) { if (signalError) { context.showToast(context.getResources().getString(R.string.err_parse_lat_lon)); } @@ -347,20 +337,20 @@ public class CoordinatesInputDialog extends Dialog { return true; } - String latDir = bLat.getText().toString(); - String lonDir = bLon.getText().toString(); - String latDeg = eLatDeg.getText().toString(); - String lonDeg = eLonDeg.getText().toString(); - String latDegFrac = eLatMin.getText().toString(); - String lonDegFrac = eLonMin.getText().toString(); - String latMin = eLatMin.getText().toString(); - String lonMin = eLonMin.getText().toString(); - String latMinFrac = eLatSec.getText().toString(); - String lonMinFrac = eLonSec.getText().toString(); - String latSec = eLatSec.getText().toString(); - String lonSec = eLonSec.getText().toString(); - String latSecFrac = eLatSub.getText().toString(); - String lonSecFrac = eLonSub.getText().toString(); + final String latDir = bLat.getText().toString(); + final String lonDir = bLon.getText().toString(); + final String latDeg = eLatDeg.getText().toString(); + final String lonDeg = eLonDeg.getText().toString(); + final String latDegFrac = eLatMin.getText().toString(); + final String lonDegFrac = eLonMin.getText().toString(); + final String latMin = eLatMin.getText().toString(); + final String lonMin = eLonMin.getText().toString(); + final String latMinFrac = eLatSec.getText().toString(); + final String lonMinFrac = eLonSec.getText().toString(); + final String latSec = eLatSec.getText().toString(); + final String lonSec = eLonSec.getText().toString(); + final String latSecFrac = eLatSub.getText().toString(); + final String lonSecFrac = eLonSub.getText().toString(); switch (currentFormat) { case Deg: diff --git a/main/src/cgeo/geocaching/ui/dialog/DateDialog.java b/main/src/cgeo/geocaching/ui/dialog/DateDialog.java index a9c579c..18f8e2e 100644 --- a/main/src/cgeo/geocaching/ui/dialog/DateDialog.java +++ b/main/src/cgeo/geocaching/ui/dialog/DateDialog.java @@ -3,15 +3,12 @@ package cgeo.geocaching.ui.dialog; import cgeo.geocaching.R; import android.app.Activity; -import android.app.Dialog; import android.os.Bundle; -import android.view.ViewGroup.LayoutParams; -import android.view.Window; import android.widget.DatePicker; import java.util.Calendar; -public class DateDialog extends Dialog { +public class DateDialog extends NoTitleDialog { public interface DateDialogParent { abstract public void setDate(final Calendar date); @@ -32,13 +29,6 @@ public class DateDialog extends Dialog { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - try { - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - } catch (Exception e) { - // nothing - } - setContentView(R.layout.date); final DatePicker picker = (DatePicker) findViewById(R.id.picker); diff --git a/main/src/cgeo/geocaching/ui/dialog/NoTitleDialog.java b/main/src/cgeo/geocaching/ui/dialog/NoTitleDialog.java new file mode 100644 index 0000000..fc5ebe6 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/dialog/NoTitleDialog.java @@ -0,0 +1,30 @@ +package cgeo.geocaching.ui.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.ViewGroup.LayoutParams; +import android.view.Window; + +public abstract class NoTitleDialog extends Dialog { + + public NoTitleDialog(Context context) { + super(context); + } + + public NoTitleDialog(Context context, int theme) { + super(context, theme); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + try { + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + } catch (final Exception e) { + // nothing + } + } +} diff --git a/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java b/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java new file mode 100644 index 0000000..a785681 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java @@ -0,0 +1,101 @@ +package cgeo.geocaching.ui.logs; + +import cgeo.geocaching.CacheDetailActivity; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.LogEntry; +import cgeo.geocaching.R; +import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.enumerations.LogType; +import cgeo.geocaching.ui.UserActionsClickListener; + +import org.apache.commons.lang3.StringUtils; + +import android.content.res.Resources; +import android.view.View; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +public class CacheLogsViewCreator extends LogsViewCreator { + private final boolean allLogs; + private final Geocache cache; + private final Resources res = cgeoapplication.getInstance().getResources(); + + public CacheLogsViewCreator(CacheDetailActivity cacheDetailActivity, final Geocache cache, boolean allLogs) { + super(cacheDetailActivity); + this.cache = cache; + this.allLogs = allLogs; + } + + @Override + protected List<LogEntry> getLogs() { + return allLogs ? cache.getLogs() : cache.getFriendsLogs(); + } + + @Override + protected void addHeaderView() { + // adds the log counts + final Map<LogType, Integer> logCounts = cache.getLogCounts(); + if (logCounts != null) { + final List<Entry<LogType, Integer>> sortedLogCounts = new ArrayList<Entry<LogType, Integer>>(logCounts.size()); + for (final Entry<LogType, Integer> entry : logCounts.entrySet()) { + // it may happen that the label is unknown -> then avoid any output for this type + if (entry.getKey() != LogType.PUBLISH_LISTING && entry.getKey().getL10n() != null) { + sortedLogCounts.add(entry); + } + } + + if (!sortedLogCounts.isEmpty()) { + // sort the log counts by type id ascending. that way the FOUND, DNF log types are the first and most visible ones + Collections.sort(sortedLogCounts, new Comparator<Entry<LogType, Integer>>() { + + @Override + public int compare(Entry<LogType, Integer> logCountItem1, Entry<LogType, Integer> logCountItem2) { + return logCountItem1.getKey().compareTo(logCountItem2.getKey()); + } + }); + + final ArrayList<String> labels = new ArrayList<String>(sortedLogCounts.size()); + for (final Entry<LogType, Integer> pair : sortedLogCounts) { + labels.add(pair.getValue() + "× " + pair.getKey().getL10n()); + } + + final TextView countView = new TextView(activity); + countView.setText(res.getString(R.string.cache_log_types) + ": " + StringUtils.join(labels, ", ")); + view.addHeaderView(countView, null, false); + } + } + } + + @Override + protected void fillCountOrLocation(LogViewHolder holder, final LogEntry log) { + // finds count + if (log.found == -1) { + holder.countOrLocation.setVisibility(View.GONE); + } else { + holder.countOrLocation.setVisibility(View.VISIBLE); + holder.countOrLocation.setText(res.getQuantityString(R.plurals.cache_counts, log.found, log.found)); + } + } + + @Override + protected boolean isValid() { + return cache != null; + } + + @Override + protected String getGeocode() { + return cache.getGeocode(); + } + + @Override + protected UserActionsClickListener createUserActionsListener() { + return new UserActionsClickListener(cache); + } + +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/LogViewHolder.java b/main/src/cgeo/geocaching/ui/logs/LogViewHolder.java index 14148d0..16f5537 100644 --- a/main/src/cgeo/geocaching/LogViewHolder.java +++ b/main/src/cgeo/geocaching/ui/logs/LogViewHolder.java @@ -1,7 +1,8 @@ -package cgeo.geocaching; +package cgeo.geocaching.ui.logs; import butterknife.InjectView; +import cgeo.geocaching.R; import cgeo.geocaching.ui.AbstractViewHolder; import android.view.View; diff --git a/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java b/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java new file mode 100644 index 0000000..ee2713a --- /dev/null +++ b/main/src/cgeo/geocaching/ui/logs/LogsViewCreator.java @@ -0,0 +1,175 @@ +package cgeo.geocaching.ui.logs; + +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.network.HtmlImage; +import cgeo.geocaching.ui.AbstractCachingPageViewCreator; +import cgeo.geocaching.ui.AnchorAwareLinkMovementMethod; +import cgeo.geocaching.ui.DecryptTextClickListener; +import cgeo.geocaching.ui.Formatter; +import cgeo.geocaching.ui.HtmlImageCounter; +import cgeo.geocaching.ui.UserActionsClickListener; +import cgeo.geocaching.utils.TextUtils; +import cgeo.geocaching.utils.UnknownTagsHandler; + +import org.apache.commons.lang3.StringEscapeUtils; + +import android.os.AsyncTask; +import android.text.Html; +import android.text.Spanned; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +public abstract class LogsViewCreator extends AbstractCachingPageViewCreator<ListView> { + + protected final AbstractActivity activity; + + public LogsViewCreator(AbstractActivity activity) { + this.activity = activity; + } + + @Override + public ListView getDispatchedView() { + if (!isValid()) { + return null; + } + + final List<LogEntry> logs = getLogs(); + + view = (ListView) activity.getLayoutInflater().inflate(R.layout.logs_page, null); + addHeaderView(); + view.setAdapter(new ArrayAdapter<LogEntry>(activity, R.layout.logs_item, logs) { + + @Override + public View getView(final int position, final View convertView, final android.view.ViewGroup parent) { + View rowView = convertView; + if (null == rowView) { + rowView = activity.getLayoutInflater().inflate(R.layout.logs_item, null); + } + LogViewHolder holder = (LogViewHolder) rowView.getTag(); + if (null == holder) { + holder = new LogViewHolder(rowView); + } + holder.setPosition(position); + + final LogEntry log = getItem(position); + fillViewHolder(convertView, holder, log); + return rowView; + } + }); + + return view; + } + + protected void fillViewHolder(final View convertView, LogViewHolder holder, final LogEntry log) { + if (log.date > 0) { + holder.date.setText(Formatter.formatShortDateVerbally(log.date)); + holder.date.setVisibility(View.VISIBLE); + } else { + holder.date.setVisibility(View.GONE); + } + + holder.type.setText(log.type.getL10n()); + holder.author.setText(StringEscapeUtils.unescapeHtml4(log.author)); + + fillCountOrLocation(holder, log); + + // logtext, avoid parsing HTML if not necessary + String logText = log.log; + if (TextUtils.containsHtml(logText)) { + logText = log.getDisplayText(); + // Fast preview: parse only HTML without loading any images + final HtmlImageCounter imageCounter = new HtmlImageCounter(); + final UnknownTagsHandler unknownTagsHandler = new UnknownTagsHandler(); + holder.text.setText(Html.fromHtml(logText, imageCounter, unknownTagsHandler), TextView.BufferType.SPANNABLE); + if (imageCounter.getImageCount() > 0) { + // Complete view: parse again with loading images - if necessary ! If there are any images causing problems the user can see at least the preview + final LogImageLoader loader = new LogImageLoader(holder); + loader.execute(logText); + } + } + else { + holder.text.setText(logText, TextView.BufferType.SPANNABLE); + } + + // images + if (log.hasLogImages()) { + holder.images.setText(log.getImageTitles()); + holder.images.setVisibility(View.VISIBLE); + holder.images.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ImagesActivity.startActivityLogImages(activity, getGeocode(), new ArrayList<Image>(log.getLogImages())); + } + }); + } else { + holder.images.setVisibility(View.GONE); + } + + // colored marker + final int marker = log.type.markerId; + if (marker != 0) { + holder.marker.setVisibility(View.VISIBLE); + holder.marker.setImageResource(marker); + } + else { + holder.marker.setVisibility(View.GONE); + } + + if (null == convertView) { + holder.author.setOnClickListener(createUserActionsListener()); + holder.text.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); + holder.text.setOnClickListener(new DecryptTextClickListener()); + activity.registerForContextMenu(holder.text); + } + } + + abstract protected UserActionsClickListener createUserActionsListener(); + + abstract protected String getGeocode(); + + abstract protected List<LogEntry> getLogs(); + + abstract protected void addHeaderView(); + + abstract protected void fillCountOrLocation(LogViewHolder holder, final LogEntry log); + + abstract protected boolean isValid(); + + /** Loads the Log Images outside the ui thread. */ + + private class LogImageLoader extends AsyncTask<String, Progress, Spanned> { + final private LogViewHolder holder; + final private int position; + + public LogImageLoader(LogViewHolder holder) { + this.holder = holder; + this.position = holder.getPosition(); + } + + @Override + protected Spanned doInBackground(String... logtext) { + return Html.fromHtml(logtext[0], new HtmlImage(getGeocode(), false, StoredList.STANDARD_LIST_ID, false), null); //, TextView.BufferType.SPANNABLE) + } + + @Override + protected void onPostExecute(Spanned result) { + // Ensure that this holder and its view still references the right item before updating the text. + if (position == holder.getPosition()) { + holder.text.setText(result); + } + } + + } + +} diff --git a/main/src/cgeo/geocaching/ui/logs/TrackableLogsViewCreator.java b/main/src/cgeo/geocaching/ui/logs/TrackableLogsViewCreator.java new file mode 100644 index 0000000..4c57406 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/logs/TrackableLogsViewCreator.java @@ -0,0 +1,70 @@ +package cgeo.geocaching.ui.logs; + +import cgeo.geocaching.CacheDetailActivity; +import cgeo.geocaching.LogEntry; +import cgeo.geocaching.Trackable; +import cgeo.geocaching.TrackableActivity; +import cgeo.geocaching.ui.UserActionsClickListener; + +import org.apache.commons.lang3.StringUtils; + +import android.text.Html; +import android.view.View; + +import java.util.List; + +public class TrackableLogsViewCreator extends LogsViewCreator { + + private final Trackable trackable; + + /** + * @param trackableActivity + */ + public TrackableLogsViewCreator(TrackableActivity trackableActivity, final Trackable trackable) { + super(trackableActivity); + this.trackable = trackable; + } + + @Override + protected boolean isValid() { + return trackable != null; + } + + @Override + protected List<LogEntry> getLogs() { + return trackable.getLogs(); + } + + @Override + protected void addHeaderView() { + // empty + } + + @Override + protected void fillCountOrLocation(LogViewHolder holder, final LogEntry log) { + if (StringUtils.isBlank(log.cacheName)) { + holder.countOrLocation.setVisibility(View.GONE); + } else { + holder.countOrLocation.setText(Html.fromHtml(log.cacheName)); + final String cacheGuid = log.cacheGuid; + final String cacheName = log.cacheName; + holder.countOrLocation.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View arg0) { + CacheDetailActivity.startActivityGuid(activity, cacheGuid, Html.fromHtml(cacheName).toString()); + } + }); + } + } + + @Override + protected String getGeocode() { + return trackable.getGeocode(); + } + + @Override + protected UserActionsClickListener createUserActionsListener() { + return new UserActionsClickListener(); + } + +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/utils/AngleUtils.java b/main/src/cgeo/geocaching/utils/AngleUtils.java index 6e59a91..fdd9a9d 100644 --- a/main/src/cgeo/geocaching/utils/AngleUtils.java +++ b/main/src/cgeo/geocaching/utils/AngleUtils.java @@ -1,6 +1,6 @@ package cgeo.geocaching.utils; -public class AngleUtils { +public final class AngleUtils { private AngleUtils() { // Do not instantiate @@ -8,7 +8,7 @@ public class AngleUtils { /** * Return the angle to turn of to go from an angle to the other - * + * * @param from * the origin angle in degrees * @param to diff --git a/main/src/cgeo/geocaching/utils/ClipboardUtils.java b/main/src/cgeo/geocaching/utils/ClipboardUtils.java index 9343576..67069b2 100644 --- a/main/src/cgeo/geocaching/utils/ClipboardUtils.java +++ b/main/src/cgeo/geocaching/utils/ClipboardUtils.java @@ -12,6 +12,10 @@ import android.content.Context; @SuppressWarnings("deprecation") public final class ClipboardUtils { + private ClipboardUtils() { + // utility class + } + /** * Places the text passed in onto the clipboard as text * diff --git a/main/src/cgeo/geocaching/utils/CryptUtils.java b/main/src/cgeo/geocaching/utils/CryptUtils.java index aecf717..18a337d 100644 --- a/main/src/cgeo/geocaching/utils/CryptUtils.java +++ b/main/src/cgeo/geocaching/utils/CryptUtils.java @@ -12,6 +12,10 @@ import javax.crypto.spec.SecretKeySpec; public final class CryptUtils { + private CryptUtils() { + // utility class + } + private static char[] base64map1 = new char[64]; private static byte[] base64map2 = new byte[128]; diff --git a/main/src/cgeo/geocaching/utils/DateUtils.java b/main/src/cgeo/geocaching/utils/DateUtils.java index fd5ef4a..b148979 100644 --- a/main/src/cgeo/geocaching/utils/DateUtils.java +++ b/main/src/cgeo/geocaching/utils/DateUtils.java @@ -2,7 +2,12 @@ package cgeo.geocaching.utils; import java.util.Calendar; -public class DateUtils { +public final class DateUtils { + + private DateUtils() { + // utility class + } + public static int daysSince(long date) { final Calendar logDate = Calendar.getInstance(); logDate.setTimeInMillis(date); diff --git a/main/src/cgeo/geocaching/utils/EditUtils.java b/main/src/cgeo/geocaching/utils/EditUtils.java index f2f89e7..d975df9 100644 --- a/main/src/cgeo/geocaching/utils/EditUtils.java +++ b/main/src/cgeo/geocaching/utils/EditUtils.java @@ -8,6 +8,10 @@ import android.widget.TextView; public final class EditUtils { + private EditUtils() { + // utility class + } + public static void setActionListener(final EditText editText, final Runnable runnable) { editText.setOnEditorActionListener(new TextView.OnEditorActionListener() { diff --git a/main/src/cgeo/geocaching/utils/FileUtils.java b/main/src/cgeo/geocaching/utils/FileUtils.java index 6fefc02..5ab8fcc 100644 --- a/main/src/cgeo/geocaching/utils/FileUtils.java +++ b/main/src/cgeo/geocaching/utils/FileUtils.java @@ -9,12 +9,16 @@ import java.io.File; import java.util.List; /** - * Utiliy class for files + * Utility class for files * * @author rsudev * */ -public class FileUtils { +public final class FileUtils { + + private FileUtils() { + // utility class + } public static void listDir(List<File> result, File directory, FileSelector chooser, Handler feedBackHandler) { diff --git a/main/src/cgeo/geocaching/utils/HtmlUtils.java b/main/src/cgeo/geocaching/utils/HtmlUtils.java index 89b1070..5717a37 100644 --- a/main/src/cgeo/geocaching/utils/HtmlUtils.java +++ b/main/src/cgeo/geocaching/utils/HtmlUtils.java @@ -10,7 +10,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -public class HtmlUtils { +public final class HtmlUtils { + + private HtmlUtils() { + // utility class + } /** * Extract the text from a HTML based string. This is similar to what HTML.fromHtml(...) does, but this method also diff --git a/main/src/cgeo/geocaching/utils/ImageHelper.java b/main/src/cgeo/geocaching/utils/ImageUtils.java index a01e836..ea7d3ff 100644 --- a/main/src/cgeo/geocaching/utils/ImageHelper.java +++ b/main/src/cgeo/geocaching/utils/ImageUtils.java @@ -11,10 +11,10 @@ import android.graphics.drawable.BitmapDrawable; import java.io.BufferedOutputStream; import java.io.FileOutputStream; -public class ImageHelper { +public final class ImageUtils { - // Do not let this class be instantiated, this is a utility class. - private ImageHelper() { + private ImageUtils() { + // Do not let this class be instantiated, this is a utility class. } /** diff --git a/main/src/cgeo/geocaching/utils/LeastRecentlyUsedSet.java b/main/src/cgeo/geocaching/utils/LeastRecentlyUsedSet.java index 698d38a..708dff0 100644 --- a/main/src/cgeo/geocaching/utils/LeastRecentlyUsedSet.java +++ b/main/src/cgeo/geocaching/utils/LeastRecentlyUsedSet.java @@ -10,13 +10,13 @@ import java.util.List; /** * Synchronized set wrapper for the LeastRecentlyUsedMap. * - * This code is heavily based on the HashSet code that represent Map as a Set. + * This code is heavily based on the HashSet code that represents Map as a Set. * Unfortunately HashSet does not allow to use a custom Map as its Storage. * Therefore overriding removeEldestEntry() is impossible for a normal LinkedHashSet. * * Synchronization is added to guard against concurrent modification. Iterator * access has to be guarded externally or the synchronized getAsList method can be used - * to get a clone for iteration + * to get a clone for iteration. */ public class LeastRecentlyUsedSet<E> extends AbstractSet<E> implements Cloneable, java.io.Serializable { @@ -28,7 +28,7 @@ public class LeastRecentlyUsedSet<E> extends AbstractSet<E> public LeastRecentlyUsedSet(int maxEntries, int initialCapacity, float loadFactor) { // because we don't use any Map.get() methods from the Set, BOUNDED and LRU_CACHE have the exact same Behaviour - // So we useLRU_CACHE mode because it should perform a bit better (as it doesn't re-add explicitly) + // So we use LRU_CACHE mode because it should perform a bit better (as it doesn't re-add explicitly) map = new LeastRecentlyUsedMap.LruCache<E, Object>(maxEntries, initialCapacity, loadFactor); } diff --git a/main/src/cgeo/geocaching/utils/LogTemplateProvider.java b/main/src/cgeo/geocaching/utils/LogTemplateProvider.java index 0a8e547..2576e64 100644 --- a/main/src/cgeo/geocaching/utils/LogTemplateProvider.java +++ b/main/src/cgeo/geocaching/utils/LogTemplateProvider.java @@ -188,7 +188,7 @@ public class LogTemplateProvider { } try { - return Integer.parseInt(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", "")); + return Integer.parseInt(TextUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", "")); } catch (NumberFormatException e) { Log.e("parseFindCount", e); return -1; diff --git a/main/src/cgeo/geocaching/utils/ProcessUtils.java b/main/src/cgeo/geocaching/utils/ProcessUtils.java index 53991fb..85cedc5 100644 --- a/main/src/cgeo/geocaching/utils/ProcessUtils.java +++ b/main/src/cgeo/geocaching/utils/ProcessUtils.java @@ -3,14 +3,55 @@ package cgeo.geocaching.utils; import cgeo.geocaching.cgeoapplication; import android.content.Intent; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -public class ProcessUtils { +import java.util.List; - public static boolean isInstalled(final String packageName) { +public final class ProcessUtils { + + private ProcessUtils() { + // utility class + } + + /** + * Preferred method to detect the availability of an external app + * + * @param packageName + * @return + */ + public static boolean isLaunchable(final String packageName) { return getLaunchIntent(packageName) != null; } + /** + * Checks whether a launch intent is available or if the package is just installed + * This function is relatively costly, so if you know that the package in question has + * a launch intent, use isLaunchable() instead. + * + * @param packageName + * @return + */ + public static boolean isInstalled(final String packageName) { + return isLaunchable(packageName) || hasPackageInstalled(packageName); + } + + /** + * 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); + for (final PackageInfo packageInfo : packs) { + if (packageName.equals(packageInfo.packageName)) { + return true; + } + } + return false; + } + + /** + * This will find applications, which can be launched. + */ public static Intent getLaunchIntent(final String packageName) { if (packageName == null) { return null; @@ -20,7 +61,7 @@ public class ProcessUtils { // This can throw an exception where the exception type is only defined on API Level > 3 // therefore surround with try-catch return packageManager.getLaunchIntentForPackage(packageName); - } catch (Exception e) { + } catch (final Exception e) { return null; } } diff --git a/main/src/cgeo/geocaching/utils/BaseUtils.java b/main/src/cgeo/geocaching/utils/TextUtils.java index b8006ad..68ac595 100644 --- a/main/src/cgeo/geocaching/utils/BaseUtils.java +++ b/main/src/cgeo/geocaching/utils/TextUtils.java @@ -9,10 +9,14 @@ import java.util.regex.Pattern; /** * Misc. utils. All methods don't use Android specific stuff to use these methods in plain JUnit tests. */ -public final class BaseUtils { +public final class TextUtils { private static final Pattern PATTERN_REMOVE_NONPRINTABLE = Pattern.compile("\\p{Cntrl}"); + private TextUtils() { + // utility class + } + /** * Searches for the pattern p in the data. If the pattern is not found defaultValue is returned * @@ -69,7 +73,7 @@ public final class BaseUtils { * @return defaultValue or the first group if the pattern matches (trimmed if wanted) */ public static String getMatch(final String data, final Pattern p, final boolean trim, final String defaultValue) { - return BaseUtils.getMatch(data, p, trim, 1, defaultValue, false); + return TextUtils.getMatch(data, p, trim, 1, defaultValue, false); } /** @@ -84,7 +88,7 @@ public final class BaseUtils { * @return defaultValue or the first group if the pattern matches (trimmed) */ public static String getMatch(final String data, final Pattern p, final String defaultValue) { - return BaseUtils.getMatch(data, p, true, 1, defaultValue, false); + 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 4d318f0..1224f7e 100644 --- a/main/src/cgeo/geocaching/utils/TranslationUtils.java +++ b/main/src/cgeo/geocaching/utils/TranslationUtils.java @@ -19,6 +19,10 @@ public final class TranslationUtils { public static final int translationTextLengthToWarn = 500; private static final String TRANSLATION_APP = "com.google.android.apps.translate"; + private TranslationUtils() { + // utility class + } + /** * Build a URI for Google Translate * @@ -31,7 +35,7 @@ public final class TranslationUtils { private static String buildTranslationURI(final String toLang, final String text) { 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.isInstalled(TRANSLATION_APP)) { + if (ProcessUtils.isLaunchable(TRANSLATION_APP)) { content = content.replace("+", "%20"); } return translationWebsite + translationForceClassicMode + translationAutoSelect + translationFieldSeparator + toLang + translationFieldSeparator + content; diff --git a/main/src/cgeo/geocaching/utils/XmlUtils.java b/main/src/cgeo/geocaching/utils/XmlUtils.java index 4e08f42..2d85950 100644 --- a/main/src/cgeo/geocaching/utils/XmlUtils.java +++ b/main/src/cgeo/geocaching/utils/XmlUtils.java @@ -4,7 +4,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; -public class XmlUtils { +public final class XmlUtils { private XmlUtils() { // Do not instantiate |
