diff options
Diffstat (limited to 'main/src')
152 files changed, 5367 insertions, 4734 deletions
diff --git a/main/src/cgeo/geocaching/AboutActivity.java b/main/src/cgeo/geocaching/AboutActivity.java index f947655..ef59cee 100644 --- a/main/src/cgeo/geocaching/AboutActivity.java +++ b/main/src/cgeo/geocaching/AboutActivity.java @@ -9,16 +9,20 @@ import com.googlecode.androidannotations.annotations.Click; import com.googlecode.androidannotations.annotations.EActivity; import com.googlecode.androidannotations.annotations.ViewById; +import org.apache.commons.lang3.StringUtils; + import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.view.View; import android.widget.TextView; @EActivity public class AboutActivity extends AbstractActivity { @ViewById(R.id.about_version_string) protected TextView version; @ViewById(R.id.contributors) protected TextView contributors; - @ViewById(R.id.changelog) protected TextView changeLog; + @ViewById(R.id.changelog_master) protected TextView changeLogMaster; + @ViewById(R.id.changelog_release) protected TextView changeLogRelease; @Override public void onCreate(Bundle savedInstanceState) { @@ -30,7 +34,13 @@ public class AboutActivity extends AbstractActivity { void initializeViews() { version.setText(Version.getVersionName(this)); contributors.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); - changeLog.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); + changeLogRelease.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); + final String changeLogMasterString = getString(R.string.changelog_master); + if (StringUtils.isBlank(changeLogMasterString)) { + changeLogMaster.setVisibility(View.GONE); + } else { + changeLogMaster.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); + } } @Click(R.id.donate) diff --git a/main/src/cgeo/geocaching/AbstractLoggingActivity.java b/main/src/cgeo/geocaching/AbstractLoggingActivity.java index 78da757..c3ba7d2 100644 --- a/main/src/cgeo/geocaching/AbstractLoggingActivity.java +++ b/main/src/cgeo/geocaching/AbstractLoggingActivity.java @@ -1,10 +1,13 @@ package cgeo.geocaching; import cgeo.geocaching.activity.AbstractActivity; +import cgeo.geocaching.activity.ActivityMixin; 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.settings.Settings; import cgeo.geocaching.utils.LogTemplateProvider; import cgeo.geocaching.utils.LogTemplateProvider.LogContext; import cgeo.geocaching.utils.LogTemplateProvider.LogTemplate; @@ -23,12 +26,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 +49,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; } @@ -83,6 +86,6 @@ public abstract class AbstractLoggingActivity extends AbstractActivity { protected void insertIntoLog(String newText, final boolean moveCursor) { final EditText log = (EditText) findViewById(R.id.log); - insertAtPosition(log, newText, moveCursor); + ActivityMixin.insertAtPosition(log, newText, moveCursor); } } diff --git a/main/src/cgeo/geocaching/AbstractPopupActivity.java b/main/src/cgeo/geocaching/AbstractPopupActivity.java index 73dc86d..1cf0353 100644 --- a/main/src/cgeo/geocaching/AbstractPopupActivity.java +++ b/main/src/cgeo/geocaching/AbstractPopupActivity.java @@ -2,12 +2,14 @@ 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; import cgeo.geocaching.gcvote.GCVoteRating; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Units; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.CacheDetailsCreator; import cgeo.geocaching.ui.LoggingUI; import cgeo.geocaching.utils.GeoDirHandler; @@ -46,7 +48,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 +64,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 +207,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 f473bb4..ac9b156 100644 --- a/main/src/cgeo/geocaching/CacheDetailActivity.java +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -4,6 +4,7 @@ import butterknife.InjectView; import butterknife.Views; import cgeo.calendar.ICalendar; +import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.AbstractViewPagerActivity; import cgeo.geocaching.activity.Progress; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; @@ -14,13 +15,13 @@ 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; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.AbstractCachingPageViewCreator; import cgeo.geocaching.ui.AnchorAwareLinkMovementMethod; import cgeo.geocaching.ui.CacheDetailsCreator; @@ -29,10 +30,13 @@ 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.IndexOutOfBoundsAvoidingTextView; import cgeo.geocaching.ui.LoggingUI; +import cgeo.geocaching.ui.OwnerActionsClickListener; import cgeo.geocaching.ui.WeakReferenceHandler; -import cgeo.geocaching.utils.TextUtils; +import cgeo.geocaching.ui.logs.CacheLogsViewCreator; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.ClipboardUtils; import cgeo.geocaching.utils.CryptUtils; @@ -42,6 +46,9 @@ import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.MatcherWrapper; import cgeo.geocaching.utils.RunnableWithArgument; +import cgeo.geocaching.utils.SimpleCancellableHandler; +import cgeo.geocaching.utils.SimpleHandler; +import cgeo.geocaching.utils.TextUtils; import cgeo.geocaching.utils.TranslationUtils; import cgeo.geocaching.utils.UnknownTagsHandler; @@ -101,14 +108,12 @@ import android.widget.ScrollView; import android.widget.TextView; import android.widget.TextView.BufferType; +import java.lang.ref.WeakReference; 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; /** @@ -148,6 +153,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc private Geocache cache; private final Progress progress = new Progress(); + private SearchResult search; private EditNoteDialogListener editNoteDialogListener; @@ -159,24 +165,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return; } - try { - final StringBuilder dist = new StringBuilder(); - - if (geo.getCoords() != null && cache != null && cache.getCoords() != null) { - dist.append(Units.getDistanceFromKilometers(geo.getCoords().distanceTo(cache.getCoords()))); - } - - if (cache != null && cache.getElevation() != null) { - if (geo.getAltitude() != 0.0) { - final float diff = (float) (cache.getElevation() - geo.getAltitude()); - dist.append(' ').append(Units.getElevation(diff)); - } - } - - cacheDistanceView.setText(dist.toString()); + if (geo.getCoords() != null && cache != null && cache.getCoords() != null) { + cacheDistanceView.setText(Units.getDistanceFromKilometers(geo.getCoords().distanceTo(cache.getCoords()))); cacheDistanceView.bringToFront(); - } catch (Exception e) { - Log.w("Failed to update location."); } } }; @@ -192,12 +183,6 @@ 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() { - @Override - public void handleMessage(Message msg) { - notifyDataSetChanged(); - } - }; protected ImagesList imagesList; @Override @@ -232,9 +217,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); @@ -292,7 +277,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return; } - final LoadCacheHandler loadCacheHandler = new LoadCacheHandler(); + final LoadCacheHandler loadCacheHandler = new LoadCacheHandler(this, progress); try { String title = res.getString(R.string.cache); @@ -302,11 +287,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 +360,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 +369,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 +418,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc break; } } - } catch (Exception e) { + } catch (final Exception e) { } } break; @@ -579,8 +564,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); } @@ -623,44 +608,56 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return true; } - private class LoadCacheHandler extends CancellableHandler { + private final static class LoadCacheHandler extends SimpleCancellableHandler { + + public LoadCacheHandler(CacheDetailActivity activity, Progress progress) { + super(activity, progress); + } + @Override public void handleRegularMessage(final Message msg) { if (UPDATE_LOAD_PROGRESS_DETAIL == msg.what && msg.obj instanceof String) { updateStatusMsg((String) msg.obj); } else { + CacheDetailActivity activity = ((CacheDetailActivity) activityRef.get()); + if (activity == null) { + return; + } + SearchResult search = activity.getSearch(); if (search == null) { - showToast(res.getString(R.string.err_dwld_details_failed)); - - progress.dismiss(); - finish(); + showToast(R.string.err_dwld_details_failed); + dismissProgress(); + finishActivity(); return; } if (search.getError() != null) { - showToast(res.getString(R.string.err_dwld_details_failed) + " " + search.getError().getErrorString(res) + "."); - - progress.dismiss(); - finish(); + activity.showToast(activity.getResources().getString(R.string.err_dwld_details_failed) + " " + search.getError().getErrorString(activity.getResources()) + "."); + dismissProgress(); + finishActivity(); return; } - updateStatusMsg(res.getString(R.string.cache_dialog_loading_details_status_render)); + updateStatusMsg(activity.getResources().getString(R.string.cache_dialog_loading_details_status_render)); // Data loaded, we're ready to show it! - notifyDataSetChanged(); + activity.notifyDataSetChanged(); } } private void updateStatusMsg(final String msg) { - progress.setMessage(res.getString(R.string.cache_dialog_loading_details) + CacheDetailActivity activity = ((CacheDetailActivity) activityRef.get()); + if (activity == null) { + return; + } + setProgressMessage(activity.getResources().getString(R.string.cache_dialog_loading_details) + "\n\n" + msg); } @Override public void handleCancel(final Object extra) { - finish(); + finishActivity(); } } @@ -680,7 +677,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } // allow cache to notify CacheDetailActivity when it changes so it can be reloaded - cache.setChangeNotificationHandler(cacheChangeNotificationHandler); + cache.setChangeNotificationHandler(new ChangeNotificationHandler(this, progress)); // action bar: title and icon if (StringUtils.isNotBlank(cache.getName())) { @@ -788,7 +785,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 +829,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 +979,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 +1000,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 +1018,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 +1083,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 +1116,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 +1144,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 +1158,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()); @@ -1277,49 +1194,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return view; } - private class StoreCacheHandler extends CancellableHandler { - @Override - public void handleRegularMessage(Message msg) { - if (UPDATE_LOAD_PROGRESS_DETAIL == msg.what && msg.obj instanceof String) { - updateStatusMsg((String) msg.obj); - } else { - storeThread = null; - CacheDetailActivity.this.notifyDataSetChanged(); // reload cache details - } - } - - private void updateStatusMsg(final String msg) { - progress.setMessage(res.getString(R.string.cache_dialog_offline_save_message) - + "\n\n" - + msg); - } - } - - private class RefreshCacheHandler extends CancellableHandler { - @Override - public void handleRegularMessage(Message msg) { - if (UPDATE_LOAD_PROGRESS_DETAIL == msg.what && msg.obj instanceof String) { - updateStatusMsg((String) msg.obj); - } else { - refreshThread = null; - CacheDetailActivity.this.notifyDataSetChanged(); // reload cache details - } - } - - private void updateStatusMsg(final String msg) { - progress.setMessage(res.getString(R.string.cache_dialog_refresh_message) - + "\n\n" - + msg); - } - } - - private class DropCacheHandler extends Handler { - @Override - public void handleMessage(Message msg) { - CacheDetailActivity.this.notifyDataSetChanged(); - } - } - private class StoreCacheClickListener implements View.OnClickListener { @Override public void onClick(View arg0) { @@ -1343,7 +1217,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } protected void storeCache(int listId) { - final StoreCacheHandler storeCacheHandler = new StoreCacheHandler(); + final StoreCacheHandler storeCacheHandler = new StoreCacheHandler(CacheDetailActivity.this, progress); progress.show(CacheDetailActivity.this, res.getString(R.string.cache_dialog_offline_save_title), res.getString(R.string.cache_dialog_offline_save_message), true, storeCacheHandler.cancelMessage()); @@ -1369,7 +1243,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return; } - final RefreshCacheHandler refreshCacheHandler = new RefreshCacheHandler(); + final RefreshCacheHandler refreshCacheHandler = new RefreshCacheHandler(CacheDetailActivity.this, progress); progress.show(CacheDetailActivity.this, res.getString(R.string.cache_dialog_refresh_title), res.getString(R.string.cache_dialog_refresh_message), true, refreshCacheHandler.cancelMessage()); @@ -1394,6 +1268,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc @Override public void run() { cache.store(listId, handler); + storeThread = null; } } @@ -1407,7 +1282,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc @Override public void run() { cache.refresh(cache.getListId(), handler); - + refreshThread = null; handler.sendEmptyMessage(0); } } @@ -1420,24 +1295,22 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return; } - final DropCacheHandler dropCacheHandler = new DropCacheHandler(); - progress.show(CacheDetailActivity.this, res.getString(R.string.cache_dialog_offline_drop_title), res.getString(R.string.cache_dialog_offline_drop_message), true, null); - new DropCacheThread(dropCacheHandler).start(); + new DropCacheThread(new ChangeNotificationHandler(CacheDetailActivity.this, progress)).start(); } } private class DropCacheThread extends Thread { + private Handler handler; - private Handler handler = null; - - public DropCacheThread(Handler handlerIn) { - handler = handlerIn; + public DropCacheThread(Handler handler) { + super(); + this.handler = handler; } @Override public void run() { - cache.drop(handler); + cache.drop(this.handler); } } @@ -1469,7 +1342,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc public void onClick(View arg0) { doExecute(R.string.cache_dialog_watchlist_add_title, R.string.cache_dialog_watchlist_add_message, - new WatchlistAddThread(new WatchlistHandler())); + new WatchlistAddThread(new WatchlistHandler(CacheDetailActivity.this, progress))); } } @@ -1481,7 +1354,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc public void onClick(View arg0) { doExecute(R.string.cache_dialog_watchlist_remove_title, R.string.cache_dialog_watchlist_remove_message, - new WatchlistRemoveThread(new WatchlistHandler())); + new WatchlistRemoveThread(new WatchlistHandler(CacheDetailActivity.this, progress))); } } @@ -1495,6 +1368,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc @Override public void run() { + watchlistThread = null; handler.sendEmptyMessage(ConnectorFactory.getConnector(cache).addToWatchlist(cache) ? 1 : -1); } } @@ -1509,6 +1383,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc @Override public void run() { + watchlistThread = null; handler.sendEmptyMessage(ConnectorFactory.getConnector(cache).removeFromWatchlist(cache) ? 1 : -1); } } @@ -1523,6 +1398,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc @Override public void run() { + watchlistThread = null; handler.sendEmptyMessage(GCConnector.addToFavorites(cache) ? 1 : -1); } } @@ -1537,22 +1413,11 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc @Override public void run() { + watchlistThread = null; handler.sendEmptyMessage(GCConnector.removeFromFavorites(cache) ? 1 : -1); } } - private class FavoriteUpdateHandler extends Handler { - @Override - public void handleMessage(Message msg) { - progress.dismiss(); - if (msg.what == -1) { - showToast(res.getString(R.string.err_favorite_failed)); - } else { - CacheDetailActivity.this.notifyDataSetChanged(); // reload cache details - } - } - } - /** * Listener for "add to favorites" button */ @@ -1561,7 +1426,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc public void onClick(View arg0) { doExecute(R.string.cache_dialog_favorite_add_title, R.string.cache_dialog_favorite_add_message, - new FavoriteAddThread(new FavoriteUpdateHandler())); + new FavoriteAddThread(new FavoriteUpdateHandler(CacheDetailActivity.this, progress))); } } @@ -1573,7 +1438,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc public void onClick(View arg0) { doExecute(R.string.cache_dialog_favorite_remove_title, R.string.cache_dialog_favorite_remove_message, - new FavoriteRemoveThread(new FavoriteUpdateHandler())); + new FavoriteRemoveThread(new FavoriteUpdateHandler(CacheDetailActivity.this, progress))); } } @@ -1613,15 +1478,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 +1512,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 +1545,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 { @@ -1701,22 +1566,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } } - /** - * Handler, called when watchlist add or remove is done - */ - private class WatchlistHandler extends Handler { - @Override - public void handleMessage(Message msg) { - watchlistThread = null; - progress.dismiss(); - if (msg.what == -1) { - showToast(res.getString(R.string.err_watchlist_failed)); - } else { - CacheDetailActivity.this.notifyDataSetChanged(); // reload cache details - } - } - } - private class PreviewMapTask extends AsyncTask<Void, Void, BitmapDrawable> { @Override protected BitmapDrawable doInBackground(Void... parameters) { @@ -1733,7 +1582,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } return ImageUtils.scaleBitmapToFitDisplay(image); - } catch (Exception e) { + } catch (final Exception e) { Log.w("CacheDetailActivity.PreviewMapTask", e); return null; } @@ -1757,12 +1606,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 +1637,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 @@ -1815,6 +1663,18 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } } }); + final Button personalNoteUpload = (Button) view.findViewById(R.id.upload_personalnote); + if (cache.isOffline() && ConnectorFactory.getConnector(cache).supportsPersonalNote()) { + personalNoteUpload.setVisibility(View.VISIBLE); + personalNoteUpload.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + uploadPersonalNote(); + } + }); + } else { + personalNoteUpload.setVisibility(View.GONE); + } // cache hint and spoiler images final View hintBoxView = view.findViewById(R.id.hint_box); @@ -1885,6 +1745,21 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } } + Thread currentThread; + + private void uploadPersonalNote() { + final SimpleCancellableHandler myHandler = new SimpleCancellableHandler(CacheDetailActivity.this, progress); + + Message cancelMessage = myHandler.cancelMessage(res.getString(R.string.cache_personal_note_upload_cancelled)); + progress.show(CacheDetailActivity.this, res.getString(R.string.cache_personal_note_uploading), res.getString(R.string.cache_personal_note_uploading), true, cancelMessage); + + if (currentThread != null) { + currentThread.interrupt(); + } + currentThread = new UploadPersonalNoteThread(cache, myHandler); + currentThread.start(); + } + private void setPersonalNote() { final String personalNote = cache.getPersonalNote(); personalNoteView.setText(personalNote, TextView.BufferType.SPANNABLE); @@ -1896,7 +1771,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); @@ -1938,21 +1813,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 /> @@ -1965,14 +1825,14 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc */ private class LoadDescriptionTask extends AsyncTask<Object, Void, Void> { private final View loadingIndicatorView; - private final TextView descriptionView; + private final IndexOutOfBoundsAvoidingTextView descriptionView; private final String descriptionString; private Spanned description; private final View shortDescView; public LoadDescriptionTask(final String description, final View descriptionView, final View loadingIndicatorView, final View shortDescView) { this.descriptionString = description; - this.descriptionView = (TextView) descriptionView; + this.descriptionView = (IndexOutOfBoundsAvoidingTextView) descriptionView; this.loadingIndicatorView = loadingIndicatorView; this.shortDescView = shortDescView; } @@ -1981,7 +1841,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(); @@ -2008,7 +1868,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc if (needsRefresh) { publishProgress(); } - } catch (Exception e) { + } catch (final Exception e) { Log.e("LoadDescriptionTask: ", e); } return null; @@ -2023,7 +1883,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 @@ -2045,7 +1905,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); } @@ -2074,7 +1934,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); @@ -2084,7 +1944,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); @@ -2096,176 +1956,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 (TextUtils.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 @@ -2315,7 +2005,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! @@ -2402,9 +2092,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()); } } @@ -2449,10 +2139,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 @@ -2504,7 +2194,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; @@ -2534,7 +2224,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 @@ -2564,6 +2254,29 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } } + private class UploadPersonalNoteThread extends Thread { + private Geocache cache = null; + private CancellableHandler handler = null; + + public UploadPersonalNoteThread(Geocache cache, CancellableHandler handler) { + this.cache = cache; + this.handler = handler; + } + + @Override + public void run() { + IConnector con = ConnectorFactory.getConnector(cache); + if (con.supportsPersonalNote()) { + con.uploadPersonalNote(cache); + } + Message msg = Message.obtain(); + Bundle bundle = new Bundle(); + bundle.putString(SimpleCancellableHandler.SUCCESS_TEXT, res.getString(R.string.cache_personal_note_upload_done)); + msg.setData(bundle); + handler.sendMessage(msg); + } + } + @Override protected String getTitle(Page page) { // show number of waypoints directly in waypoint title @@ -2606,10 +2319,10 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return new DescriptionViewCreator(); case LOGS: - return new LogsViewCreator(true); + return new CacheLogsViewCreator(this, true); case LOGSFRIENDS: - return new LogsViewCreator(false); + return new CacheLogsViewCreator(this, false); case WAYPOINTS: return new WaypointsViewCreator(); @@ -2635,7 +2348,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) { @@ -2668,4 +2381,99 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc offlineRefresh.setClickable(true); } + public Geocache getCache() { + return cache; + } + + public SearchResult getSearch() { + return search; + } + + private static final class StoreCacheHandler extends SimpleCancellableHandler { + + public StoreCacheHandler(CacheDetailActivity activity, Progress progress) { + super(activity, progress); + } + + @Override + public void handleRegularMessage(Message msg) { + if (UPDATE_LOAD_PROGRESS_DETAIL == msg.what && msg.obj instanceof String) { + updateStatusMsg(R.string.cache_dialog_offline_save_message, (String) msg.obj); + } else { + notifyDatasetChanged(activityRef); + } + } + } + + private static final class RefreshCacheHandler extends SimpleCancellableHandler { + + public RefreshCacheHandler(CacheDetailActivity activity, Progress progress) { + super(activity, progress); + } + + @Override + public void handleRegularMessage(Message msg) { + if (UPDATE_LOAD_PROGRESS_DETAIL == msg.what && msg.obj instanceof String) { + updateStatusMsg(R.string.cache_dialog_refresh_message, (String) msg.obj); + } else { + notifyDatasetChanged(activityRef); + } + } + } + + private static final class ChangeNotificationHandler extends SimpleHandler { + + public ChangeNotificationHandler(CacheDetailActivity activity, Progress progress) { + super(activity, progress); + } + + @Override + public void handleMessage(Message msg) { + notifyDatasetChanged(activityRef); + } + } + + private static final class FavoriteUpdateHandler extends SimpleHandler { + + public FavoriteUpdateHandler(CacheDetailActivity activity, Progress progress) { + super(activity, progress); + } + + @Override + public void handleMessage(Message msg) { + if (msg.what == -1) { + dismissProgress(); + showToast(R.string.err_favorite_failed); + } else { + notifyDatasetChanged(activityRef); + } + } + } + + /** + * Handler, called when watchlist add or remove is done + */ + private static final class WatchlistHandler extends SimpleHandler { + + public WatchlistHandler(CacheDetailActivity activity, Progress progress) { + super(activity, progress); + } + + @Override + public void handleMessage(Message msg) { + if (msg.what == -1) { + dismissProgress(); + showToast(R.string.err_watchlist_failed); + } else { + notifyDatasetChanged(activityRef); + } + } + } + + private static void notifyDatasetChanged(WeakReference<AbstractActivity> activityRef) { + CacheDetailActivity activity = ((CacheDetailActivity) activityRef.get()); + if (activity != null) { + activity.notifyDataSetChanged(); + } + } } diff --git a/main/src/cgeo/geocaching/CachePopup.java b/main/src/cgeo/geocaching/CachePopup.java index 39a896a..873801e 100644 --- a/main/src/cgeo/geocaching/CachePopup.java +++ b/main/src/cgeo/geocaching/CachePopup.java @@ -4,6 +4,7 @@ import cgeo.geocaching.activity.Progress; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.network.Network; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.CacheDetailsCreator; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; diff --git a/main/src/cgeo/geocaching/CompassActivity.java b/main/src/cgeo/geocaching/CompassActivity.java index 4f8af08..64be52d 100644 --- a/main/src/cgeo/geocaching/CompassActivity.java +++ b/main/src/cgeo/geocaching/CompassActivity.java @@ -7,6 +7,7 @@ import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.maps.CGeoMap; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.speech.SpeechService; import cgeo.geocaching.ui.CompassView; import cgeo.geocaching.utils.GeoDirHandler; @@ -16,8 +17,10 @@ import org.apache.commons.lang3.StringUtils; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; 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 +97,9 @@ public class CompassActivity extends AbstractActivity { setCacheInfo(); Views.inject(this); + + // make sure we can control the TTS volume + setVolumeControlStream(AudioManager.STREAM_MUSIC); } @Override @@ -118,6 +124,20 @@ public class CompassActivity extends AbstractActivity { } @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + setContentView(R.layout.compass_activity); + Views.inject(this); + + setTitle(); + setDestCoords(); + setCacheInfo(); + + geoDirHandler.updateAll(); + } + + @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.compass_activity_options, menu); menu.findItem(R.id.menu_switch_compass_gps).setVisible(hasMagneticFieldSensor); @@ -127,8 +147,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 +218,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 +266,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)); @@ -260,12 +277,7 @@ public class CompassActivity extends AbstractActivity { navAccuracy.setText(null); } - if (geo.getAltitude() != 0.0f) { - final String humanAlt = Units.getDistanceFromMeters((float) geo.getAltitude()); - navLocation.setText(geo.getCoords() + " | " + humanAlt); - } else { - navLocation.setText(geo.getCoords().toString()); - } + navLocation.setText(geo.getCoords().toString()); updateDistanceInfo(geo); } else { 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 dce49a3..56ee959 100644 --- a/main/src/cgeo/geocaching/EditWaypointActivity.java +++ b/main/src/cgeo/geocaching/EditWaypointActivity.java @@ -10,10 +10,11 @@ import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.geopoint.DistanceParser; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; -import cgeo.geocaching.utils.TextUtils; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.TextUtils; import com.googlecode.androidannotations.annotations.EActivity; import com.googlecode.androidannotations.annotations.Extra; @@ -233,7 +234,7 @@ public class EditWaypointActivity extends AbstractActivity { private void initializeDistanceUnitSelector() { distanceUnits = new ArrayList<String>(Arrays.asList(res.getStringArray(R.array.distance_units))); if (initViews) { - distanceUnitSelector.setSelection(Settings.isUseMetricUnits() ? 0 : 2); //0:m, 2:ft + distanceUnitSelector.setSelection(Settings.isUseImperialUnits() ? 2 : 0); //0:m, 2:ft } } @@ -344,7 +345,8 @@ public class EditWaypointActivity extends AbstractActivity { double distance; try { - distance = DistanceParser.parseDistance(distanceText, Settings.isUseMetricUnits()); + distance = DistanceParser.parseDistance(distanceText, + !Settings.isUseImperialUnits()); } catch (NumberFormatException e) { showToast(res.getString(R.string.err_parse_dist)); return; @@ -358,7 +360,8 @@ public class EditWaypointActivity extends AbstractActivity { final String name = StringUtils.isNotEmpty(givenName) ? givenName : res.getString(R.string.waypoint) + " " + (wpCount + 1); final String noteText = note.getText().toString().trim(); final Geopoint coordsToSave = coords; - final WaypointType type = wpTypes.get(waypointTypeSelector.getSelectedItemPosition()); + final int selectedTypeIndex = waypointTypeSelector.getSelectedItemPosition(); + final WaypointType type = selectedTypeIndex >= 0 ? wpTypes.get(selectedTypeIndex) : waypoint.getWaypointType(); final boolean visited = visitedCheckBox.isChecked(); final ProgressDialog progress = ProgressDialog.show(EditWaypointActivity.this, getString(R.string.cache), getString(R.string.waypoint_being_saved), true); final Handler finishHandler = new Handler() { diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java index 1972c7a..3c69197 100644 --- a/main/src/cgeo/geocaching/Geocache.java +++ b/main/src/cgeo/geocaching/Geocache.java @@ -23,6 +23,7 @@ import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.files.GPXParser; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.network.HtmlImage; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.LazyInitializedList; import cgeo.geocaching.utils.Log; @@ -85,7 +86,6 @@ public class Geocache implements ICache, IWaypoint { private String location = null; private Geopoint coords = null; private boolean reliableLatLon = false; - private Double elevation = null; private String personalNote = null; /** * lazy initialized @@ -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+"); @@ -277,11 +278,14 @@ public class Geocache implements ICache, IWaypoint { if (coords == null) { coords = other.coords; } - if (elevation == null) { - elevation = other.elevation; - } - if (personalNote == null) { // don't use StringUtils.isBlank here. Otherwise we cannot recognize a note which was deleted on GC + // don't use StringUtils.isBlank here. Otherwise we cannot recognize a note which was deleted on GC + if (personalNote == null) { personalNote = other.personalNote; + } else if (other.personalNote != null && !personalNote.equals(other.personalNote)) { + final PersonalNote myNote = new PersonalNote(this); + final PersonalNote otherNote = new PersonalNote(other); + final PersonalNote mergedNote = myNote.mergeWith(otherNote); + personalNote = mergedNote.toString(); } if (StringUtils.isBlank(getShortDescription())) { shortdesc = other.getShortDescription(); @@ -313,7 +317,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); } @@ -340,12 +344,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; } } @@ -396,7 +398,6 @@ public class Geocache implements ICache, IWaypoint { StringUtils.equalsIgnoreCase(cacheId, other.cacheId) && (direction != null ? direction.equals(other.direction) : null == other.direction) && (distance != null ? distance.equals(other.distance) : null == other.distance) && - (elevation != null ? elevation.equals(other.elevation) : null == other.elevation) && rating == other.rating && votes == other.votes && myVote == other.myVote && @@ -459,7 +460,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); @@ -478,7 +479,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); @@ -671,7 +672,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()); } @@ -746,7 +747,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); @@ -787,11 +788,6 @@ public class Geocache implements ICache, IWaypoint { } @Override - public boolean isWatchlist() { - return onWatchlist; - } - - @Override public Date getHiddenDate() { return hidden; } @@ -938,14 +934,6 @@ public class Geocache implements ICache, IWaypoint { this.reliableLatLon = reliableLatLon; } - public Double getElevation() { - return elevation; - } - - public void setElevation(Double elevation) { - this.elevation = elevation; - } - public void setShortDescription(String shortdesc) { this.shortdesc = shortdesc; } @@ -986,6 +974,7 @@ public class Geocache implements ICache, IWaypoint { this.inventoryItems = inventoryItems; } + @Override public boolean isOnWatchlist() { return onWatchlist; } @@ -1018,7 +1007,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; @@ -1039,8 +1028,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); } @@ -1133,7 +1122,7 @@ public class Geocache implements ICache, IWaypoint { } public void setPersonalNote(String personalNote) { - this.personalNote = personalNote; + this.personalNote = StringUtils.trimToNull(personalNote); } public void setDisabled(boolean disabled) { @@ -1255,7 +1244,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; @@ -1403,14 +1392,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); } } @@ -1420,7 +1409,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; } @@ -1478,12 +1467,16 @@ 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); } + public int getZoomLevel() { + return this.zoomlevel; + } + public void setZoomlevel(int zoomlevel) { this.zoomlevel = zoomlevel; } @@ -1509,7 +1502,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); } } @@ -1608,7 +1601,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()); } } @@ -1619,9 +1612,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()); } } @@ -1644,7 +1637,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); } } @@ -1697,7 +1690,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 } } @@ -1712,7 +1705,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 } } @@ -1741,9 +1734,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; @@ -1761,7 +1754,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; } @@ -1772,4 +1765,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/GpxFileListActivity.java b/main/src/cgeo/geocaching/GpxFileListActivity.java index de0be21..8b10d5b 100644 --- a/main/src/cgeo/geocaching/GpxFileListActivity.java +++ b/main/src/cgeo/geocaching/GpxFileListActivity.java @@ -4,6 +4,7 @@ import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector;
import cgeo.geocaching.files.AbstractFileListActivity;
import cgeo.geocaching.files.GPXImporter;
+import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.ui.GPXListAdapter;
import org.apache.commons.lang3.StringUtils;
diff --git a/main/src/cgeo/geocaching/IBasicCache.java b/main/src/cgeo/geocaching/IBasicCache.java deleted file mode 100644 index 7e9ef11..0000000 --- a/main/src/cgeo/geocaching/IBasicCache.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * - */ -package cgeo.geocaching; - -import cgeo.geocaching.enumerations.CacheSize; -import cgeo.geocaching.enumerations.CacheType; - -public interface IBasicCache extends ILogable, ICoordinates { - - public abstract String getGuid(); - - /** - * @return Tradi, multi etc. - */ - public abstract CacheType getType(); - - /** - * @return Micro, small etc. - */ - public abstract CacheSize getSize(); - - /** - * @return true if the user already found the cache - * - */ - public abstract boolean isFound(); - - /** - * @return true if the cache is disabled, false else - */ - public abstract boolean isDisabled(); - - /** - * @return Difficulty assessment - */ - public abstract float getDifficulty(); - - /** - * @return Terrain assessment - */ - public abstract float getTerrain(); - - - -} diff --git a/main/src/cgeo/geocaching/ICache.java b/main/src/cgeo/geocaching/ICache.java index 6d0d89a..b99d877 100644 --- a/main/src/cgeo/geocaching/ICache.java +++ b/main/src/cgeo/geocaching/ICache.java @@ -3,6 +3,8 @@ */ package cgeo.geocaching; +import cgeo.geocaching.enumerations.CacheSize; +import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LogType; import java.util.Date; @@ -12,7 +14,7 @@ import java.util.Map; /** * Basic interface for caches */ -public interface ICache extends IBasicCache { +public interface ICache extends ILogable, ICoordinates { /** * @return Displayed owner, might differ from the real owner @@ -63,7 +65,6 @@ public interface ICache extends IBasicCache { /** * @return Guid */ - @Override public String getGuid(); /** @@ -93,7 +94,7 @@ public interface ICache extends IBasicCache { * @return true if the cache is on the watchlist of the user * */ - public boolean isWatchlist(); + public boolean isOnWatchlist(); /** * @return The date the cache has been hidden @@ -129,4 +130,35 @@ public interface ICache extends IBasicCache { * @return normalized, cached name which sort also correct for numerical parts in the name */ public String getNameForSorting(); + + /** + * @return Tradi, multi etc. + */ + CacheType getType(); + + /** + * @return Micro, small etc. + */ + CacheSize getSize(); + + /** + * @return true if the user already found the cache + * + */ + boolean isFound(); + + /** + * @return true if the cache is disabled, false else + */ + boolean isDisabled(); + + /** + * @return Difficulty assessment + */ + float getDifficulty(); + + /** + * @return Terrain assessment + */ + float getTerrain(); } diff --git a/main/src/cgeo/geocaching/IGeoData.java b/main/src/cgeo/geocaching/IGeoData.java index 252fd4f..c5ef698 100644 --- a/main/src/cgeo/geocaching/IGeoData.java +++ b/main/src/cgeo/geocaching/IGeoData.java @@ -13,7 +13,6 @@ public interface IGeoData { public boolean isPseudoLocation(); public Geopoint getCoords(); - public double getAltitude(); public float getBearing(); public float getSpeed(); public float getAccuracy(); diff --git a/main/src/cgeo/geocaching/ImageSelectActivity.java b/main/src/cgeo/geocaching/ImageSelectActivity.java index 39824a9..52f9a7e 100644 --- a/main/src/cgeo/geocaching/ImageSelectActivity.java +++ b/main/src/cgeo/geocaching/ImageSelectActivity.java @@ -5,6 +5,7 @@ import butterknife.Views; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.compatibility.Compatibility; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.ImageUtils; import cgeo.geocaching.utils.Log; @@ -283,7 +284,6 @@ public class ImageSelectActivity extends AbstractActivity { image = BitmapFactory.decodeFile(filePath); } final BitmapDrawable scaledImage = ImageUtils.scaleBitmapTo(image, maxXY, maxXY); - image = null; final String uploadFilename = getOutputImageFile().getPath(); ImageUtils.storeBitmap(scaledImage.getBitmap(), Bitmap.CompressFormat.JPEG, 75, uploadFilename); return uploadFilename; diff --git a/main/src/cgeo/geocaching/ImagesActivity.java b/main/src/cgeo/geocaching/ImagesActivity.java index 030b3f7..0b80d53 100644 --- a/main/src/cgeo/geocaching/ImagesActivity.java +++ b/main/src/cgeo/geocaching/ImagesActivity.java @@ -1,6 +1,7 @@ package cgeo.geocaching; import cgeo.geocaching.activity.AbstractActivity; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.ImagesList; import cgeo.geocaching.ui.ImagesList.ImageType; @@ -57,7 +58,8 @@ public class ImagesActivity extends AbstractActivity { return; } - offline = cgData.isOffline(geocode, null) && (imgType == ImageType.SpoilerImages || Settings.isStoreLogImages()); + offline = cgData.isOffline(geocode, null) && (imgType == ImageType.SpoilerImages + || Settings.isStoreLogImages()); } @Override diff --git a/main/src/cgeo/geocaching/LogCacheActivity.java b/main/src/cgeo/geocaching/LogCacheActivity.java index 62c94ce..e11af0f 100644 --- a/main/src/cgeo/geocaching/LogCacheActivity.java +++ b/main/src/cgeo/geocaching/LogCacheActivity.java @@ -8,6 +8,7 @@ import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.LogTypeTrackable; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.gcvote.GCVote; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.twitter.Twitter; import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.ui.dialog.DateDialog; @@ -71,6 +72,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 +254,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 +305,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 +381,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(); @@ -418,7 +424,7 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia public boolean onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); - final boolean voteAvailable = Settings.isGCvoteLogin() && typeSelected == LogType.FOUND_IT && StringUtils.isNotBlank(cache.getGuid()) && cache.supportsGCVote(); + final boolean voteAvailable = Settings.isGCvoteLogin() && (typeSelected == LogType.FOUND_IT || typeSelected == LogType.ATTENDED || typeSelected == LogType.WEBCAM_PHOTO_TAKEN) && StringUtils.isNotBlank(cache.getGuid()) && cache.supportsGCVote(); menu.findItem(SUBMENU_VOTE).setVisible(voteAvailable); return true; @@ -484,13 +490,21 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia } private void updateTweetBox(LogType type) { - if (type == LogType.FOUND_IT && Settings.isUseTwitter()) { + if (type == LogType.FOUND_IT && Settings.isUseTwitter() && Settings.isTwitterLoginValid()) { tweetBox.setVisibility(View.VISIBLE); } else { tweetBox.setVisibility(View.GONE); } } + 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 +521,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 +534,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 +608,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/LogEntry.java b/main/src/cgeo/geocaching/LogEntry.java index df1038f..0121424 100644 --- a/main/src/cgeo/geocaching/LogEntry.java +++ b/main/src/cgeo/geocaching/LogEntry.java @@ -1,6 +1,7 @@ package cgeo.geocaching; import cgeo.geocaching.enumerations.LogType; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.DateUtils; import cgeo.geocaching.utils.MatcherWrapper; @@ -37,7 +38,7 @@ public final class LogEntry { this(Settings.getUsername(), dateInMilliSeconds, type, text); } - public LogEntry(final String author, long dateInMilliSeconds, final LogType type, final String text) { + public LogEntry(final String author, final long dateInMilliSeconds, final LogType type, final String text) { this.author = author; this.date = dateInMilliSeconds; this.type = type; @@ -50,7 +51,7 @@ public final class LogEntry { } @Override - public boolean equals(Object obj) { + public boolean equals(final Object obj) { if (this == obj) { return true; } @@ -108,7 +109,7 @@ public final class LogEntry { public String getDisplayText() { if (Settings.getPlainLogs()) { MatcherWrapper matcher = new MatcherWrapper(PATTERN_REMOVE_COLORS, log); - return matcher.replaceAll(""); + return matcher.replaceAll(StringUtils.EMPTY); } return log; } diff --git a/main/src/cgeo/geocaching/LogTrackableActivity.java b/main/src/cgeo/geocaching/LogTrackableActivity.java index 1f6feb7..f0dd7f9 100644 --- a/main/src/cgeo/geocaching/LogTrackableActivity.java +++ b/main/src/cgeo/geocaching/LogTrackableActivity.java @@ -9,6 +9,7 @@ import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.twitter.Twitter; import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.ui.dialog.DateDialog; @@ -223,7 +224,7 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat typeSelected = type; typeButton.setText(typeSelected.getL10n()); - if (Settings.isUseTwitter()) { + if (Settings.isUseTwitter() && Settings.isTwitterLoginValid()) { tweetBox.setVisibility(View.VISIBLE); } else { tweetBox.setVisibility(View.GONE); diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java index 03a1d0d..24b4a58 100644 --- a/main/src/cgeo/geocaching/MainActivity.java +++ b/main/src/cgeo/geocaching/MainActivity.java @@ -5,17 +5,18 @@ 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; import cgeo.geocaching.geopoint.Units; import cgeo.geocaching.maps.CGeoMap; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.utils.GeoDirHandler; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.ProcessUtils; import cgeo.geocaching.utils.RunnableWithArgument; import cgeo.geocaching.utils.Version; @@ -30,19 +31,19 @@ import android.app.AlertDialog.Builder; import android.app.SearchManager; import android.content.DialogInterface; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.location.Address; 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 +54,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; @@ -63,10 +62,11 @@ public class MainActivity extends AbstractActivity { @InjectView(R.id.any_button) protected ImageView any; @InjectView(R.id.filter_button) protected ImageView filter; @InjectView(R.id.nearest) protected ImageView nearestView; - @InjectView(R.id.nav_type) protected TextView navType ; - @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.nav_type) protected TextView navType; + @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; @@ -79,84 +79,66 @@ public class MainActivity extends AbstractActivity { private boolean addressObtaining = false; private boolean initialized = false; - final private UpdateLocation locationUpdater = new UpdateLocation(); + private final UpdateLocation locationUpdater = new UpdateLocation(); private Handler updateUserInfoHandler = new Handler() { @Override - public void handleMessage(Message msg) { - - //TODO: Rework to be fully dynamic - 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()); + public void handleMessage(final Message msg) { - userInfoViewGc.setText(userInfo.toString()); - userInfoViewGc.setVisibility(View.VISIBLE); - } - else { - userInfoViewGc.setVisibility(View.GONE); - } + // Get active connectors with login status + ILogin[] loginConns = ConnectorFactory.getActiveLiveConnectors(); - 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"); + // Update UI + infoArea.removeAllViews(); + LayoutInflater inflater = getLayoutInflater(); + + 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); + } } }; private Handler obtainAddressHandler = new Handler() { @Override - public void handleMessage(Message msg) { + public void handleMessage(final Message msg) { try { if (CollectionUtils.isNotEmpty(addresses)) { final Address address = addresses.get(0); - final StringBuilder addText = new StringBuilder(); + final ArrayList<String> addressParts = new ArrayList<String>(); - if (address.getCountryName() != null) { - addText.append(address.getCountryName()); + final String countryName = address.getCountryName(); + if (countryName != null) { + addressParts.add(countryName); } - if (address.getLocality() != null) { - if (addText.length() > 0) { - addText.append(", "); - } - addText.append(address.getLocality()); - } else if (address.getAdminArea() != null) { - if (addText.length() > 0) { - addText.append(", "); + final String locality = address.getLocality(); + if (locality != null) { + addressParts.add(locality); + } else { + final String adminArea = address.getAdminArea(); + if (adminArea != null) { + addressParts.add(adminArea); } - addText.append(address.getAdminArea()); } addCoords = app.currentGeo().getCoords(); - navLocation.setText(addText.toString()); + navLocation.setText(StringUtils.join(addressParts, ", ")); } } catch (Exception e) { // nothing @@ -201,7 +183,7 @@ public class MainActivity extends AbstractActivity { private Handler firstLoginHandler = new Handler() { @Override - public void handleMessage(Message msg) { + public void handleMessage(final Message msg) { try { final StatusCode reason = (StatusCode) msg.obj; @@ -215,7 +197,7 @@ public class MainActivity extends AbstractActivity { }; @Override - public void onCreate(Bundle savedInstanceState) { + public void onCreate(final Bundle savedInstanceState) { // don't call the super implementation with the layout argument, as that would set the wrong theme super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); @@ -236,7 +218,7 @@ public class MainActivity extends AbstractActivity { } @Override - public void onConfigurationChanged(Configuration newConfig) { + public void onConfigurationChanged(final Configuration newConfig) { super.onConfigurationChanged(newConfig); init(); @@ -274,31 +256,20 @@ public class MainActivity extends AbstractActivity { } @Override - public boolean onCreateOptionsMenu(Menu menu) { + public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.main_activity_options, menu); return true; } @Override - public boolean onPrepareOptionsMenu(Menu menu) { + public boolean onPrepareOptionsMenu(final Menu menu) { super.onPrepareOptionsMenu(menu); - MenuItem item = menu.findItem(R.id.menu_scan); - if (item != null) { - item.setEnabled(isIntentAvailable(SCAN_INTENT)); - } + menu.findItem(R.id.menu_scan).setEnabled(ProcessUtils.isIntentAvailable(SCAN_INTENT)); return true; } - public static boolean isIntentAvailable(String intent) { - final PackageManager packageManager = cgeoapplication.getInstance().getPackageManager(); - final List<ResolveInfo> list = packageManager.queryIntentActivities( - new Intent(intent), PackageManager.MATCH_DEFAULT_ONLY); - - return CollectionUtils.isNotEmpty(list); - } - @Override - public boolean onOptionsItemSelected(MenuItem item) { + public boolean onOptionsItemSelected(final MenuItem item) { final int id = item.getItemId(); switch (id) { case R.id.menu_about: @@ -328,7 +299,7 @@ public class MainActivity extends AbstractActivity { } @Override - public void onActivityResult(int requestCode, int resultCode, Intent intent) { + public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) { IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); if (scanResult != null) { String scan = scanResult.getContents(); @@ -364,7 +335,7 @@ public class MainActivity extends AbstractActivity { initialized = true; Settings.setLanguage(Settings.isUseEnglish()); - Settings.getLogin(); + Settings.getGcLogin(); if (app.firstRun) { (new FirstLoginThread()).start(); @@ -373,7 +344,7 @@ public class MainActivity extends AbstractActivity { findOnMap.setClickable(true); findOnMap.setOnClickListener(new OnClickListener() { @Override - public void onClick(View v) { + public void onClick(final View v) { cgeoFindOnMap(v); } }); @@ -381,18 +352,18 @@ public class MainActivity extends AbstractActivity { findByOffline.setClickable(true); findByOffline.setOnClickListener(new OnClickListener() { @Override - public void onClick(View v) { + public void onClick(final View v) { cgeoFindByOffline(v); } }); findByOffline.setOnLongClickListener(new View.OnLongClickListener() { @Override - public boolean onLongClick(View v) { + public boolean onLongClick(final View v) { new StoredList.UserInterface(MainActivity.this).promptForListSelection(R.string.list_title, new RunnableWithArgument<Integer>() { @Override - public void run(Integer selectedListId) { + public void run(final Integer selectedListId) { Settings.saveLastList(selectedListId); cgeocaches.startActivityOffline(MainActivity.this); } @@ -405,7 +376,7 @@ public class MainActivity extends AbstractActivity { advanced.setClickable(true); advanced.setOnClickListener(new OnClickListener() { @Override - public void onClick(View v) { + public void onClick(final View v) { cgeoSearch(v); } }); @@ -413,7 +384,7 @@ public class MainActivity extends AbstractActivity { any.setClickable(true); any.setOnClickListener(new OnClickListener() { @Override - public void onClick(View v) { + public void onClick(final View v) { cgeoPoint(v); } }); @@ -421,14 +392,14 @@ public class MainActivity extends AbstractActivity { filter.setClickable(true); filter.setOnClickListener(new View.OnClickListener() { @Override - public void onClick(View v) { + public void onClick(final View v) { selectGlobalTypeFilter(); } }); filter.setOnLongClickListener(new View.OnLongClickListener() { @Override - public boolean onLongClick(View v) { + public boolean onLongClick(final View v) { selectGlobalTypeFilter(); return true; } @@ -458,7 +429,7 @@ public class MainActivity extends AbstractActivity { Collections.sort(sorted, new Comparator<CacheType>() { @Override - public int compare(CacheType left, CacheType right) { + public int compare(final CacheType left, final CacheType right) { return left.getL10n().compareToIgnoreCase(right.getL10n()); } }); @@ -480,7 +451,7 @@ public class MainActivity extends AbstractActivity { builder.setSingleChoiceItems(items, checkedItem, new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialog, int position) { + public void onClick(final DialogInterface dialog, final int position) { CacheType cacheType = cacheTypes.get(position); Settings.setCacheType(cacheType); setFilterTitle(); @@ -505,7 +476,7 @@ public class MainActivity extends AbstractActivity { .setCancelable(false) .setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialog, int id) { + public void onClick(final DialogInterface dialog, final int id) { dialog.dismiss(); cgData.resetNewlyCreatedDatabase(); app.restoreDatabase(MainActivity.this); @@ -513,7 +484,7 @@ public class MainActivity extends AbstractActivity { }) .setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialog, int id) { + public void onClick(final DialogInterface dialog, final int id) { dialog.cancel(); cgData.resetNewlyCreatedDatabase(); } @@ -533,7 +504,7 @@ public class MainActivity extends AbstractActivity { nearestView.setClickable(true); nearestView.setOnClickListener(new OnClickListener() { @Override - public void onClick(View v) { + public void onClick(final View v) { cgeoFindNearest(v); } }); @@ -557,12 +528,7 @@ public class MainActivity extends AbstractActivity { (new ObtainAddressThread()).start(); } } else { - if (geo.getAltitude() != 0.0) { - final String humanAlt = Units.getDistanceFromKilometers((float) geo.getAltitude() / 1000); - navLocation.setText(geo.getCoords() + " | " + humanAlt); - } else { - navLocation.setText(geo.getCoords().toString()); - } + navLocation.setText(geo.getCoords().toString()); } } else { if (nearestView.isClickable()) { @@ -585,7 +551,7 @@ public class MainActivity extends AbstractActivity { * @param v * unused here but needed since this method is referenced from XML layout */ - public void cgeoFindOnMap(View v) { + public void cgeoFindOnMap(final View v) { findOnMap.setPressed(true); CGeoMap.startActivityLiveMap(this); } @@ -594,7 +560,7 @@ public class MainActivity extends AbstractActivity { * @param v * unused here but needed since this method is referenced from XML layout */ - public void cgeoFindNearest(View v) { + public void cgeoFindNearest(final View v) { if (app.currentGeo().getCoords() == null) { return; } @@ -607,7 +573,7 @@ public class MainActivity extends AbstractActivity { * @param v * unused here but needed since this method is referenced from XML layout */ - public void cgeoFindByOffline(View v) { + public void cgeoFindByOffline(final View v) { findByOffline.setPressed(true); cgeocaches.startActivityOffline(this); } @@ -616,7 +582,7 @@ public class MainActivity extends AbstractActivity { * @param v * unused here but needed since this method is referenced from XML layout */ - public void cgeoSearch(View v) { + public void cgeoSearch(final View v) { advanced.setPressed(true); startActivity(new Intent(this, SearchActivity.class)); } @@ -625,7 +591,7 @@ public class MainActivity extends AbstractActivity { * @param v * unused here but needed since this method is referenced from XML layout */ - public void cgeoPoint(View v) { + public void cgeoPoint(final View v) { any.setPressed(true); startActivity(new Intent(this, NavigateAnyPointActivity.class)); } @@ -634,7 +600,7 @@ public class MainActivity extends AbstractActivity { * @param v * unused here but needed since this method is referenced from XML layout */ - public void cgeoFilter(View v) { + public void cgeoFilter(final View v) { filter.setPressed(true); filter.performClick(); } @@ -643,7 +609,7 @@ public class MainActivity extends AbstractActivity { * @param v * unused here but needed since this method is referenced from XML layout */ - public void cgeoNavSettings(View v) { + public void cgeoNavSettings(final View v) { startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); } @@ -651,7 +617,7 @@ public class MainActivity extends AbstractActivity { private Handler countBubbleHandler = new Handler() { @Override - public void handleMessage(Message msg) { + public void handleMessage(final Message msg) { try { if (countBubbleCnt == 0) { countBubble.setVisibility(View.GONE); @@ -728,36 +694,11 @@ public class MainActivity extends AbstractActivity { return; } - //TODO: Rework to be fully dynamic - 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); } } } @@ -793,7 +734,7 @@ public class MainActivity extends AbstractActivity { * @param view * unused here but needed since this method is referenced from XML layout */ - public void showAbout(View view) { + public void showAbout(final View view) { AboutActivity_.intent(this).start(); } @@ -801,7 +742,7 @@ public class MainActivity extends AbstractActivity { * @param view * unused here but needed since this method is referenced from XML layout */ - public void goSearch(View view) { + public void goSearch(final View view) { onSearchRequested(); } diff --git a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java index 6f94944..be4ef05 100644 --- a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java +++ b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java @@ -8,6 +8,7 @@ import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.geopoint.DistanceParser; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.AbstractViewHolder; import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; @@ -61,7 +62,7 @@ public class NavigateAnyPointActivity extends AbstractActivity { private int contextMenuItemPosition; - private String distanceUnit = ""; + private String distanceUnit = StringUtils.EMPTY; protected static class ViewHolder extends AbstractViewHolder { @InjectView(R.id.simple_way_point_longitude) protected TextView longitude; @@ -259,12 +260,12 @@ public class NavigateAnyPointActivity extends AbstractActivity { private void initializeDistanceUnitSelector() { if (StringUtils.isBlank(distanceUnit)) { - if (Settings.isUseMetricUnits()) { - distanceUnitSelector.setSelection(0); // m - distanceUnit = res.getStringArray(R.array.distance_units)[0]; - } else { + if (Settings.isUseImperialUnits()) { distanceUnitSelector.setSelection(2); // ft distanceUnit = res.getStringArray(R.array.distance_units)[2]; + } else { + distanceUnitSelector.setSelection(0); // m + distanceUnit = res.getStringArray(R.array.distance_units)[0]; } } @@ -519,7 +520,8 @@ public class NavigateAnyPointActivity extends AbstractActivity { double distance; try { - distance = DistanceParser.parseDistance(distanceText, Settings.isUseMetricUnits()); + distance = DistanceParser.parseDistance(distanceText, + !Settings.isUseImperialUnits()); } catch (NumberFormatException e) { showToast(res.getString(R.string.err_parse_dist)); return null; diff --git a/main/src/cgeo/geocaching/PersonalNote.java b/main/src/cgeo/geocaching/PersonalNote.java new file mode 100644 index 0000000..82e88f7 --- /dev/null +++ b/main/src/cgeo/geocaching/PersonalNote.java @@ -0,0 +1,106 @@ +package cgeo.geocaching; + +import org.apache.commons.lang3.StringUtils; + + +public class PersonalNote { + private static final String SEPARATOR = "\n--\n"; + private String cgeoNote; + private String providerNote; + private boolean isOffline; + + private PersonalNote() { + // Empty default constructor + } + + public PersonalNote(final Geocache cache) { + this.isOffline = cache.isOffline(); + final String personalNote = cache.getPersonalNote(); + if (StringUtils.isEmpty(personalNote)) { + return; + } + final String[] notes = StringUtils.splitByWholeSeparator(personalNote, SEPARATOR); + if (notes.length > 1) { + this.cgeoNote = notes[0]; + this.providerNote = notes[1]; + } else { + this.providerNote = notes[0]; + } + } + + public final PersonalNote mergeWith(final PersonalNote other) { + if (StringUtils.isEmpty(cgeoNote) && StringUtils.isEmpty(other.cgeoNote)) { + return mergeOnlyProviderNotes(other); + } + final PersonalNote result = new PersonalNote(); + if (cgeoNote != null && other.cgeoNote != null) { + if (other.isOffline) { + result.cgeoNote = other.cgeoNote; + } else { + result.cgeoNote = cgeoNote; + } + } + if (other.cgeoNote != null) { + result.cgeoNote = other.cgeoNote; + } else { + result.cgeoNote = cgeoNote; + } + if (providerNote != null && other.providerNote != null) { + if (isOffline) { + result.providerNote = providerNote; + } else { + result.providerNote = other.providerNote; + } + } + if (providerNote != null) { + result.providerNote = providerNote; + } else { + result.providerNote = other.providerNote; + } + return result; + } + + /** + * Merge different provider notes from c:geo and provider. + * + * @param other + * The note to merge + * @return PersonalNote The merged note + */ + private PersonalNote mergeOnlyProviderNotes(final PersonalNote other) { + final PersonalNote result = new PersonalNote(); + if (StringUtils.isNotEmpty(other.providerNote) && StringUtils.isNotEmpty(providerNote)) { + if (providerNote.equals(other.providerNote)) { + result.providerNote = providerNote; + return result; + } + if (other.isOffline) { + result.cgeoNote = other.providerNote; + result.providerNote = providerNote; + } else { + result.cgeoNote = providerNote; + result.providerNote = other.providerNote; + } + } + return result; + } + + @Override + public final String toString() { + final StringBuilder builder = new StringBuilder(); + if (cgeoNote != null) { + builder.append(cgeoNote).append(SEPARATOR); + } + builder.append(providerNote); + return builder.toString(); + } + + public final String getCgeoNote() { + return cgeoNote; + } + + public final String getProviderNote() { + return providerNote; + } + +} diff --git a/main/src/cgeo/geocaching/SearchActivity.java b/main/src/cgeo/geocaching/SearchActivity.java index c2a7b6d..6285cda 100644 --- a/main/src/cgeo/geocaching/SearchActivity.java +++ b/main/src/cgeo/geocaching/SearchActivity.java @@ -10,6 +10,7 @@ import cgeo.geocaching.connector.capability.ISearchByGeocode; import cgeo.geocaching.connector.trackable.TrackableConnector; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; import cgeo.geocaching.utils.EditUtils; import cgeo.geocaching.utils.GeoDirHandler; @@ -126,9 +127,7 @@ public class SearchActivity extends AbstractActivity { final IConnector connector = ConnectorFactory.getConnector(geocode); if (connector instanceof ISearchByGeocode) { - final Intent cachesIntent = new Intent(this, CacheDetailActivity.class); - cachesIntent.putExtra(Intents.EXTRA_GEOCODE, geocode.toUpperCase(Locale.US)); - startActivity(cachesIntent); + CacheDetailActivity.startActivity(this, geocode.toUpperCase(Locale.US)); return true; } @@ -150,7 +149,7 @@ public class SearchActivity extends AbstractActivity { } private void init() { - Settings.getLogin(); + Settings.getGcLogin(); buttonLatitude.setOnClickListener(new FindByCoordsAction()); buttonLongitude.setOnClickListener(new FindByCoordsAction()); diff --git a/main/src/cgeo/geocaching/SearchResult.java b/main/src/cgeo/geocaching/SearchResult.java index 4cef95e..5053a85 100644 --- a/main/src/cgeo/geocaching/SearchResult.java +++ b/main/src/cgeo/geocaching/SearchResult.java @@ -24,6 +24,7 @@ import java.util.Set; public class SearchResult implements Parcelable { final private Set<String> geocodes; + final private Set<String> filteredGeocodes; private StatusCode error = null; private String url = ""; public String[] viewstates = null; @@ -55,6 +56,7 @@ public class SearchResult implements Parcelable { */ public SearchResult(final SearchResult searchResult) { geocodes = new HashSet<String>(searchResult.geocodes); + filteredGeocodes = new HashSet<String>(searchResult.filteredGeocodes); error = searchResult.error; url = searchResult.url; viewstates = searchResult.viewstates; @@ -70,6 +72,7 @@ public class SearchResult implements Parcelable { public SearchResult(final Collection<String> geocodes, final int total) { this.geocodes = new HashSet<String>(geocodes.size()); this.geocodes.addAll(geocodes); + this.filteredGeocodes = new HashSet<String>(); this.setTotal(total); } @@ -86,6 +89,9 @@ public class SearchResult implements Parcelable { final ArrayList<String> list = new ArrayList<String>(); in.readStringList(list); geocodes = new HashSet<String>(list); + final ArrayList<String> filteredList = new ArrayList<String>(); + in.readStringList(filteredList); + filteredGeocodes = new HashSet<String>(filteredList); error = (StatusCode) in.readSerializable(); url = in.readString(); final int length = in.readInt(); @@ -121,6 +127,7 @@ public class SearchResult implements Parcelable { @Override public void writeToParcel(final Parcel out, final int flags) { out.writeStringArray(geocodes.toArray(new String[geocodes.size()])); + out.writeStringArray(filteredGeocodes.toArray(new String[filteredGeocodes.size()])); out.writeSerializable(error); out.writeString(url); if (viewstates == null) { @@ -193,16 +200,21 @@ public class SearchResult implements Parcelable { result.geocodes.clear(); final ArrayList<Geocache> cachesForVote = new ArrayList<Geocache>(); final Set<Geocache> caches = cgData.loadCaches(geocodes, LoadFlags.LOAD_CACHE_OR_DB); + int excluded = 0; for (Geocache cache : caches) { // Is there any reason to exclude the cache from the list? final boolean excludeCache = (excludeDisabled && cache.isDisabled()) || (excludeMine && (cache.isOwner() || cache.isFound())) || (!cacheType.contains(cache)); - if (!excludeCache) { + if (excludeCache) { + excluded++; + } else { result.addAndPutInCache(cache); cachesForVote.add(cache); } } + // decrease maximum number of caches by filtered ones + result.setTotal(result.getTotal() - excluded); GCVote.loadRatings(cachesForVote); return result; } @@ -247,4 +259,19 @@ public class SearchResult implements Parcelable { return false; } + public void addFilteredGeocodes(Set<String> cachedMissingFromSearch) { + filteredGeocodes.addAll(cachedMissingFromSearch); + } + + public Set<String> getFilteredGeocodes() { + return Collections.unmodifiableSet(filteredGeocodes); + } + + public void addSearchResult(SearchResult other) { + if (other != null) { + addGeocodes(other.geocodes); + addFilteredGeocodes(other.filteredGeocodes); + } + } + } diff --git a/main/src/cgeo/geocaching/SelectMapfileActivity.java b/main/src/cgeo/geocaching/SelectMapfileActivity.java index aa6d46a..8f82288 100644 --- a/main/src/cgeo/geocaching/SelectMapfileActivity.java +++ b/main/src/cgeo/geocaching/SelectMapfileActivity.java @@ -3,6 +3,7 @@ package cgeo.geocaching; import cgeo.geocaching.files.AbstractFileListActivity; import cgeo.geocaching.files.IFileSelectionView; import cgeo.geocaching.files.LocalStorage; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.FileSelectionListAdapter; import android.content.Context; @@ -19,7 +20,7 @@ public class SelectMapfileActivity extends AbstractFileListActivity<FileSelectio super("map"); } - String mapFile; + private String mapFile; @Override public void onCreate(Bundle savedInstanceState) { diff --git a/main/src/cgeo/geocaching/Settings.java b/main/src/cgeo/geocaching/Settings.java deleted file mode 100644 index 7a5db12..0000000 --- a/main/src/cgeo/geocaching/Settings.java +++ /dev/null @@ -1,1494 +0,0 @@ -package cgeo.geocaching; - -import cgeo.geocaching.apps.cache.navi.NavigationAppFactory.NavigationAppsEnum; -import cgeo.geocaching.connector.gc.GCConstants; -import cgeo.geocaching.connector.gc.Login; -import cgeo.geocaching.enumerations.CacheType; -import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; -import cgeo.geocaching.enumerations.LogType; -import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.maps.MapProviderFactory; -import cgeo.geocaching.maps.google.GoogleMapProvider; -import cgeo.geocaching.maps.interfaces.GeoPointImpl; -import cgeo.geocaching.maps.interfaces.MapProvider; -import cgeo.geocaching.maps.interfaces.MapSource; -import cgeo.geocaching.maps.mapsforge.MapsforgeMapProvider; -import cgeo.geocaching.maps.mapsforge.MapsforgeMapProvider.OfflineMapSource; -import cgeo.geocaching.utils.CryptUtils; -import cgeo.geocaching.utils.FileUtils; -import cgeo.geocaching.utils.FileUtils.FileSelector; -import cgeo.geocaching.utils.Log; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.ImmutablePair; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.os.Environment; -import android.preference.PreferenceManager; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -/** - * General c:geo preferences/settings set by the user - */ -public final class Settings { - - private static final String KEY_TEMP_TWITTER_TOKEN_SECRET = "temp-token-secret"; - private static final String KEY_TEMP_TWITTER_TOKEN_PUBLIC = "temp-token-public"; - private static final String KEY_HELP_SHOWN = "helper"; - private static final String KEY_ANYLONGITUDE = "anylongitude"; - private static final String KEY_ANYLATITUDE = "anylatitude"; - private static final String KEY_USE_OFFLINEMAPS = "offlinemaps"; - private static final String KEY_USE_OFFLINEWPMAPS = "offlinewpmaps"; - private static final String KEY_WEB_DEVICE_CODE = "webDeviceCode"; - private static final String KEY_WEBDEVICE_NAME = "webDeviceName"; - private static final String KEY_MAP_LIVE = "maplive"; - private static final String KEY_MAP_SOURCE = "mapsource"; - private static final String KEY_USE_TWITTER = "twitter"; - private static final String KEY_SHOW_ADDRESS = "showaddress"; - private static final String KEY_SHOW_CAPTCHA = "showcaptcha"; - private static final String KEY_MAP_TRAIL = "maptrail"; - private static final String KEY_LAST_MAP_ZOOM = "mapzoom"; - private static final String KEY_LAST_MAP_LAT = "maplat"; - private static final String KEY_LAST_MAP_LON = "maplon"; - private static final String KEY_LIVE_LIST = "livelist"; - private static final String KEY_METRIC_UNITS = "units"; - private static final String KEY_SKIN = "skin"; - private static final String KEY_LAST_USED_LIST = "lastlist"; - private static final String KEY_CACHE_TYPE = "cachetype"; - private static final String KEY_TWITTER_TOKEN_SECRET = "tokensecret"; - private static final String KEY_TWITTER_TOKEN_PUBLIC = "tokenpublic"; - private static final String KEY_VERSION = "version"; - private static final String KEY_LOAD_DESCRIPTION = "autoloaddesc"; - private static final String KEY_RATING_WANTED = "ratingwanted"; - private static final String KEY_ELEVATION_WANTED = "elevationwanted"; - private static final String KEY_FRIENDLOGS_WANTED = "friendlogswanted"; - private static final String KEY_USE_ENGLISH = "useenglish"; - private static final String KEY_USE_COMPASS = "usecompass"; - private static final String KEY_AUTO_VISIT_TRACKABLES = "trackautovisit"; - private static final String KEY_AUTO_INSERT_SIGNATURE = "sigautoinsert"; - private static final String KEY_ALTITUDE_CORRECTION = "altcorrection"; - private static final String KEY_STORE_LOG_IMAGES = "logimages"; - private static final String KEY_EXCLUDE_DISABLED = "excludedisabled"; - private static final String KEY_EXCLUDE_OWN = "excludemine"; - private static final String KEY_MAPFILE = "mfmapfile"; - private static final String KEY_SIGNATURE = "signature"; - private static final String KEY_GCVOTE_PASSWORD = "pass-vote"; - private static final String KEY_PASSWORD = "password"; - private static final String KEY_USERNAME = "username"; - private static final String KEY_MEMBER_STATUS = "memberstatus"; - private static final String KEY_COORD_INPUT_FORMAT = "coordinputformat"; - private static final String KEY_LOG_OFFLINE = "log_offline"; - private static final String KEY_CHOOSE_LIST = "choose_list"; - private static final String KEY_LOAD_DIRECTION_IMG = "loaddirectionimg"; - private static final String KEY_GC_CUSTOM_DATE = "gccustomdate"; - private static final String KEY_SHOW_WAYPOINTS_THRESHOLD = "gcshowwaypointsthreshold"; - private static final String KEY_COOKIE_STORE = "cookiestore"; - private static final String KEY_OPEN_LAST_DETAILS_PAGE = "opendetailslastpage"; - private static final String KEY_LAST_DETAILS_PAGE = "lastdetailspage"; - private static final String KEY_DEFAULT_NAVIGATION_TOOL = "defaultNavigationTool"; - private static final String KEY_DEFAULT_NAVIGATION_TOOL_2 = "defaultNavigationTool2"; - private static final String KEY_LIVE_MAP_STRATEGY = "livemapstrategy"; - private static final String KEY_DEBUG = "debug"; - private static final String KEY_HIDE_LIVE_MAP_HINT = "hidelivemaphint"; - private static final String KEY_LIVE_MAP_HINT_SHOW_COUNT = "livemaphintshowcount"; - private static final String KEY_SETTINGS_VERSION = "settingsversion"; - private static final String KEY_DB_ON_SDCARD = "dbonsdcard"; - private static final String KEY_LAST_TRACKABLE_ACTION = "trackableaction"; - private static final String KEY_SHARE_AFTER_EXPORT = "shareafterexport"; - private static final String KEY_GPX_EXPORT_DIR = "gpxExportDir"; - private static final String KEY_RENDER_THEME_BASE_FOLDER = "renderthemepath"; - private static final String KEY_RENDER_THEME_FILE_PATH = "renderthemefile"; - private static final String KEY_GPX_IMPORT_DIR = "gpxImportDir"; - private static final String KEY_PLAIN_LOGS = "plainLogs"; - private static final String KEY_NATIVE_UA = "nativeUa"; - private static final String KEY_MAP_DIRECTORY = "mapDirectory"; - private static final String KEY_CONNECTOR_GC_ACTIVE = "connectorGCActive"; - private static final String KEY_CONNECTOR_OC_ACTIVE = "connectorOCActive"; - private static final String KEY_LOG_IMAGE_SCALE = "logImageScale"; - private static final String KEY_OCDE_TOKEN_SECRET = "ocde_tokensecret"; - private static final String KEY_OCDE_TOKEN_PUBLIC = "ocde_tokenpublic"; - private static final String KEY_TEMP_OCDE_TOKEN_SECRET = "ocde-temp-token-secret"; - private static final String KEY_TEMP_OCDE_TOKEN_PUBLIC = "ocde-temp-token-public"; - - - private final static int unitsMetric = 1; - - // twitter api keys - private final static String keyConsumerPublic = CryptUtils.rot13("ESnsCvAv3kEupF1GCR3jGj"); - private final static String keyConsumerSecret = CryptUtils.rot13("7vQWceACV9umEjJucmlpFe9FCMZSeqIqfkQ2BnhV9x"); - - private interface PrefRunnable { - void edit(final Editor edit); - } - - public enum coordInputFormatEnum { - Plain, - Deg, - Min, - Sec; - - public static coordInputFormatEnum fromInt(int id) { - final coordInputFormatEnum[] values = coordInputFormatEnum.values(); - if (id < 0 || id >= values.length) { - return Min; - } - return values[id]; - } - } - - private static String username = null; - private static String password = null; - - private static final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(cgeoapplication.getInstance().getBaseContext()); - static { - migrateSettings(); - Log.setDebugUnsaved(sharedPrefs.getBoolean(KEY_DEBUG, false)); - } - - // maps - private static MapProvider mapProvider = null; - private static String cacheTwitterMessage = "I found [NAME] ([URL])"; - - private Settings() { - // this class is not to be instantiated; - } - - private static void migrateSettings() { - // migrate from non standard file location and integer based boolean types - if (sharedPrefs.getInt(KEY_SETTINGS_VERSION, 0) < 1) { - final String oldPreferencesName = "cgeo.pref"; - final SharedPreferences old = cgeoapplication.getInstance().getSharedPreferences(oldPreferencesName, Context.MODE_PRIVATE); - final Editor e = sharedPrefs.edit(); - - e.putString(KEY_TEMP_TWITTER_TOKEN_SECRET, old.getString(KEY_TEMP_TWITTER_TOKEN_SECRET, null)); - e.putString(KEY_TEMP_TWITTER_TOKEN_PUBLIC, old.getString(KEY_TEMP_TWITTER_TOKEN_PUBLIC, null)); - e.putBoolean(KEY_HELP_SHOWN, old.getInt(KEY_HELP_SHOWN, 0) != 0); - e.putFloat(KEY_ANYLONGITUDE, old.getFloat(KEY_ANYLONGITUDE, 0)); - e.putFloat(KEY_ANYLATITUDE, old.getFloat(KEY_ANYLATITUDE, 0)); - e.putBoolean(KEY_USE_OFFLINEMAPS, 0 != old.getInt(KEY_USE_OFFLINEMAPS, 1)); - e.putBoolean(KEY_USE_OFFLINEWPMAPS, 0 != old.getInt(KEY_USE_OFFLINEWPMAPS, 0)); - e.putString(KEY_WEB_DEVICE_CODE, old.getString(KEY_WEB_DEVICE_CODE, null)); - e.putString(KEY_WEBDEVICE_NAME, old.getString(KEY_WEBDEVICE_NAME, null)); - e.putBoolean(KEY_MAP_LIVE, old.getInt(KEY_MAP_LIVE, 1) != 0); - e.putInt(KEY_MAP_SOURCE, old.getInt(KEY_MAP_SOURCE, 0)); - e.putBoolean(KEY_USE_TWITTER, 0 != old.getInt(KEY_USE_TWITTER, 0)); - e.putBoolean(KEY_SHOW_ADDRESS, 0 != old.getInt(KEY_SHOW_ADDRESS, 1)); - e.putBoolean(KEY_SHOW_CAPTCHA, old.getBoolean(KEY_SHOW_CAPTCHA, false)); - e.putBoolean(KEY_MAP_TRAIL, old.getInt(KEY_MAP_TRAIL, 1) != 0); - e.putInt(KEY_LAST_MAP_ZOOM, old.getInt(KEY_LAST_MAP_ZOOM, 14)); - e.putBoolean(KEY_LIVE_LIST, 0 != old.getInt(KEY_LIVE_LIST, 1)); - e.putBoolean(KEY_METRIC_UNITS, old.getInt(KEY_METRIC_UNITS, unitsMetric) == unitsMetric); - e.putBoolean(KEY_SKIN, old.getInt(KEY_SKIN, 0) != 0); - e.putInt(KEY_LAST_USED_LIST, old.getInt(KEY_LAST_USED_LIST, StoredList.STANDARD_LIST_ID)); - e.putString(KEY_CACHE_TYPE, old.getString(KEY_CACHE_TYPE, CacheType.ALL.id)); - e.putString(KEY_TWITTER_TOKEN_SECRET, old.getString(KEY_TWITTER_TOKEN_SECRET, null)); - e.putString(KEY_TWITTER_TOKEN_PUBLIC, old.getString(KEY_TWITTER_TOKEN_PUBLIC, null)); - e.putInt(KEY_VERSION, old.getInt(KEY_VERSION, 0)); - e.putBoolean(KEY_LOAD_DESCRIPTION, 0 != old.getInt(KEY_LOAD_DESCRIPTION, 1)); - e.putBoolean(KEY_RATING_WANTED, old.getBoolean(KEY_RATING_WANTED, true)); - e.putBoolean(KEY_ELEVATION_WANTED, old.getBoolean(KEY_ELEVATION_WANTED, false)); - e.putBoolean(KEY_FRIENDLOGS_WANTED, old.getBoolean(KEY_FRIENDLOGS_WANTED, true)); - e.putBoolean(KEY_USE_ENGLISH, old.getBoolean(KEY_USE_ENGLISH, false)); - e.putBoolean(KEY_USE_COMPASS, 0 != old.getInt(KEY_USE_COMPASS, 1)); - e.putBoolean(KEY_AUTO_VISIT_TRACKABLES, old.getBoolean(KEY_AUTO_VISIT_TRACKABLES, false)); - e.putBoolean(KEY_AUTO_INSERT_SIGNATURE, old.getBoolean(KEY_AUTO_INSERT_SIGNATURE, false)); - e.putInt(KEY_ALTITUDE_CORRECTION, old.getInt(KEY_ALTITUDE_CORRECTION, 0)); - e.putBoolean(KEY_STORE_LOG_IMAGES, old.getBoolean(KEY_STORE_LOG_IMAGES, false)); - e.putBoolean(KEY_EXCLUDE_DISABLED, 0 != old.getInt(KEY_EXCLUDE_DISABLED, 0)); - e.putBoolean(KEY_EXCLUDE_OWN, 0 != old.getInt(KEY_EXCLUDE_OWN, 0)); - e.putString(KEY_MAPFILE, old.getString(KEY_MAPFILE, null)); - e.putString(KEY_SIGNATURE, old.getString(KEY_SIGNATURE, null)); - e.putString(KEY_GCVOTE_PASSWORD, old.getString(KEY_GCVOTE_PASSWORD, null)); - e.putString(KEY_PASSWORD, old.getString(KEY_PASSWORD, null)); - e.putString(KEY_USERNAME, old.getString(KEY_USERNAME, null)); - e.putString(KEY_MEMBER_STATUS, old.getString(KEY_MEMBER_STATUS, "")); - e.putInt(KEY_COORD_INPUT_FORMAT, old.getInt(KEY_COORD_INPUT_FORMAT, 0)); - e.putBoolean(KEY_LOG_OFFLINE, old.getBoolean(KEY_LOG_OFFLINE, false)); - e.putBoolean(KEY_CHOOSE_LIST, old.getBoolean(KEY_CHOOSE_LIST, false)); - e.putBoolean(KEY_LOAD_DIRECTION_IMG, old.getBoolean(KEY_LOAD_DIRECTION_IMG, true)); - e.putString(KEY_GC_CUSTOM_DATE, old.getString(KEY_GC_CUSTOM_DATE, null)); - e.putInt(KEY_SHOW_WAYPOINTS_THRESHOLD, old.getInt(KEY_SHOW_WAYPOINTS_THRESHOLD, 0)); - e.putString(KEY_COOKIE_STORE, old.getString(KEY_COOKIE_STORE, null)); - e.putBoolean(KEY_OPEN_LAST_DETAILS_PAGE, old.getBoolean(KEY_OPEN_LAST_DETAILS_PAGE, false)); - e.putInt(KEY_LAST_DETAILS_PAGE, old.getInt(KEY_LAST_DETAILS_PAGE, 1)); - e.putInt(KEY_DEFAULT_NAVIGATION_TOOL, old.getInt(KEY_DEFAULT_NAVIGATION_TOOL, NavigationAppsEnum.COMPASS.id)); - e.putInt(KEY_DEFAULT_NAVIGATION_TOOL_2, old.getInt(KEY_DEFAULT_NAVIGATION_TOOL_2, NavigationAppsEnum.INTERNAL_MAP.id)); - e.putInt(KEY_LIVE_MAP_STRATEGY, old.getInt(KEY_LIVE_MAP_STRATEGY, Strategy.AUTO.id)); - e.putBoolean(KEY_DEBUG, old.getBoolean(KEY_DEBUG, false)); - e.putBoolean(KEY_HIDE_LIVE_MAP_HINT, old.getInt(KEY_HIDE_LIVE_MAP_HINT, 0) != 0); - e.putInt(KEY_LIVE_MAP_HINT_SHOW_COUNT, old.getInt(KEY_LIVE_MAP_HINT_SHOW_COUNT, 0)); - - e.putInt(KEY_SETTINGS_VERSION, 1); // mark migrated - e.commit(); - } - } - - public static void setLanguage(boolean useEnglish) { - final Configuration config = new Configuration(); - config.locale = useEnglish ? new Locale("en") : Locale.getDefault(); - final Resources resources = cgeoapplication.getInstance().getResources(); - resources.updateConfiguration(config, resources.getDisplayMetrics()); - } - - public static boolean isLogin() { - final String preUsername = sharedPrefs.getString(KEY_USERNAME, null); - final String prePassword = sharedPrefs.getString(KEY_PASSWORD, null); - - return !StringUtils.isBlank(preUsername) && !StringUtils.isBlank(prePassword); - } - - /** - * Get login and password information. - * - * @return a pair (login, password) or null if no valid information is stored - */ - public static ImmutablePair<String, String> getLogin() { - if (username == null || password == null) { - final String preUsername = sharedPrefs.getString(KEY_USERNAME, null); - final String prePassword = sharedPrefs.getString(KEY_PASSWORD, null); - - if (preUsername == null || prePassword == null) { - return null; - } - - username = preUsername; - password = prePassword; - } - return new ImmutablePair<String, String>(username, password); - } - - public static String getUsername() { - return username != null ? username : sharedPrefs.getString(KEY_USERNAME, null); - } - - public static boolean setLogin(final String username, final String password) { - Settings.username = username; - Settings.password = password; - return editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { - // erase username and password - edit.remove(KEY_USERNAME); - edit.remove(KEY_PASSWORD); - } else { - // save username and password - edit.putString(KEY_USERNAME, username); - edit.putString(KEY_PASSWORD, password); - } - } - }); - } - - public static boolean isGCConnectorActive() { - return sharedPrefs.getBoolean(KEY_CONNECTOR_GC_ACTIVE, true); - } - - public static boolean setGCConnectorActive(final boolean isActive) { - return editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_CONNECTOR_GC_ACTIVE, isActive); - } - }); - } - - public static boolean isPremiumMember() { - // Basic Member, Premium Member, ??? - String memberStatus = Settings.getMemberStatus(); - if (memberStatus == null) { - return false; - } - return GCConstants.MEMBER_STATUS_PM.equalsIgnoreCase(memberStatus); - } - - public static String getMemberStatus() { - return sharedPrefs.getString(KEY_MEMBER_STATUS, ""); - } - - public static boolean setMemberStatus(final String memberStatus) { - return editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - if (StringUtils.isBlank(memberStatus)) { - edit.remove(KEY_MEMBER_STATUS); - } else { - edit.putString(KEY_MEMBER_STATUS, memberStatus); - } - } - }); - } - - public static boolean isOCConnectorActive() { - return sharedPrefs.getBoolean(KEY_CONNECTOR_OC_ACTIVE, false); - } - - public static boolean setOCConnectorActive(final boolean isActive) { - return editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_CONNECTOR_OC_ACTIVE, isActive); - } - }); - } - - public static String getOCDETokenPublic() { - return sharedPrefs.getString(KEY_OCDE_TOKEN_PUBLIC, ""); - } - - public static String getOCDETokenSecret() { - return sharedPrefs.getString(KEY_OCDE_TOKEN_SECRET, ""); - } - - public static void setOCDETokens(final String tokenPublic, final String tokenSecret, boolean enableOcDe) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putString(KEY_OCDE_TOKEN_PUBLIC, tokenPublic); - edit.putString(KEY_OCDE_TOKEN_SECRET, tokenSecret); - if (tokenPublic != null) { - edit.remove(KEY_TEMP_OCDE_TOKEN_PUBLIC); - edit.remove(KEY_TEMP_OCDE_TOKEN_SECRET); - } - } - }); - setOCConnectorActive(enableOcDe); - } - - public static void setOCDETempTokens(final String tokenPublic, final String tokenSecret) { - editSharedSettings(new PrefRunnable() { - @Override - public void edit(Editor edit) { - edit.putString(KEY_TEMP_OCDE_TOKEN_PUBLIC, tokenPublic); - edit.putString(KEY_TEMP_OCDE_TOKEN_SECRET, tokenSecret); - } - }); - } - - public static ImmutablePair<String, String> getTempOCDEToken() { - String tokenPublic = sharedPrefs.getString(KEY_TEMP_OCDE_TOKEN_PUBLIC, null); - String tokenSecret = sharedPrefs.getString(KEY_TEMP_OCDE_TOKEN_SECRET, null); - return new ImmutablePair<String, String>(tokenPublic, tokenSecret); - } - - public static boolean isGCvoteLogin() { - final String preUsername = sharedPrefs.getString(KEY_USERNAME, null); - final String prePassword = sharedPrefs.getString(KEY_GCVOTE_PASSWORD, null); - - return !StringUtils.isBlank(preUsername) && !StringUtils.isBlank(prePassword); - } - - public static boolean setGCvoteLogin(final String password) { - return editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - if (StringUtils.isBlank(password)) { - // erase password - edit.remove(KEY_GCVOTE_PASSWORD); - } else { - // save password - edit.putString(KEY_GCVOTE_PASSWORD, password); - } - } - }); - } - - public static ImmutablePair<String, String> getGCvoteLogin() { - final String username = sharedPrefs.getString(KEY_USERNAME, null); - final String password = sharedPrefs.getString(KEY_GCVOTE_PASSWORD, null); - - if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { - return null; - } - - return new ImmutablePair<String, String>(username, password); - } - - public static boolean setSignature(final String signature) { - return editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - if (StringUtils.isBlank(signature)) { - // erase signature - edit.remove(KEY_SIGNATURE); - } else { - // save signature - edit.putString(KEY_SIGNATURE, signature); - } - } - }); - } - - public static String getSignature() { - return sharedPrefs.getString(KEY_SIGNATURE, null); - } - - public static boolean setCookieStore(final String cookies) { - return editSharedSettings(new PrefRunnable() { - - @Override - public void edit(final Editor edit) { - if (StringUtils.isBlank(cookies)) { - // erase cookies - edit.remove(KEY_COOKIE_STORE); - } else { - // save cookies - edit.putString(KEY_COOKIE_STORE, cookies); - } - } - }); - } - - public static String getCookieStore() { - return sharedPrefs.getString(KEY_COOKIE_STORE, null); - } - - /** - * @param cacheType - * The cache type used for future filtering - */ - public static void setCacheType(final CacheType cacheType) { - editSharedSettings(new PrefRunnable() { - @Override - public void edit(Editor edit) { - if (cacheType == null) { - edit.remove(KEY_CACHE_TYPE); - } else { - edit.putString(KEY_CACHE_TYPE, cacheType.id); - } - } - }); - } - - public static void setLiveMap(final boolean live) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_MAP_LIVE, live); - } - }); - } - - public static int getLastList() { - return sharedPrefs.getInt(KEY_LAST_USED_LIST, StoredList.STANDARD_LIST_ID); - } - - public static void saveLastList(final int listId) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_LAST_USED_LIST, listId); - } - }); - } - - public static void setWebNameCode(final String name, final String code) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - - edit.putString(KEY_WEBDEVICE_NAME, name); - edit.putString(KEY_WEB_DEVICE_CODE, code); - } - }); - } - - public static MapProvider getMapProvider() { - if (mapProvider == null) { - mapProvider = getMapSource().getMapProvider(); - } - return mapProvider; - } - - public static String getMapFile() { - return sharedPrefs.getString(KEY_MAPFILE, null); - } - - public static boolean setMapFile(final String mapFile) { - boolean result = editSharedSettings(new PrefRunnable() { - @Override - public void edit(Editor edit) { - edit.putString(KEY_MAPFILE, mapFile); - } - }); - if (mapFile != null) { - setMapFileDirectory(new File(mapFile).getParent()); - } - return result; - } - - public static String getMapFileDirectory() { - final String mapDir = sharedPrefs.getString(KEY_MAP_DIRECTORY, null); - if (mapDir != null) { - return mapDir; - } - final String mapFile = getMapFile(); - if (mapFile != null) { - return new File(mapFile).getParent(); - } - return null; - } - - public static boolean setMapFileDirectory(final String mapFileDirectory) { - return editSharedSettings(new PrefRunnable() { - @Override - public void edit(Editor edit) { - edit.putString(KEY_MAP_DIRECTORY, mapFileDirectory); - MapsforgeMapProvider.getInstance().updateOfflineMaps(); - } - }); - } - - public static boolean isValidMapFile() { - return isValidMapFile(getMapFile()); - } - - public static boolean isValidMapFile(final String mapFileIn) { - return MapsforgeMapProvider.isValidMapFile(mapFileIn); - } - - public static coordInputFormatEnum getCoordInputFormat() { - return coordInputFormatEnum.fromInt(sharedPrefs.getInt(KEY_COORD_INPUT_FORMAT, 0)); - } - - public static void setCoordInputFormat(final coordInputFormatEnum format) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_COORD_INPUT_FORMAT, format.ordinal()); - } - }); - } - - static void setLogOffline(final boolean offline) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_LOG_OFFLINE, offline); - } - }); - } - - public static boolean getLogOffline() { - return sharedPrefs.getBoolean(KEY_LOG_OFFLINE, false); - } - - static void setChooseList(final boolean choose) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_CHOOSE_LIST, choose); - } - }); - } - - public static boolean getChooseList() { - return sharedPrefs.getBoolean(KEY_CHOOSE_LIST, false); - } - - static void setLoadDirImg(final boolean value) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_LOAD_DIRECTION_IMG, value); - } - }); - } - - public static boolean getLoadDirImg() { - return !isPremiumMember() && sharedPrefs.getBoolean(KEY_LOAD_DIRECTION_IMG, true); - } - - public static void setGcCustomDate(final String format) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putString(KEY_GC_CUSTOM_DATE, format); - } - }); - } - - /** - * @return User selected date format on GC.com - * @see Login#gcCustomDateFormats - */ - public static String getGcCustomDate() { - return sharedPrefs.getString(KEY_GC_CUSTOM_DATE, null); - } - - public static boolean isExcludeMyCaches() { - return sharedPrefs.getBoolean(KEY_EXCLUDE_OWN, false); - } - - /** - * edit some settings without knowing how to get the settings editor or how to commit - * - * @param runnable - * @return - */ - private static boolean editSharedSettings(final PrefRunnable runnable) { - final SharedPreferences.Editor prefsEdit = sharedPrefs.edit(); - runnable.edit(prefsEdit); - return prefsEdit.commit(); - } - - public static void setExcludeMine(final boolean exclude) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_EXCLUDE_OWN, exclude); - } - }); - } - - public static void setUseEnglish(final boolean english) { - editSharedSettings(new PrefRunnable() { - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_USE_ENGLISH, english); - setLanguage(english); - } - }); - } - - public static boolean isUseEnglish() { - return sharedPrefs.getBoolean(KEY_USE_ENGLISH, false); - } - - public static boolean isShowAddress() { - return sharedPrefs.getBoolean(KEY_SHOW_ADDRESS, true); - } - - public static void setShowAddress(final boolean showAddress) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_SHOW_ADDRESS, showAddress); - } - }); - } - - public static boolean isShowCaptcha() { - return !isPremiumMember() && sharedPrefs.getBoolean(KEY_SHOW_CAPTCHA, false); - } - - public static void setShowCaptcha(final boolean showCaptcha) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_SHOW_CAPTCHA, showCaptcha); - } - }); - } - - public static boolean isExcludeDisabledCaches() { - return sharedPrefs.getBoolean(KEY_EXCLUDE_DISABLED, false); - } - - public static void setExcludeDisabledCaches(final boolean exclude) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_EXCLUDE_DISABLED, exclude); - } - }); - } - - public static boolean isStoreOfflineMaps() { - return sharedPrefs.getBoolean(KEY_USE_OFFLINEMAPS, true); - } - - public static void setStoreOfflineMaps(final boolean offlineMaps) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_USE_OFFLINEMAPS, offlineMaps); - } - }); - } - - public static boolean isStoreOfflineWpMaps() { - return sharedPrefs.getBoolean(KEY_USE_OFFLINEWPMAPS, false); - } - - public static void setStoreOfflineWpMaps(final boolean offlineMaps) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_USE_OFFLINEWPMAPS, offlineMaps); - } - }); - } - - public static boolean isStoreLogImages() { - return sharedPrefs.getBoolean(KEY_STORE_LOG_IMAGES, false); - } - - public static void setStoreLogImages(final boolean storeLogImages) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_STORE_LOG_IMAGES, storeLogImages); - } - }); - } - - public static boolean isAutoLoadDescription() { - return sharedPrefs.getBoolean(KEY_LOAD_DESCRIPTION, true); - } - - public static void setAutoLoadDesc(final boolean autoLoad) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_LOAD_DESCRIPTION, autoLoad); - } - }); - } - - public static boolean isRatingWanted() { - return sharedPrefs.getBoolean(KEY_RATING_WANTED, true); - } - - public static void setRatingWanted(final boolean ratingWanted) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_RATING_WANTED, ratingWanted); - } - }); - } - - public static boolean isElevationWanted() { - return sharedPrefs.getBoolean(KEY_ELEVATION_WANTED, false); - } - - public static void setElevationWanted(final boolean elevationWanted) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_ELEVATION_WANTED, elevationWanted); - } - }); - } - - public static boolean isFriendLogsWanted() { - if (!isLogin()) { - // don't show a friends log if the user is anonymous - return false; - } - return sharedPrefs.getBoolean(KEY_FRIENDLOGS_WANTED, true); - } - - public static void setFriendLogsWanted(final boolean friendLogsWanted) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_FRIENDLOGS_WANTED, friendLogsWanted); - } - }); - } - - public static boolean isLiveList() { - return sharedPrefs.getBoolean(KEY_LIVE_LIST, true); - } - - public static void setLiveList(final boolean liveList) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_LIVE_LIST, liveList); - } - }); - } - - public static boolean isTrackableAutoVisit() { - return sharedPrefs.getBoolean(KEY_AUTO_VISIT_TRACKABLES, false); - } - - public static void setTrackableAutoVisit(final boolean autoVisit) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_AUTO_VISIT_TRACKABLES, autoVisit); - } - }); - } - - public static boolean isAutoInsertSignature() { - return sharedPrefs.getBoolean(KEY_AUTO_INSERT_SIGNATURE, false); - } - - public static void setAutoInsertSignature(final boolean autoInsert) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_AUTO_INSERT_SIGNATURE, autoInsert); - } - }); - } - - public static boolean isUseMetricUnits() { - return sharedPrefs.getBoolean(KEY_METRIC_UNITS, true); - } - - public static void setUseMetricUnits(final boolean metric) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_METRIC_UNITS, metric); - } - }); - } - - public static boolean isLiveMap() { - return sharedPrefs.getBoolean(KEY_MAP_LIVE, true); - } - - public static boolean isMapTrail() { - return sharedPrefs.getBoolean(KEY_MAP_TRAIL, true); - } - - public static void setMapTrail(final boolean showTrail) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_MAP_TRAIL, showTrail); - } - }); - } - - public static int getMapZoom() { - return sharedPrefs.getInt(KEY_LAST_MAP_ZOOM, 14); - } - - public static void setMapZoom(final int mapZoomLevel) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_LAST_MAP_ZOOM, mapZoomLevel); - } - }); - } - - public static GeoPointImpl getMapCenter() { - return getMapProvider().getMapItemFactory() - .getGeoPointBase(new Geopoint(sharedPrefs.getInt(KEY_LAST_MAP_LAT, 0) / 1e6, - sharedPrefs.getInt(KEY_LAST_MAP_LON, 0) / 1e6)); - } - - public static void setMapCenter(final GeoPointImpl mapViewCenter) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_LAST_MAP_LAT, mapViewCenter.getLatitudeE6()); - edit.putInt(KEY_LAST_MAP_LON, mapViewCenter.getLongitudeE6()); - } - }); - } - - public static MapSource getMapSource() { - final int id = getConvertedMapId(); - final MapSource map = MapProviderFactory.getMapSource(id); - if (map != null) { - // don't use offline maps if the map file is not valid - if ((!(map instanceof OfflineMapSource)) || (isValidMapFile())) { - return map; - } - } - // fallback to first available map - return MapProviderFactory.getDefaultSource(); - } - - private final static int GOOGLEMAP_BASEID = 30; - private final static int MAP = 1; - private final static int SATELLITE = 2; - - private final static int MFMAP_BASEID = 40; - private final static int MAPNIK = 1; - private final static int CYCLEMAP = 3; - private final static int OFFLINE = 4; - - /** - * convert old preference ids for maps (based on constant values) into new hash based ids - * - * @return - */ - private static int getConvertedMapId() { - final int id = sharedPrefs.getInt(KEY_MAP_SOURCE, 0); - switch (id) { - case GOOGLEMAP_BASEID + MAP: - return GoogleMapProvider.GOOGLE_MAP_ID.hashCode(); - case GOOGLEMAP_BASEID + SATELLITE: - return GoogleMapProvider.GOOGLE_SATELLITE_ID.hashCode(); - case MFMAP_BASEID + MAPNIK: - return MapsforgeMapProvider.MAPSFORGE_MAPNIK_ID.hashCode(); - case MFMAP_BASEID + CYCLEMAP: - return MapsforgeMapProvider.MAPSFORGE_CYCLEMAP_ID.hashCode(); - case MFMAP_BASEID + OFFLINE: { - final String mapFile = Settings.getMapFile(); - if (StringUtils.isNotEmpty(mapFile)) { - return mapFile.hashCode(); - } - break; - } - default: - break; - } - return id; - } - - public static void setMapSource(final MapSource newMapSource) { - if (!MapProviderFactory.isSameActivity(getMapSource(), newMapSource)) { - mapProvider = null; - } - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_MAP_SOURCE, newMapSource.getNumericalId()); - } - }); - if (newMapSource instanceof OfflineMapSource) { - setMapFile(((OfflineMapSource) newMapSource).getFileName()); - } - } - - public static void setAnyCoordinates(final Geopoint coords) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - if (null != coords) { - edit.putFloat(KEY_ANYLATITUDE, (float) coords.getLatitude()); - edit.putFloat(KEY_ANYLONGITUDE, (float) coords.getLongitude()); - } else { - edit.remove(KEY_ANYLATITUDE); - edit.remove(KEY_ANYLONGITUDE); - } - } - }); - - } - - public static Geopoint getAnyCoordinates() { - if (sharedPrefs.contains(KEY_ANYLATITUDE) && sharedPrefs.contains(KEY_ANYLONGITUDE)) { - float lat = sharedPrefs.getFloat(KEY_ANYLATITUDE, 0); - float lon = sharedPrefs.getFloat(KEY_ANYLONGITUDE, 0); - return new Geopoint(lat, lon); - } - return null; - } - - public static boolean isUseCompass() { - return sharedPrefs.getBoolean(KEY_USE_COMPASS, true); - } - - public static void setUseCompass(final boolean useCompass) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_USE_COMPASS, useCompass); - } - }); - } - - public static boolean isHelpShown() { - return sharedPrefs.getBoolean(KEY_HELP_SHOWN, false); - } - - public static void setHelpShown() { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_HELP_SHOWN, true); - } - }); - } - - public static boolean isLightSkin() { - return sharedPrefs.getBoolean(KEY_SKIN, false); - } - - public static void setLightSkin(final boolean lightSkin) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_SKIN, lightSkin); - } - }); - } - - public static String getKeyConsumerPublic() { - return keyConsumerPublic; - } - - public static String getKeyConsumerSecret() { - return keyConsumerSecret; - } - - public static int getAltCorrection() { - return sharedPrefs.getInt(KEY_ALTITUDE_CORRECTION, 0); - } - - public static boolean setAltCorrection(final int altitude) { - return editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_ALTITUDE_CORRECTION, altitude); - } - }); - } - - public static String getWebDeviceCode() { - return sharedPrefs.getString(KEY_WEB_DEVICE_CODE, null); - } - - public static String getWebDeviceName() { - return sharedPrefs.getString(KEY_WEBDEVICE_NAME, null); - } - - /** - * @return The cache type used for filtering or ALL if no filter is active. Returns never null - */ - public static CacheType getCacheType() { - return CacheType.getById(sharedPrefs.getString(KEY_CACHE_TYPE, CacheType.ALL.id)); - } - - /** - * The Threshold for the showing of child waypoints - * - * @return - */ - - public static int getWayPointsThreshold() { - return sharedPrefs.getInt(KEY_SHOW_WAYPOINTS_THRESHOLD, 0); - } - - public static void setShowWaypointsThreshold(final int threshold) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_SHOW_WAYPOINTS_THRESHOLD, threshold); - } - }); - } - - public static boolean isUseTwitter() { - return sharedPrefs.getBoolean(KEY_USE_TWITTER, false); - } - - public static void setUseTwitter(final boolean useTwitter) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_USE_TWITTER, useTwitter); - } - }); - } - - public static boolean isTwitterLoginValid() { - return !StringUtils.isBlank(getTokenPublic()) && !StringUtils.isBlank(getTokenSecret()); - } - - public static String getTokenPublic() { - return sharedPrefs.getString(KEY_TWITTER_TOKEN_PUBLIC, null); - } - - public static String getTokenSecret() { - return sharedPrefs.getString(KEY_TWITTER_TOKEN_SECRET, null); - - } - - public static int getVersion() { - return sharedPrefs.getInt(KEY_VERSION, 0); - } - - public static void setTwitterTokens(final String tokenPublic, final String tokenSecret, boolean enableTwitter) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putString(KEY_TWITTER_TOKEN_PUBLIC, tokenPublic); - edit.putString(KEY_TWITTER_TOKEN_SECRET, tokenSecret); - if (tokenPublic != null) { - edit.remove(KEY_TEMP_TWITTER_TOKEN_PUBLIC); - edit.remove(KEY_TEMP_TWITTER_TOKEN_SECRET); - } - } - }); - setUseTwitter(enableTwitter); - } - - public static void setTwitterTempTokens(final String tokenPublic, final String tokenSecret) { - editSharedSettings(new PrefRunnable() { - @Override - public void edit(Editor edit) { - edit.putString(KEY_TEMP_TWITTER_TOKEN_PUBLIC, tokenPublic); - edit.putString(KEY_TEMP_TWITTER_TOKEN_SECRET, tokenSecret); - } - }); - } - - public static ImmutablePair<String, String> getTempToken() { - String tokenPublic = sharedPrefs.getString(KEY_TEMP_TWITTER_TOKEN_PUBLIC, null); - String tokenSecret = sharedPrefs.getString(KEY_TEMP_TWITTER_TOKEN_SECRET, null); - return new ImmutablePair<String, String>(tokenPublic, tokenSecret); - } - - public static void setVersion(final int version) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_VERSION, version); - } - }); - } - - public static boolean isOpenLastDetailsPage() { - return sharedPrefs.getBoolean(KEY_OPEN_LAST_DETAILS_PAGE, false); - } - - public static void setOpenLastDetailsPage(final boolean openLastPage) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_OPEN_LAST_DETAILS_PAGE, openLastPage); - } - }); - } - - public static int getLastDetailsPage() { - return sharedPrefs.getInt(KEY_LAST_DETAILS_PAGE, 1); - } - - public static void setLastDetailsPage(final int index) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_LAST_DETAILS_PAGE, index); - } - }); - } - - public static int getDefaultNavigationTool() { - return sharedPrefs.getInt(KEY_DEFAULT_NAVIGATION_TOOL, NavigationAppsEnum.COMPASS.id); - } - - public static void setDefaultNavigationTool(final int defaultNavigationTool) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_DEFAULT_NAVIGATION_TOOL, defaultNavigationTool); - } - }); - } - - public static int getDefaultNavigationTool2() { - return sharedPrefs.getInt(KEY_DEFAULT_NAVIGATION_TOOL_2, NavigationAppsEnum.INTERNAL_MAP.id); - } - - public static void setDefaultNavigationTool2(final int defaultNavigationTool) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_DEFAULT_NAVIGATION_TOOL_2, defaultNavigationTool); - } - }); - } - - public static Strategy getLiveMapStrategy() { - return Strategy.getById(sharedPrefs.getInt(KEY_LIVE_MAP_STRATEGY, Strategy.AUTO.id)); - } - - public static void setLiveMapStrategy(final Strategy strategy) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_LIVE_MAP_STRATEGY, strategy.id); - } - }); - } - - public static boolean isDebug() { - return Log.isDebug(); - } - - public static void setDebug(final boolean debug) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_DEBUG, debug); - } - }); - Log.setDebugUnsaved(debug); - } - - public static boolean getHideLiveMapHint() { - return sharedPrefs.getBoolean(KEY_HIDE_LIVE_MAP_HINT, false); - } - - public static void setHideLiveHint(final boolean hide) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_HIDE_LIVE_MAP_HINT, hide); - } - }); - } - - public static int getLiveMapHintShowCount() { - return sharedPrefs.getInt(KEY_LIVE_MAP_HINT_SHOW_COUNT, 0); - } - - public static void setLiveMapHintShowCount(final int showCount) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_LIVE_MAP_HINT_SHOW_COUNT, showCount); - } - }); - } - - public static boolean isDbOnSDCard() { - return sharedPrefs.getBoolean(KEY_DB_ON_SDCARD, false); - } - - public static void setDbOnSDCard(final boolean dbOnSDCard) { - editSharedSettings(new PrefRunnable() { - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_DB_ON_SDCARD, dbOnSDCard); - } - }); - } - - public static String getGpxExportDir() { - return sharedPrefs.getString(KEY_GPX_EXPORT_DIR, Environment.getExternalStorageDirectory().getPath() + "/gpx"); - } - - public static void setGpxExportDir(final String gpxExportDir) { - editSharedSettings(new PrefRunnable() { - @Override - public void edit(Editor edit) { - edit.putString(KEY_GPX_EXPORT_DIR, gpxExportDir); - } - }); - } - - public static String getGpxImportDir() { - return sharedPrefs.getString(KEY_GPX_IMPORT_DIR, Environment.getExternalStorageDirectory().getPath() + "/gpx"); - } - - public static void setGpxImportDir(final String gpxImportDir) { - editSharedSettings(new PrefRunnable() { - @Override - public void edit(Editor edit) { - edit.putString(KEY_GPX_IMPORT_DIR, gpxImportDir); - } - }); - } - - public static boolean getShareAfterExport() { - return sharedPrefs.getBoolean(KEY_SHARE_AFTER_EXPORT, true); - } - - public static void setShareAfterExport(final boolean shareAfterExport) { - editSharedSettings(new PrefRunnable() { - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_SHARE_AFTER_EXPORT, shareAfterExport); - } - }); - } - - public static int getTrackableAction() { - return sharedPrefs.getInt(KEY_LAST_TRACKABLE_ACTION, LogType.RETRIEVED_IT.id); - } - - public static void setTrackableAction(final int trackableAction) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_LAST_TRACKABLE_ACTION, trackableAction); - } - }); - } - - public static String getCustomRenderThemeBaseFolder() { - return sharedPrefs.getString(KEY_RENDER_THEME_BASE_FOLDER, ""); - } - - public static boolean setCustomRenderThemeBaseFolder(final String customRenderThemeBaseFolder) { - return editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putString(KEY_RENDER_THEME_BASE_FOLDER, customRenderThemeBaseFolder); - } - }); - } - - public static String getCustomRenderThemeFilePath() { - return sharedPrefs.getString(KEY_RENDER_THEME_FILE_PATH, ""); - } - - public static void setCustomRenderThemeFile(final String customRenderThemeFile) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putString(KEY_RENDER_THEME_FILE_PATH, customRenderThemeFile); - } - }); - } - - public static File[] getMapThemeFiles() { - - File directory = new File(Settings.getCustomRenderThemeBaseFolder()); - - List<File> result = new ArrayList<File>(); - - FileUtils.listDir(result, directory, new ExtensionsBasedFileSelector(new String[] { "xml" }), null); - - return result.toArray(new File[result.size()]); - } - - private static class ExtensionsBasedFileSelector extends FileSelector { - - private final String[] extensions; - - public ExtensionsBasedFileSelector(String[] extensions) { - this.extensions = extensions; - } - - @Override - public boolean isSelected(File file) { - String filename = file.getName(); - for (String ext : extensions) { - if (StringUtils.endsWithIgnoreCase(filename, ext)) { - return true; - } - } - return false; - } - - @Override - public boolean shouldEnd() { - return false; - } - } - - public static boolean getPlainLogs() { - return sharedPrefs.getBoolean(KEY_PLAIN_LOGS, false); - } - - public static void setPlainLogs(final boolean plainLogs) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_PLAIN_LOGS, plainLogs); - } - }); - } - - public static boolean getUseNativeUa() { - return sharedPrefs.getBoolean(KEY_NATIVE_UA, false); - } - - public static void setUseNativeUa(final boolean useNativeUa) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putBoolean(KEY_NATIVE_UA, useNativeUa); - } - }); - } - - public static String getCacheTwitterMessage() { - // TODO make customizable from UI - return cacheTwitterMessage; - } - - public static String getTrackableTwitterMessage() { - // TODO make customizable from UI - return "I touched [NAME] ([URL])!"; - } - - public static void setCacheTwitterMessage(final String message) { - cacheTwitterMessage = message; - } - - public static int getLogImageScale() { - return sharedPrefs.getInt(KEY_LOG_IMAGE_SCALE, -1); - } - - public static void setLogImageScale(final int scale) { - editSharedSettings(new PrefRunnable() { - - @Override - public void edit(Editor edit) { - edit.putInt(KEY_LOG_IMAGE_SCALE, scale); - } - }); - } -} diff --git a/main/src/cgeo/geocaching/SettingsActivity.java b/main/src/cgeo/geocaching/SettingsActivity.java deleted file mode 100644 index 872d5ee..0000000 --- a/main/src/cgeo/geocaching/SettingsActivity.java +++ /dev/null @@ -1,1069 +0,0 @@ -package cgeo.geocaching; - -import cgeo.geocaching.activity.AbstractActivity; -import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; -import cgeo.geocaching.apps.cache.navi.NavigationAppFactory.NavigationAppsEnum; -import cgeo.geocaching.compatibility.Compatibility; -import cgeo.geocaching.connector.gc.Login; -import cgeo.geocaching.connector.oc.OCAuthorizationActivity; -import cgeo.geocaching.enumerations.StatusCode; -import cgeo.geocaching.files.SimpleDirChooser; -import cgeo.geocaching.maps.MapProviderFactory; -import cgeo.geocaching.maps.interfaces.MapSource; -import cgeo.geocaching.network.Cookies; -import cgeo.geocaching.network.Network; -import cgeo.geocaching.network.Parameters; -import cgeo.geocaching.twitter.TwitterAuthorizationActivity; -import cgeo.geocaching.ui.Formatter; -import cgeo.geocaching.utils.Log; -import cgeo.geocaching.utils.LogTemplateProvider; -import cgeo.geocaching.utils.LogTemplateProvider.LogTemplate; -import cgeo.geocaching.utils.RunnableWithArgument; - -import ch.boye.httpclientandroidlib.HttpResponse; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.openintents.intents.FileManagerIntents; - -import android.app.ProgressDialog; -import android.content.Context; -import android.content.Intent; -import android.content.res.Configuration; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -public class SettingsActivity extends AbstractActivity { - - private final static int SELECT_MAPFILE_REQUEST = 1; - private final static int SELECT_GPX_EXPORT_REQUEST = 2; - private final static int SELECT_GPX_IMPORT_REQUEST = 3; - private final static int SELECT_THEMEFOLDER_REQUEST = 4; - - private ProgressDialog loginDialog = null; - private ProgressDialog webDialog = null; - private boolean enableTemplatesMenu = false; - private Handler logInHandler = new Handler() { - - @Override - public void handleMessage(Message msg) { - try { - if (loginDialog != null && loginDialog.isShowing()) { - loginDialog.dismiss(); - } - - if (msg.obj == null || (msg.obj instanceof Drawable)) { - helpDialog(res.getString(R.string.init_login_popup), res.getString(R.string.init_login_popup_ok), - (Drawable) msg.obj); - } else { - helpDialog(res.getString(R.string.init_login_popup), - res.getString(R.string.init_login_popup_failed_reason) + " " + - ((StatusCode) msg.obj).getErrorString(res) + "."); - } - } catch (Exception e) { - showToast(res.getString(R.string.err_login_failed)); - - Log.e("SettingsActivity.logInHandler", e); - } - - if (loginDialog != null && loginDialog.isShowing()) { - loginDialog.dismiss(); - } - - init(); - } - }; - - private Handler webAuthHandler = new Handler() { - - @Override - public void handleMessage(Message msg) { - try { - if (webDialog != null && webDialog.isShowing()) { - webDialog.dismiss(); - } - - if (msg.what > 0) { - helpDialog(res.getString(R.string.init_sendToCgeo), res.getString(R.string.init_sendToCgeo_register_ok).replace("####", String.valueOf(msg.what))); - } else { - helpDialog(res.getString(R.string.init_sendToCgeo), res.getString(R.string.init_sendToCgeo_register_fail)); - } - } catch (Exception e) { - showToast(res.getString(R.string.init_sendToCgeo_register_fail)); - - Log.e("SettingsActivity.webHandler", e); - } - - if (webDialog != null && webDialog.isShowing()) { - webDialog.dismiss(); - } - - init(); - } - }; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState, R.layout.settings_activity); - - init(); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - - init(); - } - - @Override - public void onPause() { - saveValues(); - super.onPause(); - } - - @Override - public void onStop() { - saveValues(); - Compatibility.dataChanged(getPackageName()); - super.onStop(); - } - - @Override - public void onDestroy() { - saveValues(); - - super.onDestroy(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.settings_activity_options, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.menu_clear) { - ((EditText) findViewById(R.id.username)).setText(""); - ((EditText) findViewById(R.id.password)).setText(""); - ((EditText) findViewById(R.id.passvote)).setText(""); - - if (saveValues()) { - showToast(res.getString(R.string.init_cleared)); - } else { - showToast(res.getString(R.string.err_init_cleared)); - } - - finish(); - } - - return false; - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { - if (enableTemplatesMenu) { - menu.setHeaderTitle(R.string.init_signature_template_button); - for (LogTemplate template : LogTemplateProvider.getTemplates()) { - menu.add(0, template.getItemId(), 0, template.getResourceId()); - } - } - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - LogTemplate template = LogTemplateProvider.getTemplate(item.getItemId()); - if (template != null) { - return insertSignatureTemplate(template); - } - return super.onContextItemSelected(item); - } - - private boolean insertSignatureTemplate(final LogTemplate template) { - EditText sig = (EditText) findViewById(R.id.signature); - String insertText = "[" + template.getTemplateString() + "]"; - insertAtPosition(sig, insertText, true); - return true; - } - - public void init() { - - // geocaching.com settings - final CheckBox gcCheck = (CheckBox) findViewById(R.id.gc_option); - gcCheck.setChecked(Settings.isGCConnectorActive()); - gcCheck.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setGCConnectorActive(gcCheck.isChecked()); - } - }); - final ImmutablePair<String, String> login = Settings.getLogin(); - if (login != null) { - ((EditText) findViewById(R.id.username)).setText(login.left); - ((EditText) findViewById(R.id.password)).setText(login.right); - } - - Button logMeIn = (Button) findViewById(R.id.log_me_in); - logMeIn.setOnClickListener(new LoginListener()); - - TextView legalNote = (TextView) findViewById(R.id.legal_note); - legalNote.setClickable(true); - legalNote.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View arg0) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/about/termsofuse.aspx"))); - } - }); - - // opencaching.de settings - final CheckBox ocCheck = (CheckBox) findViewById(R.id.oc_option); - ocCheck.setChecked(Settings.isOCConnectorActive()); - ocCheck.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setOCConnectorActive(ocCheck.isChecked()); - } - }); - - Button checkOCUser = (Button) findViewById(R.id.register_oc_de); - checkOCUser.setOnClickListener(new OCDEAuthorizeCgeoListener()); - - // gcvote settings - final ImmutablePair<String, String> gcvoteLogin = Settings.getGCvoteLogin(); - if (null != gcvoteLogin && null != gcvoteLogin.right) { - ((EditText) findViewById(R.id.passvote)).setText(gcvoteLogin.right); - } - - // Twitter settings - Button authorizeTwitter = (Button) findViewById(R.id.authorize_twitter); - authorizeTwitter.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View arg0) { - Intent authIntent = new Intent(SettingsActivity.this, TwitterAuthorizationActivity.class); - startActivity(authIntent); - } - }); - - final CheckBox twitterButton = (CheckBox) findViewById(R.id.twitter_option); - twitterButton.setChecked(Settings.isUseTwitter() && Settings.isTwitterLoginValid()); - twitterButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setUseTwitter(twitterButton.isChecked()); - if (Settings.isUseTwitter() && !Settings.isTwitterLoginValid()) { - Intent authIntent = new Intent(SettingsActivity.this, TwitterAuthorizationActivity.class); - startActivity(authIntent); - } - - twitterButton.setChecked(Settings.isUseTwitter()); - } - }); - - // Signature settings - EditText sigEdit = (EditText) findViewById(R.id.signature); - if (sigEdit.getText().length() == 0) { - sigEdit.setText(Settings.getSignature()); - } - Button sigBtn = (Button) findViewById(R.id.signature_help); - sigBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - helpDialog(res.getString(R.string.init_signature_help_title), res.getString(R.string.init_signature_help_text)); - } - }); - Button templates = (Button) findViewById(R.id.signature_template); - registerForContextMenu(templates); - templates.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - enableTemplatesMenu = true; - openContextMenu(v); - enableTemplatesMenu = false; - } - }); - final CheckBox autoinsertButton = (CheckBox) findViewById(R.id.sigautoinsert); - autoinsertButton.setChecked(Settings.isAutoInsertSignature()); - autoinsertButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setAutoInsertSignature(autoinsertButton.isChecked()); - } - }); - - // Cache details - final CheckBox autoloadButton = (CheckBox) findViewById(R.id.autoload); - autoloadButton.setChecked(Settings.isAutoLoadDescription()); - autoloadButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setAutoLoadDesc(autoloadButton.isChecked()); - } - }); - - final CheckBox ratingWantedButton = (CheckBox) findViewById(R.id.ratingwanted); - ratingWantedButton.setChecked(Settings.isRatingWanted()); - ratingWantedButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setRatingWanted(ratingWantedButton.isChecked()); - } - }); - - final CheckBox elevationWantedButton = (CheckBox) findViewById(R.id.elevationwanted); - elevationWantedButton.setChecked(Settings.isElevationWanted()); - elevationWantedButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setElevationWanted(elevationWantedButton.isChecked()); - } - }); - - final CheckBox friendLogsWantedButton = (CheckBox) findViewById(R.id.friendlogswanted); - friendLogsWantedButton.setChecked(Settings.isFriendLogsWanted()); - friendLogsWantedButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setFriendLogsWanted(friendLogsWantedButton.isChecked()); - } - }); - - final CheckBox openLastDetailsPageButton = (CheckBox) findViewById(R.id.openlastdetailspage); - openLastDetailsPageButton.setChecked(Settings.isOpenLastDetailsPage()); - openLastDetailsPageButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setOpenLastDetailsPage(openLastDetailsPageButton.isChecked()); - } - }); - - // Other settings - final CheckBox skinButton = (CheckBox) findViewById(R.id.skin); - skinButton.setChecked(Settings.isLightSkin()); - skinButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setLightSkin(skinButton.isChecked()); - } - }); - - final CheckBox addressButton = (CheckBox) findViewById(R.id.address); - addressButton.setChecked(Settings.isShowAddress()); - addressButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setShowAddress(addressButton.isChecked()); - } - }); - - final CheckBox captchaButton = (CheckBox) findViewById(R.id.captcha); - captchaButton.setEnabled(!Settings.isPremiumMember()); - captchaButton.setChecked(Settings.isShowCaptcha()); - captchaButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setShowCaptcha(captchaButton.isChecked()); - } - }); - - final CheckBox dirImgButton = (CheckBox) findViewById(R.id.loaddirectionimg); - dirImgButton.setEnabled(!Settings.isPremiumMember()); - dirImgButton.setChecked(Settings.getLoadDirImg()); - dirImgButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setLoadDirImg(!Settings.getLoadDirImg()); - dirImgButton.setChecked(Settings.getLoadDirImg()); - } - }); - - final CheckBox useEnglishButton = (CheckBox) findViewById(R.id.useenglish); - useEnglishButton.setChecked(Settings.isUseEnglish()); - useEnglishButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setUseEnglish(useEnglishButton.isChecked()); - } - }); - - final CheckBox excludeButton = (CheckBox) findViewById(R.id.exclude); - excludeButton.setChecked(Settings.isExcludeMyCaches()); - excludeButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setExcludeMine(excludeButton.isChecked()); - } - }); - - final CheckBox disabledButton = (CheckBox) findViewById(R.id.disabled); - disabledButton.setChecked(Settings.isExcludeDisabledCaches()); - disabledButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setExcludeDisabledCaches(disabledButton.isChecked()); - } - }); - - TextView showWaypointsThreshold = (TextView) findViewById(R.id.showwaypointsthreshold); - showWaypointsThreshold.setText(String.valueOf(Settings.getWayPointsThreshold())); - - final CheckBox autovisitButton = (CheckBox) findViewById(R.id.trackautovisit); - autovisitButton.setChecked(Settings.isTrackableAutoVisit()); - autovisitButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setTrackableAutoVisit(autovisitButton.isChecked()); - } - }); - - final CheckBox offlineButton = (CheckBox) findViewById(R.id.offline); - offlineButton.setChecked(Settings.isStoreOfflineMaps()); - offlineButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setStoreOfflineMaps(offlineButton.isChecked()); - } - }); - - final CheckBox offlineWpButton = (CheckBox) findViewById(R.id.offline_wp); - offlineWpButton.setChecked(Settings.isStoreOfflineWpMaps()); - offlineWpButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setStoreOfflineWpMaps(offlineWpButton.isChecked()); - } - }); - - final CheckBox saveLogImgButton = (CheckBox) findViewById(R.id.save_log_img); - saveLogImgButton.setChecked(Settings.isStoreLogImages()); - saveLogImgButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setStoreLogImages(saveLogImgButton.isChecked()); - } - }); - - final CheckBox livelistButton = (CheckBox) findViewById(R.id.livelist); - livelistButton.setChecked(Settings.isLiveList()); - livelistButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setLiveList(livelistButton.isChecked()); - } - }); - - final CheckBox unitsButton = (CheckBox) findViewById(R.id.units); - unitsButton.setChecked(!Settings.isUseMetricUnits()); - unitsButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setUseMetricUnits(!unitsButton.isChecked()); - } - }); - - final CheckBox logOffline = (CheckBox) findViewById(R.id.log_offline); - logOffline.setChecked(Settings.getLogOffline()); - logOffline.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setLogOffline(!Settings.getLogOffline()); - logOffline.setChecked(Settings.getLogOffline()); - } - }); - - final CheckBox chooseList = (CheckBox) findViewById(R.id.choose_list); - chooseList.setChecked(Settings.getChooseList()); - chooseList.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setChooseList(!Settings.getChooseList()); - chooseList.setChecked(Settings.getChooseList()); - } - }); - - final CheckBox plainLogs = (CheckBox) findViewById(R.id.plain_logs); - plainLogs.setChecked(Settings.getPlainLogs()); - plainLogs.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setPlainLogs(!Settings.getPlainLogs()); - plainLogs.setChecked(Settings.getPlainLogs()); - } - }); - - // Workaround for cspire customers on mobile connections #1843 - final CheckBox useNativeUserAgent = (CheckBox) findViewById(R.id.use_native_ua); - useNativeUserAgent.setChecked(Settings.getUseNativeUa()); - useNativeUserAgent.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setUseNativeUa(!Settings.getUseNativeUa()); - useNativeUserAgent.setChecked(Settings.getUseNativeUa()); - } - }); - - // Altitude settings - EditText altitudeEdit = (EditText) findViewById(R.id.altitude); - altitudeEdit.setText(String.valueOf(Settings.getAltCorrection())); - - //Send2cgeo settings - String webDeviceName = Settings.getWebDeviceName(); - - if (StringUtils.isNotBlank(webDeviceName)) { - ((EditText) findViewById(R.id.webDeviceName)).setText(webDeviceName); - } else { - String s = android.os.Build.MODEL; - ((EditText) findViewById(R.id.webDeviceName)).setText(s); - } - - Button webAuth = (Button) findViewById(R.id.sendToCgeo_register); - webAuth.setOnClickListener(new WebAuthListener()); - - // Map source settings - updateMapSourceMenu(); - - Button selectMapDirectory = (Button) findViewById(R.id.select_map_directory); - selectMapDirectory.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Intent selectIntent = new Intent(SettingsActivity.this, SelectMapfileActivity.class); - startActivityForResult(selectIntent, SELECT_MAPFILE_REQUEST); - } - }); - - // Theme folder settings - initThemefolderEdittext(false); - - Button selectThemefolder = (Button) findViewById(R.id.select_themefolder); - selectThemefolder.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - selectDirectory(Settings.getCustomRenderThemeBaseFolder(), SELECT_THEMEFOLDER_REQUEST); - } - }); - - // GPX Export directory - final EditText gpxExportDir = (EditText) findViewById(R.id.gpx_exportdir); - gpxExportDir.setText(Settings.getGpxExportDir()); - Button selectGpxExportDir = (Button) findViewById(R.id.select_gpx_exportdir); - selectGpxExportDir.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - selectDirectory(Settings.getGpxExportDir(), SELECT_GPX_EXPORT_REQUEST); - } - }); - - // GPX Import directory - final EditText gpxImportDir = (EditText) findViewById(R.id.gpx_importdir); - gpxImportDir.setText(Settings.getGpxImportDir()); - Button selectGpxImportDir = (Button) findViewById(R.id.select_gpx_importdir); - selectGpxImportDir.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - selectDirectory(Settings.getGpxImportDir(), SELECT_GPX_IMPORT_REQUEST); - } - }); - - // Display trail on map - final CheckBox trailButton = (CheckBox) findViewById(R.id.trail); - trailButton.setChecked(Settings.isMapTrail()); - trailButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setMapTrail(trailButton.isChecked()); - } - }); - - // Default navigation tool settings - Spinner defaultNavigationToolSelector = (Spinner) findViewById(R.id.default_navigation_tool); - final List<NavigationAppsEnum> apps = NavigationAppFactory.getInstalledDefaultNavigationApps(); - ArrayAdapter<NavigationAppsEnum> naviAdapter = new ArrayAdapter<NavigationAppsEnum>(this, android.R.layout.simple_spinner_item, apps) { - @Override - public View getView(int position, View convertView, ViewGroup parent) { - TextView textView = (TextView) super.getView(position, convertView, parent); - textView.setText(getItem(position).app.getName()); - return textView; - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - TextView textView = (TextView) super.getDropDownView(position, convertView, parent); - textView.setText(getItem(position).app.getName()); - return textView; - } - }; - naviAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - defaultNavigationToolSelector.setAdapter(naviAdapter); - int defaultNavigationTool = Settings.getDefaultNavigationTool(); - int ordinal = 0; - for (int i = 0; i < apps.size(); i++) { - if (apps.get(i).id == defaultNavigationTool) { - ordinal = i; - break; - } - } - defaultNavigationToolSelector.setSelection(ordinal); - defaultNavigationToolSelector.setOnItemSelectedListener(new OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - NavigationAppsEnum item = (NavigationAppsEnum) parent.getItemAtPosition(position); - if (item != null) { - Settings.setDefaultNavigationTool(item.id); - } - } - - @Override - public void onNothingSelected(AdapterView<?> arg0) { - // noop - } - }); - - // 2nd Default navigation tool settings - Spinner defaultNavigationTool2Selector = (Spinner) findViewById(R.id.default_navigation_tool_2); - // final List<NavigationAppsEnum> apps = NavigationAppFactory.getInstalledNavigationApps(this); - ArrayAdapter<NavigationAppsEnum> navi2Adapter = new ArrayAdapter<NavigationAppsEnum>(this, android.R.layout.simple_spinner_item, apps) { - @Override - public View getView(int position, View convertView, ViewGroup parent) { - TextView textView = (TextView) super.getView(position, convertView, parent); - textView.setText(getItem(position).app.getName()); - return textView; - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - TextView textView = (TextView) super.getDropDownView(position, convertView, parent); - textView.setText(getItem(position).app.getName()); - return textView; - } - }; - navi2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - defaultNavigationTool2Selector.setAdapter(navi2Adapter); - int defaultNavigationTool2 = Settings.getDefaultNavigationTool2(); - int ordinal2 = 0; - for (int i = 0; i < apps.size(); i++) { - if (apps.get(i).id == defaultNavigationTool2) { - ordinal2 = i; - break; - } - } - defaultNavigationTool2Selector.setSelection(ordinal2); - defaultNavigationTool2Selector.setOnItemSelectedListener(new OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - NavigationAppsEnum item = (NavigationAppsEnum) parent.getItemAtPosition(position); - if (item != null) { - Settings.setDefaultNavigationTool2(item.id); - } - } - - @Override - public void onNothingSelected(AdapterView<?> arg0) { - // noop - } - }); - - refreshBackupLabel(); - - // Database location - refreshDbOnSDCardSetting(); - - final CheckBox dbOnSDCardButton = (CheckBox) findViewById(R.id.dbonsdcard); - dbOnSDCardButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - app.moveDatabase(SettingsActivity.this); - } - }); - - // Debug settings - final CheckBox debugButton = (CheckBox) findViewById(R.id.debug); - debugButton.setChecked(Settings.isDebug()); - debugButton.setOnClickListener(new View.OnClickListener() { - - @Override - public void onClick(View v) { - Settings.setDebug(!Settings.isDebug()); - debugButton.setChecked(Settings.isDebug()); - } - }); - } - - private void updateMapSourceMenu() { - Collection<String> mapSourceNames = new ArrayList<String>(); - for (MapSource mapSource : MapProviderFactory.getMapSources()) { - mapSourceNames.add(mapSource.getName()); - } - Spinner mapSourceSelector = (Spinner) findViewById(R.id.mapsource); - ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item, mapSourceNames.toArray(new String[mapSourceNames.size()])); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mapSourceSelector.setAdapter(adapter); - final int index = MapProviderFactory.getMapSources().indexOf(Settings.getMapSource()); - mapSourceSelector.setSelection(index); - mapSourceSelector.setOnItemSelectedListener(new ChangeMapSourceListener()); - - initMapDirectoryEdittext(false); - } - - private void initMapDirectoryEdittext(boolean setFocus) { - final EditText mapDirectoryEdit = (EditText) findViewById(R.id.map_directory); - mapDirectoryEdit.setText(Settings.getMapFileDirectory()); - if (setFocus) { - mapDirectoryEdit.requestFocus(); - } - } - - private void initThemefolderEdittext(boolean setFocus) { - EditText themeFileEdit = (EditText) findViewById(R.id.themefolder); - themeFileEdit.setText(Settings.getCustomRenderThemeBaseFolder()); - if (setFocus) { - themeFileEdit.requestFocus(); - } - } - - /** - * @param view - * unused here but needed since this method is referenced from XML layout - */ - public void backup(View view) { - // avoid overwriting an existing backup with an empty database (can happen directly after reinstalling the app) - if (cgData.getAllCachesCount() == 0) { - helpDialog(res.getString(R.string.init_backup), res.getString(R.string.init_backup_unnecessary)); - return; - } - - final ProgressDialog dialog = ProgressDialog.show(this, res.getString(R.string.init_backup), res.getString(R.string.init_backup_running), true, false); - new Thread() { - @Override - public void run() { - final String backupFileName = cgData.backupDatabase(); - runOnUiThread(new Runnable() { - @Override - public void run() { - dialog.dismiss(); - helpDialog(res.getString(R.string.init_backup_backup), - backupFileName != null ? res.getString(R.string.init_backup_success) + "\n" + backupFileName : res.getString(R.string.init_backup_failed)); - refreshBackupLabel(); - } - }); - } - }.start(); - } - - private void refreshBackupLabel() { - TextView lastBackup = (TextView) findViewById(R.id.backup_last); - File lastBackupFile = cgData.getRestoreFile(); - if (lastBackupFile != null) { - lastBackup.setText(res.getString(R.string.init_backup_last) + " " + Formatter.formatTime(lastBackupFile.lastModified()) + ", " + Formatter.formatDate(lastBackupFile.lastModified())); - } else { - lastBackup.setText(res.getString(R.string.init_backup_last_no)); - } - } - - private void refreshDbOnSDCardSetting() { - final CheckBox dbOnSDCardButton = (CheckBox) findViewById(R.id.dbonsdcard); - dbOnSDCardButton.setChecked(Settings.isDbOnSDCard()); - } - - /** - * @param view - * unused here but needed since this method is referenced from XML layout - */ - public void restore(View view) { - app.restoreDatabase(this); - } - - public boolean saveValues() { - String usernameNew = StringUtils.trimToEmpty(((EditText) findViewById(R.id.username)).getText().toString()); - String passwordNew = StringUtils.trimToEmpty(((EditText) findViewById(R.id.password)).getText().toString()); - String passvoteNew = StringUtils.trimToEmpty(((EditText) findViewById(R.id.passvote)).getText().toString()); - // don't trim signature, user may want to have whitespace at the beginning - String signatureNew = ((EditText) findViewById(R.id.signature)).getText().toString(); - String mapDirectoryNew = StringUtils.trimToEmpty(((EditText) findViewById(R.id.map_directory)).getText().toString()); - String themesDirectoryNew = StringUtils.trimToEmpty(((EditText) findViewById(R.id.themefolder)).getText().toString()); - - String altitudeNew = StringUtils.trimToNull(((EditText) findViewById(R.id.altitude)).getText().toString()); - int altitudeNewInt = parseNumber(altitudeNew, 0); - - TextView field = (TextView) findViewById(R.id.showwaypointsthreshold); - final int waypointThreshold = parseNumber(field.getText().toString(), 5); - - final boolean status1 = Settings.setLogin(usernameNew, passwordNew); - final boolean status2 = Settings.setGCvoteLogin(passvoteNew); - final boolean status3 = Settings.setSignature(signatureNew); - final boolean status4 = Settings.setAltCorrection(altitudeNewInt); - final boolean status5 = Settings.setMapFileDirectory(mapDirectoryNew); - final boolean status6 = Settings.setCustomRenderThemeBaseFolder(themesDirectoryNew); - Settings.setShowWaypointsThreshold(waypointThreshold); - - String importNew = StringUtils.trimToEmpty(((EditText) findViewById(R.id.gpx_importdir)).getText().toString()); - String exportNew = StringUtils.trimToEmpty(((EditText) findViewById(R.id.gpx_exportdir)).getText().toString()); - Settings.setGpxImportDir(importNew); - Settings.setGpxExportDir(exportNew); - - return status1 && status2 && status3 && status4 && status5 && status6; - } - - /** - * Returns the integer value from the string - * - * @param field - * the field to retrieve the integer value from - * @param defaultValue - * the default value - * @return either the field content or the default value - */ - - static private int parseNumber(final String number, int defaultValue) { - try { - return Integer.parseInt(number); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - private static class ChangeMapSourceListener implements OnItemSelectedListener { - - @Override - public void onItemSelected(AdapterView<?> arg0, View arg1, int position, - long arg3) { - Settings.setMapSource(MapProviderFactory.getMapSources().get(position)); - } - - @Override - public void onNothingSelected(AdapterView<?> arg0) { - arg0.setSelection(MapProviderFactory.getMapSources().indexOf(Settings.getMapSource())); - } - } - - private class LoginListener implements View.OnClickListener { - - @Override - public void onClick(View arg0) { - final String username = ((EditText) findViewById(R.id.username)).getText().toString(); - final String password = ((EditText) findViewById(R.id.password)).getText().toString(); - - if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { - showToast(res.getString(R.string.err_missing_auth)); - return; - } - - loginDialog = ProgressDialog.show(SettingsActivity.this, res.getString(R.string.init_login_popup), res.getString(R.string.init_login_popup_working), true); - loginDialog.setCancelable(false); - - Settings.setLogin(username, password); - Cookies.clearCookies(); - - (new Thread() { - - @Override - public void run() { - final StatusCode loginResult = Login.login(); - Object payload = loginResult; - if (loginResult == StatusCode.NO_ERROR) { - Login.detectGcCustomDate(); - payload = Login.downloadAvatarAndGetMemberStatus(); - } - logInHandler.obtainMessage(0, payload).sendToTarget(); - } - }).start(); - } - } - - private class OCDEAuthorizeCgeoListener implements View.OnClickListener { - - @Override - public void onClick(View v) { - Intent authIntent = new Intent(SettingsActivity.this, OCAuthorizationActivity.class); - startActivity(authIntent); - } - } - - private class WebAuthListener implements View.OnClickListener { - - @Override - public void onClick(View arg0) { - final String deviceName = ((EditText) findViewById(R.id.webDeviceName)).getText().toString(); - final String deviceCode = Settings.getWebDeviceCode(); - - if (StringUtils.isBlank(deviceName)) { - showToast(res.getString(R.string.err_missing_device_name)); - return; - } - - webDialog = ProgressDialog.show(SettingsActivity.this, res.getString(R.string.init_sendToCgeo), res.getString(R.string.init_sendToCgeo_registering), true); - webDialog.setCancelable(false); - - (new Thread() { - - @Override - public void run() { - int pin = 0; - - final String nam = StringUtils.defaultString(deviceName); - final String cod = StringUtils.defaultString(deviceCode); - - final Parameters params = new Parameters("name", nam, "code", cod); - HttpResponse response = Network.getRequest("http://send2.cgeo.org/auth.html", params); - - if (response != null && response.getStatusLine().getStatusCode() == 200) { - //response was OK - String[] strings = Network.getResponseData(response).split(","); - try { - pin = Integer.parseInt(strings[1].trim()); - } catch (Exception e) { - Log.e("webDialog", e); - } - String code = strings[0]; - Settings.setWebNameCode(nam, code); - } - - webAuthHandler.sendEmptyMessage(pin); - } - }).start(); - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, final Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode != RESULT_OK) { - return; - } - - switch (requestCode) { - case SELECT_MAPFILE_REQUEST: - if (data.hasExtra(Intents.EXTRA_MAP_FILE)) { - final String mapFile = data.getStringExtra(Intents.EXTRA_MAP_FILE); - Settings.setMapFile(mapFile); - if (!Settings.isValidMapFile(Settings.getMapFile())) { - showToast(res.getString(R.string.warn_invalid_mapfile)); - } - } - updateMapSourceMenu(); - initMapDirectoryEdittext(true); - break; - case SELECT_GPX_EXPORT_REQUEST: - checkDirectory(resultCode, data, R.id.gpx_exportdir, new RunnableWithArgument<String>() { - - @Override - public void run(String directory) { - Settings.setGpxExportDir(directory); - } - }); - break; - case SELECT_GPX_IMPORT_REQUEST: - checkDirectory(resultCode, data, R.id.gpx_importdir, new RunnableWithArgument<String>() { - - @Override - public void run(String directory) { - Settings.setGpxImportDir(directory); - } - }); - break; - case SELECT_THEMEFOLDER_REQUEST: - checkDirectory(resultCode, data, R.id.themefolder, new RunnableWithArgument<String>() { - - @Override - public void run(String directory) { - Settings.setCustomRenderThemeBaseFolder(directory); - } - }); - break; - default: - throw new IllegalArgumentException(); - } - } - - private void checkDirectory(int resultCode, Intent data, int textField, RunnableWithArgument<String> runnableSetDir) { - if (resultCode != RESULT_OK) { - return; - } - final String directory = new File(data.getData().getPath()).getAbsolutePath(); - if (StringUtils.isNotBlank(directory)) { - runnableSetDir.run(directory); - EditText directoryText = (EditText) findViewById(textField); - directoryText.setText(directory); - directoryText.requestFocus(); - } - } - - private void selectDirectory(String startDirectory, int directoryKind) { - try { - final Intent dirChooser = new Intent(FileManagerIntents.ACTION_PICK_DIRECTORY); - if (StringUtils.isNotBlank(startDirectory)) { - dirChooser.setData(Uri.fromFile(new File(startDirectory))); - } - dirChooser.putExtra(FileManagerIntents.EXTRA_TITLE, res.getString(R.string.simple_dir_chooser_title)); - dirChooser.putExtra(FileManagerIntents.EXTRA_BUTTON_TEXT, res.getString(android.R.string.ok)); - startActivityForResult(dirChooser, directoryKind); - } catch (android.content.ActivityNotFoundException ex) { - // OI file manager not available - final Intent dirChooser = new Intent(this, SimpleDirChooser.class); - dirChooser.putExtra(Intents.EXTRA_START_DIR, startDirectory); - startActivityForResult(dirChooser, directoryKind); - } - } - - public static void startActivity(Context fromActivity) { - final Intent initIntent = new Intent(fromActivity, SettingsActivity.class); - fromActivity.startActivity(initIntent); - } - -} diff --git a/main/src/cgeo/geocaching/StaticMapsActivity.java b/main/src/cgeo/geocaching/StaticMapsActivity.java index 4658262..2268df9 100644 --- a/main/src/cgeo/geocaching/StaticMapsActivity.java +++ b/main/src/cgeo/geocaching/StaticMapsActivity.java @@ -33,7 +33,7 @@ public class StaticMapsActivity extends AbstractActivity { private static final String EXTRAS_GEOCODE = "geocode"; @Extra(EXTRAS_DOWNLOAD) boolean download = false; - @Extra(EXTRAS_WAYPOINT) Integer waypoint_id = null; + @Extra(EXTRAS_WAYPOINT) Integer waypointId = null; @Extra(EXTRAS_GEOCODE) String geocode = null; private final List<Bitmap> maps = new ArrayList<Bitmap>(); @@ -43,7 +43,7 @@ public class StaticMapsActivity extends AbstractActivity { private final Handler loadMapsHandler = new Handler() { @Override - public void handleMessage(Message msg) { + public void handleMessage(final Message msg) { if (waitDialog != null) { waitDialog.dismiss(); } @@ -92,7 +92,7 @@ public class StaticMapsActivity extends AbstractActivity { } @Override - public void onCreate(Bundle savedInstanceState) { + public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState, R.layout.staticmaps_activity); if (geocode == null) { @@ -107,12 +107,6 @@ public class StaticMapsActivity extends AbstractActivity { (new LoadMapsThread()).start(); } - @Override - public void onResume() { - super.onResume(); - - } - private class LoadMapsThread extends Thread { @Override @@ -120,11 +114,11 @@ public class StaticMapsActivity extends AbstractActivity { try { // try downloading 2 times for (int trials = 0; trials < 2; trials++) { - for (int level = 1; level <= 5; level++) { + for (int level = 1; level <= StaticMapsProvider.MAPS_LEVEL_MAX; level++) { try { - if (waypoint_id != null) { + if (waypointId != null) { final Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); - final Bitmap image = StaticMapsProvider.getWaypointMap(geocode, cache.getWaypointById(waypoint_id), level); + final Bitmap image = StaticMapsProvider.getWaypointMap(geocode, cache.getWaypointById(waypointId), level); if (image != null) { maps.add(image); } @@ -158,12 +152,12 @@ public class StaticMapsActivity extends AbstractActivity { private boolean downloadStaticMaps() { final Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); - if (waypoint_id == null) { + if (waypointId == null) { showToast(res.getString(R.string.info_storing_static_maps)); StaticMapsProvider.storeCacheStaticMap(cache, true); return cache.hasStaticMap(); } - final Waypoint waypoint = cache.getWaypointById(waypoint_id); + final Waypoint waypoint = cache.getWaypointById(waypointId); if (waypoint != null) { showToast(res.getString(R.string.info_storing_static_maps)); // refresh always removes old waypoint files @@ -178,7 +172,7 @@ public class StaticMapsActivity extends AbstractActivity { public static void startActivity(final Context activity, final String geocode, final boolean download, final Waypoint waypoint) { StaticMapsActivity_.IntentBuilder_ builder = StaticMapsActivity_.intent(activity).geocode(geocode).download(download); if (waypoint != null) { - builder.waypoint_id(waypoint.getId()); + builder.waypointId(waypoint.getId()); } builder.start(); } diff --git a/main/src/cgeo/geocaching/StaticMapsProvider.java b/main/src/cgeo/geocaching/StaticMapsProvider.java index 9a4c00b..2555b80 100644 --- a/main/src/cgeo/geocaching/StaticMapsProvider.java +++ b/main/src/cgeo/geocaching/StaticMapsProvider.java @@ -6,6 +6,7 @@ import cgeo.geocaching.files.LocalStorage; import cgeo.geocaching.geopoint.GeopointFormatter.Format; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; @@ -23,7 +24,8 @@ import android.view.WindowManager; import java.io.File; import java.util.concurrent.TimeUnit; -public class StaticMapsProvider { +public final class StaticMapsProvider { + static final int MAPS_LEVEL_MAX = 5; private static final String PREFIX_PREVIEW = "preview"; private static final String GOOGLE_STATICMAP_URL = "http://maps.google.com/maps/api/staticmap"; private static final String SATELLITE = "satellite"; @@ -31,16 +33,22 @@ public class StaticMapsProvider { private static final String WAYPOINT_PREFIX = "wp"; private static final String MAP_FILENAME_PREFIX = "map_"; private static final String MARKERS_URL = "http://status.cgeo.org/assets/markers/"; - /** We assume there is no real usable image with less than 1k */ + + /** We assume there is no real usable image with less than 1k. */ private static final int MIN_MAP_IMAGE_BYTES = 1000; + /** ThreadPool restricting this to 1 Thread. **/ - private static final BlockingThreadPool pool = new BlockingThreadPool(1, Thread.MIN_PRIORITY); + private static final BlockingThreadPool POOL = new BlockingThreadPool(1, Thread.MIN_PRIORITY); + + private StaticMapsProvider() { + // utility class + } - private static File getMapFile(final String geocode, String prefix, final boolean createDirs) { + private static File getMapFile(final String geocode, final String prefix, final boolean createDirs) { return LocalStorage.getStorageFile(geocode, MAP_FILENAME_PREFIX + prefix, false, createDirs); } - private static void downloadDifferentZooms(final String geocode, String markerUrl, String prefix, String latlonMap, int edge, final Parameters waypoints) { + private static void downloadDifferentZooms(final String geocode, final String markerUrl, final String prefix, final String latlonMap, final int edge, final Parameters waypoints) { downloadMap(geocode, 20, SATELLITE, markerUrl, prefix + '1', "", latlonMap, edge, edge, waypoints); downloadMap(geocode, 18, SATELLITE, markerUrl, prefix + '2', "", latlonMap, edge, edge, waypoints); downloadMap(geocode, 16, ROADMAP, markerUrl, prefix + '3', "", latlonMap, edge, edge, waypoints); @@ -48,7 +56,7 @@ public class StaticMapsProvider { downloadMap(geocode, 11, ROADMAP, markerUrl, prefix + '5', "", latlonMap, edge, edge, waypoints); } - private static void downloadMap(String geocode, int zoom, String mapType, String markerUrl, String prefix, String shadow, String latlonMap, int width, int height, final Parameters waypoints) { + private static void downloadMap(final String geocode, final int zoom, final String mapType, final String markerUrl, final String prefix, final String shadow, final String latlonMap, final int width, final int height, final Parameters waypoints) { final Parameters params = new Parameters( "center", latlonMap, "zoom", String.valueOf(zoom), @@ -79,7 +87,7 @@ public class StaticMapsProvider { } } - public static void downloadMaps(Geocache cache) { + public static void downloadMaps(final Geocache cache) { if ((!Settings.isStoreOfflineMaps() && !Settings.isStoreOfflineWpMaps()) || StringUtils.isBlank(cache.getGeocode())) { return; } @@ -109,19 +117,19 @@ public class StaticMapsProvider { * @param edge * The boundings */ - private static void refreshAllWpStaticMaps(Geocache cache, int edge) { + private static void refreshAllWpStaticMaps(final Geocache cache, final int edge) { LocalStorage.deleteFilesWithPrefix(cache.getGeocode(), MAP_FILENAME_PREFIX + WAYPOINT_PREFIX); for (Waypoint waypoint : cache.getWaypoints()) { storeWaypointStaticMap(cache.getGeocode(), edge, waypoint, false); } } - public static void storeWaypointStaticMap(Geocache cache, Waypoint waypoint, boolean waitForResult) { + public static void storeWaypointStaticMap(final Geocache cache, final Waypoint waypoint, final boolean waitForResult) { int edge = StaticMapsProvider.guessMaxDisplaySide(); storeWaypointStaticMap(cache.getGeocode(), edge, waypoint, waitForResult); } - private static void storeWaypointStaticMap(final String geocode, int edge, Waypoint waypoint, final boolean waitForResult) { + private static void storeWaypointStaticMap(final String geocode, final int edge, final Waypoint waypoint, final boolean waitForResult) { if (geocode == null) { Log.e("storeWaypointStaticMap - missing input parameter geocode"); return; @@ -141,7 +149,7 @@ public class StaticMapsProvider { } } - public static void storeCacheStaticMap(Geocache cache, final boolean waitForResult) { + public static void storeCacheStaticMap(final Geocache cache, final boolean waitForResult) { int edge = guessMaxDisplaySide(); storeCacheStaticMap(cache, edge, waitForResult); } @@ -178,7 +186,7 @@ public class StaticMapsProvider { } private static void downloadMaps(final String geocode, final String markerUrl, final String prefix, final String latlonMap, final int edge, - final Parameters waypoints, boolean waitForResult) { + final Parameters waypoints, final boolean waitForResult) { if (waitForResult) { downloadDifferentZooms(geocode, markerUrl, prefix, latlonMap, edge, waypoints); } @@ -190,7 +198,7 @@ public class StaticMapsProvider { } }; try { - pool.add(currentTask, 20, TimeUnit.SECONDS); + POOL.add(currentTask, 20, TimeUnit.SECONDS); } catch (InterruptedException e) { Log.e("StaticMapsProvider.downloadMaps error adding task", e); } @@ -214,13 +222,13 @@ public class StaticMapsProvider { return MARKERS_URL + "marker_waypoint_" + type + ".png"; } - public static void removeWpStaticMaps(Waypoint waypoint, final String geocode) { + public static void removeWpStaticMaps(final Waypoint waypoint, final String geocode) { if (waypoint == null) { return; } int waypointId = waypoint.getId(); int waypointMapHash = waypoint.getStaticMapsHashcode(); - for (int level = 1; level <= 5; level++) { + for (int level = 1; level <= MAPS_LEVEL_MAX; level++) { try { StaticMapsProvider.getMapFile(geocode, WAYPOINT_PREFIX + waypointId + "_" + waypointMapHash + '_' + level, false).delete(); } catch (Exception e) { @@ -243,7 +251,7 @@ public class StaticMapsProvider { if (StringUtils.isBlank(geocode)) { return false; } - for (int level = 1; level <= 5; level++) { + for (int level = 1; level <= MAPS_LEVEL_MAX; level++) { File mapFile = StaticMapsProvider.getMapFile(geocode, String.valueOf(level), false); if (mapFile.exists()) { return true; @@ -259,10 +267,10 @@ public class StaticMapsProvider { * @param waypoint * @return <code>true</code> if at least one map file exists; <code>false</code> otherwise */ - public static boolean hasStaticMapForWaypoint(String geocode, Waypoint waypoint) { + public static boolean hasStaticMapForWaypoint(final String geocode, final Waypoint waypoint) { int waypointId = waypoint.getId(); int waypointMapHash = waypoint.getStaticMapsHashcode(); - for (int level = 1; level <= 5; level++) { + for (int level = 1; level <= MAPS_LEVEL_MAX; level++) { File mapFile = StaticMapsProvider.getMapFile(geocode, WAYPOINT_PREFIX + waypointId + "_" + waypointMapHash + "_" + level, false); if (mapFile.exists()) { return true; @@ -278,10 +286,10 @@ public class StaticMapsProvider { * @param waypoint * @return <code>true</code> if all map files exist; <code>false</code> otherwise */ - public static boolean hasAllStaticMapsForWaypoint(String geocode, Waypoint waypoint) { + public static boolean hasAllStaticMapsForWaypoint(final String geocode, final Waypoint waypoint) { int waypointId = waypoint.getId(); int waypointMapHash = waypoint.getStaticMapsHashcode(); - for (int level = 1; level <= 5; level++) { + for (int level = 1; level <= MAPS_LEVEL_MAX; level++) { File mapFile = StaticMapsProvider.getMapFile(geocode, WAYPOINT_PREFIX + waypointId + "_" + waypointMapHash + "_" + level, false); boolean mapExists = mapFile.exists(); if (!mapExists) { @@ -295,13 +303,13 @@ public class StaticMapsProvider { return decodeFile(StaticMapsProvider.getMapFile(geocode, PREFIX_PREVIEW, false)); } - public static Bitmap getWaypointMap(final String geocode, Waypoint waypoint, int level) { + public static Bitmap getWaypointMap(final String geocode, final Waypoint waypoint, final int level) { int waypointId = waypoint.getId(); int waypointMapHash = waypoint.getStaticMapsHashcode(); return decodeFile(StaticMapsProvider.getMapFile(geocode, WAYPOINT_PREFIX + waypointId + "_" + waypointMapHash + "_" + level, false)); } - public static Bitmap getCacheMap(final String geocode, int level) { + public static Bitmap getCacheMap(final String geocode, final int level) { return decodeFile(StaticMapsProvider.getMapFile(geocode, String.valueOf(level), false)); } 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/TrackableActivity.java b/main/src/cgeo/geocaching/TrackableActivity.java index 77f7526..d1f323c 100644 --- a/main/src/cgeo/geocaching/TrackableActivity.java +++ b/main/src/cgeo/geocaching/TrackableActivity.java @@ -7,17 +7,18 @@ import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.AbstractViewPagerActivity; 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.ui.UserActionsClickListener; +import cgeo.geocaching.ui.logs.TrackableLogsViewCreator; import cgeo.geocaching.utils.HtmlUtils; import cgeo.geocaching.utils.Log; -import cgeo.geocaching.utils.TextUtils; import cgeo.geocaching.utils.UnknownTagsHandler; import org.apache.commons.lang3.StringUtils; @@ -32,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; @@ -61,12 +58,12 @@ 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 final Handler loadTrackableHandler = new Handler() { @@ -192,52 +189,6 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi } @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 - final RelativeLayout itemLayout = (RelativeLayout) view.getParent(); - final TextView itemName = (TextView) itemLayout.findViewById(R.id.name); - - final 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; @@ -281,30 +232,26 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi @Override public void run() { - trackable = cgData.loadTrackable(geocode); - - if (trackable == null || trackable.isLoggable()) { - final TrackableConnector trackableConnector = ConnectorFactory.getTrackableConnector(geocode); - trackable = trackableConnector.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 (final 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()); } } @@ -367,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(); } @@ -388,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()); - } - }); - } - - final TextView logView = holder.text; - logView.setMovementMethod(AnchorAwareLinkMovementMethod.getInstance()); - - String logText = log.log; - if (TextUtils.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); - } - - final ImageView statusMarker = holder.marker; - // colored marker - final 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; @@ -523,7 +378,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi 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 @@ -567,7 +422,7 @@ public class TrackableActivity extends AbstractViewPagerActivity<TrackableActivi } }); } else if (Trackable.SPOTTED_USER == trackable.getSpottedType()) { - spotted.setOnClickListener(new UserActionsListener()); + spotted.setOnClickListener(new UserActionsClickListener()); } } diff --git a/main/src/cgeo/geocaching/activity/AbstractActivity.java b/main/src/cgeo/geocaching/activity/AbstractActivity.java index 964ef96..9aca35e 100644 --- a/main/src/cgeo/geocaching/activity/AbstractActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractActivity.java @@ -2,7 +2,7 @@ package cgeo.geocaching.activity; import butterknife.Views; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.network.Cookies; @@ -83,33 +83,6 @@ public abstract class AbstractActivity extends FragmentActivity implements IAbst ActivityMixin.invalidateOptionsMenu(this); } - /** - * insert text into the EditText at the current cursor position - * - * @param editText - * @param insertText - * @param moveCursor - * place the cursor after the inserted text - */ - public static void insertAtPosition(final EditText editText, final String insertText, final boolean moveCursor) { - int selectionStart = editText.getSelectionStart(); - int selectionEnd = editText.getSelectionEnd(); - int start = Math.min(selectionStart, selectionEnd); - int end = Math.max(selectionStart, selectionEnd); - - final String content = editText.getText().toString(); - String completeText; - if (start > 0 && !Character.isWhitespace(content.charAt(start - 1))) { - completeText = " " + insertText; - } else { - completeText = insertText; - } - - editText.getText().replace(start, end, completeText); - int newCursor = moveCursor ? start + completeText.length() : start; - editText.setSelection(newCursor, newCursor); - } - protected void onCreate(final Bundle savedInstanceState, final int resourceLayoutID) { super.onCreate(savedInstanceState); diff --git a/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java b/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java index 0345633..952726e 100644 --- a/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractViewPagerActivity.java @@ -93,7 +93,7 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends @Override public void destroyItem(ViewGroup container, int position, Object object) { - ((ViewPager) container).removeView((View) object); + container.removeView((View) object); } @Override @@ -123,7 +123,7 @@ public abstract class AbstractViewPagerActivity<Page extends Enum<Page>> extends // Result from getView() is maybe cached, but it should be valid because the // creator should be informed about data-changes with notifyDataSetChanged() view = creator.getView(); - ((ViewPager) container).addView(view, 0); + container.addView(view, 0); } } catch (Exception e) { Log.e("ViewPagerAdapter.instantiateItem ", e); diff --git a/main/src/cgeo/geocaching/activity/ActivityMixin.java b/main/src/cgeo/geocaching/activity/ActivityMixin.java index 12ab0be..699d5ab 100644 --- a/main/src/cgeo/geocaching/activity/ActivityMixin.java +++ b/main/src/cgeo/geocaching/activity/ActivityMixin.java @@ -2,7 +2,7 @@ package cgeo.geocaching.activity; import cgeo.geocaching.MainActivity; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.compatibility.Compatibility; import org.apache.commons.lang3.StringUtils; @@ -16,6 +16,7 @@ import android.os.Build; import android.view.Gravity; import android.view.View; import android.view.WindowManager; +import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; @@ -79,6 +80,10 @@ public final class ActivityMixin { return R.style.popup_dark; } + public static void showToast(final Activity activity, final int resId) { + ActivityMixin.showToast(activity, activity.getString(resId)); + } + public static void showToast(final Activity activity, final String text) { if (StringUtils.isNotBlank(text)) { Toast toast = Toast.makeText(activity, text, Toast.LENGTH_LONG); @@ -130,4 +135,31 @@ public final class ActivityMixin { public static void invalidateOptionsMenu(Activity activity) { Compatibility.invalidateOptionsMenu(activity); } + + /** + * insert text into the EditText at the current cursor position + * + * @param editText + * @param insertText + * @param moveCursor + * place the cursor after the inserted text + */ + public static void insertAtPosition(final EditText editText, final String insertText, final boolean moveCursor) { + int selectionStart = editText.getSelectionStart(); + int selectionEnd = editText.getSelectionEnd(); + int start = Math.min(selectionStart, selectionEnd); + int end = Math.max(selectionStart, selectionEnd); + + final String content = editText.getText().toString(); + String completeText; + if (start > 0 && !Character.isWhitespace(content.charAt(start - 1))) { + completeText = " " + insertText; + } else { + completeText = insertText; + } + + editText.getText().replace(start, end, completeText); + int newCursor = moveCursor ? start + completeText.length() : start; + editText.setSelection(newCursor, newCursor); + } } diff --git a/main/src/cgeo/geocaching/apps/AbstractApp.java b/main/src/cgeo/geocaching/apps/AbstractApp.java index ef56f87..7b6b3d9 100644 --- a/main/src/cgeo/geocaching/apps/AbstractApp.java +++ b/main/src/cgeo/geocaching/apps/AbstractApp.java @@ -1,10 +1,11 @@ package cgeo.geocaching.apps; import cgeo.geocaching.Geocache; -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,10 +27,10 @@ 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); + return ProcessUtils.isIntentAvailable(intent); } protected Intent getLaunchIntent() { 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..4db889d 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java @@ -1,8 +1,8 @@ package cgeo.geocaching.apps.cache.navi; import cgeo.geocaching.Geocache; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; import cgeo.geocaching.Waypoint; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.activity.ActivityMixin; @@ -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..87710fb 100644 --- a/main/src/cgeo/geocaching/cgData.java +++ b/main/src/cgeo/geocaching/cgData.java @@ -1,5 +1,7 @@ package cgeo.geocaching; +import cgeo.geocaching.connector.IConnector; +import cgeo.geocaching.connector.gc.Tile; import cgeo.geocaching.enumerations.CacheSize; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LoadFlags; @@ -11,6 +13,7 @@ import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.files.LocalStorage; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import org.apache.commons.collections.CollectionUtils; @@ -59,18 +62,52 @@ public class cgData { DATABASE, } - /** The list of fields needed for mapping. */ - private static final String[] CACHE_COLUMNS = new String[] { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - "updated", "reason", "detailed", "detailedupdate", "visiteddate", "geocode", "cacheid", "guid", "type", "name", "owner", "owner_real", "hidden", "hint", "size", - // 15 16 17 18 19 20 21 22 23 - "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" - // reason is replaced by listId in Geocache - }; + // Columns and indices for the cache data + private static final String QUERY_CACHE_DATA = + "SELECT " + + "cg_caches.updated," + // 0 + "cg_caches.reason," + // 1 + "cg_caches.detailed," + // 2 + "cg_caches.detailedupdate," + // 3 + "cg_caches.visiteddate," + // 4 + "cg_caches.geocode," + // 5 + "cg_caches.cacheid," + // 6 + "cg_caches.guid," + // 7 + "cg_caches.type," + // 8 + "cg_caches.name," + // 9 + "cg_caches.owner," + // 10 + "cg_caches.owner_real," + // 11 + "cg_caches.hidden," + // 12 + "cg_caches.hint," + // 13 + "cg_caches.size," + // 14 + "cg_caches.difficulty," + // 15 + "cg_caches.direction," + // 16 + "cg_caches.distance," + // 17 + "cg_caches.terrain," + // 18 + "cg_caches.latlon," + // 19 + "cg_caches.location," + // 20 + "cg_caches.personal_note," + // 21 + "cg_caches.shortdesc," + // 22 + "cg_caches.favourite_cnt," + // 23 + "cg_caches.rating," + // 24 + "cg_caches.votes," + // 25 + "cg_caches.myvote," + // 26 + "cg_caches.disabled," + // 27 + "cg_caches.archived," + // 28 + "cg_caches.members," + // 29 + "cg_caches.found," + // 30 + "cg_caches.favourite," + // 31 + "cg_caches.inventoryunknown," + // 32 + "cg_caches.onWatchlist," + // 33 + "cg_caches.reliable_latlon," + // 34 + "cg_caches.coordsChanged," + // 35 + "cg_caches.latitude," + // 36 + "cg_caches.longitude," + // 37 + "cg_caches.finalDefined," + // 38 + "cg_caches._id," + // 39 + "cg_caches.inventorycoins," + // 40 + "cg_caches.inventorytags," + // 41 + "cg_caches.logPasswordRequired"; // 42 //TODO: remove "latlon" field from cache table @@ -83,10 +120,9 @@ public class cgData { /** * holds the column indexes of the cache table to avoid lookups */ - 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"; @@ -127,7 +163,6 @@ public class cgData { + "latitude double, " + "longitude double, " + "reliable_latlon integer, " - + "elevation double, " + "personal_note text, " + "shortdesc text, " + "description text, " @@ -145,7 +180,8 @@ public class cgData { + "inventoryunknown integer default 0, " + "onWatchlist integer default 0, " + "coordsChanged integer default 0, " - + "finalDefined integer default 0" + + "finalDefined integer default 0, " + + "logPasswordRequired integer default 0" + "); "; private static final String dbCreateLists = "" + "create table " + dbTableLists + " (" @@ -542,7 +578,6 @@ public class cgData { + "latitude double, " + "longitude double, " + "reliable_latlon integer, " - + "elevation double, " + "personal_note text, " + "shortdesc text, " + "description text, " @@ -563,7 +598,7 @@ public class cgData { db.execSQL(dbCreateCachesTemp); db.execSQL("insert into " + dbTableCachesTemp + " select _id,updated,detailed,detailedupdate,visiteddate,geocode,reason,cacheid,guid,type,name,own,owner,owner_real," + - "hidden,hint,size,difficulty,terrain,latlon,location,direction,distance,latitude,longitude, 0,elevation," + + "hidden,hint,size,difficulty,terrain,latlon,location,direction,distance,latitude,longitude, 0," + "personal_note,shortdesc,description,favourite_cnt,rating,votes,myvote,disabled,archived,members,found,favourite,inventorycoins," + "inventorytags,inventoryunknown,onWatchlist from " + dbTableCaches); db.execSQL("drop table " + dbTableCaches); @@ -685,6 +720,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(); @@ -1035,7 +1079,6 @@ public class cgData { values.put("direction", cache.getDirection()); putCoords(values, cache.getCoords()); values.put("reliable_latlon", cache.isReliableLatLon() ? 1 : 0); - values.put("elevation", cache.getElevation()); values.put("shortdesc", cache.getShortDescription()); values.put("personal_note", cache.getPersonalNote()); values.put("description", cache.getDescription()); @@ -1052,13 +1095,13 @@ 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(); //try to update record else insert fresh.. database.beginTransaction(); - boolean result = false; try { saveAttributesWithoutTransaction(cache); saveOriginalWaypointsWithoutTransaction(cache); @@ -1074,14 +1117,14 @@ public class cgData { database.insert(dbTableCaches, null, values); } database.setTransactionSuccessful(); - result = true; + return true; } catch (Exception e) { Log.e("SaveCache", e); } finally { database.endTransaction(); } - return result; + return false; } private static void saveAttributesWithoutTransaction(final Geocache cache) { @@ -1128,17 +1171,16 @@ public class cgData { init(); database.beginTransaction(); - boolean result = false; try { saveOriginalWaypointsWithoutTransaction(cache); database.setTransactionSuccessful(); - result = true; + return true; } catch (Exception e) { Log.e("saveWaypoints", e); } finally { database.endTransaction(); } - return result; + return false; } private static void saveOriginalWaypointsWithoutTransaction(final Geocache cache) { @@ -1332,7 +1374,7 @@ public class cgData { } } - public static boolean saveTrackable(final Trackable trackable) { + public static void saveTrackable(final Trackable trackable) { init(); database.beginTransaction(); @@ -1342,8 +1384,6 @@ public class cgData { } finally { database.endTransaction(); } - - return true; } private static void saveInventoryWithoutTransaction(final String geocode, final List<Trackable> trackables) { @@ -1478,10 +1518,7 @@ public class cgData { // do not log the entire collection of geo codes to the debug log. This can be more than 100 KB of text for large lists! init(); - final StringBuilder query = new StringBuilder("SELECT "); - for (int i = 0; i < CACHE_COLUMNS.length; i++) { - query.append(i > 0 ? ", " : "").append(dbTableCaches).append('.').append(CACHE_COLUMNS[i]).append(' '); - } + final StringBuilder query = new StringBuilder(QUERY_CACHE_DATA); if (loadFlags.contains(LoadFlag.LOAD_OFFLINE_LOG)) { query.append(',').append(dbTableLogsOffline).append(".log"); } @@ -1578,71 +1615,58 @@ public class cgData { private static Geocache createCacheFromDatabaseContent(Cursor cursor) { Geocache cache = new Geocache(); - if (cacheColumnIndex == null) { - final int[] local_cci = new int[CACHE_COLUMNS.length]; // use a local variable to avoid having the not yet fully initialized array be visible to other threads - for (int i = 0; i < CACHE_COLUMNS.length; i++) { - local_cci[i] = cursor.getColumnIndex(CACHE_COLUMNS[i]); - } - cacheColumnIndex = local_cci; - } - - cache.setUpdated(cursor.getLong(cacheColumnIndex[0])); - cache.setListId(cursor.getInt(cacheColumnIndex[1])); - cache.setDetailed(cursor.getInt(cacheColumnIndex[2]) == 1); - cache.setDetailedUpdate(cursor.getLong(cacheColumnIndex[3])); - cache.setVisitedDate(cursor.getLong(cacheColumnIndex[4])); - cache.setGeocode(cursor.getString(cacheColumnIndex[5])); - cache.setCacheId(cursor.getString(cacheColumnIndex[6])); - cache.setGuid(cursor.getString(cacheColumnIndex[7])); - cache.setType(CacheType.getById(cursor.getString(cacheColumnIndex[8]))); - cache.setName(cursor.getString(cacheColumnIndex[9])); - cache.setOwnerDisplayName(cursor.getString(cacheColumnIndex[10])); - cache.setOwnerUserId(cursor.getString(cacheColumnIndex[11])); - long dateValue = cursor.getLong(cacheColumnIndex[12]); + cache.setUpdated(cursor.getLong(0)); + cache.setListId(cursor.getInt(1)); + cache.setDetailed(cursor.getInt(2) == 1); + cache.setDetailedUpdate(cursor.getLong(3)); + cache.setVisitedDate(cursor.getLong(4)); + cache.setGeocode(cursor.getString(5)); + cache.setCacheId(cursor.getString(6)); + cache.setGuid(cursor.getString(7)); + cache.setType(CacheType.getById(cursor.getString(8))); + cache.setName(cursor.getString(9)); + cache.setOwnerDisplayName(cursor.getString(10)); + cache.setOwnerUserId(cursor.getString(11)); + long dateValue = cursor.getLong(12); if (dateValue != 0) { cache.setHidden(new Date(dateValue)); } // do not set cache.hint - cache.setSize(CacheSize.getById(cursor.getString(cacheColumnIndex[14]))); - cache.setDifficulty(cursor.getFloat(cacheColumnIndex[15])); - int index = cacheColumnIndex[16]; + cache.setSize(CacheSize.getById(cursor.getString(14))); + cache.setDifficulty(cursor.getFloat(15)); + int index = 16; if (cursor.isNull(index)) { cache.setDirection(null); } else { cache.setDirection(cursor.getFloat(index)); } - index = cacheColumnIndex[17]; + index = 17; if (cursor.isNull(index)) { cache.setDistance(null); } else { cache.setDistance(cursor.getFloat(index)); } - cache.setTerrain(cursor.getFloat(cacheColumnIndex[18])); + cache.setTerrain(cursor.getFloat(18)); // do not set cache.location - cache.setCoords(getCoords(cursor, cacheColumnIndex[37], cacheColumnIndex[38])); - index = cacheColumnIndex[21]; - if (cursor.isNull(index)) { - cache.setElevation(null); - } else { - cache.setElevation(cursor.getDouble(index)); - } - cache.setPersonalNote(cursor.getString(cacheColumnIndex[22])); + cache.setCoords(getCoords(cursor, 36, 37)); + cache.setPersonalNote(cursor.getString(21)); // do not set cache.shortdesc // do not set cache.description - cache.setFavoritePoints(cursor.getInt(cacheColumnIndex[24])); - cache.setRating(cursor.getFloat(cacheColumnIndex[25])); - cache.setVotes(cursor.getInt(cacheColumnIndex[26])); - cache.setMyVote(cursor.getFloat(cacheColumnIndex[27])); - cache.setDisabled(cursor.getInt(cacheColumnIndex[28]) == 1); - cache.setArchived(cursor.getInt(cacheColumnIndex[29]) == 1); - cache.setPremiumMembersOnly(cursor.getInt(cacheColumnIndex[30]) == 1); - cache.setFound(cursor.getInt(cacheColumnIndex[31]) == 1); - cache.setFavorite(cursor.getInt(cacheColumnIndex[32]) == 1); - cache.setInventoryItems(cursor.getInt(cacheColumnIndex[33])); - cache.setOnWatchlist(cursor.getInt(cacheColumnIndex[34]) == 1); - cache.setReliableLatLon(cursor.getInt(cacheColumnIndex[35]) > 0); - cache.setUserModifiedCoords(cursor.getInt(cacheColumnIndex[36]) > 0); - cache.setFinalDefined(cursor.getInt(cacheColumnIndex[39]) > 0); + cache.setFavoritePoints(cursor.getInt(23)); + cache.setRating(cursor.getFloat(24)); + cache.setVotes(cursor.getInt(25)); + cache.setMyVote(cursor.getFloat(26)); + cache.setDisabled(cursor.getInt(27) == 1); + cache.setArchived(cursor.getInt(28) == 1); + cache.setPremiumMembersOnly(cursor.getInt(29) == 1); + cache.setFound(cursor.getInt(30) == 1); + cache.setFavorite(cursor.getInt(31) == 1); + cache.setInventoryItems(cursor.getInt(32)); + cache.setOnWatchlist(cursor.getInt(33) == 1); + cache.setReliableLatLon(cursor.getInt(34) > 0); + cache.setUserModifiedCoords(cursor.getInt(35) > 0); + cache.setFinalDefined(cursor.getInt(38) > 0); + cache.setLogPasswordRequired(cursor.getInt(42) > 0); Log.d("Loading " + cache.toString() + " (" + cache.getListId() + ") from DB"); @@ -1814,18 +1838,17 @@ public class cgData { init(); database.beginTransaction(); - boolean success = true; try { database.delete(dbTableSearchDestionationHistory, null, null); database.setTransactionSuccessful(); + return true; } catch (Exception e) { - success = false; Log.e("Unable to clear searched destinations", e); } finally { database.endTransaction(); } - return success; + return false; } public static List<LogEntry> loadLogs(String geocode) { @@ -2665,18 +2688,17 @@ public class cgData { init(); database.beginTransaction(); - boolean result = false; try { database.delete(dbTableSearchDestionationHistory, "_id = " + destination.getId(), null); database.setTransactionSuccessful(); - result = true; + return true; } catch (Exception e) { Log.e("Unable to remove searched destination", e); } finally { database.endTransaction(); } - return result; + return false; } /** @@ -2963,4 +2985,37 @@ public class cgData { return false; } + public static Set<String> getCachedMissingFromSearch(final SearchResult searchResult, final Set<Tile> tiles, final IConnector connector, final int maxZoom) { + + // get cached cgeocaches + final Set<String> cachedGeocodes = new HashSet<String>(); + for (Tile tile : tiles) { + cachedGeocodes.addAll(cacheCache.getInViewport(tile.getViewport(), CacheType.ALL)); + } + // remove found in search result + cachedGeocodes.removeAll(searchResult.getGeocodes()); + + // check remaining against viewports + Set<String> missingFromSearch = new HashSet<String>(); + for (String geocode : cachedGeocodes) { + if (connector.canHandle(geocode)) { + Geocache geocache = cacheCache.getCacheFromCache(geocode); + if (geocache.getZoomLevel() <= maxZoom) { + boolean found = false; + for (Tile tile : tiles) { + if (tile.containsPoint(geocache)) { + found = true; + break; + } + } + if (found) { + missingFromSearch.add(geocode); + } + } + } + } + + return missingFromSearch; + } + } diff --git a/main/src/cgeo/geocaching/cgeoapplication.java b/main/src/cgeo/geocaching/cgeoapplication.java index b8f63ee..b227939 100644 --- a/main/src/cgeo/geocaching/cgeoapplication.java +++ b/main/src/cgeo/geocaching/cgeoapplication.java @@ -170,6 +170,10 @@ public class cgeoapplication extends Application { return dir; } + public Float currentDirection() { + return currentDirObject().getMemory(); + } + public StatusUpdater getStatusUpdater() { return statusUpdater; } diff --git a/main/src/cgeo/geocaching/cgeocaches.java b/main/src/cgeo/geocaching/cgeocaches.java index 40dddd4..5d7841d 100644 --- a/main/src/cgeo/geocaching/cgeocaches.java +++ b/main/src/cgeo/geocaching/cgeocaches.java @@ -31,10 +31,9 @@ import cgeo.geocaching.maps.CGeoMap; import cgeo.geocaching.network.Cookies; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.sorting.CacheComparator; import cgeo.geocaching.sorting.ComparatorUserInterface; -import cgeo.geocaching.sorting.EventDateComparator; -import cgeo.geocaching.sorting.VisitComparator; import cgeo.geocaching.ui.CacheListAdapter; import cgeo.geocaching.ui.LoggingUI; import cgeo.geocaching.ui.WeakReferenceHandler; @@ -166,18 +165,17 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity */ private MenuItem navigationMenu; + // FIXME: This method has mostly been replaced by the loaders. But it still contains a license agreement check. public void handleCachesLoaded() { try { setAdapter(); updateTitle(); - setDateComparatorForEventList(); - 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 +196,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 +209,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 +223,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 +275,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 +290,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 +301,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 +338,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 +373,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 +389,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 +411,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 +476,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 +502,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 +537,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,9 +554,9 @@ 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); - subMenu.add(0, MENU_REMOVE_FROM_HISTORY, 0, res.getString(R.string.cache_clear_history)); // remove from history + final SubMenu subMenu = menu.addSubMenu(0, SUBMENU_MANAGE_HISTORY, 0, res.getString(R.string.caches_manage)).setIcon(R.drawable.ic_menu_save); subMenu.add(0, MENU_EXPORT, 0, res.getString(R.string.export)); // export caches + subMenu.add(0, MENU_REMOVE_FROM_HISTORY, 0, res.getString(R.string.cache_clear_history)); // remove from history subMenu.add(0, MENU_CLEAR_OFFLINE_LOGS, 0, res.getString(R.string.caches_clear_offlinelogs)); menu.add(0, MENU_REFRESH_STORED, 0, res.getString(R.string.cache_offline_refresh)).setIcon(R.drawable.ic_menu_set_as); } else { @@ -579,7 +567,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)); @@ -636,12 +624,14 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity if (type == CacheListType.OFFLINE || type == CacheListType.HISTORY) { // only offline list setMenuItemLabel(menu, MENU_DROP_CACHES, R.string.caches_drop_selected, R.string.caches_drop_all); - menu.findItem(MENU_DROP_CACHES_AND_LIST).setVisible(!hasSelection && isNonDefaultList && !adapter.isFiltered()); setMenuItemLabel(menu, MENU_REFRESH_STORED, R.string.caches_refresh_selected, R.string.caches_refresh_all); setMenuItemLabel(menu, MENU_MOVE_TO_LIST, R.string.caches_move_selected, R.string.caches_move_all); } else { // search and global list (all other than offline and history) setMenuItemLabel(menu, MENU_REFRESH_STORED, R.string.caches_store_selected, R.string.caches_store_offline); } + if (type == CacheListType.OFFLINE) { + menu.findItem(MENU_DROP_CACHES_AND_LIST).setVisible(!hasSelection && isNonDefaultList && !adapter.isFiltered()); + } MenuItem item = menu.findItem(MENU_DROP_LIST); if (item != null) { @@ -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); } @@ -917,10 +907,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity NavigationAppFactory.showNavigationMenu(this, cache, null, null); break; case MENU_CACHE_DETAILS: - final Intent cachesIntent = new Intent(this, CacheDetailActivity.class); - cachesIntent.putExtra(Intents.EXTRA_GEOCODE, cache.getGeocode()); - cachesIntent.putExtra(Intents.EXTRA_NAME, cache.getName()); - startActivity(cachesIntent); + CacheDetailActivity.startActivity(this, cache.getGeocode(), cache.getName()); break; case MENU_DROP_CACHE: cache.drop(new Handler() { @@ -1106,7 +1093,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 +1111,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 +1130,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } }); - AlertDialog alert = dialog.create(); + final AlertDialog alert = dialog.create(); alert.show(); } @@ -1153,7 +1140,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 +1156,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 +1181,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } }); - AlertDialog alert = dialog.create(); + final AlertDialog alert = dialog.create(); alert.show(); } @@ -1232,7 +1219,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 +1231,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } } - for (Geocache cache : cachesWithStaticMaps) { + for (final Geocache cache : cachesWithStaticMaps) { if (!refreshCache(cache)) { break; } @@ -1276,7 +1263,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 +1278,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 +1321,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 +1361,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 +1463,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 +1552,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()); } @@ -1633,32 +1620,6 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity } } - /** - * set date comparator for pure event lists - * - * TODO: move this method into the adapter - */ - private void setDateComparatorForEventList() { - if (CollectionUtils.isNotEmpty(cacheList)) { - boolean eventsOnly = true; - for (Geocache cache : cacheList) { - if (!cache.isEventCache()) { - eventsOnly = false; - break; - } - } - if (eventsOnly) { - adapter.setComparator(new EventDateComparator()); - } - else if (type == CacheListType.HISTORY) { - adapter.setComparator(new VisitComparator()); - } - else if (adapter.getCacheComparator() != null && adapter.getCacheComparator() instanceof EventDateComparator) { - adapter.setComparator(null); - } - } - } - public static void startActivityNearest(final AbstractActivity context, final Geopoint coordsNow) { if (!isValidCoords(context, coordsNow)) { return; @@ -1726,7 +1687,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: @@ -1786,7 +1747,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity loader = new OwnerGeocacheListLoader(app, ownerName); break; case MAP: - //TODO Build Nullloader + //TODO Build Null loader title = res.getString(R.string.map_map); search = (SearchResult) extras.get(Intents.EXTRA_SEARCH); replaceCacheListFromSearch(); @@ -1820,6 +1781,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity cacheList.addAll(cachesFromSearchResult); search = searchIn; adapter.reFilter(); + adapter.setInitialComparator(); adapter.forceSort(); adapter.notifyDataSetChanged(); updateTitle(); @@ -1831,7 +1793,7 @@ public class cgeocaches extends AbstractListActivity implements FilteredActivity @Override public void onLoaderReset(Loader<SearchResult> arg0) { - //Not interessting + //Not interesting } } diff --git a/main/src/cgeo/geocaching/compatibility/AndroidLevel13.java b/main/src/cgeo/geocaching/compatibility/AndroidLevel13.java index 4eac205..f08ac22 100644 --- a/main/src/cgeo/geocaching/compatibility/AndroidLevel13.java +++ b/main/src/cgeo/geocaching/compatibility/AndroidLevel13.java @@ -23,9 +23,4 @@ public class AndroidLevel13 implements AndroidLevel13Interface { return dimensions; } - @Override - public int getDisplayHeight() { - return getDisplaySize().y; - } - } diff --git a/main/src/cgeo/geocaching/compatibility/AndroidLevel13Emulation.java b/main/src/cgeo/geocaching/compatibility/AndroidLevel13Emulation.java index 2257d83..ded20cb 100644 --- a/main/src/cgeo/geocaching/compatibility/AndroidLevel13Emulation.java +++ b/main/src/cgeo/geocaching/compatibility/AndroidLevel13Emulation.java @@ -16,11 +16,6 @@ public class AndroidLevel13Emulation implements AndroidLevel13Interface { } @Override - public int getDisplayHeight() { - return getDisplay().getHeight(); - } - - @Override public Point getDisplaySize() { final Display display = getDisplay(); return new Point(display.getWidth(), display.getHeight()); diff --git a/main/src/cgeo/geocaching/compatibility/AndroidLevel13Interface.java b/main/src/cgeo/geocaching/compatibility/AndroidLevel13Interface.java index f4e1975..8483e38 100644 --- a/main/src/cgeo/geocaching/compatibility/AndroidLevel13Interface.java +++ b/main/src/cgeo/geocaching/compatibility/AndroidLevel13Interface.java @@ -5,7 +5,5 @@ import android.graphics.Point; public interface AndroidLevel13Interface { int getDisplayWidth(); - int getDisplayHeight(); - Point getDisplaySize(); } diff --git a/main/src/cgeo/geocaching/compatibility/Compatibility.java b/main/src/cgeo/geocaching/compatibility/Compatibility.java index d846bda..48454ef 100644 --- a/main/src/cgeo/geocaching/compatibility/Compatibility.java +++ b/main/src/cgeo/geocaching/compatibility/Compatibility.java @@ -102,10 +102,6 @@ public final class Compatibility { return level13.getDisplayWidth(); } - public static int getDisplayHeight() { - return level13.getDisplayHeight(); - } - public static Point getDisplaySize() { return level13.getDisplaySize(); } diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java index 83c1b6f..28ad12b 100644 --- a/main/src/cgeo/geocaching/connector/AbstractConnector.java +++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java @@ -31,30 +31,22 @@ public abstract class AbstractConnector implements IConnector { } @Override - public boolean supportsOwnCoordinates() { + public boolean supportsPersonalNote() { return false; } - /** - * Uploading modified coordinates to website - * - * @param cache - * @param wpt - * @return success - */ @Override - public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) { + public boolean uploadPersonalNote(Geocache cache) { throw new UnsupportedOperationException(); } - /** - * Uploading personal note to website - * - * @param cache - * @return success - */ @Override - public boolean uploadPersonalNote(Geocache cache) { + public boolean supportsOwnCoordinates() { + return false; + } + + @Override + public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) { throw new UnsupportedOperationException(); } @@ -122,11 +114,6 @@ public abstract class AbstractConnector implements IConnector { } @Override - public String[] getTokens() { - return null; - } - - @Override public String getGeocodeFromUrl(final String url) { final String urlPrefix = getCacheUrlPrefix(); if (StringUtils.startsWith(url, urlPrefix)) { diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java index 1849e4b..eb09978 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; @@ -25,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"), @@ -46,8 +47,8 @@ public final class ConnectorFactory { public static final UnknownTrackableConnector UNKNOWN_TRACKABLE_CONNECTOR = new UnknownTrackableConnector(); private static final TrackableConnector[] TRACKABLE_CONNECTORS = new TrackableConnector[] { - new TravelBugConnector(), - new GeokretyConnector(), + new GeokretyConnector(), // GK must be first, as it overlaps with the secret codes of travel bugs + TravelBugConnector.getInstance(), UNKNOWN_TRACKABLE_CONNECTOR // must be last }; @@ -57,7 +58,7 @@ public final class ConnectorFactory { static { final List<ISearchByViewPort> vpConns = new ArrayList<ISearchByViewPort>(); - for (final IConnector conn : connectors) { + for (final IConnector conn : CONNECTORS) { if (conn instanceof ISearchByViewPort) { vpConns.add((ISearchByViewPort) conn); } @@ -65,7 +66,7 @@ public final class ConnectorFactory { searchByViewPortConns = vpConns.toArray(new ISearchByViewPort[vpConns.size()]); final List<ISearchByCenter> centerConns = new ArrayList<ISearchByCenter>(); - for (final IConnector conn : connectors) { + for (final IConnector conn : CONNECTORS) { // GCConnector is handled specially, omit it here! if (conn instanceof ISearchByCenter && !(conn instanceof GCConnector)) { centerConns.add((ISearchByCenter) conn); @@ -75,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 (final IConnector connector : connectors) { + for (final IConnector connector : CONNECTORS) { if (connector.canHandle(geocode)) { return true; } @@ -117,7 +128,7 @@ public final class ConnectorFactory { if (isInvalidGeocode(geocode)) { return UNKNOWN_CONNECTOR; } - for (final IConnector connector : connectors) { + for (final IConnector connector : CONNECTORS) { if (connector.canHandle(geocode)) { return connector; } @@ -136,17 +147,14 @@ public final class ConnectorFactory { final SearchResult result = new SearchResult(); for (final ISearchByViewPort vpconn : searchByViewPortConns) { if (vpconn.isActivated()) { - final SearchResult temp = vpconn.searchByViewport(viewport, tokens); - if (temp != null) { - result.addGeocodes(temp.getGeocodes()); - } + result.addSearchResult(vpconn.searchByViewport(viewport, tokens)); } } return result; } public static String getGeocodeFromURL(final String url) { - for (final IConnector connector : connectors) { + for (final IConnector connector : CONNECTORS) { final String geocode = connector.getGeocodeFromUrl(url); if (StringUtils.isNotBlank(geocode)) { return geocode; @@ -155,4 +163,8 @@ public final class ConnectorFactory { return null; } + public static TrackableConnector[] getTrackableConnectors() { + return TRACKABLE_CONNECTORS; + } + } diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java index c44b946..0c175cd 100644 --- a/main/src/cgeo/geocaching/connector/IConnector.java +++ b/main/src/cgeo/geocaching/connector/IConnector.java @@ -129,13 +129,6 @@ public interface IConnector { public boolean isReliableLatLon(boolean cacheHasReliableLatLon); /** - * Return required tokens for specific following actions - * - * @return - */ - public String[] getTokens(); - - /** * extract a geocode from the given URL, if this connector can handle that URL somehow * * @param url @@ -144,11 +137,11 @@ public interface IConnector { public String getGeocodeFromUrl(final String url); /** - * enable/disable uploading modified coordinates to website + * enable/disable uploading personal note * * @return true, when uploading is possible */ - public boolean supportsOwnCoordinates(); + public boolean supportsPersonalNote(); /** * Uploading personal note to website @@ -159,7 +152,14 @@ public interface IConnector { public boolean uploadPersonalNote(Geocache cache); /** - * Reseting of modified coordinates on website to details + * enable/disable uploading modified coordinates to website + * + * @return true, when uploading is possible + */ + public boolean supportsOwnCoordinates(); + + /** + * Resetting of modified coordinates on website to details * * @param cache * @return success 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 fab4332..e6fff79 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -4,16 +4,19 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; 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; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.settings.SettingsActivity; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; @@ -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 @@ -33,7 +38,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, * Pocket queries downloaded from the website use a numeric prefix. The pocket query creator Android app adds a * verbatim "pocketquery" prefix. */ - private static final Pattern gpxZipFilePattern = Pattern.compile("((\\d{7,})|(pocketquery))" + "(_.+)?" + "\\.zip", Pattern.CASE_INSENSITIVE); + private static final Pattern GPX_ZIP_FILE_PATTERN = Pattern.compile("((\\d{7,})|(pocketquery))" + "(_.+)?" + "\\.zip", Pattern.CASE_INSENSITIVE); /** * Pattern for GC codes @@ -74,6 +79,11 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override + public boolean supportsPersonalNote() { + return true; + } + + @Override public boolean supportsOwnCoordinates() { return true; } @@ -105,7 +115,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, @Override public String getName() { - return "GeoCaching.com"; + return "geocaching.com"; } @Override @@ -161,7 +171,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, @Override public boolean isZippedGPXFile(final String fileName) { - return gpxZipFilePattern.matcher(fileName).matches(); + return GPX_ZIP_FILE_PATTERN.matcher(fileName).matches(); } @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.jumpToServicesPage(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 f2e2e69..c2aeffd 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConstants.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConstants.java @@ -36,7 +36,7 @@ public final class GCConstants { public final static Pattern PATTERN_LATLON = Pattern.compile("<span id=\"uxLatLon\"[^>]*>(.*?)</span>"); public final static Pattern PATTERN_LATLON_ORIG = Pattern.compile("\\{\"isUserDefined\":true[^}]+?\"oldLatLngDisplay\":\"([^\"]+)\"\\}"); public final static Pattern PATTERN_LOCATION = Pattern.compile(Pattern.quote("<span id=\"ctl00_ContentBody_Location\">In ") + "(?:<a href=[^>]*>)?(.*?)<"); - public final static Pattern PATTERN_PERSONALNOTE = Pattern.compile("<p id=\"cache_note\"[^>]*>(.*?)</p>"); + public final static Pattern PATTERN_PERSONALNOTE = Pattern.compile("<p id=\"cache_note\"[^>]*>(.*?)</p>", Pattern.DOTALL); public final static Pattern PATTERN_NAME = Pattern.compile("<span id=\"ctl00_ContentBody_CacheName\">(.*?)</span>"); public final static Pattern PATTERN_DIFFICULTY = Pattern.compile("<span id=\"ctl00_ContentBody_uxLegendScale\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\""); public final static Pattern PATTERN_TERRAIN = Pattern.compile("<span id=\"ctl00_ContentBody_Localize[\\d]+\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\""); @@ -69,7 +69,7 @@ public final class GCConstants { public static final String MEMBER_STATUS_RENEW = "<a id=\"ctl00_hlRenew"; public static final String MEMBER_STATUS_PM = "Premium Member"; /** Use replaceAll("[,.]","") on the resulting string before converting to an int */ - public static final Pattern PATTERN_CACHES_FOUND = Pattern.compile("<strong[^>]*>.*?([\\d,.]+) Caches Found"); + public static final Pattern PATTERN_CACHES_FOUND = Pattern.compile("<strong[^>]*>.*?([\\d,.]+) Caches Found", Pattern.DOTALL); public static final Pattern PATTERN_AVATAR_IMAGE_PROFILE_PAGE = Pattern.compile("<img src=\"(http://img.geocaching.com/user/avatar/[0-9a-f-]+\\.jpg)\"[^>]*\\salt=\"Avatar\""); public static final Pattern PATTERN_LOGIN_NAME_LOGIN_PAGE = Pattern.compile("ctl00_ContentBody_lbUsername\">.*<strong>(.*)</strong>"); public static final Pattern PATTERN_CUSTOMDATE = Pattern.compile("<option selected=\"selected\" value=\"([ /Mdy-]+)\">"); @@ -122,11 +122,12 @@ public final class GCConstants { public final static Pattern PATTERN_SEARCH_DIFFICULTY_TERRAIN = Pattern.compile("<span class=\"small\">([0-5]([\\.,]5)?)/([0-5]([\\.,]5)?)</span><br />"); public final static Pattern PATTERN_SEARCH_CONTAINER = Pattern.compile("<img src=\"/images/icons/container/([^\\.]+)\\.gif\""); public final static Pattern PATTERN_SEARCH_GEOCODE = Pattern.compile("\\|\\W*(GC[0-9A-Z]+)[^\\|]*\\|"); - public final static Pattern PATTERN_SEARCH_ID = Pattern.compile("name=\"CID\"[^v]*value=\"([0-9]+)\""); - public final static Pattern PATTERN_SEARCH_FAVORITE = Pattern.compile("<span id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxFavoritesValue\" title=\"[^\"]*\" class=\"favorite-rank\">([0-9]+)</span>"); - public final static Pattern PATTERN_SEARCH_TOTALCOUNT = Pattern.compile("<td class=\"PageBuilderWidget\"><span>Total Records[^<]*<b>(\\d+)<\\/b>"); + public final static Pattern PATTERN_SEARCH_ID = Pattern.compile("name=\"CID\"[^v]*value=\"(\\d+)\""); + public final static Pattern PATTERN_SEARCH_FAVORITE = Pattern.compile("favorite-rank\">([0-9,.]+)</span>"); + public final static Pattern PATTERN_SEARCH_TOTALCOUNT = Pattern.compile("<span>Total Records\\D*(\\d+)<"); public final static Pattern PATTERN_SEARCH_RECAPTCHA = Pattern.compile("<script[^>]*src=\"[^\"]*/recaptcha/api/challenge\\?k=([^\"]+)\"[^>]*>"); public final static Pattern PATTERN_SEARCH_RECAPTCHACHALLENGE = Pattern.compile("challenge : '([^']+)'"); + public final static Pattern PATTERN_SEARCH_HIDDEN_DATE = Pattern.compile("<span class=\"small\">([\\d-/]{6,10})</span>"); /** * Patterns for waypoints diff --git a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java index 4f2f8c4..2aa5c75 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.settings.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/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index 643caf5..4bc55fe 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCMap.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -2,7 +2,7 @@ package cgeo.geocaching.connector.gc; import cgeo.geocaching.Geocache; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgData; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheSize; @@ -337,7 +337,7 @@ public class GCMap { Log.e("GCMap.searchByViewport: No cache parsed for viewport " + viewport); } else { - searchResult.addGeocodes(search.getGeocodes()); + searchResult.addSearchResult(search); } Tile.Cache.add(tile); } @@ -349,20 +349,21 @@ public class GCMap { } } + + // Check for vanished found caches + if (tiles.iterator().next().getZoomlevel() >= Tile.ZOOMLEVEL_MIN_PERSONALIZED) { + searchResult.addFilteredGeocodes(cgData.getCachedMissingFromSearch(searchResult, tiles, GCConnector.getInstance(), Tile.ZOOMLEVEL_MIN_PERSONALIZED - 1)); + } } - if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY)) { + if (strategy.flags.contains(StrategyFlag.SEARCH_NEARBY) && Settings.isPremiumMember()) { final Geopoint center = viewport.getCenter(); if ((lastSearchViewport == null) || !lastSearchViewport.contains(center)) { //FIXME We don't have a RecaptchaReceiver!? SearchResult search = GCParser.searchByCoords(center, Settings.getCacheType(), false, null); if (search != null && !search.isEmpty()) { final Set<String> geocodes = search.getGeocodes(); - if (Settings.isPremiumMember()) { - lastSearchViewport = cgData.getBounds(geocodes); - } else { - lastSearchViewport = new Viewport(center, center); - } + lastSearchViewport = cgData.getBounds(geocodes); searchResult.addGeocodes(geocodes); } } diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index a58b2cc..e32f72d 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -5,7 +5,6 @@ import cgeo.geocaching.Image; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; import cgeo.geocaching.Trackable; import cgeo.geocaching.TrackableLog; import cgeo.geocaching.Waypoint; @@ -27,6 +26,7 @@ import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.loaders.RecaptchaReceiver; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.DirectionImage; import cgeo.geocaching.utils.CancellableHandler; import cgeo.geocaching.utils.Log; @@ -53,6 +53,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; +import java.util.Date; import java.util.EnumSet; import java.util.GregorianCalendar; import java.util.List; @@ -120,6 +121,7 @@ public abstract class GCParser { final String[] rows = page.split("<tr class="); final int rows_count = rows.length; + int excludedCaches = 0; for (int z = 1; z < rows_count; z++) { final Geocache cache = new Geocache(); final String row = rows[z]; @@ -157,6 +159,7 @@ public abstract class GCParser { if (Settings.isExcludeDisabledCaches() && (cache.isDisabled() || cache.isArchived())) { // skip disabled and archived caches + excludedCaches++; continue; } @@ -176,7 +179,8 @@ public abstract class GCParser { // cache distance - estimated distance for basic members 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())); + cache.setDistance(DistanceParser.parseDistance(distance, + !Settings.isUseImperialUnits())); } // difficulty/terrain @@ -196,6 +200,19 @@ public abstract class GCParser { final String container = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_CONTAINER, false, 1, null, false); cache.setSize(CacheSize.getById(container)); + // date hidden, makes sorting event caches easier + final String dateHidden = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_HIDDEN_DATE, false, 1, null, false); + if (StringUtils.isNotBlank(dateHidden)) { + try { + Date date = Login.parseGcCustomDate(dateHidden); + if (date != null) { + cache.setHidden(date); + } + } catch (ParseException e) { + Log.e("Error parsing event date from search"); + } + } + // cache inventory final MatcherWrapper matcherTbs = new MatcherWrapper(GCConstants.PATTERN_SEARCH_TRACKABLES, row); String inventoryPre = null; @@ -237,7 +254,7 @@ public abstract class GCParser { // favorite count try { - result = TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_FAVORITE, false, 1, null, true); + result = getNumberString(TextUtils.getMatch(row, GCConstants.PATTERN_SEARCH_FAVORITE, false, 1, null, true)); if (null != result) { cache.setFavoritePoints(Integer.parseInt(result)); } @@ -252,7 +269,7 @@ public abstract class GCParser { try { final String result = TextUtils.getMatch(page, GCConstants.PATTERN_SEARCH_TOTALCOUNT, false, 1, null, true); if (null != result) { - searchResult.setTotal(Integer.parseInt(result)); + searchResult.setTotal(Integer.parseInt(result) - excludedCaches); } } catch (final NumberFormatException e) { Log.w("GCParser.parseSearch: Failed to parse cache count"); @@ -354,32 +371,41 @@ public abstract class GCParser { return searchResult; } - static SearchResult parseCacheFromText(final String page, final CancellableHandler handler) { + static SearchResult parseCacheFromText(final String pageIn, final CancellableHandler handler) { CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_details); - if (StringUtils.isBlank(page)) { + if (StringUtils.isBlank(pageIn)) { Log.e("GCParser.parseCache: No page given"); return null; } final SearchResult searchResult = new SearchResult(); - if (page.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || page.contains(GCConstants.STRING_UNPUBLISHED_OWNER) || page.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) { + if (pageIn.contains(GCConstants.STRING_UNPUBLISHED_OTHER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_OWNER) || pageIn.contains(GCConstants.STRING_UNPUBLISHED_FROM_SEARCH)) { searchResult.setError(StatusCode.UNPUBLISHED_CACHE); return searchResult; } - if (page.contains(GCConstants.STRING_PREMIUMONLY_1) || page.contains(GCConstants.STRING_PREMIUMONLY_2)) { + if (pageIn.contains(GCConstants.STRING_PREMIUMONLY_1) || pageIn.contains(GCConstants.STRING_PREMIUMONLY_2)) { searchResult.setError(StatusCode.PREMIUM_ONLY); return searchResult; } - final String cacheName = Html.fromHtml(TextUtils.getMatch(page, GCConstants.PATTERN_NAME, true, "")).toString(); + final String cacheName = Html.fromHtml(TextUtils.getMatch(pageIn, GCConstants.PATTERN_NAME, true, "")).toString(); if (GCConstants.STRING_UNKNOWN_ERROR.equalsIgnoreCase(cacheName)) { searchResult.setError(StatusCode.UNKNOWN_ERROR); return searchResult; } + // first handle the content with line breaks, then trim everything for easier matching and reduced memory consumption in parsed fields + String personalNoteWithLineBreaks = ""; + MatcherWrapper matcher = new MatcherWrapper(GCConstants.PATTERN_PERSONALNOTE, pageIn); + if (matcher.find()) { + personalNoteWithLineBreaks = matcher.group(1).trim(); + } + + final String page = TextUtils.replaceWhitespace(pageIn); + final Geocache cache = new Geocache(); cache.setDisabled(page.contains(GCConstants.STRING_DISABLED)); @@ -516,7 +542,7 @@ public abstract class GCParser { cache.checkFields(); // cache personal note - cache.setPersonalNote(TextUtils.getMatch(page, GCConstants.PATTERN_PERSONALNOTE, true, cache.getPersonalNote())); + cache.setPersonalNote(personalNoteWithLineBreaks); // cache short description cache.setShortDescription(TextUtils.getMatch(page, GCConstants.PATTERN_SHORTDESC, true, "")); @@ -626,7 +652,7 @@ public abstract class GCParser { while (matcherLog.find()) { final String typeStr = matcherLog.group(1); - final String countStr = matcherLog.group(2).replaceAll("[.,]", ""); + final String countStr = getNumberString(matcherLog.group(2)); if (StringUtils.isNotBlank(typeStr) && LogType.UNKNOWN != LogType.getByIconName(typeStr) @@ -736,6 +762,13 @@ public abstract class GCParser { return searchResult; } + private static String getNumberString(final String numberWithPunctuation) { + if (numberWithPunctuation == null) { + return null; + } + return numberWithPunctuation.replaceAll("[.,]", ""); + } + public static SearchResult searchByNextPage(final SearchResult search, boolean showCaptcha, RecaptchaReceiver recaptchaReceiver) { if (search == null) { return search; @@ -862,7 +895,7 @@ public abstract class GCParser { } private static boolean isSearchForMyCaches(final String userName) { - if (userName.equalsIgnoreCase(Settings.getLogin().left)) { + if (userName.equalsIgnoreCase(Settings.getGcLogin().left)) { Log.i("Overriding users choice because of self search, downloading all caches."); return true; } @@ -1008,7 +1041,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, ""); } @@ -1103,19 +1136,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); @@ -1195,7 +1221,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; } @@ -1430,7 +1456,8 @@ public abstract class GCParser { final String distance = TextUtils.getMatch(page, GCConstants.PATTERN_TRACKABLE_DISTANCE, false, null); if (null != distance) { try { - trackable.setDistance(DistanceParser.parseDistance(distance, Settings.isUseMetricUnits())); + trackable.setDistance(DistanceParser.parseDistance(distance, + !Settings.isUseImperialUnits())); } catch (final NumberFormatException e) { Log.e("GCParser.parseTrackable: Failed to parse distance", e); } @@ -1743,16 +1770,6 @@ public abstract class GCParser { } } - if (Settings.isElevationWanted()) { - if (CancellableHandler.isCancelled(handler)) { - return; - } - CancellableHandler.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_elevation); - if (cache.getCoords() != null) { - cache.setElevation(cache.getCoords().getElevation()); - } - } - if (Settings.isRatingWanted()) { if (CancellableHandler.isCancelled(handler)) { return; diff --git a/main/src/cgeo/geocaching/connector/gc/IconDecoder.java b/main/src/cgeo/geocaching/connector/gc/IconDecoder.java index d3a2959..ed44392 100644 --- a/main/src/cgeo/geocaching/connector/gc/IconDecoder.java +++ b/main/src/cgeo/geocaching/connector/gc/IconDecoder.java @@ -1,7 +1,7 @@ package cgeo.geocaching.connector.gc; import cgeo.geocaching.Geocache; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.enumerations.CacheType; import android.graphics.Bitmap; @@ -21,7 +21,7 @@ public abstract class IconDecoder { private static final int CT_MEGAEVENT = 7; private static final int CT_CITO = 8; private static final int CT_WEBCAM = 9; - private static final int CT_WHEREIGO = 10; + private static final int CT_WHERIGO = 10; private static final int CT_VIRTUAL = 11; private static final int CT_LETTERBOX = 12; @@ -116,7 +116,7 @@ public abstract class IconDecoder { case CT_WEBCAM: cache.setType(CacheType.WEBCAM); return true; - case CT_WHEREIGO: + case CT_WHERIGO: cache.setType(CacheType.WHERIGO); return true; case CT_VIRTUAL: @@ -395,12 +395,12 @@ public abstract class IconDecoder { if (g < 71) { return CT_MYSTERY; } - return r < 153 ? CT_WHEREIGO : CT_WEBCAM; + return r < 153 ? CT_WHERIGO : CT_WEBCAM; } if (b < 167) { return r < 157 ? CT_TRADITIONAL : CT_WEBCAM; } - return CT_WHEREIGO; + return CT_WHERIGO; } if (g < 199) { if (r < 142) { @@ -450,7 +450,7 @@ public abstract class IconDecoder { if (b < 252) { if (r < 243) { if (r < 225) { - return CT_WHEREIGO; + return CT_WHERIGO; } if (b < 232) { if (g < 228) { @@ -459,14 +459,14 @@ public abstract class IconDecoder { return r < 231 ? CT_VIRTUAL : CT_TRADITIONAL; } if (r < 236) { - return CT_WHEREIGO; + return CT_WHERIGO; } - return r < 240 ? CT_WEBCAM : CT_WHEREIGO; + return r < 240 ? CT_WEBCAM : CT_WHERIGO; } if (g < 247) { return r < 245 ? CT_WEBCAM : CT_FOUND; } - return CT_WHEREIGO; + return CT_WHERIGO; } return CT_LETTERBOX; } diff --git a/main/src/cgeo/geocaching/connector/gc/Login.java b/main/src/cgeo/geocaching/connector/gc/Login.java index 413cda4..4b4c93f 100644 --- a/main/src/cgeo/geocaching/connector/gc/Login.java +++ b/main/src/cgeo/geocaching/connector/gc/Login.java @@ -1,16 +1,16 @@ package cgeo.geocaching.connector.gc; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.network.Cookies; import cgeo.geocaching.network.HtmlImage; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; -import cgeo.geocaching.utils.TextUtils; 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)); } @@ -66,7 +66,7 @@ public abstract class Login { } private static StatusCode login(boolean retry) { - final ImmutablePair<String, String> login = Settings.getLogin(); + final ImmutablePair<String, String> login = Settings.getGcLogin(); if (login == null || StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) { Login.setActualStatus(cgeoapplication.getInstance().getString(R.string.err_login)); @@ -147,8 +147,8 @@ 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); + 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; } @@ -211,7 +211,7 @@ public abstract class Login { int cachesCount = 0; try { cachesCount = Integer.parseInt(TextUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", "")); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("getLoginStatus: bad cache count", e); } setActualCachesFound(cachesCount); @@ -276,7 +276,7 @@ public abstract class Login { } // 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 = TextUtils.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; } @@ -458,20 +458,31 @@ public abstract class Login { * @return */ public static String getRequestLogged(final String uri, final Parameters params) { - final String data = Network.getResponseData(Network.getRequest(uri, params)); + final String data = Network.getResponseData(Network.getRequest(uri, params), canRemoveWhitespace(uri)); if (getLoginStatus(data)) { return data; } if (login() == StatusCode.NO_ERROR) { - return Network.getResponseData(Network.getRequest(uri, params)); + return Network.getResponseData(Network.getRequest(uri, params), canRemoveWhitespace(uri)); } Log.w("Working as guest."); return data; } + /** + * Unfortunately the cache details page contains user generated whitespace in the personal note, therefore we cannot + * remove the white space from cache details pages. + * + * @param uri + * @return + */ + private static boolean canRemoveWhitespace(final String uri) { + return !StringUtils.contains(uri, "cache_details"); + } + /** Get user session & session token from the Live Map. Needed for following requests */ public static String[] getMapTokens() { final HttpResponse response = Network.getRequest(GCConstants.URL_LIVE_MAP); diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java index dd7f352..3177f2c 100644 --- a/main/src/cgeo/geocaching/connector/gc/Tile.java +++ b/main/src/cgeo/geocaching/connector/gc/Tile.java @@ -37,6 +37,7 @@ public class Tile { public static final int TILE_SIZE = 256; public static final int ZOOMLEVEL_MAX = 18; public static final int ZOOMLEVEL_MIN = 0; + public static final int ZOOMLEVEL_MIN_PERSONALIZED = 12; static final int[] NUMBER_OF_TILES = new int[ZOOMLEVEL_MAX - ZOOMLEVEL_MIN + 1]; static final int[] NUMBER_OF_PIXELS = new int[ZOOMLEVEL_MAX - ZOOMLEVEL_MIN + 1]; @@ -252,6 +253,10 @@ public class Tile { return viewPort.contains(point); } + public Viewport getViewport() { + return viewPort; + } + /** * Calculate needed tiles for the given viewport to cover it with * max 2x2 tiles @@ -319,6 +324,10 @@ public class Tile { public static void add(final Tile tile) { tileCache.put(tile.hashCode(), tile); } + + public static void clear() { + tileCache.clear(); + } } } diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java index 4c6db97..5ca2d28 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java @@ -2,13 +2,14 @@ package cgeo.geocaching.connector.oc; import cgeo.geocaching.Geocache; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.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/OCAuthorizationActivity.java b/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java index 779c1c5..08d796e 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/connector/oc/OCAuthorizationActivity.java @@ -1,7 +1,7 @@ package cgeo.geocaching.connector.oc; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.network.OAuthAuthorizationActivity; @@ -9,8 +9,6 @@ import org.apache.commons.lang3.tuple.ImmutablePair; public class OCAuthorizationActivity extends OAuthAuthorizationActivity { - private final int siteResId = R.string.auth_ocde; - public OCAuthorizationActivity() { super("www.opencaching.de", "/okapi/services/oauth/request_token", @@ -38,7 +36,7 @@ public class OCAuthorizationActivity extends OAuthAuthorizationActivity { @Override protected String getAuthTitle() { - return res.getString(siteResId); + return res.getString(R.string.auth_ocde); } @Override diff --git a/main/src/cgeo/geocaching/connector/oc/OCConnector.java b/main/src/cgeo/geocaching/connector/oc/OCConnector.java index 01738c0..29cdd10 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCConnector.java @@ -12,7 +12,7 @@ public class OCConnector extends AbstractConnector { private final String host; private final String name; private final Pattern codePattern; - private static final Pattern gpxZipFilePattern = Pattern.compile("oc[a-z]{2,3}\\d{5,}\\.zip", Pattern.CASE_INSENSITIVE); + private static final Pattern GPX_ZIP_FILE_PATTERN = Pattern.compile("oc[a-z]{2,3}\\d{5,}\\.zip", Pattern.CASE_INSENSITIVE); public OCConnector(final String name, final String host, final String prefix) { this.name = name; @@ -45,7 +45,7 @@ public class OCConnector extends AbstractConnector { @Override public boolean isZippedGPXFile(String fileName) { - return gpxZipFilePattern.matcher(fileName).matches(); + return GPX_ZIP_FILE_PATTERN.matcher(fileName).matches(); } @Override diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java index 3c99bc9..6959adf 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java @@ -4,7 +4,6 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.Image; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; import cgeo.geocaching.Waypoint; import cgeo.geocaching.cgData; import cgeo.geocaching.cgeoapplication; @@ -14,6 +13,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; @@ -27,6 +27,7 @@ import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.OAuth; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; @@ -50,16 +51,16 @@ import java.util.Locale; import java.util.Map; import java.util.TimeZone; -final public class OkapiClient { +final class OkapiClient { private static final char SEPARATOR = '|'; private static final String SEPARATOR_STRING = Character.toString(SEPARATOR); - private static final SimpleDateFormat logDateFormat; - + private static final SimpleDateFormat LOG_DATE_FORMAT; static { - logDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.US); - logDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + LOG_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.US); + LOG_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); } + private static final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); private static final String CACHE_ATTRNAMES = "attrnames"; private static final String WPT_LOCATION = "location"; @@ -93,6 +94,8 @@ 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 CACHE_MY_NOTES = "my_notes"; private static final String LOG_TYPE = "type"; private static final String LOG_COMMENT = "comment"; @@ -108,9 +111,9 @@ 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"; + private static final String SERVICE_CACHE_ADDITIONAL_L3_FIELDS = "is_watched|my_notes"; private static final String METHOD_SEARCH_NEAREST = "services/caches/search/nearest"; private static final String METHOD_SEARCH_BBOX = "services/caches/search/bbox"; @@ -118,12 +121,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"); @@ -137,17 +140,19 @@ final public class OkapiClient { return parseCache(data); } - 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); + public static List<Geocache> getCachesAround(final Geopoint center, final OCApiConnector connector) { + 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(final 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); @@ -160,13 +165,13 @@ final public class OkapiClient { } // Assumes level 3 OAuth - public static List<Geocache> getCachesBBox(final Viewport viewport, OCApiConnector connector) { + public static List<Geocache> getCachesBBox(final Viewport viewport, final OCApiConnector connector) { if (viewport.getLatitudeSpan() == 0 || viewport.getLongitudeSpan() == 0) { 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,22 +179,10 @@ 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) { + public static boolean setWatchState(final Geocache cache, final boolean watched, final OCApiConnector connector) { final Parameters params = new Parameters("cache_code", cache.getGeocode()); params.add("watched", watched ? "true" : "false"); @@ -204,15 +197,18 @@ 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, final LogType logType, final Calendar date, final String log, final String logPassword, final OCApiConnector connector) { final Parameters params = new Parameters("cache_code", cache.getGeocode()); params.add("logtype", logType.oc_type); params.add("comment", log); params.add("comment_format", "plaintext"); - params.add("when", logDateFormat.format(date.getTime())); + params.add("when", LOG_DATE_FORMAT.format(date.getTime())); 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 +222,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 +239,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 +266,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 +294,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 +315,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 +333,15 @@ final public class OkapiClient { if (!response.isNull(CACHE_IS_WATCHED)) { cache.setOnWatchlist(response.getBoolean(CACHE_IS_WATCHED)); } + if (!response.isNull(CACHE_MY_NOTES)) { + cache.setPersonalNote(response.getString(CACHE_MY_NOTES)); + } + 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; @@ -366,7 +367,7 @@ final public class OkapiClient { } } - private static String absoluteUrl(String url, String geocode) { + private static String absoluteUrl(final String url, final String geocode) { final Uri uri = Uri.parse(url); if (!uri.isAbsolute()) { @@ -379,16 +380,16 @@ final public class OkapiClient { return url; } - private static String parseUser(JSONObject user) throws JSONException { + private static String parseUser(final JSONObject user) throws JSONException { return user.getString(USER_USERNAME); } - private static List<LogEntry> parseLogs(JSONArray logsJSON) { + private static List<LogEntry> parseLogs(final JSONArray logsJSON) { 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,23 +398,23 @@ final public class OkapiClient { result = new ArrayList<LogEntry>(); } result.add(log); - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.parseLogs", e); } } return result; } - private static List<Waypoint> parseWaypoints(JSONArray wptsJson) { + private static List<Waypoint> parseWaypoints(final JSONArray wptsJson) { 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,14 +422,14 @@ final public class OkapiClient { result = new ArrayList<Waypoint>(); } result.add(wpt); - } catch (JSONException e) { + } catch (final JSONException e) { Log.e("OkapiClient.parseWaypoints", e); } } return result; } - private static LogType parseLogType(String logType) { + private static LogType parseLogType(final String logType) { if ("Found it".equalsIgnoreCase(logType)) { return LogType.FOUND_IT; } @@ -438,7 +439,7 @@ final public class OkapiClient { return LogType.NOTE; } - private static WaypointType parseWptType(String wptType) { + private static WaypointType parseWptType(final String wptType) { if ("parking".equalsIgnoreCase(wptType)) { return WaypointType.PARKING; } @@ -464,11 +465,10 @@ final public class OkapiClient { } private static Date parseDate(final String date) { - final SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); 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; @@ -484,19 +484,19 @@ final public class OkapiClient { return null; } - private static List<String> parseAttributes(JSONArray nameList) { + private static List<String> parseAttributes(final 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 +517,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)) { @@ -565,7 +565,7 @@ final public class OkapiClient { return CacheType.UNKNOWN; } - private static String getCoreFields(OCApiConnector connector) { + private static String getCoreFields(final OCApiConnector connector) { if (connector == null) { Log.e("OkapiClient.getCoreFields called with invalid connector"); return StringUtils.EMPTY; @@ -578,13 +578,13 @@ final public class OkapiClient { return SERVICE_CACHE_CORE_FIELDS; } - private static String getFullFields(OCApiConnector connector) { + private static String getFullFields(final OCApiConnector connector) { if (connector == null) { Log.e("OkapiClient.getFullFields called with invalid connector"); 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); @@ -629,7 +629,7 @@ final public class OkapiClient { return "en"; } - private static void addFilterParams(final Map<String, String> valueMap, OCApiConnector connector) { + private static void addFilterParams(final Map<String, String> valueMap, final OCApiConnector connector) { if (!Settings.isExcludeDisabledCaches()) { valueMap.put("status", "Available|Temporarily unavailable"); } @@ -642,13 +642,13 @@ final public class OkapiClient { } } - private static void addRetrieveParams(final Parameters params, OCApiConnector connector) { + private static void addRetrieveParams(final Parameters params, final OCApiConnector connector) { params.add("retr_method", METHOD_RETRIEVE_CACHES); params.add("retr_params", "{\"fields\": \"" + getCoreFields(connector) + "\"}"); params.add("wrap", "true"); } - private static String getFilterFromType(CacheType cacheType) { + private static String getFilterFromType(final CacheType cacheType) { switch (cacheType) { case EVENT: return "Event"; @@ -667,67 +667,40 @@ final public class OkapiClient { } } - public static UserInfo getUserInfo(OCApiLiveConnector connector) { + public static UserInfo getUserInfo(final OCApiLiveConnector connector) { final Parameters params = new Parameters("fields", USER_INFO_FIELDS); final JSONObject data = request(connector, OkapiService.SERVICE_USER, params); if (data == null) { - return new UserInfo(StringUtils.EMPTY, 0, false); + return new UserInfo(StringUtils.EMPTY, 0, UserInfoStatus.FAILED); } String name = StringUtils.EMPTY; - int finds = 0; - boolean success = true; + boolean successUserName = false; if (!data.isNull(USER_USERNAME)) { try { name = data.getString(USER_USERNAME); - } catch (JSONException e) { + successUserName = true; + } catch (final JSONException e) { Log.e("OkapiClient.getUserInfo - name", e); - success = false; } - } else { - success = false; } + int finds = 0; + boolean successFinds = false; + if (!data.isNull(USER_CACHES_FOUND)) { try { finds = data.getInt(USER_CACHES_FOUND); - } catch (JSONException e) { + successFinds = true; + } catch (final JSONException e) { Log.e("OkapiClient.getUserInfo - finds", e); - success = false; } - } else { - 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, successUserName && successFinds ? 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..c995975 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; @@ -19,14 +19,14 @@ import java.util.List; public class OkapiLoggingManager implements ILoggingManager { - private final OCApiConnector connector; + private final OCApiLiveConnector connector; private final Geocache cache; private LogCacheActivity activity; - private final static List<LogType> standardLogTypes = Arrays.asList(LogType.FOUND_IT, LogType.DIDNT_FIND_IT, LogType.NOTE, LogType.NEEDS_MAINTENANCE); + private final static List<LogType> standardLogTypes = Arrays.asList(LogType.FOUND_IT, LogType.DIDNT_FIND_IT, LogType.NOTE); private final static List<LogType> eventLogTypes = Arrays.asList(LogType.WILL_ATTEND, LogType.ATTENDED, LogType.NOTE); - public OkapiLoggingManager(Activity activity, OCApiConnector connector, Geocache cache) { + public OkapiLoggingManager(Activity activity, OCApiLiveConnector connector, Geocache cache) { this.connector = connector; this.cache = cache; this.activity = (LogCacheActivity) activity; @@ -38,8 +38,10 @@ 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) { + final LogResult result = OkapiClient.postLog(cache, logType, date, log, logPassword, connector); + connector.login(null, null); + return result; } @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/ox/OXConnector.java b/main/src/cgeo/geocaching/connector/ox/OXConnector.java index eec07e3..af33bb6 100644 --- a/main/src/cgeo/geocaching/connector/ox/OXConnector.java +++ b/main/src/cgeo/geocaching/connector/ox/OXConnector.java @@ -3,7 +3,7 @@ package cgeo.geocaching.connector.ox; import cgeo.geocaching.Geocache; import cgeo.geocaching.ICache; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.capability.ISearchByGeocode; diff --git a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java index 9ca7716..0dac6cc 100644 --- a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java @@ -10,7 +10,7 @@ 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]+", Pattern.CASE_INSENSITIVE); + 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) { @@ -31,4 +31,20 @@ public class TravelBugConnector extends AbstractTrackableConnector { 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/enumerations/CacheType.java b/main/src/cgeo/geocaching/enumerations/CacheType.java index 8dbadfd..6c63cac 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheType.java +++ b/main/src/cgeo/geocaching/enumerations/CacheType.java @@ -28,7 +28,7 @@ public enum CacheType { LOSTANDFOUND("lostfound", "Lost & Found", "3ea6533d-bb52-42fe-b2d2-79a3424d4728", R.string.lostfound, R.drawable.type_event), // icon missing PROJECT_APE("ape", "Project Ape Cache", "2555690d-b2bc-4b55-b5ac-0cb704c0b768", R.string.ape, R.drawable.type_ape), GCHQ("gchq", "Groundspeak HQ", "416f2494-dc17-4b6a-9bab-1a29dd292d8c", R.string.gchq, R.drawable.type_hq), - GPS_EXHIBIT("gps", "GPS Cache Exhibit", "72e69af2-7986-4990-afd9-bc16cbbb4ce3", R.string.gps, R.drawable.type_traditional), // icon missing + GPS_EXHIBIT("gps", "GPS Adventures Exhibit", "72e69af2-7986-4990-afd9-bc16cbbb4ce3", R.string.gps, R.drawable.type_event), // icon missing BLOCK_PARTY("block", "Groundspeak Block Party", "bc2f3df2-1aab-4601-b2ff-b5091f6c02e3", R.string.block, R.drawable.type_event), // icon missing UNKNOWN("unknown", "unknown", "", R.string.unknown, R.drawable.type_unknown), /** No real cache type -> filter */ diff --git a/main/src/cgeo/geocaching/export/FieldnoteExport.java b/main/src/cgeo/geocaching/export/FieldnoteExport.java index a42a48a..9d0310c 100644 --- a/main/src/cgeo/geocaching/export/FieldnoteExport.java +++ b/main/src/cgeo/geocaching/export/FieldnoteExport.java @@ -9,6 +9,8 @@ import cgeo.geocaching.connector.gc.Login; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.ui.Formatter; import cgeo.geocaching.utils.AsyncTaskWithProgress; import cgeo.geocaching.utils.IOUtils; import cgeo.geocaching.utils.Log; @@ -70,21 +72,18 @@ 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); final CheckBox onlyNewOption = (CheckBox) layout.findViewById(R.id.onlynew); - uploadOption.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onlyNewOption.setEnabled(uploadOption.isChecked()); - } - }); + if (Settings.getFieldnoteExportDate() > 0) { + onlyNewOption.setText(getString(R.string.export_fieldnotes_onlynew) + "\n(" + Formatter.formatShortDateTime(activity, Settings.getFieldnoteExportDate()) + ')'); + } builder.setPositiveButton(R.string.export, new DialogInterface.OnClickListener() { @@ -132,13 +131,16 @@ 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())); + final LogEntry log = cgData.loadLogOffline(cache.getGeocode()); + if (!onlyNew || onlyNew && log.date > Settings.getFieldnoteExportDate()) { + appendFieldNote(fieldNoteBuffer, cache, log); + } publishProgress(++i); } } - } catch (Exception e) { + } catch (final Exception e) { Log.e("FieldnoteExport.ExportTask generation", e); return false; } @@ -151,17 +153,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 +183,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); @@ -201,10 +197,6 @@ class FieldnoteExport extends AbstractExport { "__EVENTARGUMENT", "", "ctl00$ContentBody$btnUpload", "Upload Field Note"); - if (onlyNew) { - uploadParams.put("ctl00$ContentBody$chkSuppressDate", "on"); - } - Login.putViewstates(uploadParams, viewstates); Network.getResponseData(Network.postRequest(uri, uploadParams, "ctl00$ContentBody$FieldNoteLoader", "text/plain", exportFile)); @@ -222,9 +214,7 @@ class FieldnoteExport extends AbstractExport { protected void onPostExecuteInternal(Boolean result) { if (null != activity) { if (result) { - // if (onlyNew) { - // // update last export time in settings when doing it ourself (currently we use the date check from gc.com) - // } + Settings.setFieldnoteExportDate(System.currentTimeMillis()); ActivityMixin.showToast(activity, getName() + ' ' + getString(R.string.export_exportedto) + ": " + exportFile.toString()); diff --git a/main/src/cgeo/geocaching/export/GpxExport.java b/main/src/cgeo/geocaching/export/GpxExport.java index 0ba1a3c..61be3c5 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.settings.Settings; 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.TextUtils; 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", 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(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..a1c04d7 100644 --- a/main/src/cgeo/geocaching/files/GPXImporter.java +++ b/main/src/cgeo/geocaching/files/GPXImporter.java @@ -3,7 +3,7 @@ package cgeo.geocaching.files; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.StaticMapsProvider; import cgeo.geocaching.cgData; import cgeo.geocaching.activity.IAbstractActivity; @@ -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..76e3713 100644 --- a/main/src/cgeo/geocaching/files/GPXParser.java +++ b/main/src/cgeo/geocaching/files/GPXParser.java @@ -54,13 +54,13 @@ public abstract class GPXParser extends FileParser { /** * Attention: case sensitive geocode pattern to avoid matching normal words in the name or description of the cache. */ - private static final Pattern patternGeocode = Pattern.compile("([A-Z][0-9A-Z]+)"); - private static final Pattern patternGuid = Pattern.compile(".*" + Pattern.quote("guid=") + "([0-9a-z\\-]+)", Pattern.CASE_INSENSITIVE); - private static final Pattern patternUrlGeocode = Pattern.compile(".*" + Pattern.quote("wp=") + "([A-Z][0-9A-Z]+)", Pattern.CASE_INSENSITIVE); + private static final Pattern PATTERN_GEOCODE = Pattern.compile("([A-Z][0-9A-Z]+)"); + private static final Pattern PATTERN_GUID = Pattern.compile(".*" + Pattern.quote("guid=") + "([0-9a-z\\-]+)", Pattern.CASE_INSENSITIVE); + private static final Pattern PATTERN_URL_GEOCODE = Pattern.compile(".*" + Pattern.quote("wp=") + "([A-Z][0-9A-Z]+)", Pattern.CASE_INSENSITIVE); /** * supported groundspeak extensions of the GPX format */ - private static final String[] nsGCList = new String[] { + private static final String[] GROUNDSPEAK_NAMESPACE = new String[] { "http://www.groundspeak.com/cache/1/1", // PQ 1.1 "http://www.groundspeak.com/cache/1/0/1", // PQ 1.0.1 "http://www.groundspeak.com/cache/1/0", // PQ 1.0 @@ -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); } } @@ -432,16 +434,16 @@ public abstract class GPXParser extends FileParser { @Override public void end(String url) { - final MatcherWrapper matcher = new MatcherWrapper(patternGuid, url); + final MatcherWrapper matcher = new MatcherWrapper(PATTERN_GUID, url); if (matcher.matches()) { final String guid = matcher.group(1); if (StringUtils.isNotBlank(guid)) { cache.setGuid(guid); } } - final MatcherWrapper matcherCode = new MatcherWrapper(patternUrlGeocode, url); + final MatcherWrapper matcherCode = new MatcherWrapper(PATTERN_URL_GEOCODE, 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 : GROUNDSPEAK_NAMESPACE) { // 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)) { @@ -847,7 +849,7 @@ public abstract class GPXParser extends FileParser { return; } final String trimmed = input.trim(); - final MatcherWrapper matcherGeocode = new MatcherWrapper(patternGeocode, trimmed); + final MatcherWrapper matcherGeocode = new MatcherWrapper(PATTERN_GEOCODE, trimmed); if (matcherGeocode.find()) { final String geocode = matcherGeocode.group(1); // a geocode should not be part of a word diff --git a/main/src/cgeo/geocaching/files/LocalStorage.java b/main/src/cgeo/geocaching/files/LocalStorage.java index 0f3e0e1..ec09433 100644 --- a/main/src/cgeo/geocaching/files/LocalStorage.java +++ b/main/src/cgeo/geocaching/files/LocalStorage.java @@ -31,6 +31,9 @@ import java.io.OutputStream; */ public class LocalStorage { + public static final String HEADER_LAST_MODIFIED = "last-modified"; + public static final String HEADER_ETAG = "etag"; + /** Name of the local private directory used to hold cached information */ public final static String cache = ".cgeo"; @@ -177,8 +180,8 @@ public class LocalStorage { try { final boolean saved = saveToFile(response.getEntity().getContent(), targetFile); - saveHeader("etag", saved ? response : null, targetFile); - saveHeader("last-modified", saved ? response : null, targetFile); + saveHeader(HEADER_ETAG, saved ? response : null, targetFile); + saveHeader(HEADER_LAST_MODIFIED, saved ? response : null, targetFile); return saved; } catch (IOException e) { Log.e("LocalStorage.saveEntityToFile", e); diff --git a/main/src/cgeo/geocaching/filter/AbstractFilter.java b/main/src/cgeo/geocaching/filter/AbstractFilter.java index bc99959..6bd8587 100644 --- a/main/src/cgeo/geocaching/filter/AbstractFilter.java +++ b/main/src/cgeo/geocaching/filter/AbstractFilter.java @@ -8,12 +8,12 @@ import java.util.List; abstract class AbstractFilter implements IFilter { private final String name; - protected AbstractFilter(String name) { + protected AbstractFilter(final String name) { this.name = name; } @Override - public void filter(List<Geocache> list) { + public void filter(final List<Geocache> list) { final List<Geocache> itemsToRemove = new ArrayList<Geocache>(); for (Geocache item : list) { if (!accepts(item)) { @@ -30,7 +30,7 @@ abstract class AbstractFilter implements IFilter { /* * show name in array adapter - * + * * @see java.lang.Object#toString() */ @Override diff --git a/main/src/cgeo/geocaching/filter/AbstractRangeFilter.java b/main/src/cgeo/geocaching/filter/AbstractRangeFilter.java index e84174a..96d256a 100644 --- a/main/src/cgeo/geocaching/filter/AbstractRangeFilter.java +++ b/main/src/cgeo/geocaching/filter/AbstractRangeFilter.java @@ -8,7 +8,7 @@ abstract class AbstractRangeFilter extends AbstractFilter { protected final float rangeMin; protected final float rangeMax; - protected AbstractRangeFilter(int ressourceId, int range) { + protected AbstractRangeFilter(final int ressourceId, final int range) { super(cgeoapplication.getInstance().getResources().getString(ressourceId) + ' ' + (range == 5 ? '5' : range + " + " + String.format("%.1f", range + 0.5))); this.rangeMin = range; rangeMax = rangeMin + 1f; diff --git a/main/src/cgeo/geocaching/filter/DifficultyFilter.java b/main/src/cgeo/geocaching/filter/DifficultyFilter.java index 8099a51..438c25a 100644 --- a/main/src/cgeo/geocaching/filter/DifficultyFilter.java +++ b/main/src/cgeo/geocaching/filter/DifficultyFilter.java @@ -8,21 +8,25 @@ import java.util.List; class DifficultyFilter extends AbstractRangeFilter { - public DifficultyFilter(int difficulty) { + public DifficultyFilter(final int difficulty) { super(R.string.cache_difficulty, difficulty); } @Override - public boolean accepts(Geocache cache) { - return rangeMin <= cache.getDifficulty() && cache.getDifficulty() < rangeMax; + public boolean accepts(final Geocache cache) { + final float difficulty = cache.getDifficulty(); + return rangeMin <= difficulty && difficulty < rangeMax; } public static class Factory implements IFilterFactory { + private static final int DIFFICULTY_MIN = 1; + private static final int DIFFICULTY_MAX = 5; + @Override public List<IFilter> getFilters() { - final ArrayList<IFilter> filters = new ArrayList<IFilter>(5); - for (int difficulty = 1; difficulty <= 5; difficulty++) { + final ArrayList<IFilter> filters = new ArrayList<IFilter>(DIFFICULTY_MAX); + for (int difficulty = DIFFICULTY_MIN; difficulty <= DIFFICULTY_MAX; difficulty++) { filters.add(new DifficultyFilter(difficulty)); } return filters; diff --git a/main/src/cgeo/geocaching/filter/FilterUserInterface.java b/main/src/cgeo/geocaching/filter/FilterUserInterface.java index a31415d..2404f44 100644 --- a/main/src/cgeo/geocaching/filter/FilterUserInterface.java +++ b/main/src/cgeo/geocaching/filter/FilterUserInterface.java @@ -1,9 +1,9 @@ package cgeo.geocaching.filter; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.RunnableWithArgument; @@ -61,7 +61,7 @@ public final class FilterUserInterface { Collections.sort(registry, new Comparator<FactoryEntry>() { @Override - public int compare(FactoryEntry lhs, FactoryEntry rhs) { + public int compare(final FactoryEntry lhs, final FactoryEntry rhs) { return lhs.name.compareToIgnoreCase(rhs.name); } }); @@ -70,7 +70,7 @@ public final class FilterUserInterface { register(R.string.caches_filter_clear, null); } - private void register(int resourceId, Class<? extends IFilterFactory> factoryClass) { + private void register(final int resourceId, final Class<? extends IFilterFactory> factoryClass) { registry.add(new FactoryEntry(res.getString(resourceId), factoryClass)); } @@ -82,7 +82,7 @@ public final class FilterUserInterface { builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialog, int itemIndex) { + public void onClick(final DialogInterface dialog, final int itemIndex) { FactoryEntry entry = adapter.getItem(itemIndex); // reset? if (entry.filterFactory == null) { @@ -115,7 +115,7 @@ public final class FilterUserInterface { final ArrayAdapter<IFilter> adapter = new ArrayAdapter<IFilter>(activity, android.R.layout.select_dialog_item, filters); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override - public void onClick(DialogInterface dialog, int item) { + public void onClick(final DialogInterface dialog, final int item) { runAfterwards.run(filters.get(item)); } }); diff --git a/main/src/cgeo/geocaching/filter/IFilter.java b/main/src/cgeo/geocaching/filter/IFilter.java index 4a428f8..4fafe6f 100644 --- a/main/src/cgeo/geocaching/filter/IFilter.java +++ b/main/src/cgeo/geocaching/filter/IFilter.java @@ -6,13 +6,13 @@ import java.util.List; public interface IFilter { - public abstract String getName(); + String getName(); /** * @param cache * @return true if the filter accepts the cache, false otherwise */ - public abstract boolean accepts(final Geocache cache); + boolean accepts(final Geocache cache); - public void filter(final List<Geocache> list); + void filter(final List<Geocache> list); }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/filter/IFilterFactory.java b/main/src/cgeo/geocaching/filter/IFilterFactory.java index e750639..afc99af 100644 --- a/main/src/cgeo/geocaching/filter/IFilterFactory.java +++ b/main/src/cgeo/geocaching/filter/IFilterFactory.java @@ -3,5 +3,5 @@ package cgeo.geocaching.filter; import java.util.List; interface IFilterFactory { - public List<? extends IFilter> getFilters(); + List<? extends IFilter> getFilters(); } diff --git a/main/src/cgeo/geocaching/filter/OriginFilter.java b/main/src/cgeo/geocaching/filter/OriginFilter.java index bd4e41e..8c54a4c 100644 --- a/main/src/cgeo/geocaching/filter/OriginFilter.java +++ b/main/src/cgeo/geocaching/filter/OriginFilter.java @@ -13,13 +13,13 @@ public class OriginFilter extends AbstractFilter { private final IConnector connector; - public OriginFilter(IConnector connector) { + public OriginFilter(final IConnector connector) { super(connector.getName()); this.connector = connector; } @Override - public boolean accepts(Geocache cache) { + public final boolean accepts(final Geocache cache) { return ConnectorFactory.getConnector(cache) == connector; } @@ -36,7 +36,7 @@ public class OriginFilter extends AbstractFilter { Collections.sort(filters, new Comparator<OriginFilter>() { @Override - public int compare(OriginFilter lhs, OriginFilter rhs) { + public int compare(final OriginFilter lhs, final OriginFilter rhs) { return lhs.getName().compareToIgnoreCase(rhs.getName()); } }); diff --git a/main/src/cgeo/geocaching/filter/SizeFilter.java b/main/src/cgeo/geocaching/filter/SizeFilter.java index 8ddc475..13c1d87 100644 --- a/main/src/cgeo/geocaching/filter/SizeFilter.java +++ b/main/src/cgeo/geocaching/filter/SizeFilter.java @@ -9,13 +9,13 @@ import java.util.List; class SizeFilter extends AbstractFilter { private final CacheSize cacheSize; - public SizeFilter(CacheSize cacheSize) { + public SizeFilter(final CacheSize cacheSize) { super(cacheSize.id); this.cacheSize = cacheSize; } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(final Geocache cache) { return cacheSize == cache.getSize(); } diff --git a/main/src/cgeo/geocaching/filter/StateFilter.java b/main/src/cgeo/geocaching/filter/StateFilter.java index e18128d..98589ab 100644 --- a/main/src/cgeo/geocaching/filter/StateFilter.java +++ b/main/src/cgeo/geocaching/filter/StateFilter.java @@ -13,9 +13,9 @@ import java.util.List; abstract class StateFilter extends AbstractFilter { - final static Resources res = cgeoapplication.getInstance().getResources(); + static final Resources res = cgeoapplication.getInstance().getResources(); - protected StateFilter(String name) { + protected StateFilter(final String name) { super(name); } @@ -26,7 +26,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(final Geocache cache) { return cache.isFound(); } @@ -38,7 +38,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(final Geocache cache) { return cache.isArchived(); } } @@ -49,7 +49,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(final Geocache cache) { return cache.isDisabled(); } } @@ -60,7 +60,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(final Geocache cache) { return cache.isPremiumMembersOnly(); } } @@ -71,7 +71,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(final Geocache cache) { return !cache.isPremiumMembersOnly(); } } @@ -82,7 +82,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(final Geocache cache) { return cache.isLogOffline(); } } @@ -93,7 +93,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(final Geocache cache) { return cache.isOffline(); } } @@ -104,7 +104,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(final Geocache cache) { return !cache.isOffline(); } } @@ -126,7 +126,7 @@ abstract class StateFilter extends AbstractFilter { Collections.sort(filters, new Comparator<StateFilter>() { @Override - public int compare(StateFilter filter1, StateFilter filter2) { + public int compare(final StateFilter filter1, final StateFilter filter2) { return filter1.getName().compareToIgnoreCase(filter2.getName()); } }); diff --git a/main/src/cgeo/geocaching/filter/TerrainFilter.java b/main/src/cgeo/geocaching/filter/TerrainFilter.java index 87372c6..d74f954 100644 --- a/main/src/cgeo/geocaching/filter/TerrainFilter.java +++ b/main/src/cgeo/geocaching/filter/TerrainFilter.java @@ -8,20 +8,24 @@ import java.util.List; class TerrainFilter extends AbstractRangeFilter { - public TerrainFilter(int terrain) { + public TerrainFilter(final int terrain) { super(R.string.cache_terrain, terrain); } @Override - public boolean accepts(Geocache cache) { - return rangeMin <= cache.getTerrain() && cache.getTerrain() < rangeMax; + public boolean accepts(final Geocache cache) { + final float terrain = cache.getTerrain(); + return rangeMin <= terrain && terrain < rangeMax; } public static class Factory implements IFilterFactory { + private static final int TERRAIN_MIN = 1; + private static final int TERRAIN_MAX = 5; + @Override public List<IFilter> getFilters() { - final ArrayList<IFilter> filters = new ArrayList<IFilter>(5); - for (int terrain = 1; terrain <= 5; terrain++) { + final ArrayList<IFilter> filters = new ArrayList<IFilter>(TERRAIN_MAX); + for (int terrain = TERRAIN_MIN; terrain <= TERRAIN_MAX; terrain++) { filters.add(new TerrainFilter(terrain)); } return filters; diff --git a/main/src/cgeo/geocaching/filter/TrackablesFilter.java b/main/src/cgeo/geocaching/filter/TrackablesFilter.java index 5eff8a7..74f43be 100644 --- a/main/src/cgeo/geocaching/filter/TrackablesFilter.java +++ b/main/src/cgeo/geocaching/filter/TrackablesFilter.java @@ -13,7 +13,7 @@ class TrackablesFilter extends AbstractFilter implements IFilterFactory { } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(final Geocache cache) { return cache.hasTrackables(); } diff --git a/main/src/cgeo/geocaching/gcvote/GCVote.java b/main/src/cgeo/geocaching/gcvote/GCVote.java index f6cfb84..bbfddf1 100644 --- a/main/src/cgeo/geocaching/gcvote/GCVote.java +++ b/main/src/cgeo/geocaching/gcvote/GCVote.java @@ -1,7 +1,7 @@ package cgeo.geocaching.gcvote; import cgeo.geocaching.Geocache; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.utils.LeastRecentlyUsedMap; diff --git a/main/src/cgeo/geocaching/geopoint/DistanceParser.java b/main/src/cgeo/geocaching/geopoint/DistanceParser.java index 5a840a8..e3a7482 100644 --- a/main/src/cgeo/geocaching/geopoint/DistanceParser.java +++ b/main/src/cgeo/geocaching/geopoint/DistanceParser.java @@ -22,7 +22,8 @@ public final class DistanceParser { * @throws NumberFormatException * if the given number is invalid */ - public static float parseDistance(String distanceText, final boolean metricUnit) throws NumberFormatException { + public static float parseDistance(String distanceText, final boolean metricUnit) + throws NumberFormatException { final MatcherWrapper matcher = new MatcherWrapper(pattern, distanceText); if (!matcher.find()) { diff --git a/main/src/cgeo/geocaching/geopoint/Geopoint.java b/main/src/cgeo/geocaching/geopoint/Geopoint.java index 1973b03..e8a6dff 100644 --- a/main/src/cgeo/geocaching/geopoint/Geopoint.java +++ b/main/src/cgeo/geocaching/geopoint/Geopoint.java @@ -2,14 +2,8 @@ package cgeo.geocaching.geopoint; import cgeo.geocaching.ICoordinates; import cgeo.geocaching.R; -import cgeo.geocaching.geopoint.GeopointFormatter.Format; -import cgeo.geocaching.network.Network; -import cgeo.geocaching.network.Parameters; -import cgeo.geocaching.utils.Log; import org.apache.commons.lang3.StringUtils; -import org.json.JSONArray; -import org.json.JSONObject; import android.location.Location; import android.os.Parcel; @@ -323,34 +317,6 @@ public final class Geopoint implements ICoordinates, Parcelable { } } - public Double getElevation() { - try { - final String uri = "http://maps.googleapis.com/maps/api/elevation/json"; - final Parameters params = new Parameters( - "sensor", "false", - "locations", format(Format.LAT_LON_DECDEGREE_COMMA)); - final JSONObject response = Network.requestJSON(uri, params); - - if (response == null) { - return null; - } - - if (!StringUtils.equalsIgnoreCase(response.getString("status"), "OK")) { - return null; - } - - if (response.has("results")) { - JSONArray results = response.getJSONArray("results"); - JSONObject result = results.getJSONObject(0); - return result.getDouble("elevation"); - } - } catch (Exception e) { - Log.w("Geopoint.getElevation", e); - } - - return null; - } - @Override public Geopoint getCoords() { return this; diff --git a/main/src/cgeo/geocaching/geopoint/Units.java b/main/src/cgeo/geocaching/geopoint/Units.java index 3da3ad9..b99e00e 100644 --- a/main/src/cgeo/geocaching/geopoint/Units.java +++ b/main/src/cgeo/geocaching/geopoint/Units.java @@ -1,6 +1,6 @@ package cgeo.geocaching.geopoint; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -9,15 +9,7 @@ public class Units { public static ImmutablePair<Double, String> scaleDistance(final double distanceKilometers) { double distance; String units; - if (Settings.isUseMetricUnits()) { - if (distanceKilometers >= 1) { - distance = distanceKilometers; - units = "km"; - } else { - distance = distanceKilometers * 1000; - units = "m"; - } - } else { + if (Settings.isUseImperialUnits()) { distance = distanceKilometers / IConversion.MILES_TO_KILOMETER; if (distance >= 0.1) { units = "mi"; @@ -25,6 +17,14 @@ public class Units { distance *= 5280; units = "ft"; } + } else { + if (distanceKilometers >= 1) { + distance = distanceKilometers; + units = "km"; + } else { + distance = distanceKilometers * 1000; + units = "m"; + } } return new ImmutablePair<Double, String>(distance, units); } @@ -47,18 +47,6 @@ public class Units { return String.format(formatString + " %s", scaled.left, scaled.right); } - /** - * Get human readable elevation, depending on settings for metric units. - * Result is rounded to full meters/feet, as the sensors don't have that precision anyway. - * - * @param meters - * @return - */ - public static String getElevation(float meters) { - final ImmutablePair<Double, String> scaled = scaleDistance(meters / 1000f); - return (meters >= 0 ? "↥ " : "↧ ") + String.format("%d %s", Math.abs(Math.round(scaled.left)), scaled.right); - } - public static String getDistanceFromMeters(float meters) { return getDistanceFromKilometers(meters / 1000f); } @@ -68,6 +56,6 @@ public class Units { if (speed.endsWith("mi")) { return speed.substring(0, speed.length() - 2) + "mph"; } - return speed + (Settings.isUseMetricUnits() ? "/h" : "ph"); + return speed + (!Settings.isUseImperialUnits() ? "/h" : "ph"); } } diff --git a/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java index efaaabc..dd7c7a6 100644 --- a/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java @@ -1,7 +1,7 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.connector.gc.GCParser; import android.content.Context; diff --git a/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java index 09ea459..34b3a61 100644 --- a/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/CoordsGeocacheListLoader.java @@ -1,7 +1,7 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.capability.ISearchByCenter; import cgeo.geocaching.connector.gc.GCParser; @@ -27,10 +27,7 @@ public class CoordsGeocacheListLoader extends AbstractSearchLoader { for (ISearchByCenter centerConn : ConnectorFactory.getSearchByCenterConnectors()) { if (centerConn.isActivated()) { - SearchResult temp = centerConn.searchByCenter(coords); - if (temp != null) { - search.addGeocodes(temp.getGeocodes()); - } + search.addSearchResult(centerConn.searchByCenter(coords)); } } diff --git a/main/src/cgeo/geocaching/loaders/HistoryGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/HistoryGeocacheListLoader.java index 90e22d1..722f9f5 100644 --- a/main/src/cgeo/geocaching/loaders/HistoryGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/HistoryGeocacheListLoader.java @@ -1,7 +1,7 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgData; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.geopoint.Geopoint; diff --git a/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java index 36b62d6..adfc423 100644 --- a/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/KeywordGeocacheListLoader.java @@ -1,7 +1,7 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.connector.gc.GCParser; import android.content.Context; diff --git a/main/src/cgeo/geocaching/loaders/NextPageGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/NextPageGeocacheListLoader.java index faae9de..1104f83 100644 --- a/main/src/cgeo/geocaching/loaders/NextPageGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/NextPageGeocacheListLoader.java @@ -1,7 +1,7 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.connector.gc.GCParser; import android.content.Context; diff --git a/main/src/cgeo/geocaching/loaders/OfflineGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/OfflineGeocacheListLoader.java index 0081aa1..ab8ba6a 100644 --- a/main/src/cgeo/geocaching/loaders/OfflineGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/OfflineGeocacheListLoader.java @@ -1,7 +1,7 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgData; import cgeo.geocaching.geopoint.Geopoint; diff --git a/main/src/cgeo/geocaching/loaders/OwnerGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/OwnerGeocacheListLoader.java index 3bed3f4..ea9b8ad 100644 --- a/main/src/cgeo/geocaching/loaders/OwnerGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/OwnerGeocacheListLoader.java @@ -1,7 +1,7 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.connector.gc.GCParser; import android.content.Context; diff --git a/main/src/cgeo/geocaching/loaders/RemoveFromHistoryLoader.java b/main/src/cgeo/geocaching/loaders/RemoveFromHistoryLoader.java index 63b4620..2229afe 100644 --- a/main/src/cgeo/geocaching/loaders/RemoveFromHistoryLoader.java +++ b/main/src/cgeo/geocaching/loaders/RemoveFromHistoryLoader.java @@ -1,7 +1,7 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgData; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.geopoint.Geopoint; diff --git a/main/src/cgeo/geocaching/loaders/UsernameGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/UsernameGeocacheListLoader.java index 5af0fe3..f8849d5 100644 --- a/main/src/cgeo/geocaching/loaders/UsernameGeocacheListLoader.java +++ b/main/src/cgeo/geocaching/loaders/UsernameGeocacheListLoader.java @@ -1,7 +1,7 @@ package cgeo.geocaching.loaders; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.connector.gc.GCParser; import android.content.Context; diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index c876192..f892622 100644 --- a/main/src/cgeo/geocaching/maps/CGeoMap.java +++ b/main/src/cgeo/geocaching/maps/CGeoMap.java @@ -5,7 +5,6 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.IGeoData; import cgeo.geocaching.R; import cgeo.geocaching.SearchResult; -import cgeo.geocaching.Settings; import cgeo.geocaching.StoredList; import cgeo.geocaching.Waypoint; import cgeo.geocaching.cgData; @@ -14,9 +13,11 @@ import cgeo.geocaching.cgeocaches; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.connector.gc.Tile; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; import cgeo.geocaching.enumerations.LoadFlags; +import cgeo.geocaching.enumerations.LoadFlags.RemoveFlag; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.geopoint.Geopoint; @@ -30,6 +31,7 @@ import cgeo.geocaching.maps.interfaces.MapProvider; import cgeo.geocaching.maps.interfaces.MapSource; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OnMapDragListener; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.LiveMapInfoDialogBuilder; import cgeo.geocaching.utils.AngleUtils; import cgeo.geocaching.utils.CancellableHandler; @@ -70,6 +72,7 @@ import android.widget.ViewSwitcher.ViewFactory; import java.io.File; import java.util.ArrayList; import java.util.Collection; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -180,7 +183,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto /** Live mode enabled for map. **/ private boolean isLiveEnabled; // other things - private boolean liveChanged = false; // previous state for loadTimer + private boolean markersInvalidated = false; // previous state for loadTimer private boolean centered = false; // if map is already centered private boolean alreadyCentered = false; // -""- for setting my location private static Set<String> dirtyCaches = null; @@ -569,6 +572,13 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto item.setTitle(res.getString(R.string.map_live_enable)); } + item = menu.findItem(R.id.menu_mycaches_mode); // own & found caches + if (Settings.isExcludeMyCaches()) { + item.setTitle(res.getString(R.string.map_mycaches_show)); + } else { + item.setTitle(res.getString(R.string.map_mycaches_hide)); + } + final Set<String> geocodesInViewport = getGeocodesForCachesInViewport(); menu.findItem(R.id.menu_store_caches).setEnabled(!isLoading() && CollectionUtils.isNotEmpty(geocodesInViewport) && new SearchResult(geocodesInViewport).hasUnsavedCaches()); @@ -620,7 +630,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto if (mapMode == MapMode.LIVE) { Settings.setLiveMap(isLiveEnabled); } - liveChanged = true; + markersInvalidated = true; lastSearchResult = null; searchIntent = null; ActivityMixin.invalidateOptionsMenu(activity); @@ -668,6 +678,14 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto mapView.repaintRequired(overlayCaches); ActivityMixin.invalidateOptionsMenu(activity); return true; + case R.id.menu_mycaches_mode: + Settings.setExcludeMine(!Settings.isExcludeMyCaches()); + markersInvalidated = true; + ActivityMixin.invalidateOptionsMenu(activity); + if (!Settings.isExcludeMyCaches()) { + Tile.Cache.clear(); + } + return true; case R.id.menu_theme_mode: selectMapTheme(); return true; @@ -1016,7 +1034,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto // check if map moved or zoomed //TODO Portree Use Rectangle inside with bigger search window. That will stop reloading on every move - final boolean moved = liveChanged || (isLiveEnabled && !downloaded) || (viewport == null) || zoomNow != zoom || + final boolean moved = markersInvalidated || (isLiveEnabled && !downloaded) || (viewport == null) || zoomNow != zoom || (mapMoved(viewport, viewportNow) && (cachesCnt <= 0 || CollectionUtils.isEmpty(caches) || !viewport.includes(viewportNow))); // update title on any change @@ -1027,7 +1045,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto // save new values if (moved) { - liveChanged = false; + markersInvalidated = false; long currentTime = System.currentTimeMillis(); @@ -1082,8 +1100,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto } // live mode search result if (isLiveEnabled) { - SearchResult liveResult = new SearchResult(cgData.loadCachedInViewport(viewport, Settings.getCacheType())); - searchResult.addGeocodes(liveResult.getGeocodes()); + searchResult.addSearchResult(cgData.loadCachedInViewport(viewport, Settings.getCacheType())); } downloaded = true; @@ -1179,7 +1196,12 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto Set<Geocache> result = searchResult.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB); CGeoMap.filter(result); // update the caches - // new collection type needs to remove first + // first remove filtered out + final Set<String> filteredCodes = searchResult.getFilteredGeocodes(); + Log.d("Filtering out " + filteredCodes.size() + " caches: " + filteredCodes.toString()); + caches.removeAll(cgData.loadCaches(filteredCodes, LoadFlags.LOAD_CACHE_ONLY)); + cgData.removeCaches(filteredCodes, EnumSet.of(RemoveFlag.REMOVE_CACHE)); + // new collection type needs to remove first to refresh caches.removeAll(result); caches.addAll(result); lastSearchResult = searchResult; diff --git a/main/src/cgeo/geocaching/maps/CachesOverlay.java b/main/src/cgeo/geocaching/maps/CachesOverlay.java index f1dd9b3..8e117eb 100644 --- a/main/src/cgeo/geocaching/maps/CachesOverlay.java +++ b/main/src/cgeo/geocaching/maps/CachesOverlay.java @@ -4,7 +4,7 @@ import cgeo.geocaching.CachePopup; import cgeo.geocaching.Geocache; import cgeo.geocaching.IWaypoint; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.WaypointPopup; import cgeo.geocaching.cgData; import cgeo.geocaching.activity.Progress; diff --git a/main/src/cgeo/geocaching/maps/MapProviderFactory.java b/main/src/cgeo/geocaching/maps/MapProviderFactory.java index 5ce8ab6..95ea265 100644 --- a/main/src/cgeo/geocaching/maps/MapProviderFactory.java +++ b/main/src/cgeo/geocaching/maps/MapProviderFactory.java @@ -1,7 +1,7 @@ package cgeo.geocaching.maps; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.maps.google.GoogleMapProvider; import cgeo.geocaching.maps.interfaces.MapProvider; import cgeo.geocaching.maps.interfaces.MapSource; diff --git a/main/src/cgeo/geocaching/maps/PositionOverlay.java b/main/src/cgeo/geocaching/maps/PositionOverlay.java index 08acd2f..c3a0834 100644 --- a/main/src/cgeo/geocaching/maps/PositionOverlay.java +++ b/main/src/cgeo/geocaching/maps/PositionOverlay.java @@ -1,7 +1,7 @@ package cgeo.geocaching.maps; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.maps.interfaces.GeneralOverlay; import cgeo.geocaching.maps.interfaces.GeoPointImpl; diff --git a/main/src/cgeo/geocaching/maps/google/GoogleMapView.java b/main/src/cgeo/geocaching/maps/google/GoogleMapView.java index 154c3f3..6e5406e 100644 --- a/main/src/cgeo/geocaching/maps/google/GoogleMapView.java +++ b/main/src/cgeo/geocaching/maps/google/GoogleMapView.java @@ -2,7 +2,7 @@ package cgeo.geocaching.maps.google; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.maps.CachesOverlay; import cgeo.geocaching.maps.PositionOverlay; diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapProvider.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapProvider.java index 7cf18fb..7c6865d 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapProvider.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapProvider.java @@ -1,7 +1,7 @@ package cgeo.geocaching.maps.mapsforge; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.maps.AbstractMapProvider; import cgeo.geocaching.maps.MapProviderFactory; diff --git a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java index 083e5bb..2b0c1f7 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/MapsforgeMapView.java @@ -1,7 +1,7 @@ package cgeo.geocaching.maps.mapsforge; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.maps.CachesOverlay; import cgeo.geocaching.maps.PositionOverlay; diff --git a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java index cc8bc66..8e3a4d8 100644 --- a/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java +++ b/main/src/cgeo/geocaching/maps/mapsforge/v024/MapsforgeMapView024.java @@ -1,7 +1,7 @@ package cgeo.geocaching.maps.mapsforge.v024; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.geopoint.Viewport; import cgeo.geocaching.maps.CachesOverlay; import cgeo.geocaching.maps.PositionOverlay; diff --git a/main/src/cgeo/geocaching/network/Network.java b/main/src/cgeo/geocaching/network/Network.java index 57196c5..2d2ab0b 100644 --- a/main/src/cgeo/geocaching/network/Network.java +++ b/main/src/cgeo/geocaching/network/Network.java @@ -1,9 +1,9 @@ package cgeo.geocaching.network; -import cgeo.geocaching.Settings; import cgeo.geocaching.files.LocalStorage; -import cgeo.geocaching.utils.TextUtils; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.TextUtils; import ch.boye.httpclientandroidlib.Header; import ch.boye.httpclientandroidlib.HeaderElement; @@ -317,12 +317,14 @@ public abstract class Network { return null; } - final String etag = LocalStorage.getSavedHeader(cacheFile, "etag"); + final String etag = LocalStorage.getSavedHeader(cacheFile, LocalStorage.HEADER_ETAG); if (etag != null) { + // The ETag is a more robust check than a timestamp. If we have an ETag, it is enough + // to identify the right version of the resource. return new Parameters("If-None-Match", etag); } - final String lastModified = LocalStorage.getSavedHeader(cacheFile, "last-modified"); + final String lastModified = LocalStorage.getSavedHeader(cacheFile, LocalStorage.HEADER_LAST_MODIFIED); if (lastModified != null) { return new Parameters("If-Modified-Since", lastModified); } @@ -476,10 +478,10 @@ public abstract class Network { /** * Checks if the device has network connection. - * + * * @param context * context of the application, cannot be null - * + * * @return <code>true</code> if the device is connected to the network. */ public static boolean isNetworkConnected(Context context) { diff --git a/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java b/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java index 751443e..8dc8b03 100644 --- a/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/network/OAuthAuthorizationActivity.java @@ -82,6 +82,7 @@ public abstract class OAuthAuthorizationActivity extends AbstractActivity { pinEntryButton.setVisibility(View.GONE); + setResult(RESULT_OK); finish(); } else { showToast(getErrAuthProcess()); diff --git a/main/src/cgeo/geocaching/network/StatusUpdater.java b/main/src/cgeo/geocaching/network/StatusUpdater.java index bfc77ba..ee9bc31 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://faq.cgeo.org/#7_69"); + + final static public Status defaultStatus() { + return VERSION.SDK_INT < VERSION_CODES.ECLAIR_MR1 ? closeoutStatus : null; + } } @Override diff --git a/main/src/cgeo/geocaching/settings/AuthorizeOcDePreference.java b/main/src/cgeo/geocaching/settings/AuthorizeOcDePreference.java new file mode 100644 index 0000000..28807eb --- /dev/null +++ b/main/src/cgeo/geocaching/settings/AuthorizeOcDePreference.java @@ -0,0 +1,45 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.connector.oc.OCAuthorizationActivity; + +import android.content.Context; +import android.content.Intent; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +public class AuthorizeOcDePreference extends Preference { + + public AuthorizeOcDePreference(Context context) { + super(context); + } + + public AuthorizeOcDePreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public AuthorizeOcDePreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected View onCreateView(ViewGroup parent) { + final SettingsActivity activity = (SettingsActivity) getContext(); + + setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + Intent authIntent = new Intent(preference.getContext(), + OCAuthorizationActivity.class); + activity.startActivityForResult(authIntent, + SettingsActivity.OAUTH_OCDE_REQUEST); + + return false; // no shared preference has to be changed + } + }); + + activity.setOCDEAuthTitle(); + return super.onCreateView(parent); + } +} diff --git a/main/src/cgeo/geocaching/settings/AuthorizeTwitterPreference.java b/main/src/cgeo/geocaching/settings/AuthorizeTwitterPreference.java new file mode 100644 index 0000000..ed3e159 --- /dev/null +++ b/main/src/cgeo/geocaching/settings/AuthorizeTwitterPreference.java @@ -0,0 +1,45 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.twitter.TwitterAuthorizationActivity; + +import android.content.Context; +import android.content.Intent; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +public class AuthorizeTwitterPreference extends Preference { + + public AuthorizeTwitterPreference(Context context) { + super(context); + } + + public AuthorizeTwitterPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public AuthorizeTwitterPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected View onCreateView(ViewGroup parent) { + final SettingsActivity activity = (SettingsActivity) getContext(); + + setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + Intent authIntent = new Intent(preference.getContext(), + TwitterAuthorizationActivity.class); + activity.startActivityForResult(authIntent, + SettingsActivity.OAUTH_TWITTER_REQUEST); + + return false; // no shared preference has to be changed + } + }); + + activity.setTwitterAuthTitle(); + return super.onCreateView(parent); + } +} diff --git a/main/src/cgeo/geocaching/settings/CheckBoxWithPopupPreference.java b/main/src/cgeo/geocaching/settings/CheckBoxWithPopupPreference.java new file mode 100644 index 0000000..4e64b9a --- /dev/null +++ b/main/src/cgeo/geocaching/settings/CheckBoxWithPopupPreference.java @@ -0,0 +1,93 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.R; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.TypedArray; +import android.net.Uri; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +public class CheckBoxWithPopupPreference extends CheckBoxPreference { + + // strings for the popup dialog + private String title; + private String text; + private String url; + private String urlButton; + + public CheckBoxWithPopupPreference(Context context) { + super(context); + } + + public CheckBoxWithPopupPreference(Context context, AttributeSet attrs) { + super(context, attrs); + processAttributes(context, attrs, 0); + } + + public CheckBoxWithPopupPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + processAttributes(context, attrs, defStyle); + } + + private void processAttributes(Context context, AttributeSet attrs, int defStyle) { + if (attrs == null) { + return; // coward's retreat + } + + TypedArray types = context.obtainStyledAttributes(attrs, new int[] { + R.attr.title, R.attr.text, R.attr.url, R.attr.urlButton }, + defStyle, 0); + + title = types.getString(0); + text = types.getString(1); + url = types.getString(2); + urlButton = types.getString(3); + + types.recycle(); + } + + @Override + protected View onCreateView(ViewGroup parent) { + + // show dialog when checkbox enabled + setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(final Preference preference, Object newValue) { + if (!(Boolean) newValue) { + return true; + } + AlertDialog.Builder builder = new AlertDialog.Builder( + preference.getContext()); + builder.setMessage(text) + .setIcon(android.R.drawable.ic_dialog_info) + .setTitle(title) + .setPositiveButton(R.string.err_none, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }) + .setNegativeButton(urlButton, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + preference.getContext().startActivity(i); + } + }); + builder.create().show(); + return true; + } + }); + + return super.onCreateView(parent); + } + +} diff --git a/main/src/cgeo/geocaching/settings/CheckGcCredentialsPreference.java b/main/src/cgeo/geocaching/settings/CheckGcCredentialsPreference.java new file mode 100644 index 0000000..3ecd1b2 --- /dev/null +++ b/main/src/cgeo/geocaching/settings/CheckGcCredentialsPreference.java @@ -0,0 +1,123 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.R; +import cgeo.geocaching.activity.ActivityMixin; +import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.network.Cookies; +import cgeo.geocaching.utils.Log; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import android.annotation.SuppressLint; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +public class CheckGcCredentialsPreference extends Preference { + + public CheckGcCredentialsPreference(Context context) { + super(context); + } + + public CheckGcCredentialsPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CheckGcCredentialsPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected View onCreateView(ViewGroup parent) { + setOnPreferenceClickListener(GC_LOGIN_CHECK); + return super.onCreateView(parent); + } + + private final GcLoginCheck GC_LOGIN_CHECK = new GcLoginCheck(); + + private class GcLoginCheck implements OnPreferenceClickListener { + private Resources res; + private SettingsActivity activity; + + private ProgressDialog loginDialog; + @SuppressLint("HandlerLeak") + private Handler logInHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + try { + if (loginDialog != null && loginDialog.isShowing()) { + loginDialog.dismiss(); + } + + if (msg.obj == null || (msg.obj instanceof Drawable)) { + ActivityMixin.helpDialog(activity, + res.getString(R.string.init_login_popup), + res.getString(R.string.init_login_popup_ok), + (Drawable) msg.obj); + } else { + ActivityMixin.helpDialog(activity, + res.getString(R.string.init_login_popup), + res.getString(R.string.init_login_popup_failed_reason) + + " " + + ((StatusCode) msg.obj).getErrorString(res) + + "."); + } + } catch (Exception e) { + ActivityMixin.showToast(activity, R.string.err_login_failed); + Log.e("SettingsActivity.logInHandler", e); + } finally { + if (loginDialog != null && loginDialog.isShowing()) { + loginDialog.dismiss(); + } + // enable/disable basic member preferences + activity.initBasicMemberPreferences(); + } + } + }; + + @Override + public boolean onPreferenceClick(Preference preference) { + this.activity = (SettingsActivity) CheckGcCredentialsPreference.this.getContext(); + this.res = activity.getResources(); + + ImmutablePair<String, String> credentials = Settings.getGcLogin(); + + // check credentials for validity + if (credentials == null || StringUtils.isBlank(credentials.getLeft()) + || StringUtils.isBlank(credentials.getRight())) { + ActivityMixin.showToast(activity, R.string.err_missing_auth); + return false; + } + + loginDialog = ProgressDialog.show(activity, + res.getString(R.string.init_login_popup), + res.getString(R.string.init_login_popup_working), true); + loginDialog.setCancelable(false); + Cookies.clearCookies(); + + (new Thread() { + @Override + public void run() { + final StatusCode loginResult = Login.login(); + Object payload = loginResult; + if (loginResult == StatusCode.NO_ERROR) { + Login.detectGcCustomDate(); + payload = Login.downloadAvatarAndGetMemberStatus(); + } + logInHandler.obtainMessage(0, payload).sendToTarget(); + } + }).start(); + + return false; // no shared preference has to be changed + } + } +} diff --git a/main/src/cgeo/geocaching/settings/EditPasswordPreference.java b/main/src/cgeo/geocaching/settings/EditPasswordPreference.java new file mode 100644 index 0000000..d89f128 --- /dev/null +++ b/main/src/cgeo/geocaching/settings/EditPasswordPreference.java @@ -0,0 +1,29 @@ +package cgeo.geocaching.settings; + +import android.content.Context; +import android.preference.EditTextPreference; +import android.util.AttributeSet; + +/** + * This is just a dummy preference, to be able check for the type. + * <p> + * Use it exactly as an EditTextPreference + * + * @see SettingsActivity - search for EditPasswordPreference + * @author koem + */ +public class EditPasswordPreference extends EditTextPreference { + + public EditPasswordPreference(Context context) { + super(context); + } + + public EditPasswordPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public EditPasswordPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + +} diff --git a/main/src/cgeo/geocaching/settings/InfoPreference.java b/main/src/cgeo/geocaching/settings/InfoPreference.java new file mode 100644 index 0000000..ea740b4 --- /dev/null +++ b/main/src/cgeo/geocaching/settings/InfoPreference.java @@ -0,0 +1,107 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.R; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.TypedArray; +import android.net.Uri; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; + +public class InfoPreference extends Preference { + + // strings for the popup dialog + private String text; + private String url; + private String urlButton; + + private LayoutInflater inflater; + + public InfoPreference(Context context) { + super(context); + init(context, null, 0); + } + + public InfoPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs, 0); + } + + public InfoPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context, attrs, defStyle); + } + + private void init(Context context, AttributeSet attrs, int defStyle) { + inflater = ((Activity) context).getLayoutInflater(); + + setPersistent(false); + + if (attrs == null) { + return; // coward's retreat + } + + TypedArray types = context.obtainStyledAttributes(attrs, new int[] { + android.R.attr.text, R.attr.url, R.attr.urlButton }, + defStyle, 0); + + text = types.getString(0); + url = types.getString(1); + urlButton = types.getString(2); + + types.recycle(); + } + + @Override + protected View onCreateView(ViewGroup parent) { + + // show popup when clicked + setOnPreferenceClickListener(new OnPreferenceClickListener() { + + @Override + public boolean onPreferenceClick(final Preference preference) { + AlertDialog.Builder builder = new AlertDialog.Builder( + preference.getContext()); + builder.setMessage(text) + .setIcon(android.R.drawable.ic_dialog_info) + .setTitle(preference.getTitle()) + .setPositiveButton(R.string.err_none, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }) + .setNegativeButton(urlButton, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + preference.getContext().startActivity(i); + } + }); + builder.create().show(); + return false; + } + }); + + // show an Info Icon + View v = super.onCreateView(parent); + + ImageView i = (ImageView) inflater.inflate(R.layout.preference_info_icon, parent, false); + LinearLayout l = (LinearLayout) v.findViewById(android.R.id.widget_frame); + l.setVisibility(View.VISIBLE); + l.addView(i); + + return v; + } + +} diff --git a/main/src/cgeo/geocaching/settings/LogSignaturePreference.java b/main/src/cgeo/geocaching/settings/LogSignaturePreference.java new file mode 100644 index 0000000..d0c9739 --- /dev/null +++ b/main/src/cgeo/geocaching/settings/LogSignaturePreference.java @@ -0,0 +1,60 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.R; + +import android.content.Context; +import android.preference.DialogPreference; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +public class LogSignaturePreference extends DialogPreference { + + private SettingsActivity settingsActivity; + private EditText editText; + + public LogSignaturePreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public LogSignaturePreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + setDialogLayoutResource(R.layout.log_signature_preference_dialog); + } + + @Override + protected void onBindDialogView(View view) { + settingsActivity = (SettingsActivity) this.getContext(); + + editText = (EditText) view.findViewById(R.id.signature_dialog_text); + editText.setText(getPersistedString("")); + settingsActivity.setSignatureTextView(editText); + + Button templates = (Button) view.findViewById(R.id.signature_templates); + settingsActivity.registerForContextMenu(templates); + templates.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View templates) { + settingsActivity.openContextMenu(templates); + } + }); + + super.onBindDialogView(view); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + if (positiveResult) { + String text = editText.getText().toString(); + persistString(text); + callChangeListener(text); + } + super.onDialogClosed(positiveResult); + } +} diff --git a/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java b/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java new file mode 100644 index 0000000..a019c4a --- /dev/null +++ b/main/src/cgeo/geocaching/settings/RegisterSend2CgeoPreference.java @@ -0,0 +1,122 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.R; +import cgeo.geocaching.activity.ActivityMixin; +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.utils.Log; + +import ch.boye.httpclientandroidlib.HttpResponse; + +import org.apache.commons.lang3.StringUtils; + +import android.app.ProgressDialog; +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +public class RegisterSend2CgeoPreference extends Preference { + + ProgressDialog progressDialog; + SettingsActivity activity; + + public RegisterSend2CgeoPreference(Context context) { + super(context); + } + + public RegisterSend2CgeoPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public RegisterSend2CgeoPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + private Handler webAuthHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + try { + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + + if (msg.what > 0) { + ActivityMixin.helpDialog(activity, + activity.getString(R.string.init_sendToCgeo), + activity.getString(R.string.init_sendToCgeo_register_ok) + .replace("####", String.valueOf(msg.what))); + } else { + ActivityMixin.helpDialog(activity, + activity.getString(R.string.init_sendToCgeo), + activity.getString(R.string.init_sendToCgeo_register_fail)); + } + } catch (Exception e) { + ActivityMixin.showToast(activity, R.string.init_sendToCgeo_register_fail); + Log.e("SettingsActivity.webHandler", e); + } + + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + } + }; + + @Override + protected View onCreateView(ViewGroup parent) { + activity = (SettingsActivity) getContext(); + + setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + final String deviceName = Settings.getWebDeviceName(); + final String deviceCode = Settings.getWebDeviceCode(); + + if (StringUtils.isBlank(deviceName)) { + ActivityMixin.showToast(activity, R.string.err_missing_device_name); + return false; + } + + progressDialog = ProgressDialog.show(activity, + activity.getString(R.string.init_sendToCgeo), + activity.getString(R.string.init_sendToCgeo_registering), true); + progressDialog.setCancelable(false); + + (new Thread() { + + @Override + public void run() { + int pin = 0; + + final String nam = StringUtils.defaultString(deviceName); + final String cod = StringUtils.defaultString(deviceCode); + + final Parameters params = new Parameters("name", nam, "code", cod); + HttpResponse response = Network.getRequest("http://send2.cgeo.org/auth.html", params); + + if (response != null && response.getStatusLine().getStatusCode() == 200) { + //response was OK + String[] strings = Network.getResponseData(response).split(","); + try { + pin = Integer.parseInt(strings[1].trim()); + } catch (Exception e) { + Log.e("webDialog", e); + } + String code = strings[0]; + Settings.setWebNameCode(nam, code); + } + + webAuthHandler.sendEmptyMessage(pin); + } + }).start(); + + return true; + } + }); + return super.onCreateView(parent); + } + +} diff --git a/main/src/cgeo/geocaching/settings/Settings.java b/main/src/cgeo/geocaching/settings/Settings.java new file mode 100644 index 0000000..244c924 --- /dev/null +++ b/main/src/cgeo/geocaching/settings/Settings.java @@ -0,0 +1,995 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.R; +import cgeo.geocaching.StoredList; +import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.apps.cache.navi.NavigationAppFactory.NavigationAppsEnum; +import cgeo.geocaching.connector.gc.GCConstants; +import cgeo.geocaching.connector.gc.Login; +import cgeo.geocaching.enumerations.CacheType; +import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; +import cgeo.geocaching.enumerations.LogType; +import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.maps.MapProviderFactory; +import cgeo.geocaching.maps.google.GoogleMapProvider; +import cgeo.geocaching.maps.interfaces.GeoPointImpl; +import cgeo.geocaching.maps.interfaces.MapProvider; +import cgeo.geocaching.maps.interfaces.MapSource; +import cgeo.geocaching.maps.mapsforge.MapsforgeMapProvider; +import cgeo.geocaching.maps.mapsforge.MapsforgeMapProvider.OfflineMapSource; +import cgeo.geocaching.utils.CryptUtils; +import cgeo.geocaching.utils.FileUtils; +import cgeo.geocaching.utils.FileUtils.FileSelector; +import cgeo.geocaching.utils.Log; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Environment; +import android.preference.PreferenceManager; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * General c:geo preferences/settings set by the user + */ +public final class Settings { + + public static final int SHOW_WP_THRESHOLD_DEFAULT = 5; + public static final int SHOW_WP_THRESHOLD_MAX = 50; + private static final int MAP_SOURCE_DEFAULT = GoogleMapProvider.GOOGLE_MAP_ID.hashCode(); + + private final static int unitsMetric = 1; + + // twitter api keys + private final static String keyConsumerPublic = CryptUtils.rot13("ESnsCvAv3kEupF1GCR3jGj"); + private final static String keyConsumerSecret = CryptUtils.rot13("7vQWceACV9umEjJucmlpFe9FCMZSeqIqfkQ2BnhV9x"); + + public enum coordInputFormatEnum { + Plain, + Deg, + Min, + Sec; + + public static coordInputFormatEnum fromInt(int id) { + final coordInputFormatEnum[] values = coordInputFormatEnum.values(); + if (id < 0 || id >= values.length) { + return Min; + } + return values[id]; + } + } + + private static final SharedPreferences sharedPrefs = PreferenceManager + .getDefaultSharedPreferences(cgeoapplication.getInstance().getBaseContext()); + static { + migrateSettings(); + Log.setDebug(sharedPrefs.getBoolean(getKey(R.string.pref_debug), false)); + } + + // maps + private static MapProvider mapProvider = null; + + private Settings() { + // this class is not to be instantiated; + } + + private static void migrateSettings() { + // migrate from non standard file location and integer based boolean types + int oldVersion = getInt(R.string.pref_settingsversion, 0); + if (oldVersion < 1) { + final String oldPreferencesName = "cgeo.pref"; + final SharedPreferences old = cgeoapplication.getInstance().getSharedPreferences(oldPreferencesName, Context.MODE_PRIVATE); + final Editor e = sharedPrefs.edit(); + + e.putString(getKey(R.string.pref_temp_twitter_token_secret), old.getString(getKey(R.string.pref_temp_twitter_token_secret), null)); + e.putString(getKey(R.string.pref_temp_twitter_token_public), old.getString(getKey(R.string.pref_temp_twitter_token_public), null)); + e.putBoolean(getKey(R.string.pref_help_shown), old.getInt(getKey(R.string.pref_help_shown), 0) != 0); + e.putFloat(getKey(R.string.pref_anylongitude), old.getFloat(getKey(R.string.pref_anylongitude), 0)); + e.putFloat(getKey(R.string.pref_anylatitude), old.getFloat(getKey(R.string.pref_anylatitude), 0)); + e.putBoolean(getKey(R.string.pref_offlinemaps), 0 != old.getInt(getKey(R.string.pref_offlinemaps), 1)); + e.putBoolean(getKey(R.string.pref_offlinewpmaps), 0 != old.getInt(getKey(R.string.pref_offlinewpmaps), 0)); + e.putString(getKey(R.string.pref_webDeviceCode), old.getString(getKey(R.string.pref_webDeviceCode), null)); + e.putString(getKey(R.string.pref_webDeviceName), old.getString(getKey(R.string.pref_webDeviceName), null)); + e.putBoolean(getKey(R.string.pref_maplive), old.getInt(getKey(R.string.pref_maplive), 1) != 0); + e.putInt(getKey(R.string.pref_mapsource), old.getInt(getKey(R.string.pref_mapsource), MAP_SOURCE_DEFAULT)); + e.putBoolean(getKey(R.string.pref_twitter), 0 != old.getInt(getKey(R.string.pref_twitter), 0)); + e.putBoolean(getKey(R.string.pref_showaddress), 0 != old.getInt(getKey(R.string.pref_showaddress), 1)); + e.putBoolean(getKey(R.string.pref_showcaptcha), old.getBoolean(getKey(R.string.pref_showcaptcha), false)); + e.putBoolean(getKey(R.string.pref_maptrail), old.getInt(getKey(R.string.pref_maptrail), 1) != 0); + e.putInt(getKey(R.string.pref_lastmapzoom), old.getInt(getKey(R.string.pref_lastmapzoom), 14)); + e.putBoolean(getKey(R.string.pref_livelist), 0 != old.getInt(getKey(R.string.pref_livelist), 1)); + e.putBoolean(getKey(R.string.pref_units), old.getInt(getKey(R.string.pref_units), unitsMetric) == unitsMetric); + e.putBoolean(getKey(R.string.pref_skin), old.getInt(getKey(R.string.pref_skin), 0) != 0); + e.putInt(getKey(R.string.pref_lastusedlist), old.getInt(getKey(R.string.pref_lastusedlist), StoredList.STANDARD_LIST_ID)); + e.putString(getKey(R.string.pref_cachetype), old.getString(getKey(R.string.pref_cachetype), CacheType.ALL.id)); + e.putString(getKey(R.string.pref_twitter_token_secret), old.getString(getKey(R.string.pref_twitter_token_secret), null)); + e.putString(getKey(R.string.pref_twitter_token_public), old.getString(getKey(R.string.pref_twitter_token_public), null)); + e.putInt(getKey(R.string.pref_version), old.getInt(getKey(R.string.pref_version), 0)); + e.putBoolean(getKey(R.string.pref_autoloaddesc), 0 != old.getInt(getKey(R.string.pref_autoloaddesc), 1)); + e.putBoolean(getKey(R.string.pref_ratingwanted), old.getBoolean(getKey(R.string.pref_ratingwanted), true)); + e.putBoolean(getKey(R.string.pref_friendlogswanted), old.getBoolean(getKey(R.string.pref_friendlogswanted), true)); + e.putBoolean(getKey(R.string.pref_useenglish), old.getBoolean(getKey(R.string.pref_useenglish), false)); + e.putBoolean(getKey(R.string.pref_usecompass), 0 != old.getInt(getKey(R.string.pref_usecompass), 1)); + e.putBoolean(getKey(R.string.pref_trackautovisit), old.getBoolean(getKey(R.string.pref_trackautovisit), false)); + e.putBoolean(getKey(R.string.pref_sigautoinsert), old.getBoolean(getKey(R.string.pref_sigautoinsert), false)); + e.putBoolean(getKey(R.string.pref_logimages), old.getBoolean(getKey(R.string.pref_logimages), false)); + e.putBoolean(getKey(R.string.pref_excludedisabled), 0 != old.getInt(getKey(R.string.pref_excludedisabled), 0)); + e.putBoolean(getKey(R.string.pref_excludemine), 0 != old.getInt(getKey(R.string.pref_excludemine), 0)); + e.putString(getKey(R.string.pref_mapfile), old.getString(getKey(R.string.pref_mapfile), null)); + e.putString(getKey(R.string.pref_signature), old.getString(getKey(R.string.pref_signature), null)); + e.putString(getKey(R.string.pref_pass_vote), old.getString(getKey(R.string.pref_pass_vote), null)); + e.putString(getKey(R.string.pref_password), old.getString(getKey(R.string.pref_password), null)); + e.putString(getKey(R.string.pref_username), old.getString(getKey(R.string.pref_username), null)); + e.putString(getKey(R.string.pref_memberstatus), old.getString(getKey(R.string.pref_memberstatus), "")); + e.putInt(getKey(R.string.pref_coordinputformat), old.getInt(getKey(R.string.pref_coordinputformat), 0)); + e.putBoolean(getKey(R.string.pref_log_offline), old.getBoolean(getKey(R.string.pref_log_offline), false)); + e.putBoolean(getKey(R.string.pref_choose_list), old.getBoolean(getKey(R.string.pref_choose_list), false)); + e.putBoolean(getKey(R.string.pref_loaddirectionimg), old.getBoolean(getKey(R.string.pref_loaddirectionimg), true)); + e.putString(getKey(R.string.pref_gccustomdate), old.getString(getKey(R.string.pref_gccustomdate), null)); + e.putInt(getKey(R.string.pref_gcshowwaypointsthreshold), old.getInt(getKey(R.string.pref_gcshowwaypointsthreshold), 0)); + e.putString(getKey(R.string.pref_cookiestore), old.getString(getKey(R.string.pref_cookiestore), null)); + e.putBoolean(getKey(R.string.pref_opendetailslastpage), old.getBoolean(getKey(R.string.pref_opendetailslastpage), false)); + e.putInt(getKey(R.string.pref_lastdetailspage), old.getInt(getKey(R.string.pref_lastdetailspage), 1)); + e.putInt(getKey(R.string.pref_defaultNavigationTool), old.getInt(getKey(R.string.pref_defaultNavigationTool), NavigationAppsEnum.COMPASS.id)); + e.putInt(getKey(R.string.pref_defaultNavigationTool2), old.getInt(getKey(R.string.pref_defaultNavigationTool2), NavigationAppsEnum.INTERNAL_MAP.id)); + e.putInt(getKey(R.string.pref_livemapstrategy), old.getInt(getKey(R.string.pref_livemapstrategy), Strategy.AUTO.id)); + e.putBoolean(getKey(R.string.pref_debug), old.getBoolean(getKey(R.string.pref_debug), false)); + e.putBoolean(getKey(R.string.pref_hidelivemaphint), old.getInt(getKey(R.string.pref_hidelivemaphint), 0) != 0); + e.putInt(getKey(R.string.pref_livemaphintshowcount), old.getInt(getKey(R.string.pref_livemaphintshowcount), 0)); + + e.putInt(getKey(R.string.pref_settingsversion), 1); // mark migrated + e.commit(); + } + + // changes for new settings dialog + if (oldVersion < 2) { + final Editor e = sharedPrefs.edit(); + + e.putBoolean(getKey(R.string.pref_units), !isUseImperialUnits()); + + // show waypoints threshold now as a slider + int wpThreshold = getWayPointsThreshold(); + if (wpThreshold < 0) { + wpThreshold = 0; + } else if (wpThreshold > SHOW_WP_THRESHOLD_MAX) { + wpThreshold = SHOW_WP_THRESHOLD_MAX; + } + e.putInt(getKey(R.string.pref_gcshowwaypointsthreshold), wpThreshold); + + // KEY_MAP_SOURCE must be string, because it is the key for a ListPreference now + int ms = sharedPrefs.getInt(getKey(R.string.pref_mapsource), MAP_SOURCE_DEFAULT); + e.remove(getKey(R.string.pref_mapsource)); + e.putString(getKey(R.string.pref_mapsource), String.valueOf(ms)); + + // navigation tool ids must be string, because ListPreference uses strings as keys + int dnt1 = sharedPrefs.getInt(getKey(R.string.pref_defaultNavigationTool), NavigationAppsEnum.COMPASS.id); + int dnt2 = sharedPrefs.getInt(getKey(R.string.pref_defaultNavigationTool2), NavigationAppsEnum.INTERNAL_MAP.id); + e.remove(getKey(R.string.pref_defaultNavigationTool)); + e.remove(getKey(R.string.pref_defaultNavigationTool2)); + e.putString(getKey(R.string.pref_defaultNavigationTool), String.valueOf(dnt1)); + e.putString(getKey(R.string.pref_defaultNavigationTool2), String.valueOf(dnt2)); + + // defaults for gpx directories + e.putString(getKey(R.string.pref_gpxImportDir), getGpxImportDir()); + e.putString(getKey(R.string.pref_gpxExportDir), getGpxExportDir()); + + e.putInt(getKey(R.string.pref_settingsversion), 2); // mark migrated + e.commit(); + } + } + + private static String getKey(final int prefKeyId) { + return cgeoapplication.getInstance().getString(prefKeyId); + } + + static String getString(final int prefKeyId, final String defaultValue) { + return sharedPrefs.getString(getKey(prefKeyId), defaultValue); + } + + private static int getInt(final int prefKeyId, final int defaultValue) { + return sharedPrefs.getInt(getKey(prefKeyId), defaultValue); + } + + private static long getLong(final int prefKeyId, final long defaultValue) { + return sharedPrefs.getLong(getKey(prefKeyId), defaultValue); + } + + private static boolean getBoolean(final int prefKeyId, final boolean defaultValue) { + return sharedPrefs.getBoolean(getKey(prefKeyId), defaultValue); + } + + private static float getFloat(final int prefKeyId, final float defaultValue) { + return sharedPrefs.getFloat(getKey(prefKeyId), defaultValue); + } + + static boolean putString(final int prefKeyId, final String value) { + final SharedPreferences.Editor edit = sharedPrefs.edit(); + edit.putString(getKey(prefKeyId), value); + return edit.commit(); + } + + private static boolean putBoolean(final int prefKeyId, final boolean value) { + final SharedPreferences.Editor edit = sharedPrefs.edit(); + edit.putBoolean(getKey(prefKeyId), value); + return edit.commit(); + } + + private static boolean putInt(final int prefKeyId, final int value) { + final SharedPreferences.Editor edit = sharedPrefs.edit(); + edit.putInt(getKey(prefKeyId), value); + return edit.commit(); + } + + private static boolean putLong(final int prefKeyId, final long value) { + final SharedPreferences.Editor edit = sharedPrefs.edit(); + edit.putLong(getKey(prefKeyId), value); + return edit.commit(); + } + + private static boolean putFloat(final int prefKeyId, final float value) { + final SharedPreferences.Editor edit = sharedPrefs.edit(); + edit.putFloat(getKey(prefKeyId), value); + return edit.commit(); + } + + private static boolean remove(final int prefKeyId) { + final SharedPreferences.Editor edit = sharedPrefs.edit(); + edit.remove(getKey(prefKeyId)); + return edit.commit(); + } + + private static boolean contains(final int prefKeyId) { + return sharedPrefs.contains(getKey(prefKeyId)); + } + + public static void setLanguage(boolean useEnglish) { + final Configuration config = new Configuration(); + config.locale = useEnglish ? Locale.ENGLISH : Locale.getDefault(); + final Resources resources = cgeoapplication.getInstance().getResources(); + resources.updateConfiguration(config, resources.getDisplayMetrics()); + } + + public static boolean isLogin() { + final String preUsername = getString(R.string.pref_username, null); + final String prePassword = getString(R.string.pref_password, null); + + return !StringUtils.isBlank(preUsername) && !StringUtils.isBlank(prePassword); + } + + /** + * Get login and password information. + * + * @return a pair (login, password) or null if no valid information is stored + */ + public static ImmutablePair<String, String> getGcLogin() { + + final String username = getString(R.string.pref_username, null); + final String password = getString(R.string.pref_password, null); + + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { + return null; + } + + return new ImmutablePair<String, String>(username, password); + } + + public static String getUsername() { + return getString(R.string.pref_username, null); + } + + public static boolean isGCConnectorActive() { + return getBoolean(R.string.pref_connectorGCActive, true); + } + + public static boolean isPremiumMember() { + // Basic Member, Premium Member, ??? + String memberStatus = Settings.getMemberStatus(); + if (memberStatus == null) { + return false; + } + return GCConstants.MEMBER_STATUS_PM.equalsIgnoreCase(memberStatus); + } + + public static String getMemberStatus() { + return getString(R.string.pref_memberstatus, ""); + } + + public static boolean setMemberStatus(final String memberStatus) { + if (StringUtils.isBlank(memberStatus)) { + return remove(R.string.pref_memberstatus); + } + return putString(R.string.pref_memberstatus, memberStatus); + } + + public static boolean isOCConnectorActive() { + return getBoolean(R.string.pref_connectorOCActive, false); + } + + public static boolean setOCConnectorActive(final boolean isActive) { + return putBoolean(R.string.pref_connectorOCActive, isActive); + } + + public static String getOCDETokenPublic() { + return getString(R.string.pref_ocde_tokenpublic, ""); + } + + public static String getOCDETokenSecret() { + return getString(R.string.pref_ocde_tokensecret, ""); + } + + public static boolean hasOCDEAuthorization() { + return StringUtils.isNotBlank(getOCDETokenPublic()) + && StringUtils.isNotBlank(getOCDETokenSecret()); + } + + public static void setOCDETokens(final String tokenPublic, + final String tokenSecret, boolean enableOcDe) { + putString(R.string.pref_ocde_tokenpublic, tokenPublic); + putString(R.string.pref_ocde_tokensecret, tokenSecret); + if (tokenPublic != null) { + remove(R.string.pref_temp_ocde_token_public); + remove(R.string.pref_temp_ocde_token_secret); + } + setOCConnectorActive(enableOcDe); + } + + public static void setOCDETempTokens(final String tokenPublic, final String tokenSecret) { + putString(R.string.pref_temp_ocde_token_public, tokenPublic); + putString(R.string.pref_temp_ocde_token_secret, tokenSecret); + } + + public static ImmutablePair<String, String> getTempOCDEToken() { + String tokenPublic = getString(R.string.pref_temp_ocde_token_public, null); + String tokenSecret = getString(R.string.pref_temp_ocde_token_secret, null); + return new ImmutablePair<String, String>(tokenPublic, tokenSecret); + } + + public static boolean isGCvoteLogin() { + final String preUsername = getString(R.string.pref_username, null); + final String prePassword = getString(R.string.pref_pass_vote, null); + + return !StringUtils.isBlank(preUsername) && !StringUtils.isBlank(prePassword); + } + + public static ImmutablePair<String, String> getGCvoteLogin() { + final String username = getString(R.string.pref_username, null); + final String password = getString(R.string.pref_pass_vote, null); + + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { + return null; + } + + return new ImmutablePair<String, String>(username, password); + } + + public static String getSignature() { + return getString(R.string.pref_signature, null); + } + + public static boolean setCookieStore(final String cookies) { + if (StringUtils.isBlank(cookies)) { + // erase cookies + return remove(R.string.pref_cookiestore); + } + // save cookies + return putString(R.string.pref_cookiestore, cookies); + } + + public static String getCookieStore() { + return getString(R.string.pref_cookiestore, null); + } + + /** + * @param cacheType + * The cache type used for future filtering + */ + public static void setCacheType(final CacheType cacheType) { + if (cacheType == null) { + remove(R.string.pref_cachetype); + } else { + putString(R.string.pref_cachetype, cacheType.id); + } + } + + public static int getLastList() { + return getInt(R.string.pref_lastusedlist, StoredList.STANDARD_LIST_ID); + } + + public static void saveLastList(final int listId) { + putInt(R.string.pref_lastusedlist, listId); + } + + public static void setWebNameCode(final String name, final String code) { + putString(R.string.pref_webDeviceName, name); + putString(R.string.pref_webDeviceCode, code); + } + + public static MapProvider getMapProvider() { + if (mapProvider == null) { + mapProvider = getMapSource().getMapProvider(); + } + return mapProvider; + } + + public static String getMapFile() { + return getString(R.string.pref_mapfile, null); + } + + public static boolean setMapFile(final String mapFile) { + boolean result = putString(R.string.pref_mapfile, mapFile); + if (mapFile != null) { + setMapFileDirectory(new File(mapFile).getParent()); + } + return result; + } + + public static String getMapFileDirectory() { + final String mapDir = getString(R.string.pref_mapDirectory, null); + if (mapDir != null) { + return mapDir; + } + final String mapFile = getMapFile(); + if (mapFile != null) { + return new File(mapFile).getParent(); + } + return null; + } + + public static boolean setMapFileDirectory(final String mapFileDirectory) { + boolean result = putString(R.string.pref_mapDirectory, mapFileDirectory); + MapsforgeMapProvider.getInstance().updateOfflineMaps(); + return result; + } + + public static boolean isValidMapFile() { + return isValidMapFile(getMapFile()); + } + + public static boolean isValidMapFile(final String mapFileIn) { + return MapsforgeMapProvider.isValidMapFile(mapFileIn); + } + + public static coordInputFormatEnum getCoordInputFormat() { + return coordInputFormatEnum.fromInt(getInt(R.string.pref_coordinputformat, 0)); + } + + public static void setCoordInputFormat(final coordInputFormatEnum format) { + putInt(R.string.pref_coordinputformat, format.ordinal()); + } + + static void setLogOffline(final boolean offline) { + putBoolean(R.string.pref_log_offline, offline); + } + + public static boolean getLogOffline() { + return getBoolean(R.string.pref_log_offline, false); + } + + public static boolean getChooseList() { + return getBoolean(R.string.pref_choose_list, false); + } + + public static boolean getLoadDirImg() { + return !isPremiumMember() && getBoolean(R.string.pref_loaddirectionimg, true); + } + + public static void setGcCustomDate(final String format) { + putString(R.string.pref_gccustomdate, format); + } + + /** + * @return User selected date format on GC.com + * @see Login#gcCustomDateFormats + */ + public static String getGcCustomDate() { + return getString(R.string.pref_gccustomdate, null); + } + + public static boolean isExcludeMyCaches() { + return getBoolean(R.string.pref_excludemine, false); + } + + public static void setUseEnglish(final boolean english) { + putBoolean(R.string.pref_useenglish, english); + setLanguage(english); + } + + public static boolean isUseEnglish() { + return getBoolean(R.string.pref_useenglish, false); + } + + public static boolean isShowAddress() { + return getBoolean(R.string.pref_showaddress, true); + } + + public static boolean isShowCaptcha() { + return !isPremiumMember() && getBoolean(R.string.pref_showcaptcha, false); + } + + public static boolean isExcludeDisabledCaches() { + return getBoolean(R.string.pref_excludedisabled, false); + } + + public static boolean isStoreOfflineMaps() { + return getBoolean(R.string.pref_offlinemaps, true); + } + + public static boolean isStoreOfflineWpMaps() { + return getBoolean(R.string.pref_offlinewpmaps, false); + } + + public static boolean isStoreLogImages() { + return getBoolean(R.string.pref_logimages, false); + } + + public static boolean isAutoLoadDescription() { + return getBoolean(R.string.pref_autoloaddesc, true); + } + + public static boolean isRatingWanted() { + return getBoolean(R.string.pref_ratingwanted, true); + } + + public static boolean isFriendLogsWanted() { + if (!isLogin()) { + // don't show a friends log if the user is anonymous + return false; + } + return getBoolean(R.string.pref_friendlogswanted, true); + } + + public static boolean isLiveList() { + return getBoolean(R.string.pref_livelist, true); + } + + public static boolean isTrackableAutoVisit() { + return getBoolean(R.string.pref_trackautovisit, false); + } + + public static boolean isAutoInsertSignature() { + return getBoolean(R.string.pref_sigautoinsert, false); + } + + public static boolean isUseImperialUnits() { + return getBoolean(R.string.pref_units, false); + } + + public static boolean isLiveMap() { + return getBoolean(R.string.pref_maplive, true); + } + + public static void setLiveMap(final boolean live) { + putBoolean(R.string.pref_maplive, live); + } + + public static boolean isMapTrail() { + return getBoolean(R.string.pref_maptrail, true); + } + + public static void setMapTrail(final boolean showTrail) { + putBoolean(R.string.pref_maptrail, showTrail); + } + + public static int getMapZoom() { + return getInt(R.string.pref_lastmapzoom, 14); + } + + public static void setMapZoom(final int mapZoomLevel) { + putInt(R.string.pref_lastmapzoom, mapZoomLevel); + } + + public static GeoPointImpl getMapCenter() { + return getMapProvider().getMapItemFactory() + .getGeoPointBase(new Geopoint(getInt(R.string.pref_lastmaplat, 0) / 1e6, + getInt(R.string.pref_lastmaplon, 0) / 1e6)); + } + + public static void setMapCenter(final GeoPointImpl mapViewCenter) { + putInt(R.string.pref_lastmaplat, mapViewCenter.getLatitudeE6()); + putInt(R.string.pref_lastmaplon, mapViewCenter.getLongitudeE6()); + } + + public static MapSource getMapSource() { + final int id = getConvertedMapId(); + final MapSource map = MapProviderFactory.getMapSource(id); + if (map != null) { + // don't use offline maps if the map file is not valid + if ((!(map instanceof OfflineMapSource)) || (isValidMapFile())) { + return map; + } + } + // fallback to first available map + return MapProviderFactory.getDefaultSource(); + } + + private final static int GOOGLEMAP_BASEID = 30; + private final static int MAP = 1; + private final static int SATELLITE = 2; + + private final static int MFMAP_BASEID = 40; + private final static int MAPNIK = 1; + private final static int CYCLEMAP = 3; + private final static int OFFLINE = 4; + + /** + * convert old preference ids for maps (based on constant values) into new hash based ids + * + * @return + */ + private static int getConvertedMapId() { + // what the heck is happening here?? hashCodes of Strings? + // why not strings? + final int id = Integer.parseInt(getString(R.string.pref_mapsource, + String.valueOf(MAP_SOURCE_DEFAULT))); + switch (id) { + case GOOGLEMAP_BASEID + MAP: + return GoogleMapProvider.GOOGLE_MAP_ID.hashCode(); + case GOOGLEMAP_BASEID + SATELLITE: + return GoogleMapProvider.GOOGLE_SATELLITE_ID.hashCode(); + case MFMAP_BASEID + MAPNIK: + return MapsforgeMapProvider.MAPSFORGE_MAPNIK_ID.hashCode(); + case MFMAP_BASEID + CYCLEMAP: + return MapsforgeMapProvider.MAPSFORGE_CYCLEMAP_ID.hashCode(); + case MFMAP_BASEID + OFFLINE: { + final String mapFile = Settings.getMapFile(); + if (StringUtils.isNotEmpty(mapFile)) { + return mapFile.hashCode(); + } + break; + } + default: + break; + } + return id; + } + + public static void setMapSource(final MapSource newMapSource) { + if (!MapProviderFactory.isSameActivity(getMapSource(), newMapSource)) { + mapProvider = null; + } + putString(R.string.pref_mapsource, String.valueOf(newMapSource.getNumericalId())); + if (newMapSource instanceof OfflineMapSource) { + setMapFile(((OfflineMapSource) newMapSource).getFileName()); + } + } + + public static void setAnyCoordinates(final Geopoint coords) { + if (null != coords) { + putFloat(R.string.pref_anylatitude, (float) coords.getLatitude()); + putFloat(R.string.pref_anylatitude, (float) coords.getLongitude()); + } else { + remove(R.string.pref_anylatitude); + remove(R.string.pref_anylongitude); + } + } + + public static Geopoint getAnyCoordinates() { + if (contains(R.string.pref_anylatitude) && contains(R.string.pref_anylongitude)) { + float lat = getFloat(R.string.pref_anylatitude, 0); + float lon = getFloat(R.string.pref_anylongitude, 0); + return new Geopoint(lat, lon); + } + return null; + } + + public static boolean isUseCompass() { + return getBoolean(R.string.pref_usecompass, true); + } + + public static void setUseCompass(final boolean useCompass) { + putBoolean(R.string.pref_usecompass, useCompass); + } + + public static boolean isLightSkin() { + return getBoolean(R.string.pref_skin, false); + } + + public static String getKeyConsumerPublic() { + return keyConsumerPublic; + } + + public static String getKeyConsumerSecret() { + return keyConsumerSecret; + } + + public static String getWebDeviceCode() { + return getString(R.string.pref_webDeviceCode, null); + } + + public static String getWebDeviceName() { + return getString(R.string.pref_webDeviceName, android.os.Build.MODEL); + } + + /** + * @return The cache type used for filtering or ALL if no filter is active. + * Returns never null + */ + public static CacheType getCacheType() { + return CacheType.getById(getString(R.string.pref_cachetype, CacheType.ALL.id)); + } + + /** + * The Threshold for the showing of child waypoints + */ + public static int getWayPointsThreshold() { + return getInt(R.string.pref_gcshowwaypointsthreshold, SHOW_WP_THRESHOLD_DEFAULT); + } + + public static void setShowWaypointsThreshold(final int threshold) { + putInt(R.string.pref_gcshowwaypointsthreshold, threshold); + } + + public static boolean isUseTwitter() { + return getBoolean(R.string.pref_twitter, false); + } + + public static void setUseTwitter(final boolean useTwitter) { + putBoolean(R.string.pref_twitter, useTwitter); + } + + public static boolean isTwitterLoginValid() { + return !StringUtils.isBlank(getTokenPublic()) + && !StringUtils.isBlank(getTokenSecret()); + } + + public static String getTokenPublic() { + return getString(R.string.pref_twitter_token_public, null); + } + + public static String getTokenSecret() { + return getString(R.string.pref_twitter_token_secret, null); + + } + + public static boolean hasTwitterAuthorization() { + return StringUtils.isNotBlank(getTokenPublic()) + && StringUtils.isNotBlank(getTokenSecret()); + } + + public static void setTwitterTokens(final String tokenPublic, + final String tokenSecret, boolean enableTwitter) { + putString(R.string.pref_twitter_token_public, tokenPublic); + putString(R.string.pref_twitter_token_secret, tokenSecret); + if (tokenPublic != null) { + remove(R.string.pref_temp_twitter_token_public); + remove(R.string.pref_temp_twitter_token_secret); + } + setUseTwitter(enableTwitter); + } + + public static void setTwitterTempTokens(final String tokenPublic, + final String tokenSecret) { + putString(R.string.pref_temp_twitter_token_public, tokenPublic); + putString(R.string.pref_temp_twitter_token_secret, tokenSecret); + } + + public static ImmutablePair<String, String> getTempToken() { + String tokenPublic = getString(R.string.pref_temp_twitter_token_public, null); + String tokenSecret = getString(R.string.pref_temp_twitter_token_secret, null); + return new ImmutablePair<String, String>(tokenPublic, tokenSecret); + } + + public static int getVersion() { + return getInt(R.string.pref_version, 0); + } + + public static void setVersion(final int version) { + putInt(R.string.pref_version, version); + } + + public static boolean isOpenLastDetailsPage() { + return getBoolean(R.string.pref_opendetailslastpage, false); + } + + public static int getLastDetailsPage() { + return getInt(R.string.pref_lastdetailspage, 1); + } + + public static void setLastDetailsPage(final int index) { + putInt(R.string.pref_lastdetailspage, index); + } + + public static int getDefaultNavigationTool() { + return Integer.parseInt(getString( + R.string.pref_defaultNavigationTool, + String.valueOf(NavigationAppsEnum.COMPASS.id))); + } + + public static void setDefaultNavigationTool(final int defaultNavigationTool) { + putString(R.string.pref_defaultNavigationTool, + String.valueOf(defaultNavigationTool)); + } + + public static int getDefaultNavigationTool2() { + return Integer.parseInt(getString( + R.string.pref_defaultNavigationTool2, + String.valueOf(NavigationAppsEnum.INTERNAL_MAP.id))); + } + + public static void setDefaultNavigationTool2(final int defaultNavigationTool) { + putString(R.string.pref_defaultNavigationTool2, + String.valueOf(defaultNavigationTool)); + } + + public static Strategy getLiveMapStrategy() { + return Strategy.getById(getInt(R.string.pref_livemapstrategy, Strategy.AUTO.id)); + } + + public static void setLiveMapStrategy(final Strategy strategy) { + putInt(R.string.pref_livemapstrategy, strategy.id); + } + + public static boolean isDebug() { + return Log.isDebug(); + } + + public static boolean getHideLiveMapHint() { + return getBoolean(R.string.pref_hidelivemaphint, false); + } + + public static void setHideLiveHint(final boolean hide) { + putBoolean(R.string.pref_hidelivemaphint, hide); + } + + public static int getLiveMapHintShowCount() { + return getInt(R.string.pref_livemaphintshowcount, 0); + } + + public static void setLiveMapHintShowCount(final int showCount) { + putInt(R.string.pref_livemaphintshowcount, showCount); + } + + public static boolean isDbOnSDCard() { + return getBoolean(R.string.pref_dbonsdcard, false); + } + + public static void setDbOnSDCard(final boolean dbOnSDCard) { + putBoolean(R.string.pref_dbonsdcard, dbOnSDCard); + } + + public static String getGpxExportDir() { + return getString(R.string.pref_gpxExportDir, + Environment.getExternalStorageDirectory().getPath() + "/gpx"); + } + + public static String getGpxImportDir() { + return getString(R.string.pref_gpxImportDir, + Environment.getExternalStorageDirectory().getPath() + "/gpx"); + } + + public static boolean getShareAfterExport() { + return getBoolean(R.string.pref_shareafterexport, true); + } + + public static void setShareAfterExport(final boolean shareAfterExport) { + putBoolean(R.string.pref_shareafterexport, shareAfterExport); + } + + public static int getTrackableAction() { + return getInt(R.string.pref_trackableaction, LogType.RETRIEVED_IT.id); + } + + public static void setTrackableAction(final int trackableAction) { + putInt(R.string.pref_trackableaction, trackableAction); + } + + public static String getCustomRenderThemeBaseFolder() { + return getString(R.string.pref_renderthemepath, ""); + } + + public static String getCustomRenderThemeFilePath() { + return getString(R.string.pref_renderthemefile, ""); + } + + public static void setCustomRenderThemeFile(final String customRenderThemeFile) { + putString(R.string.pref_renderthemefile, customRenderThemeFile); + } + + public static File[] getMapThemeFiles() { + File directory = new File(Settings.getCustomRenderThemeBaseFolder()); + List<File> result = new ArrayList<File>(); + FileUtils.listDir(result, directory, new ExtensionsBasedFileSelector(new String[] { "xml" }), null); + + return result.toArray(new File[result.size()]); + } + + private static class ExtensionsBasedFileSelector extends FileSelector { + private final String[] extensions; + public ExtensionsBasedFileSelector(String[] extensions) { + this.extensions = extensions; + } + @Override + public boolean isSelected(File file) { + String filename = file.getName(); + for (String ext : extensions) { + if (StringUtils.endsWithIgnoreCase(filename, ext)) { + return true; + } + } + return false; + } + @Override + public boolean shouldEnd() { + return false; + } + } + + public static boolean getPlainLogs() { + return getBoolean(R.string.pref_plainLogs, false); + } + + public static boolean getUseNativeUa() { + return getBoolean(R.string.pref_nativeUa, false); + } + + public static String getCacheTwitterMessage() { + // TODO make customizable from UI + return "I found [NAME] ([URL])"; + } + + public static String getTrackableTwitterMessage() { + // TODO make customizable from UI + return "I touched [NAME] ([URL])!"; + } + + public static int getLogImageScale() { + return getInt(R.string.pref_logImageScale, -1); + } + + public static void setLogImageScale(final int scale) { + putInt(R.string.pref_logImageScale, scale); + } + + // Only for tests! + static void setExcludeDisabledCaches(final boolean exclude) { + putBoolean(R.string.pref_excludedisabled, exclude); + } + + public static void setExcludeMine(final boolean exclude) { + putBoolean(R.string.pref_excludemine, exclude); + } + + static boolean setLogin(final String username, final String password) { + + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { + // erase username and password + boolean a = remove(R.string.pref_username); + boolean b = remove(R.string.pref_password); + return a && b; + } + // save username and password + boolean a = putString(R.string.pref_username, username); + boolean b = putString(R.string.pref_password, password); + return a && b; + } + + static void setStoreOfflineMaps(final boolean offlineMaps) { + putBoolean(R.string.pref_offlinemaps, offlineMaps); + } + + static void setStoreOfflineWpMaps(final boolean offlineWpMaps) { + putBoolean(R.string.pref_offlinewpmaps, offlineWpMaps); + } + + static void setUseImperialUnits(final boolean imperial) { + putBoolean(R.string.pref_units, imperial); + } + + public static long getFieldnoteExportDate() { + return getLong(R.string.pref_fieldnoteExportDate, 0); + } + + public static void setFieldnoteExportDate(final long date) { + putLong(R.string.pref_fieldnoteExportDate, date); + } + +} diff --git a/main/src/cgeo/geocaching/settings/SettingsActivity.java b/main/src/cgeo/geocaching/settings/SettingsActivity.java new file mode 100644 index 0000000..4b1c92b --- /dev/null +++ b/main/src/cgeo/geocaching/settings/SettingsActivity.java @@ -0,0 +1,549 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.Intents; +import cgeo.geocaching.R; +import cgeo.geocaching.SelectMapfileActivity; +import cgeo.geocaching.cgData; +import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.activity.ActivityMixin; +import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; +import cgeo.geocaching.apps.cache.navi.NavigationAppFactory.NavigationAppsEnum; +import cgeo.geocaching.compatibility.Compatibility; +import cgeo.geocaching.files.SimpleDirChooser; +import cgeo.geocaching.maps.MapProviderFactory; +import cgeo.geocaching.maps.interfaces.MapSource; +import cgeo.geocaching.ui.Formatter; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.LogTemplateProvider; +import cgeo.geocaching.utils.LogTemplateProvider.LogTemplate; + +import org.apache.commons.lang3.StringUtils; +import org.openintents.intents.FileManagerIntents; + +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Environment; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceActivity; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.MenuItem; +import android.view.MenuItem.OnMenuItemClickListener; +import android.view.View; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.ListAdapter; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * A {@link PreferenceActivity} that presents a set of application settings. On + * handset devices, settings are presented as a single list. On tablets, + * settings are split by category, with category headers shown to the left of + * the list of settings. + * <p> + * See <a href="http://developer.android.com/design/patterns/settings.html"> Android Design: Settings</a> for design + * guidelines and the <a href="http://developer.android.com/guide/topics/ui/settings.html">Settings API Guide</a> for + * more information on developing a Settings UI. + * + * @author koem (initial author) + */ +public class SettingsActivity extends PreferenceActivity { + + private static final String INTENT_GOTO = "GOTO"; + private static final int INTENT_GOTO_SERVICES = 1; + + private static final int DIR_CHOOSER_MAPS_DIRECTORY_REQUEST = 4; + static final int OAUTH_OCDE_REQUEST = 5; + static final int OAUTH_TWITTER_REQUEST = 6; + + private EditText signatureText; + + /** + * Enumeration for directory choosers. This is how we can retrieve information about the + * directory and preference key in onActivityResult() easily just by knowing + * the result code. + */ + private enum DirChooserType { + GPX_IMPORT_DIR(1, R.string.pref_gpxImportDir, + Environment.getExternalStorageDirectory().getPath() + "/gpx"), + GPX_EXPORT_DIR(2, R.string.pref_gpxExportDir, + Environment.getExternalStorageDirectory().getPath() + "/gpx"), + THEMES_DIR(3, R.string.pref_renderthemepath, ""); + public final int requestCode; + public final int keyId; + public final String defaultValue; + + DirChooserType(final int requestCode, final int keyId, final String defaultValue) { + this.requestCode = requestCode; + this.keyId = keyId; + this.defaultValue = defaultValue; + } + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + setTheme(Settings.isLightSkin() ? R.style.settings_light : R.style.settings); + super.onCreate(savedInstanceState); + + SettingsActivity.addPreferencesFromResource(this, R.xml.preferences); + initPreferences(); + + Intent intent = getIntent(); + int gotoPage = intent.getIntExtra(INTENT_GOTO, 0); + if (gotoPage == INTENT_GOTO_SERVICES) { + // start with services screen + PreferenceScreen main = (PreferenceScreen) getPreference(R.string.pref_fakekey_main_screen); + int index = getPreference(R.string.pref_fakekey_services_screen).getOrder(); + main.onItemClick(null, null, index, 0); + } + } + + @Override + protected void onPause() { + Compatibility.dataChanged(getPackageName()); + super.onPause(); + } + + private void initPreferences() { + initMapSourcePreference(); + initDirChoosers(); + initDefaultNavigationPreferences(); + initBackupButtons(); + initDbLocationPreference(); + initDebugPreference(); + initBasicMemberPreferences(); + initSend2CgeoPreferences(); + + for (int k : new int[] { R.string.pref_username, R.string.pref_password, + R.string.pref_pass_vote, R.string.pref_signature, + R.string.pref_mapsource, R.string.pref_renderthemepath, + R.string.pref_gpxExportDir, R.string.pref_gpxImportDir, + R.string.pref_mapDirectory, R.string.pref_defaultNavigationTool, + R.string.pref_defaultNavigationTool2, R.string.pref_webDeviceName, + R.string.pref_fakekey_preference_backup_info, }) { + bindSummaryToStringValue(k); + } + } + + private static String getKey(final int prefKeyId) { + return cgeoapplication.getInstance().getString(prefKeyId); + } + + private Preference getPreference(final int keyId) { + return SettingsActivity.findPreference(this, getKey(keyId)); + } + + // workaround, because OnContextItemSelected nor onMenuItemSelected is never called + OnMenuItemClickListener TEMPLATE_CLICK = new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(final MenuItem item) { + LogTemplate template = LogTemplateProvider.getTemplate(item.getItemId()); + if (template != null) { + insertSignatureTemplate(template); + return true; + } + return false; + } + }; + + // workaround, because OnContextItemSelected and onMenuItemSelected are never called + void setSignatureTextView(final EditText view) { + this.signatureText = view; + } + + @Override + public void onCreateContextMenu(final ContextMenu menu, final View v, + final ContextMenuInfo menuInfo) { + // context menu for signature templates + if (v.getId() == R.id.signature_templates) { + menu.setHeaderTitle(R.string.init_signature_template_button); + ArrayList<LogTemplate> templates = LogTemplateProvider.getTemplates(); + for (int i = 0; i < templates.size(); ++i) { + menu.add(0, templates.get(i).getItemId(), 0, templates.get(i).getResourceId()); + menu.getItem(i).setOnMenuItemClickListener(TEMPLATE_CLICK); + } + } + super.onCreateContextMenu(menu, v, menuInfo); + } + + private void insertSignatureTemplate(final LogTemplate template) { + String insertText = "[" + template.getTemplateString() + "]"; + ActivityMixin.insertAtPosition(signatureText, insertText, true); + } + + /** + * Fill the choice list for map sources. + */ + private void initMapSourcePreference() { + ListPreference pref = (ListPreference) getPreference(R.string.pref_mapsource); + + List<MapSource> mapSources = MapProviderFactory.getMapSources(); + CharSequence[] entries = new CharSequence[mapSources.size()]; + CharSequence[] values = new CharSequence[mapSources.size()]; + for (int i = 0; i < mapSources.size(); ++i) { + entries[i] = mapSources.get(i).getName(); + values[i] = String.valueOf(mapSources.get(i).getNumericalId()); + } + pref.setEntries(entries); + pref.setEntryValues(values); + } + + /** + * Fill the choice list for default navigation tools. + */ + private void initDefaultNavigationPreferences() { + + final List<NavigationAppsEnum> apps = NavigationAppFactory.getInstalledDefaultNavigationApps(); + + CharSequence[] entries = new CharSequence[apps.size()]; + CharSequence[] values = new CharSequence[apps.size()]; + for (int i = 0; i < apps.size(); ++i) { + entries[i] = apps.get(i).toString(); + values[i] = String.valueOf(apps.get(i).id); + } + + ListPreference pref = (ListPreference) getPreference(R.string.pref_defaultNavigationTool); + pref.setEntries(entries); + pref.setEntryValues(values); + pref = (ListPreference) getPreference(R.string.pref_defaultNavigationTool2); + pref.setEntries(entries); + pref.setEntryValues(values); + } + + private void initDirChoosers() { + for (final DirChooserType dct : DirChooserType.values()) { + + getPreference(dct.keyId).setOnPreferenceClickListener( + new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(final Preference preference) { + startDirChooser(dct); + return false; + } + }); + } + + getPreference(R.string.pref_mapDirectory).setOnPreferenceClickListener( + new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(final Preference preference) { + Intent i = new Intent(SettingsActivity.this, + SelectMapfileActivity.class); + startActivityForResult(i, DIR_CHOOSER_MAPS_DIRECTORY_REQUEST); + return false; + } + }); + } + + /** + * Fire up a directory chooser on click on the preference. + * + * @see #onActivityResult() for processing of the selected directory + * + * @param dct + * type of directory to be selected + */ + private void startDirChooser(final DirChooserType dct) { + + final String startDirectory = Settings.getString(dct.keyId, dct.defaultValue); + + try { + final Intent dirChooser = new Intent(FileManagerIntents.ACTION_PICK_DIRECTORY); + if (StringUtils.isNotBlank(startDirectory)) { + dirChooser.setData(Uri.fromFile(new File(startDirectory))); + } + dirChooser.putExtra(FileManagerIntents.EXTRA_TITLE, + getString(R.string.simple_dir_chooser_title)); + dirChooser.putExtra(FileManagerIntents.EXTRA_BUTTON_TEXT, + getString(android.R.string.ok)); + startActivityForResult(dirChooser, dct.requestCode); + } catch (android.content.ActivityNotFoundException ex) { + // OI file manager not available + final Intent dirChooser = new Intent(this, SimpleDirChooser.class); + dirChooser.putExtra(Intents.EXTRA_START_DIR, startDirectory); + startActivityForResult(dirChooser, dct.requestCode); + } + } + + private void setChosenDirectory(final DirChooserType dct, final Intent data) { + final String directory = new File(data.getData().getPath()).getAbsolutePath(); + if (StringUtils.isNotBlank(directory)) { + Preference p = getPreference(dct.keyId); + if (p == null) { + return; + } + Settings.putString(dct.keyId, directory); + p.setSummary(directory); + } + } + + public void initBackupButtons() { + Preference backup = getPreference(R.string.pref_fakekey_preference_backup); + backup.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(final Preference preference) { + final Context context = preference.getContext(); + // avoid overwriting an existing backup with an empty database + // (can happen directly after reinstalling the app) + if (cgData.getAllCachesCount() == 0) { + ActivityMixin.helpDialog(SettingsActivity.this, + context.getString(R.string.init_backup), + context.getString(R.string.init_backup_unnecessary)); + return false; + } + + final ProgressDialog dialog = ProgressDialog.show(context, + context.getString(R.string.init_backup), + context.getString(R.string.init_backup_running), true, false); + new Thread() { + @Override + public void run() { + final String backupFileName = cgData.backupDatabase(); + runOnUiThread(new Runnable() { + @Override + public void run() { + dialog.dismiss(); + ActivityMixin.helpDialog(SettingsActivity.this, + context.getString(R.string.init_backup_backup), + backupFileName != null + ? context.getString(R.string.init_backup_success) + + "\n" + backupFileName + : context.getString(R.string.init_backup_failed)); + VALUE_CHANGE_LISTENER.onPreferenceChange(SettingsActivity.this.getPreference(R.string.pref_fakekey_preference_backup_info), ""); + } + }); + } + }.start(); + return true; + } + }); + + Preference restore = getPreference(R.string.pref_fakekey_preference_restore); + restore.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(final Preference preference) { + ((cgeoapplication) SettingsActivity.this.getApplication()) + .restoreDatabase(SettingsActivity.this); + return true; + } + }); + } + + private void initDbLocationPreference() { + Preference p = getPreference(R.string.pref_dbonsdcard); + p.setPersistent(false); + p.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(final Preference preference) { + boolean oldValue = Settings.isDbOnSDCard(); + ((cgeoapplication) SettingsActivity.this.getApplication()) + .moveDatabase(SettingsActivity.this); + return oldValue != Settings.isDbOnSDCard(); + } + }); + } + + private void initDebugPreference() { + Preference p = getPreference(R.string.pref_debug); + p.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(final Preference preference, final Object newValue) { + Log.setDebug((Boolean) newValue); + return true; + } + }); + } + + void initBasicMemberPreferences() { + getPreference(R.string.pref_fakekey_basicmembers_screen) + .setEnabled(!Settings.isPremiumMember()); + getPreference(R.string.pref_loaddirectionimg) + .setEnabled(!Settings.isPremiumMember()); + getPreference(R.string.pref_showcaptcha) + .setEnabled(!Settings.isPremiumMember()); + + redrawScreen(R.string.pref_fakekey_services_screen); + } + + void redrawScreen(int key) { + PreferenceScreen screen = (PreferenceScreen) getPreference(key); + if (screen == null) { + return; + } + ListAdapter adapter = screen.getRootAdapter(); + if (adapter instanceof BaseAdapter) { + ((BaseAdapter) adapter).notifyDataSetChanged(); + } + } + + private static void initSend2CgeoPreferences() { + Settings.putString(R.string.pref_webDeviceName, Settings.getWebDeviceName()); + } + + void setOCDEAuthTitle() { + getPreference(R.string.pref_fakekey_ocde_authorization) + .setTitle(getString(Settings.hasOCDEAuthorization() + ? R.string.init_reregister_oc_de + : R.string.init_register_oc_de)); + } + + void setTwitterAuthTitle() { + getPreference(R.string.pref_fakekey_twitter_authorization) + .setTitle(getString(Settings.hasTwitterAuthorization() + ? R.string.init_twitter_reauthorize + : R.string.init_twitter_authorize)); + } + + public static void jumpToServicesPage(final Context fromActivity) { + final Intent intent = new Intent(fromActivity, SettingsActivity.class); + intent.putExtra(INTENT_GOTO, INTENT_GOTO_SERVICES); + fromActivity.startActivity(intent); + } + + @Override + protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode != RESULT_OK) { + return; + } + + for (DirChooserType dct : DirChooserType.values()) { + if (requestCode == dct.requestCode) { + setChosenDirectory(dct, data); + return; + } + } + + switch (requestCode) { + case DIR_CHOOSER_MAPS_DIRECTORY_REQUEST: + if (data.hasExtra(Intents.EXTRA_MAP_FILE)) { + final String mapFile = data.getStringExtra(Intents.EXTRA_MAP_FILE); + Settings.setMapFile(mapFile); + if (!Settings.isValidMapFile(Settings.getMapFile())) { + ActivityMixin.showToast(this, R.string.warn_invalid_mapfile); + } + } + initMapSourcePreference(); + getPreference(R.string.pref_mapDirectory).setSummary( + Settings.getMapFileDirectory()); + break; + case OAUTH_OCDE_REQUEST: + setOCDEAuthTitle(); + redrawScreen(R.string.pref_fakekey_services_screen); + break; + case OAUTH_TWITTER_REQUEST: + setTwitterAuthTitle(); + redrawScreen(R.string.pref_fakekey_services_screen); + break; + default: + throw new IllegalArgumentException(); + } + } + + /** + * A preference value change listener that updates the preference's summary + * to reflect its new value. + */ + private static final Preference.OnPreferenceChangeListener VALUE_CHANGE_LISTENER = new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(final Preference preference, final Object value) { + String stringValue = value.toString(); + + if (preference instanceof EditPasswordPreference) { + if (StringUtils.isBlank((String) value)) { + preference.setSummary(""); + } else { + preference.setSummary("\u2022 \u2022 \u2022 \u2022 \u2022 \u2022 \u2022 \u2022 \u2022 \u2022"); + } + } else if (preference instanceof ListPreference) { + // For list preferences, look up the correct display value in + // the preference's 'entries' list. + ListPreference listPreference = (ListPreference) preference; + int index = listPreference.findIndexOfValue(stringValue); + + // Set the summary to reflect the new value. + preference.setSummary( + index >= 0 + ? listPreference.getEntries()[index] + : null); + } else if (getKey(R.string.pref_fakekey_preference_backup_info).equals(preference.getKey())) { + File lastBackupFile = cgData.getRestoreFile(); + String text; + if (lastBackupFile != null) { + text = preference.getContext().getString(R.string.init_backup_last) + " " + + Formatter.formatTime(lastBackupFile.lastModified()) + + ", " + Formatter.formatDate(lastBackupFile.lastModified()); + } else { + text = preference.getContext().getString(R.string.init_backup_last_no); + } + preference.setSummary(text); + } else { + // For all other preferences, set the summary to the value's + // simple string representation. + preference.setSummary(stringValue); + } + return true; + } + }; + + /** + * Binds a preference's summary to its value. More specifically, when the + * preference's value is changed, its summary (line of text below the + * preference title) is updated to reflect the value. The summary is also + * immediately updated upon calling this method. The exact display format is + * dependent on the type of preference. + * + * @see #VALUE_CHANGE_LISTENER + */ + private static void bindSummaryToValue(final Preference preference, final Object value) { + // Set the listener to watch for value changes. + if (preference == null) { + return; + } + preference.setOnPreferenceChangeListener(VALUE_CHANGE_LISTENER); + + // Trigger the listener immediately with the preference's + // current value. + VALUE_CHANGE_LISTENER.onPreferenceChange(preference, value); + } + + /** + * auto-care for the summary of the preference of string type with this key + * + * @param key + */ + private void bindSummaryToStringValue(final int key) { + + Preference pref = getPreference(key); + if (pref == null) { + return; + } + + String value = PreferenceManager + .getDefaultSharedPreferences(pref.getContext()) + .getString(pref.getKey(), ""); + + bindSummaryToValue(pref, value); + } + + @SuppressWarnings("deprecation") + public static Preference findPreference(final PreferenceActivity preferenceActivity, final CharSequence key) { + return preferenceActivity.findPreference(key); + } + + @SuppressWarnings("deprecation") + public static void addPreferencesFromResource(final PreferenceActivity preferenceActivity, final int preferencesResId) { + preferenceActivity.addPreferencesFromResource(preferencesResId); + } +} diff --git a/main/src/cgeo/geocaching/settings/TextPreference.java b/main/src/cgeo/geocaching/settings/TextPreference.java new file mode 100644 index 0000000..bcd03ff --- /dev/null +++ b/main/src/cgeo/geocaching/settings/TextPreference.java @@ -0,0 +1,86 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.R; + +import android.content.Context; +import android.content.res.TypedArray; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +/** + * Preference to simply show a text message. + * <p> + * Links are not shown - I tried everything (koem) + * <p> + * example: <cgeo.geocaching.settings.TextPreference android:text="@string/legal_note" + * android:layout="@string/text_preference_default_layout" /> + */ +public class TextPreference extends Preference { + + private String text; + private TextView summaryView; + private CharSequence summaryText; + + public TextPreference(Context context) { + super(context); + } + + public TextPreference(Context context, AttributeSet attrs) { + super(context, attrs); + processAttributes(context, attrs, 0); + } + + public TextPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + processAttributes(context, attrs, defStyle); + } + + private void processAttributes(Context context, AttributeSet attrs, int defStyle) { + if (attrs == null) { + return; + } + + TypedArray types = context.obtainStyledAttributes(attrs, new int[] { + android.R.attr.text }, defStyle, 0); + this.text = types.getString(0); + types.recycle(); + } + + @Override + protected View onCreateView(ViewGroup parent) { + this.setSelectable(false); + + View v = super.onCreateView(parent); + + TextView text = (TextView) v.findViewById(R.id.textPreferenceText); + text.setText(this.text); + + summaryView = (TextView) v.findViewById(R.id.textPreferenceSummary); + setSummary(null); // show saved summary text + + return v; + } + + @Override + public void setSummary(CharSequence summaryText) { + // the layout hasn't been inflated yet, save the summaryText for later use + if (this.summaryView == null) { + this.summaryText = summaryText; + return; + } + + // if summaryText is null, take it from the previous saved summary + if (summaryText == null) { + if (this.summaryText == null) { + return; + } + this.summaryView.setText(this.summaryText); + } else { + this.summaryView.setText(summaryText); + } + this.summaryView.setVisibility(View.VISIBLE); + } +} diff --git a/main/src/cgeo/geocaching/settings/WpThresholdPreference.java b/main/src/cgeo/geocaching/settings/WpThresholdPreference.java new file mode 100644 index 0000000..867714f --- /dev/null +++ b/main/src/cgeo/geocaching/settings/WpThresholdPreference.java @@ -0,0 +1,73 @@ +package cgeo.geocaching.settings; + +import cgeo.geocaching.R; +import cgeo.geocaching.settings.Settings; + +import android.content.Context; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; +import android.widget.TextView; + +public class WpThresholdPreference extends Preference { + + TextView valueView; + + public WpThresholdPreference(Context context) { + super(context); + init(); + } + + public WpThresholdPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public WpThresholdPreference(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + setPersistent(false); + } + + @Override + protected View onCreateView(ViewGroup parent) { + View v = super.onCreateView(parent); + + // get views + SeekBar seekBar = (SeekBar) v.findViewById(R.id.wp_threshold_seekbar); + valueView = (TextView) v.findViewById(R.id.wp_threshold_value_view); + + // init seekbar + seekBar.setMax(Settings.SHOW_WP_THRESHOLD_MAX); + + // set initial value + int threshold = Settings.getWayPointsThreshold(); + valueView.setText(String.valueOf(threshold)); + seekBar.setProgress(threshold); + + seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser) { + valueView.setText(String.valueOf(progress)); + } + } + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + Settings.setShowWaypointsThreshold(seekBar.getProgress()); + } + }); + + return v; + } + +} diff --git a/main/src/cgeo/geocaching/speech/SpeechService.java b/main/src/cgeo/geocaching/speech/SpeechService.java index 7226014..634f1c4 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.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 @@ -109,10 +110,10 @@ public class SpeechService extends Service implements OnInitListener { private static float getDeltaForDistance(final float distance) { if (distance > 1.0) { return 0.2f; - } else if (distance > 0.05) { + } + if (distance > 0.05) { return distance / 5.0f; } - return 0f; } diff --git a/main/src/cgeo/geocaching/speech/TextFactory.java b/main/src/cgeo/geocaching/speech/TextFactory.java index 0e13564..81a74fb 100644 --- a/main/src/cgeo/geocaching/speech/TextFactory.java +++ b/main/src/cgeo/geocaching/speech/TextFactory.java @@ -1,7 +1,7 @@ package cgeo.geocaching.speech; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.IConversion; @@ -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)); + if (Settings.isUseImperialUnits()) { + 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); } + 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); + } - 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/twitter/Twitter.java b/main/src/cgeo/geocaching/twitter/Twitter.java index 3f1f749..7233764 100644 --- a/main/src/cgeo/geocaching/twitter/Twitter.java +++ b/main/src/cgeo/geocaching/twitter/Twitter.java @@ -1,7 +1,6 @@ package cgeo.geocaching.twitter; import cgeo.geocaching.Geocache; -import cgeo.geocaching.Settings; import cgeo.geocaching.Trackable; import cgeo.geocaching.cgData; import cgeo.geocaching.cgeoapplication; @@ -11,6 +10,7 @@ import cgeo.geocaching.geopoint.GeopointFormatter.Format; import cgeo.geocaching.network.Network; import cgeo.geocaching.network.OAuth; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import ch.boye.httpclientandroidlib.HttpResponse; @@ -19,13 +19,20 @@ import org.apache.commons.lang3.StringUtils; public final class Twitter { private static final String HASH_PREFIX_WITH_BLANK = " #"; - public static final int MAX_TWEET_SIZE = 140; + private static final int MAX_TWEET_SIZE = 140; - public static void postTweet(final cgeoapplication app, final String status, final Geopoint coords) { - if (app == null) { - return; - } - if (!Settings.isTwitterLoginValid()) { + public static void postTweetCache(String geocode) { + final Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + postTweet(cgeoapplication.getInstance(), getStatusMessage(cache), null); + } + + public static void postTweetTrackable(String geocode) { + final Trackable trackable = cgData.loadTrackable(geocode); + postTweet(cgeoapplication.getInstance(), getStatusMessage(trackable), null); + } + + private static void postTweet(final cgeoapplication app, final String status, final Geopoint coords) { + if (app == null || !Settings.isUseTwitter() || !Settings.isTwitterLoginValid()) { return; } @@ -38,19 +45,23 @@ public final class Twitter { "display_coordinates", "true"); } - OAuth.signOAuth("api.twitter.com", "/1/statuses/update.json", "POST", false, parameters, Settings.getTokenPublic(), Settings.getTokenSecret(), Settings.getKeyConsumerPublic(), Settings.getKeyConsumerSecret()); - final HttpResponse httpResponse = Network.postRequest("http://api.twitter.com/1/statuses/update.json", parameters); - if (httpResponse != null && httpResponse.getStatusLine().getStatusCode() == 200) { - Log.i("Tweet posted"); + OAuth.signOAuth("api.twitter.com", "/1.1/statuses/update.json", "POST", false, parameters, Settings.getTokenPublic(), Settings.getTokenSecret(), Settings.getKeyConsumerPublic(), Settings.getKeyConsumerSecret()); + final HttpResponse httpResponse = Network.postRequest("http://api.twitter.com/1.1/statuses/update.json", parameters); + if (httpResponse != null) { + if (httpResponse.getStatusLine().getStatusCode() == 200) { + Log.i("Tweet posted"); + } else { + Log.e("Tweet could not be posted. Reason: " + httpResponse.toString()); + } } else { - Log.e("Tweet could not be posted"); + Log.e("Tweet could not be posted. Reason: httpResponse Object is null"); } } catch (Exception e) { Log.e("Twitter.postTweet", e); } } - public static void appendHashTag(final StringBuilder status, final String tag) { + private static void appendHashTag(final StringBuilder status, final String tag) { if (status.length() + HASH_PREFIX_WITH_BLANK.length() + tag.length() <= MAX_TWEET_SIZE) { final String tagWithPrefix = HASH_PREFIX_WITH_BLANK + tag; if (status.indexOf(tagWithPrefix, 0) == -1) { @@ -59,17 +70,6 @@ public final class Twitter { } } - public static void postTweetCache(String geocode) { - if (!Settings.isUseTwitter()) { - return; - } - if (!Settings.isTwitterLoginValid()) { - return; - } - final Geocache cache = cgData.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); - postTweet(cgeoapplication.getInstance(), getStatusMessage(cache), null); - } - static String getStatusMessage(Geocache cache) { String name = cache.getName(); if (name.length() > 100) { @@ -79,20 +79,6 @@ public final class Twitter { return fillTemplate(Settings.getCacheTwitterMessage(), name, url); } - private static String fillTemplate(String template, String name, final String url) { - String result = StringUtils.replace(template, "[NAME]", name); - result = StringUtils.replace(result, "[URL]", url); - StringBuilder builder = new StringBuilder(result); - appendHashTag(builder, "cgeo"); - appendHashTag(builder, "geocaching"); - return builder.toString(); - } - - public static void postTweetTrackable(String geocode) { - final Trackable trackable = cgData.loadTrackable(geocode); - postTweet(cgeoapplication.getInstance(), getStatusMessage(trackable), null); - } - static String getStatusMessage(Trackable trackable) { String name = trackable.getName(); if (name.length() > 82) { @@ -102,4 +88,13 @@ public final class Twitter { String status = Settings.getTrackableTwitterMessage(); return fillTemplate(status, name, url); } + + private static String fillTemplate(String template, String name, final String url) { + String result = StringUtils.replace(template, "[NAME]", name); + result = StringUtils.replace(result, "[URL]", url); + StringBuilder builder = new StringBuilder(result); + appendHashTag(builder, "cgeo"); + appendHashTag(builder, "geocaching"); + return builder.toString(); + } } diff --git a/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java b/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java index 7146a62..6b185267 100644 --- a/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java @@ -1,7 +1,7 @@ package cgeo.geocaching.twitter; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.network.OAuthAuthorizationActivity; import org.apache.commons.lang3.tuple.ImmutablePair; 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/CacheListAdapter.java b/main/src/cgeo/geocaching/ui/CacheListAdapter.java index d95363e..3179857 100644 --- a/main/src/cgeo/geocaching/ui/CacheListAdapter.java +++ b/main/src/cgeo/geocaching/ui/CacheListAdapter.java @@ -6,7 +6,7 @@ import cgeo.geocaching.CacheDetailActivity; import cgeo.geocaching.Geocache; import cgeo.geocaching.IGeoData; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.enumerations.CacheListType; import cgeo.geocaching.enumerations.CacheType; @@ -14,6 +14,7 @@ import cgeo.geocaching.filter.IFilter; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.sorting.CacheComparator; import cgeo.geocaching.sorting.DistanceComparator; +import cgeo.geocaching.sorting.EventDateComparator; import cgeo.geocaching.sorting.InverseComparator; import cgeo.geocaching.sorting.VisitComparator; import cgeo.geocaching.utils.AngleUtils; @@ -642,4 +643,25 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> { } return list.size(); } + + public void setInitialComparator() { + CacheComparator comparator = null; // a null comparator will automatically sort by distance + if (cacheListType == CacheListType.HISTORY) { + comparator = new VisitComparator(); + } else { + if (CollectionUtils.isNotEmpty(list)) { + boolean eventsOnly = true; + for (final Geocache cache : list) { + if (!cache.isEventCache()) { + eventsOnly = false; + break; + } + } + if (eventsOnly) { + comparator = new EventDateComparator(); + } + } + } + setComparator(comparator); + } } diff --git a/main/src/cgeo/geocaching/ui/CompassMiniView.java b/main/src/cgeo/geocaching/ui/CompassMiniView.java index da8f69e..92280dc 100644 --- a/main/src/cgeo/geocaching/ui/CompassMiniView.java +++ b/main/src/cgeo/geocaching/ui/CompassMiniView.java @@ -1,7 +1,7 @@ package cgeo.geocaching.ui; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.utils.AngleUtils; 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/DistanceView.java b/main/src/cgeo/geocaching/ui/DistanceView.java index 9611511..b36166d 100644 --- a/main/src/cgeo/geocaching/ui/DistanceView.java +++ b/main/src/cgeo/geocaching/ui/DistanceView.java @@ -36,8 +36,4 @@ public class DistanceView extends TextView { public void setDistance(Float distance) { setText("~" + Units.getDistanceFromKilometers(distance)); } - - public void clear() { - setText(null); - } }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/ui/EditNoteDialog.java b/main/src/cgeo/geocaching/ui/EditNoteDialog.java index bbf0618..9a122e2 100644 --- a/main/src/cgeo/geocaching/ui/EditNoteDialog.java +++ b/main/src/cgeo/geocaching/ui/EditNoteDialog.java @@ -1,21 +1,17 @@ package cgeo.geocaching.ui; import cgeo.geocaching.R; -import cgeo.geocaching.R.string; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; import android.os.Bundle; import android.support.v4.app.DialogFragment; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager.LayoutParams; -import android.view.inputmethod.EditorInfo; import android.widget.EditText; -import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; -public class EditNoteDialog extends DialogFragment implements OnEditorActionListener { +public class EditNoteDialog extends DialogFragment { public interface EditNoteDialogListener { void onFinishEditNoteDialog(final String inputText); @@ -24,7 +20,6 @@ public class EditNoteDialog extends DialogFragment implements OnEditorActionList public static final String ARGUMENT_INITIAL_NOTE = "initialNote"; private EditText mEditText; - private String initialNote; public static EditNoteDialog newInstance(final String initialNote) { EditNoteDialog dialog = new EditNoteDialog(); @@ -37,34 +32,35 @@ public class EditNoteDialog extends DialogFragment implements OnEditorActionList } @Override - public View onCreateView(final LayoutInflater inflater, final ViewGroup container, - final Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_edit_note, container); + public Dialog onCreateDialog(Bundle savedInstanceState) { + LayoutInflater inflater = getActivity().getLayoutInflater(); + View view = inflater.inflate(R.layout.fragment_edit_note, null); mEditText = (EditText) view.findViewById(R.id.note); - initialNote = getArguments().getString(ARGUMENT_INITIAL_NOTE); + String initialNote = getArguments().getString(ARGUMENT_INITIAL_NOTE); if (initialNote != null) { mEditText.setText(initialNote); initialNote = null; } - getDialog().setTitle(string.cache_personal_note); - mEditText.requestFocus(); - getDialog().getWindow().setSoftInputMode( - LayoutParams.SOFT_INPUT_STATE_VISIBLE); - mEditText.setOnEditorActionListener(this); - return view; + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.cache_personal_note); + builder.setView(view); + builder.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + final EditNoteDialogListener activity = (EditNoteDialogListener) getActivity(); + activity.onFinishEditNoteDialog(mEditText.getText().toString()); + dialog.dismiss(); + } + }); + builder.setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + dialog.dismiss(); + } + }); + return builder.create(); } - - @Override - public boolean onEditorAction(final TextView v, final int actionId, final KeyEvent event) { - if (EditorInfo.IME_ACTION_DONE == actionId) { - final EditNoteDialogListener activity = (EditNoteDialogListener) getActivity(); - activity.onFinishEditNoteDialog(mEditText.getText().toString()); - dismiss(); - return true; - } - return false; - } - - } 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/IndexOutOfBoundsAvoidingTextView.java b/main/src/cgeo/geocaching/ui/IndexOutOfBoundsAvoidingTextView.java new file mode 100644 index 0000000..a0c8b52 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/IndexOutOfBoundsAvoidingTextView.java @@ -0,0 +1,55 @@ +package cgeo.geocaching.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.TextView; + +/** + * Jelly beans can crash when calculating the layout of a textview. + * + * https://code.google.com/p/android/issues/detail?id=35466 + * + */ +public class IndexOutOfBoundsAvoidingTextView extends TextView { + + public IndexOutOfBoundsAvoidingTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public IndexOutOfBoundsAvoidingTextView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public IndexOutOfBoundsAvoidingTextView(Context context) { + super(context); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + try{ + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } catch (IndexOutOfBoundsException e) { + setText(getText().toString()); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + + @Override + public void setGravity(int gravity){ + try{ + super.setGravity(gravity); + } catch (IndexOutOfBoundsException e) { + setText(getText().toString()); + super.setGravity(gravity); + } + } + + @Override + public void setText(CharSequence text, BufferType type) { + try{ + super.setText(text, type); + } catch (IndexOutOfBoundsException e) { + setText(text.toString()); + } + } +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/ui/LoggingUI.java b/main/src/cgeo/geocaching/ui/LoggingUI.java index ac74dd3..0ee724a 100644 --- a/main/src/cgeo/geocaching/ui/LoggingUI.java +++ b/main/src/cgeo/geocaching/ui/LoggingUI.java @@ -3,7 +3,7 @@ package cgeo.geocaching.ui; import cgeo.geocaching.Geocache; import cgeo.geocaching.LogEntry; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgData; import cgeo.geocaching.activity.IAbstractActivity; import cgeo.geocaching.enumerations.LogType; 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..959cb14 100644 --- a/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java +++ b/main/src/cgeo/geocaching/ui/dialog/CoordinatesInputDialog.java @@ -3,8 +3,8 @@ package cgeo.geocaching.ui.dialog; import cgeo.geocaching.Geocache; import cgeo.geocaching.IGeoData; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; -import cgeo.geocaching.Settings.coordInputFormatEnum; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.settings.Settings.coordInputFormatEnum; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.activity.ActivityMixin; import cgeo.geocaching.compatibility.Compatibility; @@ -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/LiveMapInfoDialogBuilder.java b/main/src/cgeo/geocaching/ui/dialog/LiveMapInfoDialogBuilder.java index 862b1a0..2c4f38d 100644 --- a/main/src/cgeo/geocaching/ui/dialog/LiveMapInfoDialogBuilder.java +++ b/main/src/cgeo/geocaching/ui/dialog/LiveMapInfoDialogBuilder.java @@ -1,7 +1,7 @@ package cgeo.geocaching.ui.dialog; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.cgeoapplication; import android.app.Activity; 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..8da711e --- /dev/null +++ b/main/src/cgeo/geocaching/ui/logs/CacheLogsViewCreator.java @@ -0,0 +1,112 @@ +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 Resources res = cgeoapplication.getInstance().getResources(); + + public CacheLogsViewCreator(CacheDetailActivity cacheDetailActivity, boolean allLogs) { + super(cacheDetailActivity); + this.allLogs = allLogs; + } + + /** + * May return null! + * + * @return + */ + private Geocache getCache() { + if (this.activity instanceof CacheDetailActivity) { + CacheDetailActivity details = (CacheDetailActivity) this.activity; + return details.getCache(); + } + return null; + } + + @Override + protected List<LogEntry> getLogs() { + return allLogs ? getCache().getLogs() : getCache().getFriendsLogs(); + } + + @Override + protected void addHeaderView() { + // adds the log counts + final Map<LogType, Integer> logCounts = getCache().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 getCache() != null; + } + + @Override + protected String getGeocode() { + return getCache().getGeocode(); + } + + @Override + protected UserActionsClickListener createUserActionsListener() { + return new UserActionsClickListener(getCache()); + } + +}
\ 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/GeoDirHandler.java b/main/src/cgeo/geocaching/utils/GeoDirHandler.java index 78455c4..14e6426 100644 --- a/main/src/cgeo/geocaching/utils/GeoDirHandler.java +++ b/main/src/cgeo/geocaching/utils/GeoDirHandler.java @@ -1,8 +1,8 @@ package cgeo.geocaching.utils; import cgeo.geocaching.IGeoData; -import cgeo.geocaching.Settings; import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.settings.Settings; import android.os.Handler; import android.os.Message; @@ -63,6 +63,11 @@ public abstract class GeoDirHandler extends Handler implements IObserver<Object> obtainMessage(OBSERVABLE, o).sendToTarget(); } + public void updateAll() { + update(app.currentGeo()); + update(app.currentDirection()); + } + /** * Update method called when new IGeoData is available. * diff --git a/main/src/cgeo/geocaching/utils/Log.java b/main/src/cgeo/geocaching/utils/Log.java index f912ddd..f7f33d9 100644 --- a/main/src/cgeo/geocaching/utils/Log.java +++ b/main/src/cgeo/geocaching/utils/Log.java @@ -23,11 +23,11 @@ final public class Log { } /** - * make a non persisted copy of the debug flag from the settings for performance reasons - * + * save a copy of the debug flag from the settings for performance reasons + * * @param isDebug */ - public static void setDebugUnsaved(boolean isDebug) { + public static void setDebug(boolean isDebug) { Log.isDebug = isDebug; } diff --git a/main/src/cgeo/geocaching/utils/LogTemplateProvider.java b/main/src/cgeo/geocaching/utils/LogTemplateProvider.java index 2576e64..98201b5 100644 --- a/main/src/cgeo/geocaching/utils/LogTemplateProvider.java +++ b/main/src/cgeo/geocaching/utils/LogTemplateProvider.java @@ -1,12 +1,12 @@ package cgeo.geocaching.utils; +import cgeo.geocaching.Geocache; import cgeo.geocaching.R; -import cgeo.geocaching.Settings; import cgeo.geocaching.Trackable; -import cgeo.geocaching.Geocache; -import cgeo.geocaching.connector.gc.GCConstants; -import cgeo.geocaching.connector.gc.Login; -import cgeo.geocaching.network.Network; +import cgeo.geocaching.connector.ConnectorFactory; +import cgeo.geocaching.connector.IConnector; +import cgeo.geocaching.connector.capability.ILogin; +import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.Formatter; import org.apache.commons.lang3.StringUtils; @@ -14,10 +14,14 @@ import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; /** - * provides all the available templates for logging + * Provides all the available templates for logging. * */ -public class LogTemplateProvider { +public final class LogTemplateProvider { + + private LogTemplateProvider() { + // utility class + } /** * Context aware data container for log templates. @@ -40,54 +44,57 @@ public class LogTemplateProvider { this.trackable = trackable; } - public LogContext(boolean offline) { + public LogContext(final boolean offline) { this(null, offline); } - public LogContext(final Geocache cache, boolean offline) { + public LogContext(final Geocache cache, final boolean offline) { this.cache = cache; this.offline = offline; } - public Geocache getCache() { + public final Geocache getCache() { return cache; } - public Trackable getTrackable() { + public final Trackable getTrackable() { return trackable; } - public boolean isOffline() { + public final boolean isOffline() { return offline; } } - public static abstract class LogTemplate { + public abstract static class LogTemplate { private final String template; private final int resourceId; - protected LogTemplate(String template, int resourceId) { + protected LogTemplate(final String template, final int resourceId) { this.template = template; this.resourceId = resourceId; } - abstract public String getValue(LogContext context); + public abstract String getValue(LogContext context); - public int getResourceId() { + public final int getResourceId() { return resourceId; } - public int getItemId() { + public final int getItemId() { return template.hashCode(); } - public String getTemplateString() { + public final String getTemplateString() { return template; } - protected String apply(String input, LogContext context) { - if (input.contains("[" + template + "]")) { - return StringUtils.replace(input, "[" + template + "]", getValue(context)); + protected final String apply(final String input, final LogContext context) { + final String bracketedTemplate = "[" + template + "]"; + + // check containment first to not unconditionally call the getValue(...) method + if (input.contains(bracketedTemplate)) { + return StringUtils.replace(input, bracketedTemplate, getValue(context)); } return input; } @@ -128,20 +135,32 @@ public class LogTemplateProvider { @Override public String getValue(final LogContext context) { - int current = Login.getActualCachesFound(); + final Geocache cache = context.getCache(); + if (cache == null) { + return StringUtils.EMPTY; + } + + int current = 0; + final IConnector connector = ConnectorFactory.getConnector(cache); + if (connector instanceof ILogin) { + current = ((ILogin) connector).getCachesFound(); + } + + // try updating the login information, if the counter is zero if (current == 0) { if (context.isOffline()) { - return ""; + return StringUtils.EMPTY; + } + if (connector instanceof ILogin) { + ((ILogin) connector).login(null, null); + current = ((ILogin) connector).getCachesFound(); } - final String page = Network.getResponseData(Network.getRequest("http://www.geocaching.com/email/")); - current = parseFindCount(page); } - String findCount = ""; if (current >= 0) { - findCount = String.valueOf(current + 1); + return String.valueOf(current + 1); } - return findCount; + return StringUtils.EMPTY; } }); templates.add(new LogTemplate("OWNER", R.string.init_signature_template_owner) { @@ -156,13 +175,13 @@ public class LogTemplateProvider { if (cache != null) { return cache.getOwnerDisplayName(); } - return ""; + return StringUtils.EMPTY; } }); return templates; } - public static LogTemplate getTemplate(int itemId) { + public static LogTemplate getTemplate(final int itemId) { for (LogTemplate template : getTemplates()) { if (template.getItemId() == itemId) { return template; @@ -171,9 +190,9 @@ public class LogTemplateProvider { return null; } - public static String applyTemplates(String signature, LogContext context) { + public static String applyTemplates(final String signature, final LogContext context) { if (signature == null) { - return ""; + return StringUtils.EMPTY; } String result = signature; for (LogTemplate template : getTemplates()) { @@ -181,17 +200,4 @@ public class LogTemplateProvider { } return result; } - - private static int parseFindCount(String page) { - if (StringUtils.isBlank(page)) { - return -1; - } - - try { - 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 b566b79..3345ff1 100644 --- a/main/src/cgeo/geocaching/utils/ProcessUtils.java +++ b/main/src/cgeo/geocaching/utils/ProcessUtils.java @@ -2,8 +2,14 @@ package cgeo.geocaching.utils; import cgeo.geocaching.cgeoapplication; +import org.apache.commons.collections.CollectionUtils; + import android.content.Intent; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; + +import java.util.List; public final class ProcessUtils { @@ -11,10 +17,44 @@ public final class ProcessUtils { // utility class } - public static boolean isInstalled(final String packageName) { + /** + * 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; @@ -24,8 +64,16 @@ public final 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; } } + + public static boolean isIntentAvailable(final String intent) { + final PackageManager packageManager = cgeoapplication.getInstance().getPackageManager(); + final List<ResolveInfo> list = packageManager.queryIntentActivities( + new Intent(intent), PackageManager.MATCH_DEFAULT_ONLY); + + return CollectionUtils.isNotEmpty(list); + } } diff --git a/main/src/cgeo/geocaching/utils/SimpleCancellableHandler.java b/main/src/cgeo/geocaching/utils/SimpleCancellableHandler.java new file mode 100644 index 0000000..94246e0 --- /dev/null +++ b/main/src/cgeo/geocaching/utils/SimpleCancellableHandler.java @@ -0,0 +1,86 @@ +package cgeo.geocaching.utils; + +import cgeo.geocaching.CacheDetailActivity; +import cgeo.geocaching.activity.AbstractActivity; +import cgeo.geocaching.activity.Progress; + +import android.content.res.Resources; +import android.os.Message; + +import java.lang.ref.WeakReference; + +public class SimpleCancellableHandler extends CancellableHandler { + public static final String SUCCESS_TEXT = "success_message"; + protected final WeakReference<AbstractActivity> activityRef; + protected final WeakReference<Progress> progressDialogRef; + + public SimpleCancellableHandler(final AbstractActivity activity, final Progress progress) { + this.activityRef = new WeakReference<AbstractActivity>(activity); + this.progressDialogRef = new WeakReference<Progress>(progress); + } + + @Override + public void handleRegularMessage(final Message msg) { + AbstractActivity activity = activityRef.get(); + if (activity != null && msg.getData() != null && msg.getData().getString(SUCCESS_TEXT) != null) { + activity.showToast(msg.getData().getString(SUCCESS_TEXT)); + } + Progress progressDialog = progressDialogRef.get(); + if (progressDialog != null) { + progressDialog.dismiss(); + } + return; + } + + @Override + public void handleCancel(final Object extra) { + AbstractActivity activity = activityRef.get(); + if (activity != null) { + activity.showToast((String) extra); + } + Progress progressDialog = progressDialogRef.get(); + if (progressDialog != null) { + progressDialog.dismiss(); + } + } + + public final void showToast(int resId) { + AbstractActivity activity = activityRef.get(); + if (activity != null) { + Resources res = activity.getResources(); + activity.showToast(res.getText(resId).toString()); + } + } + + public final void dismissProgress() { + Progress progressDialog = progressDialogRef.get(); + if (progressDialog != null) { + progressDialog.dismiss(); + } + } + + protected final void setProgressMessage(final String txt) { + Progress progressDialog = progressDialogRef.get(); + if (progressDialog != null) { + progressDialog.setMessage(txt); + } + } + + protected final void finishActivity() { + AbstractActivity activity = activityRef.get(); + if (activity != null) { + activity.finish(); + } + + } + + protected void updateStatusMsg(final int resId, final String msg) { + CacheDetailActivity activity = ((CacheDetailActivity) activityRef.get()); + if (activity != null) { + setProgressMessage(activity.getResources().getString(resId) + + "\n\n" + + msg); + } + } + +} diff --git a/main/src/cgeo/geocaching/utils/SimpleHandler.java b/main/src/cgeo/geocaching/utils/SimpleHandler.java new file mode 100644 index 0000000..554ded6 --- /dev/null +++ b/main/src/cgeo/geocaching/utils/SimpleHandler.java @@ -0,0 +1,65 @@ +package cgeo.geocaching.utils; + +import cgeo.geocaching.activity.AbstractActivity; +import cgeo.geocaching.activity.Progress; + +import android.content.res.Resources; +import android.os.Handler; +import android.os.Message; + +import java.lang.ref.WeakReference; + +public abstract class SimpleHandler extends Handler { + public static final String SUCCESS_TEXT = "success_message"; + protected final WeakReference<AbstractActivity> activityRef; + protected final WeakReference<Progress> progressDialogRef; + + public SimpleHandler(final AbstractActivity activity, final Progress progress) { + this.activityRef = new WeakReference<AbstractActivity>(activity); + this.progressDialogRef = new WeakReference<Progress>(progress); + } + + @Override + public void handleMessage(final Message msg) { + AbstractActivity activity = activityRef.get(); + if (activity != null && msg.getData() != null && msg.getData().getString(SUCCESS_TEXT) != null) { + activity.showToast(msg.getData().getString(SUCCESS_TEXT)); + } + Progress progressDialog = progressDialogRef.get(); + if (progressDialog != null) { + progressDialog.dismiss(); + } + return; + } + + protected final void showToast(final int resId) { + AbstractActivity activity = activityRef.get(); + if (activity != null) { + Resources res = activity.getResources(); + activity.showToast(res.getText(resId).toString()); + } + } + + protected final void dismissProgress() { + Progress progressDialog = progressDialogRef.get(); + if (progressDialog != null) { + progressDialog.dismiss(); + } + } + + protected final void setProgressMessage(final String txt) { + Progress progressDialog = progressDialogRef.get(); + if (progressDialog != null) { + progressDialog.setMessage(txt); + } + } + + protected final void finishActivity() { + AbstractActivity activity = activityRef.get(); + if (activity != null) { + activity.finish(); + } + + } + +} diff --git a/main/src/cgeo/geocaching/utils/TextUtils.java b/main/src/cgeo/geocaching/utils/TextUtils.java index 68ac595..c9d4958 100644 --- a/main/src/cgeo/geocaching/utils/TextUtils.java +++ b/main/src/cgeo/geocaching/utils/TextUtils.java @@ -108,13 +108,13 @@ public final class TextUtils { } /** - * Replaces every \n, \r and \t with a single space. Afterwards multiples spaces + * Replaces every \n, \r and \t with a single space. Afterwards multiple spaces * are merged into a single space. Finally leading spaces are deleted. * * This method must be fast, but may not lead to the shortest replacement String. * * You are only allowed to change this code if you can prove it became faster on a device. - * see cgeo.geocaching.test.WhiteSpaceTest#replaceWhitespaceManually in the test project + * see cgeo.geocaching.test.WhiteSpaceTest#replaceWhitespaceManually in the test project. * * @param data * complete HTML page diff --git a/main/src/cgeo/geocaching/utils/TranslationUtils.java b/main/src/cgeo/geocaching/utils/TranslationUtils.java index 05045ee..1224f7e 100644 --- a/main/src/cgeo/geocaching/utils/TranslationUtils.java +++ b/main/src/cgeo/geocaching/utils/TranslationUtils.java @@ -35,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; |
