diff options
Diffstat (limited to 'main/src')
126 files changed, 1830 insertions, 1055 deletions
diff --git a/main/src/cgeo/geocaching/AddressListActivity.java b/main/src/cgeo/geocaching/AddressListActivity.java index 4f71ab6..4f81f8b 100644 --- a/main/src/cgeo/geocaching/AddressListActivity.java +++ b/main/src/cgeo/geocaching/AddressListActivity.java @@ -1,14 +1,17 @@ package cgeo.geocaching; import cgeo.geocaching.activity.AbstractListActivity; -import cgeo.geocaching.location.Geocoder; +import cgeo.geocaching.location.AndroidGeocoder; +import cgeo.geocaching.location.GCGeocoder; +import cgeo.geocaching.location.MapQuestGeocoder; import cgeo.geocaching.ui.AddressListAdapter; -import org.apache.commons.collections4.CollectionUtils; +import rx.Observable; +import rx.android.observables.AndroidObservable; +import rx.functions.Action1; import android.app.ProgressDialog; import android.location.Address; -import android.os.AsyncTask; import android.os.Bundle; import java.util.List; @@ -19,47 +22,35 @@ public class AddressListActivity extends AbstractListActivity { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState, R.layout.addresslist_activity); - // get parameters - final String keyword = getIntent().getStringExtra(Intents.EXTRA_KEYWORD); - - if (keyword == null) { - showToast(res.getString(R.string.err_search_address_forgot)); - finish(); - return; - } final AddressListAdapter adapter = new AddressListAdapter(this); setListAdapter(adapter); + final String keyword = getIntent().getStringExtra(Intents.EXTRA_KEYWORD); final ProgressDialog waitDialog = ProgressDialog.show(this, res.getString(R.string.search_address_started), keyword, true); waitDialog.setCancelable(true); - lookupAddressInBackground(keyword, adapter, waitDialog); } private void lookupAddressInBackground(final String keyword, final AddressListAdapter adapter, final ProgressDialog waitDialog) { - new AsyncTask<Void, Void, List<Address>>() { - + final Observable<Address> geocoderObservable = new AndroidGeocoder(this).getFromLocationName(keyword) + .onErrorResumeNext(MapQuestGeocoder.getFromLocationName(keyword)) + .onErrorResumeNext(GCGeocoder.getFromLocationName(keyword)); + AndroidObservable.bindActivity(this, geocoderObservable.toList()).subscribe(new Action1<List<Address>>() { @Override - protected List<Address> doInBackground(final Void... params) { - final Geocoder geocoder = new Geocoder(AddressListActivity.this); - return geocoder.getFromLocationName(keyword); - } - - @Override - protected void onPostExecute(final List<Address> addresses) { + public void call(final List<Address> addresses) { waitDialog.dismiss(); - if (CollectionUtils.isNotEmpty(addresses)) { - for (final Address address : addresses) { - adapter.add(address); // don't use addAll, it's only available with API >= 11 - } - } else { - finish(); - CacheListActivity.startActivityAddress(AddressListActivity.this, null, keyword); + for (final Address address : addresses) { + adapter.add(address); // don't use addAll, it's only available with API >= 11 } } - - }.execute(); + }, new Action1<Throwable>() { + @Override + public void call(final Throwable throwable) { + finish(); + showToast(res.getString(R.string.err_unknown_address)); + } + }); } }
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/AttributesGridAdapter.java b/main/src/cgeo/geocaching/AttributesGridAdapter.java new file mode 100644 index 0000000..fd81339 --- /dev/null +++ b/main/src/cgeo/geocaching/AttributesGridAdapter.java @@ -0,0 +1,79 @@ +package cgeo.geocaching; + +import cgeo.geocaching.enumerations.CacheAttribute; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import java.util.List; + +public class AttributesGridAdapter extends BaseAdapter { + private final Context context; + private final Resources resources; + private final List<String> attributes; + private final LayoutInflater inflater; + + public AttributesGridAdapter(final Context context, final Geocache cache) { + this.context = context; + resources = context.getResources(); + attributes = cache.getAttributes(); + inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + @Override + public int getCount() { + return attributes.size(); + } + + @Override + public Object getItem(final int position) { + return attributes.get(position); + } + + @Override + public long getItemId(final int position) { + return 0; + } + + @Override + public View getView(final int position, final View convertView, final ViewGroup parent) { + final FrameLayout attributeLayout; + if (convertView == null) { + attributeLayout = (FrameLayout) inflater.inflate(R.layout.attribute_image, parent, false); + } else { + attributeLayout = (FrameLayout) convertView; + } + + drawAttribute(attributeLayout, attributes.get(position)); + return attributeLayout; + } + + private void drawAttribute(final FrameLayout attributeLayout, final String attributeName) { + final ImageView imageView = (ImageView) attributeLayout.getChildAt(0); + + final boolean strikeThrough = !CacheAttribute.isEnabled(attributeName); + final CacheAttribute attrib = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attributeName)); + if (attrib != null) { + Drawable drawable = resources.getDrawable(attrib.drawableId); + imageView.setImageDrawable(drawable); + if (strikeThrough) { + // generate strike through image with same properties as attribute image + final ImageView strikeThroughImage = new ImageView(context); + strikeThroughImage.setLayoutParams(imageView.getLayoutParams()); + drawable = resources.getDrawable(R.drawable.attribute__strikethru); + strikeThroughImage.setImageDrawable(drawable); + attributeLayout.addView(strikeThroughImage); + } + } else { + imageView.setImageDrawable(resources.getDrawable(R.drawable.attribute_unknown)); + } + } + +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java index e74b2d2..ef3823e 100644 --- a/main/src/cgeo/geocaching/CacheDetailActivity.java +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -12,7 +12,6 @@ import cgeo.geocaching.activity.Progress; import cgeo.geocaching.apps.cache.navi.NavigationAppFactory; import cgeo.geocaching.apps.cache.navi.NavigationSelectionActionProvider; import cgeo.geocaching.apps.cachelist.MapsWithMeCacheListApp; -import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; import cgeo.geocaching.connector.gc.GCConnector; @@ -21,6 +20,8 @@ import cgeo.geocaching.enumerations.CacheAttribute; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.LoadFlags.SaveFlag; import cgeo.geocaching.enumerations.WaypointType; +import cgeo.geocaching.gcvote.GCVote; +import cgeo.geocaching.gcvote.GCVoteDialog; import cgeo.geocaching.list.StoredList; import cgeo.geocaching.location.Units; import cgeo.geocaching.network.HtmlImage; @@ -105,13 +106,11 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.view.ViewParent; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Button; -import android.widget.FrameLayout; +import android.widget.GridView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; @@ -505,6 +504,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc menu.findItem(R.id.menu_store).setVisible(cache != null && !cache.isOffline()); menu.findItem(R.id.menu_delete).setVisible(cache != null && cache.isOffline()); menu.findItem(R.id.menu_refresh).setVisible(cache != null && cache.isOffline()); + menu.findItem(R.id.menu_gcvote).setVisible(cache != null && GCVote.isVotingPossible(cache)); return super.onPrepareOptionsMenu(menu); } @@ -526,6 +526,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc case R.id.menu_refresh: refreshCache(); return true; + case R.id.menu_gcvote: + showVoteDialog(); + return true; default: if (NavigationAppFactory.onMenuItemSelected(item, this, cache)) { return true; @@ -539,6 +542,15 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return super.onOptionsItemSelected(item); } + private void showVoteDialog() { + GCVoteDialog.show(this, cache, new Runnable() { + @Override + public void run() { + notifyDataSetChanged(); + } + }); + } + private static final class CacheDetailsGeoDirHandler extends GeoDirHandler { private final WeakReference<CacheDetailActivity> activityRef; @@ -617,7 +629,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } private void notifyDataSetChanged() { - // This might get called asynchronically when the activity is shut down + // This might get called asynchronous when the activity is shut down if (isFinishing()) { return; } @@ -722,185 +734,6 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } } - private class AttributeViewBuilder { - private ViewGroup attributeIconsLayout; // layout for attribute icons - private ViewGroup attributeDescriptionsLayout; // layout for attribute descriptions - private boolean attributesShowAsIcons = true; // default: show icons - /** - * If the cache is from a non GC source, it might be without icons. Disable switching in those cases. - */ - private boolean noAttributeIconsFound = false; - private int attributeBoxMaxWidth; - - public void fillView(final LinearLayout attributeBox) { - // first ensure that the view is empty - attributeBox.removeAllViews(); - - // maximum width for attribute icons is screen width - paddings of parents - attributeBoxMaxWidth = Compatibility.getDisplayWidth(); - ViewParent child = attributeBox; - do { - if (child instanceof View) { - attributeBoxMaxWidth -= ((View) child).getPaddingLeft() + ((View) child).getPaddingRight(); - } - child = child.getParent(); - } while (child != null); - - // delete views holding description / icons - attributeDescriptionsLayout = null; - attributeIconsLayout = null; - - attributeBox.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(final View v) { - // toggle between attribute icons and descriptions - toggleAttributeDisplay(attributeBox, attributeBoxMaxWidth); - } - }); - - // icons or text? - // - // also show icons when noAttributeImagesFound == true. Explanation: - // 1. no icons could be found in the first invocation of this method - // 2. user refreshes cache from web - // 3. now this method is called again - // 4. attributeShowAsIcons is false but noAttributeImagesFound is true - // => try to show them now - if (attributesShowAsIcons || noAttributeIconsFound) { - showAttributeIcons(attributeBox, attributeBoxMaxWidth); - } else { - showAttributeDescriptions(attributeBox); - } - } - - /** - * lazy-creates the layout holding the icons of the caches attributes - * and makes it visible - */ - private void showAttributeIcons(final LinearLayout attribBox, final int parentWidth) { - if (attributeIconsLayout == null) { - attributeIconsLayout = createAttributeIconsLayout(parentWidth); - // no matching icons found? show text - if (noAttributeIconsFound) { - showAttributeDescriptions(attribBox); - return; - } - } - attribBox.removeAllViews(); - attribBox.addView(attributeIconsLayout); - attributesShowAsIcons = true; - } - - /** - * lazy-creates the layout holding the descriptions of the caches attributes - * and makes it visible - */ - private void showAttributeDescriptions(final LinearLayout attribBox) { - if (attributeDescriptionsLayout == null) { - attributeDescriptionsLayout = createAttributeDescriptionsLayout(attribBox); - } - attribBox.removeAllViews(); - attribBox.addView(attributeDescriptionsLayout); - attributesShowAsIcons = false; - } - - /** - * toggle attribute descriptions and icons - */ - private void toggleAttributeDisplay(final LinearLayout attribBox, final int parentWidth) { - // Don't toggle when there are no icons to show. - if (noAttributeIconsFound) { - return; - } - - // toggle - if (attributesShowAsIcons) { - showAttributeDescriptions(attribBox); - } else { - showAttributeIcons(attribBox, parentWidth); - } - } - - private ViewGroup createAttributeIconsLayout(final int parentWidth) { - final LinearLayout rows = new LinearLayout(CacheDetailActivity.this); - rows.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); - rows.setOrientation(LinearLayout.VERTICAL); - - LinearLayout attributeRow = newAttributeIconsRow(); - rows.addView(attributeRow); - - noAttributeIconsFound = true; - - for (final String attributeName : cache.getAttributes()) { - // check if another attribute icon fits in this row - attributeRow.measure(0, 0); - final int rowWidth = attributeRow.getMeasuredWidth(); - final FrameLayout fl = (FrameLayout) getLayoutInflater().inflate(R.layout.attribute_image, attributeRow, false); - final ImageView iv = (ImageView) fl.getChildAt(0); - if ((parentWidth - rowWidth) < iv.getLayoutParams().width) { - // make a new row - attributeRow = newAttributeIconsRow(); - rows.addView(attributeRow); - } - - final boolean strikeThrough = !CacheAttribute.isEnabled(attributeName); - final CacheAttribute attrib = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attributeName)); - if (attrib != null) { - noAttributeIconsFound = false; - Drawable drawable = res.getDrawable(attrib.drawableId); - iv.setImageDrawable(drawable); - // strike through? - if (strikeThrough) { - // generate strike through image with same properties as attribute image - final ImageView strikeThroughImage = new ImageView(CacheDetailActivity.this); - strikeThroughImage.setLayoutParams(iv.getLayoutParams()); - drawable = res.getDrawable(R.drawable.attribute__strikethru); - strikeThroughImage.setImageDrawable(drawable); - fl.addView(strikeThroughImage); - } - } else { - iv.setImageDrawable(res.getDrawable(R.drawable.attribute_unknown)); - } - - attributeRow.addView(fl); - } - - return rows; - } - - private LinearLayout newAttributeIconsRow() { - final LinearLayout rowLayout = new LinearLayout(CacheDetailActivity.this); - rowLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.WRAP_CONTENT)); - rowLayout.setOrientation(LinearLayout.HORIZONTAL); - return rowLayout; - } - - private ViewGroup createAttributeDescriptionsLayout(final LinearLayout parentView) { - final LinearLayout descriptions = (LinearLayout) getLayoutInflater().inflate( - R.layout.attribute_descriptions, parentView, false); - final TextView attribView = (TextView) descriptions.getChildAt(0); - - final StringBuilder buffer = new StringBuilder(); - for (String attributeName : cache.getAttributes()) { - final boolean enabled = CacheAttribute.isEnabled(attributeName); - // search for a translation of the attribute - final CacheAttribute attrib = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attributeName)); - if (attrib != null) { - attributeName = attrib.getL10n(enabled); - } - if (buffer.length() > 0) { - buffer.append('\n'); - } - buffer.append(attributeName); - } - - attribView.setText(buffer); - - return descriptions; - } - } - private void refreshCache() { if (progress.isShowing()) { showToast(res.getString(R.string.err_detail_still_working)); @@ -916,7 +749,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc progress.show(this, res.getString(R.string.cache_dialog_refresh_title), res.getString(R.string.cache_dialog_refresh_message), true, refreshCacheHandler.cancelMessage()); - cache.refresh(refreshCacheHandler, RxUtils.networkScheduler); + cache.refresh(refreshCacheHandler, RxUtils.refreshScheduler); } private void dropCache() { @@ -1043,11 +876,9 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc } // cache attributes - if (!cache.getAttributes().isEmpty()) { - final LinearLayout innerLayout = ButterKnife.findById(view, R.id.attributes_innerbox); - new AttributeViewBuilder().fillView(innerLayout); - view.findViewById(R.id.attributes_box).setVisibility(View.VISIBLE); - } + updateAttributesText(); + updateAttributesIcons(); + ButterKnife.findById(view, R.id.attributes_box).setVisibility(cache.getAttributes().isEmpty() ? View.GONE : View.VISIBLE); updateOfflineBox(view, cache, res, new RefreshCacheClickListener(), new DropCacheClickListener(), new StoreCacheClickListener()); @@ -1086,6 +917,61 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc return view; } + private void updateAttributesIcons() { + final GridView gridView = ButterKnife.findById(view, R.id.attributes_grid); + final List<String> attributes = cache.getAttributes(); + if (attributes.isEmpty()) { + gridView.setVisibility(View.GONE); + return; + } + gridView.setAdapter(new AttributesGridAdapter(CacheDetailActivity.this, cache)); + gridView.setVisibility(View.VISIBLE); + gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(final android.widget.AdapterView<?> parent, final View view, final int position, final long id) { + toggleAttributesView(); + } + }); + } + + protected void toggleAttributesView() { + final View textView = ButterKnife.findById(view, R.id.attributes_text); + textView.setVisibility(textView.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); + final View gridView = ButterKnife.findById(view, R.id.attributes_grid); + gridView.setVisibility(gridView.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); + } + + private void updateAttributesText() { + final TextView attribView = ButterKnife.findById(view, R.id.attributes_text); + final List<String> attributes = cache.getAttributes(); + if (attributes.isEmpty()) { + attribView.setVisibility(View.GONE); + return; + } + final StringBuilder text = new StringBuilder(); + for (String attributeName : attributes) { + final boolean enabled = CacheAttribute.isEnabled(attributeName); + // search for a translation of the attribute + final CacheAttribute attrib = CacheAttribute.getByRawName(CacheAttribute.trimAttributeName(attributeName)); + if (attrib != null) { + attributeName = attrib.getL10n(enabled); + } + if (text.length() > 0) { + text.append('\n'); + } + text.append(attributeName); + } + attribView.setText(text); + attribView.setVisibility(View.GONE); + attribView.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(final View v) { + toggleAttributesView(); + } + }); + } + private class StoreCacheClickListener implements View.OnClickListener { @Override public void onClick(final View arg0) { @@ -1304,11 +1190,10 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc buttonRemove.setEnabled(false); buttonRemove.setVisibility(View.GONE); } - } /** - * Show/hide buttons, set text in favourite line and box + * Show/hide buttons, set text in favorite line and box */ private void updateFavPointBox() { // Favorite counts @@ -1361,12 +1246,7 @@ public class CacheDetailActivity extends AbstractViewPagerActivity<CacheDetailAc // update text final TextView text = ButterKnife.findById(view, R.id.list_text); final StoredList list = DataStore.getList(cache.getListId()); - if (list != null) { - text.setText(res.getString(R.string.cache_list_text) + " " + list.title); - } else { - // this should not happen - text.setText(R.string.cache_list_unknown); - } + text.setText(res.getString(R.string.cache_list_text) + " " + list.title); } else { // hide box box.setVisibility(View.GONE); diff --git a/main/src/cgeo/geocaching/CacheListActivity.java b/main/src/cgeo/geocaching/CacheListActivity.java index c8f1101..4b84d08 100644 --- a/main/src/cgeo/geocaching/CacheListActivity.java +++ b/main/src/cgeo/geocaching/CacheListActivity.java @@ -27,7 +27,6 @@ import cgeo.geocaching.list.PseudoList; import cgeo.geocaching.list.StoredList; import cgeo.geocaching.loaders.AbstractSearchLoader; import cgeo.geocaching.loaders.AbstractSearchLoader.CacheListLoaderType; -import cgeo.geocaching.loaders.AddressGeocacheListLoader; import cgeo.geocaching.loaders.CoordsGeocacheListLoader; import cgeo.geocaching.loaders.FinderGeocacheListLoader; import cgeo.geocaching.loaders.HistoryGeocacheListLoader; @@ -44,6 +43,7 @@ import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Send2CgeoDownloader; import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.sorting.CacheComparator; @@ -296,21 +296,18 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA progress.setMessage(res.getString(R.string.caches_downloading) + " " + res.getQuantityString(R.plurals.caches_eta_mins, minutesRemaining, minutesRemaining)); } } else { - new AsyncTask<Void, Void, Void>() { + new AsyncTask<Void, Void, Set<Geocache>>() { @Override - protected Void doInBackground(final Void... params) { - if (search != null) { - final Set<Geocache> cacheListTmp = search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB); - if (CollectionUtils.isNotEmpty(cacheListTmp)) { - cacheList.clear(); - cacheList.addAll(cacheListTmp); - } - } - return null; + protected Set<Geocache> doInBackground(final Void... params) { + return search != null ? search.getCachesFromSearchResult(LoadFlags.LOAD_CACHE_OR_DB) : null; } @Override - protected void onPostExecute(final Void result) { + protected void onPostExecute(final Set<Geocache> result) { + if (CollectionUtils.isNotEmpty(result)) { + cacheList.clear(); + cacheList.addAll(result); + } setAdapterCurrentCoordinates(false); showProgress(false); @@ -412,7 +409,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA } } if (type == CacheListType.NEAREST) { - coords = CgeoApplication.getInstance().currentGeo().getCoords(); + coords = Sensors.getInstance().currentGeo().getCoords(); } setTitle(title); @@ -542,7 +539,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA } private void setAdapterCurrentCoordinates(final boolean forceSort) { - adapter.setActualCoordinates(app.currentGeo().getCoords()); + adapter.setActualCoordinates(Sensors.getInstance().currentGeo().getCoords()); if (forceSort) { adapter.forceSort(); } @@ -1295,9 +1292,6 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA title = res.getString(R.string.list_all_lists); } else { final StoredList list = DataStore.getList(id); - if (list == null) { - return; - } listId = list.id; title = list.title; } @@ -1549,12 +1543,7 @@ public class CacheListActivity extends AbstractListActivity implements FilteredA } else { title = coords.toString(); } - if (coords != null) { - loader = new CoordsGeocacheListLoader(app, coords); - } - else { - loader = new AddressGeocacheListLoader(app, address); - } + loader = new CoordsGeocacheListLoader(app, coords); break; case FINDER: final String username = extras.getString(Intents.EXTRA_USERNAME); diff --git a/main/src/cgeo/geocaching/CacheMenuHandler.java b/main/src/cgeo/geocaching/CacheMenuHandler.java index 82eb061..fa2f994 100644 --- a/main/src/cgeo/geocaching/CacheMenuHandler.java +++ b/main/src/cgeo/geocaching/CacheMenuHandler.java @@ -90,7 +90,6 @@ public final class CacheMenuHandler extends AbstractUIFactory { menu.findItem(R.id.menu_navigate).setVisible(hasCoords); menu.findItem(R.id.menu_caches_around).setVisible(hasCoords && cache.supportsCachesAround()); menu.findItem(R.id.menu_calendar).setVisible(cache.canBeAddedToCalendar()); - menu.findItem(R.id.menu_show_in_browser).setVisible(cache.canOpenInBrowser()); menu.findItem(R.id.menu_log_visit).setVisible(cache.supportsLogging() && !Settings.getLogOffline()); menu.findItem(R.id.menu_log_visit_offline).setVisible(cache.supportsLogging() && Settings.getLogOffline()); diff --git a/main/src/cgeo/geocaching/CgeoApplication.java b/main/src/cgeo/geocaching/CgeoApplication.java index eea8154..2217e11 100644 --- a/main/src/cgeo/geocaching/CgeoApplication.java +++ b/main/src/cgeo/geocaching/CgeoApplication.java @@ -1,12 +1,6 @@ package cgeo.geocaching; -import cgeo.geocaching.playservices.LocationProvider; -import cgeo.geocaching.sensors.GeoData; -import cgeo.geocaching.sensors.GeoDataProvider; -import cgeo.geocaching.sensors.GpsStatusProvider; -import cgeo.geocaching.sensors.GpsStatusProvider.Status; -import cgeo.geocaching.sensors.OrientationProvider; -import cgeo.geocaching.sensors.RotationProvider; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Log; import cgeo.geocaching.utils.OOMDumpingUncaughtExceptionHandler; @@ -17,10 +11,6 @@ import com.google.android.gms.common.GooglePlayServicesUtil; import org.eclipse.jdt.annotation.NonNull; -import rx.Observable; -import rx.functions.Action1; -import rx.functions.Func1; - import android.app.Application; import android.view.ViewConfiguration; @@ -32,21 +22,7 @@ public class CgeoApplication extends Application { public boolean showLoginToast = true; //login toast shown just once. private boolean liveMapHintShownInThisSession = false; // livemap hint has been shown private static CgeoApplication instance; - private Observable<GeoData> geoDataObservable; - private Observable<GeoData> geoDataObservableLowPower; - private Observable<Float> directionObservable; - private Observable<Status> gpsStatusObservable; - @NonNull private volatile GeoData currentGeo = GeoData.DUMMY_LOCATION; - private volatile boolean hasValidLocation = false; - private volatile float currentDirection = 0.0f; private boolean isGooglePlayServicesAvailable = false; - private final Action1<GeoData> rememberGeodataAction = new Action1<GeoData>() { - @Override - public void call(final GeoData geoData) { - currentGeo = geoData; - hasValidLocation = true; - } - }; public static void dumpOnOutOfMemory(final boolean enable) { @@ -66,7 +42,7 @@ public class CgeoApplication extends Application { setInstance(this); } - private static void setInstance(final CgeoApplication application) { + private static void setInstance(@NonNull final CgeoApplication application) { instance = application; } @@ -95,47 +71,14 @@ public class CgeoApplication extends Application { isGooglePlayServicesAvailable = true; } Log.i("Google Play services are " + (isGooglePlayServicesAvailable ? "" : "not ") + "available"); - setupGeoDataObservables(Settings.useGooglePlayServices(), Settings.useLowPowerMode()); - setupDirectionObservable(Settings.useLowPowerMode()); - gpsStatusObservable = GpsStatusProvider.create(this).replay(1).refCount(); + final Sensors sensors = Sensors.getInstance(); + sensors.setupGeoDataObservables(Settings.useGooglePlayServices(), Settings.useLowPowerMode()); + sensors.setupDirectionObservable(Settings.useLowPowerMode()); // Attempt to acquire an initial location before any real activity happens. - geoDataObservableLowPower.subscribeOn(RxUtils.looperCallbacksScheduler).first().subscribe(); - } - - public void setupGeoDataObservables(final boolean useGooglePlayServices, final boolean useLowPowerLocation) { - if (useGooglePlayServices) { - geoDataObservable = LocationProvider.getMostPrecise(this).doOnNext(rememberGeodataAction); - if (useLowPowerLocation) { - geoDataObservableLowPower = LocationProvider.getLowPower(this).doOnNext(rememberGeodataAction); - } else { - geoDataObservableLowPower = geoDataObservable; - } - } else { - geoDataObservable = RxUtils.rememberLast(GeoDataProvider.create(this).doOnNext(rememberGeodataAction)); - geoDataObservableLowPower = geoDataObservable; - } + sensors.geoDataObservable(true).subscribeOn(RxUtils.looperCallbacksScheduler).first().subscribe(); } - public void setupDirectionObservable(final boolean useLowPower) { - directionObservable = RxUtils.rememberLast(RotationProvider.create(this, useLowPower).onErrorResumeNext(new Func1<Throwable, Observable<? extends Float>>() { - @Override - public Observable<? extends Float> call(final Throwable throwable) { - return OrientationProvider.create(CgeoApplication.this); - } - }).onErrorResumeNext(new Func1<Throwable, Observable<? extends Float>>() { - @Override - public Observable<? extends Float> call(final Throwable throwable) { - Log.e("Device orientation will not be available as no suitable sensors were found"); - return Observable.<Float>never().startWith(0.0f); - } - }).doOnNext(new Action1<Float>() { - @Override - public void call(final Float direction) { - currentDirection = direction; - } - })); - } @Override public void onLowMemory() { @@ -143,34 +86,6 @@ public class CgeoApplication extends Application { DataStore.removeAllFromCache(); } - public Observable<GeoData> geoDataObservable(final boolean lowPower) { - return lowPower ? geoDataObservableLowPower : geoDataObservable; - } - - public Observable<Float> directionObservable() { - return directionObservable; - } - - public Observable<Status> gpsStatusObservable() { - if (gpsStatusObservable == null) { - gpsStatusObservable = GpsStatusProvider.create(this).share(); - } - return gpsStatusObservable; - } - - @NonNull - public GeoData currentGeo() { - return currentGeo; - } - - public boolean hasValidLocation() { - return hasValidLocation; - } - - public float currentDirection() { - return currentDirection; - } - public boolean isLiveMapHintShownInThisSession() { return liveMapHintShownInThisSession; } diff --git a/main/src/cgeo/geocaching/CompassActivity.java b/main/src/cgeo/geocaching/CompassActivity.java index 1a1bad2..8b0fc73 100644 --- a/main/src/cgeo/geocaching/CompassActivity.java +++ b/main/src/cgeo/geocaching/CompassActivity.java @@ -11,6 +11,7 @@ import cgeo.geocaching.maps.CGeoMap; import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; import cgeo.geocaching.sensors.GpsStatusProvider.Status; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.speech.SpeechService; import cgeo.geocaching.ui.CompassView; @@ -29,8 +30,6 @@ import rx.functions.Action1; 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.view.Menu; @@ -59,7 +58,6 @@ public class CompassActivity extends AbstractActionBarActivity { private Geocache cache = null; private Geopoint dstCoords = null; private float cacheHeading = 0; - private boolean hasMagneticFieldSensor; private String description; @Override @@ -67,12 +65,6 @@ public class CompassActivity extends AbstractActionBarActivity { super.onCreate(savedInstanceState, R.layout.compass_activity); ButterKnife.inject(this); - final SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); - hasMagneticFieldSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null; - if (!hasMagneticFieldSensor) { - Settings.setUseCompass(false); - } - // get parameters final Bundle extras = getIntent().getExtras(); if (extras == null) { @@ -89,12 +81,13 @@ public class CompassActivity extends AbstractActionBarActivity { // find the wanted navigation target if (extras.containsKey(Intents.EXTRA_WAYPOINT_ID)) { final int waypointId = extras.getInt(Intents.EXTRA_WAYPOINT_ID); - setTarget(DataStore.loadWaypoint(waypointId)); + final Waypoint waypoint = DataStore.loadWaypoint(waypointId); + if (waypoint != null) { + setTarget(waypoint); + } } else if (extras.containsKey(Intents.EXTRA_COORDS)) { - final Geopoint coords = extras.getParcelable(Intents.EXTRA_COORDS); - final String description = extras.getString(Intents.EXTRA_DESCRIPTION); - setTarget(coords, description); + setTarget(extras.<Geopoint>getParcelable(Intents.EXTRA_COORDS), extras.getString(Intents.EXTRA_DESCRIPTION)); } else { setTarget(cache); @@ -115,7 +108,7 @@ public class CompassActivity extends AbstractActionBarActivity { @Override public void onResume() { super.onResume(geoDirHandler.start(GeoDirHandler.UPDATE_GEODIR), - app.gpsStatusObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(gpsStatusHandler)); + Sensors.getInstance().gpsStatusObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(gpsStatusHandler)); forceRefresh(); } @@ -139,15 +132,14 @@ public class CompassActivity extends AbstractActionBarActivity { private void forceRefresh() { // Force a refresh of location and direction when data is available. - final CgeoApplication app = CgeoApplication.getInstance(); - final GeoData geo = app.currentGeo(); - geoDirHandler.updateGeoDir(geo, app.currentDirection()); + final Sensors sensors = Sensors.getInstance(); + geoDirHandler.updateGeoDir(sensors.currentGeo(), sensors.currentDirection()); } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.compass_activity_options, menu); - menu.findItem(R.id.menu_compass_sensor).setVisible(hasMagneticFieldSensor); + menu.findItem(R.id.menu_compass_sensor).setVisible(Sensors.getInstance().hasMagneticFieldSensor()); if (cache != null) { LoggingUI.addMenuItems(this, menu, cache); } @@ -226,12 +218,12 @@ public class CompassActivity extends AbstractActionBarActivity { return super.onOptionsItemSelected(item); } - private void setTarget(final Geopoint coords, final String description) { + private void setTarget(@NonNull final Geopoint coords, final String newDescription) { setDestCoords(coords); - setTargetDescription(description); - updateDistanceInfo(app.currentGeo()); + setTargetDescription(newDescription); + updateDistanceInfo(Sensors.getInstance().currentGeo()); - Log.d("destination set: " + description + " (" + dstCoords + ")"); + Log.d("destination set: " + newDescription + " (" + dstCoords + ")"); } private void setTarget(final @NonNull Waypoint waypoint) { @@ -253,12 +245,12 @@ public class CompassActivity extends AbstractActionBarActivity { private void setTargetDescription(final @Nullable String newDescription) { description = newDescription; - if (description == null) { + if (this.description == null) { cacheInfoView.setVisibility(View.GONE); return; } cacheInfoView.setVisibility(View.VISIBLE); - cacheInfoView.setText(description); + cacheInfoView.setText(this.description); } private void updateDistanceInfo(final GeoData geo) { diff --git a/main/src/cgeo/geocaching/CreateShortcutActivity.java b/main/src/cgeo/geocaching/CreateShortcutActivity.java index 01754e2..70ab900 100644 --- a/main/src/cgeo/geocaching/CreateShortcutActivity.java +++ b/main/src/cgeo/geocaching/CreateShortcutActivity.java @@ -99,9 +99,6 @@ public class CreateShortcutActivity extends AbstractActionBarActivity { protected void createOfflineListShortcut(final int listId) { final StoredList list = DataStore.getList(listId); - if (list == null) { - return; - } // target to be executed by the shortcut final Intent targetIntent = new Intent(this, CacheListActivity.class); targetIntent.putExtra(Intents.EXTRA_LIST_ID, list.id); diff --git a/main/src/cgeo/geocaching/DataStore.java b/main/src/cgeo/geocaching/DataStore.java index 3403f3a..b5a4a7a 100644 --- a/main/src/cgeo/geocaching/DataStore.java +++ b/main/src/cgeo/geocaching/DataStore.java @@ -374,6 +374,7 @@ public class DataStore { database = null; } + @NonNull public static File getBackupFileInternal() { return new File(LocalStorage.getStorage(), "cgeo.sqlite"); } @@ -441,14 +442,17 @@ public class DataStore { }); } + @NonNull private static File databasePath(final boolean internal) { return new File(internal ? LocalStorage.getInternalDbDirectory() : LocalStorage.getExternalDbDirectory(), dbName); } + @NonNull private static File databasePath() { return databasePath(!Settings.isDbOnSDCard()); } + @NonNull private static File databaseAlternatePath() { return databasePath(Settings.isDbOnSDCard()); } @@ -1029,6 +1033,7 @@ public class DataStore { return false; } + @Nullable public static String getGeocodeForGuid(final String guid) { if (StringUtils.isBlank(guid)) { return null; @@ -1104,7 +1109,9 @@ public class DataStore { for (final Geocache cache : caches) { final String geocode = cache.getGeocode(); final Geocache existingCache = existingCaches.get(geocode); - final boolean dbUpdateRequired = !cache.gatherMissingFrom(existingCache) || cacheCache.getCacheFromCache(geocode) != null; + boolean dbUpdateRequired = !cache.gatherMissingFrom(existingCache) || cacheCache.getCacheFromCache(geocode) != null; + // parse the note AFTER merging the local information in + dbUpdateRequired |= cache.parseWaypointsFromNote(); cache.addStorageLocation(StorageLocation.CACHE); cacheCache.putCacheInCache(cache); @@ -1150,7 +1157,7 @@ public class DataStore { values.put("hidden", hiddenDate.getTime()); } values.put("hint", cache.getHint()); - values.put("size", cache.getSize() == null ? "" : cache.getSize().id); + values.put("size", cache.getSize().id); values.put("difficulty", cache.getDifficulty()); values.put("terrain", cache.getTerrain()); values.put("location", cache.getLocation()); @@ -1340,6 +1347,7 @@ public class DataStore { * index of the longitude column * @return the coordinates, or null if latitude or longitude is null or the coordinates are invalid */ + @Nullable private static Geopoint getCoords(final Cursor cursor, final int indexLat, final int indexLon) { if (cursor.isNull(indexLat) || cursor.isNull(indexLon)) { return null; @@ -1517,6 +1525,7 @@ public class DataStore { } } + @Nullable public static Viewport getBounds(final Set<String> geocodes) { if (CollectionUtils.isEmpty(geocodes)) { return null; @@ -1533,6 +1542,7 @@ public class DataStore { * The Geocode GCXXXX * @return the loaded cache (if found). Can be null */ + @Nullable public static Geocache loadCache(final String geocode, final EnumSet<LoadFlag> loadFlags) { if (StringUtils.isBlank(geocode)) { throw new IllegalArgumentException("geocode must not be empty"); @@ -1548,6 +1558,7 @@ public class DataStore { * @param geocodes * @return Set of loaded caches. Never null. */ + @NonNull public static Set<Geocache> loadCaches(final Collection<String> geocodes, final EnumSet<LoadFlag> loadFlags) { if (CollectionUtils.isEmpty(geocodes)) { return new HashSet<>(); @@ -1604,6 +1615,7 @@ public class DataStore { * @param loadFlags * @return Set of loaded caches. Never null. */ + @NonNull private static Set<Geocache> loadCachesFromGeocodes(final Set<String> geocodes, final EnumSet<LoadFlag> loadFlags) { if (CollectionUtils.isEmpty(geocodes)) { return Collections.emptySet(); @@ -1695,6 +1707,7 @@ public class DataStore { * @return */ + @NonNull private static StringBuilder buildCoordinateWhere(final String dbTable, final Viewport viewport) { return viewport.resize(1.5).sqlWhere(dbTable); } @@ -1705,6 +1718,7 @@ public class DataStore { * @param cursor * @return Cache from DB */ + @NonNull private static Geocache createCacheFromDatabaseContent(final Cursor cursor) { final Geocache cache = new Geocache(); @@ -1766,6 +1780,7 @@ public class DataStore { return cache; } + @Nullable public static List<String> loadAttributes(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -1783,6 +1798,7 @@ public class DataStore { GET_STRING_0); } + @Nullable public static Waypoint loadWaypoint(final int id) { if (id == 0) { return null; @@ -1809,6 +1825,7 @@ public class DataStore { return waypoint; } + @Nullable public static List<Waypoint> loadWaypoints(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -1831,6 +1848,7 @@ public class DataStore { }); } + @NonNull private static Waypoint createWaypointFromDatabaseContent(final Cursor cursor) { final String name = cursor.getString(cursor.getColumnIndex("name")); final WaypointType type = WaypointType.findById(cursor.getString(cursor.getColumnIndex("type"))); @@ -1847,6 +1865,7 @@ public class DataStore { return waypoint; } + @Nullable private static List<Image> loadSpoilers(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -1875,6 +1894,7 @@ public class DataStore { * * @return A list of previously entered destinations or an empty list. */ + @NonNull public static List<Destination> loadHistoryOfSearchedLocations() { return queryToColl(dbTableSearchDestinationHistory, new String[]{"_id", "date", "latitude", "longitude"}, @@ -1953,6 +1973,7 @@ public class DataStore { return Collections.unmodifiableList(logs); } + @Nullable public static Map<LogType, Integer> loadLogCounts(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -1981,6 +2002,7 @@ public class DataStore { return logCounts; } + @Nullable private static List<Trackable> loadInventory(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -2034,6 +2056,7 @@ public class DataStore { return trackable; } + @NonNull private static Trackable createTrackableFromDatabaseContent(final Cursor cursor) { final Trackable trackable = new Trackable(); trackable.setGeocode(cursor.getString(cursor.getColumnIndex("tbcode"))); @@ -2113,6 +2136,7 @@ public class DataStore { return 0; } + @NonNull private static<T, U extends Collection<? super T>> U queryToColl(@NonNull final String table, final String[] columns, final String selection, @@ -2148,6 +2172,7 @@ public class DataStore { * @param listId * @return a non-null set of geocodes */ + @NonNull private static Set<String> loadBatchOfStoredGeocodes(final Geopoint coords, final CacheType cacheType, final int listId) { if (cacheType == null) { throw new IllegalArgumentException("cacheType must not be null"); @@ -2194,6 +2219,7 @@ public class DataStore { } } + @NonNull private static Set<String> loadBatchOfHistoricGeocodes(final boolean detailedOnly, final CacheType cacheType) { final StringBuilder selection = new StringBuilder("visiteddate > 0"); @@ -2225,11 +2251,13 @@ public class DataStore { } /** Retrieve all stored caches from DB */ + @NonNull public static SearchResult loadCachedInViewport(final Viewport viewport, final CacheType cacheType) { return loadInViewport(false, viewport, cacheType); } /** Retrieve stored caches from DB with listId >= 1 */ + @NonNull public static SearchResult loadStoredInViewport(final Viewport viewport, final CacheType cacheType) { return loadInViewport(true, viewport, cacheType); } @@ -2242,6 +2270,7 @@ public class DataStore { * @param cacheType the cache type * @return the matching caches */ + @NonNull private static SearchResult loadInViewport(final boolean stored, final Viewport viewport, final CacheType cacheType) { final Set<String> geocodes = new HashSet<>(); @@ -2335,6 +2364,9 @@ public class DataStore { Log.d("Database clean: removing obsolete log images records"); database.delete(dbTableLogImages, "log_id NOT IN (SELECT _id FROM " + dbTableLogs + ")", null); + // Remove the obsolete "_others" directory where the user avatar used to be stored. + LocalStorage.deleteDirectory(LocalStorage.getStorageDir("_others")); + if (version > -1) { Settings.setVersion(version); } @@ -2353,7 +2385,8 @@ public class DataStore { * @param geocodes * @return */ - private static Set<String> exceptCachesWithOfflineLog(final Set<String> geocodes) { + @NonNull + private static Set<String> exceptCachesWithOfflineLog(@NonNull final Set<String> geocodes) { if (geocodes.isEmpty()) { return geocodes; } @@ -2462,6 +2495,7 @@ public class DataStore { return id != -1; } + @Nullable public static LogEntry loadLogOffline(final String geocode) { if (StringUtils.isBlank(geocode)) { return null; @@ -2580,6 +2614,7 @@ public class DataStore { return lists; } + @NonNull private static ArrayList<StoredList> getListsFromCursor(final Cursor cursor) { final int indexId = cursor.getColumnIndex("_id"); final int indexTitle = cursor.getColumnIndex("title"); @@ -2593,6 +2628,7 @@ public class DataStore { }); } + @NonNull public static StoredList getList(final int id) { init(); if (id >= customListIdOffset) { @@ -2616,11 +2652,7 @@ public class DataStore { } // fall back to standard list in case of invalid list id - if (id == StoredList.STANDARD_LIST_ID || id >= customListIdOffset) { - return new StoredList(StoredList.STANDARD_LIST_ID, res.getString(R.string.list_inbox), (int) PreparedStatement.COUNT_CACHES_ON_STANDARD_LIST.simpleQueryForLong()); - } - - return null; + return new StoredList(StoredList.STANDARD_LIST_ID, res.getString(R.string.list_inbox), (int) PreparedStatement.COUNT_CACHES_ON_STANDARD_LIST.simpleQueryForLong()); } public static int getAllCachesCount() { @@ -2794,6 +2826,7 @@ public class DataStore { * @param geocode * @return */ + @NonNull public static Geocache loadCacheTexts(final String geocode) { final Geocache partial = new Geocache(); @@ -2851,6 +2884,7 @@ public class DataStore { * Creates the WHERE clause for matching multiple geocodes. This automatically converts all given codes to * UPPERCASE. */ + @NonNull private static StringBuilder whereGeocodeIn(final Collection<String> geocodes) { final StringBuilder whereExpr = new StringBuilder("geocode in ("); final Iterator<String> iterator = geocodes.iterator(); @@ -2873,6 +2907,7 @@ public class DataStore { * @return */ + @NonNull public static Set<Waypoint> loadWaypoints(final Viewport viewport, final boolean excludeMine, final boolean excludeDisabled, final CacheType type) { final StringBuilder where = buildCoordinateWhere(dbTableWaypoints, viewport); if (excludeMine) { @@ -2969,6 +3004,7 @@ public class DataStore { moveToList(caches, StoredList.TEMPORARY_LIST.id); } + @Nullable public static Viewport getBounds(final String geocode) { if (geocode == null) { return null; @@ -2981,11 +3017,13 @@ public class DataStore { setVisitDate(Arrays.asList(selected), 0); } + @NonNull public static SearchResult getBatchOfStoredCaches(final Geopoint coords, final CacheType cacheType, final int listId) { final Set<String> geocodes = DataStore.loadBatchOfStoredGeocodes(coords, cacheType, listId); return new SearchResult(geocodes, DataStore.getAllStoredCachesCount(cacheType, listId)); } + @NonNull public static SearchResult getHistoryOfCaches(final boolean detailedOnly, final CacheType cacheType) { final Set<String> geocodes = DataStore.loadBatchOfHistoricGeocodes(detailedOnly, cacheType); return new SearchResult(geocodes, DataStore.getAllHistoryCachesCount()); @@ -2999,6 +3037,7 @@ public class DataStore { return false; } + @NonNull public static Set<String> getCachedMissingFromSearch(final SearchResult searchResult, final Set<Tile> tiles, final IConnector connector, final int maxZoom) { // get cached CacheListActivity @@ -3029,6 +3068,7 @@ public class DataStore { return missingFromSearch; } + @Nullable public static Cursor findSuggestions(final String searchTerm) { // require 3 characters, otherwise there are to many results if (StringUtils.length(searchTerm) < 3) { @@ -3064,6 +3104,7 @@ public class DataStore { cursor.close(); } + @NonNull private static String getSuggestionArgument(final String input) { return "%" + StringUtils.trim(input) + "%"; } @@ -3091,6 +3132,7 @@ public class DataStore { cursor.close(); } + @NonNull public static String[] getSuggestions(final String table, final String column, final String input) { try { final Cursor cursor = database.rawQuery("SELECT DISTINCT " + column @@ -3104,22 +3146,27 @@ public class DataStore { } } + @NonNull public static String[] getSuggestionsOwnerName(final String input) { return getSuggestions(dbTableCaches, "owner_real", input); } + @NonNull public static String[] getSuggestionsTrackableCode(final String input) { return getSuggestions(dbTableTrackables, "tbcode", input); } + @NonNull public static String[] getSuggestionsFinderName(final String input) { return getSuggestions(dbTableLogs, "author", input); } + @NonNull public static String[] getSuggestionsGeocode(final String input) { return getSuggestions(dbTableCaches, "geocode", input); } + @NonNull public static String[] getSuggestionsKeyword(final String input) { return getSuggestions(dbTableCaches, "name", input); } @@ -3128,6 +3175,7 @@ public class DataStore { * * @return list of last caches opened in the details view, ordered by most recent first */ + @NonNull public static ArrayList<Geocache> getLastOpenedCaches() { final List<String> geocodes = Settings.getLastOpenedCaches(); final Set<Geocache> cachesSet = DataStore.loadCaches(geocodes, LoadFlags.LOAD_CACHE_OR_DB); diff --git a/main/src/cgeo/geocaching/EditWaypointActivity.java b/main/src/cgeo/geocaching/EditWaypointActivity.java index 7cfb9f9..8cb947a 100644 --- a/main/src/cgeo/geocaching/EditWaypointActivity.java +++ b/main/src/cgeo/geocaching/EditWaypointActivity.java @@ -12,6 +12,7 @@ import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.GeopointFormatter; import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; import cgeo.geocaching.ui.dialog.Dialogs; @@ -308,7 +309,7 @@ public class EditWaypointActivity extends AbstractActionBarActivity implements C @Override protected void onPostExecute(final Geocache cache) { - final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(cache, geopoint, app.currentGeo()); + final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(cache, geopoint, Sensors.getInstance().currentGeo()); coordsDialog.setCancelable(true); coordsDialog.show(getSupportFragmentManager(), "wpeditdialog"); } @@ -391,7 +392,7 @@ public class EditWaypointActivity extends AbstractActionBarActivity implements C return; } } else { - coords = app.currentGeo().getCoords(); + coords = Sensors.getInstance().currentGeo().getCoords(); } if (StringUtils.isNotBlank(bearingText) && StringUtils.isNotBlank(distanceText)) { diff --git a/main/src/cgeo/geocaching/Geocache.java b/main/src/cgeo/geocaching/Geocache.java index 69babc4..c842a7f 100644 --- a/main/src/cgeo/geocaching/Geocache.java +++ b/main/src/cgeo/geocaching/Geocache.java @@ -98,7 +98,7 @@ public class Geocache implements IWaypoint { * lazy initialized */ private String hint = null; - private CacheSize size = CacheSize.UNKNOWN; + @NonNull private CacheSize size = CacheSize.UNKNOWN; private float difficulty = 0; private float terrain = 0; private Float direction = null; @@ -271,7 +271,7 @@ public class Geocache implements IWaypoint { if (!detailed && StringUtils.isBlank(getHint())) { hint = other.getHint(); } - if (size == null || CacheSize.UNKNOWN == size) { + if (size == CacheSize.UNKNOWN) { size = other.size; } if (difficulty == 0) { @@ -479,6 +479,7 @@ public class Geocache implements IWaypoint { notifyChange(); } + @NonNull public List<LogType> getPossibleLogTypes() { return getConnector().getPossibleLogTypes(this); } @@ -502,19 +503,11 @@ public class Geocache implements IWaypoint { } } - - private String getCacheUrl() { - return getConnector().getCacheUrl(this); - } - + @NonNull private IConnector getConnector() { return ConnectorFactory.getConnector(this); } - public boolean canOpenInBrowser() { - return getCacheUrl() != null; - } - public boolean supportsRefresh() { return getConnector() instanceof ISearchByGeocode; } @@ -539,6 +532,7 @@ public class Geocache implements IWaypoint { return getConnector().supportsOwnCoordinates(); } + @NonNull public ILoggingManager getLoggingManager(final LogCacheActivity activity) { return getConnector().getLoggingManager(activity, this); } @@ -559,10 +553,8 @@ public class Geocache implements IWaypoint { return ownerDisplayName; } + @NonNull public CacheSize getSize() { - if (size == null) { - return CacheSize.UNKNOWN; - } return size; } @@ -597,7 +589,8 @@ public class Geocache implements IWaypoint { /** * @return GC username of the (actual) owner, might differ from the owner. Never empty. */ - @NonNull public String getOwnerUserId() { + @NonNull + public String getOwnerUserId() { return ownerUserId; } @@ -706,6 +699,8 @@ public class Geocache implements IWaypoint { fromActivity.startActivity(Intent.createChooser(intent, res.getText(R.string.cache_menu_share))); } + + @NonNull public Intent getShareIntent() { final StringBuilder subject = new StringBuilder("Geocache "); subject.append(geocode); @@ -721,15 +716,20 @@ public class Geocache implements IWaypoint { return intent; } + @NonNull public String getUrl() { return getConnector().getCacheUrl(this); } + @NonNull public String getLongUrl() { return getConnector().getLongCacheUrl(this); } - public String getCgeoUrl() { return getConnector().getCacheUrl(this); } + @NonNull + public String getCgeoUrl() { + return getConnector().getCacheUrl(this); + } public boolean supportsGCVote() { return StringUtils.startsWithIgnoreCase(geocode, "GC"); @@ -760,6 +760,7 @@ public class Geocache implements IWaypoint { return hidden; } + @NonNull public List<String> getAttributes() { return attributes.getUnderlyingList(); } @@ -775,6 +776,7 @@ public class Geocache implements IWaypoint { spoilers.add(spoiler); } + @NonNull public List<Image> getSpoilers() { return ListUtils.unmodifiableList(ListUtils.emptyIfNull(spoilers)); } @@ -972,6 +974,7 @@ public class Geocache implements IWaypoint { * * @return always non <code>null</code> */ + @NonNull public List<Waypoint> getWaypoints() { return waypoints.getUnderlyingList(); } @@ -1079,13 +1082,8 @@ public class Geocache implements IWaypoint { this.hint = hint; } - public void setSize(final CacheSize size) { - if (size == null) { - this.size = CacheSize.UNKNOWN; - } - else { - this.size = size; - } + public void setSize(@NonNull final CacheSize size) { + this.size = size; } public void setDifficulty(final float difficulty) { @@ -1368,12 +1366,15 @@ public class Geocache implements IWaypoint { /** * Detect coordinates in the personal note and convert them to user defined waypoints. Works by rule of thumb. */ - public void parseWaypointsFromNote() { + public boolean parseWaypointsFromNote() { + boolean changed = false; for (final Waypoint waypoint : Waypoint.parseWaypointsFromNote(StringUtils.defaultString(getPersonalNote()))) { if (!hasIdenticalWaypoint(waypoint.getCoords())) { addOrChangeWaypoint(waypoint, false); + changed = true; } } + return changed; } private boolean hasIdenticalWaypoint(final Geopoint point) { @@ -1475,7 +1476,7 @@ public class Geocache implements IWaypoint { warnIncorrectParsingIfBlank(getOwnerUserId(), "owner"); warnIncorrectParsingIf(getHiddenDate() == null, "hidden"); warnIncorrectParsingIf(getFavoritePoints() < 0, "favoriteCount"); - warnIncorrectParsingIf(getSize() == null, "size"); + warnIncorrectParsingIf(getSize() == CacheSize.UNKNOWN, "size"); warnIncorrectParsingIf(getType() == null || getType() == CacheType.UNKNOWN, "type"); warnIncorrectParsingIf(getCoords() == null, "coordinates"); warnIncorrectParsingIfBlank(getLocation(), "location"); @@ -1617,6 +1618,7 @@ public class Geocache implements IWaypoint { * * @return start time in minutes after midnight */ + @Nullable public String guessEventTimeMinutes() { if (!isEventCache()) { return null; @@ -1666,6 +1668,7 @@ public class Geocache implements IWaypoint { } }; + @NonNull public Collection<Image> getImages() { final LinkedList<Image> result = new LinkedList<>(); result.addAll(getSpoilers()); @@ -1735,6 +1738,7 @@ public class Geocache implements IWaypoint { return getConnector().getWaypointGpxId(prefix, geocode); } + @NonNull public String getWaypointPrefix(final String name) { return getConnector().getWaypointPrefix(name); } @@ -1757,6 +1761,7 @@ public class Geocache implements IWaypoint { return (getType().applyDistanceRule() || hasUserModifiedCoords()) && getConnector() == GCConnector.getInstance(); } + @NonNull public LogType getDefaultLogType() { if (isEventCache()) { final Date eventDate = getHiddenDate(); @@ -1782,7 +1787,8 @@ public class Geocache implements IWaypoint { * @param caches a collection of caches * @return the non-blank geocodes of the caches */ - public static Set<String> getGeocodes(final Collection<Geocache> caches) { + @NonNull + public static Set<String> getGeocodes(@NonNull final Collection<Geocache> caches) { final Set<String> geocodes = new HashSet<>(caches.size()); for (final Geocache cache : caches) { final String geocode = cache.getGeocode(); diff --git a/main/src/cgeo/geocaching/GpxFileListActivity.java b/main/src/cgeo/geocaching/GpxFileListActivity.java index 3da4927..352dbab 100644 --- a/main/src/cgeo/geocaching/GpxFileListActivity.java +++ b/main/src/cgeo/geocaching/GpxFileListActivity.java @@ -9,6 +9,7 @@ import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.GPXListAdapter; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.app.Activity; import android.content.Intent; @@ -24,7 +25,7 @@ public class GpxFileListActivity extends AbstractFileListActivity<GPXListAdapter } @Override - protected GPXListAdapter getAdapter(List<File> files) { + protected GPXListAdapter getAdapter(final List<File> files) { return new GPXListAdapter(this, files); } @@ -33,17 +34,17 @@ public class GpxFileListActivity extends AbstractFileListActivity<GPXListAdapter return Collections.singletonList(new File(Settings.getGpxImportDir())); } - public static void startSubActivity(Activity fromActivity, int listId) { + public static void startSubActivity(final Activity fromActivity, final int listId) { final Intent intent = new Intent(fromActivity, GpxFileListActivity.class); intent.putExtra(Intents.EXTRA_LIST_ID, StoredList.getConcreteList(listId)); fromActivity.startActivityForResult(intent, 0); } @Override - protected boolean filenameBelongsToList(final String filename) { + protected boolean filenameBelongsToList(@NonNull final String filename) { if (super.filenameBelongsToList(filename)) { if (StringUtils.endsWithIgnoreCase(filename, GPXImporter.ZIP_FILE_EXTENSION)) { - for (IConnector connector : ConnectorFactory.getConnectors()) { + for (final IConnector connector : ConnectorFactory.getConnectors()) { if (connector.isZippedGPXFile(filename)) { return true; } diff --git a/main/src/cgeo/geocaching/LogCacheActivity.java b/main/src/cgeo/geocaching/LogCacheActivity.java index 996b2d8..118c47b 100644 --- a/main/src/cgeo/geocaching/LogCacheActivity.java +++ b/main/src/cgeo/geocaching/LogCacheActivity.java @@ -12,6 +12,8 @@ import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.LogTypeTrackable; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.gcvote.GCVote; +import cgeo.geocaching.gcvote.GCVoteRatingBarUtil; +import cgeo.geocaching.gcvote.GCVoteRatingBarUtil.OnRatingChangeListener; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.twitter.Twitter; import cgeo.geocaching.ui.dialog.DateDialog; @@ -45,8 +47,6 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; -import android.widget.RatingBar; -import android.widget.RatingBar.OnRatingBarChangeListener; import android.widget.TextView; import java.util.ArrayList; @@ -86,7 +86,6 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia private String imageDescription; private Uri imageUri; private boolean sendButtonEnabled; - private boolean isRatingBarShown = false; public void onLoadFinished() { if (loggingManager.hasLoaderError()) { @@ -286,25 +285,14 @@ public class LogCacheActivity extends AbstractLoggingActivity implements DateDia } private void initializeRatingBar() { - if (GCVote.isVotingPossible(cache) && !isRatingBarShown) { - final RatingBar ratingBar = ButterKnife.findById(this, R.id.gcvoteRating); - final TextView label = ButterKnife.findById(this, R.id.gcvoteLabel); - isRatingBarShown = true; - ratingBar.setVisibility(View.VISIBLE); - label.setVisibility(View.VISIBLE); - ratingBar.setOnRatingBarChangeListener(new OnRatingBarChangeListener() { + if (GCVote.isVotingPossible(cache)) { + GCVoteRatingBarUtil.initializeRatingBar(cache, getWindow().getDecorView().getRootView(), new OnRatingChangeListener() { @Override - public void onRatingChanged(final RatingBar ratingBar, final float stars, final boolean fromUser) { - // 0.5 is not a valid rating, therefore we must limit - rating = GCVote.isValidRating(stars) ? stars : 0; - if (rating < stars) { - ratingBar.setRating(rating); - } - label.setText(GCVote.getDescription(rating)); + public void onRatingChanged(final float stars) { + rating = stars; } }); - ratingBar.setRating(cache.getMyVote()); } } diff --git a/main/src/cgeo/geocaching/LogTrackableActivity.java b/main/src/cgeo/geocaching/LogTrackableActivity.java index fde328a..fc4a066 100644 --- a/main/src/cgeo/geocaching/LogTrackableActivity.java +++ b/main/src/cgeo/geocaching/LogTrackableActivity.java @@ -136,19 +136,18 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat trackable = DataStore.loadTrackable(geocode); + if (trackable == null) { + Log.e("LogTrackableActivity.onCreate: cannot load trackable " + geocode); + finish(); + return; + } + if (StringUtils.isNotBlank(trackable.getName())) { setTitle(res.getString(R.string.trackable_touch) + ": " + trackable.getName()); } else { setTitle(res.getString(R.string.trackable_touch) + ": " + trackable.getGeocode()); } - if (guid == null) { - showToast(res.getString(R.string.err_tb_forgot_saw)); - - finish(); - return; - } - init(); requestKeyboardForLogging(); } @@ -256,11 +255,6 @@ public class LogTrackableActivity extends AbstractLoggingActivity implements Dat public LoadDataThread() { super("Load data for logging trackable"); - if (guid == null) { - showToast(res.getString(R.string.err_tb_forgot_saw)); - - finish(); - } } @Override diff --git a/main/src/cgeo/geocaching/MainActivity.java b/main/src/cgeo/geocaching/MainActivity.java index 321eaca..558c70a 100644 --- a/main/src/cgeo/geocaching/MainActivity.java +++ b/main/src/cgeo/geocaching/MainActivity.java @@ -19,6 +19,7 @@ import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; import cgeo.geocaching.sensors.GpsStatusProvider; import cgeo.geocaching.sensors.GpsStatusProvider.Status; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.settings.SettingsActivity; import cgeo.geocaching.ui.dialog.Dialogs; @@ -113,7 +114,7 @@ public class MainActivity extends AbstractActionBarActivity { for (final ILogin conn : loginConns) { - final TextView connectorInfo = (TextView) inflater.inflate(R.layout.main_activity_connectorstatus, null); + final TextView connectorInfo = (TextView) inflater.inflate(R.layout.main_activity_connectorstatus, infoArea, false); infoArea.addView(connectorInfo); final StringBuilder userInfo = new StringBuilder(conn.getName()).append(Formatter.SEPARATOR); @@ -226,11 +227,8 @@ public class MainActivity extends AbstractActionBarActivity { @Override public void onResume() { super.onResume(locationUpdater.start(GeoDirHandler.UPDATE_GEODATA | GeoDirHandler.LOW_POWER), - app.gpsStatusObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(satellitesHandler)); + Sensors.getInstance().gpsStatusObservable().observeOn(AndroidSchedulers.mainThread()).subscribe(satellitesHandler)); updateUserInfoHandler.sendEmptyMessage(-1); - if (app.hasValidLocation()) { - locationUpdater.updateGeoData(app.currentGeo()); - } startBackgroundLogin(); init(); @@ -239,7 +237,7 @@ public class MainActivity extends AbstractActionBarActivity { } private void startBackgroundLogin() { - assert(app != null); + assert app != null; final boolean mustLogin = app.mustRelog(); @@ -585,8 +583,7 @@ public class MainActivity extends AbstractActionBarActivity { try { addCoords = geo.getCoords(); final Geocoder geocoder = new Geocoder(MainActivity.this, Locale.getDefault()); - final Geopoint coords = app.currentGeo().getCoords(); - final List<Address> addresses = geocoder.getFromLocation(coords.getLatitude(), coords.getLongitude(), 1); + final List<Address> addresses = geocoder.getFromLocation(addCoords.getLatitude(), addCoords.getLongitude(), 1); if (!addresses.isEmpty()) { subscriber.onNext(formatAddress(addresses.get(0))); } diff --git a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java index e75d6b1..d92e441 100644 --- a/main/src/cgeo/geocaching/NavigateAnyPointActivity.java +++ b/main/src/cgeo/geocaching/NavigateAnyPointActivity.java @@ -12,6 +12,7 @@ import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.GeopointFormatter; import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.AbstractViewHolder; import cgeo.geocaching.ui.NavigationActionProvider; @@ -286,7 +287,7 @@ public class NavigateAnyPointActivity extends AbstractActionBarActivity implemen if (latButton.getText().length() > 0 && lonButton.getText().length() > 0) { gp = new Geopoint(latButton.getText().toString() + " " + lonButton.getText().toString()); } - final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(null, gp, app.currentGeo()); + final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(null, gp, Sensors.getInstance().currentGeo()); coordsDialog.setCancelable(true); coordsDialog.show(getSupportFragmentManager(),"wpedit_dialog"); } @@ -477,7 +478,7 @@ public class NavigateAnyPointActivity extends AbstractActionBarActivity implemen @Override public void onClick(final View arg0) { - final Geopoint coords = app.currentGeo().getCoords(); + final Geopoint coords = Sensors.getInstance().currentGeo().getCoords(); latButton.setText(coords.format(GeopointFormatter.Format.LAT_DECMINUTE)); lonButton.setText(coords.format(GeopointFormatter.Format.LON_DECMINUTE)); changed = false; @@ -507,7 +508,7 @@ public class NavigateAnyPointActivity extends AbstractActionBarActivity implemen return null; } } else { - coords = app.currentGeo().getCoords(); + coords = Sensors.getInstance().currentGeo().getCoords(); } // apply projection diff --git a/main/src/cgeo/geocaching/SearchActivity.java b/main/src/cgeo/geocaching/SearchActivity.java index 9de279d..8842603 100644 --- a/main/src/cgeo/geocaching/SearchActivity.java +++ b/main/src/cgeo/geocaching/SearchActivity.java @@ -13,6 +13,7 @@ import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.GeopointFormatter; import cgeo.geocaching.search.AutoCompleteAdapter; import cgeo.geocaching.sensors.GeoData; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.CoordinatesInputDialog; import cgeo.geocaching.ui.dialog.Dialogs; @@ -144,7 +145,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin } final IConnector connector = ConnectorFactory.getConnector(geocode); - if (connector instanceof ISearchByGeocode) { + if (connector instanceof ISearchByGeocode && geocode != null) { CacheDetailActivity.startActivity(this, geocode.toUpperCase(Locale.US)); return true; } @@ -161,7 +162,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin } } - if (trackableConnector != ConnectorFactory.UNKNOWN_TRACKABLE_CONNECTOR) { + if (trackableConnector != ConnectorFactory.UNKNOWN_TRACKABLE_CONNECTOR && geocode != null) { final Intent trackablesIntent = new Intent(this, TrackableActivity.class); trackablesIntent.putExtra(Intents.EXTRA_GEOCODE, geocode.toUpperCase(Locale.US)); startActivity(trackablesIntent); @@ -293,7 +294,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin } private void updateCoordinates() { - final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(null, null, app.currentGeo()); + final CoordinatesInputDialog coordsDialog = CoordinatesInputDialog.getInstance(null, null, Sensors.getInstance().currentGeo()); coordsDialog.setCancelable(true); coordsDialog.show(getSupportFragmentManager(), "wpedit_dialog"); } @@ -309,7 +310,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin final String lonText = StringUtils.trim(buttonLongitude.getText().toString()); if (StringUtils.isEmpty(latText) || StringUtils.isEmpty(lonText)) { - final GeoData geo = app.currentGeo(); + final GeoData geo = Sensors.getInstance().currentGeo(); buttonLatitude.setText(geo.getCoords().format(GeopointFormatter.Format.LAT_DECMINUTE)); buttonLongitude.setText(geo.getCoords().format(GeopointFormatter.Format.LON_DECMINUTE)); } else { @@ -373,7 +374,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin } private void findByGeocodeFn() { - final String geocodeText = StringUtils.trim(geocodeEditText.getText().toString()); + final String geocodeText = StringUtils.trimToEmpty(geocodeEditText.getText().toString()); if (StringUtils.isBlank(geocodeText) || geocodeText.equalsIgnoreCase("GC")) { Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_gccode); @@ -384,7 +385,7 @@ public class SearchActivity extends AbstractActionBarActivity implements Coordin } private void findTrackableFn() { - final String trackableText = StringUtils.trim(trackableEditText.getText().toString()); + final String trackableText = StringUtils.trimToEmpty(trackableEditText.getText().toString()); if (StringUtils.isBlank(trackableText) || trackableText.equalsIgnoreCase("TB")) { Dialogs.message(this, R.string.warn_search_help_title, R.string.warn_search_help_tb); diff --git a/main/src/cgeo/geocaching/SelectMapfileActivity.java b/main/src/cgeo/geocaching/SelectMapfileActivity.java index 697f609..a506f16 100644 --- a/main/src/cgeo/geocaching/SelectMapfileActivity.java +++ b/main/src/cgeo/geocaching/SelectMapfileActivity.java @@ -87,7 +87,7 @@ public class SelectMapfileActivity extends AbstractFileListActivity<FileSelectio for (File dir : LocalStorage.getStorages()) { folders.add(new File(dir, "mfmaps")); folders.add(new File(new File(dir, "Locus"), "mapsVector")); - folders.add(new File(dir, LocalStorage.cache)); + folders.add(new File(dir, LocalStorage.CACHE_DIRNAME)); } return folders; } diff --git a/main/src/cgeo/geocaching/StaticMapsActivity.java b/main/src/cgeo/geocaching/StaticMapsActivity.java index 7d822ec..be363f0 100644 --- a/main/src/cgeo/geocaching/StaticMapsActivity.java +++ b/main/src/cgeo/geocaching/StaticMapsActivity.java @@ -91,12 +91,14 @@ public class StaticMapsActivity extends AbstractActionBarActivity { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState, R.layout.staticmaps_activity); - if (geocode == null) { - showToast("Sorry, c:geo forgot for what cache you want to load static maps."); + cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + + if (cache == null) { + Log.e("StaticMapsActivity.onCreate: cannot find the cache " + geocode); finish(); return; } - cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + setCacheTitleBar(cache); waitDialog = ProgressDialog.show(this, null, res.getString(R.string.map_static_loading), true); diff --git a/main/src/cgeo/geocaching/StaticMapsProvider.java b/main/src/cgeo/geocaching/StaticMapsProvider.java index fd7b751..5184f71 100644 --- a/main/src/cgeo/geocaching/StaticMapsProvider.java +++ b/main/src/cgeo/geocaching/StaticMapsProvider.java @@ -17,6 +17,7 @@ import org.eclipse.jdt.annotation.NonNull; import rx.Observable; import rx.functions.Action0; +import rx.functions.Func0; import rx.util.async.Async; import android.graphics.Bitmap; @@ -38,6 +39,8 @@ public final class StaticMapsProvider { private static final String MAP_FILENAME_PREFIX = "map_"; private static final String MARKERS_URL = "http://status.cgeo.org/assets/markers/"; + private static volatile long last403 = 0; + /** We assume there is no real usable image with less than 1k. */ private static final int MIN_MAP_IMAGE_BYTES = 1000; @@ -54,32 +57,43 @@ public final class StaticMapsProvider { return LocalStorage.getStorageFile(geocode, MAP_FILENAME_PREFIX + prefix, false, createDirs); } + private static <T> Observable<T> checkDownloadPermission(final Observable<T> ifPermitted) { + return Observable.defer(new Func0<Observable<T>>() { + @Override + public Observable<T> call() { + if (System.currentTimeMillis() - last403 >= 30000) { + return ifPermitted; + } + Log.d("StaticMaps.downloadMap: request ignored because of recent \"permission denied\" answer"); + return Observable.empty(); + } + }); + } + private static Observable<String> downloadDifferentZooms(final String geocode, final String markerUrl, final String prefix, final String latlonMap, final int width, final int height, final Parameters waypoints) { - return Observable.merge(downloadMap(geocode, 20, SATELLITE, markerUrl, prefix + '1', "", latlonMap, width, height, waypoints), + return checkDownloadPermission(Observable.merge(downloadMap(geocode, 20, SATELLITE, markerUrl, prefix + '1', "", latlonMap, width, height, waypoints), downloadMap(geocode, 18, SATELLITE, markerUrl, prefix + '2', "", latlonMap, width, height, waypoints), downloadMap(geocode, 16, ROADMAP, markerUrl, prefix + '3', "", latlonMap, width, height, waypoints), downloadMap(geocode, 14, ROADMAP, markerUrl, prefix + '4', "", latlonMap, width, height, waypoints), - downloadMap(geocode, 11, ROADMAP, markerUrl, prefix + '5', "", latlonMap, width, height, waypoints)); + downloadMap(geocode, 11, ROADMAP, markerUrl, prefix + '5', "", latlonMap, width, height, waypoints))); } private static Observable<String> 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) { - int scale = 1; - if (width > GOOGLE_MAPS_MAX_SIZE) { - scale = 2; - } + // If it has been less than 30 seconds since we got a 403 (permission denied) from Google servers, + // do not try again. + final int scale = width <= GOOGLE_MAPS_MAX_SIZE ? 1 : 2; final float aspectRatio = width / (float) height; final int requestWidth = Math.min(width / scale, GOOGLE_MAPS_MAX_SIZE); final int requestHeight = (aspectRatio > 1) ? Math.round(requestWidth / aspectRatio) : requestWidth; - final int requestScale = scale; final int requestZoom = Math.min((scale == 2) ? zoom + 1 : zoom, GOOGLE_MAX_ZOOM); - return Async.fromAction(new Action0() { + return checkDownloadPermission(Async.fromAction(new Action0() { @Override public void call() { final Parameters params = new Parameters( "center", latlonMap, "zoom", String.valueOf(requestZoom), "size", String.valueOf(requestWidth) + 'x' + String.valueOf(requestHeight), - "scale", String.valueOf(requestScale), + "scale", String.valueOf(scale), "maptype", mapType, "markers", "icon:" + markerUrl + '|' + shadow + latlonMap, "sensor", "false"); @@ -92,8 +106,12 @@ public final class StaticMapsProvider { Log.e("StaticMapsProvider.downloadMap: httpResponse is null"); return; } - if (httpResponse.getStatusLine().getStatusCode() != 200) { - Log.d("StaticMapsProvider.downloadMap: httpResponseCode = " + httpResponse.getStatusLine().getStatusCode()); + final int statusCode = httpResponse.getStatusLine().getStatusCode(); + if (statusCode != 200) { + Log.d("StaticMapsProvider.downloadMap: httpResponseCode = " + statusCode); + if (statusCode == 403) { + last403 = System.currentTimeMillis(); + } return; } final File file = getMapFile(geocode, prefix, true); @@ -105,7 +123,7 @@ public final class StaticMapsProvider { } } } - }, prefix, RxUtils.networkScheduler); + }, prefix, RxUtils.networkScheduler)); } public static Observable<String> downloadMaps(final Geocache cache) { @@ -132,7 +150,7 @@ public final class StaticMapsProvider { } - return Observable.merge(downloaders); + return checkDownloadPermission(Observable.merge(downloaders)); } /** @@ -144,7 +162,7 @@ public final class StaticMapsProvider { for (final Waypoint waypoint : cache.getWaypoints()) { downloaders.add(storeWaypointStaticMap(cache.getGeocode(), width, height, waypoint)); } - return Observable.merge(downloaders); + return checkDownloadPermission(Observable.merge(downloaders)); } public static Observable<String> storeWaypointStaticMap(final Geocache cache, final Waypoint waypoint) { diff --git a/main/src/cgeo/geocaching/Trackable.java b/main/src/cgeo/geocaching/Trackable.java index 15416bc..f93ec7b 100644 --- a/main/src/cgeo/geocaching/Trackable.java +++ b/main/src/cgeo/geocaching/Trackable.java @@ -42,10 +42,12 @@ public class Trackable implements ILogable { private List<LogEntry> logs = new ArrayList<>(); private String trackingcode = null; + @NonNull public String getUrl() { return getConnector().getUrl(this); } + @NonNull private TrackableConnector getConnector() { return ConnectorFactory.getConnector(this); } @@ -219,6 +221,7 @@ public class Trackable implements ILogable { this.trackingcode = trackingcode; } + @NonNull public Collection<Image> getImages() { final List<Image> images = new LinkedList<>(); if (StringUtils.isNotBlank(image)) { @@ -231,6 +234,7 @@ public class Trackable implements ILogable { return images; } + @NonNull static List<LogType> getPossibleLogTypes() { final List<LogType> logTypes = new ArrayList<>(); logTypes.add(LogType.RETRIEVED_IT); diff --git a/main/src/cgeo/geocaching/Waypoint.java b/main/src/cgeo/geocaching/Waypoint.java index 5cfeb29..5cac4db 100644 --- a/main/src/cgeo/geocaching/Waypoint.java +++ b/main/src/cgeo/geocaching/Waypoint.java @@ -97,18 +97,11 @@ public class Waypoint implements IWaypoint { if (newPrefixes.containsKey(prefix)) { newPrefixes.get(prefix).merge(oldWaypoint); } else if (oldWaypoint.isUserDefined() || forceMerge) { - // personal note waypoints should always be taken from the new list only - if (!isPersonalNoteWaypoint(oldWaypoint)) { - newPoints.add(oldWaypoint); - } + newPoints.add(oldWaypoint); } } } - private static boolean isPersonalNoteWaypoint(final @NonNull Waypoint waypoint) { - return StringUtils.startsWith(waypoint.getName(), CgeoApplication.getInstance().getString(R.string.cache_personal_note) + " "); - } - public boolean isUserDefined() { return own || WaypointType.OWN == waypointType; } @@ -157,6 +150,7 @@ public class Waypoint implements IWaypoint { cachedOrder = ORDER_UNDEFINED; } + @NonNull public String getUrl() { return "http://www.geocaching.com/seek/cache_details.aspx?wp=" + geocode; } @@ -304,7 +298,7 @@ public class Waypoint implements IWaypoint { ((point.getLatitudeE6() % 1000) != 0 || (point.getLongitudeE6() % 1000) != 0)) { final String name = CgeoApplication.getInstance().getString(R.string.cache_personal_note) + " " + count; final String potentialWaypointType = note.substring(Math.max(0, matcher.start() - 15)); - final Waypoint waypoint = new Waypoint(name, parseWaypointType(potentialWaypointType), false); + final Waypoint waypoint = new Waypoint(name, parseWaypointType(potentialWaypointType), true); waypoint.setCoords(point); waypoints.add(waypoint); count++; diff --git a/main/src/cgeo/geocaching/WaypointPopupFragment.java b/main/src/cgeo/geocaching/WaypointPopupFragment.java index 83dad09..227e30d 100644 --- a/main/src/cgeo/geocaching/WaypointPopupFragment.java +++ b/main/src/cgeo/geocaching/WaypointPopupFragment.java @@ -60,6 +60,13 @@ public class WaypointPopupFragment extends AbstractDialogFragment { super.init(); waypoint = DataStore.loadWaypoint(waypointId); + + if (waypoint == null) { + Log.e("WaypointPopupFragment.init: unable to get waypoint " + waypointId); + getActivity().finish(); + return; + } + try { if (StringUtils.isNotBlank(waypoint.getName())) { setTitle(waypoint.getName()); diff --git a/main/src/cgeo/geocaching/activity/ActivityMixin.java b/main/src/cgeo/geocaching/activity/ActivityMixin.java index b135358..14a2fbf 100644 --- a/main/src/cgeo/geocaching/activity/ActivityMixin.java +++ b/main/src/cgeo/geocaching/activity/ActivityMixin.java @@ -157,6 +157,15 @@ public final class ActivityMixin { editText.setSelection(newCursor); } + /** + * This is the exact code from Google to implement Up navigation, with one exception: activity.isTaskRoot() was + * added as {@link NavUtils#shouldUpRecreateTask(Activity, Intent)} seems not to handle the case, that this activity + * was created from an intent by another app, and our own app is not yet running. The bug seems to be fixed in + * Android 4.4.something, however. + * + * @param activity + * @return + */ public static boolean navigateUp(@NonNull final Activity activity) { // see http://developer.android.com/training/implementing-navigation/ancestral.html final Intent upIntent = NavUtils.getParentActivityIntent(activity); @@ -164,7 +173,7 @@ public final class ActivityMixin { activity.finish(); return true; } - if (NavUtils.shouldUpRecreateTask(activity, upIntent)) { + if (NavUtils.shouldUpRecreateTask(activity, upIntent) || activity.isTaskRoot()) { // This activity is NOT part of this app's task, so create a new task // when navigating up, with a synthesized back stack. TaskStackBuilder.create(activity) diff --git a/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java b/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java index ccfd7a1..d14fca4 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/GoogleMapsDirectionApp.java @@ -1,10 +1,10 @@ package cgeo.geocaching.apps.cache.navi; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.sensors.GeoData; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.utils.Log; import android.app.Activity; @@ -25,7 +25,7 @@ class GoogleMapsDirectionApp extends AbstractPointNavigationApp { @Override public void navigate(final Activity activity, final Geopoint coords) { try { - final GeoData geo = CgeoApplication.getInstance().currentGeo(); + final GeoData geo = Sensors.getInstance().currentGeo(); activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri .parse("http://maps.google.com/maps?f=d&saddr=" + geo.getCoords().getLatitude() + "," + geo.getCoords().getLongitude() + "&daddr=" diff --git a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java index f346fc0..d542541 100644 --- a/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java +++ b/main/src/cgeo/geocaching/apps/cache/navi/NavigationAppFactory.java @@ -80,6 +80,9 @@ public final class NavigationAppFactory extends AbstractAppFactory { this.app = app; this.id = id; this.preferenceKey = preferenceKey; + if (preferenceKey == 0 || preferenceKey == -1) { + throw new IllegalStateException("Every navigation app must have a boolean preference in the settings to be enabled/disabled."); + } } /** diff --git a/main/src/cgeo/geocaching/connector/AbstractConnector.java b/main/src/cgeo/geocaching/connector/AbstractConnector.java index 834167a..4984273 100644 --- a/main/src/cgeo/geocaching/connector/AbstractConnector.java +++ b/main/src/cgeo/geocaching/connector/AbstractConnector.java @@ -39,12 +39,12 @@ public abstract class AbstractConnector implements IConnector { } @Override - public boolean addToWatchlist(Geocache cache) { + public boolean addToWatchlist(@NonNull final Geocache cache) { return false; } @Override - public boolean removeFromWatchlist(Geocache cache) { + public boolean removeFromWatchlist(@NonNull final Geocache cache) { return false; } @@ -54,7 +54,7 @@ public abstract class AbstractConnector implements IConnector { } @Override - public boolean uploadPersonalNote(Geocache cache) { + public boolean uploadPersonalNote(@NonNull final Geocache cache) { throw new UnsupportedOperationException(); } @@ -64,7 +64,7 @@ public abstract class AbstractConnector implements IConnector { } @Override - public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) { + public boolean uploadModifiedCoordinates(@NonNull final Geocache cache, @NonNull final Geopoint wpt) { throw new UnsupportedOperationException(); } @@ -72,12 +72,12 @@ public abstract class AbstractConnector implements IConnector { * {@link IConnector} */ @Override - public boolean deleteModifiedCoordinates(Geocache cache) { + public boolean deleteModifiedCoordinates(@NonNull final Geocache cache) { throw new UnsupportedOperationException(); } @Override - public boolean supportsFavoritePoints(final Geocache cache) { + public boolean supportsFavoritePoints(@NonNull final Geocache cache) { return false; } @@ -92,36 +92,38 @@ public abstract class AbstractConnector implements IConnector { } @Override - public boolean canLog(Geocache cache) { + public boolean canLog(@NonNull final Geocache cache) { return false; } @Override - public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { + @NonNull + public ILoggingManager getLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final Geocache cache) { return new NoLoggingManager(); } @Override + @NonNull public String getLicenseText(final @NonNull Geocache cache) { - return null; + return StringUtils.EMPTY; } protected static boolean isNumericId(final String str) { try { return Integer.parseInt(str) > 0; - } catch (NumberFormatException ignored) { + } catch (final NumberFormatException ignored) { } return false; } @Override - public boolean isZippedGPXFile(String fileName) { + public boolean isZippedGPXFile(@NonNull final String fileName) { // don't accept any file by default return false; } @Override - public boolean isReliableLatLon(boolean cacheHasReliableLatLon) { + public boolean isReliableLatLon(final boolean cacheHasReliableLatLon) { // let every cache have reliable coordinates by default return true; } @@ -129,9 +131,8 @@ public abstract class AbstractConnector implements IConnector { @Override public String getGeocodeFromUrl(final String url) { final String urlPrefix = getCacheUrlPrefix(); - if (StringUtils.startsWith(url, urlPrefix)) { - @NonNull - String geocode = url.substring(urlPrefix.length()); + if (StringUtils.isEmpty(urlPrefix) || StringUtils.startsWith(url, urlPrefix)) { + @NonNull final String geocode = url.substring(urlPrefix.length()); if (canHandle(geocode)) { return geocode; } @@ -139,9 +140,11 @@ public abstract class AbstractConnector implements IConnector { return null; } + @NonNull abstract protected String getCacheUrlPrefix(); @Override + @NonNull public String getLongCacheUrl(final @NonNull Geocache cache) { return getCacheUrl(cache); } @@ -152,7 +155,7 @@ public abstract class AbstractConnector implements IConnector { } @Override - public int getCacheMapMarkerId(boolean disabled) { + public int getCacheMapMarkerId(final boolean disabled) { if (disabled) { return R.drawable.marker_disabled_other; } @@ -160,7 +163,8 @@ public abstract class AbstractConnector implements IConnector { } @Override - public List<LogType> getPossibleLogTypes(Geocache geocache) { + @NonNull + public List<LogType> getPossibleLogTypes(@NonNull final Geocache geocache) { final List<LogType> logTypes = new ArrayList<>(); if (geocache.isEventCache()) { logTypes.add(LogType.WILL_ATTEND); @@ -197,13 +201,14 @@ public abstract class AbstractConnector implements IConnector { } @Override - public String getWaypointGpxId(String prefix, String geocode) { + public String getWaypointGpxId(final String prefix, final String geocode) { // Default: just return the prefix return prefix; } @Override - public String getWaypointPrefix(String name) { + @NonNull + public String getWaypointPrefix(final String name) { // Default: just return the name return name; } @@ -214,8 +219,9 @@ public abstract class AbstractConnector implements IConnector { } @Override + @NonNull public final Collection<String> getCapabilities() { - ArrayList<String> list = new ArrayList<>(); + final ArrayList<String> list = new ArrayList<>(); addCapability(list, ISearchByViewPort.class, R.string.feature_search_live_map); addCapability(list, ISearchByKeyword.class, R.string.feature_search_keyword); addCapability(list, ISearchByCenter.class, R.string.feature_search_center); @@ -246,20 +252,20 @@ public abstract class AbstractConnector implements IConnector { } } - private static String feature(int featureResourceId) { + private static String feature(final int featureResourceId) { return CgeoApplication.getInstance().getString(featureResourceId); } @Override public @NonNull List<UserAction> getUserActions() { - List<UserAction> actions = getDefaultUserActions(); + final List<UserAction> actions = getDefaultUserActions(); if (this instanceof ISearchByOwner) { actions.add(new UserAction(R.string.user_menu_view_hidden, new Action1<Context>() { @Override - public void call(Context context) { + public void call(final Context context) { CacheListActivity.startActivityOwner(context.activity, context.userName); } })); @@ -269,7 +275,7 @@ public abstract class AbstractConnector implements IConnector { actions.add(new UserAction(R.string.user_menu_view_found, new Action1<UserAction.Context>() { @Override - public void call(Context context) { + public void call(final Context context) { CacheListActivity.startActivityFinder(context.activity, context.userName); } })); @@ -287,7 +293,7 @@ public abstract class AbstractConnector implements IConnector { actions.add(new UserAction(R.string.user_menu_open_contact, new Action1<UserAction.Context>() { @Override - public void call(Context context) { + public void call(final Context context) { ContactsAddon.openContactCard(context.activity, context.userName); } })); diff --git a/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java b/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java index 9e702c4..e53fcf1 100644 --- a/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/AbstractLoggingManager.java @@ -2,6 +2,8 @@ package cgeo.geocaching.connector; import cgeo.geocaching.TrackableLog; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.List; @@ -13,6 +15,7 @@ public abstract class AbstractLoggingManager implements ILoggingManager { } @Override + @NonNull public List<TrackableLog> getTrackables() { return Collections.emptyList(); } diff --git a/main/src/cgeo/geocaching/connector/AbstractLogin.java b/main/src/cgeo/geocaching/connector/AbstractLogin.java index b40ed4f..252daeb 100644 --- a/main/src/cgeo/geocaching/connector/AbstractLogin.java +++ b/main/src/cgeo/geocaching/connector/AbstractLogin.java @@ -8,6 +8,7 @@ import cgeo.geocaching.network.Network; import cgeo.geocaching.settings.Settings; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; public abstract class AbstractLogin { @@ -39,7 +40,7 @@ public abstract class AbstractLogin { return actualLoginStatus; } - protected void setActualLoginStatus(boolean loginStatus) { + protected void setActualLoginStatus(final boolean loginStatus) { actualLoginStatus = loginStatus; } @@ -47,7 +48,7 @@ public abstract class AbstractLogin { return actualUserName; } - protected void setActualUserName(String userName) { + protected void setActualUserName(final String userName) { actualUserName = userName; } @@ -69,6 +70,7 @@ public abstract class AbstractLogin { setActualStatus(CgeoApplication.getInstance().getString(R.string.err_login)); } + @NonNull public StatusCode login() { if (!Network.isNetworkConnected()) { return StatusCode.COMMUNICATION_ERROR; @@ -76,6 +78,7 @@ public abstract class AbstractLogin { return login(true); } + @NonNull protected abstract StatusCode login(boolean retry); } diff --git a/main/src/cgeo/geocaching/connector/ConnectorFactory.java b/main/src/cgeo/geocaching/connector/ConnectorFactory.java index d4d051f..b78b009 100644 --- a/main/src/cgeo/geocaching/connector/ConnectorFactory.java +++ b/main/src/cgeo/geocaching/connector/ConnectorFactory.java @@ -36,8 +36,8 @@ import java.util.Collections; import java.util.List; public final class ConnectorFactory { - private static final @NonNull UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector(); - private static final Collection<IConnector> CONNECTORS = Collections.unmodifiableCollection(Arrays.asList(new IConnector[] { + @NonNull private static final UnknownConnector UNKNOWN_CONNECTOR = new UnknownConnector(); + @NonNull private static final Collection<IConnector> CONNECTORS = Collections.unmodifiableCollection(Arrays.asList(new IConnector[] { GCConnector.getInstance(), ECConnector.getInstance(), new OCApiLiveConnector("opencaching.de", "www.opencaching.de", "OC", "CC BY-NC-ND, alle Logeinträge © jeweiliger Autor", @@ -71,22 +71,30 @@ public final class ConnectorFactory { })); @NonNull public static final UnknownTrackableConnector UNKNOWN_TRACKABLE_CONNECTOR = new UnknownTrackableConnector(); + + @NonNull private static final Collection<TrackableConnector> TRACKABLE_CONNECTORS = Collections.unmodifiableCollection(Arrays.asList(new TrackableConnector[] { new GeokretyConnector(), // GK must be first, as it overlaps with the secret codes of travel bugs TravelBugConnector.getInstance(), UNKNOWN_TRACKABLE_CONNECTOR // must be last })); + @NonNull private static final Collection<ISearchByViewPort> searchByViewPortConns = getMatchingConnectors(ISearchByViewPort.class); + @NonNull private static final Collection<ISearchByCenter> searchByCenterConns = getMatchingConnectors(ISearchByCenter.class); + @NonNull private static final Collection<ISearchByKeyword> searchByKeywordConns = getMatchingConnectors(ISearchByKeyword.class); + @NonNull private static final Collection<ISearchByOwner> SEARCH_BY_OWNER_CONNECTORS = getMatchingConnectors(ISearchByOwner.class); + @NonNull private static final Collection<ISearchByFinder> SEARCH_BY_FINDER_CONNECTORS = getMatchingConnectors(ISearchByFinder.class); + @NonNull @SuppressWarnings("unchecked") private static <T extends IConnector> Collection<T> getMatchingConnectors(final Class<T> clazz) { final List<T> matching = new ArrayList<>(); @@ -98,26 +106,32 @@ public final class ConnectorFactory { return Collections.unmodifiableCollection(matching); } + @NonNull public static Collection<IConnector> getConnectors() { return CONNECTORS; } + @NonNull public static Collection<ISearchByCenter> getSearchByCenterConnectors() { return searchByCenterConns; } + @NonNull public static Collection<ISearchByKeyword> getSearchByKeywordConnectors() { return searchByKeywordConns; } + @NonNull public static Collection<ISearchByOwner> getSearchByOwnerConnectors() { return SEARCH_BY_OWNER_CONNECTORS; } + @NonNull public static Collection<ISearchByFinder> getSearchByFinderConnectors() { return SEARCH_BY_FINDER_CONNECTORS; } + @NonNull public static ILogin[] getActiveLiveConnectors() { final List<ILogin> liveConns = new ArrayList<>(); for (final IConnector conn : CONNECTORS) { @@ -143,11 +157,12 @@ public final class ConnectorFactory { return false; } - public static @NonNull - IConnector getConnector(final Geocache cache) { + @NonNull + public static IConnector getConnector(final Geocache cache) { return getConnector(cache.getGeocode()); } + @NonNull public static TrackableConnector getConnector(final Trackable trackable) { return getTrackableConnector(trackable.getGeocode()); } @@ -162,8 +177,8 @@ public final class ConnectorFactory { return UNKNOWN_TRACKABLE_CONNECTOR; // avoid null checks by returning a non implementing connector } - public static @NonNull - IConnector getConnector(final String geocodeInput) { + @NonNull + public static IConnector getConnector(final String geocodeInput) { // this may come from user input final String geocode = StringUtils.trim(geocodeInput); if (geocode == null) { @@ -186,7 +201,8 @@ public final class ConnectorFactory { } /** @see ISearchByViewPort#searchByViewport */ - public static SearchResult searchByViewport(final @NonNull Viewport viewport, final MapTokens tokens) { + @NonNull + public static SearchResult searchByViewport(final @NonNull Viewport viewport, @NonNull final MapTokens tokens) { return SearchResult.parallelCombineActive(searchByViewPortConns, new Func1<ISearchByViewPort, SearchResult>() { @Override public SearchResult call(final ISearchByViewPort connector) { @@ -195,6 +211,7 @@ public final class ConnectorFactory { }); } + @Nullable public static String getGeocodeFromURL(final String url) { for (final IConnector connector : CONNECTORS) { final String geocode = connector.getGeocodeFromUrl(url); @@ -205,6 +222,7 @@ public final class ConnectorFactory { return null; } + @NonNull public static Collection<TrackableConnector> getTrackableConnectors() { return TRACKABLE_CONNECTORS; } @@ -215,6 +233,7 @@ public final class ConnectorFactory { * @param url * @return {@code null} if the URL cannot be decoded */ + @Nullable public static String getTrackableFromURL(final String url) { if (url == null) { return null; diff --git a/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java b/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java index 5535472..9377d6d 100644 --- a/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java +++ b/main/src/cgeo/geocaching/connector/GeocachingAustraliaConnector.java @@ -8,22 +8,25 @@ import org.eclipse.jdt.annotation.NonNull; class GeocachingAustraliaConnector extends AbstractConnector { @Override + @NonNull public String getName() { return "Geocaching Australia"; } @Override + @NonNull public String getCacheUrl(final @NonNull Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @Override + @NonNull public String getHost() { return "geocaching.com.au"; } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @@ -33,6 +36,7 @@ class GeocachingAustraliaConnector extends AbstractConnector { } @Override + @NonNull protected String getCacheUrlPrefix() { return "http://" + getHost() + "/cache/"; } diff --git a/main/src/cgeo/geocaching/connector/GeopeitusConnector.java b/main/src/cgeo/geocaching/connector/GeopeitusConnector.java index 0dc7a44..4340cac 100644 --- a/main/src/cgeo/geocaching/connector/GeopeitusConnector.java +++ b/main/src/cgeo/geocaching/connector/GeopeitusConnector.java @@ -8,22 +8,25 @@ import org.eclipse.jdt.annotation.NonNull; class GeopeitusConnector extends AbstractConnector { @Override + @NonNull public String getName() { return "geopeitus.ee"; } @Override + @NonNull public String getCacheUrl(final @NonNull Geocache cache) { return getCacheUrlPrefix() + StringUtils.stripStart(cache.getGeocode().substring(2), "0"); } @Override + @NonNull public String getHost() { return "www.geopeitus.ee"; } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @@ -33,6 +36,7 @@ class GeopeitusConnector extends AbstractConnector { } @Override + @NonNull protected String getCacheUrlPrefix() { return "http://" + getHost() + "/aare/"; } diff --git a/main/src/cgeo/geocaching/connector/IConnector.java b/main/src/cgeo/geocaching/connector/IConnector.java index 9c1e8fc..0863723 100644 --- a/main/src/cgeo/geocaching/connector/IConnector.java +++ b/main/src/cgeo/geocaching/connector/IConnector.java @@ -16,6 +16,7 @@ public interface IConnector { * * @return */ + @NonNull public String getName(); /** @@ -33,6 +34,7 @@ public interface IConnector { * @param cache * @return */ + @NonNull public String getCacheUrl(final @NonNull Geocache cache); /** @@ -41,6 +43,7 @@ public interface IConnector { * @param cache * @return */ + @NonNull public String getLongCacheUrl(final @NonNull Geocache cache); /** @@ -56,7 +59,7 @@ public interface IConnector { * @param cache * @return True - success/False - failure */ - public boolean addToWatchlist(Geocache cache); + public boolean addToWatchlist(@NonNull Geocache cache); /** * Remove the cache from the watchlist @@ -64,14 +67,14 @@ public interface IConnector { * @param cache * @return True - success/False - failure */ - public boolean removeFromWatchlist(Geocache cache); + public boolean removeFromWatchlist(@NonNull Geocache cache); /** * enable/disable favorite points controls in cache details * * @return */ - public boolean supportsFavoritePoints(final Geocache cache); + public boolean supportsFavoritePoints(@NonNull final Geocache cache); /** * enable/disable logging controls in cache details @@ -92,13 +95,15 @@ public interface IConnector { * * @return */ - public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache); + @NonNull + public ILoggingManager getLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final Geocache cache); /** * Get host name of the connector server for dynamic loading of data. * * @return */ + @NonNull public String getHost(); /** @@ -107,6 +112,7 @@ public interface IConnector { * @param cache * @return */ + @NonNull public String getLicenseText(final @NonNull Geocache cache); /** @@ -115,7 +121,7 @@ public interface IConnector { * @param fileName * @return */ - public boolean isZippedGPXFile(final String fileName); + public boolean isZippedGPXFile(@NonNull final String fileName); /** * return true if coordinates of a cache are reliable. only implemented by GC connector @@ -147,7 +153,7 @@ public interface IConnector { * @param cache * @return success */ - public boolean uploadPersonalNote(Geocache cache); + public boolean uploadPersonalNote(@NonNull Geocache cache); /** * enable/disable uploading modified coordinates to website @@ -162,7 +168,7 @@ public interface IConnector { * @param cache * @return success */ - public boolean deleteModifiedCoordinates(Geocache cache); + public boolean deleteModifiedCoordinates(@NonNull Geocache cache); /** * Uploading modified coordinates to website @@ -171,7 +177,7 @@ public interface IConnector { * @param wpt * @return success */ - public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt); + public boolean uploadModifiedCoordinates(@NonNull Geocache cache, @NonNull Geopoint wpt); /** * Return {@code true} if this connector is active for online interaction (download details, do searches, ...). If @@ -188,7 +194,7 @@ public interface IConnector { * @param cache a cache that this connector must be able to handle * @return <code>true</code> if the current user is the cache owner, <code>false</code> otherwise */ - public boolean isOwner(final Geocache cache); + public boolean isOwner(@NonNull final Geocache cache); /** * Check if the cache information is complete enough to be @@ -197,7 +203,7 @@ public interface IConnector { * @param geocache * @return */ - public boolean canLog(Geocache geocache); + public boolean canLog(@NonNull Geocache geocache); /** * Return the marker id of the caches for this connector. This creates the different backgrounds for cache markers @@ -215,7 +221,8 @@ public interface IConnector { * @param geocache * @return */ - public List<LogType> getPossibleLogTypes(Geocache geocache); + @NonNull + public List<LogType> getPossibleLogTypes(@NonNull Geocache geocache); /** * Get the GPX id for a waypoint when exporting. For some connectors there is an inherent name logic, @@ -232,6 +239,7 @@ public interface IConnector { * @param name * @return */ + @NonNull public String getWaypointPrefix(String name); /** @@ -246,8 +254,9 @@ public interface IConnector { * * @return */ + @NonNull public Collection<String> getCapabilities(); - public @NonNull - List<UserAction> getUserActions(); + @NonNull + public List<UserAction> getUserActions(); } diff --git a/main/src/cgeo/geocaching/connector/ILoggingManager.java b/main/src/cgeo/geocaching/connector/ILoggingManager.java index a8caf69..40a5377 100644 --- a/main/src/cgeo/geocaching/connector/ILoggingManager.java +++ b/main/src/cgeo/geocaching/connector/ILoggingManager.java @@ -3,6 +3,9 @@ package cgeo.geocaching.connector; import cgeo.geocaching.TrackableLog; import cgeo.geocaching.enumerations.LogType; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + import android.net.Uri; import java.util.Calendar; @@ -12,7 +15,7 @@ public interface ILoggingManager { /** * Post a log for a cache online - * + * * @param logType * @param date * @param log @@ -21,12 +24,14 @@ public interface ILoggingManager { * @param trackableLogs * @return */ - LogResult postLog(LogType logType, - Calendar date, - String log, - String logPassword, - List<TrackableLog> trackableLogs); - + @NonNull + LogResult postLog(@NonNull LogType logType, + @NonNull Calendar date, + @NonNull String log, + @Nullable String logPassword, + @NonNull List<TrackableLog> trackableLogs); + + @NonNull ImageResult postLogImage(String logId, String imageCaption, String imageDescription, @@ -34,8 +39,10 @@ public interface ILoggingManager { public boolean hasLoaderError(); + @NonNull public List<TrackableLog> getTrackables(); + @NonNull public List<LogType> getPossibleLogTypes(); public void init(); diff --git a/main/src/cgeo/geocaching/connector/ImageResult.java b/main/src/cgeo/geocaching/connector/ImageResult.java index 9314cad..ec11a6d 100644 --- a/main/src/cgeo/geocaching/connector/ImageResult.java +++ b/main/src/cgeo/geocaching/connector/ImageResult.java @@ -2,20 +2,26 @@ package cgeo.geocaching.connector; import cgeo.geocaching.enumerations.StatusCode; +import org.eclipse.jdt.annotation.NonNull; + public class ImageResult { + @NonNull private final StatusCode postResult; + @NonNull private final String imageUri; - public ImageResult(StatusCode postResult, String imageUri) { + public ImageResult(@NonNull final StatusCode postResult, @NonNull final String imageUri) { this.postResult = postResult; this.imageUri = imageUri; } + @NonNull public StatusCode getPostResult() { return postResult; } + @NonNull public String getImageUri() { return imageUri; } diff --git a/main/src/cgeo/geocaching/connector/LogResult.java b/main/src/cgeo/geocaching/connector/LogResult.java index 62111a4..9d0cb61 100644 --- a/main/src/cgeo/geocaching/connector/LogResult.java +++ b/main/src/cgeo/geocaching/connector/LogResult.java @@ -2,20 +2,26 @@ package cgeo.geocaching.connector; import cgeo.geocaching.enumerations.StatusCode; +import org.eclipse.jdt.annotation.NonNull; + public class LogResult { + @NonNull private final StatusCode postLogResult; + @NonNull private final String logId; - public LogResult(StatusCode postLogResult, String logId) { + public LogResult(@NonNull final StatusCode postLogResult, @NonNull final String logId) { this.postLogResult = postLogResult; this.logId = logId; } + @NonNull public StatusCode getPostLogResult() { return postLogResult; } + @NonNull public String getLogId() { return logId; } diff --git a/main/src/cgeo/geocaching/connector/NoLoggingManager.java b/main/src/cgeo/geocaching/connector/NoLoggingManager.java index ff8b33a..103db68 100644 --- a/main/src/cgeo/geocaching/connector/NoLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/NoLoggingManager.java @@ -4,6 +4,9 @@ import cgeo.geocaching.TrackableLog; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + import android.net.Uri; import java.util.Calendar; @@ -18,11 +21,13 @@ class NoLoggingManager extends AbstractLoggingManager { } @Override - public LogResult postLog(final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { + @NonNull + public LogResult postLog(@NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log, @Nullable final String logPassword, @NonNull final List<TrackableLog> trackableLogs) { return new LogResult(StatusCode.LOG_POST_ERROR, ""); } @Override + @NonNull public ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { return new ImageResult(StatusCode.LOG_POST_ERROR, ""); } @@ -33,6 +38,7 @@ class NoLoggingManager extends AbstractLoggingManager { } @Override + @NonNull public List<LogType> getPossibleLogTypes() { return Collections.emptyList(); } diff --git a/main/src/cgeo/geocaching/connector/UnknownConnector.java b/main/src/cgeo/geocaching/connector/UnknownConnector.java index a88f2c5..8ed1da4 100644 --- a/main/src/cgeo/geocaching/connector/UnknownConnector.java +++ b/main/src/cgeo/geocaching/connector/UnknownConnector.java @@ -8,22 +8,25 @@ import org.eclipse.jdt.annotation.NonNull; class UnknownConnector extends AbstractConnector { @Override + @NonNull public String getName() { return "Unknown caches"; } @Override + @NonNull public String getCacheUrl(@NonNull final Geocache cache) { - return null; // we have no url for these caches + throw new IllegalStateException("getCacheUrl cannot be called on unknown caches"); } @Override + @NonNull public String getHost() { - return null; // we have no host for these caches + return StringUtils.EMPTY; // we have no host for these caches } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @@ -33,7 +36,13 @@ class UnknownConnector extends AbstractConnector { } @Override + @NonNull protected String getCacheUrlPrefix() { + throw new IllegalStateException("getCacheUrl cannot be called on unknown caches"); + } + + @Override + public String getGeocodeFromUrl(final String url) { return null; } diff --git a/main/src/cgeo/geocaching/connector/UserAction.java b/main/src/cgeo/geocaching/connector/UserAction.java index e9ee4a3..082e32c 100644 --- a/main/src/cgeo/geocaching/connector/UserAction.java +++ b/main/src/cgeo/geocaching/connector/UserAction.java @@ -1,6 +1,7 @@ package cgeo.geocaching.connector; import org.eclipse.jdt.annotation.NonNull; + import rx.functions.Action1; import android.app.Activity; @@ -8,25 +9,26 @@ import android.app.Activity; public class UserAction { public static class Context { + @NonNull public final String userName; + @NonNull public final Activity activity; - public Context(String userName, Activity activity) { + public Context(@NonNull final String userName, @NonNull final Activity activity) { this.userName = userName; this.activity = activity; } } public final int displayResourceId; - private final @NonNull - Action1<Context> runnable; + @NonNull private final Action1<Context> runnable; - public UserAction(int displayResourceId, final @NonNull Action1<Context> runnable) { + public UserAction(final int displayResourceId, final @NonNull Action1<Context> runnable) { this.displayResourceId = displayResourceId; this.runnable = runnable; } - public void run(Context context) { + public void run(@NonNull final Context context) { runnable.call(context); } } diff --git a/main/src/cgeo/geocaching/connector/WaymarkingConnector.java b/main/src/cgeo/geocaching/connector/WaymarkingConnector.java index 2ed54a0..5a6f362 100644 --- a/main/src/cgeo/geocaching/connector/WaymarkingConnector.java +++ b/main/src/cgeo/geocaching/connector/WaymarkingConnector.java @@ -8,27 +8,31 @@ import org.eclipse.jdt.annotation.NonNull; class WaymarkingConnector extends AbstractConnector { @Override + @NonNull public String getName() { return "Waymarking"; } @Override + @NonNull public String getCacheUrl(@NonNull final Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @Override + @NonNull public String getHost() { return "www.waymarking.com"; } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { // this connector has no user management return false; } @Override + @NonNull protected String getCacheUrlPrefix() { return "http://" + getHost() + "/waymarks/"; } diff --git a/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java b/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java index 4da9705..dd2bc8d 100644 --- a/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java +++ b/main/src/cgeo/geocaching/connector/capability/FieldNotesCapability.java @@ -2,6 +2,8 @@ package cgeo.geocaching.connector.capability; import cgeo.geocaching.connector.IConnector; +import org.eclipse.jdt.annotation.NonNull; + import java.io.File; /** @@ -9,5 +11,5 @@ import java.io.File; * */ public interface FieldNotesCapability extends IConnector { - public boolean uploadFieldNotes(final File exportFile); + public boolean uploadFieldNotes(@NonNull final File exportFile); } diff --git a/main/src/cgeo/geocaching/connector/capability/ILogin.java b/main/src/cgeo/geocaching/connector/capability/ILogin.java index b8b4975..003ccf4 100644 --- a/main/src/cgeo/geocaching/connector/capability/ILogin.java +++ b/main/src/cgeo/geocaching/connector/capability/ILogin.java @@ -7,13 +7,10 @@ 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. - * + * 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 @@ -28,7 +25,7 @@ public interface ILogin extends IConnector { void logout(); /** - * Returns the status of the last {@link}login() request + * Returns the status of the last {@link #login(Handler, Context)} request. * * @return */ @@ -42,18 +39,17 @@ public interface ILogin extends IConnector { 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. + * Name the user has in this connector or empty string if not applicable. + * It might be necessary to execute {@link #login(Handler, Context)} 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. + * Number of caches the user has found in this connector. + * Normally retrieved/updated with {@link #login(Handler, Context)}. + * Might be stale as changes on the connectors site are generally not notified. * * @return */ diff --git a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java index ef81c4c..0137c3b 100644 --- a/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java +++ b/main/src/cgeo/geocaching/connector/capability/ISearchByViewPort.java @@ -8,5 +8,6 @@ import cgeo.geocaching.location.Viewport; import org.eclipse.jdt.annotation.NonNull; public interface ISearchByViewPort extends IConnector { - public SearchResult searchByViewport(final @NonNull Viewport viewport, final MapTokens tokens); + @NonNull + public SearchResult searchByViewport(final @NonNull Viewport viewport, @NonNull final MapTokens tokens); } diff --git a/main/src/cgeo/geocaching/connector/ec/ECApi.java b/main/src/cgeo/geocaching/connector/ec/ECApi.java index 4d4ca76..86f7717 100644 --- a/main/src/cgeo/geocaching/connector/ec/ECApi.java +++ b/main/src/cgeo/geocaching/connector/ec/ECApi.java @@ -24,6 +24,8 @@ import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import java.io.IOException; import java.util.ArrayList; @@ -37,9 +39,13 @@ import java.util.TimeZone; final class ECApi { + @NonNull private static final String API_HOST = "https://extremcaching.com/exports/"; + @NonNull private static final ECLogin ecLogin = ECLogin.getInstance(); + + @NonNull private static final SynchronizedDateFormat LOG_DATE_FORMAT = new SynchronizedDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", TimeZone.getTimeZone("UTC"), Locale.US); private ECApi() { @@ -50,6 +56,7 @@ final class ECApi { return StringUtils.removeStartIgnoreCase(geocode, "EC"); } + @Nullable static Geocache searchByGeoCode(final String geocode) { final Parameters params = new Parameters("id", getIdFromGeocode(geocode)); final HttpResponse response = apiRequest("gpx.php", params); @@ -61,6 +68,7 @@ final class ECApi { return null; } + @NonNull static Collection<Geocache> searchByBBox(final Viewport viewport) { if (viewport.getLatitudeSpan() == 0 || viewport.getLongitudeSpan() == 0) { @@ -77,7 +85,7 @@ final class ECApi { return importCachesFromJSON(response); } - + @NonNull static Collection<Geocache> searchByCenter(final Geopoint center) { final Parameters params = new Parameters("fnc", "center"); @@ -89,11 +97,13 @@ final class ECApi { return importCachesFromJSON(response); } - static LogResult postLog(final Geocache cache, final LogType logType, final Calendar date, final String log) { + @NonNull + static LogResult postLog(@NonNull final Geocache cache, @NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log) { return postLog(cache, logType, date, log, false); } - private static LogResult postLog(final Geocache cache, final LogType logType, final Calendar date, final String log, final boolean isRetry) { + @NonNull + private static LogResult postLog(@NonNull final Geocache cache, @NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log, final boolean isRetry) { final Parameters params = new Parameters("cache_id", cache.getGeocode()); params.add("type", logType.type); params.add("log", log); @@ -127,15 +137,17 @@ final class ECApi { return new LogResult(StatusCode.LOG_POST_ERROR, ""); } - + @Nullable private static HttpResponse apiRequest(final Parameters params) { return apiRequest("api.php", params); } + @Nullable private static HttpResponse apiRequest(final String uri, final Parameters params) { return apiRequest(uri, params, false); } + @Nullable private static HttpResponse apiRequest(final String uri, final Parameters params, final boolean isRetry) { // add session and cgeo marker on every request if (!isRetry) { @@ -160,6 +172,7 @@ final class ECApi { return response; } + @NonNull private static Collection<Geocache> importCachesFromGPXResponse(final HttpResponse response) { if (response == null) { return Collections.emptyList(); @@ -173,6 +186,7 @@ final class ECApi { } } + @NonNull private static List<Geocache> importCachesFromJSON(final HttpResponse response) { if (response != null) { try { @@ -196,6 +210,7 @@ final class ECApi { return Collections.emptyList(); } + @Nullable private static Geocache parseCache(final JsonNode response) { try { final Geocache cache = new Geocache(); @@ -216,6 +231,7 @@ final class ECApi { } } + @NonNull private static CacheType getCacheType(final String cacheType) { if (cacheType.equalsIgnoreCase("Tradi")) { return CacheType.TRADITIONAL; diff --git a/main/src/cgeo/geocaching/connector/ec/ECConnector.java b/main/src/cgeo/geocaching/connector/ec/ECConnector.java index e884f85..15c2dc2 100644 --- a/main/src/cgeo/geocaching/connector/ec/ECConnector.java +++ b/main/src/cgeo/geocaching/connector/ec/ECConnector.java @@ -36,13 +36,18 @@ import java.util.regex.Pattern; public class ECConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ILogin, ICredentials { + @NonNull private static final String CACHE_URL = "http://extremcaching.com/index.php/output-2/"; /** * Pattern for EC codes */ + @NonNull private final static Pattern PATTERN_EC_CODE = Pattern.compile("EC[0-9]+", Pattern.CASE_INSENSITIVE); + private final CgeoApplication app = CgeoApplication.getInstance(); + + @NonNull private final ECLogin ecLogin = ECLogin.getInstance(); private ECConnector() { @@ -56,27 +61,30 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, private static final @NonNull ECConnector INSTANCE = new ECConnector(); } - public static @NonNull - ECConnector getInstance() { + @NonNull + public static ECConnector getInstance() { return Holder.INSTANCE; } @Override - public boolean canHandle(@NonNull String geocode) { + public boolean canHandle(@NonNull final String geocode) { return ECConnector.PATTERN_EC_CODE.matcher(geocode).matches(); } @Override - public String getCacheUrl(@NonNull Geocache cache) { + @NonNull + public String getCacheUrl(@NonNull final Geocache cache) { return CACHE_URL + cache.getGeocode().replace("EC", ""); } @Override + @NonNull public String getName() { return "extremcaching.com"; } @Override + @NonNull public String getHost() { return "extremcaching.com"; } @@ -94,31 +102,28 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByViewport(@NonNull Viewport viewport, final MapTokens tokens) { + @NonNull + public SearchResult searchByViewport(@NonNull final Viewport viewport, @NonNull final MapTokens tokens) { final Collection<Geocache> caches = ECApi.searchByBBox(viewport); - if (caches == null) { - return null; - } final SearchResult searchResult = new SearchResult(caches); return searchResult.filterSearchResults(false, false, Settings.getCacheType()); } @Override - public SearchResult searchByCenter(@NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { + @NonNull + public SearchResult searchByCenter(@NonNull final Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { final Collection<Geocache> caches = ECApi.searchByCenter(center); - if (caches == null) { - return null; - } final SearchResult searchResult = new SearchResult(caches); return searchResult.filterSearchResults(false, false, Settings.getCacheType()); } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @Override + @NonNull protected String getCacheUrlPrefix() { return CACHE_URL; } @@ -129,7 +134,7 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean login(Handler handler, Context fromActivity) { + public boolean login(final Handler handler, final Context fromActivity) { // login final StatusCode status = ecLogin.login(); @@ -166,7 +171,7 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public int getCacheMapMarkerId(boolean disabled) { + public int getCacheMapMarkerId(final boolean disabled) { final String icons = Settings.getECIconSet(); if (StringUtils.equals(icons, "1")) { return disabled ? R.drawable.marker_disabled_other : R.drawable.marker_other; @@ -175,6 +180,7 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, } @Override + @NonNull public String getLicenseText(final @NonNull Geocache cache) { // NOT TO BE TRANSLATED return "© " + cache.getOwnerDisplayName() + ", <a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a>, CC BY-NC-ND 3.0, alle Logeinträge © jeweiliger Autor"; @@ -186,17 +192,19 @@ public class ECConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean canLog(Geocache cache) { + public boolean canLog(@NonNull final Geocache cache) { return true; } @Override - public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { + @NonNull + public ILoggingManager getLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final Geocache cache) { return new ECLoggingManager(activity, this, cache); } @Override - public List<LogType> getPossibleLogTypes(Geocache geocache) { + @NonNull + public List<LogType> getPossibleLogTypes(@NonNull final Geocache geocache) { final List<LogType> logTypes = new ArrayList<>(); if (geocache.isEventCache()) { logTypes.add(LogType.WILL_ATTEND); diff --git a/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java b/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java index f380a6b..34c3d1b 100644 --- a/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/ec/ECLoggingManager.java @@ -7,6 +7,10 @@ import cgeo.geocaching.connector.AbstractLoggingManager; import cgeo.geocaching.connector.ImageResult; import cgeo.geocaching.connector.LogResult; import cgeo.geocaching.enumerations.LogType; +import cgeo.geocaching.enumerations.StatusCode; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import android.net.Uri; @@ -15,11 +19,16 @@ import java.util.List; class ECLoggingManager extends AbstractLoggingManager { + @NonNull private final ECConnector connector; + + @NonNull private final Geocache cache; + + @NonNull private final LogCacheActivity activity; - ECLoggingManager(final LogCacheActivity activity, final ECConnector connector, final Geocache cache) { + ECLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final ECConnector connector, @NonNull final Geocache cache) { this.connector = connector; this.cache = cache; this.activity = activity; @@ -31,16 +40,19 @@ class ECLoggingManager extends AbstractLoggingManager { } @Override - public final LogResult postLog(final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { + @NonNull + public final LogResult postLog(@NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log, @Nullable final String logPassword, @NonNull final List<TrackableLog> trackableLogs) { return ECApi.postLog(cache, logType, date, log); } @Override + @NonNull public final ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { - return null; + return new ImageResult(StatusCode.LOG_POST_ERROR, ""); } @Override + @NonNull public List<LogType> getPossibleLogTypes() { return connector.getPossibleLogTypes(cache); } diff --git a/main/src/cgeo/geocaching/connector/ec/ECLogin.java b/main/src/cgeo/geocaching/connector/ec/ECLogin.java index 342cbc6..3ae8298 100644 --- a/main/src/cgeo/geocaching/connector/ec/ECLogin.java +++ b/main/src/cgeo/geocaching/connector/ec/ECLogin.java @@ -16,6 +16,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import java.io.IOException; @@ -30,14 +31,17 @@ public class ECLogin extends AbstractLogin { } private static class SingletonHolder { + @NonNull private final static ECLogin INSTANCE = new ECLogin(); } + @NonNull public static ECLogin getInstance() { return SingletonHolder.INSTANCE; } @Override + @NonNull protected StatusCode login(final boolean retry) { final ImmutablePair<String, String> login = Settings.getCredentials(ECConnector.getInstance()); diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index 099fd79..e43b9b5 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -49,18 +49,22 @@ import java.util.regex.Pattern; public class GCConnector extends AbstractConnector implements ISearchByGeocode, ISearchByCenter, ISearchByViewPort, ISearchByKeyword, ILogin, ICredentials, ISearchByOwner, ISearchByFinder, FieldNotesCapability { + @NonNull private static final String CACHE_URL_SHORT = "http://coord.info/"; // Double slash is used to force open in browser + @NonNull private static final String CACHE_URL_LONG = "http://www.geocaching.com/seek/cache_details.aspx?wp="; /** * Pocket queries downloaded from the website use a numeric prefix. The pocket query creator Android app adds a * verbatim "pocketquery" prefix. */ + @NonNull private static final Pattern GPX_ZIP_FILE_PATTERN = Pattern.compile("((\\d{7,})|(pocketquery))" + "(_.+)?" + "\\.zip", Pattern.CASE_INSENSITIVE); /** * Pattern for GC codes */ + @NonNull private final static Pattern PATTERN_GC_CODE = Pattern.compile("GC[0-9A-Z]+", Pattern.CASE_INSENSITIVE); private GCConnector() { @@ -74,23 +78,25 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, private static final @NonNull GCConnector INSTANCE = new GCConnector(); } - public static @NonNull - GCConnector getInstance() { + @NonNull + public static GCConnector getInstance() { return Holder.INSTANCE; } @Override - public boolean canHandle(@NonNull String geocode) { + public boolean canHandle(@NonNull final String geocode) { return GCConnector.PATTERN_GC_CODE.matcher(geocode).matches(); } @Override - public String getLongCacheUrl(@NonNull Geocache cache) { + @NonNull + public String getLongCacheUrl(@NonNull final Geocache cache) { return CACHE_URL_LONG + cache.getGeocode(); } @Override - public String getCacheUrl(@NonNull Geocache cache) { + @NonNull + public String getCacheUrl(@NonNull final Geocache cache) { return CACHE_URL_SHORT + cache.getGeocode(); } @@ -120,21 +126,24 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { + @NonNull + public ILoggingManager getLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final Geocache cache) { return new GCLoggingManager(activity, cache); } @Override - public boolean canLog(Geocache cache) { + public boolean canLog(@NonNull final Geocache cache) { return StringUtils.isNotBlank(cache.getCacheId()); } @Override + @NonNull public String getName() { return "geocaching.com"; } @Override + @NonNull public String getHost() { return "www.geocaching.com"; } @@ -177,28 +186,29 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByViewport(@NonNull Viewport viewport, final MapTokens tokens) { + @NonNull + public SearchResult searchByViewport(@NonNull final Viewport viewport, @NonNull final MapTokens tokens) { return GCMap.searchByViewport(viewport, tokens); } @Override - public boolean isZippedGPXFile(final String fileName) { + public boolean isZippedGPXFile(@NonNull final String fileName) { return GPX_ZIP_FILE_PATTERN.matcher(fileName).matches(); } @Override - public boolean isReliableLatLon(boolean cacheHasReliableLatLon) { + public boolean isReliableLatLon(final boolean cacheHasReliableLatLon) { return cacheHasReliableLatLon; } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { final String user = Settings.getUsername(); return StringUtils.isNotEmpty(user) && StringUtils.equalsIgnoreCase(cache.getOwnerUserId(), user); } @Override - public boolean addToWatchlist(Geocache cache) { + public boolean addToWatchlist(@NonNull final Geocache cache) { final boolean added = GCParser.addToWatchlist(cache); if (added) { DataStore.saveChangedCache(cache); @@ -207,7 +217,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean removeFromWatchlist(Geocache cache) { + public boolean removeFromWatchlist(@NonNull final Geocache cache) { final boolean removed = GCParser.removeFromWatchlist(cache); if (removed) { DataStore.saveChangedCache(cache); @@ -225,7 +235,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, * @return <code>true</code> if the cache was successfully added, <code>false</code> otherwise */ - public static boolean addToFavorites(Geocache cache) { + public static boolean addToFavorites(final Geocache cache) { final boolean added = GCParser.addToFavorites(cache); if (added) { DataStore.saveChangedCache(cache); @@ -243,7 +253,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, * @return <code>true</code> if the cache was successfully added, <code>false</code> otherwise */ - public static boolean removeFromFavorites(Geocache cache) { + public static boolean removeFromFavorites(final Geocache cache) { final boolean removed = GCParser.removeFromFavorites(cache); if (removed) { DataStore.saveChangedCache(cache); @@ -252,7 +262,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean uploadModifiedCoordinates(Geocache cache, Geopoint wpt) { + public boolean uploadModifiedCoordinates(@NonNull final Geocache cache, @NonNull final Geopoint wpt) { final boolean uploaded = GCParser.uploadModifiedCoordinates(cache, wpt); if (uploaded) { DataStore.saveChangedCache(cache); @@ -261,7 +271,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean deleteModifiedCoordinates(Geocache cache) { + public boolean deleteModifiedCoordinates(@NonNull final Geocache cache) { final boolean deleted = GCParser.deleteModifiedCoordinates(cache); if (deleted) { DataStore.saveChangedCache(cache); @@ -270,7 +280,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean uploadPersonalNote(Geocache cache) { + public boolean uploadPersonalNote(@NonNull final Geocache cache) { final boolean uploaded = GCParser.uploadPersonalNote(cache); if (uploaded) { DataStore.saveChangedCache(cache); @@ -279,22 +289,23 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByCenter(@NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { + public SearchResult searchByCenter(@NonNull final Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { return GCParser.searchByCoords(center, Settings.getCacheType(), Settings.isShowCaptcha(), recaptchaReceiver); } @Override - public boolean supportsFavoritePoints(final Geocache cache) { + public boolean supportsFavoritePoints(@NonNull final Geocache cache) { return !cache.getType().isEvent(); } @Override + @NonNull protected String getCacheUrlPrefix() { - return null; // UNUSED + return StringUtils.EMPTY; // UNUSED } @Override - public String getGeocodeFromUrl(String url) { + public String getGeocodeFromUrl(final String url) { // coord.info URLs String code = StringUtils.substringAfterLast(url, "coord.info/"); if (code != null && canHandle(code)) { @@ -314,7 +325,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public int getCacheMapMarkerId(boolean disabled) { + public int getCacheMapMarkerId(final boolean disabled) { if (disabled) { return R.drawable.marker_disabled; } @@ -322,7 +333,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean login(Handler handler, Context fromActivity) { + public boolean login(final Handler handler, final Context fromActivity) { // login final StatusCode status = GCLogin.getInstance().login(); @@ -364,7 +375,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public String getWaypointGpxId(String prefix, String geocode) { + public String getWaypointGpxId(final String prefix, final String geocode) { String gpxId = prefix; if (StringUtils.isNotBlank(geocode) && geocode.length() > 2) { gpxId += geocode.substring(2); @@ -373,7 +384,8 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public String getWaypointPrefix(String name) { + @NonNull + public String getWaypointPrefix(final String name) { String prefix = name; if (StringUtils.isNotBlank(prefix) && prefix.length() >= 2) { prefix = name.substring(0, 2); @@ -382,7 +394,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public SearchResult searchByKeyword(@NonNull String keyword, final @NonNull RecaptchaReceiver recaptchaReceiver) { + public SearchResult searchByKeyword(@NonNull final String keyword, final @NonNull RecaptchaReceiver recaptchaReceiver) { return GCParser.searchByKeyword(keyword, Settings.getCacheType(), Settings.isShowCaptcha(), recaptchaReceiver); } @@ -399,18 +411,18 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, @Override public @NonNull List<UserAction> getUserActions() { - List<UserAction> actions = super.getUserActions(); + final List<UserAction> actions = super.getUserActions(); actions.add(new UserAction(R.string.user_menu_open_browser, new Action1<UserAction.Context>() { @Override - public void call(cgeo.geocaching.connector.UserAction.Context context) { + public void call(final cgeo.geocaching.connector.UserAction.Context context) { context.activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/profile/?u=" + Network.encode(context.userName)))); } })); actions.add(new UserAction(R.string.user_menu_send_message, new Action1<UserAction.Context>() { @Override - public void call(cgeo.geocaching.connector.UserAction.Context context) { + public void call(final cgeo.geocaching.connector.UserAction.Context context) { try { context.activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/email/?u=" + Network.encode(context.userName)))); } catch (final ActivityNotFoundException e) { @@ -433,7 +445,7 @@ public class GCConnector extends AbstractConnector implements ISearchByGeocode, } @Override - public boolean uploadFieldNotes(final File exportFile) { + public boolean uploadFieldNotes(@NonNull final File exportFile) { if (!GCLogin.getInstance().isActualLoginStatus()) { // no need to upload (possibly large file) if we're not logged in final StatusCode loginState = GCLogin.getInstance().login(); diff --git a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java index 057d97b..bdea155 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/gc/GCLoggingManager.java @@ -19,6 +19,7 @@ import cgeo.geocaching.utils.TextUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import android.net.Uri; @@ -36,7 +37,7 @@ class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.L private final Geocache cache; private String[] viewstates; - private List<TrackableLog> trackables; + @NonNull private List<TrackableLog> trackables = Collections.emptyList(); private List<LogType> possibleLogTypes; private boolean hasLoaderError = true; @@ -92,7 +93,8 @@ class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.L } @Override - public LogResult postLog(final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { + @NonNull + public LogResult postLog(@NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log, @Nullable final String logPassword, @NonNull final List<TrackableLog> trackableLogs) { try { final ImmutablePair<StatusCode, String> postResult = GCParser.postLog(cache.getGeocode(), cache.getCacheId(), viewstates, logType, @@ -115,6 +117,7 @@ class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.L } @Override + @NonNull public ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { if (StringUtils.isNotBlank(imageUri.getPath())) { @@ -133,6 +136,7 @@ class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.L } @Override + @NonNull public List<TrackableLog> getTrackables() { if (hasLoaderError) { return Collections.emptyList(); @@ -141,6 +145,7 @@ class GCLoggingManager extends AbstractLoggingManager implements LoaderManager.L } @Override + @NonNull public List<LogType> getPossibleLogTypes() { if (hasLoaderError) { return Collections.emptyList(); diff --git a/main/src/cgeo/geocaching/connector/gc/GCLogin.java b/main/src/cgeo/geocaching/connector/gc/GCLogin.java index 3d4f2af..035176b 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCLogin.java +++ b/main/src/cgeo/geocaching/connector/gc/GCLogin.java @@ -57,6 +57,7 @@ public class GCLogin extends AbstractLogin { } @Override + @NonNull protected StatusCode login(final boolean retry) { final ImmutablePair<String, String> credentials = Settings.getGcCredentials(); final String username = credentials.left; @@ -256,7 +257,7 @@ public class GCLogin extends AbstractLogin { final String avatarURL = TextUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null); if (avatarURL != null) { - final HtmlImage imgGetter = new HtmlImage("", false, 0, false); + final HtmlImage imgGetter = new HtmlImage(HtmlImage.SHARED, false, 0, false); return imgGetter.fetchDrawable(avatarURL.replace("avatar", "user/large")).cast(Drawable.class); } // No match? There may be no avatar set by user. @@ -470,8 +471,8 @@ public class GCLogin extends AbstractLogin { * * @return first is user session, second is session token */ - public @NonNull - MapTokens getMapTokens() { + @NonNull + public MapTokens getMapTokens() { final String data = getRequestLogged(GCConstants.URL_LIVE_MAP, null); final String userSession = TextUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, ""); final String sessionToken = TextUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, ""); diff --git a/main/src/cgeo/geocaching/connector/gc/GCMap.java b/main/src/cgeo/geocaching/connector/gc/GCMap.java index 32c8b42..1571faa 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCMap.java +++ b/main/src/cgeo/geocaching/connector/gc/GCMap.java @@ -1,6 +1,5 @@ package cgeo.geocaching.connector.gc; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.DataStore; import cgeo.geocaching.Geocache; import cgeo.geocaching.SearchResult; @@ -9,12 +8,13 @@ import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.files.ParserException; import cgeo.geocaching.location.Geopoint; +import cgeo.geocaching.location.GeopointFormatter.Format; import cgeo.geocaching.location.Units; import cgeo.geocaching.location.Viewport; -import cgeo.geocaching.location.GeopointFormatter.Format; import cgeo.geocaching.maps.LiveMapStrategy.Strategy; import cgeo.geocaching.maps.LiveMapStrategy.StrategyFlag; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.Formatter; import cgeo.geocaching.utils.JsonUtils; @@ -27,6 +27,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import rx.Observable; import rx.functions.Func2; @@ -47,7 +48,7 @@ import java.util.Set; public class GCMap { private static Viewport lastSearchViewport = null; - public static SearchResult searchByGeocodes(Set<String> geocodes) { + public static SearchResult searchByGeocodes(final Set<String> geocodes) { final SearchResult result = new SearchResult(); final String geocodeList = StringUtils.join(geocodes.toArray(), "|"); @@ -109,7 +110,7 @@ public class GCMap { * Retrieved data. * @return SearchResult. Never null. */ - public static SearchResult parseMapJSON(final String data, Tile tile, Bitmap bitmap, final Strategy strategy) { + public static SearchResult parseMapJSON(final String data, final Tile tile, final Bitmap bitmap, final Strategy strategy) { final SearchResult searchResult = new SearchResult(); try { @@ -142,13 +143,13 @@ public class GCMap { } // iterate over the data and construct all caches in this tile - Map<String, List<UTFGridPosition>> positions = new HashMap<>(); // JSON id as key - Map<String, List<UTFGridPosition>> singlePositions = new HashMap<>(); // JSON id as key + final Map<String, List<UTFGridPosition>> positions = new HashMap<>(); // JSON id as key + final Map<String, List<UTFGridPosition>> singlePositions = new HashMap<>(); // JSON id as key for (final JsonNode rawKey: keys) { final String key = rawKey.asText(); if (StringUtils.isNotBlank(key)) { // index 0 is empty - UTFGridPosition pos = UTFGridPosition.fromString(key); + final UTFGridPosition pos = UTFGridPosition.fromString(key); final ArrayNode dataForKey = (ArrayNode) dataObject.get(key); for (final JsonNode cacheInfo: dataForKey) { final String id = cacheInfo.get("i").asText(); @@ -174,18 +175,18 @@ public class GCMap { } final ArrayList<Geocache> caches = new ArrayList<>(); - for (Entry<String, List<UTFGridPosition>> entry : positions.entrySet()) { - String id = entry.getKey(); - List<UTFGridPosition> pos = entry.getValue(); - UTFGridPosition xy = UTFGrid.getPositionInGrid(pos); - Geocache cache = new Geocache(); + for (final Entry<String, List<UTFGridPosition>> entry : positions.entrySet()) { + final String id = entry.getKey(); + final List<UTFGridPosition> pos = entry.getValue(); + final UTFGridPosition xy = UTFGrid.getPositionInGrid(pos); + final Geocache cache = new Geocache(); cache.setDetailed(false); cache.setReliableLatLon(false); cache.setGeocode(id); cache.setName(nameCache.get(id)); cache.setCoords(tile.getCoord(xy), tile.getZoomLevel()); if (strategy.flags.contains(StrategyFlag.PARSE_TILES) && bitmap != null) { - for (UTFGridPosition singlePos : singlePositions.get(id)) { + for (final UTFGridPosition singlePos : singlePositions.get(id)) { if (IconDecoder.parseMapPNG(cache, bitmap, singlePos, tile.getZoomLevel())) { break; // cache parsed } @@ -227,17 +228,18 @@ public class GCMap { * Live map tokens * @return */ + @NonNull public static SearchResult searchByViewport(final Viewport viewport, final MapTokens tokens) { - int speed = (int) CgeoApplication.getInstance().currentGeo().getSpeed() * 60 * 60 / 1000; // in km/h + final int speed = (int) Sensors.getInstance().currentGeo().getSpeed() * 60 * 60 / 1000; // in km/h Strategy strategy = Settings.getLiveMapStrategy(); if (strategy == Strategy.AUTO) { strategy = speed >= 30 ? Strategy.FAST : Strategy.DETAILED; } - SearchResult result = searchByViewport(viewport, tokens, strategy); + final SearchResult result = searchByViewport(viewport, tokens, strategy); if (Settings.isDebug()) { - StringBuilder text = new StringBuilder(Formatter.SEPARATOR).append(strategy.getL10n()).append(Formatter.SEPARATOR).append(Units.getSpeed(speed)); + final StringBuilder text = new StringBuilder(Formatter.SEPARATOR).append(strategy.getL10n()).append(Formatter.SEPARATOR).append(Units.getSpeed(speed)); result.setUrl(result.getUrl() + text); } @@ -257,6 +259,7 @@ public class GCMap { * Strategy for data retrieval and parsing, @see Strategy * @return */ + @NonNull private static SearchResult searchByViewport(final Viewport viewport, final MapTokens tokens, final Strategy strategy) { Log.d("GCMap.searchByViewport" + viewport.toString()); @@ -340,7 +343,7 @@ public class GCMap { 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); + final SearchResult search = GCParser.searchByCoords(center, Settings.getCacheType(), false, null); if (search != null && !search.isEmpty()) { final Set<String> geocodes = search.getGeocodes(); lastSearchViewport = DataStore.getBounds(geocodes); @@ -354,11 +357,11 @@ public class GCMap { /** * Creates a list of caches types to filter on the live map (exclusion string) - * + * * @param typeToDisplay * - cache type to omit from exclusion list so it gets displayed * @return - * + * * cache types for live map filter: * 2 = traditional, 9 = ape, 5 = letterbox * 3 = multi @@ -366,8 +369,8 @@ public class GCMap { * 4 = virtual, 11 = webcam, 137 = earth * 8 = mystery, 1858 = whereigo */ - private static String getCacheTypeFilter(CacheType typeToDisplay) { - Set<String> filterTypes = new HashSet<>(); + private static String getCacheTypeFilter(final CacheType typeToDisplay) { + final Set<String> filterTypes = new HashSet<>(); // Put all types in set, remove what should be visible in a second step filterTypes.addAll(Arrays.asList("2", "9", "5", "3", "6", "453", "13", "1304", "4", "11", "137", "8", "1858")); switch (typeToDisplay) { diff --git a/main/src/cgeo/geocaching/connector/gc/GCParser.java b/main/src/cgeo/geocaching/connector/gc/GCParser.java index 33447c7..a25cba8 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCParser.java +++ b/main/src/cgeo/geocaching/connector/gc/GCParser.java @@ -78,10 +78,16 @@ import java.util.Set; import java.util.regex.Pattern; public abstract class GCParser { + @NonNull private final static SynchronizedDateFormat DATE_TB_IN_1 = new SynchronizedDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 + + @NonNull private final static SynchronizedDateFormat DATE_TB_IN_2 = new SynchronizedDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 + + @NonNull private final static ImmutablePair<StatusCode, Geocache> UNKNOWN_PARSE_ERROR = ImmutablePair.of(StatusCode.UNKNOWN_ERROR, null); + @Nullable private static SearchResult parseSearch(final String url, final String pageContent, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(pageContent)) { Log.e("GCParser.parseSearch: No page given"); @@ -349,11 +355,13 @@ public abstract class GCParser { return searchResult; } + @Nullable private static Float parseStars(final String value) { final float floatValue = Float.parseFloat(StringUtils.replaceChars(value, ',', '.')); return floatValue >= 0.5 && floatValue <= 5.0 ? floatValue : null; } + @Nullable static SearchResult parseCache(final String page, final CancellableHandler handler) { final ImmutablePair<StatusCode, Geocache> parsed = parseCacheFromText(page, handler); // attention: parseCacheFromText already stores implicitly through searchResult.addCache @@ -379,6 +387,7 @@ public abstract class GCParser { return new SearchResult(cache); } + @NonNull static SearchResult parseAndSaveCacheFromText(final String page, @Nullable final CancellableHandler handler) { final ImmutablePair<StatusCode, Geocache> parsed = parseCacheFromText(page, handler); final SearchResult result = new SearchResult(parsed.left); @@ -771,8 +780,6 @@ public abstract class GCParser { } } - cache.parseWaypointsFromNote(); - // last check for necessary cache conditions if (StringUtils.isBlank(cache.getGeocode())) { return UNKNOWN_PARSE_ERROR; @@ -782,10 +789,12 @@ public abstract class GCParser { return ImmutablePair.of(StatusCode.NO_ERROR, cache); } + @Nullable private static String getNumberString(final String numberWithPunctuation) { return StringUtils.replaceChars(numberWithPunctuation, ".,", ""); } + @Nullable public static SearchResult searchByNextPage(final SearchResult search, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (search == null) { return null; @@ -858,7 +867,7 @@ public abstract class GCParser { } @Nullable - private static SearchResult searchByAny(final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params, final RecaptchaReceiver recaptchaReceiver) { + private static SearchResult searchByAny(@NonNull final CacheType cacheType, final boolean my, final boolean showCaptcha, final Parameters params, final RecaptchaReceiver recaptchaReceiver) { insertCacheType(params, cacheType); final String uri = "http://www.geocaching.com/seek/nearest.aspx"; @@ -885,12 +894,12 @@ public abstract class GCParser { return search; } - public static SearchResult searchByCoords(final @NonNull Geopoint coords, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByCoords(final @NonNull Geopoint coords, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { final Parameters params = new Parameters("lat", Double.toString(coords.getLatitude()), "lng", Double.toString(coords.getLongitude())); return searchByAny(cacheType, false, showCaptcha, params, recaptchaReceiver); } - public static SearchResult searchByKeyword(final @NonNull String keyword, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByKeyword(final @NonNull String keyword, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(keyword)) { Log.e("GCParser.searchByKeyword: No keyword given"); return null; @@ -908,7 +917,7 @@ public abstract class GCParser { return false; } - public static SearchResult searchByUsername(final String userName, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByUsername(final String userName, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(userName)) { Log.e("GCParser.searchByUsername: No user name given"); return null; @@ -919,7 +928,7 @@ public abstract class GCParser { return searchByAny(cacheType, isSearchForMyCaches(userName), showCaptcha, params, recaptchaReceiver); } - public static SearchResult searchByPocketQuery(final String pocketGuid, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByPocketQuery(final String pocketGuid, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(pocketGuid)) { Log.e("GCParser.searchByPocket: No guid name given"); return null; @@ -930,7 +939,7 @@ public abstract class GCParser { return searchByAny(cacheType, false, showCaptcha, params, recaptchaReceiver); } - public static SearchResult searchByOwner(final String userName, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { + public static SearchResult searchByOwner(final String userName, @NonNull final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { if (StringUtils.isBlank(userName)) { Log.e("GCParser.searchByOwner: No user name given"); return null; @@ -940,30 +949,6 @@ public abstract class GCParser { return searchByAny(cacheType, isSearchForMyCaches(userName), showCaptcha, params, recaptchaReceiver); } - public static SearchResult searchByAddress(final String address, final CacheType cacheType, final boolean showCaptcha, final RecaptchaReceiver recaptchaReceiver) { - if (StringUtils.isBlank(address)) { - Log.e("GCParser.searchByAddress: No address given"); - return null; - } - - final ObjectNode response = Network.requestJSON("http://www.geocaching.com/api/geocode", new Parameters("q", address)); - if (response == null) { - return null; - } - - if (!StringUtils.equalsIgnoreCase(response.path("status").asText(), "success")) { - return null; - } - - final JsonNode data = response.path("data"); - final JsonNode latNode = data.get("lat"); - final JsonNode lngNode = data.get("lng"); - if (latNode == null || lngNode == null) { - return null; - } - return searchByCoords(new Geopoint(latNode.asDouble(), lngNode.asDouble()), cacheType, showCaptcha, recaptchaReceiver); - } - @Nullable public static Trackable searchTrackable(final String geocode, final String guid, final String id) { if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid) && StringUtils.isBlank(id)) { @@ -1816,9 +1801,10 @@ public abstract class GCParser { return types; } + @NonNull public static List<TrackableLog> parseTrackableLog(final String page) { if (StringUtils.isEmpty(page)) { - return null; + return Collections.emptyList(); } String table = StringUtils.substringBetween(page, "<table id=\"tblTravelBugs\"", "</table>"); @@ -1831,7 +1817,7 @@ public abstract class GCParser { table = StringUtils.substringBetween(table, "<tbody>", "</tbody>"); if (StringUtils.isBlank(table)) { Log.e("GCParser.parseTrackableLog: tbody not found on page"); - return null; + return Collections.emptyList(); } final List<TrackableLog> trackableLogs = new ArrayList<>(); @@ -1922,7 +1908,7 @@ public abstract class GCParser { } // Wait for completion of logs parsing, retrieving and merging - mergedLogs.toBlocking().last(); + RxUtils.waitForCompletion(mergedLogs); } /** diff --git a/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java b/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java index eba9301..071c3b0 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java +++ b/main/src/cgeo/geocaching/connector/gc/GCSmiliesProvider.java @@ -1,5 +1,7 @@ package cgeo.geocaching.connector.gc; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; public class GCSmiliesProvider { public enum Smiley { @@ -24,9 +26,10 @@ public class GCSmiliesProvider { DISAPPROVE("V"), QUESTION("?"); + @NonNull public final String text; - Smiley(final String text) { + Smiley(@NonNull final String text) { this.text = text; } @@ -35,12 +38,14 @@ public class GCSmiliesProvider { } } + @NonNull public static Smiley[] getSmilies() { return Smiley.values(); } - public static Smiley getSmiley(int itemId) { - for (Smiley smiley : getSmilies()) { + @Nullable + public static Smiley getSmiley(final int itemId) { + for (final Smiley smiley : getSmilies()) { if (smiley.getItemId() == itemId) { return smiley; } diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java index 9e9ec7f..a2322e0 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiConnector.java @@ -48,6 +48,7 @@ public class OCApiConnector extends OCConnector implements ISearchByGeocode { } @Override + @NonNull public String getLicenseText(final @NonNull Geocache cache) { // NOT TO BE TRANSLATED return "© " + cache.getOwnerDisplayName() + ", <a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a>, " + licenseString; diff --git a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java index e8f20c9..2c783fb 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCApiLiveConnector.java @@ -17,6 +17,7 @@ import cgeo.geocaching.connector.oc.UserInfo.UserInfoStatus; import cgeo.geocaching.loaders.RecaptchaReceiver; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.Viewport; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.utils.CryptUtils; @@ -49,7 +50,8 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public SearchResult searchByViewport(@NonNull final Viewport viewport, final MapTokens tokens) { + @NonNull + public SearchResult searchByViewport(@NonNull final Viewport viewport, @NonNull final MapTokens tokens) { return new SearchResult(OkapiClient.getCachesBBox(viewport, this)); } @@ -98,7 +100,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public boolean addToWatchlist(final Geocache cache) { + public boolean addToWatchlist(@NonNull final Geocache cache) { final boolean added = OkapiClient.setWatchState(cache, true, this); if (added) { @@ -109,7 +111,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public boolean removeFromWatchlist(final Geocache cache) { + public boolean removeFromWatchlist(@NonNull final Geocache cache) { final boolean removed = OkapiClient.setWatchState(cache, false, this); if (removed) { @@ -125,12 +127,13 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public ILoggingManager getLoggingManager(final LogCacheActivity activity, final Geocache cache) { + @NonNull + public ILoggingManager getLoggingManager(@NonNull final LogCacheActivity activity, @NonNull final Geocache cache) { return new OkapiLoggingManager(activity, this, cache); } @Override - public boolean canLog(final Geocache cache) { + public boolean canLog(@NonNull final Geocache cache) { return true; } @@ -149,7 +152,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return StringUtils.isNotEmpty(getUserName()) && StringUtils.equals(cache.getOwnerDisplayName(), getUserName()); } @@ -175,8 +178,7 @@ public class OCApiLiveConnector extends OCApiConnector implements ISearchByCente @Override public SearchResult searchByKeyword(final @NonNull String name, final @NonNull RecaptchaReceiver recaptchaReceiver) { - final Geopoint currentPos = CgeoApplication.getInstance().currentGeo().getCoords(); - return new SearchResult(OkapiClient.getCachesNamed(currentPos, name, this)); + return new SearchResult(OkapiClient.getCachesNamed(Sensors.getInstance().currentGeo().getCoords(), name, this)); } @Override diff --git a/main/src/cgeo/geocaching/connector/oc/OCConnector.java b/main/src/cgeo/geocaching/connector/oc/OCConnector.java index 9c79f71..8ac457b 100644 --- a/main/src/cgeo/geocaching/connector/oc/OCConnector.java +++ b/main/src/cgeo/geocaching/connector/oc/OCConnector.java @@ -28,42 +28,46 @@ public class OCConnector extends AbstractConnector { } @Override - public boolean canHandle(@NonNull String geocode) { + public boolean canHandle(@NonNull final String geocode) { return codePattern.matcher(geocode).matches(); } @Override + @NonNull public String getName() { return name; } @Override - public String getCacheUrl(@NonNull Geocache cache) { + @NonNull + public String getCacheUrl(@NonNull final Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @Override + @NonNull public String getHost() { return host; } @Override - public boolean isZippedGPXFile(String fileName) { + public boolean isZippedGPXFile(@NonNull final String fileName) { return GPX_ZIP_FILE_PATTERN.matcher(fileName).matches(); } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @Override + @NonNull protected String getCacheUrlPrefix() { return "http://" + host + "/viewcache.php?wp="; } @Override - public int getCacheMapMarkerId(boolean disabled) { + public int getCacheMapMarkerId(final boolean disabled) { if (disabled) { return R.drawable.marker_disabled_oc; } @@ -71,7 +75,8 @@ public class OCConnector extends AbstractConnector { } @Override - public final List<LogType> getPossibleLogTypes(Geocache cache) { + @NonNull + public final List<LogType> getPossibleLogTypes(@NonNull final Geocache cache) { if (cache.isEventCache()) { return EVENT_LOG_TYPES; } diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java index 1d0f991..ee095e3 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiClient.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiClient.java @@ -397,7 +397,6 @@ final class OkapiClient { } if (response.hasNonNull(CACHE_MY_NOTES)) { cache.setPersonalNote(response.get(CACHE_MY_NOTES).asText()); - cache.parseWaypointsFromNote(); } cache.setLogPasswordRequired(response.get(CACHE_REQ_PASSWORD).asBoolean()); @@ -620,6 +619,7 @@ final class OkapiClient { cache.setCoords(new Geopoint(latitude, longitude)); } + @NonNull private static CacheSize getCacheSize(final ObjectNode response) { if (!response.has(CACHE_SIZE2)) { return getCacheSizeDeprecated(response); @@ -633,6 +633,7 @@ final class OkapiClient { } } + @NonNull private static CacheSize getCacheSizeDeprecated(final ObjectNode response) { if (!response.has(CACHE_SIZE_DEPRECATED)) { return CacheSize.NOT_CHOSEN; diff --git a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java index 75bc987..76e597c 100644 --- a/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java +++ b/main/src/cgeo/geocaching/connector/oc/OkapiLoggingManager.java @@ -9,6 +9,9 @@ import cgeo.geocaching.connector.LogResult; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; + import android.net.Uri; import java.util.Calendar; @@ -19,7 +22,7 @@ public class OkapiLoggingManager extends AbstractLoggingManager { private final OCApiLiveConnector connector; private final Geocache cache; - private LogCacheActivity activity; + private final LogCacheActivity activity; private boolean hasLoaderError = true; public OkapiLoggingManager(final LogCacheActivity activity, final OCApiLiveConnector connector, final Geocache cache) { @@ -37,18 +40,21 @@ public class OkapiLoggingManager extends AbstractLoggingManager { } @Override - public final LogResult postLog(final LogType logType, final Calendar date, final String log, final String logPassword, final List<TrackableLog> trackableLogs) { + @NonNull + public final LogResult postLog(@NonNull final LogType logType, @NonNull final Calendar date, @NonNull final String log, @Nullable final String logPassword, @NonNull final List<TrackableLog> trackableLogs) { final LogResult result = OkapiClient.postLog(cache, logType, date, log, logPassword, connector); connector.login(null, null); return result; } @Override + @NonNull public final ImageResult postLogImage(final String logId, final String imageCaption, final String imageDescription, final Uri imageUri) { return new ImageResult(StatusCode.LOG_POST_ERROR, ""); } @Override + @NonNull public List<LogType> getPossibleLogTypes() { if (hasLoaderError) { return Collections.emptyList(); diff --git a/main/src/cgeo/geocaching/connector/ox/OXConnector.java b/main/src/cgeo/geocaching/connector/ox/OXConnector.java index dd048f9..41035d1 100644 --- a/main/src/cgeo/geocaching/connector/ox/OXConnector.java +++ b/main/src/cgeo/geocaching/connector/ox/OXConnector.java @@ -29,33 +29,37 @@ public class OXConnector extends AbstractConnector implements ISearchByCenter, I private static final Pattern PATTERN_GEOCODE = Pattern.compile("OX[A-Z0-9]+", Pattern.CASE_INSENSITIVE); @Override - public boolean canHandle(@NonNull String geocode) { + public boolean canHandle(@NonNull final String geocode) { return PATTERN_GEOCODE.matcher(geocode).matches(); } @Override - public String getCacheUrl(@NonNull Geocache cache) { + @NonNull + public String getCacheUrl(@NonNull final Geocache cache) { return getCacheUrlPrefix() + cache.getGeocode(); } @Override + @NonNull public String getName() { return "OpenCaching.com"; } @Override + @NonNull public String getHost() { return "www.opencaching.com"; } @Override - public String getLicenseText(@NonNull Geocache cache) { + @NonNull + public String getLicenseText(@NonNull final Geocache cache) { // NOT TO BE TRANSLATED return "<a href=\"" + getCacheUrl(cache) + "\">" + getName() + "</a> data licensed under the Creative Commons CC-BY-SA 3.0 License"; } @Override - public boolean isOwner(final Geocache cache) { + public boolean isOwner(@NonNull final Geocache cache) { return false; } @@ -73,17 +77,19 @@ public class OXConnector extends AbstractConnector implements ISearchByCenter, I } @Override - public SearchResult searchByCenter(@NonNull Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { + public SearchResult searchByCenter(@NonNull final Geopoint center, final @NonNull RecaptchaReceiver recaptchaReceiver) { return createSearchResult(OpenCachingApi.searchByCenter(center)); } @Override + @NonNull protected String getCacheUrlPrefix() { return "http://www.opencaching.com/#!geocache/"; } @Override - public SearchResult searchByViewport(@NonNull Viewport viewport, final MapTokens tokens) { + @NonNull + public SearchResult searchByViewport(@NonNull final Viewport viewport, @NonNull final MapTokens tokens) { return createSearchResult(OpenCachingApi.searchByBoundingBox(viewport)); } @@ -97,7 +103,7 @@ public class OXConnector extends AbstractConnector implements ISearchByCenter, I return createSearchResult(OpenCachingApi.searchByKeyword(name)); } - private static SearchResult createSearchResult(Collection<Geocache> caches) { + private static SearchResult createSearchResult(final Collection<Geocache> caches) { if (caches == null) { return null; } diff --git a/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java index fb554b9..c3a7437 100644 --- a/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/AbstractTrackableConnector.java @@ -16,14 +16,14 @@ public abstract class AbstractTrackableConnector implements TrackableConnector { } @Override - public @Nullable - String getTrackableCodeFromUrl(@NonNull String url) { + @Nullable + public String getTrackableCodeFromUrl(@NonNull final String url) { return null; } @Override - public @NonNull - List<UserAction> getUserActions() { + @NonNull + public List<UserAction> getUserActions() { return AbstractConnector.getDefaultUserActions(); } } diff --git a/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java b/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java index 03052f9..b6792f0 100644 --- a/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/GeokretyConnector.java @@ -15,17 +15,18 @@ public class GeokretyConnector extends AbstractTrackableConnector { private static final Pattern PATTERN_GK_CODE = Pattern.compile("GK[0-9A-F]{4,}"); @Override - public boolean canHandleTrackable(String geocode) { + public boolean canHandleTrackable(final String geocode) { return geocode != null && PATTERN_GK_CODE.matcher(geocode).matches(); } @Override - public String getUrl(Trackable trackable) { + @NonNull + public String getUrl(@NonNull final Trackable trackable) { return "http://geokrety.org/konkret.php?id=" + getId(trackable.getGeocode()); } @Override - public Trackable searchTrackable(String geocode, String guid, String id) { + public Trackable searchTrackable(final String geocode, final String guid, final String id) { final String page = Network.getResponseData(Network.getRequest("http://geokrety.org/export2.php?gkid=" + getId(geocode))); if (page == null) { return null; @@ -33,7 +34,7 @@ public class GeokretyConnector extends AbstractTrackableConnector { return GeokretyParser.parse(page); } - protected static int getId(String geocode) { + protected static int getId(final String geocode) { try { final String hex = geocode.substring(2); return Integer.parseInt(hex, 16); @@ -45,9 +46,9 @@ public class GeokretyConnector extends AbstractTrackableConnector { @Override public @Nullable - String getTrackableCodeFromUrl(@NonNull String url) { + String getTrackableCodeFromUrl(@NonNull final String url) { // http://geokrety.org/konkret.php?id=38545 - String id = StringUtils.substringAfterLast(url, "konkret.php?id="); + final String id = StringUtils.substringAfterLast(url, "konkret.php?id="); if (StringUtils.isNumeric(id)) { return geocode(Integer.parseInt(id)); } diff --git a/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java index 6071b5f..01c1897 100644 --- a/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/TrackableConnector.java @@ -16,16 +16,17 @@ public interface TrackableConnector { public boolean canHandleTrackable(final String geocode); - public String getUrl(final Trackable trackable); + @NonNull + public String getUrl(@NonNull final Trackable trackable); public boolean isLoggable(); public Trackable searchTrackable(String geocode, String guid, String id); - public @Nullable - String getTrackableCodeFromUrl(final @NonNull String url); + @Nullable + public String getTrackableCodeFromUrl(final @NonNull String url); - public @NonNull - List<UserAction> getUserActions(); + @NonNull + public List<UserAction> getUserActions(); } diff --git a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java index 3307481..5d825a3 100644 --- a/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/TravelBugConnector.java @@ -25,7 +25,8 @@ public class TravelBugConnector extends AbstractTrackableConnector { } @Override - public String getUrl(final Trackable trackable) { + @NonNull + public String getUrl(@NonNull final Trackable trackable) { return "http://www.geocaching.com//track/details.aspx?tracker=" + trackable.getGeocode(); } diff --git a/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java b/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java index 0295927..7e7e1b6 100644 --- a/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java +++ b/main/src/cgeo/geocaching/connector/trackable/UnknownTrackableConnector.java @@ -2,22 +2,23 @@ package cgeo.geocaching.connector.trackable; import cgeo.geocaching.Trackable; -import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; public class UnknownTrackableConnector extends AbstractTrackableConnector { @Override - public boolean canHandleTrackable(String geocode) { + public boolean canHandleTrackable(final String geocode) { return false; } @Override - public String getUrl(Trackable trackable) { - return StringUtils.EMPTY; + @NonNull + public String getUrl(@NonNull final Trackable trackable) { + throw new IllegalStateException("getUrl cannot be called on unknown trackables"); } @Override - public Trackable searchTrackable(String geocode, String guid, String id) { + public Trackable searchTrackable(final String geocode, final String guid, final String id) { return null; } diff --git a/main/src/cgeo/geocaching/enumerations/CacheAttribute.java b/main/src/cgeo/geocaching/enumerations/CacheAttribute.java index 771a508..823ea7e 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheAttribute.java +++ b/main/src/cgeo/geocaching/enumerations/CacheAttribute.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import android.util.SparseArray; @@ -163,7 +165,10 @@ public enum CacheAttribute { enabled ? stringIdYes : stringIdNo); } + @NonNull private final static Map<String, CacheAttribute> FIND_BY_GCRAWNAME; + + @NonNull private final static SparseArray<CacheAttribute> FIND_BY_OCACODE = new SparseArray<>(); static { final HashMap<String, CacheAttribute> mapGcRawNames = new HashMap<>(); @@ -176,14 +181,17 @@ public enum CacheAttribute { FIND_BY_GCRAWNAME = Collections.unmodifiableMap(mapGcRawNames); } + @Nullable public static CacheAttribute getByRawName(final String rawName) { return rawName != null ? FIND_BY_GCRAWNAME.get(rawName) : null; } + @Nullable public static CacheAttribute getByOcACode(final int ocAcode) { return FIND_BY_OCACODE.get(ocAcode); } + @NonNull public static String trimAttributeName(final String attributeName) { if (null == attributeName) { return ""; diff --git a/main/src/cgeo/geocaching/enumerations/CacheSize.java b/main/src/cgeo/geocaching/enumerations/CacheSize.java index 3a2f379..37446b9 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheSize.java +++ b/main/src/cgeo/geocaching/enumerations/CacheSize.java @@ -3,6 +3,8 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -38,6 +40,7 @@ public enum CacheSize { this.ocSize2 = ocSize2; } + @NonNull final private static Map<String, CacheSize> FIND_BY_ID; static { final HashMap<String, CacheSize> mapping = new HashMap<>(); @@ -50,6 +53,7 @@ public enum CacheSize { FIND_BY_ID = Collections.unmodifiableMap(mapping); } + @NonNull public static CacheSize getById(final String id) { if (id == null) { return UNKNOWN; @@ -73,6 +77,7 @@ public enum CacheSize { * @param id * @return */ + @NonNull private static CacheSize getByNumber(final String id) { try { final int numerical = Integer.parseInt(id); diff --git a/main/src/cgeo/geocaching/enumerations/CacheType.java b/main/src/cgeo/geocaching/enumerations/CacheType.java index 995c436..f551c52 100644 --- a/main/src/cgeo/geocaching/enumerations/CacheType.java +++ b/main/src/cgeo/geocaching/enumerations/CacheType.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -56,9 +58,15 @@ public enum CacheType { this.markerId = markerId; } + @NonNull private final static Map<String, CacheType> FIND_BY_ID; + + @NonNull private final static Map<String, CacheType> FIND_BY_PATTERN; + + @NonNull private final static Map<String, CacheType> FIND_BY_GUID; + static { final HashMap<String, CacheType> mappingId = new HashMap<>(); final HashMap<String, CacheType> mappingPattern = new HashMap<>(); @@ -80,6 +88,7 @@ public enum CacheType { FIND_BY_GUID = Collections.unmodifiableMap(mappingGuid); } + @NonNull public static CacheType getById(final String id) { final CacheType result = (id != null) ? CacheType.FIND_BY_ID.get(id.toLowerCase(Locale.US).trim()) : null; if (result == null) { @@ -88,6 +97,7 @@ public enum CacheType { return result; } + @NonNull public static CacheType getByPattern(final String pattern) { final CacheType result = (pattern != null) ? CacheType.FIND_BY_PATTERN.get(pattern.toLowerCase(Locale.US).trim()) : null; if (result == null) { @@ -96,6 +106,7 @@ public enum CacheType { return result; } + @NonNull public static CacheType getByGuid(final String id) { final CacheType result = (id != null) ? CacheType.FIND_BY_GUID.get(id) : null; if (result == null) { diff --git a/main/src/cgeo/geocaching/enumerations/LogType.java b/main/src/cgeo/geocaching/enumerations/LogType.java index 5345611..4c83321 100644 --- a/main/src/cgeo/geocaching/enumerations/LogType.java +++ b/main/src/cgeo/geocaching/enumerations/LogType.java @@ -3,6 +3,8 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -65,7 +67,10 @@ public enum LogType { this(id, iconName, type, oc_type, stringId, R.drawable.mark_gray); } + @NonNull private final static Map<String, LogType> FIND_BY_ICONNAME; + + @NonNull private final static Map<String, LogType> FIND_BY_TYPE; static { final HashMap<String, LogType> mappingPattern = new HashMap<>(); @@ -80,6 +85,7 @@ public enum LogType { FIND_BY_TYPE = Collections.unmodifiableMap(mappingType); } + @NonNull public static LogType getById(final int id) { for (final LogType logType : values()) { if (logType.id == id) { @@ -89,6 +95,7 @@ public enum LogType { return UNKNOWN; } + @NonNull public static LogType getByIconName(final String imageType) { // Special case for post reviewer note, which appears sometimes as 18.png (in individual entries) or as 68.png // (in logs counts). @@ -102,6 +109,7 @@ public enum LogType { return result; } + @NonNull public static LogType getByType(final String type) { final LogType result = type != null ? LogType.FIND_BY_TYPE.get(type.toLowerCase(Locale.US).trim()) : null; if (result == null) { diff --git a/main/src/cgeo/geocaching/enumerations/LogTypeTrackable.java b/main/src/cgeo/geocaching/enumerations/LogTypeTrackable.java index e008294..fefeb62 100644 --- a/main/src/cgeo/geocaching/enumerations/LogTypeTrackable.java +++ b/main/src/cgeo/geocaching/enumerations/LogTypeTrackable.java @@ -3,15 +3,17 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + public enum LogTypeTrackable { DO_NOTHING("", R.string.log_tb_nothing), VISITED("_Visited", R.string.log_tb_visit), DROPPED_OFF("_DroppedOff", R.string.log_tb_drop); - final public String action; + @NonNull final public String action; final private int resourceId; - LogTypeTrackable(String action, int resourceId) { + LogTypeTrackable(@NonNull final String action, final int resourceId) { this.action = action; this.resourceId = resourceId; } diff --git a/main/src/cgeo/geocaching/enumerations/WaypointType.java b/main/src/cgeo/geocaching/enumerations/WaypointType.java index 1805635..81c0e4d 100644 --- a/main/src/cgeo/geocaching/enumerations/WaypointType.java +++ b/main/src/cgeo/geocaching/enumerations/WaypointType.java @@ -3,6 +3,8 @@ package cgeo.geocaching.enumerations; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -22,11 +24,12 @@ public enum WaypointType { WAYPOINT("waypoint", R.string.wp_waypoint, R.drawable.waypoint_waypoint), ORIGINAL("original", R.string.wp_original, R.drawable.waypoint_waypoint); + @NonNull public final String id; public final int stringId; public final int markerId; - WaypointType(String id, int stringId, int markerId) { + WaypointType(@NonNull final String id, final int stringId, final int markerId) { this.id = id; this.stringId = stringId; this.markerId = markerId; @@ -36,11 +39,14 @@ public enum WaypointType { * inverse lookup of waypoint IDs<br/> * non public so that <code>null</code> handling can be handled centrally in the enum type itself */ + @NonNull private static final Map<String, WaypointType> FIND_BY_ID; + + @NonNull public static final Set<WaypointType> ALL_TYPES_EXCEPT_OWN_AND_ORIGINAL = new HashSet<>(); static { final HashMap<String, WaypointType> mapping = new HashMap<>(); - for (WaypointType wt : values()) { + for (final WaypointType wt : values()) { mapping.put(wt.id, wt); if (wt != WaypointType.OWN && wt != WaypointType.ORIGINAL) { ALL_TYPES_EXCEPT_OWN_AND_ORIGINAL.add(wt); @@ -53,11 +59,12 @@ public enum WaypointType { * inverse lookup of waypoint IDs<br/> * here the <code>null</code> handling shall be done */ + @NonNull public static WaypointType findById(final String id) { if (null == id) { return WAYPOINT; } - WaypointType waypointType = FIND_BY_ID.get(id); + final WaypointType waypointType = FIND_BY_ID.get(id); if (null == waypointType) { return WAYPOINT; } diff --git a/main/src/cgeo/geocaching/export/FieldnoteExport.java b/main/src/cgeo/geocaching/export/FieldnoteExport.java index c03b848..c4a6adc 100644 --- a/main/src/cgeo/geocaching/export/FieldnoteExport.java +++ b/main/src/cgeo/geocaching/export/FieldnoteExport.java @@ -59,10 +59,11 @@ public class FieldnoteExport extends AbstractExport { final AlertDialog.Builder builder = new AlertDialog.Builder(activity); final Context themedContext; - if (Settings.isLightSkin() && VERSION.SDK_INT < VERSION_CODES.HONEYCOMB) + if (Settings.isLightSkin() && VERSION.SDK_INT < VERSION_CODES.HONEYCOMB) { themedContext = new ContextThemeWrapper(activity, R.style.dark); - else + } else { themedContext = activity; + } final View layout = View.inflate(themedContext, R.layout.fieldnote_export_dialog, null); builder.setView(layout); @@ -135,8 +136,10 @@ public class FieldnoteExport extends AbstractExport { for (final Geocache cache : caches) { if (ConnectorFactory.getConnector(cache).equals(connector) && cache.isLogOffline()) { final LogEntry log = DataStore.loadLogOffline(cache.getGeocode()); - if (!onlyNew || log.date > Settings.getFieldnoteExportDate()) { - fieldNotes.add(cache, log); + if (log != null) { + if (!onlyNew || log.date > Settings.getFieldnoteExportDate()) { + fieldNotes.add(cache, log); + } } } publishProgress(++i); diff --git a/main/src/cgeo/geocaching/files/AbstractFileListActivity.java b/main/src/cgeo/geocaching/files/AbstractFileListActivity.java index fa84df9..427fd48 100644 --- a/main/src/cgeo/geocaching/files/AbstractFileListActivity.java +++ b/main/src/cgeo/geocaching/files/AbstractFileListActivity.java @@ -9,6 +9,7 @@ import cgeo.geocaching.utils.Log; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.app.ProgressDialog; import android.content.DialogInterface; @@ -203,7 +204,7 @@ public abstract class AbstractFileListActivity<T extends ArrayAdapter<File>> ext * @param filename * @return <code>true</code> if the filename belongs to the list */ - protected boolean filenameBelongsToList(final String filename) { + protected boolean filenameBelongsToList(@NonNull final String filename) { for (final String ext : extensions) { if (StringUtils.endsWithIgnoreCase(filename, ext)) { return true; diff --git a/main/src/cgeo/geocaching/files/FileParser.java b/main/src/cgeo/geocaching/files/FileParser.java index 9521c70..9c70a0d 100644 --- a/main/src/cgeo/geocaching/files/FileParser.java +++ b/main/src/cgeo/geocaching/files/FileParser.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Collection; -import java.util.Date; import java.util.concurrent.CancellationException; public abstract class FileParser { @@ -24,25 +23,22 @@ public abstract class FileParser { * Parses caches from input stream. * * @param stream + * the input stream * @param progressHandler - * for reporting parsing progress (in bytes read from input stream) + * for reporting parsing progress (in bytes read from input stream) * @return collection of caches * @throws IOException - * if the input stream can't be read + * if the input stream can't be read * @throws ParserException - * if the input stream contains data not matching the file format of the parser + * if the input stream contains data not matching the file format of the parser */ + @NonNull public abstract Collection<Geocache> parse(@NonNull final InputStream stream, @Nullable final CancellableHandler progressHandler) throws IOException, ParserException; /** * Convenience method for parsing a file. - * - * @param file - * @param progressHandler - * @return - * @throws IOException - * @throws ParserException */ + @NonNull public Collection<Geocache> parse(final File file, final CancellableHandler progressHandler) throws IOException, ParserException { final BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file)); try { @@ -52,6 +48,7 @@ public abstract class FileParser { } } + @NonNull protected static StringBuilder readStream(@NonNull final InputStream is, @Nullable final CancellableHandler progressHandler) throws IOException { final StringBuilder buffer = new StringBuilder(); final ProgressInputStream progressInputStream = new ProgressInputStream(is); @@ -84,7 +81,7 @@ public abstract class FileParser { } else { cache.setInventoryItems(0); } - final long time = new Date().getTime(); + final long time = System.currentTimeMillis(); cache.setUpdated(time); cache.setDetailedUpdate(time); } diff --git a/main/src/cgeo/geocaching/files/GPXParser.java b/main/src/cgeo/geocaching/files/GPXParser.java index cd99ae9..7dd5c4a 100644 --- a/main/src/cgeo/geocaching/files/GPXParser.java +++ b/main/src/cgeo/geocaching/files/GPXParser.java @@ -274,6 +274,7 @@ public abstract class GPXParser extends FileParser { } @Override + @NonNull public Collection<Geocache> parse(@NonNull final InputStream stream, @Nullable final CancellableHandler progressHandler) throws IOException, ParserException { resetCache(); final RootElement root = new RootElement(namespace, "gpx"); diff --git a/main/src/cgeo/geocaching/files/LocParser.java b/main/src/cgeo/geocaching/files/LocParser.java index bca7dcf..a5a4b9c 100644 --- a/main/src/cgeo/geocaching/files/LocParser.java +++ b/main/src/cgeo/geocaching/files/LocParser.java @@ -31,8 +31,10 @@ import java.util.Set; public final class LocParser extends FileParser { + @NonNull private static final String NAME_OWNER_SEPARATOR = " by "; + @NonNull private static final CacheSize[] SIZES = { CacheSize.NOT_CHOSEN, // 1 CacheSize.MICRO, // 2 @@ -45,16 +47,17 @@ public final class LocParser extends FileParser { }; // Used so that the initial value of the geocache is not null. Never filled. + @NonNull private static final Geocache DUMMY_GEOCACHE = new Geocache(); - private int listId; + private final int listId; public static void parseLoc(final SearchResult searchResult, final String fileContent, final Set<Geocache> caches) { final Map<String, Geocache> cidCoords = parseLoc(fileContent); // save found cache coordinates final HashSet<String> contained = new HashSet<>(); - for (String geocode : searchResult.getGeocodes()) { + for (final String geocode : searchResult.getGeocodes()) { if (cidCoords.containsKey(geocode)) { contained.add(geocode); } @@ -70,10 +73,12 @@ public final class LocParser extends FileParser { } } + @NonNull private static Map<String, Geocache> parseLoc(final String content) { return parseLoc(new ByteArrayInputStream(content.getBytes(Charsets.UTF_8))); } + @NonNull private static Map<String, Geocache> parseLoc(final InputStream content) { try { final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); @@ -146,22 +151,24 @@ public final class LocParser extends FileParser { cache.setOwnerUserId(coord.getOwnerUserId()); } + @NonNull public static Geopoint parsePoint(final String latitude, final String longitude) { // the loc file contains the coordinates as plain floating point values, therefore avoid using the GeopointParser try { return new Geopoint(Double.valueOf(latitude), Double.valueOf(longitude)); - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { Log.e("LOC format has changed", e); } // fall back to parser, just in case the format changes return new Geopoint(latitude, longitude); } - public LocParser(int listId) { + public LocParser(final int listId) { this.listId = listId; } @Override + @NonNull public Collection<Geocache> parse(@NonNull final InputStream stream, @Nullable final CancellableHandler progressHandler) throws IOException, ParserException { final int maxSize = stream.available(); final Map<String, Geocache> coords = parseLoc(stream); diff --git a/main/src/cgeo/geocaching/files/LocalStorage.java b/main/src/cgeo/geocaching/files/LocalStorage.java index 8896833..7fce27d 100644 --- a/main/src/cgeo/geocaching/files/LocalStorage.java +++ b/main/src/cgeo/geocaching/files/LocalStorage.java @@ -45,7 +45,7 @@ public final class LocalStorage { public static final String HEADER_ETAG = "etag"; /** Name of the local private directory used to hold cached information */ - public final static String cache = ".cgeo"; + public final static String CACHE_DIRNAME = ".cgeo"; private static File internalStorageBase; @@ -74,7 +74,7 @@ public final class LocalStorage { private static File getStorageSpecific(final boolean secondary) { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ^ secondary ? getExternalStorageBase() : - new File(getInternalStorageBase(), LocalStorage.cache); + new File(getInternalStorageBase(), LocalStorage.CACHE_DIRNAME); } public static File getExternalDbDirectory() { @@ -86,7 +86,7 @@ public final class LocalStorage { } private static File getExternalStorageBase() { - return new File(Environment.getExternalStorageDirectory(), LocalStorage.cache); + return new File(Environment.getExternalStorageDirectory(), LocalStorage.CACHE_DIRNAME); } private static File getInternalStorageBase() { @@ -105,7 +105,7 @@ public final class LocalStorage { * @return the file extension, including the leading dot, or the empty string if none could be determined */ static String getExtension(final String url) { - String urlExt; + final String urlExt; if (url.startsWith("data:")) { // "data:image/png;base64,i53…" -> ".png" urlExt = StringUtils.substringAfter(StringUtils.substringBefore(url, ";"), "/"); @@ -124,7 +124,7 @@ public final class LocalStorage { * the geocode * @return the cache directory */ - public static File getStorageDir(@Nullable final String geocode) { + public static File getStorageDir(@NonNull final String geocode) { return storageDir(getStorage(), geocode); } @@ -136,12 +136,12 @@ public final class LocalStorage { * the geocode * @return the cache directory */ - private static File getStorageSecDir(@Nullable final String geocode) { + private static File getStorageSecDir(@NonNull final String geocode) { return storageDir(getStorageSec(), geocode); } - private static File storageDir(final File base, @Nullable final String geocode) { - return new File(base, StringUtils.defaultIfEmpty(geocode, "_others")); + private static File storageDir(final File base, @NonNull final String geocode) { + return new File(base, geocode); } /** @@ -157,7 +157,7 @@ public final class LocalStorage { * true if an url was given, false if a file name was given * @return the file */ - public static File getStorageFile(@Nullable final String geocode, final String fileNameOrUrl, final boolean isUrl, final boolean createDirs) { + public static File getStorageFile(@NonNull final String geocode, final String fileNameOrUrl, final boolean isUrl, final boolean createDirs) { return buildFile(getStorageDir(geocode), fileNameOrUrl, isUrl, createDirs); } @@ -244,14 +244,14 @@ public final class LocalStorage { public static String getSavedHeader(final File baseFile, final String name) { try { final File file = filenameForHeader(baseFile, name); - final Reader f = new InputStreamReader(new FileInputStream(file), CharEncoding.UTF_8); + final Reader reader = new InputStreamReader(new FileInputStream(file), CharEncoding.UTF_8); try { // No header will be more than 256 bytes final char[] value = new char[256]; - final int count = f.read(value); + final int count = reader.read(value); return new String(value, 0, count); } finally { - f.close(); + reader.close(); } } catch (final FileNotFoundException ignored) { // Do nothing, the file does not exist @@ -442,10 +442,10 @@ public final class LocalStorage { try { fr = new InputStreamReader(new FileInputStream(file), CharEncoding.UTF_8); br = new BufferedReader(fr); - String s = br.readLine(); - while (s != null) { - if (s.startsWith("dev_mount")) { - final String[] tokens = StringUtils.split(s); + String str = br.readLine(); + while (str != null) { + if (str.startsWith("dev_mount")) { + final String[] tokens = StringUtils.split(str); if (tokens.length >= 3) { final String path = tokens[2]; // mountpoint if (!extStorage.equals(path)) { @@ -456,7 +456,7 @@ public final class LocalStorage { } } } - s = br.readLine(); + str = br.readLine(); } } catch (final IOException e) { Log.e("Could not get additional mount points for user content. " + diff --git a/main/src/cgeo/geocaching/filter/AbstractFilter.java b/main/src/cgeo/geocaching/filter/AbstractFilter.java index e602b0f..248c9c2 100644 --- a/main/src/cgeo/geocaching/filter/AbstractFilter.java +++ b/main/src/cgeo/geocaching/filter/AbstractFilter.java @@ -2,6 +2,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -13,9 +15,9 @@ abstract class AbstractFilter implements IFilter { } @Override - public void filter(final List<Geocache> list) { + public void filter(@NonNull final List<Geocache> list) { final List<Geocache> itemsToRemove = new ArrayList<>(); - for (Geocache item : list) { + for (final Geocache item : list) { if (!accepts(item)) { itemsToRemove.add(item); } diff --git a/main/src/cgeo/geocaching/filter/AttributeFilter.java b/main/src/cgeo/geocaching/filter/AttributeFilter.java index b59ab29..2fc6eb2 100644 --- a/main/src/cgeo/geocaching/filter/AttributeFilter.java +++ b/main/src/cgeo/geocaching/filter/AttributeFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import android.content.res.Resources; import java.util.LinkedList; @@ -25,13 +27,14 @@ class AttributeFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.getAttributes().contains(attribute); } public static class Factory implements IFilterFactory { @Override + @NonNull public List<IFilter> getFilters() { final String packageName = CgeoApplication.getInstance().getBaseContext().getPackageName(); final Resources res = CgeoApplication.getInstance().getResources(); diff --git a/main/src/cgeo/geocaching/filter/DifficultyFilter.java b/main/src/cgeo/geocaching/filter/DifficultyFilter.java index 175ad75..7989560 100644 --- a/main/src/cgeo/geocaching/filter/DifficultyFilter.java +++ b/main/src/cgeo/geocaching/filter/DifficultyFilter.java @@ -3,6 +3,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -13,7 +15,7 @@ class DifficultyFilter extends AbstractRangeFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { final float difficulty = cache.getDifficulty(); return rangeMin <= difficulty && difficulty < rangeMax; } @@ -24,6 +26,7 @@ class DifficultyFilter extends AbstractRangeFilter { private static final int DIFFICULTY_MAX = 5; @Override + @NonNull public List<IFilter> getFilters() { final ArrayList<IFilter> filters = new ArrayList<>(DIFFICULTY_MAX); for (int difficulty = DIFFICULTY_MIN; difficulty <= DIFFICULTY_MAX; difficulty++) { diff --git a/main/src/cgeo/geocaching/filter/DistanceFilter.java b/main/src/cgeo/geocaching/filter/DistanceFilter.java index 3890571..f1ba7f8 100644 --- a/main/src/cgeo/geocaching/filter/DistanceFilter.java +++ b/main/src/cgeo/geocaching/filter/DistanceFilter.java @@ -2,9 +2,12 @@ package cgeo.geocaching.filter; import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; -import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.R; import cgeo.geocaching.location.Geopoint; +import cgeo.geocaching.sensors.GeoData; +import cgeo.geocaching.sensors.Sensors; + +import org.eclipse.jdt.annotation.NonNull; import java.util.ArrayList; import java.util.List; @@ -14,15 +17,15 @@ class DistanceFilter extends AbstractFilter { private final int minDistance; private final int maxDistance; - public DistanceFilter(String name, final int minDistance, final int maxDistance) { + public DistanceFilter(final String name, final int minDistance, final int maxDistance) { super(name); this.minDistance = minDistance; this.maxDistance = maxDistance; - geo = CgeoApplication.getInstance().currentGeo(); + geo = Sensors.getInstance().currentGeo(); } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { final Geopoint currentPos = new Geopoint(geo); final Geopoint coords = cache.getCoords(); if (coords == null) { @@ -39,6 +42,7 @@ class DistanceFilter extends AbstractFilter { private static final int[] KILOMETERS = { 0, 2, 5, 10, 20, 50 }; @Override + @NonNull public List<IFilter> getFilters() { final List<IFilter> filters = new ArrayList<>(KILOMETERS.length); for (int i = 0; i < KILOMETERS.length; i++) { diff --git a/main/src/cgeo/geocaching/filter/FilterUserInterface.java b/main/src/cgeo/geocaching/filter/FilterUserInterface.java index 9f1d563..590a726 100644 --- a/main/src/cgeo/geocaching/filter/FilterUserInterface.java +++ b/main/src/cgeo/geocaching/filter/FilterUserInterface.java @@ -54,12 +54,12 @@ public final class FilterUserInterface { register(R.string.cache_attributes, AttributeFilter.Factory.class); register(R.string.cache_status, StateFilter.Factory.class); register(R.string.caches_filter_track, TrackablesFilter.class); - register(R.string.caches_filter_modified, ModifiedFilter.class); register(R.string.caches_filter_origin, OriginFilter.Factory.class); register(R.string.caches_filter_distance, DistanceFilter.Factory.class); - register(R.string.caches_filter_personal_note, PersonalNoteFilter.class); register(R.string.caches_filter_popularity, PopularityFilter.Factory.class); register(R.string.caches_filter_popularity_ratio, PopularityRatioFilter.Factory.class); + register(R.string.caches_filter_personal_data, PersonalDataFilterFactory.class); + register(R.string.caches_filter_rating, RatingFilter.class); // sort by localized names Collections.sort(registry, new Comparator<FactoryEntry>() { @@ -87,16 +87,16 @@ public final class FilterUserInterface { builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(final DialogInterface dialog, final int itemIndex) { - FactoryEntry entry = adapter.getItem(itemIndex); + final FactoryEntry entry = adapter.getItem(itemIndex); // reset? if (entry.filterFactory == null) { runAfterwards.call(null); } else { try { - IFilterFactory factoryInstance = entry.filterFactory.newInstance(); + final IFilterFactory factoryInstance = entry.filterFactory.newInstance(); selectFromFactory(factoryInstance, entry.name, runAfterwards); - } catch (Exception e) { + } catch (final Exception e) { Log.e("selectFilter", e); } } diff --git a/main/src/cgeo/geocaching/filter/IFilter.java b/main/src/cgeo/geocaching/filter/IFilter.java index 4fafe6f..de39e5a 100644 --- a/main/src/cgeo/geocaching/filter/IFilter.java +++ b/main/src/cgeo/geocaching/filter/IFilter.java @@ -2,6 +2,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; +import org.eclipse.jdt.annotation.NonNull; + import java.util.List; public interface IFilter { @@ -12,7 +14,7 @@ public interface IFilter { * @param cache * @return true if the filter accepts the cache, false otherwise */ - boolean accepts(final Geocache cache); + boolean accepts(@NonNull final Geocache cache); - void filter(final List<Geocache> list); + void filter(@NonNull 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 afc99af..82e69da 100644 --- a/main/src/cgeo/geocaching/filter/IFilterFactory.java +++ b/main/src/cgeo/geocaching/filter/IFilterFactory.java @@ -1,7 +1,10 @@ package cgeo.geocaching.filter; +import org.eclipse.jdt.annotation.NonNull; + import java.util.List; interface IFilterFactory { + @NonNull List<? extends IFilter> getFilters(); } diff --git a/main/src/cgeo/geocaching/filter/ModifiedFilter.java b/main/src/cgeo/geocaching/filter/ModifiedFilter.java index 2ac088a..c224cb4 100644 --- a/main/src/cgeo/geocaching/filter/ModifiedFilter.java +++ b/main/src/cgeo/geocaching/filter/ModifiedFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.List; @@ -14,12 +16,13 @@ class ModifiedFilter extends AbstractFilter implements IFilterFactory { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { // modified on GC return cache.hasUserModifiedCoords() || cache.hasFinalDefined(); } @Override + @NonNull public List<ModifiedFilter> getFilters() { return Collections.singletonList(this); } diff --git a/main/src/cgeo/geocaching/filter/OriginFilter.java b/main/src/cgeo/geocaching/filter/OriginFilter.java index 99d1c05..4fb3301 100644 --- a/main/src/cgeo/geocaching/filter/OriginFilter.java +++ b/main/src/cgeo/geocaching/filter/OriginFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.connector.IConnector; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -19,16 +21,17 @@ public class OriginFilter extends AbstractFilter { } @Override - public final boolean accepts(final Geocache cache) { + public final boolean accepts(@NonNull final Geocache cache) { return ConnectorFactory.getConnector(cache) == connector; } public static final class Factory implements IFilterFactory { @Override + @NonNull public List<OriginFilter> getFilters() { final ArrayList<OriginFilter> filters = new ArrayList<>(); - for (IConnector connector : ConnectorFactory.getConnectors()) { + for (final IConnector connector : ConnectorFactory.getConnectors()) { filters.add(new OriginFilter(connector)); } diff --git a/main/src/cgeo/geocaching/filter/OwnRatingFilter.java b/main/src/cgeo/geocaching/filter/OwnRatingFilter.java new file mode 100644 index 0000000..0c468a9 --- /dev/null +++ b/main/src/cgeo/geocaching/filter/OwnRatingFilter.java @@ -0,0 +1,35 @@ +package cgeo.geocaching.filter; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; +import cgeo.geocaching.gcvote.GCVote; + +import org.eclipse.jdt.annotation.NonNull; + +import java.util.Collections; +import java.util.List; + +/** + * Filter {@link Geocache}s if they have a locally stored <b>own</b> {@link GCVote} rating. This filter will not do any + * network request to find potentially missing local votes. + * + */ +public class OwnRatingFilter extends AbstractFilter implements IFilterFactory { + + protected OwnRatingFilter() { + super(CgeoApplication.getInstance().getString(R.string.caches_filter_own_rating)); + } + + @Override + public boolean accepts(@NonNull final Geocache cache) { + return cache.getMyVote() > 0; + } + + @Override + @NonNull + public List<OwnRatingFilter> getFilters() { + return Collections.singletonList(this); + } + +} diff --git a/main/src/cgeo/geocaching/filter/PersonalDataFilterFactory.java b/main/src/cgeo/geocaching/filter/PersonalDataFilterFactory.java new file mode 100644 index 0000000..6c6186b --- /dev/null +++ b/main/src/cgeo/geocaching/filter/PersonalDataFilterFactory.java @@ -0,0 +1,16 @@ +package cgeo.geocaching.filter; + +import org.eclipse.jdt.annotation.NonNull; + +import java.util.Arrays; +import java.util.List; + +public class PersonalDataFilterFactory implements IFilterFactory { + + @Override + @NonNull + public List<? extends IFilter> getFilters() { + return Arrays.asList(new OwnRatingFilter(), new PersonalNoteFilter(), new ModifiedFilter()); + } + +} diff --git a/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java b/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java index 15d262f..978ad6b 100644 --- a/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java +++ b/main/src/cgeo/geocaching/filter/PersonalNoteFilter.java @@ -5,10 +5,14 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import java.util.Collections; import java.util.List; +/** + * Filter that accepts {@link Geocache}s with a non empty personal note stored locally. + */ public class PersonalNoteFilter extends AbstractFilter implements IFilterFactory { protected PersonalNoteFilter() { @@ -16,11 +20,12 @@ public class PersonalNoteFilter extends AbstractFilter implements IFilterFactory } @Override - public boolean accepts(Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return StringUtils.isNotBlank(cache.getPersonalNote()); } @Override + @NonNull public List<PersonalNoteFilter> getFilters() { return Collections.singletonList(this); } diff --git a/main/src/cgeo/geocaching/filter/PopularityFilter.java b/main/src/cgeo/geocaching/filter/PopularityFilter.java index 0fc807d..bb564e8 100644 --- a/main/src/cgeo/geocaching/filter/PopularityFilter.java +++ b/main/src/cgeo/geocaching/filter/PopularityFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -11,14 +13,14 @@ class PopularityFilter extends AbstractFilter { private final int minFavorites; private final int maxFavorites; - public PopularityFilter(String name, final int minFavorites, final int maxFavorites) { + public PopularityFilter(final String name, final int minFavorites, final int maxFavorites) { super(name); this.minFavorites = minFavorites; this.maxFavorites = maxFavorites; } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return (cache.getFavoritePoints() > minFavorites) && (cache.getFavoritePoints() <= maxFavorites); } @@ -27,6 +29,7 @@ class PopularityFilter extends AbstractFilter { private static final int[] FAVORITES = { 10, 20, 50, 100, 200, 500 }; @Override + @NonNull public List<IFilter> getFilters() { final List<IFilter> filters = new ArrayList<>(FAVORITES.length); for (final int minRange : FAVORITES) { diff --git a/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java b/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java index f7ac4db..53904f1 100644 --- a/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java +++ b/main/src/cgeo/geocaching/filter/PopularityRatioFilter.java @@ -6,6 +6,8 @@ import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.enumerations.LogType; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -23,7 +25,7 @@ class PopularityRatioFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { final int finds = getFindsCount(cache); if (finds == 0) { // Prevent division by zero @@ -51,6 +53,7 @@ class PopularityRatioFilter extends AbstractFilter { private static final int[] RATIOS = { 10, 20, 30, 40, 50, 75 }; @Override + @NonNull public List<IFilter> getFilters() { final List<IFilter> filters = new ArrayList<>(RATIOS.length); for (final int minRange : RATIOS) { diff --git a/main/src/cgeo/geocaching/filter/RatingFilter.java b/main/src/cgeo/geocaching/filter/RatingFilter.java new file mode 100644 index 0000000..3edfcb6 --- /dev/null +++ b/main/src/cgeo/geocaching/filter/RatingFilter.java @@ -0,0 +1,35 @@ +package cgeo.geocaching.filter; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; +import cgeo.geocaching.gcvote.GCVote; + +import org.eclipse.jdt.annotation.NonNull; + +import java.util.Collections; +import java.util.List; + +/** + * Filter {@link Geocache}s if they have a locally stored {@link GCVote} rating. This filter will not do any network + * request to find potentially missing local votes. + * + */ +public class RatingFilter extends AbstractFilter implements IFilterFactory { + + protected RatingFilter() { + super(CgeoApplication.getInstance().getString(R.string.caches_filter_rating)); + } + + @Override + public boolean accepts(@NonNull final Geocache cache) { + return cache.getRating() > 0; + } + + @Override + @NonNull + public List<RatingFilter> getFilters() { + return Collections.singletonList(this); + } + +} diff --git a/main/src/cgeo/geocaching/filter/SizeFilter.java b/main/src/cgeo/geocaching/filter/SizeFilter.java index f02874c..4c7c122 100644 --- a/main/src/cgeo/geocaching/filter/SizeFilter.java +++ b/main/src/cgeo/geocaching/filter/SizeFilter.java @@ -3,6 +3,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; import cgeo.geocaching.enumerations.CacheSize; +import org.eclipse.jdt.annotation.NonNull; + import java.util.LinkedList; import java.util.List; @@ -15,7 +17,7 @@ class SizeFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cacheSize == cache.getSize(); } @@ -27,10 +29,11 @@ class SizeFilter extends AbstractFilter { public static class Factory implements IFilterFactory { @Override + @NonNull public List<IFilter> getFilters() { final CacheSize[] cacheSizes = CacheSize.values(); final List<IFilter> filters = new LinkedList<>(); - for (CacheSize cacheSize : cacheSizes) { + for (final CacheSize cacheSize : cacheSizes) { if (cacheSize != CacheSize.UNKNOWN) { filters.add(new SizeFilter(cacheSize)); } diff --git a/main/src/cgeo/geocaching/filter/StateFilter.java b/main/src/cgeo/geocaching/filter/StateFilter.java index ebe133c..f574045 100644 --- a/main/src/cgeo/geocaching/filter/StateFilter.java +++ b/main/src/cgeo/geocaching/filter/StateFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import android.content.res.Resources; import java.util.ArrayList; @@ -26,7 +28,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isFound(); } @@ -39,7 +41,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return !cache.isFound(); } @@ -51,7 +53,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isArchived(); } } @@ -62,7 +64,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isDisabled(); } } @@ -73,7 +75,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isPremiumMembersOnly(); } } @@ -84,7 +86,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return !cache.isPremiumMembersOnly(); } } @@ -95,7 +97,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isLogOffline(); } } @@ -106,7 +108,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.isOffline(); } } @@ -117,7 +119,7 @@ abstract class StateFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return !cache.isOffline(); } } @@ -125,6 +127,7 @@ abstract class StateFilter extends AbstractFilter { public static class Factory implements IFilterFactory { @Override + @NonNull public List<StateFilter> getFilters() { final List<StateFilter> filters = new ArrayList<>(6); filters.add(new StateFoundFilter()); diff --git a/main/src/cgeo/geocaching/filter/TerrainFilter.java b/main/src/cgeo/geocaching/filter/TerrainFilter.java index 7da6a19..977e4a5 100644 --- a/main/src/cgeo/geocaching/filter/TerrainFilter.java +++ b/main/src/cgeo/geocaching/filter/TerrainFilter.java @@ -3,6 +3,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.ArrayList; import java.util.List; @@ -13,7 +15,7 @@ class TerrainFilter extends AbstractRangeFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { final float terrain = cache.getTerrain(); return rangeMin <= terrain && terrain < rangeMax; } @@ -23,6 +25,7 @@ class TerrainFilter extends AbstractRangeFilter { private static final int TERRAIN_MAX = 7; @Override + @NonNull public List<IFilter> getFilters() { final ArrayList<IFilter> filters = new ArrayList<>(TERRAIN_MAX); for (int terrain = TERRAIN_MIN; terrain <= TERRAIN_MAX; terrain++) { diff --git a/main/src/cgeo/geocaching/filter/TrackablesFilter.java b/main/src/cgeo/geocaching/filter/TrackablesFilter.java index d836a0f..7ad06a1 100644 --- a/main/src/cgeo/geocaching/filter/TrackablesFilter.java +++ b/main/src/cgeo/geocaching/filter/TrackablesFilter.java @@ -4,6 +4,8 @@ import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; +import org.eclipse.jdt.annotation.NonNull; + import java.util.Collections; import java.util.List; @@ -13,11 +15,12 @@ class TrackablesFilter extends AbstractFilter implements IFilterFactory { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cache.hasTrackables(); } @Override + @NonNull public List<TrackablesFilter> getFilters() { return Collections.singletonList(this); } diff --git a/main/src/cgeo/geocaching/filter/TypeFilter.java b/main/src/cgeo/geocaching/filter/TypeFilter.java index d363d39..412cbc2 100644 --- a/main/src/cgeo/geocaching/filter/TypeFilter.java +++ b/main/src/cgeo/geocaching/filter/TypeFilter.java @@ -3,6 +3,8 @@ package cgeo.geocaching.filter; import cgeo.geocaching.Geocache; import cgeo.geocaching.enumerations.CacheType; +import org.eclipse.jdt.annotation.NonNull; + import java.util.LinkedList; import java.util.List; @@ -15,7 +17,7 @@ class TypeFilter extends AbstractFilter { } @Override - public boolean accepts(final Geocache cache) { + public boolean accepts(@NonNull final Geocache cache) { return cacheType == cache.getType(); } @@ -27,10 +29,11 @@ class TypeFilter extends AbstractFilter { public static class Factory implements IFilterFactory { @Override + @NonNull public List<IFilter> getFilters() { final CacheType[] types = CacheType.values(); final List<IFilter> filters = new LinkedList<>(); - for (CacheType cacheType : types) { + for (final CacheType cacheType : types) { if (cacheType != CacheType.ALL) { filters.add(new TypeFilter(cacheType)); } diff --git a/main/src/cgeo/geocaching/gcvote/GCVote.java b/main/src/cgeo/geocaching/gcvote/GCVote.java index eaf7687..2985e89 100644 --- a/main/src/cgeo/geocaching/gcvote/GCVote.java +++ b/main/src/cgeo/geocaching/gcvote/GCVote.java @@ -131,8 +131,11 @@ public final class GCVote { * @return {@code true} if the rating was submitted successfully */ public static boolean setRating(final Geocache cache, final float rating) { - if (!isVotingPossible(cache) || !isValidRating(rating)) { - throw new IllegalArgumentException(!isVotingPossible(cache) ? "voting is not possible for " + cache : "invalid rating " + rating); + if (!isVotingPossible(cache)) { + throw new IllegalArgumentException("voting is not possible for " + cache); + } + if (!isValidRating(rating)) { + throw new IllegalArgumentException("invalid rating " + rating); } final ImmutablePair<String, String> login = Settings.getGCvoteLogin(); @@ -199,7 +202,7 @@ public final class GCVote { return rating >= MIN_RATING && rating <= MAX_RATING; } - public static boolean isVotingPossible(final Geocache cache) { + public static boolean isVotingPossible(@NonNull final Geocache cache) { return Settings.isGCvoteLogin() && StringUtils.isNotBlank(cache.getGuid()) && cache.supportsGCVote(); } diff --git a/main/src/cgeo/geocaching/gcvote/GCVoteDialog.java b/main/src/cgeo/geocaching/gcvote/GCVoteDialog.java new file mode 100644 index 0000000..da14e0b --- /dev/null +++ b/main/src/cgeo/geocaching/gcvote/GCVoteDialog.java @@ -0,0 +1,74 @@ +package cgeo.geocaching.gcvote; + +import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; +import cgeo.geocaching.gcvote.GCVoteRatingBarUtil.OnRatingChangeListener; +import cgeo.geocaching.settings.Settings; + +import org.eclipse.jdt.annotation.Nullable; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.view.ContextThemeWrapper; +import android.view.View; +import android.widget.Button; + +/** + * Small dialog showing only a rating bar to vote on GCVote.com. Confirming the dialog will send the vote over the + * network (in the background). + */ +public class GCVoteDialog { + + public static void show(final Activity context, final Geocache cache, final @Nullable Runnable afterVoteSent) { + final Context themedContext; + + if (Settings.isLightSkin() && VERSION.SDK_INT < VERSION_CODES.HONEYCOMB) { + themedContext = new ContextThemeWrapper(context, R.style.dark); + } else { + themedContext = context; + } + + final View votingLayout = View.inflate(themedContext, R.layout.gcvote_dialog, null); + + final AlertDialog.Builder builder = new AlertDialog.Builder(themedContext); + builder.setView(votingLayout); + builder.setPositiveButton(R.string.cache_menu_vote, new OnClickListener() { + + @Override + public void onClick(final DialogInterface dialog, final int which) { + vote(cache, GCVoteRatingBarUtil.getRating(votingLayout), afterVoteSent); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int whichButton) { + dialog.dismiss(); + } + }); + final AlertDialog dialog = builder.create(); + + GCVoteRatingBarUtil.initializeRatingBar(cache, votingLayout, new OnRatingChangeListener() { + + @Override + public void onRatingChanged(final float stars) { + final Button button = dialog.getButton(DialogInterface.BUTTON_POSITIVE); + // this listener might be fired already while the dialog is not yet shown + if (button != null) { + button.setEnabled(GCVote.isValidRating(stars)); + } + } + }); + dialog.show(); + dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(GCVote.isValidRating(cache.getMyVote())); + } + + protected static void vote(final Geocache cache, final float rating, final @Nullable Runnable afterVoteSent) { + new GCVotePoster(cache, rating, afterVoteSent).execute(); + } + +} diff --git a/main/src/cgeo/geocaching/gcvote/GCVotePoster.java b/main/src/cgeo/geocaching/gcvote/GCVotePoster.java new file mode 100644 index 0000000..924aa56 --- /dev/null +++ b/main/src/cgeo/geocaching/gcvote/GCVotePoster.java @@ -0,0 +1,53 @@ +package cgeo.geocaching.gcvote; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.DataStore; +import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; +import cgeo.geocaching.utils.Log; + +import org.eclipse.jdt.annotation.Nullable; + +import android.os.AsyncTask; +import android.widget.Toast; + +class GCVotePoster extends AsyncTask<Void, Void, Boolean> { + + private final Geocache cache; + private final float rating; + private final @Nullable Runnable afterVoteSent; + + public GCVotePoster(final Geocache cache, final float rating, final @Nullable Runnable afterVoteSent) { + this.cache = cache; + this.rating = rating; + this.afterVoteSent = afterVoteSent; + } + + @Override + protected Boolean doInBackground(final Void... inputs) { + try { + if (GCVote.isValidRating(rating) && GCVote.isVotingPossible(cache)) { + // store locally + cache.setMyVote(rating); + DataStore.saveChangedCache(cache); + + // send over network + return GCVote.setRating(cache, rating); + } + } catch (final RuntimeException e) { + Log.e("GCVoteAsyncTask.doInBackground", e); + } + + return false; + } + + @Override + protected void onPostExecute(final Boolean status) { + final CgeoApplication context = CgeoApplication.getInstance(); + final String text = context.getString(status ? R.string.gcvote_sent : R.string.err_gcvote_send_rating); + Toast.makeText(context, text, Toast.LENGTH_SHORT).show(); + if (afterVoteSent != null) { + afterVoteSent.run(); + } + } +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/gcvote/GCVoteRatingBarUtil.java b/main/src/cgeo/geocaching/gcvote/GCVoteRatingBarUtil.java new file mode 100644 index 0000000..2d485bd --- /dev/null +++ b/main/src/cgeo/geocaching/gcvote/GCVoteRatingBarUtil.java @@ -0,0 +1,58 @@ +package cgeo.geocaching.gcvote; + +import butterknife.ButterKnife; + +import cgeo.geocaching.Geocache; +import cgeo.geocaching.R; + +import org.eclipse.jdt.annotation.Nullable; + +import android.view.View; +import android.widget.RatingBar; +import android.widget.RatingBar.OnRatingBarChangeListener; +import android.widget.TextView; + +/** + * TODO: convert to fragment + * + */ +public final class GCVoteRatingBarUtil { + public interface OnRatingChangeListener { + public void onRatingChanged(final float stars); + } + + private GCVoteRatingBarUtil() { + // utility class + } + + public static void initializeRatingBar(final Geocache cache, final View parentView, @Nullable final OnRatingChangeListener changeListener) { + if (GCVote.isVotingPossible(cache)) { + final RatingBar ratingBar = ButterKnife.findById(parentView, R.id.gcvoteRating); + final TextView label = ButterKnife.findById(parentView, R.id.gcvoteLabel); + ratingBar.setVisibility(View.VISIBLE); + label.setVisibility(View.VISIBLE); + ratingBar.setOnRatingBarChangeListener(new OnRatingBarChangeListener() { + + @Override + public void onRatingChanged(final RatingBar ratingBar, final float stars, final boolean fromUser) { + // 0.5 is not a valid rating, therefore we must limit + final float rating = GCVote.isValidRating(stars) ? stars : 0; + if (rating < stars) { + ratingBar.setRating(rating); + } + label.setText(GCVote.getDescription(rating)); + if (changeListener != null) { + changeListener.onRatingChanged(rating); + } + } + }); + ratingBar.setRating(cache.getMyVote()); + } + } + + public static float getRating(final View parentView) { + final RatingBar ratingBar = ButterKnife.findById(parentView, R.id.gcvoteRating); + return ratingBar.getRating(); + } + +} diff --git a/main/src/cgeo/geocaching/list/StoredList.java b/main/src/cgeo/geocaching/list/StoredList.java index 4291a0a..0ffd58a 100644 --- a/main/src/cgeo/geocaching/list/StoredList.java +++ b/main/src/cgeo/geocaching/list/StoredList.java @@ -106,11 +106,8 @@ public final class StoredList extends AbstractList { final List<AbstractList> lists = new ArrayList<>(); lists.addAll(getSortedLists()); - if (exceptListId > StoredList.TEMPORARY_LIST.id) { - final StoredList exceptList = DataStore.getList(exceptListId); - if (exceptList != null) { - lists.remove(exceptList); - } + if (exceptListId == StoredList.STANDARD_LIST_ID || exceptListId >= DataStore.customListIdOffset) { + lists.remove(DataStore.getList(exceptListId)); } if (!onlyConcreteLists) { diff --git a/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java b/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java deleted file mode 100644 index e1573c9..0000000 --- a/main/src/cgeo/geocaching/loaders/AddressGeocacheListLoader.java +++ /dev/null @@ -1,23 +0,0 @@ -package cgeo.geocaching.loaders; - -import cgeo.geocaching.SearchResult; -import cgeo.geocaching.connector.gc.GCParser; -import cgeo.geocaching.settings.Settings; - -import android.content.Context; - -public class AddressGeocacheListLoader extends AbstractSearchLoader { - - private final String address; - - public AddressGeocacheListLoader(Context context, String address) { - super(context); - this.address = address; - } - - @Override - public SearchResult runSearch() { - return GCParser.searchByAddress(address, Settings.getCacheType(), Settings.isShowCaptcha(), this); - } - -} diff --git a/main/src/cgeo/geocaching/location/AndroidGeocoder.java b/main/src/cgeo/geocaching/location/AndroidGeocoder.java new file mode 100644 index 0000000..98ea285 --- /dev/null +++ b/main/src/cgeo/geocaching/location/AndroidGeocoder.java @@ -0,0 +1,59 @@ +package cgeo.geocaching.location; + +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RxUtils; + +import org.apache.commons.collections4.CollectionUtils; +import org.eclipse.jdt.annotation.NonNull; + +import rx.Observable; +import rx.functions.Func0; + +import android.content.Context; +import android.location.Address; +import android.location.Geocoder; + +import java.util.List; +import java.util.Locale; + +/** + * Encapsulation of the Android {@link Geocoder} with default error handling. All methods of this class + * are blocking and will do network lookups. + * + */ +public class AndroidGeocoder { + private final Geocoder geocoder; + + public AndroidGeocoder(final Context context) { + geocoder = new Geocoder(context, Locale.getDefault()); + } + + /** + * Retrieve addresses from a textual location using Android geocoding API. The work happens on the network + * scheduler. + * + * @param keyword + * the location + * @return an observable containing zero or more locations + * + * @see Geocoder#getFromLocationName(String, int) + */ + public Observable<Address> getFromLocationName(@NonNull final String keyword) { + return Observable.defer(new Func0<Observable<Address>>() { + @Override + public Observable<Address> call() { + try { + final List<Address> addresses = geocoder.getFromLocationName(keyword, 20); + if (CollectionUtils.isEmpty(addresses)) { + return Observable.error(new RuntimeException("no result from Android geocoder")); + } + return Observable.from(addresses); + } catch (final Exception e) { + Log.i("Unable to use Android geocoder: " + e.getMessage()); + return Observable.error(e); + } + } + }).subscribeOn(RxUtils.networkScheduler); + } + +} diff --git a/main/src/cgeo/geocaching/location/GCGeocoder.java b/main/src/cgeo/geocaching/location/GCGeocoder.java new file mode 100644 index 0000000..549044f --- /dev/null +++ b/main/src/cgeo/geocaching/location/GCGeocoder.java @@ -0,0 +1,65 @@ +package cgeo.geocaching.location; + +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RxUtils; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; + +import rx.Observable; +import rx.functions.Func0; + +import android.location.Address; + +import java.util.Locale; + +public class GCGeocoder { + + private GCGeocoder() { + // Do not instantiate + } + + /** + * Retrieve addresses from a textual location using geocaching.com geocoding API. The work happens on the network + * scheduler. + * + * @param address + * the location + * @return an observable containing zero or more locations + * + * @see android.location.Geocoder#getFromLocationName(String, int) + */ + public static Observable<Address> getFromLocationName(@NonNull final String address) { + return Observable.defer(new Func0<Observable<Address>>() { + @Override + public Observable<Address> call() { + if (!Settings.isGCConnectorActive()) { + return Observable.error(new RuntimeException("geocaching.com connector is not active")); + } + final ObjectNode response = Network.requestJSON("https://www.geocaching.com/api/geocode", new Parameters("q", address)); + if (response == null || !StringUtils.equalsIgnoreCase(response.path("status").asText(), "success")) { + return Observable.error(new RuntimeException("unable to use geocaching.com geocoder")); + } + + final JsonNode data = response.path("data"); + final Address geocodedAddress = new Address(Locale.getDefault()); + try { + geocodedAddress.setLatitude(data.get("lat").asDouble()); + geocodedAddress.setLongitude(data.get("lng").asDouble()); + geocodedAddress.setAddressLine(0, address); + return Observable.just(geocodedAddress); + } catch (final Exception e) { + Log.e("unable to decode answer from geocaching.com geocoder", e); + return Observable.error(e); + } + } + }).subscribeOn(RxUtils.networkScheduler); + } + +} diff --git a/main/src/cgeo/geocaching/location/Geocoder.java b/main/src/cgeo/geocaching/location/Geocoder.java deleted file mode 100644 index 1582daa..0000000 --- a/main/src/cgeo/geocaching/location/Geocoder.java +++ /dev/null @@ -1,61 +0,0 @@ -package cgeo.geocaching.location; - -import cgeo.geocaching.utils.Log; - -import org.apache.commons.lang3.StringUtils; -import org.eclipse.jdt.annotation.NonNull; - -import android.content.Context; -import android.location.Address; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Locale; - -/** - * Encapsulation of the Android {@link android.location.Geocoder} with default error handling. All methods of this class - * are blocking and will do network lookups. - * - */ -public class Geocoder { - private final android.location.Geocoder geocoder; - - public Geocoder(final Context context) { - geocoder = new android.location.Geocoder(context, Locale.getDefault()); - } - - /** - * @param keyword - * @return - * - * @see android.location.Geocoder#getFromLocationName(String, int) - */ - public @NonNull List<Address> getFromLocationName(final String keyword) { - try { - return geocoder.getFromLocationName(keyword, 20); - } catch (final Exception e) { - handleException(e); - return Collections.emptyList(); - } - } - - public @NonNull List<Address> getFromLocation(final Geopoint coords) { - try { - return geocoder.getFromLocation(coords.getLatitude(), coords.getLongitude(), 20); - } catch (final IOException e) { - handleException(e); - return Collections.emptyList(); - } - } - - private static void handleException(final Exception e) { - // non Google devices come without the geocoder - if (StringUtils.containsIgnoreCase(e.getMessage(), "Service not Available")) { - Log.i("No geocoder available"); - } - else { - Log.e("Geocoder", e); - } - } -} diff --git a/main/src/cgeo/geocaching/location/GeopointParser.java b/main/src/cgeo/geocaching/location/GeopointParser.java index e73e787..a6b8e45 100644 --- a/main/src/cgeo/geocaching/location/GeopointParser.java +++ b/main/src/cgeo/geocaching/location/GeopointParser.java @@ -122,7 +122,7 @@ class GeopointParser { // Nothing found with "N 52...", try to match string as decimal degree parts (i.e. multiple doubles) try { - final String[] items = StringUtils.split(text.trim()); + final String[] items = StringUtils.split(StringUtils.trimToEmpty(text)); if (items.length > 0 && items.length <= 2) { final int index = (latlon == LatLon.LON ? items.length - 1 : 0); final String textPart = items[index]; diff --git a/main/src/cgeo/geocaching/location/MapQuestGeocoder.java b/main/src/cgeo/geocaching/location/MapQuestGeocoder.java new file mode 100644 index 0000000..537ae40 --- /dev/null +++ b/main/src/cgeo/geocaching/location/MapQuestGeocoder.java @@ -0,0 +1,117 @@ +package cgeo.geocaching.location; + +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RxUtils; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; + +import rx.Observable; +import rx.Observable.OnSubscribe; +import rx.Subscriber; +import rx.functions.Func0; + +import android.location.Address; + +import java.util.Locale; + +public class MapQuestGeocoder { + + private static final String MAPQUEST_KEY = "Fmjtd|luurn1u2n9,bs=o5-9wynua"; + + private MapQuestGeocoder() { + // Do not instantiate + } + + /** + * Retrieve addresses from a textual location using MapQuest geocoding API. The work happens on the network + * scheduler. + * + * @param address + * the location + * @return an observable containing zero or more locations + * + * @see android.location.Geocoder#getFromLocationName(String, int) + */ + public static Observable<Address> getFromLocationName(@NonNull final String address) { + return Observable.defer(new Func0<Observable<Address>>() { + @Override + public Observable<Address> call() { + final ObjectNode response = Network.requestJSON("https://www.mapquestapi.com/geocoding/v1/address", + new Parameters("key", MAPQUEST_KEY, "location", address, "maxResults", "20", "thumbMaps", "false")); + if (response == null) { + Log.w("MapQuest decoder error: no response"); + return Observable.error(new RuntimeException("no answer from MapQuest geocoder")); + } + final int statusCode = response.path("info").path("statuscode").asInt(-1); + if (statusCode != 0) { + Log.w("MapQuest decoder error: statuscode is not 0"); + return Observable.error(new RuntimeException("no correct answer from MapQuest geocoder")); + } + return Observable.create(new OnSubscribe<Address>() { + @Override + public void call(final Subscriber<? super Address> subscriber) { + try { + for (final JsonNode address: response.get("results").get(0).get("locations")) { + subscriber.onNext(mapquestToAddress(address)); + } + subscriber.onCompleted(); + } catch (final Exception e) { + Log.e("Error decoding MapQuest address", e); + subscriber.onError(e); + } + } + }); + } + }).subscribeOn(RxUtils.networkScheduler); + } + + private static Address mapquestToAddress(final JsonNode mapquestAddress) { + final Address address = new Address(Locale.getDefault()); + for (int i = 1; i <= 6; i++) { + final String adminAreaName = "adminArea" + i; + setComponent(address, mapquestAddress, adminAreaName, mapquestAddress.path(adminAreaName + "Type").asText()); + } + setComponent(address, mapquestAddress, "postalCode", "PostalCode"); + int index = 0; + for (final String addressComponent: new String[]{ mapquestAddress.path("street").asText(), address.getSubLocality(), address.getLocality(), + address.getPostalCode(), address.getSubAdminArea(), address.getAdminArea(), address.getCountryCode() }) { + if (StringUtils.isNotBlank(addressComponent)) { + address.setAddressLine(index++, addressComponent); + } + } + address.setLatitude(mapquestAddress.get("latLng").get("lat").asDouble()); + address.setLongitude(mapquestAddress.get("latLng").get("lng").asDouble()); + return address; + } + + private static void setComponent(final Address address, final JsonNode mapquestAddress, final String adminArea, final String adminAreaType) { + final String content = StringUtils.trimToNull(mapquestAddress.path(adminArea).asText()); + switch (adminAreaType) { + case "City": + address.setLocality(content); + break; + case "Neighborhood": + address.setSubLocality(content); + break; + case "PostalCode": + address.setPostalCode(content); + break; + case "State": + address.setAdminArea(content); + break; + case "County": + address.setSubAdminArea(content); + break; + case "Country": + address.setCountryCode(content); + break; + } + } + +} diff --git a/main/src/cgeo/geocaching/location/Viewport.java b/main/src/cgeo/geocaching/location/Viewport.java index e482828..b885336 100644 --- a/main/src/cgeo/geocaching/location/Viewport.java +++ b/main/src/cgeo/geocaching/location/Viewport.java @@ -87,7 +87,7 @@ public final class Viewport { */ public int count(final @NonNull Collection<? extends ICoordinates> points) { int total = 0; - for (ICoordinates point: points) { + for (final ICoordinates point: points) { if (point != null && contains(point)) { total += 1; } @@ -102,7 +102,7 @@ public final class Viewport { /** * Check whether another viewport is fully included into the current one. - * + * * @param vp * the other viewport * @return true if the viewport is fully included into this one, false otherwise @@ -118,6 +118,7 @@ public final class Viewport { * the database table to use as prefix, or null if no prefix is required * @return the string without the "where" keyword */ + @NonNull public StringBuilder sqlWhere(@Nullable final String dbTable) { final String prefix = dbTable == null ? "" : (dbTable + "."); return new StringBuilder(prefix).append("latitude >= ").append(getLatitudeMin()).append(" and ") diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index 2868679..f2a5146 100644 --- a/main/src/cgeo/geocaching/maps/CGeoMap.java +++ b/main/src/cgeo/geocaching/maps/CGeoMap.java @@ -34,6 +34,7 @@ import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OnMapDragListener; import cgeo.geocaching.sensors.GeoData; import cgeo.geocaching.sensors.GeoDirHandler; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.ui.dialog.LiveMapInfoDialogBuilder; import cgeo.geocaching.utils.AngleUtils; @@ -133,7 +134,6 @@ public class CGeoMap extends AbstractMap implements ViewFactory { // Those are initialized in onCreate() and will never be null afterwards private Resources res; private Activity activity; - private CgeoApplication app; private MapItemFactory mapItemFactory; private String mapTitle; final private LeastRecentlyUsedSet<Geocache> caches = new LeastRecentlyUsedSet<>(MAX_CACHES + DataStore.getAllCachesCount()); @@ -382,7 +382,6 @@ public class CGeoMap extends AbstractMap implements ViewFactory { // class init res = this.getResources(); activity = this.getActivity(); - app = (CgeoApplication) activity.getApplication(); final MapProvider mapProvider = Settings.getMapProvider(); mapItemFactory = mapProvider.getMapItemFactory(); @@ -491,7 +490,7 @@ public class CGeoMap extends AbstractMap implements ViewFactory { }); } - if (!app.isLiveMapHintShownInThisSession() && Settings.getLiveMapHintShowCount() <= 3) { + if (!CgeoApplication.getInstance().isLiveMapHintShownInThisSession() && Settings.getLiveMapHintShowCount() <= 3) { LiveMapInfoDialogBuilder.create(activity).show(); } } @@ -955,7 +954,7 @@ public class CGeoMap extends AbstractMap implements ViewFactory { // minimum change of location in fraction of map width/height (whatever is smaller) for position overlay update private static final float MIN_LOCATION_DELTA = 0.01f; - Location currentLocation = CgeoApplication.getInstance().currentGeo(); + Location currentLocation = Sensors.getInstance().currentGeo(); float currentHeading; private long timeLastPositionOverlayCalculation = 0; @@ -1539,7 +1538,7 @@ public class CGeoMap extends AbstractMap implements ViewFactory { if (myLocSwitch != null) { myLocSwitch.setChecked(followMyLocation); if (followMyLocation) { - myLocationInMiddle(app.currentGeo()); + myLocationInMiddle(Sensors.getInstance().currentGeo()); } } } diff --git a/main/src/cgeo/geocaching/network/HtmlImage.java b/main/src/cgeo/geocaching/network/HtmlImage.java index d1d458d..97e0e45 100644 --- a/main/src/cgeo/geocaching/network/HtmlImage.java +++ b/main/src/cgeo/geocaching/network/HtmlImage.java @@ -44,7 +44,6 @@ import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.util.Date; /** * All-purpose image getter that can also be used as a ImageGetter interface when displaying caches. @@ -70,7 +69,7 @@ public class HtmlImage implements Html.ImageGetter { }; public static final String SHARED = "shared"; - final private String geocode; + @NonNull final private String geocode; /** * on error: return large error image, if {@code true}, otherwise empty 1x1 image */ @@ -91,34 +90,45 @@ public class HtmlImage implements Html.ImageGetter { // Background loading final private PublishSubject<Observable<String>> loading = PublishSubject.create(); - final private Observable<String> waitForEnd = Observable.merge(loading).publish().refCount(); + final private Observable<String> waitForEnd = Observable.merge(loading).cache(); final private CompositeSubscription subscription = new CompositeSubscription(waitForEnd.subscribe()); /** - * Create a new HtmlImage object with different behaviours depending on <tt>onlySave</tt> and <tt>view</tt> values. + * Create a new HtmlImage object with different behaviors depending on <tt>onlySave</tt> and <tt>view</tt> values. * There are the three possible use cases: * <ul> - * <li>If onlySave is true, getDrawable() will return null immediately and will queue the image retrieval - * and saving in the loading subject. Downloads will start in parallel when the blocking - * waitForBackgroundLoading() method is called, and they can be cancelled through the given handler.</li> - * <li>If onlySave is false and the instance is called through fetchDrawable(), then an observable for the - * given URL will be returned. This observable will emit the local copy of the image if it is present</li> - * regardless of its freshness, then if needed an updated fresher copy after retrieving it from the network. - * <li>If onlySave is false and the instance is used as an ImageGetter, only the final version of the - * image will be returned, unless a view has been provided. If it has, then a dummy drawable is returned - * and is updated when the image is available, possibly several times if we had a stale copy of the image - * and then got a new one from the network.</li> + * <li>If onlySave is true, {@link #getDrawable(String)} will return <tt>null</tt> immediately and will queue the + * image retrieval and saving in the loading subject. Downloads will start in parallel when the blocking + * {@link #waitForEndObservable(cgeo.geocaching.utils.CancellableHandler)} method is called, and they can be + * cancelled through the given handler.</li> + * <li>If <tt>onlySave</tt> is <tt>false</tt> and the instance is called through {@link #fetchDrawable(String)}, + * then an observable for the given URL will be returned. This observable will emit the local copy of the image if + * it is present regardless of its freshness, then if needed an updated fresher copy after retrieving it from the + * network.</li> + * <li>If <tt>onlySave</tt> is <tt>false</tt> and the instance is used as an {@link android.text.Html.ImageGetter}, + * only the final version of the image will be returned, unless a view has been provided. If it has, then a dummy + * drawable is returned and is updated when the image is available, possibly several times if we had a stale copy of + * the image and then got a new one from the network.</li> * </ul> * - * @param geocode the geocode of the item for which we are requesting the image - * @param returnErrorImage set to <tt>true</tt> if an error image should be returned in case of a problem, - * <tt>false</tt> to get a transparent 1x1 image instead - * @param listId the list this cache belongs to, used to determine if an older image for the offline case can be used or not - * @param onlySave if set to <tt>true</tt>, {@link #getDrawable(String)} will only fetch and store the image, not return it - * @param view if non-null, {@link #getDrawable(String)} will return an initially empty drawable which will be redrawn when - * the image is ready through an invalidation of the given view + * @param geocode + * the geocode of the item for which we are requesting the image, or {@link #SHARED} to use the shared + * cache directory + * @param returnErrorImage + * set to <tt>true</tt> if an error image should be returned in case of a problem, <tt>false</tt> to get + * a transparent 1x1 image instead + * @param listId + * the list this cache belongs to, used to determine if an older image for the offline case can be used + * or not + * @param onlySave + * if set to <tt>true</tt>, {@link #getDrawable(String)} will only fetch and store the image, not return + * it + * @param view + * if non-null, {@link #getDrawable(String)} will return an initially empty drawable which will be + * redrawn when + * the image is ready through an invalidation of the given view */ - public HtmlImage(final String geocode, final boolean returnErrorImage, final int listId, final boolean onlySave, final TextView view) { + public HtmlImage(@NonNull final String geocode, final boolean returnErrorImage, final int listId, final boolean onlySave, final TextView view) { this.geocode = geocode; this.returnErrorImage = returnErrorImage; this.listId = listId; @@ -132,12 +142,12 @@ public class HtmlImage implements Html.ImageGetter { } /** - * Create a new HtmlImage object with different behaviours depending on <tt>onlySave</tt> value. No view object + * Create a new HtmlImage object with different behaviors depending on <tt>onlySave</tt> value. No view object * will be tied to this HtmlImage. * * For documentation, see {@link #HtmlImage(String, boolean, int, boolean, TextView)}. */ - public HtmlImage(final String geocode, final boolean returnErrorImage, final int listId, final boolean onlySave) { + public HtmlImage(@NonNull final String geocode, final boolean returnErrorImage, final int listId, final boolean onlySave) { this(geocode, returnErrorImage, listId, onlySave, null); } @@ -243,23 +253,23 @@ public class HtmlImage implements Html.ImageGetter { } if (onlySave) { subscriber.onCompleted(); - } else { - RxUtils.computationScheduler.createWorker().schedule(new Action0() { - @Override - public void call() { - final ImmutablePair<BitmapDrawable, Boolean> loaded = loadFromDisk(); - final BitmapDrawable image = loaded.left; - if (image != null) { - subscriber.onNext(image); - } else { - subscriber.onNext(returnErrorImage ? - new BitmapDrawable(resources, BitmapFactory.decodeResource(resources, R.drawable.image_not_loaded)) : - ImageUtils.getTransparent1x1Drawable(resources)); - } - subscriber.onCompleted(); - } - }); + return; } + RxUtils.computationScheduler.createWorker().schedule(new Action0() { + @Override + public void call() { + final ImmutablePair<BitmapDrawable, Boolean> loaded = loadFromDisk(); + final BitmapDrawable image = loaded.left; + if (image != null) { + subscriber.onNext(image); + } else { + subscriber.onNext(returnErrorImage ? + new BitmapDrawable(resources, BitmapFactory.decodeResource(resources, R.drawable.image_not_loaded)) : + ImageUtils.getTransparent1x1Drawable(resources)); + } + subscriber.onCompleted(); + } + }); } }); } @@ -338,7 +348,7 @@ public class HtmlImage implements Html.ImageGetter { * @return A pair whose first element is the bitmap if available, and the second one is <code>true</code> if the image is present and fresh enough. */ @NonNull - private ImmutablePair<Bitmap, Boolean> loadImageFromStorage(final String url, final String pseudoGeocode, final boolean forceKeep) { + private ImmutablePair<Bitmap, Boolean> loadImageFromStorage(final String url, @NonNull final String pseudoGeocode, final boolean forceKeep) { try { final File file = LocalStorage.getStorageFile(pseudoGeocode, url, true, false); final ImmutablePair<Bitmap, Boolean> image = loadCachedImage(file, forceKeep); @@ -390,7 +400,7 @@ public class HtmlImage implements Html.ImageGetter { @NonNull private ImmutablePair<Bitmap, Boolean> loadCachedImage(final File file, final boolean forceKeep) { if (file.exists()) { - final boolean freshEnough = listId >= StoredList.STANDARD_LIST_ID || file.lastModified() > (new Date().getTime() - (24 * 60 * 60 * 1000)) || forceKeep; + final boolean freshEnough = listId >= StoredList.STANDARD_LIST_ID || file.lastModified() > (System.currentTimeMillis() - (24 * 60 * 60 * 1000)) || forceKeep; if (freshEnough && onlySave) { return ImmutablePair.of((Bitmap) null, true); } diff --git a/main/src/cgeo/geocaching/network/OAuth.java b/main/src/cgeo/geocaching/network/OAuth.java index c23ffbf..4f1fcc0 100644 --- a/main/src/cgeo/geocaching/network/OAuth.java +++ b/main/src/cgeo/geocaching/network/OAuth.java @@ -8,7 +8,6 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.jdt.annotation.NonNull; import java.util.ArrayList; -import java.util.Date; import java.util.List; public class OAuth { @@ -24,7 +23,7 @@ public class OAuth { "oauth_consumer_key", consumerKey, "oauth_nonce", CryptUtils.md5(Long.toString(System.currentTimeMillis())), "oauth_signature_method", "HMAC-SHA1", - "oauth_timestamp", Long.toString(new Date().getTime() / 1000), + "oauth_timestamp", Long.toString(System.currentTimeMillis() / 1000), "oauth_token", StringUtils.defaultString(tokens.getTokenPublic()), "oauth_version", "1.0"); params.sort(); @@ -41,10 +40,7 @@ public class OAuth { } /** - * percent encode following http://tools.ietf.org/html/rfc5849#section-3.6 - * - * @param url - * @return + * Percent encode following http://tools.ietf.org/html/rfc5849#section-3.6 */ static String percentEncode(@NonNull final String url) { return StringUtils.replace(Network.rfc3986URLEncode(url), "*", "%2A"); diff --git a/main/src/cgeo/geocaching/sensors/GeoData.java b/main/src/cgeo/geocaching/sensors/GeoData.java index 880efd0..b8b16fd 100644 --- a/main/src/cgeo/geocaching/sensors/GeoData.java +++ b/main/src/cgeo/geocaching/sensors/GeoData.java @@ -91,7 +91,7 @@ public class GeoData extends Location { final String homeLocationStr = Settings.getHomeLocation(); if (StringUtils.isNotBlank(homeLocationStr)) { try { - assert (homeLocationStr != null); + assert homeLocationStr != null; final Geopoint homeLocation = new Geopoint(homeLocationStr); Log.i("No last known location available, using home location"); final Location initialLocation = new Location(HOME_PROVIDER); diff --git a/main/src/cgeo/geocaching/sensors/GeoDirHandler.java b/main/src/cgeo/geocaching/sensors/GeoDirHandler.java index d043a4a..4743140 100644 --- a/main/src/cgeo/geocaching/sensors/GeoDirHandler.java +++ b/main/src/cgeo/geocaching/sensors/GeoDirHandler.java @@ -1,16 +1,11 @@ package cgeo.geocaching.sensors; -import cgeo.geocaching.CgeoApplication; -import cgeo.geocaching.settings.Settings; -import cgeo.geocaching.utils.AngleUtils; - import org.apache.commons.lang3.tuple.ImmutablePair; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Action1; -import rx.functions.Func1; import rx.functions.Func2; import rx.subscriptions.CompositeSubscription; @@ -34,8 +29,6 @@ public abstract class GeoDirHandler { public static final int UPDATE_GEODIR = 1 << 3; public static final int LOW_POWER = 1 << 4; - private static final CgeoApplication app = CgeoApplication.getInstance(); - /** * Update method called when new geodata is available. This method is called on the UI thread. * {@link #start(int)} must be called with the {@link #UPDATE_GEODATA} flag set. @@ -67,22 +60,6 @@ public abstract class GeoDirHandler { public void updateGeoDir(final GeoData geoData, final float direction) { } - private static Observable<Float> fixedDirection() { - return app.directionObservable().map(new Func1<Float, Float>() { - @Override - public Float call(final Float direction) { - final GeoData geoData = app.currentGeo(); - return fixDirection(geoData, direction); - } - }); - - } - - private static float fixDirection(final GeoData geoData, final float direction) { - final boolean useGPSBearing = !Settings.isUseCompass() || geoData.getSpeed() > 5; - return useGPSBearing ? AngleUtils.reverseDirectionNow(geoData.getBearing()) : direction; - } - private static <T> Observable<T> throttleIfNeeded(final Observable<T> observable, final long windowDuration, final TimeUnit unit) { return windowDuration > 0 ? observable.throttleFirst(windowDuration, unit) : observable; } @@ -108,8 +85,10 @@ public abstract class GeoDirHandler { public Subscription start(final int flags, final long windowDuration, final TimeUnit unit) { final CompositeSubscription subscriptions = new CompositeSubscription(); final boolean lowPower = (flags & LOW_POWER) != 0; + final Sensors sensors = Sensors.getInstance(); + if ((flags & UPDATE_GEODATA) != 0) { - subscriptions.add(throttleIfNeeded(app.geoDataObservable(lowPower), windowDuration, unit).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<GeoData>() { + subscriptions.add(throttleIfNeeded(sensors.geoDataObservable(lowPower), windowDuration, unit).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<GeoData>() { @Override public void call(final GeoData geoData) { updateGeoData(geoData); @@ -117,7 +96,7 @@ public abstract class GeoDirHandler { })); } if ((flags & UPDATE_DIRECTION) != 0) { - subscriptions.add(throttleIfNeeded(fixedDirection(), windowDuration, unit).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<Float>() { + subscriptions.add(throttleIfNeeded(sensors.directionObservable(), windowDuration, unit).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<Float>() { @Override public void call(final Float direction) { updateDirection(direction); @@ -126,10 +105,10 @@ public abstract class GeoDirHandler { } if ((flags & UPDATE_GEODIR) != 0) { // combineOnLatest() does not implement backpressure handling, so we need to explicitely use a backpressure operator there. - subscriptions.add(throttleIfNeeded(Observable.combineLatest(app.geoDataObservable(lowPower), app.directionObservable(), new Func2<GeoData, Float, ImmutablePair<GeoData, Float>>() { + subscriptions.add(throttleIfNeeded(Observable.combineLatest(sensors.geoDataObservable(lowPower), sensors.directionObservable(), new Func2<GeoData, Float, ImmutablePair<GeoData, Float>>() { @Override public ImmutablePair<GeoData, Float> call(final GeoData geoData, final Float direction) { - return ImmutablePair.of(geoData, fixDirection(geoData, direction)); + return ImmutablePair.of(geoData, direction); } }), windowDuration, unit).onBackpressureDrop().observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<ImmutablePair<GeoData, Float>>() { @Override diff --git a/main/src/cgeo/geocaching/sensors/Sensors.java b/main/src/cgeo/geocaching/sensors/Sensors.java new file mode 100644 index 0000000..498ec0e --- /dev/null +++ b/main/src/cgeo/geocaching/sensors/Sensors.java @@ -0,0 +1,160 @@ +package cgeo.geocaching.sensors; + +import cgeo.geocaching.CgeoApplication; +import cgeo.geocaching.playservices.LocationProvider; +import cgeo.geocaching.sensors.GpsStatusProvider.Status; +import cgeo.geocaching.settings.Settings; +import cgeo.geocaching.utils.AngleUtils; +import cgeo.geocaching.utils.Log; +import cgeo.geocaching.utils.RxUtils; + +import org.eclipse.jdt.annotation.NonNull; + +import rx.Observable; +import rx.functions.Action1; +import rx.functions.Func1; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorManager; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class Sensors { + + private Observable<GeoData> geoDataObservable; + private Observable<GeoData> geoDataObservableLowPower; + private Observable<Float> directionObservable; + private Observable<Status> gpsStatusObservable; + @NonNull private volatile GeoData currentGeo = GeoData.DUMMY_LOCATION; + private volatile float currentDirection = 0.0f; + private volatile boolean hasValidLocation = false; + private final boolean hasMagneticFieldSensor; + private final CgeoApplication app = CgeoApplication.getInstance(); + + private static class InstanceHolder { + static final Sensors INSTANCE = new Sensors(); + } + + private final Action1<GeoData> rememberGeodataAction = new Action1<GeoData>() { + @Override + public void call(final GeoData geoData) { + currentGeo = geoData; + hasValidLocation = true; + } + }; + + private final Action1<Float> onNextrememberDirectionAction = new Action1<Float>() { + @Override + public void call(final Float direction) { + currentDirection = direction; + } + }; + + private Sensors() { + gpsStatusObservable = GpsStatusProvider.create(app).replay(1).refCount(); + final SensorManager sensorManager = (SensorManager) app.getSystemService(Context.SENSOR_SERVICE); + hasMagneticFieldSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null; + } + + public static final Sensors getInstance() { + return InstanceHolder.INSTANCE; + } + + public void setupGeoDataObservables(final boolean useGooglePlayServices, final boolean useLowPowerLocation) { + if (useGooglePlayServices) { + geoDataObservable = LocationProvider.getMostPrecise(app).doOnNext(rememberGeodataAction); + if (useLowPowerLocation) { + geoDataObservableLowPower = LocationProvider.getLowPower(app).doOnNext(rememberGeodataAction); + } else { + geoDataObservableLowPower = geoDataObservable; + } + } else { + geoDataObservable = RxUtils.rememberLast(GeoDataProvider.create(app).doOnNext(rememberGeodataAction), null); + geoDataObservableLowPower = geoDataObservable; + } + } + + private static final Func1<GeoData, Float> GPS_TO_DIRECTION = new Func1<GeoData, Float>() { + @Override + public Float call(final GeoData geoData) { + return AngleUtils.reverseDirectionNow(geoData.getBearing()); + } + }; + + public void setupDirectionObservable(final boolean useLowPower) { + // If we have no magnetic sensor, there is no point in trying to setup any, we will always get the direction from the GPS. + if (!hasMagneticFieldSensor) { + Log.i("No magnetic field sensor, using only the GPS for the orientation"); + directionObservable = RxUtils.rememberLast(geoDataObservableLowPower.map(GPS_TO_DIRECTION).doOnNext(onNextrememberDirectionAction), 0f); + return; + } + + // Combine the magnetic direction observable with the GPS when compass is disabled or speed is high enough. + + final AtomicBoolean useDirectionFromGps = new AtomicBoolean(false); + + final Observable<Float> magneticDirectionObservable = RotationProvider.create(app, useLowPower).onErrorResumeNext(new Func1<Throwable, Observable<? extends Float>>() { + @Override + public Observable<? extends Float> call(final Throwable throwable) { + return OrientationProvider.create(app); + } + }).onErrorResumeNext(new Func1<Throwable, Observable<? extends Float>>() { + @Override + public Observable<? extends Float> call(final Throwable throwable) { + Log.e("Device orientation will not be available as no suitable sensors were found, disabling compass"); + Settings.setUseCompass(false); + return Observable.<Float>never().startWith(0.0f); + } + }).filter(new Func1<Float, Boolean>() { + @Override + public Boolean call(final Float aFloat) { + return Settings.isUseCompass() && !useDirectionFromGps.get(); + } + }); + + final Observable<Float> directionFromGpsObservable = geoDataObservable(true).filter(new Func1<GeoData, Boolean>() { + @Override + public Boolean call(final GeoData geoData) { + final boolean useGps = geoData.getSpeed() > 5.0f; + useDirectionFromGps.set(useGps); + return useGps || !Settings.isUseCompass(); + } + }).map(GPS_TO_DIRECTION); + + directionObservable = RxUtils.rememberLast(Observable.merge(magneticDirectionObservable, directionFromGpsObservable).doOnNext(onNextrememberDirectionAction), 0f); + } + + public Observable<GeoData> geoDataObservable(final boolean lowPower) { + return lowPower ? geoDataObservableLowPower : geoDataObservable; + } + + public Observable<Float> directionObservable() { + return directionObservable; + } + + public Observable<Status> gpsStatusObservable() { + if (gpsStatusObservable == null) { + gpsStatusObservable = GpsStatusProvider.create(app).share(); + } + return gpsStatusObservable; + } + + @NonNull + public GeoData currentGeo() { + return currentGeo; + } + + public boolean hasValidLocation() { + return hasValidLocation; + } + + public float currentDirection() { + return currentDirection; + } + + public boolean hasMagneticFieldSensor() { + return hasMagneticFieldSensor; + } + +} diff --git a/main/src/cgeo/geocaching/settings/Settings.java b/main/src/cgeo/geocaching/settings/Settings.java index b678d3b..ad15d81 100644 --- a/main/src/cgeo/geocaching/settings/Settings.java +++ b/main/src/cgeo/geocaching/settings/Settings.java @@ -627,7 +627,7 @@ public class Settings { } public static boolean isMapTrail() { - return getBoolean(R.string.pref_maptrail, true); + return getBoolean(R.string.pref_maptrail, false); } public static void setMapTrail(final boolean showTrail) { @@ -808,6 +808,7 @@ public class Settings { * @return The cache type used for filtering or ALL if no filter is active. * Returns never null */ + @NonNull public static CacheType getCacheType() { return CacheType.getById(getString(R.string.pref_cachetype, CacheType.ALL.id)); } diff --git a/main/src/cgeo/geocaching/settings/SettingsActivity.java b/main/src/cgeo/geocaching/settings/SettingsActivity.java index 662ba33..99de30f 100644 --- a/main/src/cgeo/geocaching/settings/SettingsActivity.java +++ b/main/src/cgeo/geocaching/settings/SettingsActivity.java @@ -12,6 +12,7 @@ import cgeo.geocaching.connector.gc.GCConnector; import cgeo.geocaching.files.SimpleDirChooser; import cgeo.geocaching.maps.MapProviderFactory; import cgeo.geocaching.maps.interfaces.MapSource; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.utils.DatabaseBackupUtils; import cgeo.geocaching.utils.DebugUtils; import cgeo.geocaching.utils.Log; @@ -421,11 +422,12 @@ public class SettingsActivity extends PreferenceActivity { } private void initGeoDirPreferences() { + final Sensors sensors = Sensors.getInstance(); final Preference playServices = getPreference(R.string.pref_googleplayservices); playServices.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { - CgeoApplication.getInstance().setupGeoDataObservables((Boolean) newValue, Settings.useLowPowerMode()); + sensors.setupGeoDataObservables((Boolean) newValue, Settings.useLowPowerMode()); return true; } }); @@ -433,10 +435,9 @@ public class SettingsActivity extends PreferenceActivity { getPreference(R.string.pref_lowpowermode).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { - final CgeoApplication app = CgeoApplication.getInstance(); final Boolean useLowPower = (Boolean) newValue; - app.setupGeoDataObservables(Settings.useGooglePlayServices(), useLowPower); - app.setupDirectionObservable(useLowPower); + sensors.setupGeoDataObservables(Settings.useGooglePlayServices(), useLowPower); + sensors.setupDirectionObservable(useLowPower); return true; } }); diff --git a/main/src/cgeo/geocaching/sorting/DateComparator.java b/main/src/cgeo/geocaching/sorting/DateComparator.java index 6d294db..af50213 100644 --- a/main/src/cgeo/geocaching/sorting/DateComparator.java +++ b/main/src/cgeo/geocaching/sorting/DateComparator.java @@ -1,7 +1,7 @@ package cgeo.geocaching.sorting; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; +import cgeo.geocaching.sensors.Sensors; import java.util.ArrayList; import java.util.Date; @@ -22,7 +22,7 @@ class DateComparator extends AbstractCacheComparator { final ArrayList<Geocache> list = new ArrayList<>(); list.add(cache1); list.add(cache2); - final DistanceComparator distanceComparator = new DistanceComparator(CgeoApplication.getInstance().currentGeo().getCoords(), list); + final DistanceComparator distanceComparator = new DistanceComparator(Sensors.getInstance().currentGeo().getCoords(), list); return distanceComparator.compare(cache1, cache2); } return dateDifference; diff --git a/main/src/cgeo/geocaching/sorting/SizeComparator.java b/main/src/cgeo/geocaching/sorting/SizeComparator.java index 9e911f5..8cb5178 100644 --- a/main/src/cgeo/geocaching/sorting/SizeComparator.java +++ b/main/src/cgeo/geocaching/sorting/SizeComparator.java @@ -9,11 +9,6 @@ import cgeo.geocaching.Geocache; class SizeComparator extends AbstractCacheComparator { @Override - protected boolean canCompare(final Geocache cache) { - return cache.getSize() != null; - } - - @Override protected int compareCaches(final Geocache cache1, final Geocache cache2) { return cache2.getSize().comparable - cache1.getSize().comparable; } diff --git a/main/src/cgeo/geocaching/ui/AddressListAdapter.java b/main/src/cgeo/geocaching/ui/AddressListAdapter.java index 691c8d2..901bffc 100644 --- a/main/src/cgeo/geocaching/ui/AddressListAdapter.java +++ b/main/src/cgeo/geocaching/ui/AddressListAdapter.java @@ -3,12 +3,13 @@ package cgeo.geocaching.ui; import butterknife.InjectView; import cgeo.geocaching.CacheListActivity; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.R; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.Units; +import cgeo.geocaching.sensors.Sensors; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jdt.annotation.NonNull; import android.app.Activity; import android.location.Address; @@ -23,7 +24,7 @@ import java.util.ArrayList; public class AddressListAdapter extends ArrayAdapter<Address> { final private LayoutInflater inflater; - final private Geopoint location; + @NonNull final private Geopoint location; protected static final class ViewHolder extends AbstractViewHolder { @InjectView(R.id.label) protected TextView label; @@ -37,7 +38,7 @@ public class AddressListAdapter extends ArrayAdapter<Address> { public AddressListAdapter(final Activity context) { super(context, 0); inflater = context.getLayoutInflater(); - location = CgeoApplication.getInstance().currentGeo().getCoords(); + location = Sensors.getInstance().currentGeo().getCoords(); } @Override @@ -72,7 +73,7 @@ public class AddressListAdapter extends ArrayAdapter<Address> { } private CharSequence getDistanceText(final Address address) { - if (location != null && address.hasLatitude() && address.hasLongitude()) { + if (address.hasLatitude() && address.hasLongitude()) { return Units.getDistanceFromKilometers(location.distanceTo(new Geopoint(address.getLatitude(), address.getLongitude()))); } diff --git a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java index 13887c0..e2af419 100644 --- a/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java +++ b/main/src/cgeo/geocaching/ui/CacheDetailsCreator.java @@ -2,13 +2,13 @@ package cgeo.geocaching.ui; import butterknife.ButterKnife; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.ICoordinates; import cgeo.geocaching.R; import cgeo.geocaching.Waypoint; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.location.Units; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.utils.Formatter; import org.apache.commons.lang3.StringUtils; @@ -18,11 +18,9 @@ import org.eclipse.jdt.annotation.NonNull; import android.annotation.SuppressLint; import android.app.Activity; import android.content.res.Resources; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; +import android.widget.RatingBar; import android.widget.RelativeLayout; import android.widget.TextView; @@ -74,33 +72,18 @@ public final class CacheDetailsCreator { final RelativeLayout layout = (RelativeLayout) activity.getLayoutInflater().inflate(R.layout.cache_information_item, null, false); final TextView nameView = ButterKnife.findById(layout, R.id.name); lastValueView = ButterKnife.findById(layout, R.id.value); - final LinearLayout layoutStars = ButterKnife.findById(layout, R.id.stars); nameView.setText(activity.getResources().getString(nameId)); lastValueView.setText(String.format("%.1f", value) + ' ' + activity.getResources().getString(R.string.cache_rating_of) + " " + String.format("%d", max)); - createStarImages(layoutStars, value, max); + + final RatingBar layoutStars = ButterKnife.findById(layout, R.id.stars); + layoutStars.setRating(value); layoutStars.setVisibility(View.VISIBLE); parentView.addView(layout); return layout; } - private void createStarImages(final ViewGroup starsContainer, final float value, final int max) { - final LayoutInflater inflater = LayoutInflater.from(activity); - - for (int i = 0; i < max; i++) { - final ImageView star = (ImageView) inflater.inflate(R.layout.star_image, starsContainer, false); - if (value - i >= 0.75) { - star.setImageResource(R.drawable.star_on); - } else if (value - i >= 0.25) { - star.setImageResource(R.drawable.star_half); - } else { - star.setImageResource(R.drawable.star_off); - } - starsContainer.addView(star); - } - } - public void addCacheState(final Geocache cache) { if (cache.isLogOffline() || cache.isArchived() || cache.isDisabled() || cache.isPremiumMembersOnly() || cache.isFound()) { final List<String> states = new ArrayList<>(5); @@ -135,7 +118,7 @@ public final class CacheDetailsCreator { if (target.getCoords() == null) { return null; } - return CgeoApplication.getInstance().currentGeo().getCoords().distanceTo(target); + return Sensors.getInstance().currentGeo().getCoords().distanceTo(target); } public void addRating(final Geocache cache) { @@ -150,7 +133,7 @@ public final class CacheDetailsCreator { } public void addSize(final Geocache cache) { - if (null != cache.getSize() && cache.showSize()) { + if (cache.showSize()) { add(R.string.cache_size, cache.getSize().getL10n()); } } diff --git a/main/src/cgeo/geocaching/ui/CacheListAdapter.java b/main/src/cgeo/geocaching/ui/CacheListAdapter.java index f080761..34cac01 100644 --- a/main/src/cgeo/geocaching/ui/CacheListAdapter.java +++ b/main/src/cgeo/geocaching/ui/CacheListAdapter.java @@ -3,7 +3,6 @@ package cgeo.geocaching.ui; import butterknife.InjectView; import cgeo.geocaching.CacheDetailActivity; -import cgeo.geocaching.CgeoApplication; import cgeo.geocaching.Geocache; import cgeo.geocaching.R; import cgeo.geocaching.enumerations.CacheListType; @@ -11,6 +10,7 @@ import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.filter.IFilter; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.sensors.GeoData; +import cgeo.geocaching.sensors.Sensors; import cgeo.geocaching.settings.Settings; import cgeo.geocaching.sorting.CacheComparator; import cgeo.geocaching.sorting.DistanceComparator; @@ -119,7 +119,7 @@ public class CacheListAdapter extends ArrayAdapter<Geocache> { public CacheListAdapter(final Activity activity, final List<Geocache> list, final CacheListType cacheListType) { super(activity, 0, list); - final GeoData currentGeo = CgeoApplication.getInstance().currentGeo(); + final GeoData currentGeo = Sensors.getInstance().currentGeo(); coords = currentGeo.getCoords(); this.res = activity.getResources(); this.list = list; diff --git a/main/src/cgeo/geocaching/ui/ImagesList.java b/main/src/cgeo/geocaching/ui/ImagesList.java index 458f8db..32e29bb 100644 --- a/main/src/cgeo/geocaching/ui/ImagesList.java +++ b/main/src/cgeo/geocaching/ui/ImagesList.java @@ -106,7 +106,7 @@ public class ImagesList { for (final Image img : images) { final LinearLayout rowView = (LinearLayout) inflater.inflate(R.layout.cache_image_item, imagesView, false); - assert(rowView != null); + assert rowView != null; if (StringUtils.isNotBlank(img.getTitle())) { final TextView titleView = ButterKnife.findById(rowView, R.id.title); @@ -121,7 +121,7 @@ public class ImagesList { } final ImageView imageView = (ImageView) inflater.inflate(R.layout.image_item, rowView, false); - assert(imageView != null); + assert imageView != null; subscriptions.add(AndroidObservable.bindActivity(activity, imgGetter.fetchDrawable(img.getUrl())).subscribe(new Action1<BitmapDrawable>() { @Override public void call(final BitmapDrawable image) { diff --git a/main/src/cgeo/geocaching/ui/WrappingGridView.java b/main/src/cgeo/geocaching/ui/WrappingGridView.java new file mode 100644 index 0000000..2c85887 --- /dev/null +++ b/main/src/cgeo/geocaching/ui/WrappingGridView.java @@ -0,0 +1,38 @@ +package cgeo.geocaching.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.GridView; + +/** + * GridView that will adjust its height to really use wrap_content. The standard GridView only shows one line of items. + * + * @see <a href="https://gist.github.com/runemart/9781609">https://gist.github.com/runemart/9781609</a> + * + */ +public class WrappingGridView extends GridView { + + public WrappingGridView(final Context context) { + super(context); + } + + public WrappingGridView(final Context context, final AttributeSet attrs) { + super(context, attrs); + } + + public WrappingGridView(final Context context, final AttributeSet attrs, final int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { + int heightSpec = heightMeasureSpec; + if (getLayoutParams().height == android.view.ViewGroup.LayoutParams.WRAP_CONTENT) { + // The two leftmost bits in the height measure spec have + // a special meaning, hence we can't use them to describe height. + heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); + } + super.onMeasure(widthMeasureSpec, heightSpec); + } + +}
\ No newline at end of file diff --git a/main/src/cgeo/geocaching/utils/CryptUtils.java b/main/src/cgeo/geocaching/utils/CryptUtils.java index f2ff0c2..70d5895 100644 --- a/main/src/cgeo/geocaching/utils/CryptUtils.java +++ b/main/src/cgeo/geocaching/utils/CryptUtils.java @@ -23,8 +23,8 @@ public final class CryptUtils { } private static final byte[] EMPTY = {}; - private static char[] BASE64MAP1 = new char[64]; - private static byte[] BASE64MAP2 = new byte[128]; + private static final char[] BASE64MAP1 = new char[64]; + private static final byte[] BASE64MAP2 = new byte[128]; static { int i = 0; diff --git a/main/src/cgeo/geocaching/utils/Log.java b/main/src/cgeo/geocaching/utils/Log.java index f338a8e..2f51e42 100644 --- a/main/src/cgeo/geocaching/utils/Log.java +++ b/main/src/cgeo/geocaching/utils/Log.java @@ -43,56 +43,60 @@ public final class Log { Log.isDebug = isDebug; } + private static String addThreadInfo(final String msg) { + return new StringBuilder("[").append(Thread.currentThread().getName()).append("] ").append(msg).toString(); + } + public static void v(final String msg) { if (isDebug) { - android.util.Log.v(TAG, msg); + android.util.Log.v(TAG, addThreadInfo(msg)); } } public static void v(final String msg, final Throwable t) { if (isDebug) { - android.util.Log.v(TAG, msg, t); + android.util.Log.v(TAG, addThreadInfo(msg), t); } } public static void d(final String msg) { if (isDebug) { - android.util.Log.d(TAG, msg); + android.util.Log.d(TAG, addThreadInfo(msg)); } } public static void d(final String msg, final Throwable t) { if (isDebug) { - android.util.Log.d(TAG, msg, t); + android.util.Log.d(TAG, addThreadInfo(msg), t); } } public static void i(final String msg) { if (isDebug) { - android.util.Log.i(TAG, msg); + android.util.Log.i(TAG, addThreadInfo(msg)); } } public static void i(final String msg, final Throwable t) { if (isDebug) { - android.util.Log.i(TAG, msg, t); + android.util.Log.i(TAG, addThreadInfo(msg), t); } } public static void w(final String msg) { - android.util.Log.w(TAG, msg); + android.util.Log.w(TAG, addThreadInfo(msg)); } public static void w(final String msg, final Throwable t) { - android.util.Log.w(TAG, msg, t); + android.util.Log.w(TAG, addThreadInfo(msg), t); } public static void e(final String msg) { - android.util.Log.e(TAG, msg); + android.util.Log.e(TAG, addThreadInfo(msg)); } public static void e(final String msg, final Throwable t) { - android.util.Log.e(TAG, msg, t); + android.util.Log.e(TAG, addThreadInfo(msg), t); } /** @@ -116,7 +120,7 @@ public final class Log { Writer writer = null; try { writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true), CharEncoding.UTF_8)); - writer.write(msg); + writer.write(addThreadInfo(msg)); } catch (final IOException e) { Log.e("logToFile: cannot write to " + file, e); } finally { diff --git a/main/src/cgeo/geocaching/utils/RxUtils.java b/main/src/cgeo/geocaching/utils/RxUtils.java index beb5e18..af58214 100644 --- a/main/src/cgeo/geocaching/utils/RxUtils.java +++ b/main/src/cgeo/geocaching/utils/RxUtils.java @@ -11,6 +11,7 @@ import rx.functions.Action0; import rx.functions.Action1; import rx.functions.Func0; import rx.functions.Func1; +import rx.internal.util.RxThreadFactory; import rx.observables.BlockingObservable; import rx.observers.Subscribers; import rx.schedulers.Schedulers; @@ -25,8 +26,7 @@ import android.os.Process; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -39,12 +39,12 @@ public class RxUtils { public final static Scheduler computationScheduler = Schedulers.computation(); - public static final Scheduler networkScheduler = Schedulers.from(new ThreadPoolExecutor(10, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>())); + public static final Scheduler networkScheduler = Schedulers.from(Executors.newFixedThreadPool(10, new RxThreadFactory("network-"))); - public static final Scheduler refreshScheduler = Schedulers.from(new ThreadPoolExecutor(3, 3, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>())); + public static final Scheduler refreshScheduler = Schedulers.from(Executors.newFixedThreadPool(3, new RxThreadFactory("refresh-"))); private static final HandlerThread looperCallbacksThread = - new HandlerThread("Looper callbacks thread", Process.THREAD_PRIORITY_DEFAULT); + new HandlerThread("looper callbacks", Process.THREAD_PRIORITY_DEFAULT); static { looperCallbacksThread.start(); @@ -158,8 +158,8 @@ public class RxUtils { }; } - public static<T> Observable<T> rememberLast(final Observable<T> observable) { - final AtomicReference<T> lastValue = new AtomicReference<>(null); + public static<T> Observable<T> rememberLast(final Observable<T> observable, final T initialValue) { + final AtomicReference<T> lastValue = new AtomicReference<>(initialValue); return observable.doOnNext(new Action1<T>() { @Override public void call(final T value) { @@ -224,7 +224,7 @@ public class RxUtils { if (cached.containsKey(key)) { return cached.get(key); } - final Observable<V> value = func.call(key).replay().refCount(); + final Observable<V> value = func.call(key).share(); cached.put(key, value); return value; } |
