diff options
author | Bananeweizen <bananeweizen@gmx.de> | 2012-03-10 07:37:59 +0100 |
---|---|---|
committer | Bananeweizen <bananeweizen@gmx.de> | 2012-03-10 07:37:59 +0100 |
commit | b297de18e81872e9725867ba4da4a9481aedfcbf (patch) | |
tree | 80db5404f602577fdbf2d3e1bb34c36f850300d2 /main | |
parent | 5daaa3d454ff680e74fdbf8f673674d0eb339817 (diff) | |
download | cgeo-b297de18e81872e9725867ba4da4a9481aedfcbf.zip cgeo-b297de18e81872e9725867ba4da4a9481aedfcbf.tar.gz cgeo-b297de18e81872e9725867ba4da4a9481aedfcbf.tar.bz2 |
refactoring: first part of removing cgBase
* extract network stuff (non GC related)
* extract GC login stuff
* move UI related pieces to activities
* to do: move parsing and searching to better places, remove cgBase
completely afterwards
Diffstat (limited to 'main')
30 files changed, 1072 insertions, 1020 deletions
diff --git a/main/src/cgeo/geocaching/CacheDetailActivity.java b/main/src/cgeo/geocaching/CacheDetailActivity.java index 969bc5e..dea2b42 100644 --- a/main/src/cgeo/geocaching/CacheDetailActivity.java +++ b/main/src/cgeo/geocaching/CacheDetailActivity.java @@ -16,6 +16,7 @@ import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.geopoint.HumanDistance; import cgeo.geocaching.geopoint.IConversion; import cgeo.geocaching.network.HtmlImage; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.ui.DecryptTextClickListener; import cgeo.geocaching.ui.Formatter; @@ -1559,7 +1560,7 @@ public class CacheDetailActivity extends AbstractActivity { viewName.setText(res.getString(nameId)); viewValue.setText(String.format("%.1f", value) + ' ' + res.getString(R.string.cache_rating_of) + " 5"); - layoutStars.addView(cgBase.createStarRating(value, 5, CacheDetailActivity.this), 1); + layoutStars.addView(createStarRating(value, 5, CacheDetailActivity.this), 1); detailsList.addView(layout); return layout; @@ -1889,7 +1890,7 @@ public class CacheDetailActivity extends AbstractActivity { final int height = (int) (110 * metrics.density); // TODO move this code to StaticMapProvider and use its constant values - final String markerUrl = cgBase.urlencode_rfc3986("http://cgeo.carnero.cc/_markers/my_location_mdpi.png"); + final String markerUrl = Network.urlencode_rfc3986("http://cgeo.carnero.cc/_markers/my_location_mdpi.png"); final HtmlImage mapGetter = new HtmlImage(CacheDetailActivity.this, cache.getGeocode(), false, 0, false); image = mapGetter.getDrawable("http://maps.google.com/maps/api/staticmap?zoom=15&size=" + width + "x" + height + "&maptype=roadmap&markers=icon%3A" + markerUrl + "%7Cshadow:false%7C" + latlonMap + "&sensor=false"); diff --git a/main/src/cgeo/geocaching/Settings.java b/main/src/cgeo/geocaching/Settings.java index 7479227..294521c 100644 --- a/main/src/cgeo/geocaching/Settings.java +++ b/main/src/cgeo/geocaching/Settings.java @@ -418,7 +418,7 @@ public final class Settings { return sharedPrefs.getBoolean(KEY_LOAD_DIRECTION_IMG, true); } - static void setGcCustomDate(final String format) { + public static void setGcCustomDate(final String format) { editSharedSettings(new PrefRunnable() { @Override diff --git a/main/src/cgeo/geocaching/StaticMapsProvider.java b/main/src/cgeo/geocaching/StaticMapsProvider.java index 04ddb0c..fcb8ed9 100644 --- a/main/src/cgeo/geocaching/StaticMapsProvider.java +++ b/main/src/cgeo/geocaching/StaticMapsProvider.java @@ -4,6 +4,7 @@ import cgeo.geocaching.concurrent.BlockingThreadPool; import cgeo.geocaching.concurrent.Task; import cgeo.geocaching.files.LocalStorage; import cgeo.geocaching.geopoint.GeopointFormatter.Format; +import cgeo.geocaching.network.Network; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -41,7 +42,7 @@ public class StaticMapsProvider { final String mapUrl = "http://maps.google.com/maps/api/staticmap?center=" + latlonMap; final String url = mapUrl + "&zoom=" + zoom + "&size=" + edge + "x" + edge + "&maptype=" + mapType + "&markers=icon%3A" + markerUrl + "%7C" + latlonMap + waypoints + "&sensor=false"; final File file = getMapFile(cache.getGeocode(), prefix, level, true); - final HttpResponse httpResponse = cgBase.request(url, null, false); + final HttpResponse httpResponse = Network.request(url, null, false); if (httpResponse != null) { if (LocalStorage.saveEntityToFile(httpResponse, file)) { @@ -167,12 +168,12 @@ public class StaticMapsProvider { type += "_disabled"; } - return cgBase.urlencode_rfc3986(MARKERS_URL + "marker_cache_" + type + ".png"); + return Network.urlencode_rfc3986(MARKERS_URL + "marker_cache_" + type + ".png"); } private static String getWpMarkerUrl(final cgWaypoint waypoint) { String type = waypoint.getWaypointType() != null ? waypoint.getWaypointType().id : null; - return cgBase.urlencode_rfc3986(MARKERS_URL + "marker_waypoint_" + type + ".png"); + return Network.urlencode_rfc3986(MARKERS_URL + "marker_waypoint_" + type + ".png"); } public static void removeWpStaticMaps(int wp_id, String geocode) { diff --git a/main/src/cgeo/geocaching/VisitCacheActivity.java b/main/src/cgeo/geocaching/VisitCacheActivity.java index 6d0097a..f0acd97 100644 --- a/main/src/cgeo/geocaching/VisitCacheActivity.java +++ b/main/src/cgeo/geocaching/VisitCacheActivity.java @@ -8,7 +8,10 @@ import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.LogTypeTrackable; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.gcvote.GCVote; +import cgeo.geocaching.network.Login; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.twitter.Twitter; import cgeo.geocaching.ui.DateDialog; import cgeo.geocaching.utils.LogTemplateProvider; import cgeo.geocaching.utils.LogTemplateProvider.LogTemplate; @@ -352,7 +355,7 @@ public class VisitCacheActivity extends AbstractActivity implements DateDialog.D private void insertIntoLog(String newText, final boolean moveCursor) { final EditText log = (EditText) findViewById(R.id.log); - cgBase.insertAtPosition(log, newText, moveCursor); + insertAtPosition(log, newText, moveCursor); } private static String ratingTextValue(final double rating) { @@ -653,9 +656,9 @@ public class VisitCacheActivity extends AbstractActivity implements DateDialog.D return; } - final String page = cgBase.getResponseData(cgBase.request("http://www.geocaching.com/seek/log.aspx", params, false, false, false)); + final String page = Network.getResponseData(Network.request("http://www.geocaching.com/seek/log.aspx", params, false, false, false)); - viewstates = cgBase.getViewstates(page); + viewstates = Login.getViewstates(page); trackables = cgBase.parseTrackableLog(page); final List<LogType> typesPre = cgBase.parseTypes(page); @@ -729,7 +732,7 @@ public class VisitCacheActivity extends AbstractActivity implements DateDialog.D if (status == StatusCode.NO_ERROR && typeSelected == LogType.LOG_FOUND_IT && Settings.isUseTwitter() && Settings.isTwitterLoginValid() && tweetCheck.isChecked() && tweetBox.getVisibility() == View.VISIBLE) { - cgBase.postTweetCache(geocode); + Twitter.postTweetCache(geocode); } if (status == StatusCode.NO_ERROR && typeSelected == LogType.LOG_FOUND_IT && Settings.isGCvoteLogin()) { diff --git a/main/src/cgeo/geocaching/activity/AbstractActivity.java b/main/src/cgeo/geocaching/activity/AbstractActivity.java index 941cd08..8e8ad9d 100644 --- a/main/src/cgeo/geocaching/activity/AbstractActivity.java +++ b/main/src/cgeo/geocaching/activity/AbstractActivity.java @@ -1,18 +1,24 @@ package cgeo.geocaching.activity; +import cgeo.geocaching.R; import cgeo.geocaching.Settings; import cgeo.geocaching.cgBase; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.compatibility.Compatibility; +import cgeo.geocaching.network.Network; import android.app.Activity; +import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; public abstract class AbstractActivity extends Activity implements IAbstractActivity { @@ -82,7 +88,7 @@ public abstract class AbstractActivity extends Activity implements IAbstractActi cgBase.initialize(app); // Restore cookie store if needed - cgBase.restoreCookieStore(Settings.getCookieStore()); + Network.restoreCookieStore(Settings.getCookieStore()); ActivityMixin.keepScreenOn(this, keepScreenOn); } @@ -103,4 +109,51 @@ public abstract class AbstractActivity extends Activity implements IAbstractActi ActivityMixin.invalidateOptionsMenu(this); } + public static LinearLayout createStarRating(final float value, final int count, final Context context) { + LayoutInflater inflater = LayoutInflater.from(context); + LinearLayout starsContainer = new LinearLayout(context); + starsContainer.setOrientation(LinearLayout.HORIZONTAL); + + for (int i = 0; i < count; i++) { + ImageView star = (ImageView) inflater.inflate(R.layout.star, null); + 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); + } + + return starsContainer; + } + + /** + * insert text into the EditText at the current cursor position + * + * @param editText + * @param insertText + * @param moveCursor + * place the cursor after the inserted text + */ + public static void insertAtPosition(final EditText editText, final String insertText, final boolean moveCursor) { + int selectionStart = editText.getSelectionStart(); + int selectionEnd = editText.getSelectionEnd(); + int start = Math.min(selectionStart, selectionEnd); + int end = Math.max(selectionStart, selectionEnd); + + final String content = editText.getText().toString(); + String completeText; + if (start > 0 && !Character.isWhitespace(content.charAt(start - 1))) { + completeText = " " + insertText; + } else { + completeText = insertText; + } + + editText.getText().replace(start, end, completeText); + int newCursor = moveCursor ? start + completeText.length() : start; + editText.setSelection(newCursor, newCursor); + } + } diff --git a/main/src/cgeo/geocaching/cgBase.java b/main/src/cgeo/geocaching/cgBase.java index befdabb..91c61c0 100644 --- a/main/src/cgeo/geocaching/cgBase.java +++ b/main/src/cgeo/geocaching/cgBase.java @@ -19,10 +19,10 @@ import cgeo.geocaching.gcvote.GCVote; import cgeo.geocaching.gcvote.GCVoteRating; import cgeo.geocaching.geopoint.DistanceParser; import cgeo.geocaching.geopoint.Geopoint; -import cgeo.geocaching.geopoint.GeopointFormatter.Format; import cgeo.geocaching.network.HtmlImage; +import cgeo.geocaching.network.Login; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; -import cgeo.geocaching.twitter.Twitter; import cgeo.geocaching.ui.DirectionImage; import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.CancellableHandler; @@ -30,34 +30,7 @@ import cgeo.geocaching.utils.CancellableHandler; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.http.Header; -import org.apache.http.HeaderElement; -import org.apache.http.HttpEntity; -import org.apache.http.HttpException; -import org.apache.http.HttpRequest; -import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; -import org.apache.http.HttpResponseInterceptor; -import org.apache.http.NameValuePair; -import org.apache.http.client.CookieStore; -import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.cookie.Cookie; -import org.apache.http.entity.HttpEntityWrapper; -import org.apache.http.impl.client.BasicCookieStore; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.cookie.BasicClientCookie; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.CoreConnectionPNames; -import org.apache.http.params.CoreProtocolPNames; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.HTTP; -import org.apache.http.protocol.HttpContext; -import org.apache.http.util.EntityUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -69,9 +42,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; import android.net.Uri; import android.os.Handler; import android.os.Message; @@ -81,83 +51,29 @@ import android.text.Spanned; import android.text.format.DateUtils; import android.text.style.StrikethroughSpan; import android.util.Log; -import android.view.LayoutInflater; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; + import java.net.URLDecoder; -import java.net.URLEncoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; -import java.util.Collections; -import java.util.Date; import java.util.EnumSet; -import java.util.Enumeration; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.regex.Matcher; -import java.util.zip.GZIPInputStream; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLSession; public class cgBase { - private static final String passMatch = "(?<=[\\?&])[Pp]ass(w(or)?d)?=[^&#$]+"; - - private final static Map<String, SimpleDateFormat> gcCustomDateFormats; - static { - final String[] formats = new String[] { - "MM/dd/yyyy", - "yyyy-MM-dd", - "yyyy/MM/dd", - "dd/MMM/yyyy", - "MMM/dd/yyyy", - "dd MMM yy", - "dd/MM/yyyy" - }; - - Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>(); - - for (String format : formats) { - map.put(format, new SimpleDateFormat(format, Locale.ENGLISH)); - } - - gcCustomDateFormats = Collections.unmodifiableMap(map); - } private final static SimpleDateFormat dateTbIn1 = new SimpleDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 private final static SimpleDateFormat dateTbIn2 = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 public static String version = null; private static Context context; - private static Resources res; - - private static final int NB_DOWNLOAD_RETRIES = 4; + public static Resources res; public static final int UPDATE_LOAD_PROGRESS_DETAIL = 42186; - // false = not logged in - private static boolean actualLoginStatus = false; - private static String actualUserName = ""; - private static String actualMemberStatus = ""; - private static int actualCachesFound = -1; - private static String actualStatus = ""; - - /** User agent id */ - public final static String USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:9.0.1) Gecko/20100101 Firefox/9.0.1"; - private cgBase() { //initialize(app); throw new UnsupportedOperationException(); // static class, not to be instantiated @@ -182,10 +98,6 @@ public class cgBase { } } - public static String hidePassword(final String message) { - return message.replaceAll(passMatch, "password=***"); - } - public static void sendLoadProgressDetail(final Handler handler, final int str) { if (null != handler) { handler.obtainMessage(UPDATE_LOAD_PROGRESS_DETAIL, cgeoapplication.getInstance().getString(str)).sendToTarget(); @@ -193,72 +105,6 @@ public class cgBase { } /** - * read all viewstates from page - * - * @return String[] with all view states - */ - public static String[] getViewstates(String page) { - // Get the number of viewstates. - // If there is only one viewstate, __VIEWSTATEFIELDCOUNT is not present - - if (page == null) { // no network access - return null; - } - - int count = 1; - final Matcher matcherViewstateCount = GCConstants.PATTERN_VIEWSTATEFIELDCOUNT.matcher(page); - if (matcherViewstateCount.find()) { - count = Integer.parseInt(matcherViewstateCount.group(1)); - } - - String[] viewstates = new String[count]; - - // Get the viewstates - int no; - final Matcher matcherViewstates = GCConstants.PATTERN_VIEWSTATES.matcher(page); - while (matcherViewstates.find()) { - String sno = matcherViewstates.group(1); // number of viewstate - if (sno.length() == 0) { - no = 0; - } - else { - no = Integer.parseInt(sno); - } - viewstates[no] = matcherViewstates.group(2); - } - - if (viewstates.length != 1 || viewstates[0] != null) { - return viewstates; - } - // no viewstates were present - return null; - } - - /** - * put viewstates into request parameters - */ - private static void putViewstates(final Parameters params, final String[] viewstates) { - if (ArrayUtils.isEmpty(viewstates)) { - return; - } - params.put("__VIEWSTATE", viewstates[0]); - if (viewstates.length > 1) { - for (int i = 1; i < viewstates.length; i++) { - params.put("__VIEWSTATE" + i, viewstates[i]); - } - params.put("__VIEWSTATEFIELDCOUNT", String.valueOf(viewstates.length)); - } - } - - /** - * transfers the viewstates variables from a page (response) to parameters - * (next request) - */ - public static void transferViewstates(final String page, final Parameters params) { - putViewstates(params, getViewstates(page)); - } - - /** * checks if an Array of Strings is empty or not. Empty means: * - Array is null * - or all elements are null or empty strings @@ -276,157 +122,6 @@ public class cgBase { return true; } - public static StatusCode login() { - final ImmutablePair<String, String> login = Settings.getLogin(); - - if (login == null || StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) { - cgBase.setActualStatus(res.getString(R.string.err_login)); - Log.e(Settings.tag, "cgeoBase.login: No login information stored"); - return StatusCode.NO_LOGIN_INFO_STORED; - } - - // res is null during the unit tests - if (res != null) { - cgBase.setActualStatus(res.getString(R.string.init_login_popup_working)); - } - HttpResponse loginResponse = request("https://www.geocaching.com/login/default.aspx", null, false, false, false); - String loginData = getResponseData(loginResponse); - if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) { - return StatusCode.MAINTENANCE; - } - - if (StringUtils.isBlank(loginData)) { - Log.e(Settings.tag, "cgeoBase.login: Failed to retrieve login page (1st)"); - return StatusCode.CONNECTION_FAILED; // no loginpage - } - - if (getLoginStatus(loginData)) { - Log.i(Settings.tag, "Already logged in Geocaching.com as " + login.left); - switchToEnglish(loginData); - return StatusCode.NO_ERROR; // logged in - } - - clearCookies(); - Settings.setCookieStore(null); - - final Parameters params = new Parameters( - "__EVENTTARGET", "", - "__EVENTARGUMENT", "", - "ctl00$ContentBody$tbUsername", login.left, - "ctl00$ContentBody$tbPassword", login.right, - "ctl00$ContentBody$cbRememberMe", "on", - "ctl00$ContentBody$btnSignIn", "Login"); - final String[] viewstates = getViewstates(loginData); - if (isEmpty(viewstates)) { - Log.e(Settings.tag, "cgeoBase.login: Failed to find viewstates"); - return StatusCode.LOGIN_PARSE_ERROR; // no viewstates - } - putViewstates(params, viewstates); - - loginResponse = postRequest("https://www.geocaching.com/login/default.aspx", params); - loginData = getResponseData(loginResponse); - - if (StringUtils.isNotBlank(loginData)) { - if (getLoginStatus(loginData)) { - Log.i(Settings.tag, "Successfully logged in Geocaching.com as " + login.left); - - switchToEnglish(loginData); - Settings.setCookieStore(dumpCookieStore()); - - return StatusCode.NO_ERROR; // logged in - } else { - if (loginData.contains("Your username/password combination does not match.")) { - Log.i(Settings.tag, "Failed to log in Geocaching.com as " + login.left + " because of wrong username/password"); - return StatusCode.WRONG_LOGIN_DATA; // wrong login - } else { - Log.i(Settings.tag, "Failed to log in Geocaching.com as " + login.left + " for some unknown reason"); - return StatusCode.UNKNOWN_ERROR; // can't login - } - } - } else { - Log.e(Settings.tag, "cgeoBase.login: Failed to retrieve login page (2nd)"); - // FIXME: should it be CONNECTION_FAILED to match the first attempt? - return StatusCode.COMMUNICATION_ERROR; // no login page - } - } - - public static StatusCode logout() { - HttpResponse logoutResponse = request("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f", null, false, false, false); - String logoutData = getResponseData(logoutResponse); - if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) { - return StatusCode.MAINTENANCE; - } - - clearCookies(); - Settings.setCookieStore(null); - return StatusCode.NO_ERROR; - } - - /** - * Check if the user has been logged in when he retrieved the data. - * - * @param page - * @return <code>true</code> if user is logged in, <code>false</code> otherwise - */ - private static boolean getLoginStatus(final String page) { - if (StringUtils.isBlank(page)) { - Log.e(Settings.tag, "cgeoBase.checkLogin: No page given"); - return false; - } - - // res is null during the unit tests - if (res != null) { - setActualStatus(res.getString(R.string.init_login_popup_ok)); - } - - // on every page except login page - setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME)); - if (isActualLoginStatus()) { - setActualUserName(BaseUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???")); - setActualMemberStatus(BaseUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, "???")); - setActualCachesFound(Integer.parseInt(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", ""))); - return true; - } - - // login page - setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME_LOGIN_PAGE)); - if (isActualLoginStatus()) { - setActualUserName(Settings.getUsername()); - setActualMemberStatus(Settings.getMemberStatus()); - // number of caches found is not part of this page - return true; - } - - // res is null during the unit tests - if (res != null) { - setActualStatus(res.getString(R.string.init_login_popup_failed)); - } - return false; - } - - public static void switchToEnglish(String previousPage) { - final String ENGLISH = "English▼"; - if (previousPage != null && previousPage.indexOf(ENGLISH) >= 0) { - Log.i(Settings.tag, "Geocaching.com language already set to English"); - // get find count - getLoginStatus(getResponseData(request("http://www.geocaching.com/email/", null, false))); - } else { - final String page = getResponseData(request("http://www.geocaching.com/default.aspx", null, false)); - getLoginStatus(page); - if (page == null) { - Log.e(Settings.tag, "Failed to read viewstates to set geocaching.com language"); - } - final Parameters params = new Parameters( - "__EVENTTARGET", "ctl00$uxLocaleList$uxLocaleList$ctl00$uxLocaleItem", // switch to english - "__EVENTARGUMENT", ""); - transferViewstates(page, params); - final HttpResponse response = postRequest("http://www.geocaching.com/default.aspx", params); - if (!isSuccess(response)) { - Log.e(Settings.tag, "Failed to set geocaching.com language to English"); - } - } - } - private static SearchResult parseSearch(final cgSearchThread thread, final String url, final String pageContent, final boolean showCaptcha) { if (StringUtils.isBlank(pageContent)) { Log.e(Settings.tag, "cgeoBase.parseSearch: No page given"); @@ -441,7 +136,7 @@ public class cgBase { final SearchResult searchResult = new SearchResult(); searchResult.setUrl(url); - searchResult.viewstates = getViewstates(page); + searchResult.viewstates = Login.getViewstates(page); // recaptcha if (showCaptcha) { @@ -449,7 +144,7 @@ public class cgBase { if (recaptchaJsParam != null) { final Parameters params = new Parameters("k", recaptchaJsParam.trim()); - final String recaptchaJs = cgBase.getResponseData(request("http://www.google.com/recaptcha/api/challenge", params, true)); + final String recaptchaJs = Network.getResponseData(Network.request("http://www.google.com/recaptcha/api/challenge", params, true)); if (StringUtils.isNotBlank(recaptchaJs)) { recaptchaChallenge = BaseUtils.getMatch(recaptchaJs, GCConstants.PATTERN_SEARCH_RECAPTCHACHALLENGE, true, 1, null, true); @@ -649,7 +344,7 @@ public class cgBase { } params.put("ctl00$ContentBody$uxDownloadLoc", "Download Waypoints"); - final String coordinates = getResponseData(postRequest("http://www.geocaching.com/seek/nearest.aspx", params), false); + final String coordinates = Network.getResponseData(Network.postRequest("http://www.geocaching.com/seek/nearest.aspx", params), false); if (StringUtils.isNotBlank(coordinates)) { if (coordinates.contains("You have not agreed to the license agreement. The license agreement is required before you can start downloading GPX or LOC files from Geocaching.com")) { @@ -795,13 +490,13 @@ public class cgBase { try { String hiddenString = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDEN, true, null); if (StringUtils.isNotBlank(hiddenString)) { - cache.setHidden(parseGcCustomDate(hiddenString)); + cache.setHidden(Login.parseGcCustomDate(hiddenString)); } if (cache.getHiddenDate() == null) { // event date hiddenString = BaseUtils.getMatch(tableInside, GCConstants.PATTERN_HIDDENEVENT, true, null); if (StringUtils.isNotBlank(hiddenString)) { - cache.setHidden(parseGcCustomDate(hiddenString)); + cache.setHidden(Login.parseGcCustomDate(hiddenString)); } } } catch (ParseException e) { @@ -1105,7 +800,7 @@ public class cgBase { } sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_elevation); if (cache.getCoords() != null) { - cache.setElevation(getElevation(cache.getCoords())); + cache.setElevation(cache.getCoords().getElevation()); } } @@ -1152,7 +847,7 @@ public class cgBase { // "sp", Boolean.toString(personal), // personal logs "sf", Boolean.toString(friends)); - final HttpResponse response = request("http://www.geocaching.com/seek/geocache.logbook", params, false, false, false); + final HttpResponse response = Network.request("http://www.geocaching.com/seek/geocache.logbook", params, false, false, false); if (response == null) { Log.e(Settings.tag, "cgBase.loadLogsFromDetails: cannot log logs, response is null"); return null; @@ -1162,7 +857,7 @@ public class cgBase { Log.e(Settings.tag, "cgBase.loadLogsFromDetails: error " + statusCode + " when requesting log information"); return null; } - rawResponse = cgBase.getResponseData(response); + rawResponse = Network.getResponseData(response); if (rawResponse == null) { Log.e(Settings.tag, "cgBase.loadLogsFromDetails: unable to read whole response"); return null; @@ -1194,7 +889,7 @@ public class cgBase { logDone.type = LogType.getByIconName(logIconName); try { - logDone.date = parseGcCustomDate(entry.getString("Visited")).getTime(); + logDone.date = Login.parseGcCustomDate(entry.getString("Visited")).getTime(); } catch (ParseException e) { Log.e(Settings.tag, "cgBase.loadLogsFromDetails: failed to parse log date."); } @@ -1267,73 +962,6 @@ public class cgBase { } } - public static Date parseGcCustomDate(final String input, final String format) throws ParseException { - if (StringUtils.isBlank(input)) { - throw new ParseException("Input is null", 0); - } - - final String trimmed = input.trim(); - - if (gcCustomDateFormats.containsKey(format)) { - try { - return gcCustomDateFormats.get(format).parse(trimmed); - } catch (ParseException e) { - } - } - - for (SimpleDateFormat sdf : gcCustomDateFormats.values()) { - try { - return sdf.parse(trimmed); - } catch (ParseException e) { - } - } - - throw new ParseException("No matching pattern", 0); - } - - public static Date parseGcCustomDate(final String input) throws ParseException { - return parseGcCustomDate(input, Settings.getGcCustomDate()); - } - - /** - * Detect user date settings on geocaching.com - */ - public static void detectGcCustomDate() { - - final String result = getResponseData(request("http://www.geocaching.com/account/ManagePreferences.aspx", null, false, false, false)); - - if (null == result) { - Log.w(Settings.tag, "cgeoBase.detectGcCustomDate: result is null"); - return; - } - - String customDate = BaseUtils.getMatch(result, GCConstants.PATTERN_CUSTOMDATE, true, null); - if (null != customDate) { - Settings.setGcCustomDate(customDate); - } - } - - public static BitmapDrawable downloadAvatarAndGetMemberStatus(final Context context) { - try { - final String profile = BaseUtils.replaceWhitespace(getResponseData(request("http://www.geocaching.com/my/", null, false))); - - Settings.setMemberStatus(BaseUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null)); - - setActualCachesFound(Integer.parseInt(BaseUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", ""))); - - final String avatarURL = BaseUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null); - if (null != avatarURL) { - final HtmlImage imgGetter = new HtmlImage(context, "", false, 0, false); - return imgGetter.getDrawable(avatarURL); - } - // No match? There may be no avatar set by user. - Log.d(Settings.tag, "No avatar set for user"); - } catch (Exception e) { - Log.w(Settings.tag, "Error when retrieving user avatar", e); - } - return null; - } - /** * Parse a trackable HTML description into a cgTrackable object * @@ -1479,7 +1107,7 @@ public class cgBase { try { - logDone.date = parseGcCustomDate(matcherLogs.group(2)).getTime(); + logDone.date = Login.parseGcCustomDate(matcherLogs.group(2)).getTime(); } catch (ParseException e) { } @@ -1648,13 +1276,13 @@ public class cgBase { final Parameters params = new Parameters( "__EVENTTARGET", "ctl00$ContentBody$pgrBottom$ctl08", "__EVENTARGUMENT", ""); - putViewstates(params, viewstates); + Login.putViewstates(params, viewstates); - String page = getResponseData(postRequest(uri, params)); - if (!getLoginStatus(page)) { - final StatusCode loginState = login(); + String page = Network.getResponseData(Network.postRequest(uri, params)); + if (!Login.getLoginStatus(page)) { + final StatusCode loginState = Login.login(); if (loginState == StatusCode.NO_ERROR) { - page = getResponseData(postRequest(uri, params)); + page = Network.getResponseData(Network.postRequest(uri, params)); } else if (loginState == StatusCode.NO_LOGIN_INFO_STORED) { Log.i(Settings.tag, "Working as guest."); } else { @@ -1725,7 +1353,7 @@ public class cgBase { final String uri = "http://www.geocaching.com/seek/nearest.aspx"; final String fullUri = uri + "?" + addFToParams(params, false, true); - String page = requestLogged(uri, params, false, my, true); + String page = Network.requestLogged(uri, params, false, my, true); if (StringUtils.isBlank(page)) { Log.e(Settings.tag, "cgeoBase.searchByAny: No data from server"); @@ -1740,7 +1368,7 @@ public class cgBase { final SearchResult search = searchResult.filterSearchResults(Settings.isExcludeDisabledCaches(), false, cacheType); - getLoginStatus(page); + Login.getLoginStatus(page); return search; } @@ -1787,30 +1415,6 @@ public class cgBase { return searchByAny(thread, cacheType, false, showCaptcha, params); } - /** Request .png image for a tile. */ - public static Bitmap requestMapTile(final String url, final String referer) { - final HttpGet request = new HttpGet(url); - request.addHeader("Accept", "image/png,image/*;q=0.8,*/*;q=0.5"); - request.addHeader("Referer", referer); - request.addHeader("X-Requested-With", "XMLHttpRequest"); - final HttpResponse response = request(request); - try { - return response != null ? BitmapFactory.decodeStream(response.getEntity().getContent()) : null; - } catch (IOException e) { - Log.e(Settings.tag, "cgBase.requestMapTile() " + e.getMessage()); - } - return null; - } - - /** Request JSON informations for a tile */ - public static String requestMapInfo(final String url, final String referer) { - final HttpGet request = new HttpGet(url); - request.addHeader("Accept", "application/json, text/javascript, */*; q=0.01"); - request.addHeader("Referer", referer); - request.addHeader("X-Requested-With", "XMLHttpRequest"); - return getResponseData(request(request), false); - } - public static cgTrackable searchTrackable(final String geocode, final String guid, final String id) { if (StringUtils.isBlank(geocode) && StringUtils.isBlank(guid) && StringUtils.isBlank(id)) { Log.w(Settings.tag, "cgeoBase.searchTrackable: No geocode nor guid nor id given"); @@ -1829,7 +1433,7 @@ public class cgBase { params.put("id", id); } - String page = requestLogged("http://www.geocaching.com/track/details.aspx", params, false, false, false); + String page = Network.requestLogged("http://www.geocaching.com/track/details.aspx", params, false, false, false); if (StringUtils.isBlank(page)) { Log.e(Settings.tag, "cgeoBase.searchTrackable: No data from server"); @@ -1894,7 +1498,7 @@ public class cgBase { "ctl00$ContentBody$LogBookPanel1$uxLogInfo", logInfo, "ctl00$ContentBody$LogBookPanel1$LogButton", "Submit Log Entry", "ctl00$ContentBody$uxVistOtherListingGC", ""); - putViewstates(params, viewstates); + Login.putViewstates(params, viewstates); if (trackables != null && !trackables.isEmpty()) { // we have some trackables to proceed final StringBuilder hdnSelected = new StringBuilder(); @@ -1911,11 +1515,11 @@ public class cgBase { } final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/seek/log.aspx").encodedQuery("ID=" + cacheid).build().toString(); - String page = getResponseData(postRequest(uri, params)); - if (!getLoginStatus(page)) { - final StatusCode loginState = login(); + String page = Network.getResponseData(Network.postRequest(uri, params)); + if (!Login.getLoginStatus(page)) { + final StatusCode loginState = Login.login(); if (loginState == StatusCode.NO_ERROR) { - page = getResponseData(postRequest(uri, params)); + page = Network.getResponseData(Network.postRequest(uri, params)); } else { Log.e(Settings.tag, "cgeoBase.postLog: Can not log in geocaching (error: " + loginState + ")"); return loginState; @@ -1933,7 +1537,7 @@ public class cgBase { try { if (matcher.find() && matcher.groupCount() > 0) { - final String[] viewstatesConfirm = getViewstates(page); + final String[] viewstatesConfirm = Login.getViewstates(page); if (isEmpty(viewstatesConfirm)) { Log.e(Settings.tag, "cgeoBase.postLog: No viewstate for confirm log"); @@ -1941,7 +1545,7 @@ public class cgBase { } params.clear(); - putViewstates(params, viewstatesConfirm); + Login.putViewstates(params, viewstatesConfirm); params.put("__EVENTTARGET", ""); params.put("__EVENTARGUMENT", ""); params.put("__LASTFOCUS", ""); @@ -1972,7 +1576,7 @@ public class cgBase { params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnCurrentFilter", ""); } - page = getResponseData(postRequest(uri, params)); + page = Network.getResponseData(Network.postRequest(uri, params)); } } catch (Exception e) { Log.e(Settings.tag, "cgeoBase.postLog.confim: " + e.toString()); @@ -1988,10 +1592,10 @@ public class cgBase { cgeoapplication.getInstance().saveVisitDate(geocode); } - getLoginStatus(page); + Login.getLoginStatus(page); // the log-successful-page contains still the old value - if (getActualCachesFound() >= 0) { - setActualCachesFound(getActualCachesFound() + 1); + if (Login.getActualCachesFound() >= 0) { + Login.setActualCachesFound(Login.getActualCachesFound() + 1); } return StatusCode.NO_ERROR; } @@ -2026,7 +1630,7 @@ public class cgBase { "__LASTFOCUS", "", "ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType.id), "ctl00$ContentBody$LogBookPanel1$tbCode", trackingCode); - putViewstates(params, viewstates); + Login.putViewstates(params, viewstates); if (currentDate.get(Calendar.YEAR) == year && (currentDate.get(Calendar.MONTH) + 1) == month && currentDate.get(Calendar.DATE) == day) { params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", ""); } else { @@ -2041,11 +1645,11 @@ public class cgBase { "ctl00$ContentBody$uxVistOtherListingGC", ""); final String uri = new Uri.Builder().scheme("http").authority("www.geocaching.com").path("/track/log.aspx").encodedQuery("wid=" + tbid).build().toString(); - String page = getResponseData(postRequest(uri, params)); - if (!getLoginStatus(page)) { - final StatusCode loginState = login(); + String page = Network.getResponseData(Network.postRequest(uri, params)); + if (!Login.getLoginStatus(page)) { + final StatusCode loginState = Login.login(); if (loginState == StatusCode.NO_ERROR) { - page = getResponseData(postRequest(uri, params)); + page = Network.getResponseData(Network.postRequest(uri, params)); } else { Log.e(Settings.tag, "cgeoBase.postLogTrackable: Can not log in geocaching (error: " + loginState + ")"); return loginState; @@ -2081,7 +1685,7 @@ public class cgBase { */ public static int addToWatchlist(final cgCache cache) { final String uri = "http://www.geocaching.com/my/watchlist.aspx?w=" + cache.getCacheId(); - String page = postRequestLogged(uri); + String page = Network.postRequestLogged(uri); if (StringUtils.isBlank(page)) { Log.e(Settings.tag, "cgBase.addToWatchlist: No data from server"); @@ -2107,7 +1711,7 @@ public class cgBase { */ public static int removeFromWatchlist(final cgCache cache) { final String uri = "http://www.geocaching.com/my/watchlist.aspx?ds=1&action=rem&id=" + cache.getCacheId(); - String page = postRequestLogged(uri); + String page = Network.postRequestLogged(uri); if (StringUtils.isBlank(page)) { Log.e(Settings.tag, "cgBase.removeFromWatchlist: No data from server"); @@ -2119,9 +1723,9 @@ public class cgBase { "__EVENTTARGET", "", "__EVENTARGUMENT", "", "ctl00$ContentBody$btnYes", "Yes"); - transferViewstates(page, params); + Login.transferViewstates(page, params); - page = getResponseData(postRequest(uri, params)); + page = Network.getResponseData(Network.postRequest(uri, params)); boolean guidOnPage = cache.isGuidContainedInPage(page); if (!guidOnPage) { Log.i(Settings.tag, "cgBase.removeFromWatchlist: cache removed from watchlist"); @@ -2132,75 +1736,6 @@ public class cgBase { return guidOnPage ? -1 : 0; // on watchlist (=error) / not on watchlist } - final public static HostnameVerifier doNotVerify = new HostnameVerifier() { - - public boolean verify(String hostname, SSLSession session) { - return true; - } - }; - - public static void postTweetCache(String geocode) { - final cgCache cache = cgeoapplication.getInstance().loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); - String status; - final String url = cache.getUrl(); - if (url.length() >= 100) { - status = "I found " + url; - } - else { - String name = cache.getName(); - status = "I found " + name + " (" + url + ")"; - if (status.length() > Twitter.MAX_TWEET_SIZE) { - name = name.substring(0, name.length() - (status.length() - Twitter.MAX_TWEET_SIZE) - 3) + "..."; - } - status = "I found " + name + " (" + url + ")"; - status = Twitter.appendHashTag(status, "cgeo"); - status = Twitter.appendHashTag(status, "geocaching"); - } - - Twitter.postTweet(cgeoapplication.getInstance(), status, null); - } - - public static void postTweetTrackable(String geocode) { - final cgTrackable trackable = cgeoapplication.getInstance().getTrackableByGeocode(geocode); - String name = trackable.getName(); - if (name.length() > 82) { - name = name.substring(0, 79) + "..."; - } - StringBuilder builder = new StringBuilder("I touched "); - builder.append(name); - if (trackable.getUrl() != null) { - builder.append(" (").append(trackable.getUrl()).append(')'); - } - builder.append('!'); - String status = Twitter.appendHashTag(builder.toString(), "cgeo"); - status = Twitter.appendHashTag(status, "geocaching"); - Twitter.postTweet(cgeoapplication.getInstance(), status, null); - } - - public static String getLocalIpAddress() { - try { - for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { - NetworkInterface intf = en.nextElement(); - for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { - InetAddress inetAddress = enumIpAddr.nextElement(); - if (!inetAddress.isLoopbackAddress()) { - return inetAddress.getHostAddress(); - } - } - } - } catch (SocketException e) { - // nothing - } - - return null; - } - - public static String urlencode_rfc3986(String text) { - final String encoded = StringUtils.replace(URLEncoder.encode(text).replace("+", "%20"), "%7E", "~"); - - return encoded; - } - /** * Possibly hide caches found or hidden by user. This mutates its params argument when possible. * @@ -2222,324 +1757,6 @@ public class cgBase { return params; } - static private String prepareParameters(final String baseUri, final Parameters params) { - return CollectionUtils.isNotEmpty(params) ? baseUri + "?" + params.toString() : baseUri; - } - - static public String[] requestViewstates(final String uri, final Parameters params, boolean xContentType, boolean my) { - final HttpResponse response = request(uri, params, xContentType, my, false); - - return getViewstates(getResponseData(response)); - } - - static private String getResponseDataNoError(final HttpResponse response, boolean replaceWhitespace) { - try { - String data = EntityUtils.toString(response.getEntity(), HTTP.UTF_8); - return replaceWhitespace ? BaseUtils.replaceWhitespace(data) : data; - } catch (Exception e) { - Log.e(Settings.tag, "getResponseData", e); - return null; - } - } - - static public String getResponseData(final HttpResponse response) { - return getResponseData(response, true); - } - - static public String getResponseData(final HttpResponse response, boolean replaceWhitespace) { - if (!isSuccess(response)) { - return null; - } - return getResponseDataNoError(response, replaceWhitespace); - } - - /** - * POST HTTP request. Do the request a second time if the user is not logged in - * - * @param uri - * @return - */ - public static String postRequestLogged(final String uri) { - HttpResponse response = postRequest(uri, null); - String data = getResponseData(response); - - if (!getLoginStatus(data)) { - if (login() == StatusCode.NO_ERROR) { - response = postRequest(uri, null); - data = getResponseData(response); - } else { - Log.i(Settings.tag, "Working as guest."); - } - } - return data; - } - - /** - * GET HTTP request. Do the request a second time if the user is not logged in - * - * @param uri - * @param params - * @param xContentType - * @param my - * @param addF - * @return - */ - public static String requestLogged(final String uri, final Parameters params, boolean xContentType, boolean my, boolean addF) { - HttpResponse response = request(uri, params, xContentType, my, addF); - String data = getResponseData(response); - - if (!getLoginStatus(data)) { - if (login() == StatusCode.NO_ERROR) { - response = request(uri, params, xContentType, my, addF); - data = getResponseData(response); - } else { - Log.i(Settings.tag, "Working as guest."); - } - } - return data; - } - - /** - * GET HTTP request - * - * @param uri - * @param params - * @param xContentType - * @param my - * @param addF - * @return - */ - public static HttpResponse request(final String uri, final Parameters params, boolean xContentType, boolean my, boolean addF) { - return request(uri, addFToParams(params, my, addF), xContentType); - } - - final private static CookieStore cookieStore = new BasicCookieStore(); - private static boolean cookieStoreRestored = false; - final private static HttpParams clientParams = new BasicHttpParams(); - - static { - clientParams.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, HTTP.UTF_8); - clientParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 30000); - clientParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, 30000); - } - - public static HttpClient getHttpClient() { - final DefaultHttpClient client = new DefaultHttpClient(); - client.setCookieStore(cookieStore); - client.setParams(clientParams); - - client.addRequestInterceptor(new HttpRequestInterceptor() { - - @Override - public void process( - final HttpRequest request, - final HttpContext context) throws HttpException, IOException { - if (!request.containsHeader("Accept-Encoding")) { - request.addHeader("Accept-Encoding", "gzip"); - } - } - }); - client.addResponseInterceptor(new HttpResponseInterceptor() { - - public void process( - final HttpResponse response, - final HttpContext context) throws HttpException, IOException { - HttpEntity entity = response.getEntity(); - if (null != entity) { - Header ceheader = entity.getContentEncoding(); - if (ceheader != null) { - HeaderElement[] codecs = ceheader.getElements(); - for (int i = 0; i < codecs.length; i++) { - if (codecs[i].getName().equalsIgnoreCase("gzip")) { - Log.d(Settings.tag, "Decompressing response"); - response.setEntity( - new GzipDecompressingEntity(response.getEntity())); - return; - } - } - } - } - } - - }); - - return client; - } - - static class GzipDecompressingEntity extends HttpEntityWrapper { - public GzipDecompressingEntity(final HttpEntity entity) { - super(entity); - } - - @Override - public InputStream getContent() throws IOException, IllegalStateException { - // the wrapped entity's getContent() decides about repeatability - InputStream wrappedin = wrappedEntity.getContent(); - return new GZIPInputStream(wrappedin); - } - - @Override - public long getContentLength() { - // length of ungzipped content is not known - return -1; - } - } - - public static void restoreCookieStore(final String oldCookies) { - if (!cookieStoreRestored) { - clearCookies(); - if (oldCookies != null) { - for (final String cookie : StringUtils.split(oldCookies, ';')) { - final String[] split = StringUtils.split(cookie, "=", 3); - if (split.length == 3) { - final BasicClientCookie newCookie = new BasicClientCookie(split[0], split[1]); - newCookie.setDomain(split[2]); - cookieStore.addCookie(newCookie); - } - } - } - cookieStoreRestored = true; - } - } - - public static String dumpCookieStore() { - StringBuilder cookies = new StringBuilder(); - for (final Cookie cookie : cookieStore.getCookies()) { - cookies.append(cookie.getName()); - cookies.append('='); - cookies.append(cookie.getValue()); - cookies.append('='); - cookies.append(cookie.getDomain()); - cookies.append(';'); - } - return cookies.toString(); - } - - public static void clearCookies() { - cookieStore.clear(); - } - - /** - * POST HTTP request - * - * @param uri - * @param params - * @return - */ - public static HttpResponse postRequest(final String uri, final List<? extends NameValuePair> params) { - try { - HttpPost request = new HttpPost(uri); - if (params != null) { - request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); - } - request.setHeader("X-Requested-With", "XMLHttpRequest"); - return request(request); - } catch (Exception e) { - // Can be UnsupportedEncodingException, ClientProtocolException or IOException - Log.e(Settings.tag, "postRequest", e); - return null; - } - } - - /** - * GET HTTP request - * - * @param uri - * @param params - * @param xContentType - * @return - */ - public static HttpResponse request(final String uri, final Parameters params, final boolean xContentType) { - final String fullUri = params == null ? uri : Uri.parse(uri).buildUpon().encodedQuery(params.toString()).build().toString(); - final HttpRequestBase request = new HttpGet(fullUri); - - request.setHeader("X-Requested-With", "XMLHttpRequest"); - - if (xContentType) { - request.setHeader("Content-Type", "application/x-www-form-urlencoded"); - } - - return request(request); - } - - private static HttpResponse request(final HttpRequestBase request) { - request.setHeader("Accept-Charset", "utf-8,iso-8859-1;q=0.8,utf-16;q=0.8,*;q=0.7"); - request.setHeader("Accept-Language", "en-US,*;q=0.9"); - request.getParams().setParameter(CoreProtocolPNames.USER_AGENT, cgBase.USER_AGENT); - return doRequest(request); - } - - static private String formatTimeSpan(final long before) { - // don't use String.format in a pure logging routine, it has very bad performance - return " (" + (System.currentTimeMillis() - before) + " ms) "; - } - - static public boolean isSuccess(final HttpResponse response) { - return response != null && response.getStatusLine().getStatusCode() == 200; - } - - static public HttpResponse doRequest(final HttpRequestBase request) { - final String reqLogStr = request.getMethod() + " " + hidePassword(request.getURI().toString()); - Log.d(Settings.tag, reqLogStr); - - final HttpClient client = getHttpClient(); - for (int i = 0; i <= NB_DOWNLOAD_RETRIES; i++) { - final long before = System.currentTimeMillis(); - try { - final HttpResponse response = client.execute(request); - int status = response.getStatusLine().getStatusCode(); - if (status == 200) { - Log.d(Settings.tag, status + formatTimeSpan(before) + reqLogStr); - } else { - Log.w(Settings.tag, status + " [" + response.getStatusLine().getReasonPhrase() + "]" + formatTimeSpan(before) + reqLogStr); - } - return response; - } catch (IOException e) { - final String timeSpan = formatTimeSpan(before); - final String tries = (i + 1) + "/" + (NB_DOWNLOAD_RETRIES + 1); - if (i == NB_DOWNLOAD_RETRIES) { - Log.e(Settings.tag, "Failure " + tries + timeSpan + reqLogStr, e); - } else { - Log.e(Settings.tag, "Failure " + tries + " (" + e.toString() + ")" + timeSpan + "- retrying " + reqLogStr); - } - } - } - - return null; - } - - public static JSONObject requestJSON(final String uri, final Parameters params) { - final HttpGet request = new HttpGet(prepareParameters(uri, params)); - request.setHeader("Accept", "application/json, text/javascript, */*; q=0.01"); - request.setHeader("Content-Type", "application/json; charset=UTF-8"); - request.setHeader("X-Requested-With", "XMLHttpRequest"); - - final HttpResponse response = doRequest(request); - if (response != null && response.getStatusLine().getStatusCode() == 200) { - try { - return new JSONObject(getResponseData(response)); - } catch (JSONException e) { - Log.e(Settings.tag, "cgeoBase.requestJSON", e); - } - } - - return null; - } - - public static boolean deleteDirectory(File path) { - if (path.exists()) { - for (final File file : path.listFiles()) { - if (file.isDirectory()) { - deleteDirectory(file); - } else { - file.delete(); - } - } - } - - return path.delete(); - } - public static void storeCache(Activity activity, cgCache origCache, String geocode, int listId, CancellableHandler handler) { try { cgCache cache; @@ -2685,34 +1902,6 @@ public class cgBase { return false; } - public static Double getElevation(final Geopoint coords) { - try { - final String uri = "http://maps.googleapis.com/maps/api/elevation/json"; - final Parameters params = new Parameters( - "sensor", "false", - "locations", coords.format(Format.LAT_LON_DECDEGREE_COMMA)); - final JSONObject response = requestJSON(uri, params); - - if (response == null) { - return null; - } - - if (!StringUtils.equalsIgnoreCase(response.getString("status"), "OK")) { - return null; - } - - if (response.has("results")) { - JSONArray results = response.getJSONArray("results"); - JSONObject result = results.getJSONObject(0); - return result.getDouble("elevation"); - } - } catch (Exception e) { - Log.w(Settings.tag, "cgBase.getElevation: " + e.toString()); - } - - return null; - } - /** * Generate a time string according to system-wide settings (locale, 12/24 hour) * such as "13:24". @@ -2779,94 +1968,6 @@ public class cgBase { } /** - * insert text into the EditText at the current cursor position - * - * @param editText - * @param insertText - * @param moveCursor - * place the cursor after the inserted text - */ - public static void insertAtPosition(final EditText editText, final String insertText, final boolean moveCursor) { - int selectionStart = editText.getSelectionStart(); - int selectionEnd = editText.getSelectionEnd(); - int start = Math.min(selectionStart, selectionEnd); - int end = Math.max(selectionStart, selectionEnd); - - final String content = editText.getText().toString(); - String completeText; - if (start > 0 && !Character.isWhitespace(content.charAt(start - 1))) { - completeText = " " + insertText; - } else { - completeText = insertText; - } - - editText.getText().replace(start, end, completeText); - int newCursor = moveCursor ? start + completeText.length() : start; - editText.setSelection(newCursor, newCursor); - } - - public static LinearLayout createStarRating(final float value, final int count, final Context context) { - LayoutInflater inflater = LayoutInflater.from(context); - LinearLayout starsContainer = new LinearLayout(context); - starsContainer.setOrientation(LinearLayout.HORIZONTAL); - - for (int i = 0; i < count; i++) { - ImageView star = (ImageView) inflater.inflate(R.layout.star, null); - 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); - } - - return starsContainer; - } - - public static boolean isActualLoginStatus() { - return actualLoginStatus; - } - - private static void setActualLoginStatus(boolean actualLoginStatus) { - cgBase.actualLoginStatus = actualLoginStatus; - } - - public static String getActualUserName() { - return actualUserName; - } - - private static void setActualUserName(String actualUserName) { - cgBase.actualUserName = actualUserName; - } - - public static String getActualMemberStatus() { - return actualMemberStatus; - } - - private static void setActualMemberStatus(final String actualMemberStatus) { - cgBase.actualMemberStatus = actualMemberStatus; - } - - public static int getActualCachesFound() { - return actualCachesFound; - } - - private static void setActualCachesFound(final int actualCachesFound) { - cgBase.actualCachesFound = actualCachesFound; - } - - public static String getActualStatus() { - return actualStatus; - } - - private static void setActualStatus(final String actualStatus) { - cgBase.actualStatus = actualStatus; - } - - - /** * Indicates whether the specified action can be used as an intent. This * method queries the package manager for installed packages that can * respond to an intent with the specified action. If no suitable package is diff --git a/main/src/cgeo/geocaching/cgData.java b/main/src/cgeo/geocaching/cgData.java index ac0be8f..f6d17c7 100644 --- a/main/src/cgeo/geocaching/cgData.java +++ b/main/src/cgeo/geocaching/cgData.java @@ -969,7 +969,7 @@ public class cgData { public void run() { for (final File dir : toRemove) { Log.i(Settings.tag, "Removing obsolete cache directory for " + dir.getName()); - cgBase.deleteDirectory(dir); + LocalStorage.deleteDirectory(dir); } } }).start(); @@ -3047,7 +3047,7 @@ public class cgData { // Delete cache directories for (final String geocode : geocodes) { - cgBase.deleteDirectory(LocalStorage.getStorageDir(geocode)); + LocalStorage.deleteDirectory(LocalStorage.getStorageDir(geocode)); } } } diff --git a/main/src/cgeo/geocaching/cgeo.java b/main/src/cgeo/geocaching/cgeo.java index 03184b3..7367d82 100644 --- a/main/src/cgeo/geocaching/cgeo.java +++ b/main/src/cgeo/geocaching/cgeo.java @@ -12,6 +12,7 @@ import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.HumanDistance; import cgeo.geocaching.geopoint.IConversion; import cgeo.geocaching.maps.CGeoMap; +import cgeo.geocaching.network.Login; import cgeo.geocaching.ui.Formatter; import org.apache.commons.collections.CollectionUtils; @@ -77,14 +78,14 @@ public class cgeo extends AbstractActivity { TextView userInfoView = (TextView) findViewById(R.id.user_info); StringBuilder userInfo = new StringBuilder("geocaching.com").append(Formatter.SEPARATOR); - if (cgBase.isActualLoginStatus()) { - userInfo.append(cgBase.getActualUserName()); - if (cgBase.getActualCachesFound() >= 0) { - userInfo.append(" (").append(String.valueOf(cgBase.getActualCachesFound())).append(')'); + if (Login.isActualLoginStatus()) { + userInfo.append(Login.getActualUserName()); + if (Login.getActualCachesFound() >= 0) { + userInfo.append(" (").append(String.valueOf(Login.getActualCachesFound())).append(')'); } userInfo.append(Formatter.SEPARATOR); } - userInfo.append(cgBase.getActualStatus()); + userInfo.append(Login.getActualStatus()); userInfoView.setText(userInfo.toString()); } @@ -790,11 +791,11 @@ public class cgeo extends AbstractActivity { } // login - final StatusCode status = cgBase.login(); + final StatusCode status = Login.login(); if (status == StatusCode.NO_ERROR) { app.firstRun = false; - cgBase.detectGcCustomDate(); + Login.detectGcCustomDate(); updateUserInfoHandler.sendEmptyMessage(-1); } diff --git a/main/src/cgeo/geocaching/cgeocaches.java b/main/src/cgeo/geocaching/cgeocaches.java index b38c466..f1d332c 100644 --- a/main/src/cgeo/geocaching/cgeocaches.java +++ b/main/src/cgeo/geocaching/cgeocaches.java @@ -21,6 +21,7 @@ import cgeo.geocaching.filter.TrackablesFilter; import cgeo.geocaching.filter.TypeFilter; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.maps.CGeoMap; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.sorting.CacheComparator; import cgeo.geocaching.sorting.DateComparator; @@ -205,14 +206,14 @@ public class cgeocaches extends AbstractListActivity { dialog.setNegativeButton(res.getString(R.string.license_dismiss), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - cgBase.clearCookies(); + Network.clearCookies(); dialog.cancel(); } }); dialog.setPositiveButton(res.getString(R.string.license_show), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - cgBase.clearCookies(); + Network.clearCookies(); startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/software/agreement.aspx?ID=0"))); } }); @@ -1903,10 +1904,10 @@ public class cgeocaches extends AbstractListActivity { deviceCode = ""; } final Parameters params = new Parameters("code", deviceCode); - HttpResponse responseFromWeb = cgBase.request("http://send2.cgeo.org/read.html", params, true); + HttpResponse responseFromWeb = Network.request("http://send2.cgeo.org/read.html", params, true); if (responseFromWeb != null && responseFromWeb.getStatusLine().getStatusCode() == 200) { - final String response = cgBase.getResponseData(responseFromWeb); + final String response = Network.getResponseData(responseFromWeb); if (response.length() > 2) { String GCcode = response; diff --git a/main/src/cgeo/geocaching/cgeoinit.java b/main/src/cgeo/geocaching/cgeoinit.java index 1a694bd..90b0316 100644 --- a/main/src/cgeo/geocaching/cgeoinit.java +++ b/main/src/cgeo/geocaching/cgeoinit.java @@ -7,6 +7,8 @@ import cgeo.geocaching.compatibility.Compatibility; import cgeo.geocaching.enumerations.CacheType; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.maps.MapProviderFactory; +import cgeo.geocaching.network.Login; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.twitter.TwitterAuthorizationActivity; import cgeo.geocaching.utils.LogTemplateProvider; @@ -213,7 +215,7 @@ public class cgeoinit extends AbstractActivity { private boolean insertSignatureTemplate(final LogTemplate template) { EditText sig = (EditText) findViewById(R.id.signature); String insertText = "[" + template.getTemplateString() + "]"; - cgBase.insertAtPosition(sig, insertText, true); + insertAtPosition(sig, insertText, true); return true; } @@ -807,17 +809,17 @@ public class cgeoinit extends AbstractActivity { loginDialog.setCancelable(false); Settings.setLogin(username, password); - cgBase.clearCookies(); + Network.clearCookies(); (new Thread() { @Override public void run() { - final StatusCode loginResult = cgBase.login(); + final StatusCode loginResult = Login.login(); Object payload = loginResult; if (loginResult == StatusCode.NO_ERROR) { - cgBase.detectGcCustomDate(); - payload = cgBase.downloadAvatarAndGetMemberStatus(cgeoinit.this); + Login.detectGcCustomDate(); + payload = Login.downloadAvatarAndGetMemberStatus(cgeoinit.this); } logInHandler.obtainMessage(0, payload).sendToTarget(); } @@ -849,12 +851,12 @@ public class cgeoinit extends AbstractActivity { final String cod = StringUtils.defaultString(deviceCode); final Parameters params = new Parameters("name", nam, "code", cod); - HttpResponse response = cgBase.request("http://send2.cgeo.org/auth.html", params, true); + HttpResponse response = Network.request("http://send2.cgeo.org/auth.html", params, true); if (response != null && response.getStatusLine().getStatusCode() == 200) { //response was OK - String[] strings = cgBase.getResponseData(response).split(","); + String[] strings = Network.getResponseData(response).split(","); try { pin = Integer.parseInt(strings[1].trim()); } catch (Exception e) { diff --git a/main/src/cgeo/geocaching/cgeopopup.java b/main/src/cgeo/geocaching/cgeopopup.java index ac74519..91546b8 100644 --- a/main/src/cgeo/geocaching/cgeopopup.java +++ b/main/src/cgeo/geocaching/cgeopopup.java @@ -605,7 +605,7 @@ public class cgeopopup extends AbstractActivity { itemName.setText(res.getString(R.string.cache_rating)); itemValue.setText(String.format("%.1f", rating) + ' ' + res.getString(R.string.cache_rating_of) + " 5"); - itemStars.addView(cgBase.createStarRating(rating, 5, this), 1); + itemStars.addView(createStarRating(rating, 5, this), 1); if (votes > 0) { final TextView itemAddition = (TextView) itemLayout.findViewById(R.id.addition); diff --git a/main/src/cgeo/geocaching/cgeotouch.java b/main/src/cgeo/geocaching/cgeotouch.java index 6271ec1..e5af4d3 100644 --- a/main/src/cgeo/geocaching/cgeotouch.java +++ b/main/src/cgeo/geocaching/cgeotouch.java @@ -3,7 +3,10 @@ package cgeo.geocaching; import cgeo.geocaching.activity.AbstractActivity; import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.network.Login; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; +import cgeo.geocaching.twitter.Twitter; import cgeo.geocaching.ui.DateDialog; import org.apache.commons.lang3.StringUtils; @@ -389,9 +392,9 @@ public class cgeotouch extends AbstractActivity implements DateDialog.DateDialog return; } - final String page = cgBase.getResponseData(cgBase.request("http://www.geocaching.com/track/log.aspx", params, false, false, false)); + final String page = Network.getResponseData(Network.request("http://www.geocaching.com/track/log.aspx", params, false, false, false)); - viewstates = cgBase.getViewstates(page); + viewstates = Login.getViewstates(page); final List<LogType> typesPre = cgBase.parseTypes(page); if (typesPre.size() > 0) { @@ -444,7 +447,7 @@ public class cgeotouch extends AbstractActivity implements DateDialog.DateDialog if (status == StatusCode.NO_ERROR && Settings.isUseTwitter() && Settings.isTwitterLoginValid() && tweetCheck.isChecked() && tweetBox.getVisibility() == View.VISIBLE) { - cgBase.postTweetTrackable(geocode); + Twitter.postTweetTrackable(geocode); } return status; diff --git a/main/src/cgeo/geocaching/connector/gc/GCBase.java b/main/src/cgeo/geocaching/connector/gc/GCBase.java index e7008aa..7780ba2 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCBase.java +++ b/main/src/cgeo/geocaching/connector/gc/GCBase.java @@ -10,6 +10,7 @@ import cgeo.geocaching.enumerations.LiveMapStrategy.Strategy; import cgeo.geocaching.enumerations.LiveMapStrategy.StrategyFlag; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.network.Network; import cgeo.geocaching.utils.BaseUtils; import cgeo.geocaching.utils.LeastRecentlyUsedCache; @@ -136,12 +137,12 @@ public class GCBase { final String urlString = url.toString(); // The PNG must be request before ! Else the following request would return with 204 - No Content - Bitmap bitmap = cgBase.requestMapTile(GCConstants.URL_MAP_TILE + urlString, referer); + Bitmap bitmap = Tile.requestMapTile(GCConstants.URL_MAP_TILE + urlString, referer); assert bitmap.getWidth() == Tile.TILE_SIZE : "Bitmap has wrong width"; assert bitmap.getHeight() == Tile.TILE_SIZE : "Bitmap has wrong height"; - String data = cgBase.requestMapInfo(GCConstants.URL_MAP_INFO + urlString, referer); + String data = Tile.requestMapInfo(GCConstants.URL_MAP_INFO + urlString, referer); if (StringUtils.isEmpty(data)) { Log.e(Settings.tag, "GCBase.searchByViewport: No data from server for tile (" + tile.getX() + "/" + tile.getY() + ")"); } else { @@ -258,7 +259,7 @@ public class GCBase { for (String id : positions.keySet()) { List<UTFGridPosition> pos = positions.get(id); - UTFGridPosition xy = getPositionInGrid(pos); + UTFGridPosition xy = UTFGrid.getPositionInGrid(pos); cgCache cache = new cgCache(); cache.setDetailed(false); cache.setReliableLatLon(false); @@ -387,21 +388,6 @@ public class GCBase { return tiles; } - /** Calculate from a list of positions (x/y) the coords */ - protected static UTFGridPosition getPositionInGrid(List<UTFGridPosition> positions) { - int minX = UTFGrid.GRID_MAXX; - int maxX = 0; - int minY = UTFGrid.GRID_MAXY; - int maxY = 0; - for (UTFGridPosition pos : positions) { - minX = Math.min(minX, pos.x); - maxX = Math.max(maxX, pos.x); - minY = Math.min(minY, pos.y); - maxY = Math.max(maxY, pos.y); - } - return new UTFGridPosition((minX + maxX) / 2, (minY + maxY) / 2); - } - /** * Convert GCCode (geocode) to (old) GCIds * @@ -485,8 +471,8 @@ public class GCBase { /** Get user session & session token from the Live Map. Needed for following requests */ public static String[] getTokens() { - final HttpResponse response = cgBase.request(GCConstants.URL_LIVE_MAP, null, false); - final String data = cgBase.getResponseData(response); + final HttpResponse response = Network.request(GCConstants.URL_LIVE_MAP, null, false); + final String data = Network.getResponseData(response); String userSession = BaseUtils.getMatch(data, GCConstants.PATTERN_USERSESSION, ""); String sessionToken = BaseUtils.getMatch(data, GCConstants.PATTERN_SESSIONTOKEN, ""); return new String[] { userSession, sessionToken }; diff --git a/main/src/cgeo/geocaching/connector/gc/GCConnector.java b/main/src/cgeo/geocaching/connector/gc/GCConnector.java index 0e9ef1f..4f319f3 100644 --- a/main/src/cgeo/geocaching/connector/gc/GCConnector.java +++ b/main/src/cgeo/geocaching/connector/gc/GCConnector.java @@ -9,6 +9,7 @@ import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.connector.AbstractConnector; import cgeo.geocaching.enumerations.StatusCode; import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.utils.CancellableHandler; @@ -103,7 +104,7 @@ public class GCConnector extends AbstractConnector { cgBase.sendLoadProgressDetail(handler, R.string.cache_dialog_loading_details_status_loadpage); - final String page = cgBase.requestLogged("http://www.geocaching.com/seek/cache_details.aspx", params, false, false, false); + final String page = Network.requestLogged("http://www.geocaching.com/seek/cache_details.aspx", params, false, false, false); if (StringUtils.isEmpty(page)) { final SearchResult search = new SearchResult(); diff --git a/main/src/cgeo/geocaching/connector/gc/Tile.java b/main/src/cgeo/geocaching/connector/gc/Tile.java index 9d38a64..eec83b0 100644 --- a/main/src/cgeo/geocaching/connector/gc/Tile.java +++ b/main/src/cgeo/geocaching/connector/gc/Tile.java @@ -1,6 +1,17 @@ package cgeo.geocaching.connector.gc; +import cgeo.geocaching.Settings; import cgeo.geocaching.geopoint.Geopoint; +import cgeo.geocaching.network.Network; + +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.Log; + +import java.io.IOException; /** * All about tiles. @@ -200,4 +211,28 @@ public class Tile { public int hashCode() { return toString().hashCode(); } + + /** Request JSON informations for a tile */ + public static String requestMapInfo(final String url, final String referer) { + final HttpGet request = new HttpGet(url); + request.addHeader("Accept", "application/json, text/javascript, */*; q=0.01"); + request.addHeader("Referer", referer); + request.addHeader("X-Requested-With", "XMLHttpRequest"); + return Network.getResponseData(Network.request(request), false); + } + + /** Request .png image for a tile. */ + public static Bitmap requestMapTile(final String url, final String referer) { + final HttpGet request = new HttpGet(url); + request.addHeader("Accept", "image/png,image/*;q=0.8,*/*;q=0.5"); + request.addHeader("Referer", referer); + request.addHeader("X-Requested-With", "XMLHttpRequest"); + final HttpResponse response = Network.request(request); + try { + return response != null ? BitmapFactory.decodeStream(response.getEntity().getContent()) : null; + } catch (IOException e) { + Log.e(Settings.tag, "cgBase.requestMapTile() " + e.getMessage()); + } + return null; + } } diff --git a/main/src/cgeo/geocaching/connector/gc/UTFGrid.java b/main/src/cgeo/geocaching/connector/gc/UTFGrid.java index cf490ec..a663a71 100644 --- a/main/src/cgeo/geocaching/connector/gc/UTFGrid.java +++ b/main/src/cgeo/geocaching/connector/gc/UTFGrid.java @@ -1,5 +1,7 @@ package cgeo.geocaching.connector.gc; +import java.util.List; + /** * * @author blafoo @@ -28,4 +30,19 @@ public final class UTFGrid { return (short) (result - 32); } + /** Calculate from a list of positions (x/y) the coords */ + protected static UTFGridPosition getPositionInGrid(List<UTFGridPosition> positions) { + int minX = GRID_MAXX; + int maxX = 0; + int minY = GRID_MAXY; + int maxY = 0; + for (UTFGridPosition pos : positions) { + minX = Math.min(minX, pos.x); + maxX = Math.max(maxX, pos.x); + minY = Math.min(minY, pos.y); + maxY = Math.max(maxY, pos.y); + } + return new UTFGridPosition((minX + maxX) / 2, (minY + maxY) / 2); + } + } diff --git a/main/src/cgeo/geocaching/connector/opencaching/OkapiClient.java b/main/src/cgeo/geocaching/connector/opencaching/OkapiClient.java index a69d5c9..9c3ab4a 100644 --- a/main/src/cgeo/geocaching/connector/opencaching/OkapiClient.java +++ b/main/src/cgeo/geocaching/connector/opencaching/OkapiClient.java @@ -1,7 +1,6 @@ package cgeo.geocaching.connector.opencaching; import cgeo.geocaching.Settings; -import cgeo.geocaching.cgBase; import cgeo.geocaching.cgCache; import cgeo.geocaching.cgImage; import cgeo.geocaching.cgLog; @@ -13,6 +12,7 @@ import cgeo.geocaching.enumerations.LogType; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter; import cgeo.geocaching.geopoint.GeopointParser; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import org.apache.commons.lang3.StringUtils; @@ -304,6 +304,6 @@ final public class OkapiClient { final String uri = "http://" + host + service; ((ApiOpenCachingConnector) connector).addAuthentication(params); - return cgBase.requestJSON(uri, params); + return Network.requestJSON(uri, params); } } diff --git a/main/src/cgeo/geocaching/files/LocalStorage.java b/main/src/cgeo/geocaching/files/LocalStorage.java index 0ba663d..b5fa5f6 100644 --- a/main/src/cgeo/geocaching/files/LocalStorage.java +++ b/main/src/cgeo/geocaching/files/LocalStorage.java @@ -257,4 +257,18 @@ public class LocalStorage { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } + public static boolean deleteDirectory(File path) { + if (path.exists()) { + for (final File file : path.listFiles()) { + if (file.isDirectory()) { + deleteDirectory(file); + } else { + file.delete(); + } + } + } + + return path.delete(); + } + } diff --git a/main/src/cgeo/geocaching/gcvote/GCVote.java b/main/src/cgeo/geocaching/gcvote/GCVote.java index c98d4d9..fa2af0d 100644 --- a/main/src/cgeo/geocaching/gcvote/GCVote.java +++ b/main/src/cgeo/geocaching/gcvote/GCVote.java @@ -1,8 +1,8 @@ package cgeo.geocaching.gcvote; import cgeo.geocaching.Settings; -import cgeo.geocaching.cgBase; import cgeo.geocaching.cgCache; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.utils.LeastRecentlyUsedCache; @@ -96,7 +96,7 @@ public final class GCVote { params.put("waypoints", StringUtils.join(geocodes.toArray(), ',')); } params.put("version", "cgeo"); - final String page = cgBase.getResponseData(cgBase.request("http://gcvote.com/getVotes.php", params, false, false, false)); + final String page = Network.getResponseData(Network.request("http://gcvote.com/getVotes.php", params, false, false, false)); if (page == null) { return null; } @@ -220,7 +220,7 @@ public final class GCVote { "voteUser", String.format("%.1f", vote).replace(',', '.'), "version", "cgeo"); - final String result = cgBase.getResponseData(cgBase.request("http://gcvote.com/setVote.php", params, false, false, false)); + final String result = Network.getResponseData(Network.request("http://gcvote.com/setVote.php", params, false, false, false)); return result.trim().equalsIgnoreCase("ok"); } diff --git a/main/src/cgeo/geocaching/geopoint/Geopoint.java b/main/src/cgeo/geocaching/geopoint/Geopoint.java index ca67014..fdcc663 100644 --- a/main/src/cgeo/geocaching/geopoint/Geopoint.java +++ b/main/src/cgeo/geocaching/geopoint/Geopoint.java @@ -1,8 +1,16 @@ package cgeo.geocaching.geopoint; +import cgeo.geocaching.Settings; +import cgeo.geocaching.geopoint.GeopointFormatter.Format; +import cgeo.geocaching.network.Network; +import cgeo.geocaching.network.Parameters; + import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONObject; import android.location.Location; +import android.util.Log; import java.math.BigDecimal; import java.math.RoundingMode; @@ -234,7 +242,7 @@ public final class Geopoint /** * Returns formatted coordinates with default format. * Default format is decimalminutes, e.g. N 52° 36.123 E 010° 03.456 - * + * * @return formatted coordinates */ @Override @@ -475,4 +483,33 @@ public final class Geopoint super(msg); } } + + public Double getElevation() { + try { + final String uri = "http://maps.googleapis.com/maps/api/elevation/json"; + final Parameters params = new Parameters( + "sensor", "false", + "locations", format(Format.LAT_LON_DECDEGREE_COMMA)); + final JSONObject response = Network.requestJSON(uri, params); + + if (response == null) { + return null; + } + + if (!StringUtils.equalsIgnoreCase(response.getString("status"), "OK")) { + return null; + } + + if (response.has("results")) { + JSONArray results = response.getJSONArray("results"); + JSONObject result = results.getJSONObject(0); + return result.getDouble("elevation"); + } + } catch (Exception e) { + Log.w(Settings.tag, "cgBase.getElevation: " + e.toString()); + } + + return null; + } + } diff --git a/main/src/cgeo/geocaching/go4cache/Go4Cache.java b/main/src/cgeo/geocaching/go4cache/Go4Cache.java index 7243383..fb53c27 100644 --- a/main/src/cgeo/geocaching/go4cache/Go4Cache.java +++ b/main/src/cgeo/geocaching/go4cache/Go4Cache.java @@ -6,6 +6,7 @@ import cgeo.geocaching.cgeoapplication; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter.Format; import cgeo.geocaching.geopoint.Viewport; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import cgeo.geocaching.utils.CryptUtils; @@ -99,7 +100,7 @@ public final class Go4Cache extends Thread { params.put("v", cgBase.version); } - cgBase.postRequest("http://api.go4cache.com/", params); + Network.postRequest("http://api.go4cache.com/", params); // Update our coordinates even if the request was not successful, as not to hammer the server // with invalid requests for every new GPS position. @@ -134,7 +135,7 @@ public final class Go4Cache extends Thread { "lnm", viewport.bottomLeft.format(Format.LON_DECDEGREE_RAW), "lnx", viewport.topRight.format(Format.LON_DECDEGREE_RAW)); - final String data = cgBase.getResponseData(cgBase.postRequest("http://api.go4cache.com/get.php", params)); + final String data = Network.getResponseData(Network.postRequest("http://api.go4cache.com/get.php", params)); if (StringUtils.isBlank(data)) { Log.e(Settings.tag, "cgeoBase.getGeocachersInViewport: No data from server"); diff --git a/main/src/cgeo/geocaching/maps/CGeoMap.java b/main/src/cgeo/geocaching/maps/CGeoMap.java index 0272c27..65d50c9 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.MapProvider; import cgeo.geocaching.maps.interfaces.MapViewImpl; import cgeo.geocaching.maps.interfaces.OnMapDragListener; import cgeo.geocaching.maps.interfaces.OtherCachersOverlayItemImpl; +import cgeo.geocaching.network.Login; import cgeo.geocaching.utils.CancellableHandler; import org.apache.commons.collections.CollectionUtils; @@ -1335,7 +1336,7 @@ public class CGeoMap extends AbstractMap implements OnMapDragListener, ViewFacto if (search != null) { downloaded = true; if (search.error == StatusCode.NOT_LOGGED_IN) { - cgBase.login(); + Login.login(); tokens = null; } else { break; diff --git a/main/src/cgeo/geocaching/network/HtmlImage.java b/main/src/cgeo/geocaching/network/HtmlImage.java index acac552..1811110 100644 --- a/main/src/cgeo/geocaching/network/HtmlImage.java +++ b/main/src/cgeo/geocaching/network/HtmlImage.java @@ -3,7 +3,6 @@ package cgeo.geocaching.network; import cgeo.geocaching.R; import cgeo.geocaching.Settings; import cgeo.geocaching.StoredList; -import cgeo.geocaching.cgBase; import cgeo.geocaching.connector.ConnectorFactory; import cgeo.geocaching.files.LocalStorage; @@ -86,7 +85,7 @@ public class HtmlImage implements Html.ImageGetter { if (absoluteURL != null) { try { - final HttpResponse httpResponse = cgBase.request(absoluteURL, null, false); + final HttpResponse httpResponse = Network.request(absoluteURL, null, false); if (httpResponse != null) { final File file = LocalStorage.getStorageFile(geocode, url, true, true); LocalStorage.saveEntityToFile(httpResponse, file); diff --git a/main/src/cgeo/geocaching/network/Login.java b/main/src/cgeo/geocaching/network/Login.java new file mode 100644 index 0000000..8db370d --- /dev/null +++ b/main/src/cgeo/geocaching/network/Login.java @@ -0,0 +1,389 @@ +package cgeo.geocaching.network; + +import cgeo.geocaching.R; +import cgeo.geocaching.Settings; +import cgeo.geocaching.cgBase; +import cgeo.geocaching.connector.gc.GCConstants; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.utils.BaseUtils; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.http.HttpResponse; + +import android.content.Context; +import android.graphics.drawable.BitmapDrawable; +import android.util.Log; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; + +public abstract class Login { + + private final static String ENGLISH = "English▼"; + + // false = not logged in + private static boolean actualLoginStatus = false; + private static String actualUserName = ""; + private static String actualMemberStatus = ""; + private static int actualCachesFound = -1; + private static String actualStatus = ""; + + private final static Map<String, SimpleDateFormat> gcCustomDateFormats; + static { + final String[] formats = new String[] { + "MM/dd/yyyy", + "yyyy-MM-dd", + "yyyy/MM/dd", + "dd/MMM/yyyy", + "MMM/dd/yyyy", + "dd MMM yy", + "dd/MM/yyyy" + }; + + Map<String, SimpleDateFormat> map = new HashMap<String, SimpleDateFormat>(); + + for (String format : formats) { + map.put(format, new SimpleDateFormat(format, Locale.ENGLISH)); + } + + gcCustomDateFormats = Collections.unmodifiableMap(map); + } + + public static StatusCode login() { + final ImmutablePair<String, String> login = Settings.getLogin(); + + if (login == null || StringUtils.isEmpty(login.left) || StringUtils.isEmpty(login.right)) { + Login.setActualStatus(cgBase.res.getString(R.string.err_login)); + Log.e(Settings.tag, "cgeoBase.login: No login information stored"); + return StatusCode.NO_LOGIN_INFO_STORED; + } + + // res is null during the unit tests + if (cgBase.res != null) { + Login.setActualStatus(cgBase.res.getString(R.string.init_login_popup_working)); + } + HttpResponse loginResponse = Network.request("https://www.geocaching.com/login/default.aspx", null, false, false, false); + String loginData = Network.getResponseData(loginResponse); + if (loginResponse != null && loginResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(loginData, GCConstants.PATTERN_MAINTENANCE)) { + return StatusCode.MAINTENANCE; + } + + if (StringUtils.isBlank(loginData)) { + Log.e(Settings.tag, "cgeoBase.login: Failed to retrieve login page (1st)"); + return StatusCode.CONNECTION_FAILED; // no loginpage + } + + if (Login.getLoginStatus(loginData)) { + Log.i(Settings.tag, "Already logged in Geocaching.com as " + login.left); + Login.switchToEnglish(loginData); + return StatusCode.NO_ERROR; // logged in + } + + Network.clearCookies(); + Settings.setCookieStore(null); + + final Parameters params = new Parameters( + "__EVENTTARGET", "", + "__EVENTARGUMENT", "", + "ctl00$ContentBody$tbUsername", login.left, + "ctl00$ContentBody$tbPassword", login.right, + "ctl00$ContentBody$cbRememberMe", "on", + "ctl00$ContentBody$btnSignIn", "Login"); + final String[] viewstates = Login.getViewstates(loginData); + if (cgBase.isEmpty(viewstates)) { + Log.e(Settings.tag, "cgeoBase.login: Failed to find viewstates"); + return StatusCode.LOGIN_PARSE_ERROR; // no viewstates + } + Login.putViewstates(params, viewstates); + + loginResponse = Network.postRequest("https://www.geocaching.com/login/default.aspx", params); + loginData = Network.getResponseData(loginResponse); + + if (StringUtils.isNotBlank(loginData)) { + if (Login.getLoginStatus(loginData)) { + Log.i(Settings.tag, "Successfully logged in Geocaching.com as " + login.left); + + Login.switchToEnglish(loginData); + Settings.setCookieStore(Network.dumpCookieStore()); + + return StatusCode.NO_ERROR; // logged in + } else { + if (loginData.contains("Your username/password combination does not match.")) { + Log.i(Settings.tag, "Failed to log in Geocaching.com as " + login.left + " because of wrong username/password"); + return StatusCode.WRONG_LOGIN_DATA; // wrong login + } else { + Log.i(Settings.tag, "Failed to log in Geocaching.com as " + login.left + " for some unknown reason"); + return StatusCode.UNKNOWN_ERROR; // can't login + } + } + } else { + Log.e(Settings.tag, "cgeoBase.login: Failed to retrieve login page (2nd)"); + // FIXME: should it be CONNECTION_FAILED to match the first attempt? + return StatusCode.COMMUNICATION_ERROR; // no login page + } + } + + public static StatusCode logout() { + HttpResponse logoutResponse = Network.request("https://www.geocaching.com/login/default.aspx?RESET=Y&redir=http%3a%2f%2fwww.geocaching.com%2fdefault.aspx%3f", null, false, false, false); + String logoutData = Network.getResponseData(logoutResponse); + if (logoutResponse != null && logoutResponse.getStatusLine().getStatusCode() == 503 && BaseUtils.matches(logoutData, GCConstants.PATTERN_MAINTENANCE)) { + return StatusCode.MAINTENANCE; + } + + Network.clearCookies(); + Settings.setCookieStore(null); + return StatusCode.NO_ERROR; + } + + public static void setActualCachesFound(final int found) { + actualCachesFound = found; + } + + public static String getActualStatus() { + return actualStatus; + } + + public static void setActualStatus(final String status) { + actualStatus = status; + } + + public static boolean isActualLoginStatus() { + return actualLoginStatus; + } + + public static void setActualLoginStatus(boolean loginStatus) { + actualLoginStatus = loginStatus; + } + + public static String getActualUserName() { + return actualUserName; + } + + public static void setActualUserName(String userName) { + actualUserName = userName; + } + + public static String getActualMemberStatus() { + return actualMemberStatus; + } + + public static void setActualMemberStatus(final String memberStatus) { + actualMemberStatus = memberStatus; + } + + public static int getActualCachesFound() { + return actualCachesFound; + } + + /** + * Check if the user has been logged in when he retrieved the data. + * + * @param page + * @return <code>true</code> if user is logged in, <code>false</code> otherwise + */ + public static boolean getLoginStatus(final String page) { + if (StringUtils.isBlank(page)) { + Log.e(Settings.tag, "cgeoBase.checkLogin: No page given"); + return false; + } + + // res is null during the unit tests + if (cgBase.res != null) { + setActualStatus(cgBase.res.getString(R.string.init_login_popup_ok)); + } + + // on every page except login page + setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME)); + if (isActualLoginStatus()) { + setActualUserName(BaseUtils.getMatch(page, GCConstants.PATTERN_LOGIN_NAME, true, "???")); + setActualMemberStatus(BaseUtils.getMatch(page, GCConstants.PATTERN_MEMBER_STATUS, true, "???")); + setActualCachesFound(Integer.parseInt(BaseUtils.getMatch(page, GCConstants.PATTERN_CACHES_FOUND, true, "0").replaceAll("[,.]", ""))); + return true; + } + + // login page + setActualLoginStatus(BaseUtils.matches(page, GCConstants.PATTERN_LOGIN_NAME_LOGIN_PAGE)); + if (isActualLoginStatus()) { + setActualUserName(Settings.getUsername()); + setActualMemberStatus(Settings.getMemberStatus()); + // number of caches found is not part of this page + return true; + } + + // res is null during the unit tests + if (cgBase.res != null) { + setActualStatus(cgBase.res.getString(R.string.init_login_popup_failed)); + } + return false; + } + + private static void switchToEnglish(String previousPage) { + if (previousPage != null && previousPage.indexOf(ENGLISH) >= 0) { + Log.i(Settings.tag, "Geocaching.com language already set to English"); + // get find count + getLoginStatus(Network.getResponseData(Network.request("http://www.geocaching.com/email/", null, false))); + } else { + final String page = Network.getResponseData(Network.request("http://www.geocaching.com/default.aspx", null, false)); + getLoginStatus(page); + if (page == null) { + Log.e(Settings.tag, "Failed to read viewstates to set geocaching.com language"); + } + final Parameters params = new Parameters( + "__EVENTTARGET", "ctl00$uxLocaleList$uxLocaleList$ctl00$uxLocaleItem", // switch to english + "__EVENTARGUMENT", ""); + Login.transferViewstates(page, params); + final HttpResponse response = Network.postRequest("http://www.geocaching.com/default.aspx", params); + if (!Network.isSuccess(response)) { + Log.e(Settings.tag, "Failed to set geocaching.com language to English"); + } + } + } + + public static BitmapDrawable downloadAvatarAndGetMemberStatus(final Context context) { + try { + final String profile = BaseUtils.replaceWhitespace(Network.getResponseData(Network.request("http://www.geocaching.com/my/", null, false))); + + Settings.setMemberStatus(BaseUtils.getMatch(profile, GCConstants.PATTERN_MEMBER_STATUS, true, null)); + + setActualCachesFound(Integer.parseInt(BaseUtils.getMatch(profile, GCConstants.PATTERN_CACHES_FOUND, true, "-1").replaceAll("[,.]", ""))); + + final String avatarURL = BaseUtils.getMatch(profile, GCConstants.PATTERN_AVATAR_IMAGE_PROFILE_PAGE, false, null); + if (null != avatarURL) { + final HtmlImage imgGetter = new HtmlImage(context, "", false, 0, false); + return imgGetter.getDrawable(avatarURL); + } + // No match? There may be no avatar set by user. + Log.d(Settings.tag, "No avatar set for user"); + } catch (Exception e) { + Log.w(Settings.tag, "Error when retrieving user avatar", e); + } + return null; + } + + /** + * Detect user date settings on geocaching.com + */ + public static void detectGcCustomDate() { + + final String result = Network.getResponseData(Network.request("http://www.geocaching.com/account/ManagePreferences.aspx", null, false, false, false)); + + if (null == result) { + Log.w(Settings.tag, "cgeoBase.detectGcCustomDate: result is null"); + return; + } + + String customDate = BaseUtils.getMatch(result, GCConstants.PATTERN_CUSTOMDATE, true, null); + if (null != customDate) { + Settings.setGcCustomDate(customDate); + } + } + + public static Date parseGcCustomDate(final String input, final String format) throws ParseException { + if (StringUtils.isBlank(input)) { + throw new ParseException("Input is null", 0); + } + + final String trimmed = input.trim(); + + if (gcCustomDateFormats.containsKey(format)) { + try { + return gcCustomDateFormats.get(format).parse(trimmed); + } catch (ParseException e) { + } + } + + for (SimpleDateFormat sdf : gcCustomDateFormats.values()) { + try { + return sdf.parse(trimmed); + } catch (ParseException e) { + } + } + + throw new ParseException("No matching pattern", 0); + } + + public static Date parseGcCustomDate(final String input) throws ParseException { + return parseGcCustomDate(input, Settings.getGcCustomDate()); + } + + /** + * read all viewstates from page + * + * @return String[] with all view states + */ + public static String[] getViewstates(String page) { + // Get the number of viewstates. + // If there is only one viewstate, __VIEWSTATEFIELDCOUNT is not present + + if (page == null) { // no network access + return null; + } + + int count = 1; + final Matcher matcherViewstateCount = GCConstants.PATTERN_VIEWSTATEFIELDCOUNT.matcher(page); + if (matcherViewstateCount.find()) { + count = Integer.parseInt(matcherViewstateCount.group(1)); + } + + String[] viewstates = new String[count]; + + // Get the viewstates + int no; + final Matcher matcherViewstates = GCConstants.PATTERN_VIEWSTATES.matcher(page); + while (matcherViewstates.find()) { + String sno = matcherViewstates.group(1); // number of viewstate + if (sno.length() == 0) { + no = 0; + } + else { + no = Integer.parseInt(sno); + } + viewstates[no] = matcherViewstates.group(2); + } + + if (viewstates.length != 1 || viewstates[0] != null) { + return viewstates; + } + // no viewstates were present + return null; + } + + /** + * put viewstates into request parameters + */ + public static void putViewstates(final Parameters params, final String[] viewstates) { + if (ArrayUtils.isEmpty(viewstates)) { + return; + } + params.put("__VIEWSTATE", viewstates[0]); + if (viewstates.length > 1) { + for (int i = 1; i < viewstates.length; i++) { + params.put("__VIEWSTATE" + i, viewstates[i]); + } + params.put("__VIEWSTATEFIELDCOUNT", String.valueOf(viewstates.length)); + } + } + + /** + * transfers the viewstates variables from a page (response) to parameters + * (next request) + */ + public static void transferViewstates(final String page, final Parameters params) { + putViewstates(params, getViewstates(page)); + } + + static public String[] requestViewstates(final String uri, final Parameters params, boolean xContentType, boolean my) { + final HttpResponse response = Network.request(uri, params, xContentType, my, false); + + return getViewstates(Network.getResponseData(response)); + } + +} diff --git a/main/src/cgeo/geocaching/network/Network.java b/main/src/cgeo/geocaching/network/Network.java new file mode 100644 index 0000000..0afe095 --- /dev/null +++ b/main/src/cgeo/geocaching/network/Network.java @@ -0,0 +1,364 @@ +package cgeo.geocaching.network; + +import cgeo.geocaching.Settings; +import cgeo.geocaching.cgBase; +import cgeo.geocaching.enumerations.StatusCode; +import cgeo.geocaching.utils.BaseUtils; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.apache.http.HeaderElement; +import org.apache.http.HttpEntity; +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.HttpResponse; +import org.apache.http.HttpResponseInterceptor; +import org.apache.http.NameValuePair; +import org.apache.http.client.CookieStore; +import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.cookie.Cookie; +import org.apache.http.entity.HttpEntityWrapper; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.cookie.BasicClientCookie; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HTTP; +import org.apache.http.protocol.HttpContext; +import org.apache.http.util.EntityUtils; +import org.json.JSONException; +import org.json.JSONObject; + +import android.net.Uri; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.util.List; +import java.util.zip.GZIPInputStream; + + +public abstract class Network { + + static class GzipDecompressingEntity extends HttpEntityWrapper { + public GzipDecompressingEntity(final HttpEntity entity) { + super(entity); + } + + @Override + public InputStream getContent() throws IOException, IllegalStateException { + // the wrapped entity's getContent() decides about repeatability + InputStream wrappedin = wrappedEntity.getContent(); + return new GZIPInputStream(wrappedin); + } + + @Override + public long getContentLength() { + // length of ungzipped content is not known + return -1; + } + } + + private static final int NB_DOWNLOAD_RETRIES = 4; + /** User agent id */ + private final static String USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:9.0.1) Gecko/20100101 Firefox/9.0.1"; + private static final String PATTERN_PASSWORD = "(?<=[\\?&])[Pp]ass(w(or)?d)?=[^&#$]+"; + + private final static HttpParams clientParams = new BasicHttpParams(); + private static boolean cookieStoreRestored = false; + private final static CookieStore cookieStore = new BasicCookieStore(); + + static { + Network.clientParams.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, HTTP.UTF_8); + Network.clientParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 30000); + Network.clientParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, 30000); + } + + private static String hidePassword(final String message) { + return message.replaceAll(Network.PATTERN_PASSWORD, "password=***"); + } + + private static HttpClient getHttpClient() { + final DefaultHttpClient client = new DefaultHttpClient(); + client.setCookieStore(cookieStore); + client.setParams(clientParams); + + client.addRequestInterceptor(new HttpRequestInterceptor() { + + @Override + public void process( + final HttpRequest request, + final HttpContext context) throws HttpException, IOException { + if (!request.containsHeader("Accept-Encoding")) { + request.addHeader("Accept-Encoding", "gzip"); + } + } + }); + client.addResponseInterceptor(new HttpResponseInterceptor() { + + public void process( + final HttpResponse response, + final HttpContext context) throws HttpException, IOException { + HttpEntity entity = response.getEntity(); + if (null != entity) { + Header ceheader = entity.getContentEncoding(); + if (ceheader != null) { + HeaderElement[] codecs = ceheader.getElements(); + for (int i = 0; i < codecs.length; i++) { + if (codecs[i].getName().equalsIgnoreCase("gzip")) { + Log.d(Settings.tag, "Decompressing response"); + response.setEntity( + new Network.GzipDecompressingEntity(response.getEntity())); + return; + } + } + } + } + } + + }); + + return client; + } + + public static void restoreCookieStore(final String oldCookies) { + if (!cookieStoreRestored) { + Network.clearCookies(); + if (oldCookies != null) { + for (final String cookie : StringUtils.split(oldCookies, ';')) { + final String[] split = StringUtils.split(cookie, "=", 3); + if (split.length == 3) { + final BasicClientCookie newCookie = new BasicClientCookie(split[0], split[1]); + newCookie.setDomain(split[2]); + cookieStore.addCookie(newCookie); + } + } + } + cookieStoreRestored = true; + } + } + + public static String dumpCookieStore() { + StringBuilder cookies = new StringBuilder(); + for (final Cookie cookie : cookieStore.getCookies()) { + cookies.append(cookie.getName()); + cookies.append('='); + cookies.append(cookie.getValue()); + cookies.append('='); + cookies.append(cookie.getDomain()); + cookies.append(';'); + } + return cookies.toString(); + } + + public static void clearCookies() { + cookieStore.clear(); + } + + /** + * POST HTTP request + * + * @param uri + * @param params + * @return + */ + public static HttpResponse postRequest(final String uri, final List<? extends NameValuePair> params) { + try { + HttpPost request = new HttpPost(uri); + if (params != null) { + request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); + } + request.setHeader("X-Requested-With", "XMLHttpRequest"); + return Network.request(request); + } catch (Exception e) { + // Can be UnsupportedEncodingException, ClientProtocolException or IOException + Log.e(Settings.tag, "postRequest", e); + return null; + } + } + + /** + * GET HTTP request + * + * @param uri + * @param params + * @param xContentType + * @param my + * @param addF + * @return + */ + public static HttpResponse request(final String uri, final Parameters params, boolean xContentType, boolean my, boolean addF) { + return Network.request(uri, cgBase.addFToParams(params, my, addF), xContentType); + } + + /** + * GET HTTP request + * + * @param uri + * @param params + * @param xContentType + * @return + */ + public static HttpResponse request(final String uri, final Parameters params, final boolean xContentType) { + final String fullUri = params == null ? uri : Uri.parse(uri).buildUpon().encodedQuery(params.toString()).build().toString(); + final HttpRequestBase request = new HttpGet(fullUri); + + request.setHeader("X-Requested-With", "XMLHttpRequest"); + + if (xContentType) { + request.setHeader("Content-Type", "application/x-www-form-urlencoded"); + } + + return Network.request(request); + } + + public static HttpResponse request(final HttpRequestBase request) { + request.setHeader("Accept-Charset", "utf-8,iso-8859-1;q=0.8,utf-16;q=0.8,*;q=0.7"); + request.setHeader("Accept-Language", "en-US,*;q=0.9"); + request.getParams().setParameter(CoreProtocolPNames.USER_AGENT, USER_AGENT); + return Network.doRequest(request); + } + + private static HttpResponse doRequest(final HttpRequestBase request) { + final String reqLogStr = request.getMethod() + " " + hidePassword(request.getURI().toString()); + Log.d(Settings.tag, reqLogStr); + + final HttpClient client = getHttpClient(); + for (int i = 0; i <= NB_DOWNLOAD_RETRIES; i++) { + final long before = System.currentTimeMillis(); + try { + final HttpResponse response = client.execute(request); + int status = response.getStatusLine().getStatusCode(); + if (status == 200) { + Log.d(Settings.tag, status + Network.formatTimeSpan(before) + reqLogStr); + } else { + Log.w(Settings.tag, status + " [" + response.getStatusLine().getReasonPhrase() + "]" + Network.formatTimeSpan(before) + reqLogStr); + } + return response; + } catch (IOException e) { + final String timeSpan = Network.formatTimeSpan(before); + final String tries = (i + 1) + "/" + (NB_DOWNLOAD_RETRIES + 1); + if (i == NB_DOWNLOAD_RETRIES) { + Log.e(Settings.tag, "Failure " + tries + timeSpan + reqLogStr, e); + } else { + Log.e(Settings.tag, "Failure " + tries + " (" + e.toString() + ")" + timeSpan + "- retrying " + reqLogStr); + } + } + } + + return null; + } + + private static String formatTimeSpan(final long before) { + // don't use String.format in a pure logging routine, it has very bad performance + return " (" + (System.currentTimeMillis() - before) + " ms) "; + } + + static public boolean isSuccess(final HttpResponse response) { + return response != null && response.getStatusLine().getStatusCode() == 200; + } + + public static JSONObject requestJSON(final String uri, final Parameters params) { + final HttpGet request = new HttpGet(Network.prepareParameters(uri, params)); + request.setHeader("Accept", "application/json, text/javascript, */*; q=0.01"); + request.setHeader("Content-Type", "application/json; charset=UTF-8"); + request.setHeader("X-Requested-With", "XMLHttpRequest"); + + final HttpResponse response = doRequest(request); + if (response != null && response.getStatusLine().getStatusCode() == 200) { + try { + return new JSONObject(Network.getResponseData(response)); + } catch (JSONException e) { + Log.e(Settings.tag, "Network.requestJSON", e); + } + } + + return null; + } + + private static String prepareParameters(final String baseUri, final Parameters params) { + return CollectionUtils.isNotEmpty(params) ? baseUri + "?" + params.toString() : baseUri; + } + + private static String getResponseDataNoError(final HttpResponse response, boolean replaceWhitespace) { + try { + String data = EntityUtils.toString(response.getEntity(), HTTP.UTF_8); + return replaceWhitespace ? BaseUtils.replaceWhitespace(data) : data; + } catch (Exception e) { + Log.e(Settings.tag, "getResponseData", e); + return null; + } + } + + public static String getResponseData(final HttpResponse response) { + return Network.getResponseData(response, true); + } + + static public String getResponseData(final HttpResponse response, boolean replaceWhitespace) { + if (!isSuccess(response)) { + return null; + } + return getResponseDataNoError(response, replaceWhitespace); + } + + /** + * POST HTTP request. Do the request a second time if the user is not logged in + * + * @param uri + * @return + */ + public static String postRequestLogged(final String uri) { + HttpResponse response = postRequest(uri, null); + String data = getResponseData(response); + + if (!Login.getLoginStatus(data)) { + if (Login.login() == StatusCode.NO_ERROR) { + response = postRequest(uri, null); + data = getResponseData(response); + } else { + Log.i(Settings.tag, "Working as guest."); + } + } + return data; + } + + /** + * GET HTTP request. Do the request a second time if the user is not logged in + * + * @param uri + * @param params + * @param xContentType + * @param my + * @param addF + * @return + */ + public static String requestLogged(final String uri, final Parameters params, boolean xContentType, boolean my, boolean addF) { + HttpResponse response = request(uri, params, xContentType, my, addF); + String data = getResponseData(response); + + if (!Login.getLoginStatus(data)) { + if (Login.login() == StatusCode.NO_ERROR) { + response = request(uri, params, xContentType, my, addF); + data = getResponseData(response); + } else { + Log.i(Settings.tag, "Working as guest."); + } + } + return data; + } + + public static String urlencode_rfc3986(String text) { + final String encoded = StringUtils.replace(URLEncoder.encode(text).replace("+", "%20"), "%7E", "~"); + + return encoded; + } +} diff --git a/main/src/cgeo/geocaching/network/OAuth.java b/main/src/cgeo/geocaching/network/OAuth.java index a5393f6..cefa90d 100644 --- a/main/src/cgeo/geocaching/network/OAuth.java +++ b/main/src/cgeo/geocaching/network/OAuth.java @@ -1,7 +1,6 @@ package cgeo.geocaching.network; import cgeo.geocaching.Settings; -import cgeo.geocaching.cgBase; import cgeo.geocaching.utils.CryptUtils; import org.apache.commons.lang3.StringUtils; @@ -24,11 +23,11 @@ public class OAuth { final List<String> paramsEncoded = new ArrayList<String>(); for (final NameValuePair nameValue : params) { - paramsEncoded.add(nameValue.getName() + "=" + cgBase.urlencode_rfc3986(nameValue.getValue())); + paramsEncoded.add(nameValue.getName() + "=" + Network.urlencode_rfc3986(nameValue.getValue())); } final String keysPacked = Settings.getKeyConsumerSecret() + "&" + StringUtils.defaultString(tokenSecret); // both even if empty some of them! - final String requestPacked = method + "&" + cgBase.urlencode_rfc3986((https ? "https" : "http") + "://" + host + path) + "&" + cgBase.urlencode_rfc3986(StringUtils.join(paramsEncoded.toArray(), '&')); + final String requestPacked = method + "&" + Network.urlencode_rfc3986((https ? "https" : "http") + "://" + host + path) + "&" + Network.urlencode_rfc3986(StringUtils.join(paramsEncoded.toArray(), '&')); params.put("oauth_signature", CryptUtils.base64Encode(CryptUtils.hashHmac(requestPacked, keysPacked))); } } diff --git a/main/src/cgeo/geocaching/twitter/Twitter.java b/main/src/cgeo/geocaching/twitter/Twitter.java index a47409f..fa1a39d 100644 --- a/main/src/cgeo/geocaching/twitter/Twitter.java +++ b/main/src/cgeo/geocaching/twitter/Twitter.java @@ -1,10 +1,13 @@ package cgeo.geocaching.twitter; import cgeo.geocaching.Settings; -import cgeo.geocaching.cgBase; +import cgeo.geocaching.cgCache; +import cgeo.geocaching.cgTrackable; import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.geopoint.Geopoint; import cgeo.geocaching.geopoint.GeopointFormatter.Format; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.OAuth; import cgeo.geocaching.network.Parameters; @@ -33,7 +36,7 @@ public final class Twitter { } OAuth.signOAuth("api.twitter.com", "/1/statuses/update.json", "POST", false, parameters, Settings.getTokenPublic(), Settings.getTokenSecret()); - final HttpResponse httpResponse = cgBase.postRequest("http://api.twitter.com/1/statuses/update.json", parameters); + final HttpResponse httpResponse = Network.postRequest("http://api.twitter.com/1/statuses/update.json", parameters); if (httpResponse != null && httpResponse.getStatusLine().getStatusCode() == 200) { Log.i(Settings.tag, "Tweet posted"); } else { @@ -51,4 +54,42 @@ public final class Twitter { } return result; } + + public static void postTweetCache(String geocode) { + final cgCache cache = cgeoapplication.getInstance().loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); + String status; + final String url = cache.getUrl(); + if (url.length() >= 100) { + status = "I found " + url; + } + else { + String name = cache.getName(); + status = "I found " + name + " (" + url + ")"; + if (status.length() > MAX_TWEET_SIZE) { + name = name.substring(0, name.length() - (status.length() - MAX_TWEET_SIZE) - 3) + "..."; + } + status = "I found " + name + " (" + url + ")"; + status = appendHashTag(status, "cgeo"); + status = appendHashTag(status, "geocaching"); + } + + postTweet(cgeoapplication.getInstance(), status, null); + } + + public static void postTweetTrackable(String geocode) { + final cgTrackable trackable = cgeoapplication.getInstance().getTrackableByGeocode(geocode); + String name = trackable.getName(); + if (name.length() > 82) { + name = name.substring(0, 79) + "..."; + } + StringBuilder builder = new StringBuilder("I touched "); + builder.append(name); + if (trackable.getUrl() != null) { + builder.append(" (").append(trackable.getUrl()).append(')'); + } + builder.append('!'); + String status = appendHashTag(builder.toString(), "cgeo"); + status = appendHashTag(status, "geocaching"); + postTweet(cgeoapplication.getInstance(), status, null); + } } diff --git a/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java b/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java index 83b1569..fffe12e 100644 --- a/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java +++ b/main/src/cgeo/geocaching/twitter/TwitterAuthorizationActivity.java @@ -2,8 +2,8 @@ package cgeo.geocaching.twitter; import cgeo.geocaching.R; import cgeo.geocaching.Settings; -import cgeo.geocaching.cgBase; import cgeo.geocaching.activity.AbstractActivity; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.OAuth; import cgeo.geocaching.network.Parameters; @@ -141,7 +141,7 @@ public class TwitterAuthorizationActivity extends AbstractActivity { try { final Parameters params = new Parameters(); OAuth.signOAuth(host, pathRequest, method, true, params, null, null); - final String line = cgBase.getResponseData(cgBase.request("https://" + host + pathRequest, params, false)); + final String line = Network.getResponseData(Network.request("https://" + host + pathRequest, params, false)); if (StringUtils.isNotBlank(line)) { @@ -186,7 +186,7 @@ public class TwitterAuthorizationActivity extends AbstractActivity { final Parameters params = new Parameters("oauth_verifier", pinEntry.getText().toString()); OAuth.signOAuth(host, path, method, true, params, OAtoken, OAtokenSecret); - final String line = StringUtils.defaultString(cgBase.getResponseData(cgBase.postRequest("https://" + host + path, params))); + final String line = StringUtils.defaultString(Network.getResponseData(Network.postRequest("https://" + host + path, params))); OAtoken = ""; OAtokenSecret = ""; diff --git a/main/src/cgeo/geocaching/ui/DirectionImage.java b/main/src/cgeo/geocaching/ui/DirectionImage.java index 35b209f..aa01b97 100644 --- a/main/src/cgeo/geocaching/ui/DirectionImage.java +++ b/main/src/cgeo/geocaching/ui/DirectionImage.java @@ -1,7 +1,7 @@ package cgeo.geocaching.ui; -import cgeo.geocaching.cgBase; import cgeo.geocaching.files.LocalStorage; +import cgeo.geocaching.network.Network; import cgeo.geocaching.network.Parameters; import org.apache.commons.lang3.StringUtils; @@ -17,7 +17,7 @@ public class DirectionImage { } final HttpResponse httpResponse = - cgBase.request("http://www.geocaching.com/ImgGen/seek/CacheDir.ashx", new Parameters("k", code), false); + Network.request("http://www.geocaching.com/ImgGen/seek/CacheDir.ashx", new Parameters("k", code), false); if (httpResponse != null) { LocalStorage.saveEntityToFile(httpResponse, getDirectionFile(geocode, true)); } diff --git a/main/src/cgeo/geocaching/utils/LogTemplateProvider.java b/main/src/cgeo/geocaching/utils/LogTemplateProvider.java index 522bbf4..da7fb86 100644 --- a/main/src/cgeo/geocaching/utils/LogTemplateProvider.java +++ b/main/src/cgeo/geocaching/utils/LogTemplateProvider.java @@ -4,6 +4,8 @@ import cgeo.geocaching.R; import cgeo.geocaching.Settings; import cgeo.geocaching.cgBase; import cgeo.geocaching.connector.gc.GCConstants; +import cgeo.geocaching.network.Login; +import cgeo.geocaching.network.Network; import org.apache.commons.lang3.StringUtils; @@ -81,12 +83,12 @@ public class LogTemplateProvider { @Override public String getValue(final boolean offline) { - int current = cgBase.getActualCachesFound(); + int current = Login.getActualCachesFound(); if (current == 0) { if (offline) { return ""; } - final String page = cgBase.getResponseData(cgBase.request("http://www.geocaching.com/email/", null, false, false, false)); + final String page = Network.getResponseData(Network.request("http://www.geocaching.com/email/", null, false, false, false)); current = parseFindCount(page); } |