diff options
Diffstat (limited to 'src/cgeo/geocaching')
118 files changed, 31825 insertions, 0 deletions
diff --git a/src/cgeo/geocaching/cg8.java b/src/cgeo/geocaching/cg8.java new file mode 100644 index 0000000..698988e --- /dev/null +++ b/src/cgeo/geocaching/cg8.java @@ -0,0 +1,17 @@ +package cgeo.geocaching; + +import android.app.Activity; +import android.view.Display; + +public class cg8 { + private Activity activity = null; + + public cg8(Activity activityIn) { + activity = activityIn; + } + + public int getRotation() { + Display display = activity.getWindowManager().getDefaultDisplay(); + return display.getRotation(); + } +} diff --git a/src/cgeo/geocaching/cg8wrap.java b/src/cgeo/geocaching/cg8wrap.java new file mode 100644 index 0000000..2903c8d --- /dev/null +++ b/src/cgeo/geocaching/cg8wrap.java @@ -0,0 +1,27 @@ +package cgeo.geocaching; + +import android.app.Activity; + +public class cg8wrap { + static { + try { + Class.forName("cgeo.geocaching.cg8"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private cg8 cg8; + + public static void check() { + // nothing + } + + public cg8wrap(Activity activityIn) { + cg8 = new cg8(activityIn); + } + + public int getRotation() { + return cg8.getRotation(); + } +} diff --git a/src/cgeo/geocaching/cgAddressImg.java b/src/cgeo/geocaching/cgAddressImg.java new file mode 100644 index 0000000..bdc0ccf --- /dev/null +++ b/src/cgeo/geocaching/cgAddressImg.java @@ -0,0 +1,60 @@ +package cgeo.geocaching; + +import android.util.Log; +import android.graphics.Rect; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.impl.client.DefaultHttpClient; + +public class cgAddressImg { + public static BitmapDrawable getDrawable(String url) { + Bitmap imagePre = null; + + if (url == null || url.length() == 0) return null; + + HttpClient client = null; + HttpGet getMethod = null; + HttpResponse httpResponse = null; + HttpEntity entity = null; + BufferedHttpEntity bufferedEntity = null; + + for (int i = 0; i < 2; i ++) { + if (i > 0) Log.w(cgSettings.tag, "cgAddressImg.getDrawable: Failed to download data, retrying. Attempt #" + (i + 1)); + + try { + client = new DefaultHttpClient(); + getMethod = new HttpGet(url); + httpResponse = client.execute(getMethod); + entity = httpResponse.getEntity(); + bufferedEntity = new BufferedHttpEntity(entity); + + Log.i(cgSettings.tag, "[" + entity.getContentLength() + "B] Downloading address map " + url); + + if (bufferedEntity != null) imagePre = BitmapFactory.decodeStream(bufferedEntity.getContent(), null, null); + if (imagePre != null) break; + } catch (Exception e) { + Log.e(cgSettings.tag, "cgAddressImg.getDrawable (downloading from web): " + e.toString()); + } + } + + if (imagePre == null) { + Log.d(cgSettings.tag, "cgAddressImg.getDrawable: Failed to obtain image"); + + return null; + } + + final BitmapDrawable image = new BitmapDrawable(imagePre); + image.setBounds(new Rect(0, 0, imagePre.getWidth(), imagePre.getHeight())); + + // imagePre.recycle(); + imagePre = null; + + return image; + } +} diff --git a/src/cgeo/geocaching/cgBase.java b/src/cgeo/geocaching/cgBase.java new file mode 100644 index 0000000..9a13bda --- /dev/null +++ b/src/cgeo/geocaching/cgBase.java @@ -0,0 +1,5828 @@ +package cgeo.geocaching; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import java.io.InputStreamReader; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.BufferedReader; +import java.io.IOException; +import java.net.URL; +import java.net.URLEncoder; +import java.net.URLConnection; +import java.net.HttpURLConnection; +import java.util.Map; +import java.util.Calendar; +import java.util.Locale; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import android.util.Log; +import android.content.SharedPreferences; +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.net.Uri; +import android.os.Handler; +import android.os.Message; +import android.text.Html; +import android.text.Spannable; +import android.text.style.StrikethroughSpan; +import android.view.Display; +import android.view.View; +import android.view.WindowManager; +import android.widget.ProgressBar; +import android.widget.TextView; +import com.google.android.apps.analytics.GoogleAnalyticsTracker; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.URLDecoder; +import java.security.MessageDigest; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.json.JSONArray; +import org.json.JSONObject; + +public class cgBase { + + public static HashMap<String, String> cacheTypes = new HashMap<String, String>(); + public static HashMap<String, String> cacheTypesInv = new HashMap<String, String>(); + public static HashMap<String, String> cacheIDs = new HashMap<String, String>(); + public static HashMap<String, String> cacheIDsChoices = new HashMap<String, String>(); + public static HashMap<String, String> waypointTypes = new HashMap<String, String>(); + public static HashMap<String, Integer> logTypes = new HashMap<String, Integer>(); + public static HashMap<String, Integer> logTypes0 = new HashMap<String, Integer>(); + public static HashMap<Integer, String> logTypes1 = new HashMap<Integer, String>(); + public static HashMap<Integer, String> logTypes2 = new HashMap<Integer, String>(); + public static HashMap<Integer, String> logTypesTrackable = new HashMap<Integer, String>(); + public static HashMap<Integer, String> logTypesTrackableAction = new HashMap<Integer, String>(); + public static HashMap<Integer, String> errorRetrieve = new HashMap<Integer, String>(); + public static SimpleDateFormat dateIn = new SimpleDateFormat("MM/dd/yyyy"); + public static SimpleDateFormat dateEvIn = new SimpleDateFormat("dd MMMMM yyyy", Locale.ENGLISH); // 28 March 2009 + public static SimpleDateFormat dateTbIn1 = new SimpleDateFormat("EEEEE, dd MMMMM yyyy", Locale.ENGLISH); // Saturday, 28 March 2009 + public static SimpleDateFormat dateTbIn2 = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy", Locale.ENGLISH); // Saturday, March 28, 2009 + public static SimpleDateFormat dateSqlIn = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 2010-07-25 14:44:01 + public static SimpleDateFormat dateGPXIn = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); // 2010-04-20T07:00:00Z + public static DateFormat dateOut = DateFormat.getDateInstance(DateFormat.LONG, Locale.getDefault()); + public static DateFormat timeOut = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.getDefault()); + public static DateFormat dateOutShort = DateFormat.getDateInstance(DateFormat.SHORT, Locale.getDefault()); + private Resources res = null; + private HashMap<String, String> cookies = new HashMap<String, String>(); + private final String passMatch = "[/\\?&]*[Pp]ass(word)?=[^&^#^$]+"; + private final Pattern patternLoggedIn = Pattern.compile("<span class=\"Success\">You are logged in as[^<]*<strong[^>]*>([^<]+)</strong>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final Pattern patternLogged2In = Pattern.compile("<strong>[^\\w]*Hello,[^<]*<a[^>]+>([^<]+)</a>[^<]*</strong>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final Pattern patternViewstate = Pattern.compile("id=\"__VIEWSTATE\"[^(value)]+value=\"([^\"]+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + private final Pattern patternViewstate1 = Pattern.compile("id=\"__VIEWSTATE1\"[^(value)]+value=\"([^\"]+)\"[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + public static final double kmInMiles = 1 / 1.609344; + public static final double deg2rad = Math.PI / 180; + public static final double rad2deg = 180 / Math.PI; + public static final float erad = 6371.0f; + public static final int mapAppAny = 0; + public static final int mapAppLocus = 1; + public static final int mapAppRmaps = 2; + private cgeoapplication app = null; + private cgSettings settings = null; + private SharedPreferences prefs = null; + public String version = null; + private String idBrowser = "Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4"; + Context context = null; + final private static HashMap<String, Integer> gcIcons = new HashMap<String, Integer>(); + final private static HashMap<String, Integer> wpIcons = new HashMap<String, Integer>(); + + public static final int LOG_FOUND_IT = 2; + public static final int LOG_DIDNT_FIND_IT = 3; + public static final int LOG_NOTE = 4; + public static final int LOG_PUBLISH_LISTING = 1003; // unknown ID; used number doesn't match any GC.com's ID + public static final int LOG_ENABLE_LISTING = 23; + public static final int LOG_ARCHIVE = 5; + public static final int LOG_TEMP_DISABLE_LISTING = 22; + public static final int LOG_NEEDS_ARCHIVE = 7; + public static final int LOG_WILL_ATTEND = 9; + public static final int LOG_ATTENDED = 10; + public static final int LOG_RETRIEVED_IT = 13; + public static final int LOG_PLACED_IT = 14; + public static final int LOG_GRABBED_IT = 19; + public static final int LOG_NEEDS_MAINTENANCE = 45; + public static final int LOG_OWNER_MAINTENANCE = 46; + public static final int LOG_UPDATE_COORDINATES = 47; + public static final int LOG_DISCOVERED_IT = 48; + public static final int LOG_POST_REVIEWER_NOTE = 18; + public static final int LOG_VISIT = 1001; // unknown ID; used number doesn't match any GC.com's ID + public static final int LOG_WEBCAM_PHOTO_TAKEN = 11; + public static final int LOG_ANNOUNCEMENT = 74; + + public cgBase(cgeoapplication appIn, cgSettings settingsIn, SharedPreferences prefsIn) { + context = appIn.getBaseContext(); + res = appIn.getBaseContext().getResources(); + + // cache types + cacheTypes.put("traditional cache", "traditional"); + cacheTypes.put("multi-cache", "multi"); + cacheTypes.put("unknown cache", "mystery"); + cacheTypes.put("letterbox hybrid", "letterbox"); + cacheTypes.put("event cache", "event"); + cacheTypes.put("mega-event cache", "mega"); + cacheTypes.put("earthcache", "earth"); + cacheTypes.put("cache in trash out event", "cito"); + cacheTypes.put("webcam cache", "webcam"); + cacheTypes.put("virtual cache", "virtual"); + cacheTypes.put("wherigo cache", "wherigo"); + cacheTypes.put("lost & found", "lostfound"); + cacheTypes.put("project ape cache", "ape"); + cacheTypes.put("groundspeak hq", "gchq"); + cacheTypes.put("gps cache exhibit", "gps"); + + // cache types inverted + cacheTypesInv.put("traditional", res.getString(R.string.traditional)); + cacheTypesInv.put("multi", res.getString(R.string.multi)); + cacheTypesInv.put("mystery", res.getString(R.string.mystery)); + cacheTypesInv.put("letterbox", res.getString(R.string.letterbox)); + cacheTypesInv.put("event", res.getString(R.string.event)); + cacheTypesInv.put("mega", res.getString(R.string.mega)); + cacheTypesInv.put("earth", res.getString(R.string.earth)); + cacheTypesInv.put("cito", res.getString(R.string.cito)); + cacheTypesInv.put("webcam", res.getString(R.string.webcam)); + cacheTypesInv.put("virtual", res.getString(R.string.virtual)); + cacheTypesInv.put("wherigo", res.getString(R.string.wherigo)); + cacheTypesInv.put("lostfound", res.getString(R.string.lostfound)); + cacheTypesInv.put("ape", res.getString(R.string.ape)); + cacheTypesInv.put("gchq", res.getString(R.string.gchq)); + cacheTypesInv.put("gps", res.getString(R.string.gps)); + + // cache ids + cacheIDs.put("all", "9a79e6ce-3344-409c-bbe9-496530baf758"); // hard-coded also in cgSettings + cacheIDs.put("traditional", "32bc9333-5e52-4957-b0f6-5a2c8fc7b257"); + cacheIDs.put("multi", "a5f6d0ad-d2f2-4011-8c14-940a9ebf3c74"); + cacheIDs.put("mystery", "40861821-1835-4e11-b666-8d41064d03fe"); + cacheIDs.put("letterbox", "4bdd8fb2-d7bc-453f-a9c5-968563b15d24"); + cacheIDs.put("event", "69eb8534-b718-4b35-ae3c-a856a55b0874"); + cacheIDs.put("mega-event", "69eb8535-b718-4b35-ae3c-a856a55b0874"); + cacheIDs.put("earth", "c66f5cf3-9523-4549-b8dd-759cd2f18db8"); + cacheIDs.put("cito", "57150806-bc1a-42d6-9cf0-538d171a2d22"); + cacheIDs.put("webcam", "31d2ae3c-c358-4b5f-8dcd-2185bf472d3d"); + cacheIDs.put("virtual", "294d4360-ac86-4c83-84dd-8113ef678d7e"); + cacheIDs.put("wherigo", "0544fa55-772d-4e5c-96a9-36a51ebcf5c9"); + cacheIDs.put("lostfound", "3ea6533d-bb52-42fe-b2d2-79a3424d4728"); + cacheIDs.put("ape", "2555690d-b2bc-4b55-b5ac-0cb704c0b768"); + cacheIDs.put("gchq", "416f2494-dc17-4b6a-9bab-1a29dd292d8c"); + cacheIDs.put("gps", "72e69af2-7986-4990-afd9-bc16cbbb4ce3"); + + // cache choices + cacheIDsChoices.put(res.getString(R.string.all), cacheIDs.get("all")); + cacheIDsChoices.put(res.getString(R.string.traditional), cacheIDs.get("traditional")); + cacheIDsChoices.put(res.getString(R.string.multi), cacheIDs.get("multi")); + cacheIDsChoices.put(res.getString(R.string.mystery), cacheIDs.get("mystery")); + cacheIDsChoices.put(res.getString(R.string.letterbox), cacheIDs.get("letterbox")); + cacheIDsChoices.put(res.getString(R.string.event), cacheIDs.get("event")); + cacheIDsChoices.put(res.getString(R.string.mega), cacheIDs.get("mega")); + cacheIDsChoices.put(res.getString(R.string.earth), cacheIDs.get("earth")); + cacheIDsChoices.put(res.getString(R.string.cito), cacheIDs.get("cito")); + cacheIDsChoices.put(res.getString(R.string.webcam), cacheIDs.get("webcam")); + cacheIDsChoices.put(res.getString(R.string.virtual), cacheIDs.get("virtual")); + cacheIDsChoices.put(res.getString(R.string.wherigo), cacheIDs.get("whereigo")); + cacheIDsChoices.put(res.getString(R.string.lostfound), cacheIDs.get("lostfound")); + cacheIDsChoices.put(res.getString(R.string.ape), cacheIDs.get("ape")); + cacheIDsChoices.put(res.getString(R.string.gchq), cacheIDs.get("gchq")); + cacheIDsChoices.put(res.getString(R.string.gps), cacheIDs.get("gps")); + + // waypoint types + waypointTypes.put("flag", res.getString(R.string.wp_final)); + waypointTypes.put("stage", res.getString(R.string.wp_stage)); + waypointTypes.put("puzzle", res.getString(R.string.wp_puzzle)); + waypointTypes.put("pkg", res.getString(R.string.wp_pkg)); + waypointTypes.put("trailhead", res.getString(R.string.wp_trailhead)); + waypointTypes.put("waypoint", res.getString(R.string.wp_waypoint)); + + // log types + logTypes.put("icon_smile", LOG_FOUND_IT); + logTypes.put("icon_sad", LOG_DIDNT_FIND_IT); + logTypes.put("icon_note", LOG_NOTE); + logTypes.put("icon_greenlight", LOG_PUBLISH_LISTING); + logTypes.put("icon_enabled", LOG_ENABLE_LISTING); + logTypes.put("traffic_cone", LOG_ARCHIVE); + logTypes.put("icon_disabled", LOG_TEMP_DISABLE_LISTING); + logTypes.put("icon_remove", LOG_NEEDS_ARCHIVE); + logTypes.put("icon_rsvp", LOG_WILL_ATTEND); + logTypes.put("icon_attended", LOG_ATTENDED); + logTypes.put("picked_up", LOG_RETRIEVED_IT); + logTypes.put("dropped_off", LOG_PLACED_IT); + logTypes.put("transfer", LOG_GRABBED_IT); + logTypes.put("icon_needsmaint", LOG_NEEDS_MAINTENANCE); + logTypes.put("icon_maint", LOG_OWNER_MAINTENANCE); + logTypes.put("coord_update", LOG_UPDATE_COORDINATES); + logTypes.put("icon_discovered", LOG_DISCOVERED_IT); + logTypes.put("big_smile", LOG_POST_REVIEWER_NOTE); + logTypes.put("icon_visited", LOG_VISIT); // unknown ID; used number doesn't match any GC.com's ID + logTypes.put("icon_camera", LOG_WEBCAM_PHOTO_TAKEN); // unknown ID; used number doesn't match any GC.com's ID + logTypes.put("icon_announcement", LOG_ANNOUNCEMENT); // unknown ID; used number doesn't match any GC.com's ID + + logTypes0.put("found it", LOG_FOUND_IT); + logTypes0.put("didn't find it", LOG_DIDNT_FIND_IT); + logTypes0.put("write note", LOG_NOTE); + logTypes0.put("publish listing", LOG_PUBLISH_LISTING); + logTypes0.put("enable listing", LOG_ENABLE_LISTING); + logTypes0.put("archive", LOG_ARCHIVE); + logTypes0.put("temporarily disable listing", LOG_TEMP_DISABLE_LISTING); + logTypes0.put("needs archived", LOG_NEEDS_ARCHIVE); + logTypes0.put("will attend", LOG_WILL_ATTEND); + logTypes0.put("attended", LOG_ATTENDED); + logTypes0.put("retrieved it", LOG_RETRIEVED_IT); + logTypes0.put("placed it", LOG_PLACED_IT); + logTypes0.put("grabbed it", LOG_GRABBED_IT); + logTypes0.put("needs maintenance", LOG_NEEDS_MAINTENANCE); + logTypes0.put("owner maintenance", LOG_OWNER_MAINTENANCE); + logTypes0.put("update coordinates", LOG_UPDATE_COORDINATES); + logTypes0.put("discovered it", LOG_DISCOVERED_IT); + logTypes0.put("post reviewer note", LOG_POST_REVIEWER_NOTE); + logTypes0.put("visit", LOG_VISIT); // unknown ID; used number doesn't match any GC.com's ID + logTypes0.put("webcam photo taken", LOG_WEBCAM_PHOTO_TAKEN); // unknown ID; used number doesn't match any GC.com's ID + logTypes0.put("announcement", LOG_ANNOUNCEMENT); // unknown ID; used number doesn't match any GC.com's ID + + logTypes1.put(LOG_FOUND_IT, res.getString(R.string.log_found)); + logTypes1.put(LOG_DIDNT_FIND_IT, res.getString(R.string.log_dnf)); + logTypes1.put(LOG_NOTE, res.getString(R.string.log_note)); + logTypes1.put(LOG_PUBLISH_LISTING, res.getString(R.string.log_published)); + logTypes1.put(LOG_ENABLE_LISTING, res.getString(R.string.log_enabled)); + logTypes1.put(LOG_ARCHIVE, res.getString(R.string.log_archived)); + logTypes1.put(LOG_TEMP_DISABLE_LISTING, res.getString(R.string.log_disabled)); + logTypes1.put(LOG_NEEDS_ARCHIVE, res.getString(R.string.log_needs_archived)); + logTypes1.put(LOG_WILL_ATTEND, res.getString(R.string.log_attend)); + logTypes1.put(LOG_ATTENDED, res.getString(R.string.log_attended)); + logTypes1.put(LOG_RETRIEVED_IT, res.getString(R.string.log_retrieved)); + logTypes1.put(LOG_PLACED_IT, res.getString(R.string.log_placed)); + logTypes1.put(LOG_GRABBED_IT, res.getString(R.string.log_grabbed)); + logTypes1.put(LOG_NEEDS_MAINTENANCE, res.getString(R.string.log_maintenance_needed)); + logTypes1.put(LOG_OWNER_MAINTENANCE, res.getString(R.string.log_maintained)); + logTypes1.put(LOG_UPDATE_COORDINATES, res.getString(R.string.log_update)); + logTypes1.put(LOG_DISCOVERED_IT, res.getString(R.string.log_discovered)); + logTypes1.put(LOG_POST_REVIEWER_NOTE, res.getString(R.string.log_reviewed)); + logTypes1.put(LOG_VISIT, res.getString(R.string.log_taken)); + logTypes1.put(LOG_WEBCAM_PHOTO_TAKEN, res.getString(R.string.log_webcam)); + logTypes1.put(LOG_ANNOUNCEMENT, res.getString(R.string.log_announcement)); + + logTypes2.put(LOG_FOUND_IT, res.getString(R.string.log_found)); // traditional, multi, unknown, earth, wherigo, virtual, letterbox + logTypes2.put(LOG_DIDNT_FIND_IT, res.getString(R.string.log_dnf)); // traditional, multi, unknown, earth, wherigo, virtual, letterbox, webcam + logTypes2.put(LOG_NOTE, res.getString(R.string.log_note)); // traditional, multi, unknown, earth, wherigo, virtual, event, letterbox, webcam, trackable + logTypes2.put(LOG_PUBLISH_LISTING, res.getString(R.string.log_published)); // X + logTypes2.put(LOG_ENABLE_LISTING, res.getString(R.string.log_enabled)); // owner + logTypes2.put(LOG_ARCHIVE, res.getString(R.string.log_archived)); // traditional, multi, unknown, earth, event, wherigo, virtual, letterbox, webcam + logTypes2.put(LOG_TEMP_DISABLE_LISTING, res.getString(R.string.log_disabled)); // owner + logTypes2.put(LOG_NEEDS_ARCHIVE, res.getString(R.string.log_needs_archived)); // traditional, multi, unknown, earth, event, wherigo, virtual, letterbox, webcam + logTypes2.put(LOG_WILL_ATTEND, res.getString(R.string.log_attend)); // event + logTypes2.put(LOG_ATTENDED, res.getString(R.string.log_attended)); // event + logTypes2.put(LOG_WEBCAM_PHOTO_TAKEN, res.getString(R.string.log_webcam)); // webcam + logTypes2.put(LOG_RETRIEVED_IT, res.getString(R.string.log_retrieved)); //trackable + logTypes2.put(LOG_GRABBED_IT, res.getString(R.string.log_grabbed)); //trackable + logTypes2.put(LOG_NEEDS_MAINTENANCE, res.getString(R.string.log_maintenance_needed)); // traditional, unknown, multi, wherigo, virtual, letterbox, webcam + logTypes2.put(LOG_OWNER_MAINTENANCE, res.getString(R.string.log_maintained)); // owner + logTypes2.put(LOG_DISCOVERED_IT, res.getString(R.string.log_discovered)); //trackable + logTypes2.put(LOG_POST_REVIEWER_NOTE, res.getString(R.string.log_reviewed)); // X + logTypes2.put(LOG_ANNOUNCEMENT, res.getString(R.string.log_announcement)); // X + + // trackables for logs + logTypesTrackable.put(0, res.getString(R.string.log_tb_nothing)); // do nothing + logTypesTrackable.put(1, res.getString(R.string.log_tb_visit)); // visit cache + logTypesTrackable.put(2, res.getString(R.string.log_tb_drop)); // drop here + logTypesTrackableAction.put(0, ""); // do nothing + logTypesTrackableAction.put(1, "_Visited"); // visit cache + logTypesTrackableAction.put(2, "_DroppedOff"); // drop here + + // retrieving errors (because of ____ ) + errorRetrieve.put(1, res.getString(R.string.err_none)); + errorRetrieve.put(0, res.getString(R.string.err_start)); + errorRetrieve.put(-1, res.getString(R.string.err_parse)); + errorRetrieve.put(-2, res.getString(R.string.err_server)); + errorRetrieve.put(-3, res.getString(R.string.err_login)); + errorRetrieve.put(-4, res.getString(R.string.err_unknown)); + errorRetrieve.put(-5, res.getString(R.string.err_comm)); + errorRetrieve.put(-6, res.getString(R.string.err_wrong)); + errorRetrieve.put(-7, res.getString(R.string.err_license)); + + // init + app = appIn; + settings = settingsIn; + prefs = prefsIn; + + try { + PackageManager manager = app.getPackageManager(); + PackageInfo info = manager.getPackageInfo(app.getPackageName(), 0); + version = info.versionName; + } catch (Exception e) { + // nothing + } + + if (settings.asBrowser == 1) { + final long rndBrowser = Math.round(Math.random() * 6); + if (rndBrowser == 0) { + idBrowser = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.1 (KHTML, like Gecko) Chrome/5.0.322.2 Safari/533.1"; + } else if (rndBrowser == 1) { + idBrowser = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; MDDC)"; + } else if (rndBrowser == 2) { + idBrowser = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3"; + } else if (rndBrowser == 3) { + idBrowser = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10"; + } else if (rndBrowser == 4) { + idBrowser = "Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5H11a Safari/525.20"; + } else if (rndBrowser == 5) { + idBrowser = "Mozilla/5.0 (Linux; U; Android 1.1; en-gb; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2"; + } else if (rndBrowser == 6) { + idBrowser = "Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4"; + } else { + idBrowser = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/532.9 (KHTML, like Gecko) Chrome/5.0.307.11 Safari/532.9"; + } + } + } + + public String findViewstate(String page, int index) { + String viewstate = null; + + if (index == 0) { + final Matcher matcherViewstate = patternViewstate.matcher(page); + while (matcherViewstate.find()) { + if (matcherViewstate.groupCount() > 0) { + viewstate = matcherViewstate.group(1); + } + } + } else if (index == 1) { + final Matcher matcherViewstate = patternViewstate1.matcher(page); + while (matcherViewstate.find()) { + if (matcherViewstate.groupCount() > 0) { + viewstate = matcherViewstate.group(1); + } + } + } + + return viewstate; + } + + public class loginThread extends Thread { + + @Override + public void run() { + login(); + } + } + + public int login() { + final String host = "www.geocaching.com"; + final String path = "/login/default.aspx"; + cgResponse loginResponse = null; + String loginData = null; + + String viewstate = null; + String viewstate1 = null; + + final HashMap<String, String> loginStart = settings.getLogin(); + + if (loginStart == null) { + return -3; // no login information stored + } + + loginResponse = request(true, host, path, "GET", new HashMap<String, String>(), false, false, false); + loginData = loginResponse.getData(); + if (loginData != null && loginData.length() > 0) { + if (checkLogin(loginData) == true) { + Log.i(cgSettings.tag, "Already logged in Geocaching.com as " + loginStart.get("username")); + + switchToEnglish(viewstate, viewstate1); + + return 1; // logged in + } + + viewstate = findViewstate(loginData, 0); + viewstate1 = findViewstate(loginData, 1); + + if (viewstate == null || viewstate.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.login: Failed to find viewstate"); + return -1; // no viewstate + } + } else { + Log.e(cgSettings.tag, "cgeoBase.login: Failed to retrieve login page (1st)"); + return -2; // no loginpage + } + + final HashMap<String, String> login = settings.getLogin(); + final HashMap<String, String> params = new HashMap<String, String>(); + + if (login == null || login.get("username") == null || login.get("username").length() == 0 || login.get("password") == null || login.get("password").length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.login: No login information stored"); + return -3; + } + + settings.deleteCookies(); + + params.put("__EVENTTARGET", ""); + params.put("__EVENTARGUMENT", ""); + params.put("__VIEWSTATE", viewstate); + if (viewstate1 != null) { + params.put("__VIEWSTATE1", viewstate1); + params.put("__VIEWSTATEFIELDCOUNT", "2"); + } + params.put("ctl00$SiteContent$tbUsername", login.get("username")); + params.put("ctl00$SiteContent$tbPassword", login.get("password")); + params.put("ctl00$SiteContent$cbRememberMe", "on"); + params.put("ctl00$SiteContent$btnSignIn", "Login"); + + loginResponse = request(true, host, path, "POST", params, false, false, false); + loginData = loginResponse.getData(); + + if (loginData != null && loginData.length() > 0) { + if (checkLogin(loginData) == true) { + Log.i(cgSettings.tag, "Successfully logged in Geocaching.com as " + login.get("username")); + + switchToEnglish(findViewstate(loginData, 0), findViewstate(loginData, 1)); + + return 1; // logged in + } else { + if (loginData.indexOf("Your username/password combination does not match.") != -1) { + Log.i(cgSettings.tag, "Failed to log in Geocaching.com as " + login.get("username") + " because of wrong username/password"); + + return -6; // wrong login + } else { + Log.i(cgSettings.tag, "Failed to log in Geocaching.com as " + login.get("username") + " for some unknown reason"); + + return -4; // can't login + } + } + } else { + Log.e(cgSettings.tag, "cgeoBase.login: Failed to retrieve login page (2nd)"); + + return -5; // no login page + } + } + + public Boolean checkLogin(String page) { + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.checkLogin: No page given"); + return false; + } + + // on every page + final Matcher matcherLogged2In = patternLogged2In.matcher(page); + while (matcherLogged2In.find()) { + return true; + } + + // after login + final Matcher matcherLoggedIn = patternLoggedIn.matcher(page); + while (matcherLoggedIn.find()) { + return true; + } + + return false; + } + + public String switchToEnglish(String viewstate, String viewstate1) { + final String host = "www.geocaching.com"; + final String path = "/default.aspx"; + final HashMap<String, String> params = new HashMap<String, String>(); + + if (viewstate != null) { + params.put("__VIEWSTATE", viewstate); + } + if (viewstate1 != null) { + params.put("__VIEWSTATE1", viewstate1); + params.put("__VIEWSTATEFIELDCOUNT", "2"); + } + params.put("__EVENTTARGET", "ctl00$uxLocaleList$uxLocaleList$ctl00$uxLocaleItem"); // switch to english + params.put("__EVENTARGUMENT", ""); + + return request(false, host, path, "POST", params, false, false, false).getData(); + } + + public cgCacheWrap parseSearch(cgSearchThread thread, String url, String page, boolean showCaptcha) { + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.parseSearch: No page given"); + return null; + } + + final cgCacheWrap caches = new cgCacheWrap(); + final ArrayList<String> cids = new ArrayList<String>(); + final ArrayList<String> guids = new ArrayList<String>(); + String recaptchaChallenge = null; + String recaptchaText = null; + + caches.url = url; + + final Pattern patternCacheType = Pattern.compile("<td class=\"Merge\">[^<]*<a href=\"[^\"]*/seek/cache_details\\.aspx\\?guid=[^\"]+\"[^>]+>[^<]*<img src=\"[^\"]*/images/wpttypes/[^\\.]+\\.gif\" alt=\"([^\"]+)\" title=\"[^\"]+\"[^>]*>[^<]*</a>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternGuidAndDisabled = Pattern.compile("<img src=\"[^\"]*/images/wpttypes/[^>]*>[^<]*</a></td><td class=\"Merge\">[^<]*<a href=\"[^\"]*/seek/cache_details\\.aspx\\?guid=([a-z0-9\\-]+)\" class=\"lnk([^\"]*)\">([^<]*<span>)?([^<]*)(</span>[^<]*)?</a>[^<]+<br />([^<]*)<span[^>]+>([^<]*)</span>([^<]*<img[^>]+>)?[^<]*<br />[^<]*</td>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternTbs = Pattern.compile("<a id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxTravelBugList\" class=\"tblist\" data-tbcount=\"([0-9]+)\" data-id=\"[^\"]*\"[^>]*>(.*)</a>", Pattern.CASE_INSENSITIVE); + final Pattern patternTbsInside = Pattern.compile("(<img src=\"[^\"]+\" alt=\"([^\"]+)\" title=\"[^\"]*\" />[^<]*)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternDirection = Pattern.compile("<img id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxDistanceAndHeading\" title=\"[^\"]*\" src=\"[^\"]*/seek/CacheDir\\.ashx\\?k=([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); + final Pattern patternCode = Pattern.compile("\\|[^\\w]*(GC[a-z0-9]+)[^\\|]*\\|", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternId = Pattern.compile("name=\"CID\"[^v]*value=\"([0-9]+)\"", Pattern.CASE_INSENSITIVE); + final Pattern patternFavourite = Pattern.compile("<span id=\"ctl00_ContentBody_dlResults_ctl[0-9]+_uxFavoritesValue\" title=\"[^\"]*\" class=\"favorite-rank\">([0-9]+)</span>", Pattern.CASE_INSENSITIVE); + final Pattern patternTotalCnt = Pattern.compile("<td class=\"PageBuilderWidget\"><span>Total Records[^<]*<b>(\\d+)<\\/b>", Pattern.CASE_INSENSITIVE); + final Pattern patternRecaptcha = Pattern.compile("<script[^>]*src=\"[^\"]*/recaptcha/api/challenge\\?k=([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); + final Pattern patternRecaptchaChallenge = Pattern.compile("challenge : '([^']+)'", Pattern.CASE_INSENSITIVE); + + caches.viewstate = findViewstate(page, 0); + caches.viewstate1 = findViewstate(page, 1); + + // recaptcha + if (showCaptcha == true) { + try { + String recaptchaJsParam = null; + final Matcher matcherRecaptcha = patternRecaptcha.matcher(page); + while (matcherRecaptcha.find()) { + if (matcherRecaptcha.groupCount() > 0) { + recaptchaJsParam = matcherRecaptcha.group(1); + } + } + + if (recaptchaJsParam != null) { + final String recaptchaJs = request(false, "www.google.com", "/recaptcha/api/challenge", "GET", "k=" + urlencode_rfc3986(recaptchaJsParam.trim()), 0, true).getData(); + + if (recaptchaJs != null && recaptchaJs.length() > 0) { + final Matcher matcherRecaptchaChallenge = patternRecaptchaChallenge.matcher(recaptchaJs); + while (matcherRecaptchaChallenge.find()) { + if (matcherRecaptchaChallenge.groupCount() > 0) { + recaptchaChallenge = matcherRecaptchaChallenge.group(1).trim(); + } + } + } + } + } catch (Exception e) { + // failed to parse recaptcha challenge + Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse recaptcha challenge"); + } + + if (thread != null && recaptchaChallenge != null && recaptchaChallenge.length() > 0) { + thread.setChallenge(recaptchaChallenge); + thread.notifyNeed(); + } + } + + int startPos = -1; + int endPos = -1; + + startPos = page.indexOf("<div id=\"ctl00_ContentBody_ResultsPanel\""); + if (startPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseSearch: ID \"ctl00_ContentBody_dlResults\" not found on page"); + return null; + } + + page = page.substring(startPos); // cut on <table + + startPos = page.indexOf(">"); + endPos = page.indexOf("ctl00_ContentBody_UnitTxt"); + if (startPos == -1 || endPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseSearch: ID \"ctl00_ContentBody_UnitTxt\" not found on page"); + return null; + } + + page = page.substring(startPos + 1, endPos - startPos + 1); // cut between <table> and </table> + + final String[] rows = page.split("<tr class="); + final int rows_count = rows.length; + + for (int z = 1; z < rows_count; z++) { + cgCache cache = new cgCache(); + String row = rows[z]; + + // check for cache type presence + if (row.indexOf("images/wpttypes") == -1) { + continue; + } + + try { + final Matcher matcherGuidAndDisabled = patternGuidAndDisabled.matcher(row); + + while (matcherGuidAndDisabled.find()) { + if (matcherGuidAndDisabled.groupCount() > 0) { + guids.add(matcherGuidAndDisabled.group(1)); + + cache.guid = matcherGuidAndDisabled.group(1); + if (matcherGuidAndDisabled.group(4) != null) { + cache.name = Html.fromHtml(matcherGuidAndDisabled.group(4).trim()).toString(); + } + if (matcherGuidAndDisabled.group(6) != null) { + cache.location = Html.fromHtml(matcherGuidAndDisabled.group(6).trim()).toString(); + } + + final String attr = matcherGuidAndDisabled.group(2); + if (attr != null) { + if (attr.contains("Strike") == true) { + cache.disabled = true; + } else { + cache.disabled = false; + } + + if (attr.contains("OldWarning") == true) { + cache.archived = true; + } else { + cache.archived = false; + } + } + } + } + } catch (Exception e) { + // failed to parse GUID and/or Disabled + Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse GUID and/or Disabled data"); + } + + if (settings.excludeDisabled == 1 && (cache.disabled == true || cache.archived == true)) { + // skip disabled and archived caches + cache = null; + continue; + } + + String inventoryPre = null; + + // GC* code + try { + final Matcher matcherCode = patternCode.matcher(row); + while (matcherCode.find()) { + if (matcherCode.groupCount() > 0) { + cache.geocode = matcherCode.group(1).toUpperCase(); + } + } + } catch (Exception e) { + // failed to parse code + Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse cache code"); + } + + // cache type + try { + final Matcher matcherCacheType = patternCacheType.matcher(row); + while (matcherCacheType.find()) { + if (matcherCacheType.groupCount() > 0) { + cache.type = cacheTypes.get(matcherCacheType.group(1).toLowerCase()); + } + } + } catch (Exception e) { + // failed to parse type + Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse cache type"); + } + + // cache direction - image + try { + final Matcher matcherDirection = patternDirection.matcher(row); + while (matcherDirection.find()) { + if (matcherDirection.groupCount() > 0) { + cache.directionImg = matcherDirection.group(1); + } + } + } catch (Exception e) { + // failed to parse direction image + Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse cache direction image"); + } + + // cache inventory + try { + final Matcher matcherTbs = patternTbs.matcher(row); + while (matcherTbs.find()) { + if (matcherTbs.groupCount() > 0) { + cache.inventoryItems = Integer.parseInt(matcherTbs.group(1)); + inventoryPre = matcherTbs.group(2); + } + } + } catch (Exception e) { + // failed to parse inventory + Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse cache inventory (1)"); + } + + if (inventoryPre != null && inventoryPre.trim().length() > 0) { + try { + final Matcher matcherTbsInside = patternTbsInside.matcher(inventoryPre); + while (matcherTbsInside.find()) { + if (matcherTbsInside.groupCount() == 2 && matcherTbsInside.group(2) != null) { + final String inventoryItem = matcherTbsInside.group(2).toLowerCase(); + if (inventoryItem.equals("premium member only cache")) { + continue; + } else { + if (cache.inventoryItems <= 0) { + cache.inventoryItems = 1; + } + } + } + } + } catch (Exception e) { + // failed to parse cache inventory info + Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse cache inventory info"); + } + } + + // premium cache + if (row.indexOf("/images/small_profile.gif") != -1) { + cache.members = true; + } else { + cache.members = false; + } + + // found it + if (row.indexOf("/images/icons/icon_smile") != -1) { + cache.found = true; + } else { + cache.found = false; + } + + // own it + if (row.indexOf("/images/silk/star.png") != -1) { + cache.own = true; + } else { + cache.own = false; + } + + // id + try { + final Matcher matcherId = patternId.matcher(row); + while (matcherId.find()) { + if (matcherId.groupCount() > 0) { + cache.cacheid = matcherId.group(1); + cids.add(cache.cacheid); + } + } + } catch (Exception e) { + // failed to parse cache id + Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse cache id"); + } + + // favourite count + try { + final Matcher matcherFavourite = patternFavourite.matcher(row); + while (matcherFavourite.find()) { + if (matcherFavourite.groupCount() > 0) { + cache.favouriteCnt = Integer.parseInt(matcherFavourite.group(1)); + } + } + } catch (Exception e) { + // failed to parse favourite count + Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse favourite count"); + } + + if (cache.nameSp == null) { + cache.nameSp = (new Spannable.Factory()).newSpannable(cache.name); + if (cache.disabled == true || cache.archived == true) { // strike + cache.nameSp.setSpan(new StrikethroughSpan(), 0, cache.nameSp.toString().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + caches.cacheList.add(cache); + } + + // total caches found + try { + final Matcher matcherTotalCnt = patternTotalCnt.matcher(page); + while (matcherTotalCnt.find()) { + if (matcherTotalCnt.groupCount() > 0) { + if (matcherTotalCnt.group(1) != null) { + caches.totalCnt = new Integer(matcherTotalCnt.group(1)); + } + } + } + } catch (Exception e) { + // failed to parse cache count + Log.w(cgSettings.tag, "cgeoBase.parseSearch: Failed to parse cache count"); + } + + if (thread != null && recaptchaChallenge != null) { + if (thread.getText() == null) { + thread.waitForUser(); + } + + recaptchaText = thread.getText(); + } + + if (cids.size() > 0 && (recaptchaChallenge == null || (recaptchaChallenge != null && recaptchaText != null && recaptchaText.length() > 0))) { + Log.i(cgSettings.tag, "Trying to get .loc for " + cids.size() + " caches"); + + try { + // get coordinates for parsed caches + final String host = "www.geocaching.com"; + final String path = "/seek/nearest.aspx"; + final StringBuilder params = new StringBuilder(); + params.append("__EVENTTARGET="); + params.append("&"); + params.append("__EVENTARGUMENT="); + params.append("&"); + params.append("__VIEWSTATE="); + params.append(urlencode_rfc3986(caches.viewstate)); + if (caches.viewstate1 != null) { + params.append("&"); + params.append("__VIEWSTATE1="); + params.append(urlencode_rfc3986(caches.viewstate1)); + params.append("&"); + params.append("__VIEWSTATEFIELDCOUNT=2"); + } + + for (String cid : cids) { + params.append("&"); + params.append("CID="); + params.append(urlencode_rfc3986(cid)); + } + + if (recaptchaChallenge != null && recaptchaText != null && recaptchaText.length() > 0) { + params.append("&"); + params.append("recaptcha_challenge_field="); + params.append(urlencode_rfc3986(recaptchaChallenge)); + params.append("&"); + params.append("recaptcha_response_field="); + params.append(urlencode_rfc3986(recaptchaText)); + } + params.append("&"); + params.append("ctl00%24ContentBody%24uxDownloadLoc=Download+Waypoints"); + + final String coordinates = request(false, host, path, "POST", params.toString(), 0, true).getData(); + + if (coordinates != null && coordinates.length() > 0) { + if (coordinates.indexOf("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") > -1) { + Log.i(cgSettings.tag, "User has not agreed to the license agreement. Can\'t download .loc file."); + + caches.error = errorRetrieve.get(-7); + + return caches; + } + } + + if (coordinates != null && coordinates.length() > 0) { + final HashMap<String, cgCoord> cidCoords = new HashMap<String, cgCoord>(); + final Pattern patternCidCode = Pattern.compile("name id=\"([^\"]+)\""); + final Pattern patternCidLat = Pattern.compile("lat=\"([^\"]+)\""); + final Pattern patternCidLon = Pattern.compile("lon=\"([^\"]+)\""); + // premium only >> + final Pattern patternCidDif = Pattern.compile("<difficulty>([^<]+)</difficulty>"); + final Pattern patternCidTer = Pattern.compile("<terrain>([^<]+)</terrain>"); + final Pattern patternCidCon = Pattern.compile("<container>([^<]+)</container>"); + // >> premium only + + final String[] points = coordinates.split("<waypoint>"); + + // parse coordinates + for (String point : points) { + final cgCoord pointCoord = new cgCoord(); + final Matcher matcherCidCode = patternCidCode.matcher(point); + final Matcher matcherLatCode = patternCidLat.matcher(point); + final Matcher matcherLonCode = patternCidLon.matcher(point); + final Matcher matcherDifCode = patternCidDif.matcher(point); + final Matcher matcherTerCode = patternCidTer.matcher(point); + final Matcher matcherConCode = patternCidCon.matcher(point); + HashMap<String, Object> tmp = null; + + if (matcherCidCode.find() == true) { + pointCoord.name = matcherCidCode.group(1).trim().toUpperCase(); + } + if (matcherLatCode.find() == true) { + tmp = parseCoordinate(matcherLatCode.group(1), "lat"); + pointCoord.latitude = (Double) tmp.get("coordinate"); + } + if (matcherLonCode.find() == true) { + tmp = parseCoordinate(matcherLonCode.group(1), "lon"); + pointCoord.longitude = (Double) tmp.get("coordinate"); + } + if (matcherDifCode.find() == true) { + pointCoord.difficulty = new Float(matcherDifCode.group(1)); + } + if (matcherTerCode.find() == true) { + pointCoord.terrain = new Float(matcherTerCode.group(1)); + } + if (matcherConCode.find() == true) { + final int size = Integer.parseInt(matcherConCode.group(1)); + + if (size == 1) { + pointCoord.size = "not chosen"; + } else if (size == 2) { + pointCoord.size = "micro"; + } else if (size == 3) { + pointCoord.size = "regular"; + } else if (size == 4) { + pointCoord.size = "large"; + } else if (size == 5) { + pointCoord.size = "virtual"; + } else if (size == 6) { + pointCoord.size = "other"; + } else if (size == 8) { + pointCoord.size = "small"; + } else { + pointCoord.size = "unknown"; + } + } + + cidCoords.put(pointCoord.name, pointCoord); + } + + Log.i(cgSettings.tag, "Coordinates found in .loc file: " + cidCoords.size()); + + // save found cache coordinates + for (cgCache oneCache : caches.cacheList) { + if (cidCoords.containsKey(oneCache.geocode) == true) { + cgCoord thisCoords = cidCoords.get(oneCache.geocode); + + oneCache.latitude = thisCoords.latitude; + oneCache.longitude = thisCoords.longitude; + oneCache.difficulty = thisCoords.difficulty; + oneCache.terrain = thisCoords.terrain; + oneCache.size = thisCoords.size; + } + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.parseSearch.CIDs: " + e.toString()); + } + } + + // get direction images + cgDirectionImg dirImgDownloader = new cgDirectionImg(settings); + for (cgCache oneCache : caches.cacheList) { + if (oneCache.latitude == null && oneCache.longitude == null && oneCache.direction == null && oneCache.directionImg != null) { + dirImgDownloader.getDrawable(oneCache.geocode, oneCache.directionImg); + } + } + dirImgDownloader = null; + + // get ratings + if (guids.size() > 0) { + Log.i(cgSettings.tag, "Trying to get ratings for " + cids.size() + " caches"); + + try { + final HashMap<String, cgRating> ratings = getRating(guids, null); + + if (ratings != null) { + // save found cache coordinates + for (cgCache oneCache : caches.cacheList) { + if (ratings.containsKey(oneCache.guid) == true) { + cgRating thisRating = ratings.get(oneCache.guid); + + oneCache.rating = thisRating.rating; + oneCache.votes = thisRating.votes; + oneCache.myVote = thisRating.myVote; + } + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.parseSearch.GCvote: " + e.toString()); + } + } + + return caches; + } + + public cgCacheWrap parseMapJSON(String url, String data) { + if (data == null || data.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.parseMapJSON: No page given"); + return null; + } + + final cgCacheWrap caches = new cgCacheWrap(); + caches.url = url; + + try { + final JSONObject yoDawg = new JSONObject(data); + final String json = yoDawg.getString("d"); + + if (json == null || json.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.parseMapJSON: No JSON inside JSON"); + return null; + } + + final JSONObject dataJSON = new JSONObject(json); + final JSONObject extra = dataJSON.getJSONObject("cs"); + if (extra != null && extra.length() > 0) { + int count = extra.getInt("count"); + + if (count > 0 && extra.has("cc")) { + final JSONArray cachesData = extra.getJSONArray("cc"); + if (cachesData != null && cachesData.length() > 0) { + JSONObject oneCache = null; + for (int i = 0; i < count; i++) { + oneCache = cachesData.getJSONObject(i); + if (oneCache == null) { + break; + } + + final cgCache cacheToAdd = new cgCache(); + cacheToAdd.reliableLatLon = false; + cacheToAdd.geocode = oneCache.getString("gc"); + cacheToAdd.latitude = oneCache.getDouble("lat"); + cacheToAdd.longitude = oneCache.getDouble("lon"); + cacheToAdd.name = oneCache.getString("nn"); + cacheToAdd.found = oneCache.getBoolean("f"); + cacheToAdd.own = oneCache.getBoolean("o"); + cacheToAdd.disabled = !oneCache.getBoolean("ia"); + int ctid = oneCache.getInt("ctid"); + if (ctid == 2) { + cacheToAdd.type = "traditional"; + } else if (ctid == 3) { + cacheToAdd.type = "multi"; + } else if (ctid == 4) { + cacheToAdd.type = "virtual"; + } else if (ctid == 5) { + cacheToAdd.type = "letterbox"; + } else if (ctid == 6) { + cacheToAdd.type = "event"; + } else if (ctid == 8) { + cacheToAdd.type = "mystery"; + } else if (ctid == 11) { + cacheToAdd.type = "webcam"; + } else if (ctid == 13) { + cacheToAdd.type = "cito"; + } else if (ctid == 137) { + cacheToAdd.type = "earth"; + } else if (ctid == 453) { + cacheToAdd.type = "mega"; + } else if (ctid == 1858) { + cacheToAdd.type = "wherigo"; + } else if (ctid == 3653) { + cacheToAdd.type = "lost"; + } + + caches.cacheList.add(cacheToAdd); + } + } + } else { + Log.w(cgSettings.tag, "There are no caches in viewport"); + } + caches.totalCnt = caches.cacheList.size(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.parseMapJSON: " + e.toString()); + } + + return caches; + } + + public cgCacheWrap parseCache(String page, int reason) { + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.parseCache: No page given"); + return null; + } + + final Pattern patternGeocode = Pattern.compile("<meta name=\"og:url\" content=\"[^\"]+/(GC[0-9A-Z]+)\"[^>]*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternCacheId = Pattern.compile("/seek/log\\.aspx\\?ID=(\\d+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternCacheGuid = Pattern.compile("<link rel=\"alternate\" href=\"[^\"]*/datastore/rss_galleryimages\\.ashx\\?guid=([0-9a-z\\-]+)\"[^>]*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternType = Pattern.compile("<img src=\"[^\"]*/WptTypes/\\d+\\.gif\" alt=\"([^\"]+)\" (title=\"[^\"]*\" )?width=\"32\" height=\"32\"[^>]*>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + + final Pattern patternName = Pattern.compile("<h2[^>]*>[^<]*<span id=\"ctl00_ContentBody_CacheName\">([^<]+)<\\/span>[^<]*<\\/h2>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternSize = Pattern.compile("<div class=\"CacheSize[^\"]*\">[^<]*<p[^>]*>[^S]*Size[^:]*:[^<]*<span[^>]*>[^<]*<img src=\"[^\"]*/icons/container/[a-z_]+\\.gif\" alt=\"Size: ([^\"]+)\"[^>]*>[^<]*<small>[^<]*</small>[^<]*</span>[^<]*</p>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternDifficulty = Pattern.compile("<span id=\"ctl00_ContentBody_uxLegendScale\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\" alt=\"[^\"]+\"[^>]*>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternTerrain = Pattern.compile("<span id=\"ctl00_ContentBody_Localize6\"[^>]*>[^<]*<img src=\"[^\"]*/images/stars/stars([0-9_]+)\\.gif\" alt=\"[^\"]+\"[^>]*>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternOwner = Pattern.compile("<span class=\"minorCacheDetails\">[^\\w]*An?([^\\w]*Event)?[^\\w]*cache[^\\w]*by[^<]*<a href=\"[^\"]+\">([^<]+)</a>[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternOwnerReal = Pattern.compile("<a id=\"ctl00_ContentBody_uxFindLinksHiddenByThisUser\" href=\"[^\"]*/seek/nearest\\.aspx\\?u=*([^\"]+)\">[^<]+</a>", Pattern.CASE_INSENSITIVE); + final Pattern patternHidden = Pattern.compile("<span[^>]*>[^\\w]*Hidden[^:]*:[^\\d]*((\\d+)\\/(\\d+)\\/(\\d+))[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternHiddenEvent = Pattern.compile("<span[^>]*>[^\\w]*Event[^\\w]*Date[^:]*:[^\\w]*[a-zA-Z]+,[^\\d]*((\\d+)[^\\w]*(\\w+)[^\\d]*(\\d+))[^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternFavourite = Pattern.compile("<a id=\"uxFavContainerLink\"[^>]*>[^<]*<div[^<]*<span class=\"favorite-value\">[^\\d]*([0-9]+)[^\\d^<]*</span>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + + final Pattern patternFound = Pattern.compile("<p>[^<]*<a id=\"ctl00_ContentBody_hlFoundItLog\"[^<]*<img src=\".*/images/stockholm/16x16/check\\.gif\"[^>]*>[^<]*</a>[^<]*</p>", Pattern.CASE_INSENSITIVE); + final Pattern patternLatLon = Pattern.compile("<span id=\"ctl00_ContentBody_LatLon\"[^>]*>(<b>)?([^<]*)(<\\/b>)?<\\/span>", Pattern.CASE_INSENSITIVE); + final Pattern patternLocation = Pattern.compile("<span id=\"ctl00_ContentBody_Location\"[^>]*>In ([^<]*)", Pattern.CASE_INSENSITIVE); + final Pattern patternHint = Pattern.compile("<p>([^<]*<strong>)?[^\\w]*Additional Hints([^<]*<\\/strong>)?[^\\(]*\\(<a[^>]+>Encrypt</a>\\)[^<]*<\\/p>[^<]*<div id=\"div_hint\"[^>]*>(.*)</div>[^<]*<div id=[\\'|\"]dk[\\'|\"]", Pattern.CASE_INSENSITIVE); + final Pattern patternDescShort = Pattern.compile("<div class=\"UserSuppliedContent\">[^<]*<span id=\"ctl00_ContentBody_ShortDescription\"[^>]*>((?:(?!</span>[^\\w^<]*</div>).)*)</span>[^\\w^<]*</div>", Pattern.CASE_INSENSITIVE); + final Pattern patternDesc = Pattern.compile("<div class=\"UserSuppliedContent\">[^<]*<span id=\"ctl00_ContentBody_LongDescription\"[^>]*>((?:(?!</span>[^\\w^<]*</div>).)*)</span>[^<]*</div>[^<]*<p>[^<]*</p>[^<]*<p>[^<]*<strong>[^\\w]*Additional Hints</strong>", Pattern.CASE_INSENSITIVE); + final Pattern patternCountLogs = Pattern.compile("<span id=\"ctl00_ContentBody_lblFindCounts\"><p>(.*)<\\/p><\\/span>", Pattern.CASE_INSENSITIVE); + final Pattern patternCountLog = Pattern.compile(" src=\"\\/images\\/icons\\/([^\\.]*).gif\" alt=\"[^\"]*\" title=\"[^\"]*\" />([0-9]*)[^0-9]+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternLogs = Pattern.compile("<table class=\"LogsTable[^\"]*\"[^>]*>[^<]*<tr>(.*)</tr>[^<]*</table>[^<]*<p", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternLog = Pattern.compile("<td[^>]*>[^<]*<strong>[^<]*<img src=\"[^\"]*/images/icons/([^\\.]+)\\.[a-z]{2,5}\"[^>]*> ([a-zA-Z]+) (\\d+)(, (\\d+))? by <a href=[^>]+>([^<]+)</a>[<^]*</strong>([^\\(]*\\((\\d+) found\\))?(<br[^>]*>)+((?:(?!<small>).)*)(<br[^>]*>)+<small>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Pattern patternAttributes = Pattern.compile("<h3 class=\"WidgetHeader\">[^<]*<img[^>]+>[^\\w]*Attributes[^<]*</h3>[^<]*<div class=\"WidgetBody\">(([^<]*<img src=\"[^\"]+\" alt=\"[^\"]+\"[^>]*>)+)[^<]*<p", Pattern.CASE_INSENSITIVE); + final Pattern patternAttributesInside = Pattern.compile("[^<]*<img src=\"([^\"]+)\" alt=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); + final Pattern patternSpoilers = Pattern.compile("<span id=\"ctl00_ContentBody_Images\">((<a href=\"[^\"]+\"[^>]*>[^<]*<img[^>]+>[^<]*<span>[^>]+</span>[^<]*</a>[^<]*<br[^>]*>([^<]*(<br[^>]*>)+)?)+)[^<]*</span>", Pattern.CASE_INSENSITIVE); + final Pattern patternSpoilersInside = Pattern.compile("[^<]*<a href=\"([^\"]+)\"[^>]*>[^<]*<img[^>]+>[^<]*<span>([^>]+)</span>[^<]*</a>[^<]*<br[^>]*>(([^<]*)(<br[^<]*>)+)?", Pattern.CASE_INSENSITIVE); + final Pattern patternInventory = Pattern.compile("<span id=\"ctl00_ContentBody_uxTravelBugList_uxInventoryLabel\">[^\\w]*Inventory[^<]*</span>[^<]*</h3>[^<]*<div class=\"WidgetBody\">([^<]*<ul>(([^<]*<li>[^<]*<a href=\"[^\"]+\"[^>]*>[^<]*<img src=\"[^\"]+\"[^>]*>[^<]*<span>[^<]+<\\/span>[^<]*<\\/a>[^<]*<\\/li>)+)[^<]*<\\/ul>)?", Pattern.CASE_INSENSITIVE); + final Pattern patternInventoryInside = Pattern.compile("[^<]*<li>[^<]*<a href=\"[a-z0-9\\-\\_\\.\\?\\/\\:\\@]*\\/track\\/details\\.aspx\\?guid=([0-9a-z\\-]+)[^\"]*\"[^>]*>[^<]*<img src=\"[^\"]+\"[^>]*>[^<]*<span>([^<]+)<\\/span>[^<]*<\\/a>[^<]*<\\/li>", Pattern.CASE_INSENSITIVE); + + final cgCacheWrap caches = new cgCacheWrap(); + final cgCache cache = new cgCache(); + + if (page.indexOf("Cache is Unpublished") > -1) { + caches.error = "cache was unpublished"; + return caches; + } + + if (page.indexOf("Sorry, the owner of this listing has made it viewable to Premium Members only.") != -1) { + caches.error = "requested cache is for premium members only"; + return caches; + } + + if (page.indexOf("has chosen to make this cache listing visible to Premium Members only.") != -1) { + caches.error = "requested cache is for premium members only"; + return caches; + } + + if (page.indexOf("<li>This cache is temporarily unavailable.") != -1) { + cache.disabled = true; + } else { + cache.disabled = false; + } + + if (page.indexOf("<li>This cache has been archived,") != -1) { + cache.archived = true; + } else { + cache.archived = false; + } + + if (page.indexOf("<p class=\"Warning\">This is a Premium Member Only cache.</p>") != -1) { + cache.members = true; + } else { + cache.members = false; + } + + cache.reason = reason; + + // cache geocode + try { + final Matcher matcherGeocode = patternGeocode.matcher(page); + while (matcherGeocode.find()) { + if (matcherGeocode.groupCount() > 0) { + cache.geocode = (String) matcherGeocode.group(1); + } + } + } catch (Exception e) { + // failed to parse cache geocode + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache geocode"); + } + + // cache id + try { + final Matcher matcherCacheId = patternCacheId.matcher(page); + while (matcherCacheId.find()) { + if (matcherCacheId.groupCount() > 0) { + cache.cacheid = (String) matcherCacheId.group(1); + } + } + } catch (Exception e) { + // failed to parse cache id + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache id"); + } + + // cache guid + try { + final Matcher matcherCacheGuid = patternCacheGuid.matcher(page); + while (matcherCacheGuid.find()) { + if (matcherCacheGuid.groupCount() > 0) { + cache.guid = (String) matcherCacheGuid.group(1); + } + } + } catch (Exception e) { + // failed to parse cache guid + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache guid"); + } + + // name + try { + final Matcher matcherName = patternName.matcher(page); + while (matcherName.find()) { + if (matcherName.groupCount() > 0) { + cache.name = Html.fromHtml(matcherName.group(1)).toString(); + } + } + } catch (Exception e) { + // failed to parse cache name + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache name"); + } + + // owner real name + try { + final Matcher matcherOwnerReal = patternOwnerReal.matcher(page); + while (matcherOwnerReal.find()) { + if (matcherOwnerReal.groupCount() > 0) { + cache.ownerReal = URLDecoder.decode(matcherOwnerReal.group(1)); + } + } + } catch (Exception e) { + // failed to parse owner real name + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache owner real name"); + } + + final String username = settings.getUsername(); + if (cache.ownerReal != null && username != null && cache.ownerReal.equalsIgnoreCase(username)) { + cache.own = true; + } + + int pos = -1; + String tableInside = page; + + pos = tableInside.indexOf("id=\"cacheDetails\""); + if (pos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseCache: ID \"cacheDetails\" not found on page"); + return null; + } + + tableInside = tableInside.substring(pos); + + pos = tableInside.indexOf("<div class=\"CacheInformationTable\""); + if (pos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseCache: ID \"CacheInformationTable\" not found on page"); + return null; + } + + tableInside = tableInside.substring(0, pos); + + if (tableInside != null && tableInside.length() > 0) { + // cache terrain + try { + final Matcher matcherTerrain = patternTerrain.matcher(tableInside); + while (matcherTerrain.find()) { + if (matcherTerrain.groupCount() > 0) { + cache.terrain = new Float(Pattern.compile("_").matcher(matcherTerrain.group(1)).replaceAll(".")); + } + } + } catch (Exception e) { + // failed to parse terrain + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache terrain"); + } + + // cache difficulty + try { + final Matcher matcherDifficulty = patternDifficulty.matcher(tableInside); + while (matcherDifficulty.find()) { + if (matcherDifficulty.groupCount() > 0) { + cache.difficulty = new Float(Pattern.compile("_").matcher(matcherDifficulty.group(1)).replaceAll(".")); + } + } + } catch (Exception e) { + // failed to parse difficulty + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache difficulty"); + } + + // owner + try { + final Matcher matcherOwner = patternOwner.matcher(tableInside); + while (matcherOwner.find()) { + if (matcherOwner.groupCount() > 0) { + cache.owner = Html.fromHtml(matcherOwner.group(2)).toString(); + } + } + } catch (Exception e) { + // failed to parse owner + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache owner"); + } + + // hidden + try { + final Matcher matcherHidden = patternHidden.matcher(tableInside); + while (matcherHidden.find()) { + if (matcherHidden.groupCount() > 0) { + cache.hidden = dateIn.parse(matcherHidden.group(1)); + } + } + } catch (Exception e) { + // failed to parse cache hidden date + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache hidden date"); + } + + if (cache.hidden == null) { + // event date + try { + final Matcher matcherHiddenEvent = patternHiddenEvent.matcher(tableInside); + while (matcherHiddenEvent.find()) { + if (matcherHiddenEvent.groupCount() > 0) { + cache.hidden = dateEvIn.parse(matcherHiddenEvent.group(1)); + } + } + } catch (Exception e) { + // failed to parse cache event date + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache event date"); + } + } + + // favourite + try { + final Matcher matcherFavourite = patternFavourite.matcher(tableInside); + while (matcherFavourite.find()) { + if (matcherFavourite.groupCount() > 0) { + cache.favouriteCnt = Integer.parseInt(matcherFavourite.group(1)); + } + } + } catch (Exception e) { + // failed to parse favourite count + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse favourite count"); + } + + // cache size + try { + final Matcher matcherSize = patternSize.matcher(tableInside); + while (matcherSize.find()) { + if (matcherSize.groupCount() > 0) { + cache.size = matcherSize.group(1).toLowerCase(); + } + } + } catch (Exception e) { + // failed to parse size + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache size"); + } + } + + // cache found + try { + final Matcher matcherFound = patternFound.matcher(page); + while (matcherFound.find()) { + if (matcherFound.group() != null && matcherFound.group().length() > 0) { + cache.found = true; + } + } + } catch (Exception e) { + // failed to parse found + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse found"); + } + + // cache type + try { + final Matcher matcherType = patternType.matcher(page); + while (matcherType.find()) { + if (matcherType.groupCount() > 0) { + cache.type = cacheTypes.get(matcherType.group(1).toLowerCase()); + } + } + } catch (Exception e) { + // failed to parse type + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache type"); + } + + // latitude and logitude + try { + final Matcher matcherLatLon = patternLatLon.matcher(page); + while (matcherLatLon.find()) { + if (matcherLatLon.groupCount() > 0) { + cache.latlon = matcherLatLon.group(2); // first is <b> + + HashMap<String, Object> tmp = this.parseLatlon(cache.latlon); + if (tmp.size() > 0) { + cache.latitude = (Double) tmp.get("latitude"); + cache.longitude = (Double) tmp.get("longitude"); + cache.latitudeString = (String) tmp.get("latitudeString"); + cache.longitudeString = (String) tmp.get("longitudeString"); + cache.reliableLatLon = true; + } + tmp = null; + } + } + } catch (Exception e) { + // failed to parse latitude and/or longitude + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache coordinates"); + } + + // cache location + try { + final Matcher matcherLocation = patternLocation.matcher(page); + while (matcherLocation.find()) { + if (matcherLocation.groupCount() > 0) { + cache.location = matcherLocation.group(1); + } + } + } catch (Exception e) { + // failed to parse location + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache location"); + } + + // cache hint + try { + final Matcher matcherHint = patternHint.matcher(page); + while (matcherHint.find()) { + if (matcherHint.groupCount() > 2 && matcherHint.group(3) != null) { + // replace linebreak and paragraph tags + String hint = Pattern.compile("<(br|p)[^>]*>").matcher(matcherHint.group(3)).replaceAll("\n"); + if (hint != null) { + cache.hint = hint.replaceAll(Pattern.quote("</p>"), "").trim(); + } + } + } + } catch (Exception e) { + // failed to parse hint + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache hint"); + } + + /* + // short info debug + Log.d(cgSettings.tag, "gc-code: " + cache.geocode); + Log.d(cgSettings.tag, "id: " + cache.cacheid); + Log.d(cgSettings.tag, "guid: " + cache.guid); + Log.d(cgSettings.tag, "name: " + cache.name); + Log.d(cgSettings.tag, "terrain: " + cache.terrain); + Log.d(cgSettings.tag, "difficulty: " + cache.difficulty); + Log.d(cgSettings.tag, "owner: " + cache.owner); + Log.d(cgSettings.tag, "owner (real): " + cache.ownerReal); + Log.d(cgSettings.tag, "hidden: " + dateOutShort.format(cache.hidden)); + Log.d(cgSettings.tag, "favorite: " + cache.favouriteCnt); + Log.d(cgSettings.tag, "size: " + cache.size); + if (cache.found) { + Log.d(cgSettings.tag, "found!"); + } else { + Log.d(cgSettings.tag, "not found"); + } + Log.d(cgSettings.tag, "type: " + cache.type); + Log.d(cgSettings.tag, "latitude: " + String.format("%.6f", cache.latitude)); + Log.d(cgSettings.tag, "longitude: " + String.format("%.6f", cache.longitude)); + Log.d(cgSettings.tag, "location: " + cache.location); + Log.d(cgSettings.tag, "hint: " + cache.hint); + */ + + // cache short description + try { + final Matcher matcherDescShort = patternDescShort.matcher(page); + while (matcherDescShort.find()) { + if (matcherDescShort.groupCount() > 0) { + cache.shortdesc = matcherDescShort.group(1).trim(); + } + } + } catch (Exception e) { + // failed to parse short description + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache short description"); + } + + // cache description + try { + final Matcher matcherDesc = patternDesc.matcher(page); + while (matcherDesc.find()) { + if (matcherDesc.groupCount() > 0) { + cache.description = matcherDesc.group(1); + } + } + } catch (Exception e) { + // failed to parse short description + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache description"); + } + + // cache attributes + try { + final Matcher matcherAttributes = patternAttributes.matcher(page); + while (matcherAttributes.find()) { + if (matcherAttributes.groupCount() > 0) { + final String attributesPre = matcherAttributes.group(1); + final Matcher matcherAttributesInside = patternAttributesInside.matcher(attributesPre); + + while (matcherAttributesInside.find()) { + if (matcherAttributesInside.groupCount() > 1 && matcherAttributesInside.group(2).equalsIgnoreCase("blank") != true) { + if (cache.attributes == null) { + cache.attributes = new ArrayList<String>(); + } + // by default, use the tooltip of the attribute + String attribute = matcherAttributesInside.group(2).toLowerCase(); + + // if the image name can be recognized, use the image name as attribute + String imageName = matcherAttributesInside.group(1).trim(); + if (imageName.length() > 0) { + int start = imageName.lastIndexOf('/'); + int end = imageName.lastIndexOf('.'); + if (start >= 0 && end>= 0) { + attribute = imageName.substring(start + 1, end).replace('-', '_'); + } + } + cache.attributes.add(attribute); + } + } + } + } + } catch (Exception e) { + // failed to parse cache attributes + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache attributes"); + } + + // cache spoilers + try { + final Matcher matcherSpoilers = patternSpoilers.matcher(page); + while (matcherSpoilers.find()) { + if (matcherSpoilers.groupCount() > 0) { + final String spoilersPre = matcherSpoilers.group(1); + final Matcher matcherSpoilersInside = patternSpoilersInside.matcher(spoilersPre); + + while (matcherSpoilersInside.find()) { + if (matcherSpoilersInside.groupCount() > 0) { + final cgSpoiler spoiler = new cgSpoiler(); + spoiler.url = matcherSpoilersInside.group(1); + + if (matcherSpoilersInside.group(2) != null) { + spoiler.title = matcherSpoilersInside.group(2); + } + if (matcherSpoilersInside.group(4) != null) { + spoiler.description = matcherSpoilersInside.group(4); + } + + if (cache.spoilers == null) { + cache.spoilers = new ArrayList<cgSpoiler>(); + } + cache.spoilers.add(spoiler); + } + } + } + } + } catch (Exception e) { + // failed to parse cache spoilers + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache spoilers"); + } + + // cache inventory + try { + cache.inventoryItems = 0; + + final Matcher matcherInventory = patternInventory.matcher(page); + while (matcherInventory.find()) { + if (cache.inventory == null) { + cache.inventory = new ArrayList<cgTrackable>(); + } + + if (matcherInventory.groupCount() > 1) { + final String inventoryPre = matcherInventory.group(2); + + if (inventoryPre != null && inventoryPre.length() > 0) { + final Matcher matcherInventoryInside = patternInventoryInside.matcher(inventoryPre); + + while (matcherInventoryInside.find()) { + if (matcherInventoryInside.groupCount() > 0) { + final cgTrackable inventoryItem = new cgTrackable(); + inventoryItem.guid = matcherInventoryInside.group(1); + inventoryItem.name = matcherInventoryInside.group(2); + + cache.inventory.add(inventoryItem); + cache.inventoryItems++; + } + } + } + } + } + } catch (Exception e) { + // failed to parse cache inventory + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache inventory (2)"); + } + + // cache logs counts + try { + final Matcher matcherLogCounts = patternCountLogs.matcher(page); + while (matcherLogCounts.find()) { + if (matcherLogCounts.groupCount() > 0) { + final String[] logs = matcherLogCounts.group(1).split("<img"); + final int logsCnt = logs.length; + + for (int k = 1; k < logsCnt; k++) { + Integer type = null; + Integer count = null; + final Matcher matcherLog = patternCountLog.matcher(logs[k]); + + if (matcherLog.find()) { + String typeStr = matcherLog.group(1); + String countStr = matcherLog.group(2); + if (typeStr != null && typeStr.length() > 0) { + if (logTypes.containsKey(typeStr.toLowerCase()) == true) { + type = logTypes.get(typeStr.toLowerCase()); + } + } + if (countStr != null && countStr.length() > 0) { + count = Integer.parseInt(countStr); + } + if (type != null && count != null) { + cache.logCounts.put(type, count); + } + } + } + } + } + } catch (Exception e) { + // failed to parse logs + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache log count"); + } + + // cache logs + try { + final Matcher matcherLogs = patternLogs.matcher(page); + while (matcherLogs.find()) { + if (matcherLogs.groupCount() > 0) { + final String[] logs = matcherLogs.group(1).split("</tr><tr>"); + final int logsCnt = logs.length; + + for (int k = 0; k < logsCnt; k++) { + final Matcher matcherLog = patternLog.matcher(logs[k]); + + if (matcherLog.find()) { + final cgLog logDone = new cgLog(); + + String logTmp = matcherLog.group(10); + + int day = -1; + try { + day = Integer.parseInt(matcherLog.group(3)); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse logs date (day): " + e.toString()); + } + + int month = -1; + // January | February | March | April | May | June | July | August | September | October | November | December + if (matcherLog.group(2).equalsIgnoreCase("January")) { + month = 0; + } else if (matcherLog.group(2).equalsIgnoreCase("February")) { + month = 1; + } else if (matcherLog.group(2).equalsIgnoreCase("March")) { + month = 2; + } else if (matcherLog.group(2).equalsIgnoreCase("April")) { + month = 3; + } else if (matcherLog.group(2).equalsIgnoreCase("May")) { + month = 4; + } else if (matcherLog.group(2).equalsIgnoreCase("June")) { + month = 5; + } else if (matcherLog.group(2).equalsIgnoreCase("July")) { + month = 6; + } else if (matcherLog.group(2).equalsIgnoreCase("August")) { + month = 7; + } else if (matcherLog.group(2).equalsIgnoreCase("September")) { + month = 8; + } else if (matcherLog.group(2).equalsIgnoreCase("October")) { + month = 9; + } else if (matcherLog.group(2).equalsIgnoreCase("November")) { + month = 10; + } else if (matcherLog.group(2).equalsIgnoreCase("December")) { + month = 11; + } else { + Log.w(cgSettings.tag, "Failed to parse logs date (month)."); + } + + + int year = -1; + final String yearPre = matcherLog.group(5); + + if (yearPre == null) { + Calendar date = Calendar.getInstance(); + year = date.get(Calendar.YEAR); + } else { + try { + year = Integer.parseInt(matcherLog.group(5)); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse logs date (year): " + e.toString()); + } + } + + long logDate; + if (year > 0 && month >= 0 && day > 0) { + Calendar date = Calendar.getInstance(); + date.set(year, month, day, 12, 0, 0); + logDate = date.getTimeInMillis(); + logDate = (long) (Math.ceil(logDate / 1000)) * 1000; + } else { + logDate = 0; + } + + if (logTypes.containsKey(matcherLog.group(1).toLowerCase()) == true) { + logDone.type = logTypes.get(matcherLog.group(1).toLowerCase()); + } else { + logDone.type = logTypes.get("icon_note"); + } + + logDone.author = Html.fromHtml(matcherLog.group(6)).toString(); + logDone.date = logDate; + if (matcherLog.group(8) != null) { + logDone.found = new Integer(matcherLog.group(8)); + } + logDone.log = logTmp; + + if (cache.logs == null) { + cache.logs = new ArrayList<cgLog>(); + } + cache.logs.add(logDone); + } + } + } + } + } catch (Exception e) { + // failed to parse logs + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache logs"); + } + + int wpBegin = 0; + int wpEnd = 0; + + wpBegin = page.indexOf("<table class=\"Table\" id=\"ctl00_ContentBody_Waypoints\">"); + if (wpBegin != -1) { // parse waypoints + final Pattern patternWpType = Pattern.compile("\\/wpttypes\\/sm\\/(.+)\\.jpg", Pattern.CASE_INSENSITIVE); + final Pattern patternWpPrefixOrLookupOrLatlon = Pattern.compile(">([^<]*<[^>]+>)?([^<]+)(<[^>]+>[^<]*)?<\\/td>", Pattern.CASE_INSENSITIVE); + final Pattern patternWpName = Pattern.compile(">[^<]*<a[^>]+>([^<]*)<\\/a>", Pattern.CASE_INSENSITIVE); + final Pattern patternWpNote = Pattern.compile("colspan=\"6\">(.*)<\\/td>", Pattern.CASE_INSENSITIVE); + + String wpList = page.substring(wpBegin); + + wpEnd = wpList.indexOf("</p>"); + if (wpEnd > -1 && wpEnd <= wpList.length()) { + wpList = wpList.substring(0, wpEnd); + } + + if (wpList.indexOf("No additional waypoints to display.") == -1) { + wpEnd = wpList.indexOf("</table>"); + wpList = wpList.substring(0, wpEnd); + + wpBegin = wpList.indexOf("<tbody>"); + wpEnd = wpList.indexOf("</tbody>"); + if (wpBegin >= 0 && wpEnd >= 0 && wpEnd <= wpList.length()) { + wpList = wpList.substring(wpBegin + 7, wpEnd); + } + + final String[] wpItems = wpList.split("<tr"); + + String[] wp; + for (int j = 1; j < wpItems.length; j++) { + final cgWaypoint waypoint = new cgWaypoint(); + + wp = wpItems[j].split("<td"); + + // waypoint type + try { + final Matcher matcherWpType = patternWpType.matcher(wp[3]); + while (matcherWpType.find()) { + if (matcherWpType.groupCount() > 0) { + waypoint.type = matcherWpType.group(1); + if (waypoint.type != null) { + waypoint.type = waypoint.type.trim(); + } + } + } + } catch (Exception e) { + // failed to parse type + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint type"); + } + + // waypoint prefix + try { + final Matcher matcherWpPrefix = patternWpPrefixOrLookupOrLatlon.matcher(wp[4]); + while (matcherWpPrefix.find()) { + if (matcherWpPrefix.groupCount() > 1) { + waypoint.prefix = matcherWpPrefix.group(2); + if (waypoint.prefix != null) { + waypoint.prefix = waypoint.prefix.trim(); + } + } + } + } catch (Exception e) { + // failed to parse prefix + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint prefix"); + } + + // waypoint lookup + try { + final Matcher matcherWpLookup = patternWpPrefixOrLookupOrLatlon.matcher(wp[5]); + while (matcherWpLookup.find()) { + if (matcherWpLookup.groupCount() > 1) { + waypoint.lookup = matcherWpLookup.group(2); + if (waypoint.lookup != null) { + waypoint.lookup = waypoint.lookup.trim(); + } + } + } + } catch (Exception e) { + // failed to parse lookup + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint lookup"); + } + + // waypoint name + try { + final Matcher matcherWpName = patternWpName.matcher(wp[6]); + while (matcherWpName.find()) { + if (matcherWpName.groupCount() > 0) { + waypoint.name = matcherWpName.group(1); + if (waypoint.name != null) { + waypoint.name = waypoint.name.trim(); + } + } + } + } catch (Exception e) { + // failed to parse name + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint name"); + } + + // waypoint latitude and logitude + try { + final Matcher matcherWpLatLon = patternWpPrefixOrLookupOrLatlon.matcher(wp[7]); + while (matcherWpLatLon.find()) { + if (matcherWpLatLon.groupCount() > 1) { + waypoint.latlon = Html.fromHtml(matcherWpLatLon.group(2)).toString(); + + final HashMap<String, Object> tmp = this.parseLatlon(waypoint.latlon); + if (tmp.size() > 0) { + waypoint.latitude = (Double) tmp.get("latitude"); + waypoint.longitude = (Double) tmp.get("longitude"); + waypoint.latitudeString = (String) tmp.get("latitudeString"); + waypoint.longitudeString = (String) tmp.get("longitudeString"); + } + } + } + } catch (Exception e) { + // failed to parse latitude and/or longitude + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint coordinates"); + } + + j++; + if (wpItems.length > j) { + wp = wpItems[j].split("<td"); + } + + // waypoint note + try { + final Matcher matcherWpNote = patternWpNote.matcher(wp[3]); + while (matcherWpNote.find()) { + if (matcherWpNote.groupCount() > 0) { + waypoint.note = matcherWpNote.group(1); + if (waypoint.note != null) { + waypoint.note = waypoint.note.trim(); + } + } + } + } catch (Exception e) { + // failed to parse note + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse waypoint note"); + } + + if (cache.waypoints == null) + cache.waypoints = new ArrayList<cgWaypoint>(); + cache.waypoints.add(waypoint); + } + } + } + + if (cache.latitude != null && cache.longitude != null) { + cache.elevation = getElevation(cache.latitude, cache.longitude); + } + + final cgRating rating = getRating(cache.guid, cache.geocode); + if (rating != null) { + cache.rating = rating.rating; + cache.votes = rating.votes; + cache.myVote = rating.myVote; + } + + cache.updated = System.currentTimeMillis(); + cache.detailedUpdate = System.currentTimeMillis(); + cache.detailed = true; + caches.cacheList.add(cache); + + return caches; + } + + public cgRating getRating(String guid, String geocode) { + ArrayList<String> guids = null; + ArrayList<String> geocodes = null; + + if (guid != null && guid.length() > 0) { + guids = new ArrayList<String>(); + guids.add(guid); + } else if (geocode != null && geocode.length() > 0) { + geocodes = new ArrayList<String>(); + geocodes.add(geocode); + } else { + return null; + } + + final HashMap<String, cgRating> ratings = getRating(guids, geocodes); + if(ratings != null){ + final Set<String> ratingKeys = ratings.keySet(); + for (String ratingKey : ratingKeys) { + return ratings.get(ratingKey); + } + } + + return null; + } + + public HashMap<String, cgRating> getRating(ArrayList<String> guids, ArrayList<String> geocodes) { + if (guids == null && geocodes == null) { + return null; + } + + final HashMap<String, cgRating> ratings = new HashMap<String, cgRating>(); + + try { + final HashMap<String, String> params = new HashMap<String, String>(); + if (settings.isLogin() == true) { + final HashMap<String, String> login = settings.getGCvoteLogin(); + if (login != null) { + params.put("userName", login.get("username")); + params.put("password", login.get("password")); + } + } + if (guids != null && guids.size() > 0) { + params.put("cacheIds", implode(",", guids.toArray())); + } else { + params.put("waypoints", implode(",", geocodes.toArray())); + } + params.put("version", "cgeo"); + final String votes = request(false, "gcvote.com", "/getVotes.php", "GET", params, false, false, false).getData(); + if (votes == null) { + return null; + } + + final Pattern patternLogIn = Pattern.compile("loggedIn='([^']+)'", Pattern.CASE_INSENSITIVE); + final Pattern patternGuid = Pattern.compile("cacheId='([^']+)'", Pattern.CASE_INSENSITIVE); + final Pattern patternRating = Pattern.compile("voteAvg='([0-9\\.]+)'", Pattern.CASE_INSENSITIVE); + final Pattern patternVotes = Pattern.compile("voteCnt='([0-9]+)'", Pattern.CASE_INSENSITIVE); + final Pattern patternVote = Pattern.compile("voteUser='([0-9\\.]+)'", Pattern.CASE_INSENSITIVE); + + String voteData = null; + final Pattern patternVoteElement = Pattern.compile("<vote ([^>]+)>", Pattern.CASE_INSENSITIVE); + final Matcher matcherVoteElement = patternVoteElement.matcher(votes); + while (matcherVoteElement.find()) { + if (matcherVoteElement.groupCount() > 0) { + voteData = matcherVoteElement.group(1); + } + + if (voteData == null) { + continue; + } + + String guid = null; + cgRating rating = new cgRating(); + boolean loggedIn = false; + + try { + final Matcher matcherGuid = patternGuid.matcher(voteData); + if (matcherGuid.find()) { + if (matcherGuid.groupCount() > 0) { + guid = (String) matcherGuid.group(1); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse guid"); + } + + try { + final Matcher matcherLoggedIn = patternLogIn.matcher(votes); + if (matcherLoggedIn.find()) { + if (matcherLoggedIn.groupCount() > 0) { + if (matcherLoggedIn.group(1).equalsIgnoreCase("true") == true) { + loggedIn = true; + } + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse loggedIn"); + } + + try { + final Matcher matcherRating = patternRating.matcher(voteData); + if (matcherRating.find()) { + if (matcherRating.groupCount() > 0) { + rating.rating = Float.parseFloat(matcherRating.group(1)); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse rating"); + } + + try { + final Matcher matcherVotes = patternVotes.matcher(voteData); + if (matcherVotes.find()) { + if (matcherVotes.groupCount() > 0) { + rating.votes = Integer.parseInt(matcherVotes.group(1)); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse vote count"); + } + + if (loggedIn == true) { + try { + final Matcher matcherVote = patternVote.matcher(voteData); + if (matcherVote.find()) { + if (matcherVote.groupCount() > 0) { + rating.myVote = Float.parseFloat(matcherVote.group(1)); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getRating: Failed to parse user's vote"); + } + } + + if (guid != null) { + ratings.put(guid, rating); + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.getRating: " + e.toString()); + } + + return ratings; + } + + public boolean setRating(String guid, int vote) { + if (guid == null || guid.length() == 0) { + return false; + } + if (vote < 0 || vote > 5) { + return false; + } + + final HashMap<String, String> login = settings.getGCvoteLogin(); + if (login == null) { + return false; + } + + final HashMap<String, String> params = new HashMap<String, String>(); + params.put("userName", login.get("username")); + params.put("password", login.get("password")); + params.put("cacheId", guid); + params.put("voteUser", Integer.toString(vote)); + params.put("version", "cgeo"); + + final String result = request(false, "gcvote.com", "/setVote.php", "GET", params, false, false, false).getData(); + + if (result.trim().equalsIgnoreCase("ok") == true) { + return true; + } + + return false; + } + + public Long parseGPX(cgeoapplication app, File file, int listId, Handler handler) { + cgSearch search = new cgSearch(); + long searchId = 0l; + + try { + cgGPXParser GPXparser = new cgGPXParser(app, this, listId, search); + + searchId = GPXparser.parse(file, 10, handler); + if (searchId == 0l) { + searchId = GPXparser.parse(file, 11, handler); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.parseGPX: " + e.toString()); + } + + Log.i(cgSettings.tag, "Caches found in .gpx file: " + app.getCount(searchId)); + + return search.getCurrentId(); + } + + public cgTrackable parseTrackable(String page) { + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.parseTrackable: No page given"); + return null; + } + + final Pattern patternTrackableId = Pattern.compile("<a id=\"ctl00_ContentBody_LogLink\" title=\"[^\"]*\" href=\".*log\\.aspx\\?wid=([a-z0-9\\-]+)\"[^>]*>[^<]*</a>", Pattern.CASE_INSENSITIVE); + final Pattern patternGeocode = Pattern.compile("<span id=\"ctl00_ContentBody_BugDetails_BugTBNum\" String=\"[^\"]*\">Use[^<]*<strong>(TB[0-9a-z]+)[^<]*</strong> to reference this item.[^<]*</span>", Pattern.CASE_INSENSITIVE); + final Pattern patternName = Pattern.compile("<h2>([^<]*<img[^>]*>)?[^<]*<span id=\"ctl00_ContentBody_lbHeading\">([^<]+)</span>[^<]*</h2>", Pattern.CASE_INSENSITIVE); + final Pattern patternOwner = Pattern.compile("<dt>[^\\w]*Owner:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugOwner\" title=\"[^\"]*\" href=\"[^\"]*/profile/\\?guid=([a-z0-9\\-]+)\">([^<]+)<\\/a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + final Pattern patternReleased = Pattern.compile("<dt>[^\\w]*Released:[^<]*</dt>[^<]*<dd>[^<]*<span id=\"ctl00_ContentBody_BugDetails_BugReleaseDate\">([^<]+)<\\/span>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + final Pattern patternOrigin = Pattern.compile("<dt>[^\\w]*Origin:[^<]*</dt>[^<]*<dd>[^<]*<span id=\"ctl00_ContentBody_BugDetails_BugOrigin\">([^<]+)<\\/span>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + final Pattern patternSpottedCache = Pattern.compile("<dt>[^\\w]*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\" title=\"[^\"]*\" href=\"[^\"]*/seek/cache_details.aspx\\?guid=([a-z0-9\\-]+)\">In ([^<]+)</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + final Pattern patternSpottedUser = Pattern.compile("<dt>[^\\w]*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\" href=\"[^\"]*/profile/\\?guid=([a-z0-9\\-]+)\">In the hands of ([^<]+).</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + final Pattern patternSpottedUnknown = Pattern.compile("<dt>[^\\w]*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\">Unknown Location[^<]*</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + final Pattern patternSpottedOwner = Pattern.compile("<dt>[^\\w]*Recently Spotted:[^<]*</dt>[^<]*<dd>[^<]*<a id=\"ctl00_ContentBody_BugDetails_BugLocation\">In the hands of the owner[^<]*</a>[^<]*</dd>", Pattern.CASE_INSENSITIVE); + final Pattern patternGoal = Pattern.compile("<h3>[^\\w]*Current GOAL[^<]*</h3>[^<]*<p[^>]*>(.*)</p>[^<]*<h3>[^\\w]*About This Item[^<]*</h3>", Pattern.CASE_INSENSITIVE); + final Pattern patternDetailsImage = Pattern.compile("<h3>[^\\w]*About This Item[^<]*</h3>([^<]*<p>([^<]*<img id=\"ctl00_ContentBody_BugDetails_BugImage\" class=\"[^\"]+\" src=\"([^\"]+)\"[^>]*>)?[^<]*</p>)?[^<]*<p[^>]*>(.*)</p>[^<]*<div id=\"ctl00_ContentBody_BugDetails_uxAbuseReport\">", Pattern.CASE_INSENSITIVE); + final Pattern patternLogs = Pattern.compile("<table class=\"TrackableItemLogTable Table\">(.*)<\\/table>[^<]*<ul", Pattern.CASE_INSENSITIVE); + final Pattern patternIcon = Pattern.compile("<img id=\"ctl00_ContentBody_BugTypeImage\" class=\"TravelBugHeaderIcon\" src=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); + final Pattern patternType = Pattern.compile("<img id=\"ctl00_ContentBody_BugTypeImage\" class=\"TravelBugHeaderIcon\" src=\"[^\"]+\" alt=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); + final Pattern patternDistance = Pattern.compile("<h4[^>]*[^\\w]*Tracking History \\(([0-9\\.,]+(km|mi))[^\\)]*\\)", Pattern.CASE_INSENSITIVE); + + final cgTrackable trackable = new cgTrackable(); + + // trackable geocode + try { + final Matcher matcherGeocode = patternGeocode.matcher(page); + while (matcherGeocode.find()) { + if (matcherGeocode.groupCount() > 0) { + trackable.geocode = matcherGeocode.group(1).toUpperCase(); + } + } + } catch (Exception e) { + // failed to parse trackable geocode + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable geocode"); + } + + // trackable id + try { + final Matcher matcherTrackableId = patternTrackableId.matcher(page); + while (matcherTrackableId.find()) { + if (matcherTrackableId.groupCount() > 0) { + trackable.guid = matcherTrackableId.group(1); + } + } + } catch (Exception e) { + // failed to parse trackable id + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable id"); + } + + // trackable icon + try { + final Matcher matcherTrackableIcon = patternIcon.matcher(page); + while (matcherTrackableIcon.find()) { + if (matcherTrackableIcon.groupCount() > 0) { + trackable.iconUrl = matcherTrackableIcon.group(1); + } + } + } catch (Exception e) { + // failed to parse trackable icon + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable icon"); + } + + // trackable name + try { + final Matcher matcherName = patternName.matcher(page); + while (matcherName.find()) { + if (matcherName.groupCount() > 1) { + trackable.name = matcherName.group(2); + } + } + } catch (Exception e) { + // failed to parse trackable name + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable name"); + } + + // trackable type + if (trackable.name != null && trackable.name.length() > 0) { + try { + final Matcher matcherType = patternType.matcher(page); + while (matcherType.find()) { + if (matcherType.groupCount() > 0) { + trackable.type = matcherType.group(1); + } + } + } catch (Exception e) { + // failed to parse trackable type + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable type"); + } + } + + // trackable owner name + try { + final Matcher matcherOwner = patternOwner.matcher(page); + while (matcherOwner.find()) { + if (matcherOwner.groupCount() > 0) { + trackable.ownerGuid = matcherOwner.group(1); + trackable.owner = matcherOwner.group(2); + } + } + } catch (Exception e) { + // failed to parse trackable owner name + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable owner name"); + } + + // trackable origin + try { + final Matcher matcherOrigin = patternOrigin.matcher(page); + while (matcherOrigin.find()) { + if (matcherOrigin.groupCount() > 0) { + trackable.origin = matcherOrigin.group(1); + } + } + } catch (Exception e) { + // failed to parse trackable origin + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable origin"); + } + + // trackable spotted + try { + final Matcher matcherSpottedCache = patternSpottedCache.matcher(page); + while (matcherSpottedCache.find()) { + if (matcherSpottedCache.groupCount() > 0) { + trackable.spottedGuid = matcherSpottedCache.group(1); + trackable.spottedName = matcherSpottedCache.group(2); + trackable.spottedType = cgTrackable.SPOTTED_CACHE; + } + } + + final Matcher matcherSpottedUser = patternSpottedUser.matcher(page); + while (matcherSpottedUser.find()) { + if (matcherSpottedUser.groupCount() > 0) { + trackable.spottedGuid = matcherSpottedUser.group(1); + trackable.spottedName = matcherSpottedUser.group(2); + trackable.spottedType = cgTrackable.SPOTTED_USER; + } + } + + final Matcher matcherSpottedUnknown = patternSpottedUnknown.matcher(page); + if (matcherSpottedUnknown.find()) { + trackable.spottedType = cgTrackable.SPOTTED_UNKNOWN; + } + + final Matcher matcherSpottedOwner = patternSpottedOwner.matcher(page); + if (matcherSpottedOwner.find()) { + trackable.spottedType = cgTrackable.SPOTTED_OWNER; + } + } catch (Exception e) { + // failed to parse trackable last known place + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable last known place"); + } + + // released + try { + final Matcher matcherReleased = patternReleased.matcher(page); + while (matcherReleased.find()) { + if (matcherReleased.groupCount() > 0 && matcherReleased.group(1) != null) { + try { + if (trackable.released == null) { + trackable.released = dateTbIn1.parse(matcherReleased.group(1)); + } + } catch (Exception e) { + // + } + + try { + if (trackable.released == null) { + trackable.released = dateTbIn2.parse(matcherReleased.group(1)); + } + } catch (Exception e) { + // + } + } + } + } catch (Exception e) { + // failed to parse trackable released date + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable released date"); + } + + // trackable distance + try { + final Matcher matcherDistance = patternDistance.matcher(page); + while (matcherDistance.find()) { + if (matcherDistance.groupCount() > 0) { + trackable.distance = parseDistance(matcherDistance.group(1)); + } + } + } catch (Exception e) { + // failed to parse trackable distance + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable distance"); + } + + // trackable goal + try { + final Matcher matcherGoal = patternGoal.matcher(page); + while (matcherGoal.find()) { + if (matcherGoal.groupCount() > 0) { + trackable.goal = matcherGoal.group(1); + } + } + } catch (Exception e) { + // failed to parse trackable goal + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable goal"); + } + + // trackable details & image + try { + final Matcher matcherDetailsImage = patternDetailsImage.matcher(page); + while (matcherDetailsImage.find()) { + if (matcherDetailsImage.groupCount() > 0) { + final String image = matcherDetailsImage.group(3); + final String details = matcherDetailsImage.group(4); + + if (image != null) { + trackable.image = image; + } + if (details != null) { + trackable.details = details; + } + } + } + } catch (Exception e) { + // failed to parse trackable details & image + Log.w(cgSettings.tag, "cgeoBase.parseTrackable: Failed to parse trackable details & image"); + } + + // trackable logs + try { + final Matcher matcherLogs = patternLogs.matcher(page); + while (matcherLogs.find()) { + if (matcherLogs.groupCount() > 0) { + final Pattern patternLog = Pattern.compile("[^>]*>" + + "[^<]*<td[^<]*<img src=[\"|'].*\\/icons\\/([^\\.]+)\\.[a-z]{2,5}[\"|'][^>]*> (\\d+).(\\d+).(\\d+)[^<]*</td>" + + "[^<]*<td>[^<]*<a href=[^>]+>([^<]+)<.a>([^<]*|[^<]*<a href=[\"|'].*guid=([^\"]*)\">([^<]*)</a>[^<]*)</td>" + + "[^<]*<td>[^<]*</td>" + + "[^<]*<td[^<]*<a href=[^>]+>[^<]+</a>[^<]*</td>[^<]*</tr>" + + "[^<]*<tr[^>]*>[^<]*<td[^>]*>(.*?)</td>[^<]*</tr>.*" + + ""); + // 1 filename == type + // 2 month + // 3 date + // 4 year + // 5 user + // 6 action dependent + // 7 cache guid + // 8 cache name + // 9 text + final String[] logs = matcherLogs.group(1).split("<tr class=\"Data BorderTop"); + final int logsCnt = logs.length; + + for (int k = 1; k < logsCnt; k++) { + final Matcher matcherLog = patternLog.matcher(logs[k]); + if (matcherLog.find()) { + final cgLog logDone = new cgLog(); + + String logTmp = matcherLog.group(9); + logTmp = Pattern.compile("<p>").matcher(logTmp).replaceAll("\n"); + logTmp = Pattern.compile("<br[^>]*>").matcher(logTmp).replaceAll("\n"); + logTmp = Pattern.compile("<\\/p>").matcher(logTmp).replaceAll(""); + logTmp = Pattern.compile("\r+").matcher(logTmp).replaceAll("\n"); + + int day = -1; + try { + day = Integer.parseInt(matcherLog.group(3)); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse logs date (day): " + e.toString()); + } + + int month = -1; + try { + month = Integer.parseInt(matcherLog.group(2)); + month -= 1; + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse logs date (month): " + e.toString()); + } + + int year = -1; + try { + year = Integer.parseInt(matcherLog.group(4)); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse logs date (year): " + e.toString()); + } + + long logDate; + if (year > 0 && month >= 0 && day > 0) { + Calendar date = Calendar.getInstance(); + date.set(year, month, day, 12, 0, 0); + logDate = date.getTimeInMillis(); + logDate = (long) (Math.ceil(logDate / 1000)) * 1000; + } else { + logDate = 0; + } + + if (logTypes.containsKey(matcherLog.group(1).toLowerCase()) == true) { + logDone.type = logTypes.get(matcherLog.group(1).toLowerCase()); + } else { + logDone.type = logTypes.get("icon_note"); + } + + logDone.author = Html.fromHtml(matcherLog.group(5)).toString(); + logDone.date = logDate; + logDone.log = logTmp; + if (matcherLog.group(7) != null && matcherLog.group(8) != null) { + logDone.cacheGuid = matcherLog.group(7); + logDone.cacheName = matcherLog.group(8); + } + + trackable.logs.add(logDone); + } + } + } + } + } catch (Exception e) { + // failed to parse logs + Log.w(cgSettings.tag, "cgeoBase.parseCache: Failed to parse cache logs"); + } + + app.saveTrackable(trackable); + + return trackable; + } + + public ArrayList<Integer> parseTypes(String page) { + if (page == null || page.length() == 0) { + return null; + } + + final ArrayList<Integer> types = new ArrayList<Integer>(); + + final Pattern typeBoxPattern = Pattern.compile("<select name=\"ctl00\\$ContentBody\\$LogBookPanel1\\$ddLogType\" id=\"ctl00_ContentBody_LogBookPanel1_ddLogType\"[^>]*>" + + "(([^<]*<option[^>]*>[^<]+</option>)+)[^<]*</select>", Pattern.CASE_INSENSITIVE); + final Matcher typeBoxMatcher = typeBoxPattern.matcher(page); + String typesText = null; + if (typeBoxMatcher.find()) { + if (typeBoxMatcher.groupCount() > 0) { + typesText = typeBoxMatcher.group(1); + } + } + + if (typesText != null) { + final Pattern typePattern = Pattern.compile("<option( selected=\"selected\")? value=\"(\\d+)\">[^<]+</option>", Pattern.CASE_INSENSITIVE); + final Matcher typeMatcher = typePattern.matcher(typesText); + while (typeMatcher.find()) { + if (typeMatcher.groupCount() > 1) { + final int type = Integer.parseInt(typeMatcher.group(2)); + + if (type > 0) { + types.add(type); + } + } + } + } + + return types; + } + + public ArrayList<cgTrackableLog> parseTrackableLog(String page) { + if (page == null || page.length() == 0) { + return null; + } + + final ArrayList<cgTrackableLog> trackables = new ArrayList<cgTrackableLog>(); + + int startPos = -1; + int endPos = -1; + + startPos = page.indexOf("<table id=\"tblTravelBugs\""); + if (startPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: ID \"tblTravelBugs\" not found on page"); + return null; + } + + page = page.substring(startPos); // cut on <table + + endPos = page.indexOf("</table>"); + if (endPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: end of ID \"tblTravelBugs\" not found on page"); + return null; + } + + page = page.substring(0, endPos); // cut on </table> + + startPos = page.indexOf("<tbody>"); + if (startPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: tbody not found on page"); + return null; + } + + page = page.substring(startPos); // cut on <tbody> + + endPos = page.indexOf("</tbody>"); + if (endPos == -1) { + Log.e(cgSettings.tag, "cgeoBase.parseTrackableLog: end of tbody not found on page"); + return null; + } + + page = page.substring(0, endPos); // cut on </tbody> + + final Pattern trackablePattern = Pattern.compile("<tr id=\"ctl00_ContentBody_LogBookPanel1_uxTrackables_repTravelBugs_ctl[0-9]+_row\"[^>]*>" + + "[^<]*<td>[^<]*<a href=\"[^\"]+\">([A-Z0-9]+)</a>[^<]*</td>[^<]*<td>([^<]+)</td>[^<]*<td>" + + "[^<]*<select name=\"ctl00\\$ContentBody\\$LogBookPanel1\\$uxTrackables\\$repTravelBugs\\$ctl([0-9]+)\\$ddlAction\"[^>]*>" + + "([^<]*<option value=\"([0-9]+)(_[a-z]+)?\">[^<]+</option>)+" + + "[^<]*</select>[^<]*</td>[^<]*</tr>", Pattern.CASE_INSENSITIVE); + final Matcher trackableMatcher = trackablePattern.matcher(page); + while (trackableMatcher.find()) { + if (trackableMatcher.groupCount() > 0) { + final cgTrackableLog trackable = new cgTrackableLog(); + + if (trackableMatcher.group(1) != null) { + trackable.trackCode = trackableMatcher.group(1); + } else { + continue; + } + if (trackableMatcher.group(2) != null) { + trackable.name = Html.fromHtml(trackableMatcher.group(2)).toString(); + } else { + continue; + } + if (trackableMatcher.group(3) != null) { + trackable.ctl = new Integer(trackableMatcher.group(3)); + } else { + continue; + } + if (trackableMatcher.group(5) != null) { + trackable.id = new Integer(trackableMatcher.group(5)); + } else { + continue; + } + + Log.i(cgSettings.tag, "Trackable in inventory (#" + trackable.ctl + "/" + trackable.id + "): " + trackable.trackCode + " - " + trackable.name); + + trackables.add(trackable); + } + } + + return trackables; + } + + public int parseFindCount(String page) { + if (page == null || page.length() == 0) { + return -1; + } + + int findCount = -1; + + try { + final Pattern findPattern = Pattern.compile("<strong>Caches Found:<\\/strong>([^<]+)<br", Pattern.CASE_INSENSITIVE); + final Matcher findMatcher = findPattern.matcher(page); + if (findMatcher.find() == true) { + if (findMatcher.groupCount() > 0) { + String count = findMatcher.group(1); + + if (count != null) { + count = count.trim(); + + if (count.length() == 0) { + findCount = 0; + } else { + findCount = Integer.parseInt(count); + } + } + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.parseFindCount: " + e.toString()); + } + + return findCount; + } + + + public static String stripParagraphs(String text) { + if (text == null) { + return ""; + } + + final Pattern patternP = Pattern.compile("(<p>|</p>|<br \\/>|<br>)", Pattern.CASE_INSENSITIVE); + final Pattern patternP2 = Pattern.compile("([ ]+)", Pattern.CASE_INSENSITIVE); + final Matcher matcherP = patternP.matcher(text); + final Matcher matcherP2 = patternP2.matcher(text); + + matcherP.replaceAll(" "); + matcherP2.replaceAll(" "); + + return text.trim(); + } + + public static String stripTags(String text) { + if (text == null) { + return ""; + } + + final Pattern patternP = Pattern.compile("(<[^>]+>)", Pattern.CASE_INSENSITIVE); + final Matcher matcherP = patternP.matcher(text); + + matcherP.replaceAll(" "); + + return text.trim(); + } + + public static String capitalizeSentence(String sentence) { + if (sentence == null) { + return ""; + } + + final String[] word = sentence.split(" "); + + for (int i = 0; i < word.length; i++) { + word[i] = capitalizeWord(word[i]); + } + + return implode(" ", word); + } + + public static String capitalizeWord(String word) { + if (word.length() == 0) { + return word; + } + + return (word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase()); + } + + public static Double parseDistance(String dst) { + Double distance = null; + + final Pattern pattern = Pattern.compile("([0-9\\.,]+)[ ]*(km|mi)", Pattern.CASE_INSENSITIVE); + final Matcher matcher = pattern.matcher(dst); + while (matcher.find()) { + if (matcher.groupCount() > 1) { + if (matcher.group(2).equalsIgnoreCase("km") == true) { + distance = new Double(matcher.group(1)); + } else { + distance = new Double(matcher.group(1)) / kmInMiles; + } + } + } + + return distance; + } + + public static double getDistance(Double lat1, Double lon1, Double lat2, Double lon2) { + if (lat1 == null || lon1 == null || lat2 == null || lon2 == null) { + return 0d; + } + + lat1 *= deg2rad; + lon1 *= deg2rad; + lat2 *= deg2rad; + lon2 *= deg2rad; + + final double d = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2); + final double distance = erad * Math.acos(d); // distance in km + + if (Double.isNaN(distance) == false && distance > 0) { + return distance; + } else { + return 0d; + } + } + + public static Double getHeading(Double lat1, Double lon1, Double lat2, Double lon2) { + Double result = new Double(0); + + int ilat1 = (int) Math.round(0.5 + lat1 * 360000); + int ilon1 = (int) Math.round(0.5 + lon1 * 360000); + int ilat2 = (int) Math.round(0.5 + lat2 * 360000); + int ilon2 = (int) Math.round(0.5 + lon2 * 360000); + + lat1 *= deg2rad; + lon1 *= deg2rad; + lat2 *= deg2rad; + lon2 *= deg2rad; + + if (ilat1 == ilat2 && ilon1 == ilon2) { + return new Double(result); + } else if (ilat1 == ilat2) { + if (ilon1 > ilon2) { + result = new Double(270); + } else { + result = new Double(90); + } + } else if (ilon1 == ilon2) { + if (ilat1 > ilat2) { + result = new Double(180); + } + } else { + Double c = Math.acos(Math.sin(lat2) * Math.sin(lat1) + Math.cos(lat2) * Math.cos(lat1) * Math.cos(lon2 - lon1)); + Double A = Math.asin(Math.cos(lat2) * Math.sin(lon2 - lon1) / Math.sin(c)); + result = new Double(A * rad2deg); + if (ilat2 > ilat1 && ilon2 > ilon1) { + // result don't need change + } else if (ilat2 < ilat1 && ilon2 < ilon1) { + result = 180f - result; + } else if (ilat2 < ilat1 && ilon2 > ilon1) { + result = 180f - result; + } else if (ilat2 > ilat1 && ilon2 < ilon1) { + result += 360f; + } + } + + return result; + } + + public HashMap<String, Double> getRadialDistance(Double latitude, Double longitude, Double bearing, Double distance) { + final Double rlat1 = latitude * deg2rad; + final Double rlon1 = longitude * deg2rad; + final Double rbearing = bearing * deg2rad; + final Double rdistance = distance / erad; + + final Double rlat = Math.asin(Math.sin(rlat1) * Math.cos(rdistance) + Math.cos(rlat1) * Math.sin(rdistance) * Math.cos(rbearing)); + final Double rlon = rlon1 + Math.atan2(Math.sin(rbearing) * Math.sin(rdistance) * Math.cos(rlat1), Math.cos(rdistance) - Math.sin(rlat1) * Math.sin(rlat)); + + HashMap<String, Double> result = new HashMap<String, Double>(); + result.put("latitude", rlat * rad2deg); + result.put("longitude", rlon * rad2deg); + + return result; + } + + public String getHumanDistance(Float distance) { + if (distance == null) { + return "?"; + } + + return getHumanDistance(new Double(distance)); + } + + public String getHumanDistance(Double distance) { + if (distance == null) { + return "?"; + } + + if (settings.units == cgSettings.unitsImperial) { + distance *= kmInMiles; + if (distance > 100) { + return String.format(Locale.getDefault(), "%.0f", new Double(Math.round(distance))) + " mi"; + } else if (distance > 0.5) { + return String.format(Locale.getDefault(), "%.1f", new Double(Math.round(distance * 10.0) / 10.0)) + " mi"; + } else if (distance > 0.1) { + return String.format(Locale.getDefault(), "%.2f", new Double(Math.round(distance * 100.0) / 100.0)) + " mi"; + } else if (distance > 0.05) { + return String.format(Locale.getDefault(), "%.0f", new Double(Math.round(distance * 5280.0))) + " ft"; + } else if (distance > 0.01) { + return String.format(Locale.getDefault(), "%.1f", new Double(Math.round(distance * 5280 * 10.0) / 10.0)) + " ft"; + } else { + return String.format(Locale.getDefault(), "%.2f", new Double(Math.round(distance * 5280 * 100.0) / 100.0)) + " ft"; + } + } else { + if (distance > 100) { + return String.format(Locale.getDefault(), "%.0f", new Double(Math.round(distance))) + " km"; + } else if (distance > 10) { + return String.format(Locale.getDefault(), "%.1f", new Double(Math.round(distance * 10.0) / 10.0)) + " km"; + } else if (distance > 1) { + return String.format(Locale.getDefault(), "%.2f", new Double(Math.round(distance * 100.0) / 100.0)) + " km"; + } else if (distance > 0.1) { + return String.format(Locale.getDefault(), "%.0f", new Double(Math.round(distance * 1000.0))) + " m"; + } else if (distance > 0.01) { + return String.format(Locale.getDefault(), "%.1f", new Double(Math.round(distance * 1000.0 * 10.0) / 10.0)) + " m"; + } else { + return String.format(Locale.getDefault(), "%.2f", new Double(Math.round(distance * 1000.0 * 100.0) / 100.0)) + " m"; + } + } + } + + public String getHumanSpeed(float speed) { + double kph = speed * 3.6; + String unit = "kmh"; + + if (this.settings.units == cgSettings.unitsImperial) { + kph *= kmInMiles; + unit = "mph"; + } + + if (kph < 10) { + return String.format(Locale.getDefault(), "%.1f", new Double((Math.round(kph * 10) / 10))) + " " + unit; + } else { + return String.format(Locale.getDefault(), "%.0f", new Double(Math.round(kph))) + " " + unit; + } + } + + public HashMap<String, Object> parseLatlon(String latlon) { + final HashMap<String, Object> result = new HashMap<String, Object>(); + final Pattern patternLatlon = Pattern.compile("([NS])[^\\d]*(\\d+)[^°]*° (\\d+)\\.(\\d+) ([WE])[^\\d]*(\\d+)[^°]*° (\\d+)\\.(\\d+)", Pattern.CASE_INSENSITIVE); + final Matcher matcherLatlon = patternLatlon.matcher(latlon); + + while (matcherLatlon.find()) { + if (matcherLatlon.groupCount() > 0) { + result.put("latitudeString", (String) (matcherLatlon.group(1) + " " + matcherLatlon.group(2) + "° " + matcherLatlon.group(3) + "." + matcherLatlon.group(4))); + result.put("longitudeString", (String) (matcherLatlon.group(5) + " " + matcherLatlon.group(6) + "° " + matcherLatlon.group(7) + "." + matcherLatlon.group(8))); + int latNegative = -1; + int lonNegative = -1; + if (matcherLatlon.group(1).equalsIgnoreCase("N")) { + latNegative = 1; + } + if (matcherLatlon.group(5).equalsIgnoreCase("E")) { + lonNegative = 1; + } + result.put("latitude", new Double(latNegative * (new Float(matcherLatlon.group(2)) + new Float(matcherLatlon.group(3) + "." + matcherLatlon.group(4)) / 60))); + result.put("longitude", new Double(lonNegative * (new Float(matcherLatlon.group(6)) + new Float(matcherLatlon.group(7) + "." + matcherLatlon.group(8)) / 60))); + } else { + Log.w(cgSettings.tag, "cgBase.parseLatlon: Failed to parse coordinates."); + } + } + + return result; + } + + public String formatCoordinate(Double coord, String latlon, boolean degrees) { + String formatted = ""; + + if (coord == null) { + return formatted; + } + + String worldSide = ""; + if (latlon.equalsIgnoreCase("lat") == true) { + if (coord >= 0) { + // have the blanks here at the direction to avoid one String concatenation + worldSide = "N "; + } else { + worldSide = "S "; + } + } else if (latlon.equalsIgnoreCase("lon") == true) { + if (coord >= 0) { + worldSide = "E "; + } else { + worldSide = "W "; + } + } + + coord = Math.abs(coord); + + if (latlon.equalsIgnoreCase("lat") == true) { + if (degrees == true) { + formatted = worldSide + String.format(Locale.getDefault(), "%02.0f", Math.floor(coord)) + "° " + String.format(Locale.getDefault(), "%06.3f", ((coord - Math.floor(coord)) * 60)); + } else { + formatted = worldSide + String.format(Locale.getDefault(), "%02.0f", Math.floor(coord)) + " " + String.format(Locale.getDefault(), "%06.3f", ((coord - Math.floor(coord)) * 60)); + } + } else { + if (degrees == true) { + formatted = worldSide + String.format(Locale.getDefault(), "%03.0f", Math.floor(coord)) + "° " + String.format(Locale.getDefault(), "%06.3f", ((coord - Math.floor(coord)) * 60)); + } else { + formatted = worldSide + String.format(Locale.getDefault(), "%03.0f", Math.floor(coord)) + " " + String.format(Locale.getDefault(), "%06.3f", ((coord - Math.floor(coord)) * 60)); + } + } + + return formatted; + } + + public HashMap<String, Object> parseCoordinate(String coord, String latlon) { + final HashMap<String, Object> coords = new HashMap<String, Object>(); + + final Pattern patternA = Pattern.compile("^([NSWE])[^\\d]*(\\d+)°? +(\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); + final Pattern patternB = Pattern.compile("^([NSWE])[^\\d]*(\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); + final Pattern patternC = Pattern.compile("^(-?\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); + final Pattern patternD = Pattern.compile("^([NSWE])[^\\d]*(\\d+)°?$", Pattern.CASE_INSENSITIVE); + final Pattern patternE = Pattern.compile("^(-?\\d+)°?$", Pattern.CASE_INSENSITIVE); + final Pattern patternF = Pattern.compile("^([NSWE])[^\\d]*(\\d+)$", Pattern.CASE_INSENSITIVE); + final Pattern pattern0 = Pattern.compile("^(-?\\d+)([\\.|,](\\d+))?$", Pattern.CASE_INSENSITIVE); + + coord = coord.trim().toUpperCase(); + + final Matcher matcherA = patternA.matcher(coord); + final Matcher matcherB = patternB.matcher(coord); + final Matcher matcherC = patternC.matcher(coord); + final Matcher matcherD = patternD.matcher(coord); + final Matcher matcherE = patternE.matcher(coord); + final Matcher matcherF = patternF.matcher(coord); + final Matcher matcher0 = pattern0.matcher(coord); + + int latlonNegative; + if (matcherA.find() == true && matcherA.groupCount() > 0) { + if (matcherA.group(1).equalsIgnoreCase("N") || matcherA.group(1).equalsIgnoreCase("E")) { + latlonNegative = 1; + } else { + latlonNegative = -1; + } + + if (matcherA.groupCount() < 5 || matcherA.group(5) == null) { + coords.put("coordinate", new Double(latlonNegative * (new Double(matcherA.group(2)) + new Double(matcherA.group(3) + ".0") / 60))); + coords.put("string", matcherA.group(1) + " " + matcherA.group(2) + "° " + matcherA.group(3) + ".000"); + } else { + coords.put("coordinate", new Double(latlonNegative * (new Double(matcherA.group(2)) + new Double(matcherA.group(3) + "." + matcherA.group(5)) / 60))); + coords.put("string", matcherA.group(1) + " " + matcherA.group(2) + "° " + matcherA.group(3) + "." + matcherA.group(5)); + } + + return coords; + } else if (matcherB.find() == true && matcherB.groupCount() > 0) { + if (matcherB.group(1).equalsIgnoreCase("N") || matcherB.group(1).equalsIgnoreCase("E")) { + latlonNegative = 1; + } else { + latlonNegative = -1; + } + + if (matcherB.groupCount() < 4 || matcherB.group(4) == null) { + coords.put("coordinate", new Double(latlonNegative * (new Double(matcherB.group(2) + ".0")))); + } else { + coords.put("coordinate", new Double(latlonNegative * (new Double(matcherB.group(2) + "." + matcherB.group(4))))); + } + } else if (matcherC.find() == true && matcherC.groupCount() > 0) { + if (matcherC.groupCount() < 3 || matcherC.group(3) == null) { + coords.put("coordinate", new Double(new Float(matcherC.group(1) + ".0"))); + } else { + coords.put("coordinate", new Double(new Float(matcherC.group(1) + "." + matcherC.group(3)))); + } + } else if (matcherD.find() == true && matcherD.groupCount() > 0) { + if (matcherD.group(1).equalsIgnoreCase("N") || matcherD.group(1).equalsIgnoreCase("E")) { + latlonNegative = 1; + } else { + latlonNegative = -1; + } + + coords.put("coordinate", new Double(latlonNegative * (new Double(matcherB.group(2))))); + } else if (matcherE.find() == true && matcherE.groupCount() > 0) { + coords.put("coordinate", new Double(matcherE.group(1))); + } else if (matcherF.find() == true && matcherF.groupCount() > 0) { + if (matcherF.group(1).equalsIgnoreCase("N") || matcherF.group(1).equalsIgnoreCase("E")) { + latlonNegative = 1; + } else { + latlonNegative = -1; + } + + coords.put("coordinate", new Double(latlonNegative * (new Double(matcherB.group(2))))); + } else { + return null; + } + + if (matcher0.find() == true && matcher0.groupCount() > 0) { + String tmpDir = null; + Float tmpCoord; + if (matcher0.groupCount() < 3 || matcher0.group(3) == null) { + tmpCoord = new Float("0.0"); + } else { + tmpCoord = new Float("0." + matcher0.group(3)); + } + + if (latlon.equalsIgnoreCase("lat")) { + if (matcher0.group(1).equals("+")) { + tmpDir = "N"; + } + if (matcher0.group(1).equals("-")) { + tmpDir = "S"; + } + } else if (latlon.equalsIgnoreCase("lon")) { + if (matcher0.group(1).equals("+")) { + tmpDir = "E"; + } + if (matcher0.group(1).equals("-")) { + tmpDir = "W"; + } + } + + coords.put("string", tmpDir + " " + matcher0.group(1) + "° " + (Math.round(tmpCoord / (1 / 60) * 1000) * 1000)); + + return coords; + } else { + return new HashMap<String, Object>(); + } + } + + public Long searchByNextPage(cgSearchThread thread, Long searchId, int reason, boolean showCaptcha) { + final String viewstate = app.getViewstate(searchId); + final String viewstate1 = app.getViewstate1(searchId); + cgCacheWrap caches = new cgCacheWrap(); + String url = app.getUrl(searchId); + + if (url == null || url.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No url found"); + return searchId; + } + + if (viewstate == null || viewstate.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No viewstate given"); + return searchId; + } + + String host = "www.geocaching.com"; + String path = "/"; + final String method = "POST"; + + int dash = -1; + if (url.indexOf("http://") > -1) { + url = url.substring(7); + } + + dash = url.indexOf("/"); + if (dash > -1) { + host = url.substring(0, dash); + url = url.substring(dash); + } else { + host = url; + url = ""; + } + + dash = url.indexOf("?"); + if (dash > -1) { + path = url.substring(0, dash); + } else { + path = url; + } + + final HashMap<String, String> params = new HashMap<String, String>(); + params.put("__VIEWSTATE", viewstate); + if (viewstate1 != null) { + params.put("__VIEWSTATE1", viewstate1); + params.put("__VIEWSTATEFIELDCOUNT", "2"); + } + params.put("__EVENTTARGET", "ctl00$ContentBody$pgrBottom$ctl08"); + params.put("__EVENTARGUMENT", ""); + + String page = request(false, host, path, method, params, false, false, true).getData(); + if (checkLogin(page) == false) { + int loginState = login(); + if (loginState == 1) { + page = request(false, host, path, method, params, false, false, true).getData(); + } else if (loginState == -3) { + Log.i(cgSettings.tag, "Working as guest."); + } else { + app.setError(searchId, errorRetrieve.get(loginState)); + Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: Can not log in geocaching"); + return searchId; + } + } + + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No data from server"); + return searchId; + } + + caches = parseSearch(thread, url, page, showCaptcha); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByNextPage: No cache parsed"); + return searchId; + } + + // save to application + app.setError(searchId, caches.error); + app.setViewstate(searchId, caches.viewstate); + app.setViewstate1(searchId, caches.viewstate1); + + final ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); + for (cgCache cache : caches.cacheList) { + app.addGeocode(searchId, cache.geocode); + cacheList.add(cache); + } + + app.addSearch(searchId, cacheList, true, reason); + + return searchId; + } + + public Long searchByGeocode(HashMap<String, String> parameters, int reason, boolean forceReload) { + final cgSearch search = new cgSearch(); + String geocode = parameters.get("geocode"); + String guid = parameters.get("guid"); + + if ((geocode == null || geocode.length() == 0) && ((guid == null || guid.length() == 0))) { + Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No geocode nor guid given"); + return null; + } + + if (forceReload == false && reason == 0 && (app.isOffline(geocode, guid) == true || app.isThere(geocode, guid, true, true) == true)) { + if ((geocode == null || geocode.length() == 0) && guid != null && guid.length() > 0) { + geocode = app.getGeocode(guid); + } + + ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); + cacheList.add(app.getCacheByGeocode(geocode, true, true, true, true, true, true)); + search.addGeocode(geocode); + + app.addSearch(search, cacheList, false, reason); + + cacheList.clear(); + cacheList = null; + + return search.getCurrentId(); + } + + final String host = "www.geocaching.com"; + final String path = "/seek/cache_details.aspx"; + final String method = "GET"; + final HashMap<String, String> params = new HashMap<String, String>(); + if (geocode != null && geocode.length() > 0) { + params.put("wp", geocode); + } else if (guid != null && guid.length() > 0) { + params.put("guid", guid); + } + params.put("decrypt", "y"); + params.put("log", "y"); // download logs (more than 5 + params.put("numlogs", "35"); // 35 logs + + String page = requestLogged(false, host, path, method, params, false, false, false); + + if (page == null || page.length() == 0) { + if (app.isThere(geocode, guid, true, false) == true) { + if ((geocode == null || geocode.length() == 0) && guid != null && guid.length() > 0) { + Log.i(cgSettings.tag, "Loading old cache from cache."); + + geocode = app.getGeocode(guid); + } + + final ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); + cacheList.add(app.getCacheByGeocode(geocode)); + search.addGeocode(geocode); + search.error = null; + search.errorRetrieve = 0; // reset errors from previous failed request + + app.addSearch(search, cacheList, false, reason); + + cacheList.clear(); + + return search.getCurrentId(); + } + + Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No data from server"); + return null; + } + + final cgCacheWrap caches = parseCache(page, reason); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + if (caches != null && caches.error != null && caches.error.length() > 0) { + search.error = caches.error; + } + if (caches != null && caches.url != null && caches.url.length() > 0) { + search.url = caches.url; + } + + app.addSearch(search, null, true, reason); + + Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No cache parsed"); + return null; + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByGeocode: No application found"); + return null; + } + + final ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); + if (caches != null) { + if (caches.error != null && caches.error.length() > 0) { + search.error = caches.error; + } + if (caches.url != null && caches.url.length() > 0) { + search.url = caches.url; + } + if (caches.viewstate != null && caches.viewstate.length() > 0) { + search.viewstate = caches.viewstate; + } + if (caches.viewstate1 != null && caches.viewstate1.length() > 0) { + search.viewstate1 = caches.viewstate1; + } + search.totalCnt = caches.totalCnt; + + for (cgCache cache : caches.cacheList) { + search.addGeocode(cache.geocode); + cacheList.add(cache); + } + } + + app.addSearch(search, cacheList, true, reason); + + page = null; + cacheList.clear(); + + return search.getCurrentId(); + } + + public Long searchByOffline(HashMap<String, Object> parameters) { + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByOffline: No application found"); + return null; + } + + Double latitude = null; + Double longitude = null; + String cachetype = null; + Integer list = 1; + + if (parameters.containsKey("latitude") == true && parameters.containsKey("longitude") == true) { + latitude = (Double) parameters.get("latitude"); + longitude = (Double) parameters.get("longitude"); + } + + if (parameters.containsKey("cachetype") == true) { + cachetype = (String) parameters.get("cachetype"); + } + + if (parameters.containsKey("list") == true) { + list = (Integer) parameters.get("list"); + } + + final cgSearch search = app.getBatchOfStoredCaches(true, latitude, longitude, cachetype, list); + search.totalCnt = app.getAllStoredCachesCount(true, cachetype, list); + + return search.getCurrentId(); + } + + public Long searchByHistory(HashMap<String, Object> parameters) { + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByHistory: No application found"); + return null; + } + + String cachetype = null; + + if (parameters.containsKey("cachetype") == true) { + cachetype = (String) parameters.get("cachetype"); + } + + final cgSearch search = app.getHistoryOfCaches(true, cachetype); + search.totalCnt = app.getAllHistoricCachesCount(true, cachetype); + + return search.getCurrentId(); + } + + public Long searchByCoords(cgSearchThread thread, HashMap<String, String> parameters, int reason, boolean showCaptcha) { + final cgSearch search = new cgSearch(); + final String latitude = parameters.get("latitude"); + final String longitude = parameters.get("longitude"); + cgCacheWrap caches = new cgCacheWrap(); + String cacheType = parameters.get("cachetype"); + + if (latitude == null || latitude.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No latitude given"); + return null; + } + + if (longitude == null || longitude.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No longitude given"); + return null; + } + + if (cacheType != null && cacheType.length() == 0) { + cacheType = null; + } + + final String host = "www.geocaching.com"; + final String path = "/seek/nearest.aspx"; + final String method = "GET"; + final HashMap<String, String> params = new HashMap<String, String>(); + if (cacheType != null && cacheIDs.containsKey(cacheType) == true) { + params.put("tx", cacheIDs.get(cacheType)); + } else { + params.put("tx", cacheIDs.get("all")); + } + params.put("lat", latitude); + params.put("lng", longitude); + + final String url = "http://" + host + path + "?" + prepareParameters(params, false, true); + String page = requestLogged(false, host, path, method, params, false, false, true); + + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No data from server"); + return null; + } + + caches = parseSearch(thread, url, page, showCaptcha); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No cache parsed"); + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No application found"); + return null; + } + + final ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); + if (caches != null) { + if (caches.error != null && caches.error.length() > 0) { + search.error = caches.error; + } + if (caches.url != null && caches.url.length() > 0) { + search.url = caches.url; + } + if (caches.viewstate != null && caches.viewstate.length() > 0) { + search.viewstate = caches.viewstate; + } + if (caches.viewstate1 != null && caches.viewstate1.length() > 0) { + search.viewstate1 = caches.viewstate1; + } + search.totalCnt = caches.totalCnt; + + for (cgCache cache : caches.cacheList) { + if (settings.excludeDisabled == 0 || (settings.excludeDisabled == 1 && cache.disabled == false)) { + search.addGeocode(cache.geocode); + cacheList.add(cache); + } + } + } + + app.addSearch(search, cacheList, true, reason); + + return search.getCurrentId(); + } + + public Long searchByKeyword(cgSearchThread thread, HashMap<String, String> parameters, int reason, boolean showCaptcha) { + final cgSearch search = new cgSearch(); + final String keyword = parameters.get("keyword"); + cgCacheWrap caches = new cgCacheWrap(); + String cacheType = parameters.get("cachetype"); + + if (keyword == null || keyword.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByKeyword: No keyword given"); + return null; + } + + if (cacheType != null && cacheType.length() == 0) { + cacheType = null; + } + + final String host = "www.geocaching.com"; + final String path = "/seek/nearest.aspx"; + final String method = "GET"; + final HashMap<String, String> params = new HashMap<String, String>(); + if (cacheType != null && cacheIDs.containsKey(cacheType) == true) { + params.put("tx", cacheIDs.get(cacheType)); + } else { + params.put("tx", cacheIDs.get("all")); + } + params.put("key", keyword); + + final String url = "http://" + host + path + "?" + prepareParameters(params, false, true); + String page = requestLogged(false, host, path, method, params, false, false, true); + + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByKeyword: No data from server"); + return null; + } + + caches = parseSearch(thread, url, page, showCaptcha); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByKeyword: No cache parsed"); + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No application found"); + return null; + } + + final ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); + if (caches != null) { + if (caches.error != null && caches.error.length() > 0) { + search.error = caches.error; + } + if (caches.url != null && caches.url.length() > 0) { + search.url = caches.url; + } + if (caches.viewstate != null && caches.viewstate.length() > 0) { + search.viewstate = caches.viewstate; + } + if (caches.viewstate1 != null && caches.viewstate1.length() > 0) { + search.viewstate1 = caches.viewstate1; + } + search.totalCnt = caches.totalCnt; + + for (cgCache cache : caches.cacheList) { + if (settings.excludeDisabled == 0 || (settings.excludeDisabled == 1 && cache.disabled == false)) { + search.addGeocode(cache.geocode); + cacheList.add(cache); + } + } + } + + app.addSearch(search, cacheList, true, reason); + + return search.getCurrentId(); + } + + public Long searchByUsername(cgSearchThread thread, HashMap<String, String> parameters, int reason, boolean showCaptcha) { + final cgSearch search = new cgSearch(); + final String userName = parameters.get("username"); + cgCacheWrap caches = new cgCacheWrap(); + String cacheType = parameters.get("cachetype"); + + if (userName == null || userName.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No user name given"); + return null; + } + + if (cacheType != null && cacheType.length() == 0) { + cacheType = null; + } + + final String host = "www.geocaching.com"; + final String path = "/seek/nearest.aspx"; + final String method = "GET"; + final HashMap<String, String> params = new HashMap<String, String>(); + if (cacheType != null && cacheIDs.containsKey(cacheType) == true) { + params.put("tx", cacheIDs.get(cacheType)); + } else { + params.put("tx", cacheIDs.get("all")); + } + params.put("ul", userName); + + boolean my = false; + if (userName.equalsIgnoreCase(settings.getLogin().get("username")) == true) { + my = true; + Log.i(cgSettings.tag, "cgBase.searchByUsername: Overriding users choice, downloading all caches."); + } + + final String url = "http://" + host + path + "?" + prepareParameters(params, my, true); + String page = requestLogged(false, host, path, method, params, false, my, true); + + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No data from server"); + return null; + } + + caches = parseSearch(thread, url, page, showCaptcha); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByUsername: No cache parsed"); + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No application found"); + return null; + } + + final ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); + if (caches != null) { + if (caches.error != null && caches.error.length() > 0) { + search.error = caches.error; + } + if (caches.url != null && caches.url.length() > 0) { + search.url = caches.url; + } + if (caches.viewstate != null && caches.viewstate.length() > 0) { + search.viewstate = caches.viewstate; + } + if (caches.viewstate1 != null && caches.viewstate1.length() > 0) { + search.viewstate1 = caches.viewstate1; + } + search.totalCnt = caches.totalCnt; + + for (cgCache cache : caches.cacheList) { + if (settings.excludeDisabled == 0 || (settings.excludeDisabled == 1 && cache.disabled == false)) { + search.addGeocode(cache.geocode); + cacheList.add(cache); + } + } + } + + app.addSearch(search, cacheList, true, reason); + + return search.getCurrentId(); + } + + public Long searchByOwner(cgSearchThread thread, HashMap<String, String> parameters, int reason, boolean showCaptcha) { + final cgSearch search = new cgSearch(); + final String userName = parameters.get("username"); + cgCacheWrap caches = new cgCacheWrap(); + String cacheType = parameters.get("cachetype"); + + if (userName == null || userName.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByOwner: No user name given"); + return null; + } + + if (cacheType != null && cacheType.length() == 0) { + cacheType = null; + } + + final String host = "www.geocaching.com"; + final String path = "/seek/nearest.aspx"; + final String method = "GET"; + final HashMap<String, String> params = new HashMap<String, String>(); + if (cacheType != null && cacheIDs.containsKey(cacheType) == true) { + params.put("tx", cacheIDs.get(cacheType)); + } else { + params.put("tx", cacheIDs.get("all")); + } + params.put("u", userName); + + final String url = "http://" + host + path + "?" + prepareParameters(params, false, true); + String page = requestLogged(false, host, path, method, params, false, false, true); + + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByOwner: No data from server"); + return null; + } + + caches = parseSearch(thread, url, page, showCaptcha); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByOwner: No cache parsed"); + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByCoords: No application found"); + return null; + } + + final ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); + if (caches != null) { + if (caches.error != null && caches.error.length() > 0) { + search.error = caches.error; + } + if (caches.url != null && caches.url.length() > 0) { + search.url = caches.url; + } + if (caches.viewstate != null && caches.viewstate.length() > 0) { + search.viewstate = caches.viewstate; + } + if (caches.viewstate1 != null && caches.viewstate1.length() > 0) { + search.viewstate1 = caches.viewstate1; + } + search.totalCnt = caches.totalCnt; + + for (cgCache cache : caches.cacheList) { + if (settings.excludeDisabled == 0 || (settings.excludeDisabled == 1 && cache.disabled == false)) { + search.addGeocode(cache.geocode); + cacheList.add(cache); + } + } + } + + app.addSearch(search, cacheList, true, reason); + + return search.getCurrentId(); + } + + public Long searchByViewport(HashMap<String, String> parameters, int reason) { + final cgSearch search = new cgSearch(); + final String latMin = parameters.get("latitude-min"); + final String latMax = parameters.get("latitude-max"); + final String lonMin = parameters.get("longitude-min"); + final String lonMax = parameters.get("longitude-max"); + + String usertoken = null; + if (parameters.get("usertoken") != null) { + usertoken = parameters.get("usertoken"); + } else { + usertoken = ""; + } + cgCacheWrap caches = new cgCacheWrap(); + + String page = null; + + if (latMin == null || latMin.length() == 0 || latMax == null || latMax.length() == 0 || lonMin == null || lonMin.length() == 0 || lonMax == null || lonMax.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByViewport: Not enough parameters to recognize viewport"); + return null; + } + + final String host = "www.geocaching.com"; + final String path = "/map/default.aspx/MapAction"; + + String params = "{\"dto\":{\"data\":{\"c\":1,\"m\":\"\",\"d\":\"" + latMax + "|" + latMin + "|" + lonMax + "|" + lonMin + "\"},\"ut\":\"" + usertoken + "\"}}"; + + final String url = "http://" + host + path + "?" + params; + page = requestJSONgc(host, path, params); + + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchByViewport: No data from server"); + return null; + } + + caches = parseMapJSON(url, page); + if (caches == null || caches.cacheList == null || caches.cacheList.isEmpty()) { + Log.e(cgSettings.tag, "cgeoBase.searchByViewport: No cache parsed"); + } + + if (app == null) { + Log.e(cgSettings.tag, "cgeoBase.searchByViewport: No application found"); + return null; + } + + final ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); + if (caches != null) { + if (caches.error != null && caches.error.length() > 0) { + search.error = caches.error; + } + if (caches.url != null && caches.url.length() > 0) { + search.url = caches.url; + } + if (caches.viewstate != null && caches.viewstate.length() > 0) { + search.viewstate = caches.viewstate; + } + if (caches.viewstate1 != null && caches.viewstate1.length() > 0) { + search.viewstate1 = caches.viewstate1; + } + search.totalCnt = caches.totalCnt; + + if (caches.cacheList != null && caches.cacheList.size() > 0) { + for (cgCache cache : caches.cacheList) { + if ((settings.excludeDisabled == 0 || (settings.excludeDisabled == 1 && cache.disabled == false)) + && (settings.excludeMine == 0 || (settings.excludeMine == 1 && cache.own == false)) + && (settings.excludeMine == 0 || (settings.excludeMine == 1 && cache.found == false)) + && (settings.cacheType == null || (settings.cacheType.equals(cache.type) == true))) { + search.addGeocode(cache.geocode); + cacheList.add(cache); + } + } + } + } + + app.addSearch(search, cacheList, true, reason); + + return search.getCurrentId(); + } + + public ArrayList<cgUser> getGeocachersInViewport(String username, Double latMin, Double latMax, Double lonMin, Double lonMax) { + final ArrayList<cgUser> users = new ArrayList<cgUser>(); + + if (username == null) { + return users; + } + if (latMin == null || latMax == null || lonMin == null || lonMax == null) { + return users; + } + + final String host = "api.go4cache.com"; + final String path = "/get.php"; + final String method = "POST"; + final HashMap<String, String> params = new HashMap<String, String>(); + + params.put("u", username); + params.put("ltm", String.format((Locale) null, "%.6f", latMin)); + params.put("ltx", String.format((Locale) null, "%.6f", latMax)); + params.put("lnm", String.format((Locale) null, "%.6f", lonMin)); + params.put("lnx", String.format((Locale) null, "%.6f", lonMax)); + + final String data = request(false, host, path, method, params, false, false, false).getData(); + + if (data == null || data.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.getGeocachersInViewport: No data from server"); + return null; + } + + try { + final JSONObject dataJSON = new JSONObject(data); + + final JSONArray usersData = dataJSON.getJSONArray("users"); + if (usersData != null && usersData.length() > 0) { + int count = usersData.length(); + JSONObject oneUser = null; + for (int i = 0; i < count; i++) { + final cgUser user = new cgUser(); + oneUser = usersData.getJSONObject(i); + if (oneUser != null) { + final String located = oneUser.getString("located"); + if (located != null) { + user.located = dateSqlIn.parse(located); + } else { + user.located = new Date(); + } + user.username = oneUser.getString("user"); + user.latitude = oneUser.getDouble("latitude"); + user.longitude = oneUser.getDouble("longitude"); + user.action = oneUser.getString("action"); + user.client = oneUser.getString("client"); + + if (user.latitude != null && user.longitude != null) { + users.add(user); + } + } + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.getGeocachersInViewport: " + e.toString()); + } + + return users; + } + + public cgTrackable searchTrackable(HashMap<String, String> parameters) { + final String geocode = parameters.get("geocode"); + final String guid = parameters.get("guid"); + final String id = parameters.get("id"); + cgTrackable trackable = new cgTrackable(); + + if ((geocode == null || geocode.length() == 0) && (guid == null || guid.length() == 0) && (id == null || id.length() == 0)) { + Log.e(cgSettings.tag, "cgeoBase.searchTrackable: No geocode nor guid nor id given"); + return null; + } + + final String host = "www.geocaching.com"; + final String path = "/track/details.aspx"; + final String method = "GET"; + final HashMap<String, String> params = new HashMap<String, String>(); + if (geocode != null && geocode.length() > 0) { + params.put("tracker", geocode); + } else if (guid != null && guid.length() > 0) { + params.put("guid", guid); + } else if (id != null && id.length() > 0) { + params.put("id", id); + } + + String page = requestLogged(false, host, path, method, params, false, false, false); + + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.searchTrackable: No data from server"); + return trackable; + } + + trackable = parseTrackable(page); + if (trackable == null) { + Log.e(cgSettings.tag, "cgeoBase.searchTrackable: No trackable parsed"); + return trackable; + } + + return trackable; + } + + public int postLog(cgeoapplication app, String geocode, String cacheid, String viewstate, String viewstate1, int logType, int year, int month, int day, String log, ArrayList<cgTrackableLog> trackables) { + if (viewstate == null || viewstate.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.postLog: No viewstate given"); + return 1000; + } + + if (logTypes2.containsKey(logType) == false) { + Log.e(cgSettings.tag, "cgeoBase.postLog: Unknown logtype"); + return 1000; + } + + if (log == null || log.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.postLog: No log text given"); + return 1001; + } + + // fix log (non-Latin characters converted to HTML entities) + final int logLen = log.length(); + final StringBuilder logUpdated = new StringBuilder(); + + for (int i = 0; i < logLen; i++) { + char c = log.charAt(i); + + if (c > 300) { + logUpdated.append("&#"); + logUpdated.append(Integer.toString((int) c)); + logUpdated.append(";"); + } else { + logUpdated.append(c); + } + } + log = logUpdated.toString(); + + log = log.replace("\n", "\r\n"); // windows' eol + + if (trackables != null) { + Log.i(cgSettings.tag, "Trying to post log for cache #" + cacheid + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + log + "; trackables: " + trackables.size()); + } else { + Log.i(cgSettings.tag, "Trying to post log for cache #" + cacheid + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + log + "; trackables: 0"); + } + + final String host = "www.geocaching.com"; + final String path = "/seek/log.aspx?ID=" + cacheid; + final String method = "POST"; + final HashMap<String, String> params = new HashMap<String, String>(); + + params.put("__VIEWSTATE", viewstate); + if (viewstate1 != null) { + params.put("__VIEWSTATE1", viewstate1); + params.put("__VIEWSTATEFIELDCOUNT", "2"); + } + params.put("__EVENTTARGET", ""); + params.put("__EVENTARGUMENT", ""); + params.put("__LASTFOCUS", ""); + params.put("ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", String.format("%02d", month) + "/" + String.format("%02d", day) + "/" + String.format("%04d", year)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Month", Integer.toString(month)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Day", Integer.toString(day)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Year", Integer.toString(year)); + params.put("ctl00$ContentBody$LogBookPanel1$uxLogInfo", log); + params.put("ctl00$ContentBody$LogBookPanel1$LogButton", "Submit Log Entry"); + params.put("ctl00$ContentBody$uxVistOtherListingGC", ""); + if (trackables != null && trackables.isEmpty() == false) { // we have some trackables to proceed + final StringBuilder hdnSelected = new StringBuilder(); + + for (cgTrackableLog tb : trackables) { + final String action = Integer.toString(tb.id) + logTypesTrackableAction.get(tb.action); + + if (tb.action > 0) { + hdnSelected.append(action); + hdnSelected.append(","); + } + } + + params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnSelectedActions", hdnSelected.toString()); // selected trackables + params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnCurrentFilter", ""); + } + + String page = request(false, host, path, method, params, false, false, false).getData(); + if (checkLogin(page) == false) { + int loginState = login(); + if (loginState == 1) { + page = request(false, host, path, method, params, false, false, false).getData(); + } else { + Log.e(cgSettings.tag, "cgeoBase.postLog: Can not log in geocaching (error: " + loginState + ")"); + return loginState; + } + } + + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.postLog: No data from server"); + return 1002; + } + + // maintenance, archived needs to be confirmed + final Pattern pattern = Pattern.compile("<span id=\"ctl00_ContentBody_LogBookPanel1_lbConfirm\"[^>]*>([^<]*<font[^>]*>)?([^<]+)(</font>[^<]*)?</span>", Pattern.CASE_INSENSITIVE); + final Matcher matcher = pattern.matcher(page); + + try { + if (matcher.find() == true && matcher.groupCount() > 0) { + final String viewstateConfirm = findViewstate(page, 0); + final String viewstate1Confirm = findViewstate(page, 1); + + if (viewstateConfirm == null || viewstateConfirm.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.postLog: No viewstate for confirm log"); + return 1000; + } + + params.clear(); + params.put("__VIEWSTATE", viewstateConfirm); + if (viewstate1 != null) { + params.put("__VIEWSTATE1", viewstate1Confirm); + params.put("__VIEWSTATEFIELDCOUNT", "2"); + } + params.put("__EVENTTARGET", ""); + params.put("__EVENTARGUMENT", ""); + params.put("__LASTFOCUS", ""); + params.put("ctl00$ContentBody$LogBookPanel1$btnConfirm", "Yes"); + params.put("ctl00$ContentBody$LogBookPanel1$uxLogInfo", log); + params.put("ctl00$ContentBody$uxVistOtherListingGC", ""); + if (trackables != null && trackables.isEmpty() == false) { // we have some trackables to proceed + final StringBuilder hdnSelected = new StringBuilder(); + + for (cgTrackableLog tb : trackables) { + String ctl = null; + final String action = Integer.toString(tb.id) + logTypesTrackableAction.get(tb.action); + + if (tb.ctl < 10) { + ctl = "0" + Integer.toString(tb.ctl); + } else { + ctl = Integer.toString(tb.ctl); + } + + params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$repTravelBugs$ctl" + ctl + "$ddlAction", action); + if (tb.action > 0) { + hdnSelected.append(action); + hdnSelected.append(","); + } + } + + params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnSelectedActions", hdnSelected.toString()); // selected trackables + params.put("ctl00$ContentBody$LogBookPanel1$uxTrackables$hdnCurrentFilter", ""); + } + + page = request(false, host, path, method, params, false, false, false).getData(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.postLog.confim: " + e.toString()); + } + + try { + final Pattern patternOk = Pattern.compile("<h2[^>]*>[^<]*<span id=\"ctl00_ContentBody_lbHeading\"[^>]*>[^<]*</span>[^<]*</h2>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + final Matcher matcherOk = patternOk.matcher(page); + if (matcherOk.find() == true) { + Log.i(cgSettings.tag, "Log successfully posted to cache #" + cacheid); + + if (app != null && geocode != null) { + app.saveVisitDate(geocode); + } + + return 1; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.postLog.check: " + e.toString()); + } + + Log.e(cgSettings.tag, "cgeoBase.postLog: Failed to post log because of unknown error"); + return 1000; + } + + public int postLogTrackable(String tbid, String trackingCode, String viewstate, String viewstate1, int logType, int year, int month, int day, String log) { + if (viewstate == null || viewstate.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: No viewstate given"); + return 1000; + } + + if (logTypes2.containsKey(logType) == false) { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: Unknown logtype"); + return 1000; + } + + if (log == null || log.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: No log text given"); + return 1001; + } + + Log.i(cgSettings.tag, "Trying to post log for trackable #" + trackingCode + " - action: " + logType + "; date: " + year + "." + month + "." + day + ", log: " + log); + + log = log.replace("\n", "\r\n"); // windows' eol + + final Calendar currentDate = Calendar.getInstance(); + final String host = "www.geocaching.com"; + final String path = "/track/log.aspx?wid=" + tbid; + final String method = "POST"; + final HashMap<String, String> params = new HashMap<String, String>(); + + params.put("__VIEWSTATE", viewstate); + if (viewstate1 != null) { + params.put("__VIEWSTATE1", viewstate1); + params.put("__VIEWSTATEFIELDCOUNT", "2"); + } + params.put("__EVENTTARGET", ""); + params.put("__EVENTARGUMENT", ""); + params.put("__LASTFOCUS", ""); + params.put("ctl00$ContentBody$LogBookPanel1$ddLogType", Integer.toString(logType)); + params.put("ctl00$ContentBody$LogBookPanel1$tbCode", trackingCode); + if (currentDate.get(Calendar.YEAR) == year && (currentDate.get(Calendar.MONTH) + 1) == month && currentDate.get(Calendar.DATE) == day) { + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", ""); + } else { + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged", Integer.toString(month) + "/" + Integer.toString(day) + "/" + Integer.toString(year)); + } + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Day", Integer.toString(day)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Month", Integer.toString(month)); + params.put("ctl00$ContentBody$LogBookPanel1$DateTimeLogged$Year", Integer.toString(year)); + params.put("ctl00$ContentBody$LogBookPanel1$uxLogInfo", log); + params.put("ctl00$ContentBody$LogBookPanel1$LogButton", "Submit Log Entry"); + params.put("ctl00$ContentBody$uxVistOtherListingGC", ""); + + String page = request(false, host, path, method, params, false, false, false).getData(); + if (checkLogin(page) == false) { + int loginState = login(); + if (loginState == 1) { + page = request(false, host, path, method, params, false, false, false).getData(); + } else { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: Can not log in geocaching (error: " + loginState + ")"); + return loginState; + } + } + + if (page == null || page.length() == 0) { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: No data from server"); + return 1002; + } + + try { + final Pattern patternOk = Pattern.compile("<div id=[\"|']ctl00_ContentBody_LogBookPanel1_ViewLogPanel[\"|']>", Pattern.CASE_INSENSITIVE); + final Matcher matcherOk = patternOk.matcher(page); + if (matcherOk.find() == true) { + Log.i(cgSettings.tag, "Log successfully posted to trackable #" + trackingCode); + return 1; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable.check: " + e.toString()); + } + + Log.e(cgSettings.tag, "cgeoBase.postLogTrackable: Failed to post log because of unknown error"); + return 1000; + } + + final public static HostnameVerifier doNotVerify = new HostnameVerifier() { + + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + + public static void trustAllHosts() { + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + } + }; + + try { + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.trustAllHosts: " + e.toString()); + } + } + + public void postTweetCache(cgeoapplication app, cgSettings settings, String geocode) { + final cgCache cache = app.getCacheByGeocode(geocode); + String name = cache.name; + if (name.length() > 84) { + name = name.substring(0, 81) + "..."; + } + final String status = "I found " + name + " (http://coord.info/" + cache.geocode.toUpperCase() + ")! #cgeo #geocaching"; // 56 chars + cache name + + postTweet(app, settings, status, null, null); + } + + public void postTweetTrackable(cgeoapplication app, cgSettings settings, String geocode) { + final cgTrackable trackable = app.getTrackableByGeocode(geocode); + String name = trackable.name; + if (name.length() > 82) { + name = name.substring(0, 79) + "..."; + } + final String status = "I touched " + name + " (http://coord.info/" + trackable.geocode.toUpperCase() + ")! #cgeo #geocaching"; // 58 chars + trackable name + + postTweet(app, settings, status, null, null); + } + + public void postTweet(cgeoapplication app, cgSettings settings, String status, Double latitude, Double longitude) { + if (app == null) { + return; + } + if (settings == null || settings.tokenPublic == null || settings.tokenPublic.length() == 0 || settings.tokenSecret == null || settings.tokenSecret.length() == 0) { + return; + } + + try { + HashMap<String, String> parameters = new HashMap<String, String>(); + + parameters.put("status", status); + if (latitude != null && longitude != null) { + parameters.put("lat", String.format("%.6f", latitude)); + parameters.put("long", String.format("%.6f", longitude)); + parameters.put("display_coordinates", "true"); + } + + final String paramsDone = cgOAuth.signOAuth("api.twitter.com", "/1/statuses/update.json", "POST", false, parameters, settings.tokenPublic, settings.tokenSecret); + + HttpURLConnection connection = null; + try { + final StringBuffer buffer = new StringBuffer(); + final URL u = new URL("http://api.twitter.com/1/statuses/update.json"); + final URLConnection uc = u.openConnection(); + + uc.setRequestProperty("Host", "api.twitter.com"); + + connection = (HttpURLConnection) uc; + connection.setReadTimeout(30000); + connection.setRequestMethod("POST"); + HttpURLConnection.setFollowRedirects(true); + connection.setDoInput(true); + connection.setDoOutput(true); + + final OutputStream out = connection.getOutputStream(); + final OutputStreamWriter wr = new OutputStreamWriter(out); + wr.write(paramsDone); + wr.flush(); + wr.close(); + + Log.i(cgSettings.tag, "Twitter.com: " + connection.getResponseCode() + " " + connection.getResponseMessage()); + + InputStream ins; + final String encoding = connection.getContentEncoding(); + + if (encoding != null && encoding.equalsIgnoreCase("gzip")) { + ins = new GZIPInputStream(connection.getInputStream()); + } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) { + ins = new InflaterInputStream(connection.getInputStream(), new Inflater(true)); + } else { + ins = connection.getInputStream(); + } + + final InputStreamReader inr = new InputStreamReader(ins); + final BufferedReader br = new BufferedReader(inr); + + readIntoBuffer(br, buffer); + + br.close(); + ins.close(); + inr.close(); + connection.disconnect(); + } catch (IOException e) { + Log.e(cgSettings.tag, "cgBase.postTweet.IO: " + connection.getResponseCode() + ": " + connection.getResponseMessage() + " ~ " + e.toString()); + + final InputStream ins = connection.getErrorStream(); + final StringBuffer buffer = new StringBuffer(); + final InputStreamReader inr = new InputStreamReader(ins); + final BufferedReader br = new BufferedReader(inr); + + readIntoBuffer(br, buffer); + + br.close(); + ins.close(); + inr.close(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.postTweet.inner: " + e.toString()); + } + + connection.disconnect(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.postTweet: " + e.toString()); + } + } + + private void readIntoBuffer(BufferedReader br, StringBuffer buffer) throws IOException { + int bufferSize = 1024*16; + char[] bytes = new char[bufferSize]; + int bytesRead; + while ((bytesRead = br.read(bytes)) > 0) { + if (bytesRead == bufferSize) { + buffer.append(bytes); + } + else { + buffer.append(bytes, 0, bytesRead); + } + } + } + + /* + public ArrayList<String> translate(ArrayList<String> text, String target) { + if (settings.translate == false) { + return text; + } + + String[] languages = null; + if (settings.languages != null) { + languages = settings.languages.split(" "); + } + + ArrayList<String> translated = new ArrayList<String>(); + String language = null; + + if (text == null || text.isEmpty()) { + return text; + } + + // cut to 5000 characters (limitation of Google Translation API) + for (String textOne : text) { + int len = urlencode_rfc3986(textOne).length(); + if (len > 5000) { + textOne = Html.fromHtml(textOne).toString(); + len = urlencode_rfc3986(textOne).length(); + + if (len > 5000) { + int cut = 2000; + if (textOne.length() > cut) { + cut = 1000; + } + + textOne = textOne.substring(0, cut) + "..."; + } + } + } + + try { + if (target == null) { + final Locale locale = Locale.getDefault(); + target = locale.getLanguage(); + } + + final String scheme = "https://"; + final String host = "www.googleapis.com"; + final String path = "/language/translate/v2"; + + final ArrayList<String> params = new ArrayList<String>(); + params.add("key=" + urlencode_rfc3986("AIzaSyAJH8x5etFHUbFifmgChlWoCVmwBFSwShQ")); + params.add("target=" + urlencode_rfc3986(target)); + for (String textOne : text) { + params.add("q=" + urlencode_rfc3986(textOne)); + } + params.add("format=" + urlencode_rfc3986("html")); + + String page = requestJSON(scheme, host, path, "POST", implode("&", params.toArray())); + + if (page == null || page.length() == 0) { + return text; + } + + JSONObject json = new JSONObject(page); + JSONObject jsonData = json.getJSONObject("data"); + JSONArray jsonTranslations = jsonData.getJSONArray("translations"); + int translationCnt = jsonTranslations.length(); + + for (int i = 0; i < translationCnt; i ++) { + JSONObject jsonTranslation = jsonTranslations.getJSONObject(i); + language = jsonTranslation.getString("detectedSourceLanguage"); + + boolean toTranslate = true; + if (languages != null) { + for (String lng : languages) { + if (lng.equalsIgnoreCase(language)) { + toTranslate = false; + } + } + } + + if (toTranslate == false) { + translated.add(text.get(i)); + } else { + Log.i(cgSettings.tag, "Translating #" + i + ": " + language + ">" + target); + translated.add(jsonTranslation.getString("translatedText")); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.translate: " + e.toString()); + } + + return translated; + } + */ + + public 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().toString(); + } + } + } + } catch (SocketException e) { + // nothing + } + + return null; + } + + public static String implode(String delim, Object[] array) { + String out = ""; + + try { + for (int i = 0; i < array.length; i++) { + if (i != 0) { + out += delim; + } + out += array[i].toString(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.implode: " + e.toString()); + } + return out; + } + + public static String urlencode_rfc3986(String text) { + final String encoded = URLEncoder.encode(text).replace("+", "%20").replaceAll("%7E", "~"); + + return encoded; + } + + public String prepareParameters(HashMap<String, String> params, boolean my, boolean addF) { + String paramsDone = null; + + if (my != true && settings.excludeMine > 0) { + if (params == null) { + params = new HashMap<String, String>(); + } + if (addF == true) { + params.put("f", "1"); + } + + Log.i(cgSettings.tag, "Skipping caches found or hidden by user."); + } + + if (params != null) { + Object[] keys = params.keySet().toArray(); + ArrayList<String> paramsEncoded = new ArrayList<String>(); + String key; + String value; + + for (int i = 0; i < keys.length; i++) { + key = (String) keys[i]; + value = (String) params.get(key); + + if (key.charAt(0) == '^') { + key = ""; + } + if (value == null) { + value = ""; + } + + paramsEncoded.add(key + "=" + urlencode_rfc3986(value)); + } + + paramsDone = implode("&", paramsEncoded.toArray()); + } else { + paramsDone = ""; + } + + return paramsDone; + } + + public String requestViewstate(boolean secure, String host, String path, String method, HashMap<String, String> params, boolean xContentType, boolean my) { + final cgResponse response = request(secure, host, path, method, params, xContentType, my, false); + + return findViewstate(response.getData(), 0); + } + + public String requestViewstate1(boolean secure, String host, String path, String method, HashMap<String, String> params, boolean xContentType, boolean my) { + final cgResponse response = request(secure, host, path, method, params, xContentType, my, false); + + return findViewstate(response.getData(), 1); + } + + public String requestLogged(boolean secure, String host, String path, String method, HashMap<String, String> params, boolean xContentType, boolean my, boolean addF) { + cgResponse response = request(secure, host, path, method, params, xContentType, my, addF); + String data = response.getData(); + + if (checkLogin(data) == false) { + int loginState = login(); + if (loginState == 1) { + response = request(secure, host, path, method, params, xContentType, my, addF); + data = response.getData(); + } else { + Log.i(cgSettings.tag, "Working as guest."); + } + } + + return data; + } + + public cgResponse request(boolean secure, String host, String path, String method, HashMap<String, String> params, boolean xContentType, boolean my, boolean addF) { + // prepare parameters + final String paramsDone = prepareParameters(params, my, addF); + + return request(secure, host, path, method, paramsDone, 0, xContentType); + } + + public cgResponse request(boolean secure, String host, String path, String method, HashMap<String, String> params, int requestId, boolean xContentType, boolean my, boolean addF) { + // prepare parameters + final String paramsDone = prepareParameters(params, my, addF); + + return request(secure, host, path, method, paramsDone, requestId, xContentType); + } + + public cgResponse request(boolean secure, String host, String path, String method, String params, int requestId, Boolean xContentType) { + URL u = null; + int httpCode = -1; + String httpMessage = null; + String httpLocation = null; + + if (requestId == 0) { + requestId = (int) (Math.random() * 1000); + } + + if (method == null || (method.equalsIgnoreCase("GET") == false && method.equalsIgnoreCase("POST") == false)) { + method = "POST"; + } else { + method = method.toUpperCase(); + } + + // https + String scheme = "http://"; + if (secure) { + scheme = "https://"; + } + + // prepare cookies + String cookiesDone = null; + if (cookies == null || cookies.isEmpty() == true) { + if (cookies == null) { + cookies = new HashMap<String, String>(); + } + + final Map<String, ?> prefsAll = prefs.getAll(); + final Set<String> prefsKeys = prefsAll.keySet(); + + for (String key : prefsKeys) { + if (key.matches("cookie_.+") == true) { + final String cookieKey = key.substring(7); + final String cookieValue = (String) prefsAll.get(key); + + cookies.put(cookieKey, cookieValue); + } + } + } + + if (cookies != null && !cookies.isEmpty() && cookies.keySet().size() > 0) { + final Object[] keys = cookies.keySet().toArray(); + final ArrayList<String> cookiesEncoded = new ArrayList<String>(); + + for (int i = 0; i < keys.length; i++) { + String value = cookies.get(keys[i].toString()); + cookiesEncoded.add(keys[i] + "=" + value); + } + + if (cookiesEncoded.size() > 0) { + cookiesDone = implode("; ", cookiesEncoded.toArray()); + } + } + + if (cookiesDone == null) { + Map<String, ?> prefsValues = prefs.getAll(); + + if (prefsValues != null && prefsValues.size() > 0 && prefsValues.keySet().size() > 0) { + final Object[] keys = prefsValues.keySet().toArray(); + final ArrayList<String> cookiesEncoded = new ArrayList<String>(); + final int length = keys.length; + + for (int i = 0; i < length; i++) { + if (keys[i].toString().length() > 7 && keys[i].toString().substring(0, 7).equals("cookie_") == true) { + cookiesEncoded.add(keys[i].toString().substring(7) + "=" + prefsValues.get(keys[i].toString())); + } + } + + if (cookiesEncoded.size() > 0) { + cookiesDone = implode("; ", cookiesEncoded.toArray()); + } + } + } + + if (cookiesDone == null) { + cookiesDone = ""; + } + + URLConnection uc = null; + HttpURLConnection connection = null; + Integer timeout = 30000; + StringBuffer buffer = null; + + for (int i = 0; i < 5; i++) { + if (i > 0) { + Log.w(cgSettings.tag, "Failed to download data, retrying. Attempt #" + (i + 1)); + } + + buffer = new StringBuffer(); + timeout = 30000 + (i * 10000); + + try { + if (method.equals("GET")) { + // GET + u = new URL(scheme + host + path + "?" + params); + uc = u.openConnection(); + + uc.setRequestProperty("Host", host); + uc.setRequestProperty("Cookie", cookiesDone); + if (xContentType == true) { + uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + } + + if (settings.asBrowser == 1) { + uc.setRequestProperty("Accept", "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); + // uc.setRequestProperty("Accept-Encoding", "gzip"); // not supported via cellular network + uc.setRequestProperty("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); + uc.setRequestProperty("Accept-Language", "en-US"); + uc.setRequestProperty("User-Agent", idBrowser); + uc.setRequestProperty("Connection", "keep-alive"); + uc.setRequestProperty("Keep-Alive", "300"); + } + + connection = (HttpURLConnection) uc; + connection.setReadTimeout(timeout); + connection.setRequestMethod(method); + HttpURLConnection.setFollowRedirects(false); + connection.setDoInput(true); + connection.setDoOutput(false); + } else { + // POST + u = new URL(scheme + host + path); + uc = u.openConnection(); + + uc.setRequestProperty("Host", host); + uc.setRequestProperty("Cookie", cookiesDone); + if (xContentType == true) { + uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + } + + if (settings.asBrowser == 1) { + uc.setRequestProperty("Accept", "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); + // uc.setRequestProperty("Accept-Encoding", "gzip"); // not supported via cellular network + uc.setRequestProperty("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); + uc.setRequestProperty("Accept-Language", "en-US"); + uc.setRequestProperty("User-Agent", idBrowser); + uc.setRequestProperty("Connection", "keep-alive"); + uc.setRequestProperty("Keep-Alive", "300"); + } + + connection = (HttpURLConnection) uc; + connection.setReadTimeout(timeout); + connection.setRequestMethod(method); + HttpURLConnection.setFollowRedirects(false); + connection.setDoInput(true); + connection.setDoOutput(true); + + final OutputStream out = connection.getOutputStream(); + final OutputStreamWriter wr = new OutputStreamWriter(out); + wr.write(params); + wr.flush(); + wr.close(); + } + + String headerName = null; + final SharedPreferences.Editor prefsEditor = prefs.edit(); + for (int j = 1; (headerName = uc.getHeaderFieldKey(j)) != null; j++) { + if (headerName != null && headerName.equalsIgnoreCase("Set-Cookie")) { + int index; + String cookie = uc.getHeaderField(j); + + index = cookie.indexOf(";"); + if (index > -1) { + cookie = cookie.substring(0, cookie.indexOf(";")); + } + + index = cookie.indexOf("="); + if (index > - 1 && cookie.length() > (index + 1)) { + String name = cookie.substring(0, cookie.indexOf("=")); + String value = cookie.substring(cookie.indexOf("=") + 1, cookie.length()); + + cookies.put(name, value); + prefsEditor.putString("cookie_" + name, value); + } + } + } + prefsEditor.commit(); + + final String encoding = connection.getContentEncoding(); + InputStream ins; + + if (encoding != null && encoding.equalsIgnoreCase("gzip")) { + ins = new GZIPInputStream(connection.getInputStream()); + } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) { + ins = new InflaterInputStream(connection.getInputStream(), new Inflater(true)); + } else { + ins = connection.getInputStream(); + } + final InputStreamReader inr = new InputStreamReader(ins); + final BufferedReader br = new BufferedReader(inr); + + readIntoBuffer(br, buffer); + + httpCode = connection.getResponseCode(); + httpMessage = connection.getResponseMessage(); + httpLocation = uc.getHeaderField("Location"); + + final String paramsLog = params.replaceAll(passMatch, "password=***"); + if (buffer != null && connection != null) { + Log.i(cgSettings.tag + "|" + requestId, "[" + method + " " + (int)(params.length() / 1024) + "k | " + httpCode + " | " + (int)(buffer.length() / 1024) + "k] Downloaded " + scheme + host + path + "?" + paramsLog); + } else { + Log.i(cgSettings.tag + "|" + requestId, "[" + method + " | " + httpCode + "] Failed to download " + scheme + host + path + "?" + paramsLog); + } + + connection.disconnect(); + br.close(); + ins.close(); + inr.close(); + } catch (IOException e) { + Log.e(cgSettings.tag, "cgeoBase.request.IOException: " + e.toString()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.request: " + e.toString()); + } + + if (buffer != null && buffer.length() > 0) { + break; + } + } + + cgResponse response = new cgResponse(); + String data = null; + + try { + if (httpCode == 302 && httpLocation != null) { + final Uri newLocation = Uri.parse(httpLocation); + if (newLocation.isRelative() == true) { + response = request(secure, host, path, "GET", new HashMap<String, String>(), requestId, false, false, false); + } else { + boolean secureRedir = false; + if (newLocation.getScheme().equals("https")) { + secureRedir = true; + } + response = request(secureRedir, newLocation.getHost(), newLocation.getPath(), "GET", new HashMap<String, String>(), requestId, false, false, false); + } + } else { + if (buffer != null && buffer.length() > 0) { + data = replaceWhitespace(buffer); + buffer = null; + + if (data != null) { + response.setData(data); + } else { + response.setData(""); + } + response.setStatusCode(httpCode); + response.setStatusMessage(httpMessage); + response.setUrl(u.toString()); + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.page: " + e.toString()); + } + + return response; + } + + private String replaceWhitespace(final StringBuffer buffer) { + final int length = buffer.length(); + final char[] bytes = new char[length]; + buffer.getChars(0, length, bytes, 0); + int resultSize = 0; + boolean lastWasWhitespace = false; + for (int i = 0; i < length; i++) { + char c = bytes[i]; + if (c == ' ' || c == '\n' || c == '\r' || c == '\t') { + if (!lastWasWhitespace) { + bytes[resultSize++] =' '; + } + lastWasWhitespace = true; + } else { + bytes[resultSize++] = c; + lastWasWhitespace = false; + } + } + + return new String(bytes, 0, resultSize); + } + + public String requestJSONgc(String host, String path, String params) { + int httpCode = -1; + String httpLocation = null; + + // prepare cookies + String cookiesDone = null; + if (cookies == null || cookies.isEmpty() == true) { + if (cookies == null) { + cookies = new HashMap<String, String>(); + } + + final Map<String, ?> prefsAll = prefs.getAll(); + final Set<String> prefsKeys = prefsAll.keySet(); + + for (String key : prefsKeys) { + if (key.matches("cookie_.+") == true) { + final String cookieKey = key.substring(7); + final String cookieValue = (String) prefsAll.get(key); + + cookies.put(cookieKey, cookieValue); + } + } + } + + if (cookies != null) { + final Object[] keys = cookies.keySet().toArray(); + final ArrayList<String> cookiesEncoded = new ArrayList<String>(); + + for (int i = 0; i < keys.length; i++) { + String value = cookies.get(keys[i].toString()); + cookiesEncoded.add(keys[i] + "=" + value); + } + + if (cookiesEncoded.size() > 0) { + cookiesDone = implode("; ", cookiesEncoded.toArray()); + } + } + + if (cookiesDone == null) { + Map<String, ?> prefsValues = prefs.getAll(); + + if (prefsValues != null && prefsValues.size() > 0) { + final Object[] keys = prefsValues.keySet().toArray(); + final ArrayList<String> cookiesEncoded = new ArrayList<String>(); + final int length = keys.length; + + for (int i = 0; i < length; i++) { + if (keys[i].toString().length() > 7 && keys[i].toString().substring(0, 7).equals("cookie_") == true) { + cookiesEncoded.add(keys[i].toString().substring(7) + "=" + prefsValues.get(keys[i].toString())); + } + } + + if (cookiesEncoded.size() > 0) { + cookiesDone = implode("; ", cookiesEncoded.toArray()); + } + } + } + + if (cookiesDone == null) { + cookiesDone = ""; + } + + URLConnection uc = null; + HttpURLConnection connection = null; + Integer timeout = 30000; + final StringBuffer buffer = new StringBuffer(); + + for (int i = 0; i < 3; i++) { + if (i > 0) { + Log.w(cgSettings.tag, "Failed to download data, retrying. Attempt #" + (i + 1)); + } + + buffer.delete(0, buffer.length()); + timeout = 30000 + (i * 15000); + + try { + // POST + final URL u = new URL("http://" + host + path); + uc = u.openConnection(); + + uc.setRequestProperty("Host", host); + uc.setRequestProperty("Cookie", cookiesDone); + uc.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + uc.setRequestProperty("X-Requested-With", "XMLHttpRequest"); + uc.setRequestProperty("Accept", "application/json, text/javascript, */*; q=0.01"); + uc.setRequestProperty("Referer", host + "/" + path); + + if (settings.asBrowser == 1) { + uc.setRequestProperty("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); + uc.setRequestProperty("Accept-Language", "en-US"); + uc.setRequestProperty("User-Agent", idBrowser); + uc.setRequestProperty("Connection", "keep-alive"); + uc.setRequestProperty("Keep-Alive", "300"); + } + + connection = (HttpURLConnection) uc; + connection.setReadTimeout(timeout); + connection.setRequestMethod("POST"); + HttpURLConnection.setFollowRedirects(false); // TODO: Fix these (FilCab) + connection.setDoInput(true); + connection.setDoOutput(true); + + final OutputStream out = connection.getOutputStream(); + final OutputStreamWriter wr = new OutputStreamWriter(out); + wr.write(params); + wr.flush(); + wr.close(); + + String headerName = null; + final SharedPreferences.Editor prefsEditor = prefs.edit(); + for (int j = 1; (headerName = uc.getHeaderFieldKey(j)) != null; j++) { + if (headerName != null && headerName.equalsIgnoreCase("Set-Cookie")) { + int index; + String cookie = uc.getHeaderField(j); + + index = cookie.indexOf(";"); + if (index > -1) { + cookie = cookie.substring(0, cookie.indexOf(";")); + } + + index = cookie.indexOf("="); + if (index > - 1 && cookie.length() > (index + 1)) { + String name = cookie.substring(0, cookie.indexOf("=")); + String value = cookie.substring(cookie.indexOf("=") + 1, cookie.length()); + + cookies.put(name, value); + prefsEditor.putString("cookie_" + name, value); + } + } + } + prefsEditor.commit(); + + final String encoding = connection.getContentEncoding(); + InputStream ins; + + if (encoding != null && encoding.equalsIgnoreCase("gzip")) { + ins = new GZIPInputStream(connection.getInputStream()); + } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) { + ins = new InflaterInputStream(connection.getInputStream(), new Inflater(true)); + } else { + ins = connection.getInputStream(); + } + final InputStreamReader inr = new InputStreamReader(ins); + final BufferedReader br = new BufferedReader(inr); + + readIntoBuffer(br, buffer); + + httpCode = connection.getResponseCode(); + httpLocation = uc.getHeaderField("Location"); + + final String paramsLog = params.replaceAll(passMatch, "password=***"); + Log.i(cgSettings.tag + " | JSON", "[POST " + (int)(params.length() / 1024) + "k | " + httpCode + " | " + (int)(buffer.length() / 1024) + "k] Downloaded " + "http://" + host + path + "?" + paramsLog); + + connection.disconnect(); + br.close(); + ins.close(); + inr.close(); + } catch (IOException e) { + Log.e(cgSettings.tag, "cgeoBase.requestJSONgc.IOException: " + e.toString()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.requestJSONgc: " + e.toString()); + } + + if (buffer != null && buffer.length() > 0) { + break; + } + } + + String page = null; + if (httpCode == 302 && httpLocation != null) { + final Uri newLocation = Uri.parse(httpLocation); + if (newLocation.isRelative() == true) { + page = requestJSONgc(host, path, params); + } else { + page = requestJSONgc(newLocation.getHost(), newLocation.getPath(), params); + } + } else { + page = replaceWhitespace(buffer); + } + + if (page != null) { + return page; + } else { + return ""; + } + } + + public String requestJSON(String host, String path, String params) { + return requestJSON("http://", host, path, "GET", params); + } + + public String requestJSON(String scheme, String host, String path, String method, String params) { + int httpCode = -1; + String httpLocation = null; + + if (method == null) { + method = "GET"; + } else { + method = method.toUpperCase(); + } + + boolean methodPost = false; + if (method.equalsIgnoreCase("POST")) { + methodPost = true; + } + + URLConnection uc = null; + HttpURLConnection connection = null; + Integer timeout = 30000; + final StringBuffer buffer = new StringBuffer(); + + for (int i = 0; i < 3; i++) { + if (i > 0) { + Log.w(cgSettings.tag, "Failed to download data, retrying. Attempt #" + (i + 1)); + } + + buffer.delete(0, buffer.length()); + timeout = 30000 + (i * 15000); + + try { + try { + URL u = null; + if (methodPost) { + u = new URL(scheme + host + path); + } else { + u = new URL(scheme + host + path + "?" + params); + } + + if (u.getProtocol().toLowerCase().equals("https")) { + trustAllHosts(); + HttpsURLConnection https = (HttpsURLConnection) u.openConnection(); + https.setHostnameVerifier(doNotVerify); + uc = https; + } else { + uc = (HttpURLConnection) u.openConnection(); + } + + uc.setRequestProperty("Host", host); + uc.setRequestProperty("Accept", "application/json, text/javascript, */*; q=0.01"); + if (methodPost) { + uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + uc.setRequestProperty("Content-Length", Integer.toString(params.length())); + uc.setRequestProperty("X-HTTP-Method-Override", "GET"); + } else { + uc.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + } + uc.setRequestProperty("X-Requested-With", "XMLHttpRequest"); + + connection = (HttpURLConnection) uc; + connection.setReadTimeout(timeout); + connection.setRequestMethod(method); + HttpURLConnection.setFollowRedirects(false); // TODO: Fix these (FilCab) + connection.setDoInput(true); + if (methodPost) { + connection.setDoOutput(true); + + final OutputStream out = connection.getOutputStream(); + final OutputStreamWriter wr = new OutputStreamWriter(out); + wr.write(params); + wr.flush(); + wr.close(); + } else { + connection.setDoOutput(false); + } + + + final String encoding = connection.getContentEncoding(); + InputStream ins; + + if (encoding != null && encoding.equalsIgnoreCase("gzip")) { + ins = new GZIPInputStream(connection.getInputStream()); + } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) { + ins = new InflaterInputStream(connection.getInputStream(), new Inflater(true)); + } else { + ins = connection.getInputStream(); + } + final InputStreamReader inr = new InputStreamReader(ins); + final BufferedReader br = new BufferedReader(inr); + + readIntoBuffer(br, buffer); + + httpCode = connection.getResponseCode(); + + final String paramsLog = params.replaceAll(passMatch, "password=***"); + Log.i(cgSettings.tag + " | JSON", "[POST " + (int)(params.length() / 1024) + "k | " + httpCode + " | " + (int)(buffer.length() / 1024) + "k] Downloaded " + "http://" + host + path + "?" + paramsLog); + + connection.disconnect(); + br.close(); + ins.close(); + inr.close(); + } catch (IOException e) { + httpCode = connection.getResponseCode(); + + Log.e(cgSettings.tag, "cgeoBase.requestJSON.IOException: " + httpCode + ": " + connection.getResponseMessage() + " ~ " + e.toString()); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoBase.requestJSON: " + e.toString()); + } + + if (buffer != null && buffer.length() > 0) { + break; + } + + if (httpCode == 403) { + // we're not allowed to download content, so let's move + break; + } + } + + String page = null; + if (httpCode == 302 && httpLocation != null) { + final Uri newLocation = Uri.parse(httpLocation); + if (newLocation.isRelative() == true) { + page = requestJSONgc(host, path, params); + } else { + page = requestJSONgc(newLocation.getHost(), newLocation.getPath(), params); + } + } else { + page = replaceWhitespace(buffer); + } + + if (page != null) { + return page; + } else { + return ""; + } + } + + public static String rot13(String text) { + final StringBuilder result = new StringBuilder(); + // plaintext flag (do not convert) + boolean plaintext = false; + + int length = text.length(); + for (int index = 0; index < length; index++) { + int c = text.charAt(index); + if (c == '[') { + plaintext = true; + } else if (c == ']') { + plaintext = false; + } else if (!plaintext) { + int capitalized = c & 32; + c &= ~capitalized; + c = ((c >= 'A') && (c <= 'Z') ? ((c - 'A' + 13) % 26 + 'A') : c) + | capitalized; + } + result.append((char) c); + } + return result.toString(); + } + + public static String md5(String text) { + String hashed = ""; + + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + digest.update(text.getBytes(), 0, text.length()); + hashed = new BigInteger(1, digest.digest()).toString(16); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.md5: " + e.toString()); + } + + return hashed; + } + + public static String sha1(String text) { + String hashed = ""; + + try { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + digest.update(text.getBytes(), 0, text.length()); + hashed = new BigInteger(1, digest.digest()).toString(16); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.sha1: " + e.toString()); + } + + return hashed; + } + + public static byte[] hashHmac(String text, String salt) { + byte[] macBytes = {}; + + try { + SecretKeySpec secretKeySpec = new SecretKeySpec(salt.getBytes(), "HmacSHA1"); + Mac mac = Mac.getInstance("HmacSHA1"); + mac.init(secretKeySpec); + macBytes = mac.doFinal(text.getBytes()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.hashHmac: " + e.toString()); + } + + return macBytes; + } + + public static boolean deleteDirectory(File path) { + if (path.exists()) { + File[] files = path.listFiles(); + + for (int i = 0; i < files.length; i++) { + if (files[i].isDirectory()) { + deleteDirectory(files[i]); + } else { + files[i].delete(); + } + } + } + + return (path.delete()); + } + + public static boolean isIntentAvailable(Context context, String action) { + final Intent intent = new Intent(action); + + return isIntentAvailable(context, intent); + } + + public static boolean isIntentAvailable(Context context, Intent intent) { + final PackageManager packageManager = context.getPackageManager(); + final List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); + + return (list.size() > 0); + } + + public void storeCache(cgeoapplication app, Activity activity, cgCache cache, String geocode, int listId, Handler handler) { + try { + // cache details + if (cache != null) { + final HashMap<String, String> params = new HashMap<String, String>(); + params.put("geocode", cache.geocode); + final Long searchId = searchByGeocode(params, listId, false); + cache = app.getCache(searchId); + } else if (geocode != null) { + final HashMap<String, String> params = new HashMap<String, String>(); + params.put("geocode", geocode); + final Long searchId = searchByGeocode(params, listId, false); + cache = app.getCache(searchId); + } + + if (cache == null) { + if (handler != null) { + handler.sendMessage(new Message()); + } + + return; + } + + final cgHtmlImg imgGetter = new cgHtmlImg(activity, settings, cache.geocode, false, listId, true); + + // store images from description + if (cache.description != null) { + Html.fromHtml(cache.description, imgGetter, null); + } + + // store spoilers + if (cache.spoilers != null && cache.spoilers.isEmpty() == false) { + for (cgSpoiler oneSpoiler : cache.spoilers) { + imgGetter.getDrawable(oneSpoiler.url); + } + } + + // store map previews + if (settings.storeOfflineMaps == 1 && cache.latitude != null && cache.longitude != null) { + final String latlonMap = String.format((Locale) null, "%.6f", cache.latitude) + "," + String.format((Locale) null, "%.6f", cache.longitude); + final Display display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + final int maxWidth = display.getWidth() - 25; + final int maxHeight = display.getHeight() - 25; + int edge = 0; + if (maxWidth > maxHeight) { + edge = maxWidth; + } else { + edge = maxHeight; + } + + String type = "mystery"; + if (cache.found == true) { + type = cache.type + "_found"; + } else if (cache.disabled == true) { + type = cache.type + "_disabled"; + } else { + type = cache.type; + } + + final String markerUrl = urlencode_rfc3986("http://cgeo.carnero.cc/_markers/marker_cache_" + type + ".png"); + final StringBuilder waypoints = new StringBuilder(); + if (cache.waypoints != null && cache.waypoints.size() > 0) { + for (cgWaypoint waypoint : cache.waypoints) { + if (waypoint.latitude == null && waypoint.longitude == null) { + continue; + } + + waypoints.append("&markers=icon%3Ahttp://cgeo.carnero.cc/_markers/marker_waypoint_"); + waypoints.append(waypoint.type); + waypoints.append(".png%7C"); + waypoints.append(String.format((Locale) null, "%.6f", waypoint.latitude)); + waypoints.append(","); + waypoints.append(String.format((Locale) null, "%.6f", waypoint.longitude)); + } + } + + // download map images in separate background thread for higher performance + final String code = cache.geocode; + final int finalEdge = edge; + Thread staticMapsThread = new Thread("getting static map") {@Override + public void run() { + cgMapImg mapGetter = new cgMapImg(settings, code); + + mapGetter.getDrawable("http://maps.google.com/maps/api/staticmap?center=" + latlonMap + "&zoom=20&size=" + finalEdge + "x" + finalEdge + "&maptype=satellite&markers=icon%3A" + markerUrl + "%7C" + latlonMap + waypoints.toString() + "&sensor=false", 1); + mapGetter.getDrawable("http://maps.google.com/maps/api/staticmap?center=" + latlonMap + "&zoom=18&size=" + finalEdge + "x" + finalEdge + "&maptype=satellite&markers=icon%3A" + markerUrl + "%7C" + latlonMap + waypoints.toString() + "&sensor=false", 2); + mapGetter.getDrawable("http://maps.google.com/maps/api/staticmap?center=" + latlonMap + "&zoom=16&size=" + finalEdge + "x" + finalEdge + "&maptype=roadmap&markers=icon%3A" + markerUrl + "%7C" + latlonMap + waypoints.toString() + "&sensor=false", 3); + mapGetter.getDrawable("http://maps.google.com/maps/api/staticmap?center=" + latlonMap + "&zoom=14&size=" + finalEdge + "x" + finalEdge + "&maptype=roadmap&markers=icon%3A" + markerUrl + "%7C" + latlonMap + waypoints.toString() + "&sensor=false", 4); + mapGetter.getDrawable("http://maps.google.com/maps/api/staticmap?center=" + latlonMap + "&zoom=11&size=" + finalEdge + "x" + finalEdge + "&maptype=roadmap&markers=icon%3A" + markerUrl + "%7C" + latlonMap + waypoints.toString() + "&sensor=false", 5); + }}; + staticMapsThread.setPriority(Thread.MIN_PRIORITY); + staticMapsThread.start(); + } + + app.markStored(cache.geocode, listId); + app.removeCacheFromCache(cache.geocode); + + if (handler != null) { + handler.sendMessage(new Message()); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.storeCache: " + e.toString()); + } + } + + public void dropCache(cgeoapplication app, Activity activity, cgCache cache, Handler handler) { + try { + app.markDropped(cache.geocode); + app.removeCacheFromCache(cache.geocode); + + handler.sendMessage(new Message()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.dropCache: " + e.toString()); + } + } + + public boolean isInViewPort(int centerLat1, int centerLon1, int centerLat2, int centerLon2, int spanLat1, int spanLon1, int spanLat2, int spanLon2) { + try { + // expects coordinates in E6 format + final int left1 = centerLat1 - (spanLat1 / 2); + final int right1 = centerLat1 + (spanLat1 / 2); + final int top1 = centerLon1 + (spanLon1 / 2); + final int bottom1 = centerLon1 - (spanLon1 / 2); + + final int left2 = centerLat2 - (spanLat2 / 2); + final int right2 = centerLat2 + (spanLat2 / 2); + final int top2 = centerLon2 + (spanLon2 / 2); + final int bottom2 = centerLon2 - (spanLon2 / 2); + + if (left2 <= left1) { + return false; + } + if (right2 >= right1) { + return false; + } + if (top2 >= top1) { + return false; + } + if (bottom2 <= bottom1) { + return false; + } + + return true; + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.isInViewPort: " + e.toString()); + return false; + } + } + + public boolean isCacheInViewPort(int centerLat, int centerLon, int spanLat, int spanLon, Double cacheLat, Double cacheLon) { + if (cacheLat == null || cacheLon == null) { + return false; + } + + // viewport is defined by center, span and some (10%) reserve on every side + int minLat = centerLat - (spanLat / 2) - (spanLat / 10); + int maxLat = centerLat + (spanLat / 2) + (spanLat / 10); + int minLon = centerLon - (spanLon / 2) - (spanLon / 10); + int maxLon = centerLon + (spanLon / 2) + (spanLon / 10); + int cLat = (int) Math.round(cacheLat * 1e6); + int cLon = (int) Math.round(cacheLon * 1e6); + int mid = 0; + + if (maxLat < minLat) { + mid = minLat; + minLat = maxLat; + maxLat = mid; + } + + if (maxLon < minLon) { + mid = minLon; + minLon = maxLon; + maxLon = mid; + } + + boolean latOk = false; + boolean lonOk = false; + + if (cLat >= minLat && cLat <= maxLat) { + latOk = true; + } + if (cLon >= minLon && cLon <= maxLon) { + lonOk = true; + } + + if (latOk == true && lonOk == true) { + return true; + } else { + return false; + } + } + private static char[] base64map1 = new char[64]; + + static { + int i = 0; + for (char c = 'A'; c <= 'Z'; c++) { + base64map1[i++] = c; + } + for (char c = 'a'; c <= 'z'; c++) { + base64map1[i++] = c; + } + for (char c = '0'; c <= '9'; c++) { + base64map1[i++] = c; + } + base64map1[i++] = '+'; + base64map1[i++] = '/'; + } + private static byte[] base64map2 = new byte[128]; + + static { + for (int i = 0; i < base64map2.length; i++) { + base64map2[i] = -1; + } + for (int i = 0; i < 64; i++) { + base64map2[base64map1[i]] = (byte) i; + } + } + + public static String base64Encode(byte[] in) { + int iLen = in.length; + int oDataLen = (iLen * 4 + 2) / 3; // output length without padding + int oLen = ((iLen + 2) / 3) * 4; // output length including padding + char[] out = new char[oLen]; + int ip = 0; + int op = 0; + + while (ip < iLen) { + int i0 = in[ip++] & 0xff; + int i1 = ip < iLen ? in[ip++] & 0xff : 0; + int i2 = ip < iLen ? in[ip++] & 0xff : 0; + int o0 = i0 >>> 2; + int o1 = ((i0 & 3) << 4) | (i1 >>> 4); + int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); + int o3 = i2 & 0x3F; + out[op++] = base64map1[o0]; + out[op++] = base64map1[o1]; + out[op] = op < oDataLen ? base64map1[o2] : '='; + op++; + out[op] = op < oDataLen ? base64map1[o3] : '='; + op++; + } + + return new String(out); + } + + public static byte[] base64Decode(String text) { + char[] in = text.toCharArray(); + + int iLen = in.length; + if (iLen % 4 != 0) { + throw new IllegalArgumentException("Length of Base64 encoded input string is not a multiple of 4."); + } + while (iLen > 0 && in[iLen - 1] == '=') { + iLen--; + } + int oLen = (iLen * 3) / 4; + byte[] out = new byte[oLen]; + int ip = 0; + int op = 0; + while (ip < iLen) { + int i0 = in[ip++]; + int i1 = in[ip++]; + int i2 = ip < iLen ? in[ip++] : 'A'; + int i3 = ip < iLen ? in[ip++] : 'A'; + if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) { + throw new IllegalArgumentException("Illegal character in Base64 encoded data."); + } + int b0 = base64map2[i0]; + int b1 = base64map2[i1]; + int b2 = base64map2[i2]; + int b3 = base64map2[i3]; + if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) { + throw new IllegalArgumentException("Illegal character in Base64 encoded data."); + } + int o0 = (b0 << 2) | (b1 >>> 4); + int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); + int o2 = ((b2 & 3) << 6) | b3; + out[op++] = (byte) o0; + if (op < oLen) { + out[op++] = (byte) o1; + } + if (op < oLen) { + out[op++] = (byte) o2; + } + } + return out; + } + + public int getIcon(boolean cache, String type, boolean own, boolean found, boolean disabled) { + if (gcIcons.isEmpty()) { + // default markers + gcIcons.put("ape", R.drawable.marker_cache_ape); + gcIcons.put("cito", R.drawable.marker_cache_cito); + gcIcons.put("earth", R.drawable.marker_cache_earth); + gcIcons.put("event", R.drawable.marker_cache_event); + gcIcons.put("letterbox", R.drawable.marker_cache_letterbox); + gcIcons.put("locationless", R.drawable.marker_cache_locationless); + gcIcons.put("mega", R.drawable.marker_cache_mega); + gcIcons.put("multi", R.drawable.marker_cache_multi); + gcIcons.put("traditional", R.drawable.marker_cache_traditional); + gcIcons.put("virtual", R.drawable.marker_cache_virtual); + gcIcons.put("webcam", R.drawable.marker_cache_webcam); + gcIcons.put("wherigo", R.drawable.marker_cache_wherigo); + gcIcons.put("mystery", R.drawable.marker_cache_mystery); + gcIcons.put("gchq", R.drawable.marker_cache_gchq); + // own cache markers + gcIcons.put("ape-own", R.drawable.marker_cache_ape_own); + gcIcons.put("cito-own", R.drawable.marker_cache_cito_own); + gcIcons.put("earth-own", R.drawable.marker_cache_earth_own); + gcIcons.put("event-own", R.drawable.marker_cache_event_own); + gcIcons.put("letterbox-own", R.drawable.marker_cache_letterbox_own); + gcIcons.put("locationless-own", R.drawable.marker_cache_locationless_own); + gcIcons.put("mega-own", R.drawable.marker_cache_mega_own); + gcIcons.put("multi-own", R.drawable.marker_cache_multi_own); + gcIcons.put("traditional-own", R.drawable.marker_cache_traditional_own); + gcIcons.put("virtual-own", R.drawable.marker_cache_virtual_own); + gcIcons.put("webcam-own", R.drawable.marker_cache_webcam_own); + gcIcons.put("wherigo-own", R.drawable.marker_cache_wherigo_own); + gcIcons.put("mystery-own", R.drawable.marker_cache_mystery_own); + gcIcons.put("gchq-own", R.drawable.marker_cache_gchq_own); + // found cache markers + gcIcons.put("ape-found", R.drawable.marker_cache_ape_found); + gcIcons.put("cito-found", R.drawable.marker_cache_cito_found); + gcIcons.put("earth-found", R.drawable.marker_cache_earth_found); + gcIcons.put("event-found", R.drawable.marker_cache_event_found); + gcIcons.put("letterbox-found", R.drawable.marker_cache_letterbox_found); + gcIcons.put("locationless-found", R.drawable.marker_cache_locationless_found); + gcIcons.put("mega-found", R.drawable.marker_cache_mega_found); + gcIcons.put("multi-found", R.drawable.marker_cache_multi_found); + gcIcons.put("traditional-found", R.drawable.marker_cache_traditional_found); + gcIcons.put("virtual-found", R.drawable.marker_cache_virtual_found); + gcIcons.put("webcam-found", R.drawable.marker_cache_webcam_found); + gcIcons.put("wherigo-found", R.drawable.marker_cache_wherigo_found); + gcIcons.put("mystery-found", R.drawable.marker_cache_mystery_found); + gcIcons.put("gchq-found", R.drawable.marker_cache_gchq_found); + // disabled cache markers + gcIcons.put("ape-disabled", R.drawable.marker_cache_ape_disabled); + gcIcons.put("cito-disabled", R.drawable.marker_cache_cito_disabled); + gcIcons.put("earth-disabled", R.drawable.marker_cache_earth_disabled); + gcIcons.put("event-disabled", R.drawable.marker_cache_event_disabled); + gcIcons.put("letterbox-disabled", R.drawable.marker_cache_letterbox_disabled); + gcIcons.put("locationless-disabled", R.drawable.marker_cache_locationless_disabled); + gcIcons.put("mega-disabled", R.drawable.marker_cache_mega_disabled); + gcIcons.put("multi-disabled", R.drawable.marker_cache_multi_disabled); + gcIcons.put("traditional-disabled", R.drawable.marker_cache_traditional_disabled); + gcIcons.put("virtual-disabled", R.drawable.marker_cache_virtual_disabled); + gcIcons.put("webcam-disabled", R.drawable.marker_cache_webcam_disabled); + gcIcons.put("wherigo-disabled", R.drawable.marker_cache_wherigo_disabled); + gcIcons.put("mystery-disabled", R.drawable.marker_cache_mystery_disabled); + gcIcons.put("gchq-disabled", R.drawable.marker_cache_gchq_disabled); + } + + if (wpIcons.isEmpty()) { + wpIcons.put("waypoint", R.drawable.marker_waypoint_waypoint); + wpIcons.put("flag", R.drawable.marker_waypoint_flag); + wpIcons.put("pkg", R.drawable.marker_waypoint_pkg); + wpIcons.put("puzzle", R.drawable.marker_waypoint_puzzle); + wpIcons.put("stage", R.drawable.marker_waypoint_stage); + wpIcons.put("trailhead", R.drawable.marker_waypoint_trailhead); + } + + int icon = -1; + String iconTxt = null; + + if (cache == true) { + if (type != null && type.length() > 0) { + if (own == true) { + iconTxt = type + "-own"; + } else if (found == true) { + iconTxt = type + "-found"; + } else if (disabled == true) { + iconTxt = type + "-disabled"; + } else { + iconTxt = type; + } + } else { + iconTxt = "traditional"; + } + + if (gcIcons.containsKey(iconTxt) == true) { + icon = gcIcons.get(iconTxt); + } else { + icon = gcIcons.get("traditional"); + } + } else { + if (type != null && type.length() > 0) { + iconTxt = type; + } else { + iconTxt = "waypoint"; + } + + if (wpIcons.containsKey(iconTxt) == true) { + icon = wpIcons.get(iconTxt); + } else { + icon = wpIcons.get("waypoint"); + } + } + + return icon; + } + + public boolean isLocus(Context context) { + boolean locus = false; + final Intent intentTest = new Intent(Intent.ACTION_VIEW); + intentTest.setData(Uri.parse("menion.points:x")); + if (isIntentAvailable(context, intentTest) == true) { + locus = true; + } + + return locus; + } + + public boolean isRmaps(Context context) { + boolean rmaps = false; + final Intent intent = new Intent("com.robert.maps.action.SHOW_POINTS"); + if (isIntentAvailable(context, intent) == true) { + rmaps = true; + } + + return rmaps; + } + + public boolean runExternalMap(int application, Activity activity, Resources res, cgWarning warning, GoogleAnalyticsTracker tracker, Double latitude, Double longitude) { + // waypoint + return runExternalMap(application, activity, res, warning, tracker, null, null, latitude, longitude); + } + + public boolean runExternalMap(int application, Activity activity, Resources res, cgWarning warning, GoogleAnalyticsTracker tracker, cgWaypoint waypoint) { + // waypoint + return runExternalMap(application, activity, res, warning, tracker, null, waypoint, null, null); + } + + public boolean runExternalMap(int application, Activity activity, Resources res, cgWarning warning, GoogleAnalyticsTracker tracker, cgCache cache) { + // cache + return runExternalMap(application, activity, res, warning, tracker, cache, null, null, null); + } + + public boolean runExternalMap(int application, Activity activity, Resources res, cgWarning warning, GoogleAnalyticsTracker tracker, cgCache cache, cgWaypoint waypoint, Double latitude, Double longitude) { + if (cache == null && waypoint == null && latitude == null && longitude == null) { + return false; + } + + if (application == mapAppLocus) { + // locus + try { + final Intent intentTest = new Intent(Intent.ACTION_VIEW); + intentTest.setData(Uri.parse("menion.points:x")); + + if (isIntentAvailable(activity, intentTest) == true) { + final ArrayList<cgWaypoint> waypoints = new ArrayList<cgWaypoint>(); + // get only waypoints with coordinates + if (cache != null && cache.waypoints != null && cache.waypoints.isEmpty() == false) { + for (cgWaypoint wp : cache.waypoints) { + if (wp.latitude != null && wp.longitude != null) { + waypoints.add(wp); + } + } + } + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final DataOutputStream dos = new DataOutputStream(baos); + + dos.writeInt(1); // not used + if (cache != null) { + if (waypoints == null || waypoints.isEmpty() == true) { + dos.writeInt(1); // cache only + } else { + dos.writeInt((1 + waypoints.size())); // cache and waypoints + } + } else { + dos.writeInt(1); // one waypoint + } + + int icon = -1; + if (cache != null) { + icon = getIcon(true, cache.type, cache.own, cache.found, cache.disabled || cache.archived); + } else if (waypoint != null) { + icon = getIcon(false, waypoint.type, false, false, false); + } else { + icon = getIcon(false, "waypoint", false, false, false); + } + + if (icon > 0) { + // load icon + Bitmap bitmap = BitmapFactory.decodeResource(res, icon); + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos2); + byte[] image = baos2.toByteArray(); + + dos.writeInt(image.length); + dos.write(image); + } else { + // no icon + dos.writeInt(0); // no image + } + + // name + if (cache != null && cache.name != null && cache.name.length() > 0) { + dos.writeUTF(cache.name); + } else if (waypoint != null && waypoint.name != null && waypoint.name.length() > 0) { + dos.writeUTF(waypoint.name); + } else { + dos.writeUTF(""); + } + + // description + if (cache != null && cache.geocode != null && cache.geocode.length() > 0) { + dos.writeUTF(cache.geocode.toUpperCase()); + } else if (waypoint != null && waypoint.lookup != null && waypoint.lookup.length() > 0) { + dos.writeUTF(waypoint.lookup.toUpperCase()); + } else { + dos.writeUTF(""); + } + + // additional data :: keyword, button title, package, activity, data name, data content + if (cache != null && cache.geocode != null && cache.geocode.length() > 0) { + dos.writeUTF("intent;c:geo;cgeo.geocaching;cgeo.geocaching.cgeodetail;geocode;" + cache.geocode); + } else if (waypoint != null && waypoint.id != null && waypoint.id > 0) { + dos.writeUTF("intent;c:geo;cgeo.geocaching;cgeo.geocaching.cgeowaypoint;id;" + waypoint.id); + } else { + dos.writeUTF(""); + } + + if (cache != null && cache.latitude != null && cache.longitude != null) { + dos.writeDouble(cache.latitude); // latitude + dos.writeDouble(cache.longitude); // longitude + } else if (waypoint != null && waypoint.latitude != null && waypoint.longitude != null) { + dos.writeDouble(waypoint.latitude); // latitude + dos.writeDouble(waypoint.longitude); // longitude + } else { + dos.writeDouble(latitude); // latitude + dos.writeDouble(longitude); // longitude + } + + // cache waypoints + if (waypoints != null && waypoints.isEmpty() == false) { + for (cgWaypoint wp : waypoints) { + if (wp == null || wp.latitude == null || wp.longitude == null) { + continue; + } + + final int wpIcon = getIcon(false, wp.type, false, false, false); + + if (wpIcon > 0) { + // load icon + Bitmap bitmap = BitmapFactory.decodeResource(res, wpIcon); + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos2); + byte[] image = baos2.toByteArray(); + + dos.writeInt(image.length); + dos.write(image); + } else { + // no icon + dos.writeInt(0); // no image + } + + // name + if (wp.lookup != null && wp.lookup.length() > 0) { + dos.writeUTF(wp.lookup.toUpperCase()); + } else { + dos.writeUTF(""); + } + + // description + if (wp.name != null && wp.name.length() > 0) { + dos.writeUTF(wp.name); + } else { + dos.writeUTF(""); + } + + // additional data :: keyword, button title, package, activity, data name, data content + if (wp.id != null && wp.id > 0) { + dos.writeUTF("intent;c:geo;cgeo.geocaching;cgeo.geocaching.cgeowaypoint;id;" + wp.id); + } else { + dos.writeUTF(""); + } + + dos.writeDouble(wp.latitude); // latitude + dos.writeDouble(wp.longitude); // longitude + } + } + + final Intent intent = new Intent(); + intent.setAction(Intent.ACTION_VIEW); + intent.setData(Uri.parse("menion.points:data")); + intent.putExtra("data", baos.toByteArray()); + + activity.startActivity(intent); + + sendAnal(activity, tracker, "/external/locus"); + + return true; + } + } catch (Exception e) { + // nothing + } + } + + if (application == mapAppRmaps) { + // rmaps + try { + final Intent intent = new Intent("com.robert.maps.action.SHOW_POINTS"); + + if (isIntentAvailable(activity, intent) == true) { + final ArrayList<String> locations = new ArrayList<String>(); + if (cache != null && cache.latitude != null && cache.longitude != null) { + locations.add(String.format((Locale) null, "%.6f", cache.latitude) + "," + String.format((Locale) null, "%.6f", cache.longitude) + ";" + cache.geocode + ";" + cache.name); + } else if (waypoint != null && waypoint.latitude != null && waypoint.longitude != null) { + locations.add(String.format((Locale) null, "%.6f", waypoint.latitude) + "," + String.format((Locale) null, "%.6f", waypoint.longitude) + ";" + waypoint.lookup + ";" + waypoint.name); + } + + intent.putStringArrayListExtra("locations", locations); + + activity.startActivity(intent); + + sendAnal(activity, tracker, "/external/rmaps"); + + return true; + } + } catch (Exception e) { + // nothing + } + } + + if (application == mapAppAny) { + // fallback + try { + if (cache != null && cache.latitude != null && cache.longitude != null) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("geo:" + cache.latitude + "," + cache.longitude))); + // INFO: q parameter works with Google Maps, but breaks cooperation with all other apps + } else if (waypoint != null && waypoint.latitude != null && waypoint.longitude != null) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("geo:" + waypoint.latitude + "," + waypoint.longitude))); + // INFO: q parameter works with Google Maps, but breaks cooperation with all other apps + } + + sendAnal(activity, tracker, "/external/native/maps"); + + return true; + } catch (Exception e) { + // nothing + } + } + + Log.i(cgSettings.tag, "cgBase.runExternalMap: No maps application available."); + + if (warning != null && res != null) { + warning.showToast(res.getString(R.string.err_application_no)); + } + + return false; + } + + public boolean runNavigation(Activity activity, Resources res, cgSettings settings, cgWarning warning, GoogleAnalyticsTracker tracker, Double latitude, Double longitude) { + return runNavigation(activity, res, settings, warning, tracker, latitude, longitude, null, null); + } + + public boolean runNavigation(Activity activity, Resources res, cgSettings settings, cgWarning warning, GoogleAnalyticsTracker tracker, Double latitude, Double longitude, Double latitudeNow, Double longitudeNow) { + if (activity == null) { + return false; + } + if (settings == null) { + return false; + } + + // Google Navigation + if (settings.useGNavigation == 1) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:ll=" + latitude + "," + longitude))); + + sendAnal(activity, tracker, "/external/native/navigation"); + + return true; + } catch (Exception e) { + // nothing + } + } + + // Google Maps Directions + try { + if (latitudeNow != null && longitudeNow != null) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://maps.google.com/maps?f=d&saddr=" + latitudeNow + "," + longitudeNow + "&daddr=" + latitude + "," + longitude))); + } else { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://maps.google.com/maps?f=d&daddr=" + latitude + "," + longitude))); + } + + sendAnal(activity, tracker, "/external/native/maps"); + + return true; + } catch (Exception e) { + // nothing + } + + Log.i(cgSettings.tag, "cgBase.runNavigation: No navigation application available."); + + if (warning != null && res != null) { + warning.showToast(res.getString(R.string.err_navigation_no)); + } + + return false; + } + + public String getMapUserToken(Handler noTokenHandler) { + final cgResponse response = request(false, "www.geocaching.com", "/map/default.aspx", "GET", "", 0, false); + final String data = response.getData(); + String usertoken = null; + + if (data != null && data.length() > 0) { + final Pattern pattern = Pattern.compile("var userToken[^=]*=[^']*'([^']+)';", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + + final Matcher matcher = pattern.matcher(data); + while (matcher.find()) { + if (matcher.groupCount() > 0) { + usertoken = matcher.group(1); + } + } + } + + if (noTokenHandler != null && (usertoken == null || usertoken.length() == 0)) { + noTokenHandler.sendEmptyMessage(0); + } + + return usertoken; + } + + public void sendAnal(Context context, String page) { + (new sendAnalThread(context, null, page)).start(); + } + + public void sendAnal(Context context, GoogleAnalyticsTracker tracker, String page) { + (new sendAnalThread(context, tracker, page)).start(); + } + + private class sendAnalThread extends Thread { + + Context context = null; + GoogleAnalyticsTracker tracker = null; + String page = null; + boolean startedHere = false; + + public sendAnalThread(Context contextIn, GoogleAnalyticsTracker trackerIn, String pageIn) { + context = contextIn; + tracker = trackerIn; + page = pageIn; + } + + @Override + public void run() { + try { + if (page == null || page.length() == 0) { + page = "/"; + } + + if (tracker == null && context != null) { + startedHere = true; + tracker = GoogleAnalyticsTracker.getInstance(); + tracker.start(cgSettings.analytics, context); + } + + tracker.trackPageView(page); + tracker.dispatch(); + + Log.i(cgSettings.tag, "Logged use of " + page); + + if (startedHere == true) { + tracker.stop(); + } + } catch (Exception e) { + // nothing + } + } + } + + public Double getElevation(Double latitude, Double longitude) { + Double elv = null; + + try { + final String host = "maps.googleapis.com"; + final String path = "/maps/api/elevation/json"; + final String params = "sensor=false&locations=" + String.format((Locale) null, "%.6f", latitude) + "," + String.format((Locale) null, "%.6f", longitude); + + final String data = requestJSON(host, path, params); + + if (data == null || data.length() == 0) { + return elv; + } + + JSONObject response = new JSONObject(data); + String status = response.getString("status"); + + if (status == null || status.equalsIgnoreCase("OK") == false) { + return elv; + } + + if (response.has("results") == true) { + JSONArray results = response.getJSONArray("results"); + JSONObject result = results.getJSONObject(0); + elv = result.getDouble("elevation"); + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.getElevation: " + e.toString()); + } + + return elv; + } + + public void showProgress(Activity activity, boolean status) { + if (activity == null) { + return; + } + + final ProgressBar progress = (ProgressBar) activity.findViewById(R.id.actionbar_progress); + if (status == true) { + progress.setVisibility(View.VISIBLE); + } else { + progress.setVisibility(View.GONE); + } + } + + public void goHome(Activity activity) { + final Intent intent = new Intent(activity, cgeo.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + + activity.startActivity(intent); + activity.finish(); + } + + public void setTitle(Activity activity, String text) { + if (activity == null || text == null) { + return; + } + + final TextView title = (TextView) activity.findViewById(R.id.actionbar_title); + if (title != null) { + title.setText(text); + } + } +} diff --git a/src/cgeo/geocaching/cgCache.java b/src/cgeo/geocaching/cgCache.java new file mode 100644 index 0000000..5bf42f2 --- /dev/null +++ b/src/cgeo/geocaching/cgCache.java @@ -0,0 +1,218 @@ +package cgeo.geocaching; + +import android.text.Spannable; +import java.util.Date; +import java.util.ArrayList; +import java.util.HashMap; + +public class cgCache { + + public Long updated = null; + public Long detailedUpdate = null; + public Long visitedDate = null; + public Integer reason = 0; + public Boolean detailed = false; + public String geocode = ""; + public String cacheid = ""; + public String guid = ""; + public String type = ""; + public String name = ""; + public Spannable nameSp = null; + public String owner = ""; + public String ownerReal = ""; + public Date hidden = null; + public String hint = ""; + public String size = ""; + public Float difficulty = new Float(0); + public Float terrain = new Float(0); + public Double direction = null; + public Double distance = null; + public String latlon = ""; + public String latitudeString = ""; + public String longitudeString = ""; + public String location = ""; + public Double latitude = null; + public Double longitude = null; + public boolean reliableLatLon = false; + public Double elevation = null; + public String shortdesc = ""; + public String description = ""; + public boolean disabled = false; + public boolean archived = false; + public boolean members = false; + public boolean found = false; + public boolean favourite = false; + public boolean own = false; + public Integer favouriteCnt = null; + public Float rating = null; + public Integer votes = null; + public Float myVote = null; + public int inventoryItems = 0; + public ArrayList<String> attributes = null; + public ArrayList<cgWaypoint> waypoints = null; + public ArrayList<cgSpoiler> spoilers = null; + public ArrayList<cgLog> logs = null; + public ArrayList<cgTrackable> inventory = null; + public HashMap<Integer, Integer> logCounts = new HashMap<Integer, Integer>(); + public boolean logOffline = false; + // temporary values + public boolean statusChecked = false; + public boolean statusCheckedView = false; + public String directionImg = null; + + public cgCache merge(cgData storage) { + //LeeB - loading cache from db is slow + if(false){ + return this; + } + + boolean loadA = true; + boolean loadW = true; + boolean loadS = true; + boolean loadL = true; + boolean loadI = true; + + if (attributes == null || attributes.isEmpty() == true) { + loadA = false; + } + if (waypoints == null || waypoints.isEmpty() == true) { + loadW = false; + } + if (spoilers == null || spoilers.isEmpty() == true) { + loadS = false; + } + if (logs == null || logs.isEmpty() == true) { + loadL = false; + } + if (inventory == null || inventory.isEmpty() == true) { + loadI = false; + } + + final cgCache oldCache = storage.loadCache(geocode, guid, loadA, loadW, loadS, loadL, loadI, false); + + if (oldCache == null) { + return this; + } + + updated = System.currentTimeMillis(); + if (detailed == false && oldCache.detailed == true) { + detailed = true; + detailedUpdate = System.currentTimeMillis(); + } + + if (visitedDate == null || visitedDate == 0) { + visitedDate = oldCache.visitedDate; + } + if (reason == null || reason == 0) { + reason = oldCache.reason; + } + if (geocode == null || geocode.length() == 0) { + geocode = oldCache.geocode; + } + if (cacheid == null || cacheid.length() == 0) { + cacheid = oldCache.cacheid; + } + if (guid == null || guid.length() == 0) { + guid = oldCache.guid; + } + if (type == null || type.length() == 0) { + type = oldCache.type; + } + if (name == null || name.length() == 0) { + name = oldCache.name; + } + if (nameSp == null || nameSp.length() == 0) { + nameSp = oldCache.nameSp; + } + if (owner == null || owner.length() == 0) { + owner = oldCache.owner; + } + if (ownerReal == null || ownerReal.length() == 0) { + ownerReal = oldCache.ownerReal; + } + if (hidden == null) { + hidden = oldCache.hidden; + } + if (hint == null || hint.length() == 0) { + hint = oldCache.hint; + } + if (size == null || size.length() == 0) { + size = oldCache.size; + } + if (difficulty == null || difficulty == 0) { + difficulty = oldCache.difficulty; + } + if (terrain == null || terrain == 0) { + terrain = oldCache.terrain; + } + if (direction == null) { + direction = oldCache.direction; + } + if (distance == null) { + distance = oldCache.distance; + } + if (latlon == null || latlon.length() == 0) { + latlon = oldCache.latlon; + } + if (latitudeString == null || latitudeString.length() == 0) { + latitudeString = oldCache.latitudeString; + } + if (longitudeString == null || longitudeString.length() == 0) { + longitudeString = oldCache.longitudeString; + } + if (location == null || location.length() == 0) { + location = oldCache.location; + } + if (latitude == null) { + latitude = oldCache.latitude; + } + if (longitude == null) { + longitude = oldCache.longitude; + } + if (elevation == null) { + elevation = oldCache.elevation; + } + if (shortdesc == null || shortdesc.length() == 0) { + shortdesc = oldCache.shortdesc; + } + if (description == null || description.length() == 0) { + description = oldCache.description; + } + if (favouriteCnt == null) { + favouriteCnt = oldCache.favouriteCnt; + } + if (rating == null) { + rating = oldCache.rating; + } + if (votes == null) { + votes = oldCache.votes; + } + if (myVote == null) { + myVote = oldCache.myVote; + } + if (inventoryItems == 0) { + inventoryItems = oldCache.inventoryItems; + } + if (attributes == null) { + attributes = oldCache.attributes; + } + if (waypoints == null) { + waypoints = oldCache.waypoints; + } + if (spoilers == null) { + spoilers = oldCache.spoilers; + } + if (inventory == null) { + inventory = oldCache.inventory; + } + if (logs == null || logs.isEmpty()) { // keep last known logs if none + logs = oldCache.logs; + } + + return this; + } + + public boolean hasTrackables(){ + return inventoryItems > 0; + } +} diff --git a/src/cgeo/geocaching/cgCacheDifficultyComparator.java b/src/cgeo/geocaching/cgCacheDifficultyComparator.java new file mode 100644 index 0000000..69971a4 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheDifficultyComparator.java @@ -0,0 +1,26 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; + +public class cgCacheDifficultyComparator implements Comparator<cgCache> { + + public int compare(cgCache cache1, cgCache cache2) { + try { + if (cache1.difficulty == null || cache2.difficulty == null) { + return 0; + } + + if (cache1.difficulty > cache2.difficulty) { + return 1; + } else if (cache2.difficulty > cache1.difficulty) { + return -1; + } else { + return 0; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCacheDifficultyComparator.compare: " + e.toString()); + } + return 0; + } +} diff --git a/src/cgeo/geocaching/cgCacheDistanceComparator.java b/src/cgeo/geocaching/cgCacheDistanceComparator.java new file mode 100644 index 0000000..2fbdd64 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheDistanceComparator.java @@ -0,0 +1,51 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; + +public class cgCacheDistanceComparator implements Comparator<cgCache> { + private Double latitude = null; + private Double longitude = null; + + public cgCacheDistanceComparator() { + // nothing + } + + public cgCacheDistanceComparator(Double latitudeIn, Double longitudeIn) { + latitude = latitudeIn; + longitude = longitudeIn; + } + + public void setCoords(Double latitudeIn, Double longitudeIn) { + latitude = latitudeIn; + longitude = longitudeIn; + } + + public int compare(cgCache cache1, cgCache cache2) { + int result = 0; + try { + if ( + (cache1.latitude == null || cache1.longitude == null || cache2.latitude == null || cache2.longitude == null) && + cache1.distance != null && cache2.distance != null + ) { + if (cache1.distance < cache2.distance) return -1; + else if (cache1.distance > cache2.distance) return 1; + else return 0; + } else { + if (cache1.latitude == null || cache1.longitude == null) return 1; + if (cache2.latitude == null || cache2.longitude == null) return -1; + + Double distance1 = cgBase.getDistance(latitude, longitude, cache1.latitude, cache1.longitude); + Double distance2 = cgBase.getDistance(latitude, longitude, cache2.latitude, cache2.longitude); + + if (distance1 < distance2) result = -1; + else if (distance1 > distance2) result = 1; + else result = 0; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCacheDistanceComparator.compare: " + e.toString()); + } + + return result; + } +} diff --git a/src/cgeo/geocaching/cgCacheGeocodeComparator.java b/src/cgeo/geocaching/cgCacheGeocodeComparator.java new file mode 100644 index 0000000..e06d9f3 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheGeocodeComparator.java @@ -0,0 +1,26 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; + +public class cgCacheGeocodeComparator implements Comparator<cgCache> { + + public int compare(cgCache cache1, cgCache cache2) { + try { + if (cache1.geocode == null || cache1.geocode.length() <= 0 || cache2.geocode == null || cache2.geocode.length() <= 0) { + return 0; + } + + if (cache1.geocode.length() > cache2.geocode.length()) { + return 1; + } else if (cache2.geocode.length() > cache1.geocode.length()) { + return -1; + } else { + return cache1.geocode.compareToIgnoreCase(cache2.geocode); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCacheGeocodeComparator.compare: " + e.toString()); + } + return 0; + } +} diff --git a/src/cgeo/geocaching/cgCacheInventoryComparator.java b/src/cgeo/geocaching/cgCacheInventoryComparator.java new file mode 100644 index 0000000..e58eb43 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheInventoryComparator.java @@ -0,0 +1,36 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; + +/** + * compares by number of items in inventory + * @author bananeweizen + * + */ +public class cgCacheInventoryComparator implements Comparator<cgCache> { + + public int compare(cgCache cache1, cgCache cache2) { + try { + int itemCount1 = 0; + int itemCount2 = 0; + if (cache1.difficulty != null) { + itemCount1 = cache1.inventoryItems; + } + if (cache2.difficulty != null) { + itemCount2 = cache2.inventoryItems; + } + + if (itemCount1 < itemCount2) { + return 1; + } else if (itemCount2 < itemCount1) { + return -1; + } else { + return 0; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCacheInventoryComparator.compare: " + e.toString()); + } + return 0; + } +} diff --git a/src/cgeo/geocaching/cgCacheListAdapter.java b/src/cgeo/geocaching/cgCacheListAdapter.java new file mode 100644 index 0000000..b1eded2 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheListAdapter.java @@ -0,0 +1,909 @@ +package cgeo.geocaching; + +import java.util.List; +import java.util.HashMap; +import android.app.Activity; +import android.text.Spannable; +import android.text.style.StrikethroughSpan; +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.ArrayAdapter; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.Drawable; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.AnimationSet; +import android.view.animation.TranslateAnimation; +import android.widget.CheckBox; +import android.widget.ImageView; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Locale; + +import cgeo.geocaching.filter.cgFilter; + +public class cgCacheListAdapter extends ArrayAdapter<cgCache> { + + private Resources res = null; + private List<cgCache> list = null; + private cgSettings settings = null; + private cgCacheView holder = null; + private LayoutInflater inflater = null; + private Activity activity = null; + private cgBase base = null; + private cgCacheDistanceComparator dstComparator = null; + private Comparator statComparator = null; + private boolean historic = false; + private Double latitude = null; + private Double longitude = null; + private Double azimuth = new Double(0); + private long lastSort = 0l; + private boolean sort = true; + private int checked = 0; + private boolean selectMode = false; + private HashMap<String, Drawable> gcIcons = new HashMap<String, Drawable>(); + private ArrayList<cgCompassMini> compasses = new ArrayList<cgCompassMini>(); + private ArrayList<cgDistanceView> distances = new ArrayList<cgDistanceView>(); + private int[] ratingBcgs = new int[3]; + private float pixelDensity = 1f; + private static final int SWIPE_MIN_DISTANCE = 60; + private static final int SWIPE_MAX_OFF_PATH = 100; + private static final int SWIPE_DISTANCE = 80; + private static final float SWIPE_OPACITY = 0.5f; + private cgFilter currentFilter = null; + private List<cgCache> originalList = null; + + public cgCacheListAdapter(Activity activityIn, cgSettings settingsIn, List<cgCache> listIn, cgBase baseIn) { + super(activityIn, 0, listIn); + + res = activityIn.getResources(); + activity = activityIn; + settings = settingsIn; + list = listIn; + base = baseIn; + dstComparator = new cgCacheDistanceComparator(); + + DisplayMetrics metrics = new DisplayMetrics(); + activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); + pixelDensity = metrics.density; + + if (gcIcons == null || gcIcons.isEmpty()) { + gcIcons.put("ape", (Drawable) activity.getResources().getDrawable(R.drawable.type_ape)); + gcIcons.put("cito", (Drawable) activity.getResources().getDrawable(R.drawable.type_cito)); + gcIcons.put("earth", (Drawable) activity.getResources().getDrawable(R.drawable.type_earth)); + gcIcons.put("event", (Drawable) activity.getResources().getDrawable(R.drawable.type_event)); + gcIcons.put("letterbox", (Drawable) activity.getResources().getDrawable(R.drawable.type_letterbox)); + gcIcons.put("locationless", (Drawable) activity.getResources().getDrawable(R.drawable.type_locationless)); + gcIcons.put("mega", (Drawable) activity.getResources().getDrawable(R.drawable.type_mega)); + gcIcons.put("multi", (Drawable) activity.getResources().getDrawable(R.drawable.type_multi)); + gcIcons.put("traditional", (Drawable) activity.getResources().getDrawable(R.drawable.type_traditional)); + gcIcons.put("virtual", (Drawable) activity.getResources().getDrawable(R.drawable.type_virtual)); + gcIcons.put("webcam", (Drawable) activity.getResources().getDrawable(R.drawable.type_webcam)); + gcIcons.put("wherigo", (Drawable) activity.getResources().getDrawable(R.drawable.type_wherigo)); + gcIcons.put("mystery", (Drawable) activity.getResources().getDrawable(R.drawable.type_mystery)); + gcIcons.put("gchq", (Drawable) activity.getResources().getDrawable(R.drawable.type_hq)); + } + + if (settings.skin == 0) { + ratingBcgs[0] = R.drawable.favourite_background_red_dark; + ratingBcgs[1] = R.drawable.favourite_background_orange_dark; + ratingBcgs[2] = R.drawable.favourite_background_green_dark; + } else { + ratingBcgs[0] = R.drawable.favourite_background_red_light; + ratingBcgs[1] = R.drawable.favourite_background_orange_light; + ratingBcgs[2] = R.drawable.favourite_background_green_light; + } + } + + public void setComparator(Comparator comparator) { + statComparator = comparator; + + forceSort(latitude, longitude); + } + + /** + * Called when a new page of caches was loaded. + */ + public void reFilter(){ + if(currentFilter != null){ + // Back up the list again + originalList = new ArrayList<cgCache>(list); + + currentFilter.filter(list); + } + } + + /** + * Called after a user action on the filter menu. + */ + public void setFilter(cgFilter filter){ + // Backup current caches list if it isn't backed up yet + if (originalList == null) { + originalList = new ArrayList<cgCache>(list); + } + + // If there is already a filter in place, this is a request to change or clear the filter, so we have to + // replace the original cache list + if (currentFilter != null) { + list.clear(); + list.addAll(originalList); + } + + // Do the filtering or clear it + if (filter != null) { + filter.filter(list); + } + currentFilter = filter; + + notifyDataSetChanged(); + } + + public void clearFilter() { + if (originalList != null) { + list.clear(); + list.addAll(originalList); + + currentFilter = null; + } + + notifyDataSetChanged(); + } + + public boolean isFilter() { + if (currentFilter != null) { + return true; + } else { + return false; + } + } + + public void setHistoric(boolean historicIn) { + historic = historicIn; + + if (historic == true) { + statComparator = new cgCacheVisitComparator(); + } else { + statComparator = null; + } + } + + public int getChecked() { + return checked; + } + + public boolean setSelectMode(boolean status, boolean clear) { + selectMode = status; + + if (selectMode == false && clear == true) { + for (cgCache cache : list) { + cache.statusChecked = false; + cache.statusCheckedView = false; + } + checked = 0; + } else if (selectMode == true) { + for (cgCache cache : list) { + cache.statusCheckedView = false; + } + } + checkChecked(0); + + notifyDataSetChanged(); + + return selectMode; + } + + public boolean getSelectMode() { + return selectMode; + } + + public void switchSelectMode() { + selectMode = !selectMode; + + if (selectMode == false) { + for (cgCache cache : list) { + cache.statusChecked = false; + cache.statusCheckedView = false; + } + checked = 0; + } else if (selectMode == true) { + for (cgCache cache : list) { + cache.statusCheckedView = false; + } + } + checkChecked(0); + + notifyDataSetChanged(); + } + + public void invertSelection() { + int check = 0; + + for (cgCache cache : list) { + if (cache.statusChecked == true) { + cache.statusChecked = false; + cache.statusCheckedView = false; + } else { + cache.statusChecked = true; + cache.statusCheckedView = true; + + check++; + } + } + checkChecked(check); + + notifyDataSetChanged(); + } + + public void forceSort(Double latitudeIn, Double longitudeIn) { + if (list == null || list.isEmpty() == true) { + return; + } + if (sort == false) { + return; + } + + try { + if (statComparator != null) { + Collections.sort((List<cgCache>) list, statComparator); + } else { + if (latitudeIn == null || longitudeIn == null) { + return; + } + + dstComparator.setCoords(latitudeIn, longitudeIn); + Collections.sort((List<cgCache>) list, dstComparator); + } + notifyDataSetChanged(); + } catch (Exception e) { + Log.w(cgSettings.tag, "cgCacheListAdapter.setActualCoordinates: failed to sort caches in list"); + } + } + + public void setActualCoordinates(Double latitudeIn, Double longitudeIn) { + if (latitudeIn == null || longitudeIn == null) { + return; + } + + latitude = latitudeIn; + longitude = longitudeIn; + + if (list != null && list.isEmpty() == false && (System.currentTimeMillis() - lastSort) > 1000 && sort == true) { + try { + if (statComparator != null) { + Collections.sort((List<cgCache>) list, statComparator); + } else { + dstComparator.setCoords(latitudeIn, longitudeIn); + Collections.sort((List<cgCache>) list, dstComparator); + } + notifyDataSetChanged(); + } catch (Exception e) { + Log.w(cgSettings.tag, "cgCacheListAdapter.setActualCoordinates: failed to sort caches in list"); + } + + lastSort = System.currentTimeMillis(); + } + + if (distances != null && distances.size() > 0) { + for (cgDistanceView distance : distances) { + distance.update(latitudeIn, longitudeIn); + } + } + + if (compasses != null && compasses.size() > 0) { + for (cgCompassMini compass : compasses) { + compass.updateCoords(latitudeIn, longitudeIn); + } + } + } + + public void setActualHeading(Double azimuthIn) { + if (azimuthIn == null) { + return; + } + + azimuth = azimuthIn; + + if (compasses != null && compasses.size() > 0) { + for (cgCompassMini compass : compasses) { + compass.updateAzimuth(azimuth); + } + } + } + + public boolean resetChecks() { + if (list.isEmpty() == true) { + return false; + } + if (checked <= 0) { + return false; + } + + boolean status = getSelectMode(); + int cleared = 0; + for (cgCache cache : list) { + if (cache.statusChecked == true) { + cache.statusChecked = false; + + checkChecked(-1); + cleared++; + } + } + setSelectMode(false, false); + notifyDataSetChanged(); + + if (cleared > 0 || status == true) { + return true; + } else { + return false; + } + } + + @Override + public View getView(int position, View rowView, ViewGroup parent) { + if (inflater == null) { + inflater = ((Activity) getContext()).getLayoutInflater(); + } + + if (position > getCount()) { + Log.w(cgSettings.tag, "cgCacheListAdapter.getView: Attempt to access missing item #" + position); + return null; + } + + cgCache cache = getItem(position); + + if (rowView == null) { + rowView = (View) inflater.inflate(R.layout.cache, null); + + holder = new cgCacheView(); + holder.oneCache = (RelativeLayout) rowView.findViewById(R.id.one_cache); + holder.checkbox = (CheckBox) rowView.findViewById(R.id.checkbox); + holder.oneInfo = (RelativeLayout) rowView.findViewById(R.id.one_info); + holder.oneCheckbox = (RelativeLayout) rowView.findViewById(R.id.one_checkbox); + holder.foundMark = (ImageView) rowView.findViewById(R.id.found_mark); + holder.offlineMark = (ImageView) rowView.findViewById(R.id.offline_mark); + holder.oneCache = (RelativeLayout) rowView.findViewById(R.id.one_cache); + holder.text = (TextView) rowView.findViewById(R.id.text); + holder.directionLayout = (RelativeLayout) rowView.findViewById(R.id.direction_layout); + holder.distance = (cgDistanceView) rowView.findViewById(R.id.distance); + holder.direction = (cgCompassMini) rowView.findViewById(R.id.direction); + holder.dirImgLayout = (RelativeLayout) rowView.findViewById(R.id.dirimg_layout); + holder.dirImg = (ImageView) rowView.findViewById(R.id.dirimg); + holder.inventory = (RelativeLayout) rowView.findViewById(R.id.inventory); + holder.favourite = (TextView) rowView.findViewById(R.id.favourite); + holder.info = (TextView) rowView.findViewById(R.id.info); + + rowView.setTag(holder); + } else { + holder = (cgCacheView) rowView.getTag(); + } + + if (cache.own == true) { + if (settings.skin == 1) { + holder.oneInfo.setBackgroundResource(R.color.owncache_background_light); + holder.oneCheckbox.setBackgroundResource(R.color.owncache_background_light); + } else { + holder.oneInfo.setBackgroundResource(R.color.owncache_background_dark); + holder.oneCheckbox.setBackgroundResource(R.color.owncache_background_dark); + } + } else { + if (settings.skin == 1) { + holder.oneInfo.setBackgroundResource(R.color.background_light); + holder.oneCheckbox.setBackgroundResource(R.color.background_light); + } else { + holder.oneInfo.setBackgroundResource(R.color.background_dark); + holder.oneCheckbox.setBackgroundResource(R.color.background_dark); + } + } + + final touchListener touchLst = new touchListener(cache.geocode, cache.name, cache); + rowView.setOnClickListener(touchLst); + rowView.setOnLongClickListener(touchLst); + rowView.setOnTouchListener(touchLst); + rowView.setLongClickable(true); + + if (selectMode == true) { + if (cache.statusCheckedView == true) { + moveRight(holder, cache, true); // move fast when already slided + } else { + moveRight(holder, cache, false); + } + } else if (cache.statusChecked == true) { + holder.checkbox.setChecked(true); + if (cache.statusCheckedView == true) { + moveRight(holder, cache, true); // move fast when already slided + } else { + moveRight(holder, cache, false); + } + } else { + holder.checkbox.setChecked(false); + if (cache.statusCheckedView == false) { + holder.oneInfo.clearAnimation(); + } else { + moveLeft(holder, cache, false); + } + } + + holder.checkbox.setOnClickListener(new checkBoxListener(cache)); + + if (distances.contains(holder.distance) == false) { + distances.add(holder.distance); + } + holder.distance.setContent(base, cache.latitude, cache.longitude); + if (compasses.contains(holder.direction) == false) { + compasses.add(holder.direction); + } + holder.direction.setContent(base, cache.latitude, cache.longitude); + + if (cache.logOffline == true) { + holder.offlineMark.setVisibility(View.VISIBLE); + holder.foundMark.setVisibility(View.GONE); + } else if (cache.found == true) { + holder.offlineMark.setVisibility(View.GONE); + holder.foundMark.setVisibility(View.VISIBLE); + } else { + holder.offlineMark.setVisibility(View.GONE); + holder.foundMark.setVisibility(View.GONE); + } + + if (cache.nameSp == null) { + cache.nameSp = (new Spannable.Factory()).newSpannable(cache.name); + if (cache.disabled == true || cache.archived == true) { // strike + cache.nameSp.setSpan(new StrikethroughSpan(), 0, cache.nameSp.toString().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + holder.text.setText(cache.nameSp, TextView.BufferType.SPANNABLE); + if (gcIcons.containsKey(cache.type) == true) { // cache icon + holder.text.setCompoundDrawablesWithIntrinsicBounds(gcIcons.get(cache.type), null, null, null); + } else { // unknown cache type, "mystery" icon + holder.text.setCompoundDrawablesWithIntrinsicBounds(gcIcons.get("mystery"), null, null, null); + } + + if (holder.inventory.getChildCount() > 0) { + holder.inventory.removeAllViews(); + } + + ImageView tbIcon = null; + if (cache.inventoryItems > 0) { + tbIcon = (ImageView) inflater.inflate(R.layout.trackable_icon, null); + tbIcon.setImageResource(R.drawable.trackable_all); + + holder.inventory.addView(tbIcon); + holder.inventory.setVisibility(View.VISIBLE); + } else { + holder.inventory.setVisibility(View.GONE); + } + + boolean setDiDi = false; + if (cache.latitude != null && cache.longitude != null) { + holder.direction.setVisibility(View.VISIBLE); + holder.direction.updateAzimuth(azimuth); + if (latitude != null && longitude != null) { + holder.distance.update(latitude, longitude); + holder.direction.updateCoords(latitude, longitude); + } + setDiDi = true; + } else { + if (cache.distance != null) { + holder.distance.setDistance(cache.distance); + setDiDi = true; + } + if (cache.direction != null) { + holder.direction.setVisibility(View.VISIBLE); + holder.direction.updateAzimuth(azimuth); + holder.direction.updateHeading(cache.direction); + setDiDi = true; + } + } + + if (setDiDi == true) { + holder.directionLayout.setVisibility(View.VISIBLE); + holder.dirImgLayout.setVisibility(View.GONE); + } else { + holder.directionLayout.setVisibility(View.GONE); + holder.distance.clear(); + + Bitmap dirImgPre = null; + Bitmap dirImg = null; + try { + dirImgPre = BitmapFactory.decodeFile(settings.getStorage() + cache.geocode + "/direction.png"); + dirImg = dirImgPre.copy(Bitmap.Config.ARGB_8888, true); + + dirImgPre.recycle(); + dirImgPre = null; + } catch (Exception e) { + // nothing + } + + if (dirImg != null) { + if (settings.skin == 0) { + int length = dirImg.getWidth() * dirImg.getHeight(); + int[] pixels = new int[length]; + dirImg.getPixels(pixels, 0, dirImg.getWidth(), 0, 0, dirImg.getWidth(), dirImg.getHeight()); + for (int i = 0; i < length; i++) { + if (pixels[i] == 0xff000000) { // replace black with white + pixels[i] = 0xffffffff; + } + } + dirImg.setPixels(pixels, 0, dirImg.getWidth(), 0, 0, dirImg.getWidth(), dirImg.getHeight()); + } + + holder.dirImg.setImageBitmap(dirImg); + holder.dirImgLayout.setVisibility(View.VISIBLE); + } else { + holder.dirImg.setImageBitmap(null); + holder.dirImgLayout.setVisibility(View.GONE); + } + } + + if (cache.favouriteCnt != null) { + holder.favourite.setText(String.format("%d", cache.favouriteCnt)); + } else { + holder.favourite.setText("---"); + } + + int favoriteBack; + // set default background, neither vote nor rating may be available + if (settings.skin == 1) { + favoriteBack = R.drawable.favourite_background_light; + } else { + favoriteBack = R.drawable.favourite_background_dark; + } + if (cache.myVote != null && cache.myVote > 0) { + if (cache.myVote >= 4) { + favoriteBack = ratingBcgs[2]; + } else if (cache.myVote >= 3) { + favoriteBack = ratingBcgs[1]; + } else if (cache.myVote > 0) { + favoriteBack = ratingBcgs[0]; + } + } else if (cache.rating != null && cache.rating > 0) { + if (cache.rating >= 3.5) { + favoriteBack = ratingBcgs[2]; + } else if (cache.rating >= 2.1) { + favoriteBack = ratingBcgs[1]; + } else if (cache.rating > 0.0) { + favoriteBack = ratingBcgs[0]; + } + } + holder.favourite.setBackgroundResource(favoriteBack); + + StringBuilder cacheInfo = new StringBuilder(); + if (historic == true && cache.visitedDate != null) { + cacheInfo.append(cgBase.timeOut.format(cache.visitedDate)); + cacheInfo.append("; "); + cacheInfo.append(cgBase.dateOut.format(cache.visitedDate)); + } else { + if (cache.geocode != null && cache.geocode.length() > 0) { + cacheInfo.append(cache.geocode); + } + if (cache.size != null && cache.size.length() > 0) { + if (cacheInfo.length() > 0) { + cacheInfo.append(" | "); + } + cacheInfo.append(cache.size); + } + if ((cache.difficulty != null && cache.difficulty > 0f) || (cache.terrain != null && cache.terrain > 0f) || (cache.rating != null && cache.rating > 0f)) { + if (cacheInfo.length() > 0 && ((cache.difficulty != null && cache.difficulty > 0f) || (cache.terrain != null && cache.terrain > 0f))) { + cacheInfo.append(" |"); + } + + if (cache.difficulty != null && cache.difficulty > 0f) { + cacheInfo.append(" D:"); + cacheInfo.append(String.format(Locale.getDefault(), "%.1f", cache.difficulty)); + } + if (cache.terrain != null && cache.terrain > 0f) { + cacheInfo.append(" T:"); + cacheInfo.append(String.format(Locale.getDefault(), "%.1f", cache.terrain)); + } + } + if (cache.members == true) { + if (cacheInfo.length() > 0) { + cacheInfo.append(" | "); + } + cacheInfo.append(res.getString(R.string.cache_premium)); + } + if (cache.reason != null && cache.reason == 1) { + if (cacheInfo.length() > 0) { + cacheInfo.append(" | "); + } + cacheInfo.append(res.getString(R.string.cache_offline)); + } + } + holder.info.setText(cacheInfo.toString()); + + return rowView; + } + + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + + checked = 0; + for (cgCache cache : list) { + if (cache.statusChecked == true) { + checked++; + } + } + + distances.clear(); + compasses.clear(); + } + + private class checkBoxListener implements View.OnClickListener { + + private cgCache cache = null; + + public checkBoxListener(cgCache cacheIn) { + cache = cacheIn; + } + + public void onClick(View view) { + final boolean checkNow = ((CheckBox) view).isChecked(); + + if (checkNow == true) { + cache.statusChecked = true; + checked++; + } else { + cache.statusChecked = false; + checked--; + } + } + } + + private class touchListener implements View.OnLongClickListener, View.OnClickListener, View.OnTouchListener { + + private String geocode = null; + private String name = null; + private cgCache cache = null; + private boolean touch = true; + private GestureDetector gestureDetector = null; + + public touchListener(String geocodeIn, String nameIn, cgCache cacheIn) { + geocode = geocodeIn; + name = nameIn; + cache = cacheIn; + + final detectGesture dGesture = new detectGesture(holder, cache); + gestureDetector = new GestureDetector(dGesture); + } + + // tap on item + public void onClick(View view) { + if (touch == false) { + touch = true; + + return; + } + + if (getSelectMode() == true || getChecked() > 0) { + return; + } + + // load cache details + Intent cachesIntent = new Intent(getContext(), cgeodetail.class); + cachesIntent.putExtra("geocode", geocode); + cachesIntent.putExtra("name", name); + getContext().startActivity(cachesIntent); + } + + // long tap on item + public boolean onLongClick(View view) { + if (touch == false) { + touch = true; + + return true; + } + + return view.showContextMenu(); + } + + // swipe on item + public boolean onTouch(View view, MotionEvent event) { + if (gestureDetector.onTouchEvent(event) == true) { + touch = false; + + return true; + } + + return false; + } + } + + class detectGesture extends GestureDetector.SimpleOnGestureListener { + + private cgCacheView holder = null; + private cgCache cache = null; + + public detectGesture(cgCacheView holderIn, cgCache cacheIn) { + holder = holderIn; + cache = cacheIn; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + try { + if (getSelectMode() == true) { + return false; + } + + if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) { + return false; + } + + if ((e2.getX() - e1.getX()) > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > Math.abs(velocityY)) { + // left to right swipe + if (cache.statusChecked == true) { + return true; + } + + if (holder != null && holder.oneInfo != null) { + checkChecked(+1); + holder.checkbox.setChecked(true); + cache.statusChecked = true; + moveRight(holder, cache, false); + } + + return true; + } else if ((e1.getX() - e2.getX()) > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > Math.abs(velocityY)) { + // right to left swipe + if (cache.statusChecked == false) { + return true; + } + + if (holder != null && holder.oneInfo != null) { + if (getSelectMode() == true) { + setSelectMode(false, false); + } + + checkChecked(-1); + holder.checkbox.setChecked(false); + cache.statusChecked = false; + moveLeft(holder, cache, false); + } + + return true; + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgCacheListAdapter.detectGesture.onFling: " + e.toString()); + } + + return false; + } + } + + private void checkChecked(int cnt) { + // check how many caches are selected, if any block sorting of list + boolean statusChecked = false; + boolean statusSort = false; + checked += cnt; + + if (checked > 0) { + statusChecked = false; + } else { + statusChecked = true; + } + + if (getSelectMode() == true) { + statusSort = false; + } else { + statusSort = true; + } + + if (statusChecked == false || statusSort == false) { + sort = false; + } else { + sort = true; + } + + if (sort == true) { + forceSort(latitude, longitude); + } + } + + private void moveRight(cgCacheView holder, cgCache cache, boolean force) { + if (cache == null) { + return; + } + + try { + holder.checkbox.setChecked(cache.statusChecked); + + // slide cache info + Animation showCheckbox = new TranslateAnimation(0, (int) (SWIPE_DISTANCE * pixelDensity), 0, 0); + showCheckbox.setRepeatCount(0); + if (force == true) { + showCheckbox.setDuration(0); + } else { + showCheckbox.setDuration(400); + } + showCheckbox.setFillEnabled(true); + showCheckbox.setFillAfter(true); + showCheckbox.setInterpolator(new AccelerateDecelerateInterpolator()); + + // dim cache info + Animation dimInfo = new AlphaAnimation(1.0f, SWIPE_OPACITY); + dimInfo.setRepeatCount(0); + if (force == true) { + dimInfo.setDuration(0); + } else { + dimInfo.setDuration(400); + } + dimInfo.setFillEnabled(true); + dimInfo.setFillAfter(true); + dimInfo.setInterpolator(new AccelerateDecelerateInterpolator()); + + // animation set (container) + AnimationSet selectAnimation = new AnimationSet(true); + selectAnimation.setFillEnabled(true); + selectAnimation.setFillAfter(true); + + selectAnimation.addAnimation(showCheckbox); + selectAnimation.addAnimation(dimInfo); + + holder.oneInfo.startAnimation(selectAnimation); + cache.statusCheckedView = true; + } catch (Exception e) { + // nothing + } + } + + private void moveLeft(cgCacheView holder, cgCache cache, boolean force) { + if (cache == null) { + return; + } + + try { + holder.checkbox.setChecked(cache.statusChecked); + + // slide cache info + Animation hideCheckbox = new TranslateAnimation((int) (SWIPE_DISTANCE * pixelDensity), 0, 0, 0); + hideCheckbox.setRepeatCount(0); + if (force == true) { + hideCheckbox.setDuration(0); + } else { + hideCheckbox.setDuration(400); + } + hideCheckbox.setFillEnabled(true); + hideCheckbox.setFillAfter(true); + hideCheckbox.setInterpolator(new AccelerateDecelerateInterpolator()); + + // brighten cache info + Animation brightenInfo = new AlphaAnimation(SWIPE_OPACITY, 1.0f); + brightenInfo.setRepeatCount(0); + if (force == true) { + brightenInfo.setDuration(0); + } else { + brightenInfo.setDuration(400); + } + brightenInfo.setFillEnabled(true); + brightenInfo.setFillAfter(true); + brightenInfo.setInterpolator(new AccelerateDecelerateInterpolator()); + + // animation set (container) + AnimationSet selectAnimation = new AnimationSet(true); + selectAnimation.setFillEnabled(true); + selectAnimation.setFillAfter(true); + + selectAnimation.addAnimation(hideCheckbox); + selectAnimation.addAnimation(brightenInfo); + + holder.oneInfo.startAnimation(selectAnimation); + cache.statusCheckedView = false; + } catch (Exception e) { + // nothing + } + } +} diff --git a/src/cgeo/geocaching/cgCacheNameComparator.java b/src/cgeo/geocaching/cgCacheNameComparator.java new file mode 100644 index 0000000..fa247fa --- /dev/null +++ b/src/cgeo/geocaching/cgCacheNameComparator.java @@ -0,0 +1,20 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; + +public class cgCacheNameComparator implements Comparator<cgCache> { + + public int compare(cgCache cache1, cgCache cache2) { + try { + if (cache1.name == null || cache2.name == null) { + return 0; + } + + return cache1.name.compareToIgnoreCase(cache2.name); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCacheNameComparator.compare: " + e.toString()); + } + return 0; + } +} diff --git a/src/cgeo/geocaching/cgCachePopularityComparator.java b/src/cgeo/geocaching/cgCachePopularityComparator.java new file mode 100644 index 0000000..10500f9 --- /dev/null +++ b/src/cgeo/geocaching/cgCachePopularityComparator.java @@ -0,0 +1,26 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; + +public class cgCachePopularityComparator implements Comparator<cgCache> { + + public int compare(cgCache cache1, cgCache cache2) { + try { + if (cache1.favouriteCnt == null || cache2.favouriteCnt == null) { + return 0; + } + + if (cache1.favouriteCnt < cache2.favouriteCnt) { + return 1; + } else if (cache2.favouriteCnt < cache1.favouriteCnt) { + return -1; + } else { + return 0; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCachePopularityComparator.compare: " + e.toString()); + } + return 0; + } +} diff --git a/src/cgeo/geocaching/cgCacheRatingComparator.java b/src/cgeo/geocaching/cgCacheRatingComparator.java new file mode 100644 index 0000000..b7b9720 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheRatingComparator.java @@ -0,0 +1,36 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; + +public class cgCacheRatingComparator implements Comparator<cgCache> { + + public int compare(cgCache cache1, cgCache cache2) { + try { + Float rating1 = cache1.rating; + Float rating2 = cache2.rating; + if (rating1 == null || rating2 == null) { + return 0; + } + + // voting can be disabled for caches, then assume an average rating instead + if (rating1 == 0.0) { + rating1 = 2.5f; + } + if (rating2 == 0.0) { + rating2 = 2.5f; + } + + if (rating1 < rating2) { + return 1; + } else if (rating2 < rating1) { + return -1; + } else { + return 0; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCacheRatingComparator.compare: " + e.toString()); + } + return 0; + } +} diff --git a/src/cgeo/geocaching/cgCacheSizeComparator.java b/src/cgeo/geocaching/cgCacheSizeComparator.java new file mode 100644 index 0000000..a5d0298 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheSizeComparator.java @@ -0,0 +1,47 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; +import java.util.ArrayList; + +public class cgCacheSizeComparator implements Comparator<cgCache> { + public static ArrayList<String> cacheSizes = new ArrayList<String>(); + + public cgCacheSizeComparator() { + // list sizes + cacheSizes.add("micro"); + cacheSizes.add("small"); + cacheSizes.add("regular"); + cacheSizes.add("large"); + } + + public int compare(cgCache cache1, cgCache cache2) { + try { + if (cache1.size == null || cache1.size.length() == 0 || cache2.size == null || cache2.size.length() == 0) { + return 0; + } + + int size1 = 0; + int size2 = 0; + + int cnt = 1; + for (String size : cacheSizes) { + if (size.equalsIgnoreCase(cache1.size)) size1 = cnt; + if (size.equalsIgnoreCase(cache2.size)) size2 = cnt; + + cnt ++; + } + + if (size1 < size2) { + return 1; + } else if (size2 < size1) { + return -1; + } else { + return 0; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCacheSizeComparator.compare: " + e.toString()); + } + return 0; + } +} diff --git a/src/cgeo/geocaching/cgCacheTerrainComparator.java b/src/cgeo/geocaching/cgCacheTerrainComparator.java new file mode 100644 index 0000000..3d31a37 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheTerrainComparator.java @@ -0,0 +1,26 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; + +public class cgCacheTerrainComparator implements Comparator<cgCache> { + + public int compare(cgCache cache1, cgCache cache2) { + try { + if (cache1.terrain == null || cache2.terrain == null) { + return 0; + } + + if (cache1.terrain > cache2.terrain) { + return 1; + } else if (cache2.terrain > cache1.terrain) { + return -1; + } else { + return 0; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCacheTerrainComparator.compare: " + e.toString()); + } + return 0; + } +} diff --git a/src/cgeo/geocaching/cgCacheView.java b/src/cgeo/geocaching/cgCacheView.java new file mode 100644 index 0000000..6705729 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheView.java @@ -0,0 +1,29 @@ +package cgeo.geocaching; + +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +public class cgCacheView { + // layouts & views + public RelativeLayout oneCache; + public RelativeLayout oneInfo; + public RelativeLayout oneCheckbox; + public CheckBox checkbox; + public ImageView foundMark; + public ImageView offlineMark; + public TextView text; + public TextView favourite; + public TextView info; + public RelativeLayout inventory; + public RelativeLayout directionLayout; + public cgDistanceView distance; + public cgCompassMini direction; + public RelativeLayout dirImgLayout; + public ImageView dirImg; + + // status + public float startX = -1; + public float prevX = -1; +} diff --git a/src/cgeo/geocaching/cgCacheVisitComparator.java b/src/cgeo/geocaching/cgCacheVisitComparator.java new file mode 100644 index 0000000..19a4b52 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheVisitComparator.java @@ -0,0 +1,27 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; + +public class cgCacheVisitComparator implements Comparator<cgCache> { + + public int compare(cgCache cache1, cgCache cache2) { + try { + if (cache1.visitedDate == null || cache1.visitedDate <= 0 || cache2.visitedDate == null || cache2.visitedDate <= 0) { + return 0; + } + + if (cache1.visitedDate > cache2.visitedDate) { + return -1; + } else if (cache1.visitedDate < cache2.visitedDate) { + return 1; + } else { + return 0; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCacheVisitComparator.compare: " + e.toString()); + } + + return 0; + } +} diff --git a/src/cgeo/geocaching/cgCacheVoteComparator.java b/src/cgeo/geocaching/cgCacheVoteComparator.java new file mode 100644 index 0000000..581656c --- /dev/null +++ b/src/cgeo/geocaching/cgCacheVoteComparator.java @@ -0,0 +1,38 @@ +package cgeo.geocaching; + +import java.util.Comparator; +import android.util.Log; + +/** + * sorts caches by the users own voting (if available at all) + * @author bananeweizen + * + */ +public class cgCacheVoteComparator implements Comparator<cgCache> { + + public int compare(cgCache cache1, cgCache cache2) { + try { + // if there is no vote available, put that cache at the end of the list + float vote1 = 0; + if (cache1.myVote != null) { + vote1 = cache1.myVote; + } + + float vote2 = 0; + if (cache2.myVote != null) { + vote2 = cache2.myVote; + } + + if (vote1 < vote2) { + return 1; + } else if (vote2 < vote1) { + return -1; + } else { + return 0; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCacheVoteComparator.compare: " + e.toString()); + } + return 0; + } +} diff --git a/src/cgeo/geocaching/cgCacheWrap.java b/src/cgeo/geocaching/cgCacheWrap.java new file mode 100644 index 0000000..23d77f5 --- /dev/null +++ b/src/cgeo/geocaching/cgCacheWrap.java @@ -0,0 +1,12 @@ +package cgeo.geocaching; + +import java.util.ArrayList; + +public class cgCacheWrap { + public String error = null; + public String url = ""; + public String viewstate = ""; + public String viewstate1 = ""; + public int totalCnt = 0; + public ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgCompass.java b/src/cgeo/geocaching/cgCompass.java new file mode 100644 index 0000000..9d4473e --- /dev/null +++ b/src/cgeo/geocaching/cgCompass.java @@ -0,0 +1,322 @@ +package cgeo.geocaching; + +import android.util.AttributeSet; +import android.view.View; +import android.content.Context; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.Canvas; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +public class cgCompass extends View { + + private changeThread watchdog = null; + private boolean wantStop = false; + private boolean lock = false; + private boolean drawing = false; + private Context context = null; + private Bitmap compassUnderlay = null; + private Bitmap compassRose = null; + private Bitmap compassArrow = null; + private Bitmap compassOverlay = null; + private Double azimuth = new Double(0); + private Double heading = new Double(0); + private Double cacheHeading = new Double(0); + private Double northHeading = new Double(0); + private PaintFlagsDrawFilter setfil = null; + private PaintFlagsDrawFilter remfil = null; + private int compassUnderlayWidth = 0; + private int compassUnderlayHeight = 0; + private int compassRoseWidth = 0; + private int compassRoseHeight = 0; + private int compassArrowWidth = 0; + private int compassArrowHeight = 0; + private int compassOverlayWidth = 0; + private int compassOverlayHeight = 0; + private Handler changeHandler = new Handler() { + + @Override + public void handleMessage(Message message) { + try { + invalidate(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgCompass.changeHandler: " + e.toString()); + } + } + }; + + public cgCompass(Context contextIn) { + super(contextIn); + context = contextIn; + } + + public cgCompass(Context contextIn, AttributeSet attrs) { + super(contextIn, attrs); + context = contextIn; + } + + @Override + public void onAttachedToWindow() { + compassUnderlay = BitmapFactory.decodeResource(context.getResources(), R.drawable.compass_underlay); + compassRose = BitmapFactory.decodeResource(context.getResources(), R.drawable.compass_rose); + compassArrow = BitmapFactory.decodeResource(context.getResources(), R.drawable.compass_arrow); + compassOverlay = BitmapFactory.decodeResource(context.getResources(), R.drawable.compass_overlay); + + compassUnderlayWidth = compassUnderlay.getWidth(); + compassUnderlayHeight = compassUnderlay.getWidth(); + compassRoseWidth = compassRose.getWidth(); + compassRoseHeight = compassRose.getWidth(); + compassArrowWidth = compassArrow.getWidth(); + compassArrowHeight = compassArrow.getWidth(); + compassOverlayWidth = compassOverlay.getWidth(); + compassOverlayHeight = compassOverlay.getWidth(); + + setfil = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG); + remfil = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0); + + wantStop = false; + + watchdog = new changeThread(changeHandler); + watchdog.start(); + } + + @Override + public void onDetachedFromWindow() { + wantStop = true; + + if (compassUnderlay != null) { + compassUnderlay.recycle(); + } + + if (compassRose != null) { + compassRose.recycle(); + } + + if (compassArrow != null) { + compassArrow.recycle(); + } + + if (compassOverlay != null) { + compassOverlay.recycle(); + } + } + + protected void updateNorth(Double northHeadingIn, Double cacheHeadingIn) { + northHeading = northHeadingIn; + cacheHeading = cacheHeadingIn; + } + + private class changeThread extends Thread { + + Handler handler = null; + + public changeThread(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + while (wantStop == false) { + try { + sleep(50); + } catch (Exception e) { + // nothing + } + + if (Math.abs(azimuth - northHeading) < 2 && Math.abs(heading - cacheHeading) < 2) { + continue; + } + + lock = true; + + Double diff = new Double(0); + Double diffAbs = new Double(0); + Double tempAzimuth = new Double(0); + Double tempHeading = new Double(0); + + Double actualAzimuth = azimuth; + Double actualHeading = heading; + + diff = northHeading - actualAzimuth; + diffAbs = Math.abs(northHeading - actualAzimuth); + if (diff < 0) { + diff = diff + 360; + } else if (diff >= 360) { + diff = diff - 360; + } + + if (diff > 0 && diff <= 180) { + if (diffAbs > 5) { + tempAzimuth = actualAzimuth + 2; + } else if (diffAbs > 1) { + tempAzimuth = actualAzimuth + 1; + } else { + tempAzimuth = actualAzimuth; + } + } else if (diff > 180 && diff < 360) { + if (diffAbs > 5) { + tempAzimuth = actualAzimuth - 2; + } else if (diffAbs > 1) { + tempAzimuth = actualAzimuth - 1; + } else { + tempAzimuth = actualAzimuth; + } + } else { + tempAzimuth = actualAzimuth; + } + + diff = cacheHeading - actualHeading; + diffAbs = Math.abs(cacheHeading - actualHeading); + if (diff < 0) { + diff = diff + 360; + } else if (diff >= 360) { + diff = diff - 360; + } + + if (diff > 0 && diff <= 180) { + if (diffAbs > 5) { + tempHeading = actualHeading + 2; + } else if (diffAbs > 1) { + tempHeading = actualHeading + 1; + } else { + tempHeading = actualHeading; + } + } else if (diff > 180 && diff < 360) { + if (diffAbs > 5) { + tempHeading = actualHeading - 2; + } else if (diffAbs > 1) { + tempHeading = actualHeading - 1; + } else { + tempHeading = actualHeading; + } + } else { + tempHeading = actualHeading; + } + + if (tempAzimuth >= 360) { + tempAzimuth = tempAzimuth - 360; + } else if (tempAzimuth < 0) { + tempAzimuth = tempAzimuth + 360; + } + + if (tempHeading >= 360) { + tempHeading = tempHeading - 360; + } else if (tempHeading < 0) { + tempHeading = tempHeading + 360; + } + + azimuth = tempAzimuth; + heading = tempHeading; + + lock = false; + + changeHandler.sendMessage(new Message()); + } + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (lock == true) { + return; + } + if (drawing == true) { + return; + } + + Double azimuthTemp = azimuth; + Double azimuthRelative = azimuthTemp - heading; + if (azimuthRelative < 0) { + azimuthRelative = azimuthRelative + 360; + } else if (azimuthRelative >= 360) { + azimuthRelative = azimuthRelative - 360; + } + + // compass margins + int canvasCenterX = (compassRoseWidth / 2) + ((getWidth() - compassRoseWidth) / 2); + int canvasCenterY = (compassRoseHeight / 2) + ((getHeight() - compassRoseHeight) / 2); + + int marginLeftTemp = 0; + int marginTopTemp = 0; + + drawing = true; + super.onDraw(canvas); + + canvas.save(); + canvas.setDrawFilter(setfil); + + marginLeftTemp = (getWidth() - compassUnderlayWidth) / 2; + marginTopTemp = (getHeight() - compassUnderlayHeight) / 2; + + canvas.drawBitmap(compassUnderlay, marginLeftTemp, marginTopTemp, null); + + marginLeftTemp = (getWidth() - compassRoseWidth) / 2; + marginTopTemp = (getHeight() - compassRoseHeight) / 2; + + canvas.rotate(new Float(-(azimuthTemp)), canvasCenterX, canvasCenterY); + canvas.drawBitmap(compassRose, marginLeftTemp, marginTopTemp, null); + canvas.rotate(new Float(azimuthTemp), canvasCenterX, canvasCenterY); + + marginLeftTemp = (getWidth() - compassArrowWidth) / 2; + marginTopTemp = (getHeight() - compassArrowHeight) / 2; + + canvas.rotate(new Float(-(azimuthRelative)), canvasCenterX, canvasCenterY); + canvas.drawBitmap(compassArrow, marginLeftTemp, marginTopTemp, null); + canvas.rotate(new Float(azimuthRelative), canvasCenterX, canvasCenterY); + + marginLeftTemp = (getWidth() - compassOverlayWidth) / 2; + marginTopTemp = (getHeight() - compassOverlayHeight) / 2; + + canvas.drawBitmap(compassOverlay, marginLeftTemp, marginTopTemp, null); + + canvas.setDrawFilter(remfil); + canvas.restore(); + + drawing = false; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); + } + + private int measureWidth(int measureSpec) { + int result = 0; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if (specMode == MeasureSpec.EXACTLY) { + result = specSize; + } else { + result = compassArrow.getWidth() + getPaddingLeft() + getPaddingRight(); + + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + + return result; + } + + private int measureHeight(int measureSpec) { + int result = 0; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if (specMode == MeasureSpec.EXACTLY) { + result = specSize; + } else { + result = compassArrow.getHeight() + getPaddingTop() + getPaddingBottom(); + + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + + return result; + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgCompassMini.java b/src/cgeo/geocaching/cgCompassMini.java new file mode 100644 index 0000000..cc815bf --- /dev/null +++ b/src/cgeo/geocaching/cgCompassMini.java @@ -0,0 +1,176 @@ +package cgeo.geocaching; + +import android.util.AttributeSet; +import android.view.View; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.Canvas; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +public class cgCompassMini extends View { + private int arrowSkin = R.drawable.compass_arrow_mini_white; + private Boolean lock = false; + private Context context = null; + private cgBase base = null; + private Double cacheLat = null; + private Double cacheLon = null; + private Bitmap compassArrow = null; + private Double azimuth = new Double(0); + private Double heading = new Double(0); + private PaintFlagsDrawFilter setfil = null; + private PaintFlagsDrawFilter remfil = null; + + public cgCompassMini(Context contextIn) { + super(contextIn); + context = contextIn; + } + + public cgCompassMini(Context contextIn, AttributeSet attrs) { + super(contextIn, attrs); + context = contextIn; + + TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.cgCompassMini); + int usedSkin = attributes.getInt(R.styleable.cgCompassMini_skin, 0); + if (usedSkin == 1) arrowSkin = R.drawable.compass_arrow_mini_black; + else arrowSkin = R.drawable.compass_arrow_mini_white; + } + + @Override + public void onAttachedToWindow() { + compassArrow = BitmapFactory.decodeResource(context.getResources(), arrowSkin); + + setfil = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG); + remfil = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0); + } + + @Override + public void onDetachedFromWindow() { + if (compassArrow != null) { + compassArrow.recycle(); + compassArrow = null; + } + } + + public void setContent(cgBase baseIn, Double cacheLatIn, Double cacheLonIn) { + base = baseIn; + cacheLat = cacheLatIn; + cacheLon = cacheLonIn; + } + + protected void updateAzimuth(Double azimuthIn) { + azimuth = azimuthIn; + + updateDirection(); + } + + protected void updateHeading(Double headingIn) { + heading = headingIn; + + updateDirection(); + } + + protected void updateCoords(Double latitudeIn, Double longitudeIn) { + if (latitudeIn == null || longitudeIn == null || cacheLat == null || cacheLon == null) { + return; + } + + heading = cgBase.getHeading(latitudeIn, longitudeIn, cacheLat, cacheLon); + + updateDirection(); + } + + protected void updateDirection() { + if (compassArrow == null || compassArrow.isRecycled() == true) { + return; + } + + // compass margins + int compassRoseWidth = compassArrow.getWidth(); + int compassRoseHeight = compassArrow.getWidth(); + int marginLeft = (getWidth() - compassRoseWidth) / 2; + int marginTop = (getHeight() - compassRoseHeight) / 2; + + invalidate(marginLeft, marginTop, (marginLeft + compassRoseWidth), (marginTop + compassRoseHeight)); + } + + @Override + protected void onDraw(Canvas canvas){ + lock = true; + + super.onDraw(canvas); + + Double azimuthRelative = azimuth - heading; + if (azimuthRelative < 0) { + azimuthRelative = azimuthRelative + 360; + } else if (azimuthRelative >= 360) { + azimuthRelative = azimuthRelative - 360; + } + + // compass margins + canvas.setDrawFilter(setfil); + + int marginLeft = 0; + int marginTop = 0; + + int compassArrowWidth = compassArrow.getWidth(); + int compassArrowHeight = compassArrow.getWidth(); + + int canvasCenterX = (compassArrowWidth / 2) + ((getWidth() - compassArrowWidth) / 2); + int canvasCenterY = (compassArrowHeight / 2) + ((getHeight() - compassArrowHeight) / 2); + + marginLeft = (getWidth() - compassArrowWidth) / 2; + marginTop = (getHeight() - compassArrowHeight) / 2; + + canvas.rotate(new Float(-(azimuthRelative)), canvasCenterX, canvasCenterY); + canvas.drawBitmap(compassArrow, marginLeft, marginTop, null); + canvas.rotate(new Float(azimuthRelative), canvasCenterX, canvasCenterY); + + canvas.setDrawFilter(remfil); + + lock = false; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); + } + + private int measureWidth(int measureSpec) { + int result = 0; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if (specMode == MeasureSpec.EXACTLY) { + result = specSize; + } else { + result = 21 + getPaddingLeft() + getPaddingRight(); + + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + + return result; + } + + private int measureHeight(int measureSpec) { + int result = 0; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if (specMode == MeasureSpec.EXACTLY) { + result = specSize; + } else { + result = 21 + getPaddingTop() + getPaddingBottom(); + + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + + return result; + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgCoord.java b/src/cgeo/geocaching/cgCoord.java new file mode 100644 index 0000000..4e7d3b2 --- /dev/null +++ b/src/cgeo/geocaching/cgCoord.java @@ -0,0 +1,46 @@ +package cgeo.geocaching; + +public class cgCoord { + + public Integer id = null; + public String geocode = ""; + public String type = "cache"; + public String typeSpec = "traditional"; + public String name = ""; + public boolean found = false; + public boolean disabled = false; + public Double latitude = new Double(0); + public Double longitude = new Double(0); + public Float difficulty = null; + public Float terrain = null; + public String size = null; + + public cgCoord() { + } + + public cgCoord(cgCache cache) { + disabled = cache.disabled; + found = cache.found; + geocode = cache.geocode; + latitude = cache.latitude; + longitude = cache.longitude; + name = cache.name; + type = "cache"; + typeSpec = cache.type; + difficulty = cache.difficulty; + terrain = cache.terrain; + size = cache.size; + } + + public cgCoord(cgWaypoint waypoint) { + id = waypoint.id; + disabled = false; + found = false; + geocode = ""; + latitude = waypoint.latitude; + longitude = waypoint.longitude; + name = waypoint.name; + type = "waypoint"; + typeSpec = waypoint.type; + } +} diff --git a/src/cgeo/geocaching/cgData.java b/src/cgeo/geocaching/cgData.java new file mode 100644 index 0000000..0338cf7 --- /dev/null +++ b/src/cgeo/geocaching/cgData.java @@ -0,0 +1,3046 @@ +package cgeo.geocaching; + +import android.content.ContentValues; +import android.content.Context; +import android.content.res.Resources; +import android.database.Cursor; +import android.util.Log; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteStatement; +import android.os.Environment; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map.Entry; +import java.util.Set; + +public class cgData { + + public cgCacheWrap caches; + private Context context = null; + private String path = null; + private cgDbHelper dbHelper = null; + private SQLiteDatabase databaseRO = null; + private SQLiteDatabase databaseRW = null; + private static final int dbVersion = 51; + private static final String dbName = "data"; + private static final String dbTableCaches = "cg_caches"; + private static final String dbTableLists = "cg_lists"; + private static final String dbTableAttributes = "cg_attributes"; + private static final String dbTableWaypoints = "cg_waypoints"; + private static final String dbTableSpoilers = "cg_spoilers"; + private static final String dbTableLogs = "cg_logs"; + private static final String dbTableLogCount = "cg_logCount"; + private static final String dbTableLogsOffline = "cg_logs_offline"; + private static final String dbTableTrackables = "cg_trackables"; + private static final String dbCreateCaches = "" + + "create table " + dbTableCaches + " (" + + "_id integer primary key autoincrement, " + + "updated long not null, " + + "detailed integer not null default 0, " + + "detailedupdate long, " + + "visiteddate long, " + + "geocode text unique not null, " + + "reason integer not null default 0, " // cached, favourite... + + "cacheid text, " + + "guid text, " + + "type text, " + + "name text, " + + "own integer not null default 0, " + + "owner text, " + + "owner_real text, " + + "hidden long, " + + "hint text, " + + "size text, " + + "difficulty float, " + + "terrain float, " + + "latlon text, " + + "latitude_string text, " + + "longitude_string text, " + + "location text, " + + "direction double, " + + "distance double, " + + "latitude double, " + + "longitude double, " + + "reliable_latlon integer, " + + "elevation double, " + + "shortdesc text, " + + "description text, " + + "favourite_cnt integer, " + + "rating float, " + + "votes integer, " + + "myvote float, " + + "disabled integer not null default 0, " + + "archived integer not null default 0, " + + "members integer not null default 0, " + + "found integer not null default 0, " + + "favourite integer not null default 0, " + + "inventorycoins integer default 0, " + + "inventorytags integer default 0, " + + "inventoryunknown integer default 0 " + + "); "; + private static final String dbCreateLists = "" + + "create table " + dbTableLists + " (" + + "_id integer primary key autoincrement, " + + "title text not null, " + + "updated long not null, " + + "latitude double, " + + "longitude double " + + "); "; + private static final String dbCreateAttributes = "" + + "create table " + dbTableAttributes + " (" + + "_id integer primary key autoincrement, " + + "geocode text not null, " + + "updated long not null, " // date of save + + "attribute text " + + "); "; + private static final String dbCreateWaypoints = "" + + "create table " + dbTableWaypoints + " (" + + "_id integer primary key autoincrement, " + + "geocode text not null, " + + "updated long not null, " // date of save + + "type text not null default 'waypoint', " + + "prefix text, " + + "lookup text, " + + "name text, " + + "latlon text, " + + "latitude_string text, " + + "longitude_string text, " + + "latitude double, " + + "longitude double, " + + "note text " + + "); "; + private static final String dbCreateSpoilers = "" + + "create table " + dbTableSpoilers + " (" + + "_id integer primary key autoincrement, " + + "geocode text not null, " + + "updated long not null, " // date of save + + "url text, " + + "title text, " + + "description text " + + "); "; + private static final String dbCreateLogs = "" + + "create table " + dbTableLogs + " (" + + "_id integer primary key autoincrement, " + + "geocode text not null, " + + "updated long not null, " // date of save + + "type integer not null default 4, " + + "author text, " + + "log text, " + + "date long, " + + "found integer not null default 0 " + + "); "; + private static final String dbCreateLogCount = "" + + "create table " + dbTableLogCount + " (" + + "_id integer primary key autoincrement, " + + "geocode text not null, " + + "updated long not null, " // date of save + + "type integer not null default 4, " + + "count integer not null default 0 " + + "); "; + private static final String dbCreateLogsOffline = "" + + "create table " + dbTableLogsOffline + " (" + + "_id integer primary key autoincrement, " + + "geocode text not null, " + + "updated long not null, " // date of save + + "type integer not null default 4, " + + "log text, " + + "date long " + + "); "; + private static final String dbCreateTrackables = "" + + "create table " + dbTableTrackables + " (" + + "_id integer primary key autoincrement, " + + "updated long not null, " // date of save + + "tbcode text not null, " + + "guid text, " + + "title text, " + + "owner text, " + + "released long, " + + "goal text, " + + "description text, " + + "geocode text " + + "); "; + public boolean initialized = false; + + public cgData(Context contextIn) { + context = contextIn; + } + + public void init() { + if (databaseRW == null || databaseRW.isOpen() == false) { + try { + if (dbHelper == null) { + dbHelper = new cgDbHelper(context); + } + databaseRW = dbHelper.getWritableDatabase(); + + if (databaseRW != null && databaseRW.isOpen()) { + Log.i(cgSettings.tag, "Connection to RW database established."); + } else { + Log.e(cgSettings.tag, "Failed to open connection to RW database."); + } + + if (databaseRW.inTransaction() == true) { + databaseRW.endTransaction(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.openDb.RW: " + e.toString()); + } + } + + if (databaseRO == null || databaseRO.isOpen() == false) { + try { + if (dbHelper == null) { + dbHelper = new cgDbHelper(context); + } + databaseRO = dbHelper.getReadableDatabase(); + + if (databaseRO.needUpgrade(dbVersion) == true) { + databaseRO = null; + } + + if (databaseRO != null && databaseRO.isOpen()) { + Log.i(cgSettings.tag, "Connection to RO database established."); + } else { + Log.e(cgSettings.tag, "Failed to open connection to RO database."); + } + + if (databaseRO.inTransaction() == true) { + databaseRO.endTransaction(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.openDb.RO: " + e.toString()); + } + } + + initialized = true; + } + + public void closeDb() { + if (databaseRO != null) { + path = databaseRO.getPath(); + + if (databaseRO.inTransaction() == true) { + databaseRO.endTransaction(); + } + + databaseRO.close(); + databaseRO = null; + SQLiteDatabase.releaseMemory(); + + Log.d(cgSettings.tag, "Closing RO database"); + } + + if (databaseRW != null) { + path = databaseRW.getPath(); + + if (databaseRW.inTransaction() == true) { + databaseRW.endTransaction(); + } + + databaseRW.close(); + databaseRW = null; + SQLiteDatabase.releaseMemory(); + + Log.d(cgSettings.tag, "Closing RW database"); + } + + if (dbHelper != null) { + dbHelper.close(); + dbHelper = null; + } + } + + public String backupDatabase() { + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) == false) { + Log.w(cgSettings.tag, "Database wasn't backed up: no external memory"); + + return null; + } + + closeDb(); + + try { + final String directoryImg = cgSettings.cache; + final String directoryTarget = Environment.getExternalStorageDirectory() + "/" + directoryImg + "/"; + final String fileTarget = directoryTarget + "cgeo.sqlite"; + final String fileSource = path; + + File directoryTargetFile = new File(directoryTarget); + if (directoryTargetFile.exists() == false) { + directoryTargetFile.mkdir(); + } + + InputStream input = new FileInputStream(fileSource); + OutputStream output = new FileOutputStream(fileTarget); + + byte[] buffer = new byte[1024]; + int length; + while ((length = input.read(buffer)) > 0) { + output.write(buffer, 0, length); + } + + output.flush(); + output.close(); + input.close(); + + Log.i(cgSettings.tag, "Database was copied to " + fileTarget); + + init(); + + return fileTarget; + } catch (Exception e) { + Log.w(cgSettings.tag, "Database wasn't backed up: " + e.toString()); + } + + init(); + + return null; + } + + public File isRestoreFile() { + final String directoryImg = cgSettings.cache; + final String fileSource = Environment.getExternalStorageDirectory() + "/" + directoryImg + "/cgeo.sqlite"; + + File fileSourceFile = new File(fileSource); + if (fileSourceFile.exists()) { + return fileSourceFile; + } else { + return null; + } + } + + public boolean restoreDatabase() { + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) == false) { + Log.w(cgSettings.tag, "Database wasn't restored: no external memory"); + + return false; + } + + closeDb(); + + try { + final String directoryImg = cgSettings.cache; + final String fileSource = Environment.getExternalStorageDirectory() + "/" + directoryImg + "/cgeo.sqlite"; + final String fileTarget = path; + + File fileSourceFile = new File(fileSource); + if (fileSourceFile.exists() == false) { + Log.w(cgSettings.tag, "Database backup was not found"); + + init(); + + return false; + } + + InputStream input = new FileInputStream(fileSource); + OutputStream output = new FileOutputStream(fileTarget); + + byte[] buffer = new byte[1024]; + int length; + while ((length = input.read(buffer)) > 0) { + output.write(buffer, 0, length); + } + + output.flush(); + output.close(); + input.close(); + + Log.i(cgSettings.tag, "Database was restored"); + + init(); + + return true; + } catch (Exception e) { + Log.w(cgSettings.tag, "Database wasn't restored: " + e.toString()); + } + + init(); + + return false; + } + + private class cgDbHelper extends SQLiteOpenHelper { + + cgDbHelper(Context context) { + super(context, dbName, null, dbVersion); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(dbCreateCaches); + db.execSQL(dbCreateLists); + db.execSQL(dbCreateAttributes); + db.execSQL(dbCreateWaypoints); + db.execSQL(dbCreateSpoilers); + db.execSQL(dbCreateLogs); + db.execSQL(dbCreateLogCount); + db.execSQL(dbCreateLogsOffline); + db.execSQL(dbCreateTrackables); + + db.execSQL("create index if not exists in_a on " + dbTableCaches + " (geocode)"); + db.execSQL("create index if not exists in_b on " + dbTableCaches + " (guid)"); + db.execSQL("create index if not exists in_c on " + dbTableCaches + " (reason)"); + db.execSQL("create index if not exists in_d on " + dbTableCaches + " (detailed)"); + db.execSQL("create index if not exists in_e on " + dbTableCaches + " (type)"); + db.execSQL("create index if not exists in_f on " + dbTableCaches + " (visiteddate, detailedupdate)"); + db.execSQL("create index if not exists in_a on " + dbTableAttributes + " (geocode)"); + db.execSQL("create index if not exists in_a on " + dbTableWaypoints + " (geocode)"); + db.execSQL("create index if not exists in_b on " + dbTableWaypoints + " (geocode, type)"); + db.execSQL("create index if not exists in_a on " + dbTableSpoilers + " (geocode)"); + db.execSQL("create index if not exists in_a on " + dbTableLogs + " (geocode)"); + db.execSQL("create index if not exists in_a on " + dbTableLogCount + " (geocode)"); + db.execSQL("create index if not exists in_a on " + dbTableLogsOffline + " (geocode)"); + db.execSQL("create index if not exists in_a on " + dbTableTrackables + " (geocode)"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + Log.i(cgSettings.tag, "Upgrade database from ver. " + oldVersion + " to ver. " + newVersion + ": start"); + + try { + if (db.isReadOnly() == true) { + return; + } + + db.beginTransaction(); + + if (oldVersion <= 0) { // new table + dropDatabase(db); + onCreate(db); + + Log.i(cgSettings.tag, "Database structure created."); + } + + if (oldVersion > 0) { + db.execSQL("delete from " + dbTableCaches + " where reason = 0"); + + if (oldVersion < 34) { // upgrade to 34 + try { + db.execSQL("create index if not exists in_a on " + dbTableCaches + " (geocode)"); + db.execSQL("create index if not exists in_b on " + dbTableCaches + " (guid)"); + db.execSQL("create index if not exists in_c on " + dbTableCaches + " (reason)"); + db.execSQL("create index if not exists in_d on " + dbTableCaches + " (detailed)"); + db.execSQL("create index if not exists in_e on " + dbTableCaches + " (type)"); + db.execSQL("create index if not exists in_a on " + dbTableAttributes + " (geocode)"); + db.execSQL("create index if not exists in_a on " + dbTableWaypoints + " (geocode)"); + db.execSQL("create index if not exists in_b on " + dbTableWaypoints + " (geocode, type)"); + db.execSQL("create index if not exists in_a on " + dbTableSpoilers + " (geocode)"); + db.execSQL("create index if not exists in_a on " + dbTableLogs + " (geocode)"); + db.execSQL("create index if not exists in_a on " + dbTableTrackables + " (geocode)"); + + Log.i(cgSettings.tag, "Indexes added."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 34: " + e.toString()); + } + } + + if (oldVersion < 37) { // upgrade to 37 + try { + db.execSQL("alter table " + dbTableCaches + " add column direction text"); + db.execSQL("alter table " + dbTableCaches + " add column distance double"); + + Log.i(cgSettings.tag, "Columns direction and distance added to " + dbTableCaches + "."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 37: " + e.toString()); + } + } + + if (oldVersion < 38) { // upgrade to 38 + try { + db.execSQL("drop table " + dbTableLogs); + db.execSQL(dbCreateLogs); + + Log.i(cgSettings.tag, "Changed type column in " + dbTableLogs + " to integer."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 38: " + e.toString()); + } + } + + if (oldVersion < 39) { // upgrade to 39 + try { + db.execSQL(dbCreateLists); + + Log.i(cgSettings.tag, "Created lists table."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 39: " + e.toString()); + } + } + + if (oldVersion < 40) { // upgrade to 40 + try { + db.execSQL("drop table " + dbTableTrackables); + db.execSQL(dbCreateTrackables); + + Log.i(cgSettings.tag, "Changed type of geocode column in trackables table."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 40: " + e.toString()); + } + } + + if (oldVersion < 41) { // upgrade to 41 + try { + db.execSQL("alter table " + dbTableCaches + " add column rating float"); + db.execSQL("alter table " + dbTableCaches + " add column votes integer"); + db.execSQL("alter table " + dbTableCaches + " add column vote integer"); + + Log.i(cgSettings.tag, "Added columns for GCvote."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 41: " + e.toString()); + } + } + + if (oldVersion < 42) { // upgrade to 42 + try { + db.execSQL(dbCreateLogsOffline); + + Log.i(cgSettings.tag, "Added table for offline logs"); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 42: " + e.toString()); + } + } + + if (oldVersion < 43) { // upgrade to 43 + try { + final String dbCreateCachesTemp = "" + + "create temporary table " + dbTableCaches + "_temp (" + + "_id integer primary key autoincrement, " + + "updated long not null, " + + "detailed integer not null default 0, " + + "detailedupdate long, " + + "geocode text unique not null, " + + "reason integer not null default 0, " // cached, favourite... + + "cacheid text, " + + "guid text, " + + "type text, " + + "name text, " + + "owner text, " + + "hidden long, " + + "hint text, " + + "size text, " + + "difficulty float, " + + "terrain float, " + + "latlon text, " + + "latitude_string text, " + + "longitude_string text, " + + "location text, " + + "distance double, " + + "latitude double, " + + "longitude double, " + + "shortdesc text, " + + "description text, " + + "rating float, " + + "votes integer, " + + "vote integer, " + + "disabled integer not null default 0, " + + "archived integer not null default 0, " + + "members integer not null default 0, " + + "found integer not null default 0, " + + "favourite integer not null default 0, " + + "inventorycoins integer default 0, " + + "inventorytags integer default 0, " + + "inventoryunknown integer default 0 " + + "); "; + final String dbCreateCachesNew = "" + + "create table " + dbTableCaches + " (" + + "_id integer primary key autoincrement, " + + "updated long not null, " + + "detailed integer not null default 0, " + + "detailedupdate long, " + + "geocode text unique not null, " + + "reason integer not null default 0, " // cached, favourite... + + "cacheid text, " + + "guid text, " + + "type text, " + + "name text, " + + "owner text, " + + "hidden long, " + + "hint text, " + + "size text, " + + "difficulty float, " + + "terrain float, " + + "latlon text, " + + "latitude_string text, " + + "longitude_string text, " + + "location text, " + + "direction double, " + + "distance double, " + + "latitude double, " + + "longitude double, " + + "shortdesc text, " + + "description text, " + + "rating float, " + + "votes integer, " + + "vote integer, " + + "disabled integer not null default 0, " + + "archived integer not null default 0, " + + "members integer not null default 0, " + + "found integer not null default 0, " + + "favourite integer not null default 0, " + + "inventorycoins integer default 0, " + + "inventorytags integer default 0, " + + "inventoryunknown integer default 0 " + + "); "; + + db.beginTransaction(); + db.execSQL(dbCreateCachesTemp); + db.execSQL("insert into " + dbTableCaches + "_temp select _id, updated, detailed, detailedupdate, geocode, reason, cacheid, guid, type, name, owner, hidden, hint, size, difficulty, terrain, latlon, latitude_string, longitude_string, location, distance, latitude, longitude, shortdesc, description, rating, votes, vote, disabled, archived, members, found, favourite, inventorycoins, inventorytags, inventoryunknown from " + dbTableCaches); + db.execSQL("drop table " + dbTableCaches); + db.execSQL(dbCreateCachesNew); + db.execSQL("insert into " + dbTableCaches + " select _id, updated, detailed, detailedupdate, geocode, reason, cacheid, guid, type, name, owner, hidden, hint, size, difficulty, terrain, latlon, latitude_string, longitude_string, location, null, distance, latitude, longitude, shortdesc, description, rating, votes, vote, disabled, archived, members, found, favourite, inventorycoins, inventorytags, inventoryunknown from " + dbTableCaches + "_temp"); + db.execSQL("drop table " + dbTableCaches + "_temp"); + db.setTransactionSuccessful(); + + Log.i(cgSettings.tag, "Changed direction column"); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 43: " + e.toString()); + } finally { + db.endTransaction(); + } + } + + if (oldVersion < 44) { // upgrade to 44 + try { + db.execSQL("alter table " + dbTableCaches + " add column favourite_cnt integer"); + + Log.i(cgSettings.tag, "Column favourite_cnt added to " + dbTableCaches + "."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 44: " + e.toString()); + } + } + + if (oldVersion < 45) { // upgrade to 45 + try { + db.execSQL("alter table " + dbTableCaches + " add column owner_real text"); + + Log.i(cgSettings.tag, "Column owner_real added to " + dbTableCaches + "."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 45: " + e.toString()); + } + } + + if (oldVersion < 46) { // upgrade to 46 + try { + db.execSQL("alter table " + dbTableCaches + " add column visiteddate long"); + db.execSQL("create index if not exists in_f on " + dbTableCaches + " (visiteddate, detailedupdate)"); + + Log.i(cgSettings.tag, "Added column for date of visit."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 46: " + e.toString()); + } + } + if (oldVersion < 47) { // upgrade to 47 + try { + db.execSQL("alter table " + dbTableCaches + " add column own integer not null default 0"); + + Log.i(cgSettings.tag, "Added column own."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 47: " + e.toString()); + } + } + + if (oldVersion < 48) { // upgrade to 48 + try { + db.execSQL("alter table " + dbTableCaches + " add column elevation double"); + + Log.i(cgSettings.tag, "Column elevation added to " + dbTableCaches + "."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 48: " + e.toString()); + } + } + + if (oldVersion < 49) { // upgrade to 49 + try { + db.execSQL(dbCreateLogCount); + + Log.i(cgSettings.tag, "Created table " + dbTableLogCount + "."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 49: " + e.toString()); + } + } + + if (oldVersion < 50) { // upgrade to 50 + try { + db.execSQL("alter table " + dbTableCaches + " add column myvote float"); + + Log.i(cgSettings.tag, "Added float column for votes to " + dbTableCaches + "."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 50: " + e.toString()); + } + } + + if (oldVersion < 51) { // upgrade to 51 + try { + db.execSQL("alter table " + dbTableCaches + " add column reliable_latlon integer"); + + Log.i(cgSettings.tag, "Column reliable_latlon added to " + dbTableCaches + "."); + } catch (Exception e) { + Log.e(cgSettings.tag, "Failed to upgrade to ver. 51: " + e.toString()); + } + } + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + Log.i(cgSettings.tag, "Upgrade database from ver. " + oldVersion + " to ver. " + newVersion + ": completed"); + } + } + + private static void dropDatabase(SQLiteDatabase db) { + db.execSQL("drop table if exists " + dbTableCaches); + db.execSQL("drop table if exists " + dbTableAttributes); + db.execSQL("drop table if exists " + dbTableWaypoints); + db.execSQL("drop table if exists " + dbTableSpoilers); + db.execSQL("drop table if exists " + dbTableLogs); + db.execSQL("drop table if exists " + dbTableLogCount); + db.execSQL("drop table if exists " + dbTableLogsOffline); + db.execSQL("drop table if exists " + dbTableTrackables); + } + + public String[] allDetailedThere() { + init(); + + Cursor cursor = null; + ArrayList<String> thereA = new ArrayList<String>(); + + try { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "geocode"}, + "(detailed = 1 and detailedupdate > " + (System.currentTimeMillis() - (3 * 24 * 60 * 60 * 1000)) + ") or reason > 0", + null, + null, + null, + "detailedupdate desc", + "100"); + + if (cursor != null) { + int index = 0; + String geocode = null; + + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + index = cursor.getColumnIndex("geocode"); + geocode = (String) cursor.getString(index); + + thereA.add(geocode); + } while (cursor.moveToNext()); + } else { + if (cursor != null) { + cursor.close(); + } + + return null; + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.allDetailedThere: " + e.toString()); + } + + if (cursor != null) { + cursor.close(); + } + + return thereA.toArray(new String[thereA.size()]); + } + + public boolean isThere(String geocode, String guid, boolean detailed, boolean checkTime) { + init(); + + Cursor cursor = null; + + int cnt = 0; + long dataUpdated = 0; + long dataDetailedUpdate = 0; + int dataDetailed = 0; + + try { + if (geocode != null && geocode.length() > 0) { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "detailed", "detailedupdate", "updated"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + null, + "1"); + } else if (guid != null && guid.length() > 0) { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "detailed", "detailedupdate", "updated"}, + "guid = \"" + guid + "\"", + null, + null, + null, + null, + "1"); + } else { + return false; + } + + if (cursor != null) { + int index = 0; + cnt = cursor.getCount(); + + if (cnt > 0) { + cursor.moveToFirst(); + + index = cursor.getColumnIndex("updated"); + dataUpdated = (long) cursor.getLong(index); + index = cursor.getColumnIndex("detailedupdate"); + dataDetailedUpdate = (long) cursor.getLong(index); + index = cursor.getColumnIndex("detailed"); + dataDetailed = (int) cursor.getInt(index); + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.isThere: " + e.toString()); + } + + if (cursor != null) { + cursor.close(); + } + + if (cnt > 0) { + if (detailed == true && dataDetailed == 0) { + // we want details, but these are not stored + return false; + } + + if (checkTime == true && detailed == true && dataDetailedUpdate < (System.currentTimeMillis() - (3 * 24 * 60 * 60 * 1000))) { + // we want to check time for detailed cache, but data are older than 3 hours + return false; + } + + if (checkTime == true && detailed == false && dataUpdated < (System.currentTimeMillis() - (3 * 24 * 60 * 60 * 1000))) { + // we want to check time for short cache, but data are older than 3 hours + return false; + } + + // we have some cache + return true; + } + + // we have no such cache stored in cache + return false; + } + + public boolean isOffline(String geocode, String guid) { + init(); + + Cursor cursor = null; + long reason = 0; + + try { + if (geocode != null && geocode.length() > 0) { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"reason"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + null, + "1"); + } else if (guid != null && guid.length() > 0) { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"reason"}, + "guid = \"" + guid + "\"", + null, + null, + null, + null, + "1"); + } else { + return false; + } + + if (cursor != null) { + final int cnt = cursor.getCount(); + int index = 0; + + if (cnt > 0) { + cursor.moveToFirst(); + + index = cursor.getColumnIndex("reason"); + reason = (long) cursor.getLong(index); + } + + cursor.close(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.isOffline: " + e.toString()); + } + + if (reason >= 1) { + return true; + } else { + return false; + } + } + + public boolean isReliableLatLon(String geocode, String guid) { + init(); + + Cursor cursor = null; + int rel = 0; + + try { + if (geocode != null && geocode.length() > 0) { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"reliable_latlon"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + null, + "1"); + } else if (guid != null && guid.length() > 0) { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"reliable_latlon"}, + "guid = \"" + guid + "\"", + null, + null, + null, + null, + "1"); + } else { + return false; + } + + if (cursor != null) { + final int cnt = cursor.getCount(); + int index = 0; + + if (cnt > 0) { + cursor.moveToFirst(); + + index = cursor.getColumnIndex("reliable_latlon"); + rel = (int) cursor.getInt(index); + } + + cursor.close(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.isOffline: " + e.toString()); + } + + if (rel >= 1) { + return true; + } else { + return false; + } + } + + public String getGeocodeForGuid(String guid) { + init(); + + if (guid == null || guid.length() == 0) { + return null; + } + + Cursor cursor = null; + String geocode = null; + + try { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "geocode"}, + "guid = \"" + guid + "\"", + null, + null, + null, + null, + "1"); + + if (cursor != null) { + int index = 0; + + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + index = cursor.getColumnIndex("geocode"); + geocode = (String) cursor.getString(index); + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.getGeocodeForGuid: " + e.toString()); + } + + if (cursor != null) { + cursor.close(); + } + + return geocode; + } + + public String getCacheidForGeocode(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return null; + } + + Cursor cursor = null; + String cacheid = null; + + try { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "cacheid"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + null, + "1"); + + if (cursor != null) { + int index = 0; + + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + index = cursor.getColumnIndex("cacheid"); + cacheid = (String) cursor.getString(index); + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.getCacheidForGeocode: " + e.toString()); + } + + if (cursor != null) { + cursor.close(); + } + + return cacheid; + } + + public boolean saveCache(cgCache cache) { + //LeeB - writing to the DB is slow + if (cache == null) { + return false; + } + + ContentValues values = new ContentValues(); + + if (cache.updated == null) { + values.put("updated", System.currentTimeMillis()); + } else { + values.put("updated", cache.updated); + } + values.put("reason", cache.reason); + if (cache.detailed == true) { + values.put("detailed", 1); + } else { + values.put("detailed", 0); + } + values.put("detailedupdate", cache.detailedUpdate); + values.put("visiteddate", cache.visitedDate); + values.put("geocode", cache.geocode); + values.put("cacheid", cache.cacheid); + values.put("guid", cache.guid); + values.put("type", cache.type); + values.put("name", cache.name); + if (cache.own == true) { + values.put("own", 1); + } else { + values.put("own", 0); + } + values.put("owner", cache.owner); + values.put("owner_real", cache.ownerReal); + if (cache.hidden == null) { + values.put("hidden", 0); + } else { + values.put("hidden", cache.hidden.getTime()); + } + values.put("hint", cache.hint); + values.put("size", cache.size); + values.put("difficulty", cache.difficulty); + values.put("terrain", cache.terrain); + values.put("latlon", cache.latlon); + values.put("latitude_string", cache.latitudeString); + values.put("longitude_string", cache.longitudeString); + values.put("location", cache.location); + values.put("distance", cache.distance); + values.put("direction", cache.direction); + // save coordinates + final boolean rel = isReliableLatLon(cache.geocode, cache.guid); + if (cache.reliableLatLon) { // new cache has reliable coordinates, store + values.put("latitude", cache.latitude); + values.put("longitude", cache.longitude); + values.put("reliable_latlon", 1); + } else if (!rel) { // new cache neither stored cache is not reliable, just update + values.put("latitude", cache.latitude); + values.put("longitude", cache.longitude); + values.put("reliable_latlon", 0); + } + values.put("elevation", cache.elevation); + values.put("shortdesc", cache.shortdesc); + values.put("description", cache.description); + values.put("favourite_cnt", cache.favouriteCnt); + values.put("rating", cache.rating); + values.put("votes", cache.votes); + values.put("myvote", cache.myVote); + if (cache.disabled == true) { + values.put("disabled", 1); + } else { + values.put("disabled", 0); + } + if (cache.archived == true) { + values.put("archived", 1); + } else { + values.put("archived", 0); + } + if (cache.members == true) { + values.put("members", 1); + } else { + values.put("members", 0); + } + if (cache.found == true) { + values.put("found", 1); + } else { + values.put("found", 0); + } + if (cache.favourite == true) { + values.put("favourite", 1); + } else { + values.put("favourite", 0); + } + values.put("inventoryunknown", cache.inventoryItems); + + boolean status = false; + boolean statusOk = true; + + if (cache.attributes != null) { + status = saveAttributes(cache.geocode, cache.attributes); + if (status == false) { + statusOk = false; + } + } + + if (cache.waypoints != null) { + status = saveWaypoints(cache.geocode, cache.waypoints, true); + if (status == false) { + statusOk = false; + } + } + + if (cache.spoilers != null) { + status = saveSpoilers(cache.geocode, cache.spoilers); + if (status == false) { + statusOk = false; + } + } + + if (cache.logs != null) { + status = saveLogs(cache.geocode, cache.logs); + if (status == false) { + statusOk = false; + } + } + + if (cache.logCounts != null && cache.logCounts.isEmpty() == false) { + status = saveLogCount(cache.geocode, cache.logCounts); + if (status == false) { + statusOk = false; + } + } + + if (cache.inventory != null) { + status = saveInventory(cache.geocode, cache.inventory); + if (status == false) { + statusOk = false; + } + } + + if (statusOk == false) { + cache.detailed = false; + cache.detailedUpdate = 0l; + } + + init(); + + //try to update record else insert fresh.. + try { + int rows = databaseRW.update(dbTableCaches, values, "geocode = \"" + cache.geocode + "\"", null); + if (rows > 0) { + values = null; + return true; + } + } catch (Exception e) { + // nothing + } + + try { + long id = databaseRW.insert(dbTableCaches, null, values); + if (id > 0) { + values = null; + return true; + } + } catch (Exception e) { + // nothing + } + + values = null; + + return false; + } + + public boolean saveAttributes(String geocode, ArrayList<String> attributes) { + init(); + + if (geocode == null || geocode.length() == 0 || attributes == null) { + return false; + } + + databaseRW.beginTransaction(); + try { + databaseRW.delete(dbTableAttributes, "geocode = \"" + geocode + "\"", null); + + if (!attributes.isEmpty()) { + ContentValues values = new ContentValues(); + for (String oneAttribute : attributes) { + values.clear(); + values.put("geocode", geocode); + values.put("updated", System.currentTimeMillis()); + values.put("attribute", oneAttribute); + + databaseRW.insert(dbTableAttributes, null, values); + } + } + databaseRW.setTransactionSuccessful(); + } finally { + databaseRW.endTransaction(); + } + + return true; + } + + public boolean saveWaypoints(String geocode, ArrayList<cgWaypoint> waypoints, boolean drop) { + init(); + + if (geocode == null || geocode.length() == 0 || waypoints == null) { + return false; + } + + boolean ok = false; + databaseRW.beginTransaction(); + try { + if (drop == true) { + databaseRW.delete(dbTableWaypoints, "geocode = \"" + geocode + "\" and type <> \"own\"", null); + } + + if (!waypoints.isEmpty()) { + ContentValues values = new ContentValues(); + for (cgWaypoint oneWaypoint : waypoints) { + if (oneWaypoint.type.equalsIgnoreCase("own") == true) { + continue; + } + + values.clear(); + values.put("geocode", geocode); + values.put("updated", System.currentTimeMillis()); + values.put("type", oneWaypoint.type); + values.put("prefix", oneWaypoint.prefix); + values.put("lookup", oneWaypoint.lookup); + values.put("name", oneWaypoint.name); + values.put("latlon", oneWaypoint.latlon); + values.put("latitude_string", oneWaypoint.latitudeString); + values.put("longitude_string", oneWaypoint.longitudeString); + values.put("latitude", oneWaypoint.latitude); + values.put("longitude", oneWaypoint.longitude); + values.put("note", oneWaypoint.note); + + databaseRW.insert(dbTableWaypoints, null, values); + } + } + + databaseRW.setTransactionSuccessful(); + ok = true; + } finally { + databaseRW.endTransaction(); + } + + return ok; + } + + public boolean saveOwnWaypoint(int id, String geocode, cgWaypoint waypoint) { + init(); + + if (((geocode == null || geocode.length() == 0) && id <= 0) || waypoint == null) { + return false; + } + + boolean ok = false; + databaseRW.beginTransaction(); + try { + ContentValues values = new ContentValues(); + values.put("geocode", geocode); + values.put("updated", System.currentTimeMillis()); + values.put("type", waypoint.type); + values.put("prefix", waypoint.prefix); + values.put("lookup", waypoint.lookup); + values.put("name", waypoint.name); + values.put("latlon", waypoint.latlon); + values.put("latitude_string", waypoint.latitudeString); + values.put("longitude_string", waypoint.longitudeString); + values.put("latitude", waypoint.latitude); + values.put("longitude", waypoint.longitude); + values.put("note", waypoint.note); + + if (id <= 0) { + databaseRW.insert(dbTableWaypoints, null, values); + ok = true; + } else { + final int rows = databaseRW.update(dbTableWaypoints, values, "_id = " + id, null); + if (rows > 0) { + ok = true; + } else { + ok = false; + } + } + databaseRW.setTransactionSuccessful(); + } finally { + databaseRW.endTransaction(); + } + + return ok; + } + + public boolean deleteWaypoint(int id) { + init(); + + if (id == 0) { + return false; + } + + int deleted = databaseRW.delete(dbTableWaypoints, "_id = " + id, null); + + if (deleted > 0) { + return true; + } + + return false; + } + + public boolean saveSpoilers(String geocode, ArrayList<cgSpoiler> spoilers) { + init(); + + if (geocode == null || geocode.length() == 0 || spoilers == null) { + return false; + } + + databaseRW.beginTransaction(); + try { + databaseRW.delete(dbTableSpoilers, "geocode = \"" + geocode + "\"", null); + + if (!spoilers.isEmpty()) { + ContentValues values = new ContentValues(); + for (cgSpoiler oneSpoiler : spoilers) { + values.clear(); + values.put("geocode", geocode); + values.put("updated", System.currentTimeMillis()); + values.put("url", oneSpoiler.url); + values.put("title", oneSpoiler.title); + values.put("description", oneSpoiler.description); + + databaseRW.insert(dbTableSpoilers, null, values); + } + } + databaseRW.setTransactionSuccessful(); + } finally { + databaseRW.endTransaction(); + } + + return true; + } + + public boolean saveLogs(String geocode, ArrayList<cgLog> logs) { + return saveLogs(geocode, logs, true); + } + + public boolean saveLogs(String geocode, ArrayList<cgLog> logs, boolean drop) { + init(); + + if (geocode == null || geocode.length() == 0 || logs == null) { + return false; + } + + databaseRW.beginTransaction(); + try { + if (drop == true) { + databaseRW.delete(dbTableLogs, "geocode = \"" + geocode + "\"", null); + } + + if (!logs.isEmpty()) { + ContentValues values = new ContentValues(); + for (cgLog oneLog : logs) { + values.clear(); + values.put("geocode", geocode); + values.put("updated", System.currentTimeMillis()); + values.put("type", oneLog.type); + values.put("author", oneLog.author); + values.put("log", oneLog.log); + values.put("date", oneLog.date); + values.put("found", oneLog.found); + + databaseRW.insert(dbTableLogs, null, values); + } + } + databaseRW.setTransactionSuccessful(); + } finally { + databaseRW.endTransaction(); + } + + return true; + } + + public boolean saveLogCount(String geocode, HashMap<Integer, Integer> logCounts) { + return saveLogCount(geocode, logCounts, true); + } + + public boolean saveLogCount(String geocode, HashMap<Integer, Integer> logCounts, boolean drop) { + init(); + + if (geocode == null || geocode.length() == 0 || logCounts == null || logCounts.isEmpty()) { + return false; + } + + databaseRW.beginTransaction(); + try { + if (drop == true) { + databaseRW.delete(dbTableLogCount, "geocode = \"" + geocode + "\"", null); + } + + ContentValues values = new ContentValues(); + + Set<Entry<Integer, Integer>> logCountsItems = logCounts.entrySet(); + for (Entry<Integer, Integer> pair : logCountsItems) { + values.clear(); + values.put("geocode", geocode); + values.put("updated", System.currentTimeMillis()); + values.put("type", pair.getKey().intValue()); + values.put("count", pair.getValue().intValue()); + + databaseRW.insert(dbTableLogCount, null, values); + } + databaseRW.setTransactionSuccessful(); + } finally { + databaseRW.endTransaction(); + } + + return true; + } + + public boolean saveInventory(String geocode, ArrayList<cgTrackable> trackables) { + init(); + + if (trackables == null) { + return false; + } + + databaseRW.beginTransaction(); + try { + if (geocode != null) { + databaseRW.delete(dbTableTrackables, "geocode = \"" + geocode + "\"", null); + } + + if (!trackables.isEmpty()) { + ContentValues values = new ContentValues(); + for (cgTrackable oneTrackable : trackables) { + values.clear(); + if (geocode != null) { + values.put("geocode", geocode); + } + values.put("updated", System.currentTimeMillis()); + values.put("tbcode", oneTrackable.geocode); + values.put("guid", oneTrackable.guid); + values.put("title", oneTrackable.name); + values.put("owner", oneTrackable.owner); + if (oneTrackable.released != null) { + values.put("released", oneTrackable.released.getTime()); + } else { + values.put("released", 0l); + } + values.put("goal", oneTrackable.goal); + values.put("description", oneTrackable.details); + + databaseRW.insert(dbTableTrackables, null, values); + + saveLogs(oneTrackable.geocode, oneTrackable.logs); + } + } + databaseRW.setTransactionSuccessful(); + } finally { + databaseRW.endTransaction(); + } + + return true; + } + + public ArrayList<Object> getBounds(Object[] geocodes) { + init(); + + Cursor cursor = null; + + final ArrayList<Object> viewport = new ArrayList<Object>(); + + try { + final StringBuilder where = new StringBuilder(); + + if (geocodes != null && geocodes.length > 0) { + StringBuilder all = new StringBuilder(); + for (Object one : geocodes) { + if (all.length() > 0) { + all.append(", "); + } + all.append("\""); + all.append((String) one); + all.append("\""); + } + + if (where.length() > 0) { + where.append(" and "); + } + where.append("geocode in ("); + where.append(all); + where.append(")"); + } + + cursor = databaseRO.query( + dbTableCaches, + new String[]{"count(_id) as cnt", "min(latitude) as latMin", "max(latitude) as latMax", "min(longitude) as lonMin", "max(longitude) as lonMax"}, + where.toString(), + null, + null, + null, + null, + null); + + if (cursor != null) { + int cnt = cursor.getCount(); + + if (cnt > 0) { + cursor.moveToFirst(); + + viewport.add((Integer) cursor.getInt(cursor.getColumnIndex("cnt"))); + viewport.add((Double) cursor.getDouble(cursor.getColumnIndex("latMin"))); + viewport.add((Double) cursor.getDouble(cursor.getColumnIndex("latMax"))); + viewport.add((Double) cursor.getDouble(cursor.getColumnIndex("lonMin"))); + viewport.add((Double) cursor.getDouble(cursor.getColumnIndex("lonMax"))); + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.getBounds: " + e.toString()); + } + + if (cursor != null) { + cursor.close(); + } + + return viewport; + } + + public cgCache loadCache(String geocode, String guid) { + return loadCache(geocode, guid, false, true, false, false, false, false); + } + + public cgCache loadCache(String geocode, String guid, boolean loadA, boolean loadW, boolean loadS, boolean loadL, boolean loadI, boolean loadO) { + Object[] geocodes = new Object[1]; + Object[] guids = new Object[1]; + + if (geocode != null && geocode.length() > 0) { + geocodes[0] = geocode; + } else { + geocodes = null; + } + + if (guid != null && guid.length() > 0) { + guids[0] = guid; + } else { + guids = null; + } + + ArrayList<cgCache> caches = loadCaches(geocodes, guids, null, null, null, null, loadA, loadW, loadS, loadL, loadI, loadO); + if (caches != null && caches.isEmpty() == false) { + return caches.get(0); + } + + return null; + } + + public ArrayList<cgCache> loadCaches(Object[] geocodes, Object[] guids) { + return loadCaches(geocodes, guids, null, null, null, null, false, true, false, false, false, false); + } + + public ArrayList<cgCache> loadCaches(Object[] geocodes, Object[] guids, boolean lite) { + if (lite == true) { + return loadCaches(geocodes, guids, null, null, null, null, false, true, false, false, false, false); + } else { + return loadCaches(geocodes, guids, null, null, null, null, true, true, true, true, true, true); + } + } + + public ArrayList<cgCache> loadCaches(Object[] geocodes, Object[] guids, Long centerLat, Long centerLon, Long spanLat, Long spanLon, boolean loadA, boolean loadW, boolean loadS, boolean loadL, boolean loadI, boolean loadO) { + init(); + + StringBuilder where = new StringBuilder(); + Cursor cursor = null; + ArrayList<cgCache> caches = new ArrayList<cgCache>(); + + try { + if (geocodes != null && geocodes.length > 0) { + StringBuilder all = new StringBuilder(); + for (Object one : geocodes) { + if (all.length() > 0) { + all.append(", "); + } + all.append("\""); + all.append((String) one); + all.append("\""); + } + + if (where.length() > 0) { + where.append(" and "); + } + where.append("geocode in ("); + where.append(all); + where.append(")"); + } else if (guids != null && guids.length > 0) { + StringBuilder all = new StringBuilder(); + for (Object one : guids) { + if (all.length() > 0) { + all.append(", "); + } + all.append("\""); + all.append((String) one); + all.append("\""); + } + + if (where.length() > 0) { + where.append(" and "); + } + where.append("guid in ("); + where.append(all); + where.append(")"); + } else { + return caches; + } + + // viewport limitation + if (centerLat != null && centerLon != null && spanLat != null && spanLon != null) { + double latMin = (centerLat / 1e6) - ((spanLat / 1e6) / 2) - ((spanLat / 1e6) / 4); + double latMax = (centerLat / 1e6) + ((spanLat / 1e6) / 2) + ((spanLat / 1e6) / 4); + double lonMin = (centerLon / 1e6) - ((spanLon / 1e6) / 2) - ((spanLon / 1e6) / 4); + double lonMax = (centerLon / 1e6) + ((spanLon / 1e6) / 2) + ((spanLon / 1e6) / 4); + double llCache; + + if (latMin > latMax) { + llCache = latMax; + latMax = latMin; + latMin = llCache; + } + if (lonMin > lonMax) { + llCache = lonMax; + lonMax = lonMin; + lonMin = llCache; + } + + if (where.length() > 0) { + where.append(" and "); + } + where.append("("); + where.append("latitude >= "); + where.append(String.format((Locale) null, "%.6f", latMin)); + where.append(" and latitude <= "); + where.append(String.format((Locale) null, "%.6f", latMax)); + where.append(" and longitude >= "); + where.append(String.format((Locale) null, "%.6f", lonMin)); + where.append(" and longitude <= "); + where.append(String.format((Locale) null, "%.6f", lonMax)); + where.append(")"); + } + + cursor = databaseRO.query( + dbTableCaches, + new String[]{ + "_id", "updated", "reason", "detailed", "detailedupdate", "visiteddate", "geocode", "cacheid", "guid", "type", "name", "own", "owner", "owner_real", "hidden", "hint", "size", + "difficulty", "distance", "direction", "terrain", "latlon", "latitude_string", "longitude_string", "location", "latitude", "longitude", "elevation", "shortdesc", + "description", "favourite_cnt", "rating", "votes", "myvote", "disabled", "archived", "members", "found", "favourite", "inventorycoins", "inventorytags", + "inventoryunknown" + }, + where.toString(), + null, + null, + null, + null, + null); + + if (cursor != null) { + int index = 0; + + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + cgCache cache = new cgCache(); + + cache.updated = (long) cursor.getLong(cursor.getColumnIndex("updated")); + cache.reason = (int) cursor.getInt(cursor.getColumnIndex("reason")); + index = cursor.getColumnIndex("detailed"); + if ((int) cursor.getInt(index) == 1) { + cache.detailed = true; + } else { + cache.detailed = false; + } + cache.detailedUpdate = (Long) cursor.getLong(cursor.getColumnIndex("detailedupdate")); + cache.visitedDate = (Long) cursor.getLong(cursor.getColumnIndex("visiteddate")); + cache.geocode = (String) cursor.getString(cursor.getColumnIndex("geocode")); + cache.cacheid = (String) cursor.getString(cursor.getColumnIndex("cacheid")); + cache.guid = (String) cursor.getString(cursor.getColumnIndex("guid")); + cache.type = (String) cursor.getString(cursor.getColumnIndex("type")); + cache.name = (String) cursor.getString(cursor.getColumnIndex("name")); + index = cursor.getColumnIndex("own"); + if ((int) cursor.getInt(index) == 1) { + cache.own = true; + } else { + cache.own = false; + } + cache.owner = (String) cursor.getString(cursor.getColumnIndex("owner")); + cache.ownerReal = (String) cursor.getString(cursor.getColumnIndex("owner_real")); + cache.hidden = new Date((long) cursor.getLong(cursor.getColumnIndex("hidden"))); + cache.hint = (String) cursor.getString(cursor.getColumnIndex("hint")); + cache.size = (String) cursor.getString(cursor.getColumnIndex("size")); + cache.difficulty = (Float) cursor.getFloat(cursor.getColumnIndex("difficulty")); + index = cursor.getColumnIndex("direction"); + if (cursor.isNull(index) == true) { + cache.direction = null; + } else { + cache.direction = (Double) cursor.getDouble(index); + } + index = cursor.getColumnIndex("distance"); + if (cursor.isNull(index) == true) { + cache.distance = null; + } else { + cache.distance = (Double) cursor.getDouble(index); + } + cache.terrain = (Float) cursor.getFloat(cursor.getColumnIndex("terrain")); + cache.latlon = (String) cursor.getString(cursor.getColumnIndex("latlon")); + cache.latitudeString = (String) cursor.getString(cursor.getColumnIndex("latitude_string")); + cache.longitudeString = (String) cursor.getString(cursor.getColumnIndex("longitude_string")); + cache.location = (String) cursor.getString(cursor.getColumnIndex("location")); + index = cursor.getColumnIndex("latitude"); + if (cursor.isNull(index) == true) { + cache.latitude = null; + } else { + cache.latitude = (Double) cursor.getDouble(index); + } + index = cursor.getColumnIndex("longitude"); + if (cursor.isNull(index) == true) { + cache.longitude = null; + } else { + cache.longitude = (Double) cursor.getDouble(index); + } + index = cursor.getColumnIndex("elevation"); + if (cursor.isNull(index) == true) { + cache.elevation = null; + } else { + cache.elevation = (Double) cursor.getDouble(index); + } + cache.shortdesc = (String) cursor.getString(cursor.getColumnIndex("shortdesc")); + cache.description = (String) cursor.getString(cursor.getColumnIndex("description")); + cache.favouriteCnt = (Integer) cursor.getInt(cursor.getColumnIndex("favourite_cnt")); + cache.rating = (Float) cursor.getFloat(cursor.getColumnIndex("rating")); + cache.votes = (Integer) cursor.getInt(cursor.getColumnIndex("votes")); + cache.myVote = (Float) cursor.getFloat(cursor.getColumnIndex("myvote")); + index = cursor.getColumnIndex("disabled"); + if ((int) cursor.getLong(index) == 1) { + cache.disabled = true; + } else { + cache.disabled = false; + } + index = cursor.getColumnIndex("archived"); + if ((int) cursor.getLong(index) == 1) { + cache.archived = true; + } else { + cache.archived = false; + } + index = cursor.getColumnIndex("members"); + if ((int) cursor.getLong(index) == 1) { + cache.members = true; + } else { + cache.members = false; + } + index = cursor.getColumnIndex("found"); + if ((int) cursor.getLong(index) == 1) { + cache.found = true; + } else { + cache.found = false; + } + index = cursor.getColumnIndex("favourite"); + if ((int) cursor.getLong(index) == 1) { + cache.favourite = true; + } else { + cache.favourite = false; + } + cache.inventoryItems = (Integer) cursor.getInt(cursor.getColumnIndex("inventoryunknown")); + + if (loadA == true) { + ArrayList<String> attributes = loadAttributes(cache.geocode); + if (attributes != null && attributes.isEmpty() == false) { + if (cache.attributes == null) + cache.attributes = new ArrayList<String>(); + else + cache.attributes.clear(); + cache.attributes.addAll(attributes); + } + } + + if (loadW == true) { + ArrayList<cgWaypoint> waypoints = loadWaypoints(cache.geocode); + if (waypoints != null && waypoints.isEmpty() == false) { + if (cache.waypoints == null) + cache.waypoints = new ArrayList<cgWaypoint>(); + else + cache.waypoints.clear(); + cache.waypoints.addAll(waypoints); + } + } + + if (loadS == true) { + ArrayList<cgSpoiler> spoilers = loadSpoilers(cache.geocode); + if (spoilers != null && spoilers.isEmpty() == false) { + if (cache.spoilers == null) + cache.spoilers = new ArrayList<cgSpoiler>(); + else + cache.spoilers.clear(); + cache.spoilers.addAll(spoilers); + } + } + + if (loadL == true) { + ArrayList<cgLog> logs = loadLogs(cache.geocode); + if (logs != null && logs.isEmpty() == false) { + if (cache.logs == null) + cache.logs = new ArrayList<cgLog>(); + else + cache.logs.clear(); + cache.logs.addAll(logs); + } + HashMap<Integer, Integer> logCounts = loadLogCounts(cache.geocode); + if (logCounts != null && logCounts.isEmpty() == false) { + cache.logCounts.clear(); + cache.logCounts.putAll(logCounts); + } + } + + if (loadI == true) { + ArrayList<cgTrackable> inventory = loadInventory(cache.geocode); + if (inventory != null && inventory.isEmpty() == false) { + if (cache.inventory == null) + cache.inventory = new ArrayList<cgTrackable>(); + else + cache.inventory.clear(); + cache.inventory.addAll(inventory); + } + } + + if (loadO == true) { + cache.logOffline = hasLogOffline(cache.geocode); + } + + caches.add(cache); + } while (cursor.moveToNext()); + } else { + if (cursor != null) { + cursor.close(); + } + + return null; + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.loadCaches: " + e.toString()); + } + + if (cursor != null) { + cursor.close(); + } + + return caches; + } + + public ArrayList<String> loadAttributes(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return null; + } + + Cursor cursor = null; + ArrayList<String> attributes = new ArrayList<String>(); + + cursor = databaseRO.query( + dbTableAttributes, + new String[]{"_id", "attribute"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + null, + "100"); + + if (cursor != null && cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + attributes.add((String) cursor.getString(cursor.getColumnIndex("attribute"))); + } while (cursor.moveToNext()); + } + + if (cursor != null) { + cursor.close(); + } + + return attributes; + } + + public cgWaypoint loadWaypoint(Integer id) { + init(); + + if (id == null || id == 0) { + return null; + } + + Cursor cursor = null; + cgWaypoint waypoint = new cgWaypoint(); + + cursor = databaseRO.query( + dbTableWaypoints, + new String[]{"_id", "geocode", "updated", "type", "prefix", "lookup", "name", "latlon", "latitude_string", "longitude_string", "latitude", "longitude", "note"}, + "_id = " + id, + null, + null, + null, + null, + "100"); + + if (cursor != null && cursor.getCount() > 0) { + int index; + cursor.moveToFirst(); + + waypoint.id = (int) cursor.getInt(cursor.getColumnIndex("_id")); + waypoint.geocode = (String) cursor.getString(cursor.getColumnIndex("geocode")); + waypoint.type = (String) cursor.getString(cursor.getColumnIndex("type")); + waypoint.prefix = (String) cursor.getString(cursor.getColumnIndex("prefix")); + waypoint.lookup = (String) cursor.getString(cursor.getColumnIndex("lookup")); + waypoint.name = (String) cursor.getString(cursor.getColumnIndex("name")); + waypoint.latlon = (String) cursor.getString(cursor.getColumnIndex("latlon")); + waypoint.latitudeString = (String) cursor.getString(cursor.getColumnIndex("latitude_string")); + waypoint.longitudeString = (String) cursor.getString(cursor.getColumnIndex("longitude_string")); + index = cursor.getColumnIndex("latitude"); + if (cursor.isNull(index) == true) { + waypoint.latitude = null; + } else { + waypoint.latitude = (Double) cursor.getDouble(index); + } + index = cursor.getColumnIndex("longitude"); + if (cursor.isNull(index) == true) { + waypoint.longitude = null; + } else { + waypoint.longitude = (Double) cursor.getDouble(index); + } + waypoint.note = (String) cursor.getString(cursor.getColumnIndex("note")); + } + + if (cursor != null) { + cursor.close(); + } + + return waypoint; + } + + public ArrayList<cgWaypoint> loadWaypoints(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return null; + } + + Cursor cursor = null; + ArrayList<cgWaypoint> waypoints = new ArrayList<cgWaypoint>(); + + cursor = databaseRO.query( + dbTableWaypoints, + new String[]{"_id", "geocode", "updated", "type", "prefix", "lookup", "name", "latlon", "latitude_string", "longitude_string", "latitude", "longitude", "note"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + null, + "100"); + + if (cursor != null && cursor.getCount() > 0) { + int index; + cursor.moveToFirst(); + + do { + cgWaypoint waypoint = new cgWaypoint(); + waypoint.id = (int) cursor.getInt(cursor.getColumnIndex("_id")); + waypoint.geocode = (String) cursor.getString(cursor.getColumnIndex("geocode")); + waypoint.type = (String) cursor.getString(cursor.getColumnIndex("type")); + waypoint.prefix = (String) cursor.getString(cursor.getColumnIndex("prefix")); + waypoint.lookup = (String) cursor.getString(cursor.getColumnIndex("lookup")); + waypoint.name = (String) cursor.getString(cursor.getColumnIndex("name")); + waypoint.latlon = (String) cursor.getString(cursor.getColumnIndex("latlon")); + waypoint.latitudeString = (String) cursor.getString(cursor.getColumnIndex("latitude_string")); + waypoint.longitudeString = (String) cursor.getString(cursor.getColumnIndex("longitude_string")); + index = cursor.getColumnIndex("latitude"); + if (cursor.isNull(index) == true) { + waypoint.latitude = null; + } else { + waypoint.latitude = (Double) cursor.getDouble(index); + } + index = cursor.getColumnIndex("longitude"); + if (cursor.isNull(index) == true) { + waypoint.longitude = null; + } else { + waypoint.longitude = (Double) cursor.getDouble(index); + } + waypoint.note = (String) cursor.getString(cursor.getColumnIndex("note")); + + waypoints.add(waypoint); + } while (cursor.moveToNext()); + } + + if (cursor != null) { + cursor.close(); + } + + return waypoints; + } + + public ArrayList<cgSpoiler> loadSpoilers(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return null; + } + + Cursor cursor = null; + ArrayList<cgSpoiler> spoilers = new ArrayList<cgSpoiler>(); + + cursor = databaseRO.query( + dbTableSpoilers, + new String[]{"_id", "url", "title", "description"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + null, + "100"); + + if (cursor != null && cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + cgSpoiler spoiler = new cgSpoiler(); + spoiler.url = (String) cursor.getString(cursor.getColumnIndex("url")); + spoiler.title = (String) cursor.getString(cursor.getColumnIndex("title")); + spoiler.description = (String) cursor.getString(cursor.getColumnIndex("description")); + + spoilers.add(spoiler); + } while (cursor.moveToNext()); + } + + if (cursor != null) { + cursor.close(); + } + + return spoilers; + } + + public ArrayList<cgLog> loadLogs(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return null; + } + + Cursor cursor = null; + ArrayList<cgLog> logs = new ArrayList<cgLog>(); + + cursor = databaseRO.query( + dbTableLogs, + new String[]{"_id", "type", "author", "log", "date", "found"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + "date desc, _id asc", + "100"); + + if (cursor != null && cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + cgLog log = new cgLog(); + log.id = (int) cursor.getInt(cursor.getColumnIndex("_id")); + log.type = (int) cursor.getInt(cursor.getColumnIndex("type")); + log.author = (String) cursor.getString(cursor.getColumnIndex("author")); + log.log = (String) cursor.getString(cursor.getColumnIndex("log")); + log.date = (long) cursor.getLong(cursor.getColumnIndex("date")); + log.found = (int) cursor.getInt(cursor.getColumnIndex("found")); + + logs.add(log); + } while (cursor.moveToNext()); + } + + if (cursor != null) { + cursor.close(); + } + + return logs; + } + + public HashMap<Integer, Integer> loadLogCounts(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return null; + } + + Cursor cursor = null; + HashMap<Integer, Integer> logCounts = new HashMap<Integer, Integer>(); + + cursor = databaseRO.query( + dbTableLogCount, + new String[]{"_id", "type", "count"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + null, + "100"); + + if (cursor != null && cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + Integer type = new Integer(cursor.getInt(cursor.getColumnIndex("type"))); + Integer count = new Integer(cursor.getInt(cursor.getColumnIndex("count"))); + + logCounts.put(type, count); + } while (cursor.moveToNext()); + } + + if (cursor != null) { + cursor.close(); + } + + return logCounts; + } + + public ArrayList<cgTrackable> loadInventory(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return null; + } + + Cursor cursor = null; + ArrayList<cgTrackable> trackables = new ArrayList<cgTrackable>(); + + cursor = databaseRO.query( + dbTableTrackables, + new String[]{"_id", "updated", "tbcode", "guid", "title", "owner", "released", "goal", "description"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + null, + "100"); + + if (cursor != null && cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + cgTrackable trackable = new cgTrackable(); + trackable.geocode = (String) cursor.getString(cursor.getColumnIndex("tbcode")); + trackable.guid = (String) cursor.getString(cursor.getColumnIndex("guid")); + trackable.name = (String) cursor.getString(cursor.getColumnIndex("title")); + trackable.owner = (String) cursor.getString(cursor.getColumnIndex("owner")); + String releasedPre = cursor.getString(cursor.getColumnIndex("released")); + if (releasedPre != null && Long.getLong(releasedPre) != null) { + trackable.released = new Date(Long.getLong(releasedPre)); + } else { + trackable.released = null; + } + trackable.goal = (String) cursor.getString(cursor.getColumnIndex("goal")); + trackable.details = (String) cursor.getString(cursor.getColumnIndex("description")); + trackable.logs = loadLogs(trackable.geocode); + + trackables.add(trackable); + } while (cursor.moveToNext()); + } + + if (cursor != null) { + cursor.close(); + } + + return trackables; + } + + public cgTrackable loadTrackable(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return null; + } + + Cursor cursor = null; + cgTrackable trackable = new cgTrackable(); + + cursor = databaseRO.query( + dbTableTrackables, + new String[]{"_id", "updated", "tbcode", "guid", "title", "owner", "released", "goal", "description"}, + "tbcode = \"" + geocode + "\"", + null, + null, + null, + null, + "1"); + + if (cursor != null && cursor.getCount() > 0) { + cursor.moveToFirst(); + + trackable.geocode = (String) cursor.getString(cursor.getColumnIndex("tbcode")); + trackable.guid = (String) cursor.getString(cursor.getColumnIndex("guid")); + trackable.name = (String) cursor.getString(cursor.getColumnIndex("title")); + trackable.owner = (String) cursor.getString(cursor.getColumnIndex("owner")); + String releasedPre = cursor.getString(cursor.getColumnIndex("released")); + if (releasedPre != null && Long.getLong(releasedPre) != null) { + trackable.released = new Date(Long.getLong(releasedPre)); + } else { + trackable.released = null; + } + trackable.goal = (String) cursor.getString(cursor.getColumnIndex("goal")); + trackable.details = (String) cursor.getString(cursor.getColumnIndex("description")); + trackable.logs = loadLogs(trackable.geocode); + } + + if (cursor != null) { + cursor.close(); + } + + return trackable; + } + + public int getAllStoredCachesCount(boolean detailedOnly, String cachetype, Integer list) { + int count = 0; + + String listSql = null; + String listSqlW = null; + if (list == null) { + listSql = " where reason >= 1"; + listSqlW = " and reason >= 1"; + } else if (list >= 1) { + listSql = " where reason = " + list; + listSqlW = " and reason = " + list; + } else { + return count; + } + + try { + if (detailedOnly == false) { + if (cachetype == null) { + SQLiteStatement sqlCount = databaseRO.compileStatement("select count(_id) from " + dbTableCaches + listSql); + count = (int) sqlCount.simpleQueryForLong(); + } else { + SQLiteStatement sqlCount = databaseRO.compileStatement("select count(_id) from " + dbTableCaches + " where type = \"" + cachetype + "\"" + listSqlW); + count = (int) sqlCount.simpleQueryForLong(); + } + } else { + if (cachetype == null) { + SQLiteStatement sqlCount = databaseRO.compileStatement("select count(_id) from " + dbTableCaches + " where detailed = 1" + listSqlW); + count = (int) sqlCount.simpleQueryForLong(); + } else { + SQLiteStatement sqlCount = databaseRO.compileStatement("select count(_id) from " + dbTableCaches + " where detailed = 1 and type = \"" + cachetype + "\"" + listSqlW); + count = (int) sqlCount.simpleQueryForLong(); + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.loadAllStoredCachesCount: " + e.toString()); + } + + return count; + } + + public int getAllHistoricCachesCount(boolean detailedOnly, String cachetype) { + init(); + + int count = 0; + + try { + SQLiteStatement sqlCount = databaseRO.compileStatement("select count(_id) from " + dbTableCaches + " where visiteddate > 0"); + count = (int) sqlCount.simpleQueryForLong(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.getAllHistoricCachesCount: " + e.toString()); + } + + return count; + } + + public ArrayList<String> loadBatchOfStoredGeocodes(boolean detailedOnly, Double latitude, Double longitude, String cachetype, int list) { + init(); + + if (list < 1) { + list = 1; + } + + Cursor cursor = null; + ArrayList<String> geocodes = new ArrayList<String>(); + + StringBuilder specifySql = new StringBuilder(); + + specifySql.append("reason = "); + specifySql.append(list); + + if (detailedOnly == true) { + if (specifySql.length() > 0) { + specifySql.append(" and "); + } + + specifySql.append("detailed = 1"); + } + + if (cachetype != null) { + if (specifySql.length() > 0) { + specifySql.append(" and "); + } + + specifySql.append("type = \""); + specifySql.append(cachetype); + specifySql.append("\""); + } + + try { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "geocode", "(abs(latitude-" + String.format((Locale) null, "%.6f", latitude) + ") + abs(longitude-" + String.format((Locale) null, "%.6f", longitude) + ")) as dif"}, + specifySql.toString(), + null, + null, + null, + "dif", + null); + + if (cursor != null) { + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + geocodes.add((String) cursor.getString(cursor.getColumnIndex("geocode"))); + } while (cursor.moveToNext()); + } else { + cursor.close(); + return null; + } + + cursor.close(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.loadBatchOfStoredGeocodes: " + e.toString()); + } + + return geocodes; + } + + public ArrayList<String> loadBatchOfHistoricGeocodes(boolean detailedOnly, String cachetype) { + init(); + + Cursor cursor = null; + ArrayList<String> geocodes = new ArrayList<String>(); + + StringBuilder specifySql = new StringBuilder(); + if (detailedOnly == true) { + specifySql.append(" and detailed = 1"); + } + if (cachetype != null) { + specifySql.append(" and type = \""); + specifySql.append(cachetype); + specifySql.append("\""); + } + + try { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "geocode"}, + "visiteddate > 0" + specifySql.toString(), + null, + null, + null, + "visiteddate", + null); + + if (cursor != null) { + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + geocodes.add((String) cursor.getString(cursor.getColumnIndex("geocode"))); + } while (cursor.moveToNext()); + } else { + cursor.close(); + return null; + } + + cursor.close(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.loadBatchOfHistoricGeocodes: " + e.toString()); + } + + return geocodes; + } + + public ArrayList<String> getCachedInViewport(Long centerLat, Long centerLon, Long spanLat, Long spanLon, String cachetype) { + return getInViewport(false, centerLat, centerLon, spanLat, spanLon, cachetype); + } + + public ArrayList<String> getStoredInViewport(Long centerLat, Long centerLon, Long spanLat, Long spanLon, String cachetype) { + return getInViewport(true, centerLat, centerLon, spanLat, spanLon, cachetype); + } + + public ArrayList<String> getInViewport(boolean stored, Long centerLat, Long centerLon, Long spanLat, Long spanLon, String cachetype) { + if (centerLat == null || centerLon == null || spanLat == null || spanLon == null) { + return null; + } + + init(); + + Cursor cursor = null; + ArrayList<String> geocodes = new ArrayList<String>(); + + // viewport limitation + double latMin = (centerLat / 1e6) - ((spanLat / 1e6) / 2) - ((spanLat / 1e6) / 4); + double latMax = (centerLat / 1e6) + ((spanLat / 1e6) / 2) + ((spanLat / 1e6) / 4); + double lonMin = (centerLon / 1e6) - ((spanLon / 1e6) / 2) - ((spanLon / 1e6) / 4); + double lonMax = (centerLon / 1e6) + ((spanLon / 1e6) / 2) + ((spanLon / 1e6) / 4); + double llCache; + + if (latMin > latMax) { + llCache = latMax; + latMax = latMin; + latMin = llCache; + } + if (lonMin > lonMax) { + llCache = lonMax; + lonMax = lonMin; + lonMin = llCache; + } + + StringBuilder where = new StringBuilder(); + where.append("latitude >= "); + where.append(String.format((Locale) null, "%.6f", latMin)); + where.append(" and latitude <= "); + where.append(String.format((Locale) null, "%.6f", latMax)); + where.append(" and longitude >= "); + where.append(String.format((Locale) null, "%.6f", lonMin)); + where.append(" and longitude <= "); + where.append(String.format((Locale) null, "%.6f", lonMax)); + + // cachetype limitation + if (cachetype != null) { + where.append(" and type = \""); + where.append(cachetype); + where.append("\""); + } + + // offline caches only + if (stored) { + where.append(" and reason >= 1"); + } + + try { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "geocode"}, + where.toString(), + null, + null, + null, + null, + "500"); + + if (cursor != null) { + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + geocodes.add((String) cursor.getString(cursor.getColumnIndex("geocode"))); + } while (cursor.moveToNext()); + } else { + cursor.close(); + return null; + } + + cursor.close(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.getOfflineInViewport: " + e.toString()); + } + + return geocodes; + } + + public ArrayList<String> getOfflineAll(String cachetype) { + init(); + + Cursor cursor = null; + ArrayList<String> geocodes = new ArrayList<String>(); + + StringBuilder where = new StringBuilder(); + + // cachetype limitation + if (cachetype != null) { + where.append(cachetype); + where.append("\""); + } + + // offline caches only + if (where.length() > 0) { + where.append(" and "); + } + where.append("reason >= 1"); + + try { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "geocode"}, + where.toString(), + null, + null, + null, + null, + "5000"); + + if (cursor != null) { + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + geocodes.add((String) cursor.getString(cursor.getColumnIndex("geocode"))); + } while (cursor.moveToNext()); + } else { + cursor.close(); + return null; + } + + cursor.close(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.getOfflineAll: " + e.toString()); + } + + return geocodes; + } + + public void markStored(String geocode, int listId) { + init(); + + if (geocode == null || geocode.length() == 0) { + return; + } + + if (listId <= 0) { + listId = 1; + } + + ContentValues values = new ContentValues(); + values.put("reason", listId); + databaseRW.update(dbTableCaches, values, "geocode = \"" + geocode + "\" and reason < 1", null); + } + + public boolean markDropped(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return false; + } + + try { + ContentValues values = new ContentValues(); + values.put("reason", 0); + int rows = databaseRW.update(dbTableCaches, values, "geocode = \"" + geocode + "\"", null); + + if (rows > 0) { + return true; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.markDropped: " + e.toString()); + } + + return false; + } + + public boolean markFound(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return false; + } + + try { + ContentValues values = new ContentValues(); + values.put("found", 1); + int rows = databaseRW.update(dbTableCaches, values, "geocode = \"" + geocode + "\"", null); + + if (rows > 0) { + return true; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.markFound: " + e.toString()); + } + + return false; + } + + public void clean() { + clean(false); + } + + public void clean(boolean more) { + init(); + + Log.d(cgSettings.tag, "Database clean: started"); + + Cursor cursor = null; + ArrayList<String> geocodes = new ArrayList<String>(); + + try { + if (more) { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "geocode"}, + "reason = 0", + null, + null, + null, + null, + null); + } else { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "geocode"}, + "reason = 0 and detailed < " + (System.currentTimeMillis() - (3 * 24 * 60 * 60 * 1000)) + " and detailedupdate < " + (System.currentTimeMillis() - (3 * 24 * 60 * 60 * 1000)) + " and visiteddate < " + (System.currentTimeMillis() - (3 * 24 * 60 * 60 * 1000)), + null, + null, + null, + null, + null); + } + + if (cursor != null) { + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + geocodes.add("\"" + (String) cursor.getString(cursor.getColumnIndex("geocode")) + "\""); + } while (cursor.moveToNext()); + } + + cursor.close(); + } + + final int size = geocodes.size(); + if (size > 0) { + Log.d(cgSettings.tag, "Database clean: removing " + size + " geocaches"); + + databaseRW.execSQL("delete from " + dbTableCaches + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableAttributes + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableSpoilers + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableLogs + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableLogCount + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableLogsOffline + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableWaypoints + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ") and type <> \"own\""); + databaseRW.execSQL("delete from " + dbTableTrackables + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + + geocodes.clear(); + } + + databaseRW.execSQL("delete from " + dbTableCaches + " where geocode = \"\""); + + final SQLiteStatement countSql = databaseRO.compileStatement("select count(_id) from " + dbTableCaches + " where reason = 0"); + final int count = (int) countSql.simpleQueryForLong(); + Log.d(cgSettings.tag, "Database clean: " + count + " cached geocaches remaining"); + } catch (Exception e) { + Log.w(cgSettings.tag, "cgData.clean: " + e.toString()); + } + + Log.d(cgSettings.tag, "Database clean: finished"); + } + + public void dropStored(int listId) { + init(); + + Cursor cursor = null; + ArrayList<String> geocodes = new ArrayList<String>(); + + try { + cursor = databaseRO.query( + dbTableCaches, + new String[]{"_id", "geocode"}, + "reason = " + listId, + null, + null, + null, + null, + null); + + if (cursor != null) { + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + geocodes.add("\"" + (String) cursor.getString(cursor.getColumnIndex("geocode")) + "\""); + } while (cursor.moveToNext()); + } else { + cursor.close(); + return; + } + + cursor.close(); + } + + if (geocodes.size() > 0) { + databaseRW.execSQL("delete from " + dbTableCaches + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableAttributes + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableSpoilers + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableLogs + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableLogCount + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableLogsOffline + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + databaseRW.execSQL("delete from " + dbTableWaypoints + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ") and type <> \"own\""); + databaseRW.execSQL("delete from " + dbTableTrackables + " where geocode in (" + cgBase.implode(", ", geocodes.toArray()) + ")"); + + geocodes.clear(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.dropStored: " + e.toString()); + } + } + + public boolean saveLogOffline(String geocode, Date date, int type, String log) { + if (geocode == null || geocode.length() == 0) { + return false; + } + if (type <= 0 && (log == null || log.length() == 0)) { + return false; + } + + boolean status = false; + + ContentValues values = new ContentValues(); + values.put("geocode", geocode); + values.put("updated", System.currentTimeMillis()); + values.put("type", type); + values.put("log", log); + values.put("date", date.getTime()); + + try { + if (hasLogOffline(geocode) == true) { + final int rows = databaseRW.update(dbTableLogsOffline, values, "geocode = \"" + geocode + "\"", null); + + if (rows > 0) { + status = true; + } + } else { + final long id = databaseRW.insert(dbTableLogsOffline, null, values); + + if (id > 0) { + status = true; + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.saveLogOffline: " + e.toString()); + } + + return status; + } + + public cgLog loadLogOffline(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return null; + } + + Cursor cursor = null; + cgLog log = null; + + cursor = databaseRO.query( + dbTableLogsOffline, + new String[]{"_id", "type", "log", "date"}, + "geocode = \"" + geocode + "\"", + null, + null, + null, + "_id desc", + "1"); + + if (cursor != null && cursor.getCount() > 0) { + cursor.moveToFirst(); + + log = new cgLog(); + log.id = (int) cursor.getInt(cursor.getColumnIndex("_id")); + log.type = (int) cursor.getInt(cursor.getColumnIndex("type")); + log.log = (String) cursor.getString(cursor.getColumnIndex("log")); + log.date = (long) cursor.getLong(cursor.getColumnIndex("date")); + } + + if (cursor != null) { + cursor.close(); + } + + return log; + } + + public void clearLogOffline(String geocode) { + init(); + + if (geocode == null || geocode.length() == 0) { + return; + } + + databaseRW.delete(dbTableLogsOffline, "geocode = \"" + geocode + "\"", null); + } + + public boolean hasLogOffline(String geocode) { + if (geocode == null || geocode.length() == 0) { + return false; + } + + init(); + + try { + final SQLiteStatement countSql = databaseRO.compileStatement("select count(_id) from " + dbTableLogsOffline + " where geocode = \"" + geocode.toUpperCase() + "\""); + final int count = (int) countSql.simpleQueryForLong(); + + if (count > 0) { + return true; + } + + countSql.close(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.hasLogOffline: " + e.toString()); + } + + return false; + } + + public void saveVisitDate(String geocode) { + if (geocode == null || geocode.length() == 0) { + return; + } + + ContentValues values = new ContentValues(); + values.put("visiteddate", System.currentTimeMillis()); + + try { + databaseRW.update(dbTableCaches, values, "geocode = \"" + geocode + "\"", null); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.saveVisitDate: " + e.toString()); + } + } + + public ArrayList<cgList> getLists(Resources res) { + init(); + + Cursor cursor = null; + ArrayList<cgList> lists = new ArrayList<cgList>(); + + lists.add(new cgList(true, 1, res.getString(R.string.list_inbox))); + // lists.add(new cgList(true, 2, res.getString(R.string.list_wpt))); + + try { + cursor = databaseRO.query( + dbTableLists, + new String[]{"_id", "title", "updated", "latitude", "longitude"}, + null, + null, + null, + null, + "title COLLATE NOCASE ASC", + null); + + if (cursor != null) { + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + cgList list = new cgList(false); + + list.id = ((int) cursor.getInt(cursor.getColumnIndex("_id"))) + 10; + list.title = (String) cursor.getString(cursor.getColumnIndex("title")); + list.updated = (Long) cursor.getLong(cursor.getColumnIndex("updated")); + list.latitude = (Double) cursor.getDouble(cursor.getColumnIndex("latitude")); + list.longitude = (Double) cursor.getDouble(cursor.getColumnIndex("longitude")); + + lists.add(list); + } while (cursor.moveToNext()); + } + + cursor.close(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.getLists: " + e.toString()); + } + + return lists; + } + + public cgList getList(int id, Resources res) { + cgList list = null; + + if (id == 1) { + list = new cgList(true, 1, res.getString(R.string.list_inbox)); + } else if (id == 2) { + list = new cgList(true, 2, res.getString(R.string.list_wpt)); + } else if (id >= 10) { + init(); + + Cursor cursor = null; + + try { + cursor = databaseRO.query( + dbTableLists, + new String[]{"_id", "title", "updated", "latitude", "longitude"}, + "_id = " + (id - 10), + null, + null, + null, + null, + null); + + if (cursor != null) { + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + + do { + list = new cgList(false); + + list.id = ((int) cursor.getInt(cursor.getColumnIndex("_id"))) + 10; + list.title = (String) cursor.getString(cursor.getColumnIndex("title")); + list.updated = (Long) cursor.getLong(cursor.getColumnIndex("updated")); + list.latitude = (Double) cursor.getDouble(cursor.getColumnIndex("latitude")); + list.longitude = (Double) cursor.getDouble(cursor.getColumnIndex("longitude")); + } while (cursor.moveToNext()); + } + + cursor.close(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgData.getList: " + e.toString()); + } + } + + return list; + } + + public int createList(String name) { + init(); + + int id = -1; + if (name == null || name.length() == 0) { + return id; + } + + databaseRW.beginTransaction(); + try { + ContentValues values = new ContentValues(); + values.put("title", name); + values.put("updated", System.currentTimeMillis()); + + id = (int) databaseRW.insert(dbTableLists, null, values); + databaseRW.setTransactionSuccessful(); + } finally { + databaseRW.endTransaction(); + } + + if (id < 0) { + return -1; + } else { + return (id + 10); + } + } + + public boolean removeList(int id) { + init(); + + boolean status = false; + if (id < 10) { + return status; + } + + databaseRW.beginTransaction(); + try { + int cnt = databaseRW.delete(dbTableLists, "_id = " + (id - 10), null); + + if (cnt > 0) { + ContentValues values = new ContentValues(); + values.put("reason", 1); + databaseRW.update(dbTableCaches, values, "reason = " + id, null); + + status = true; + } + + databaseRW.setTransactionSuccessful(); + } finally { + databaseRW.endTransaction(); + } + + return status; + } + + public void moveToList(String geocode, int listId) { + if (geocode == null || geocode.length() == 0 || listId <= 0) { + return; + } + + databaseRW.beginTransaction(); + try { + ContentValues values = new ContentValues(); + values.put("reason", listId); + databaseRW.update(dbTableCaches, values, "geocode = \"" + geocode + "\"", null); + + databaseRW.setTransactionSuccessful(); + } finally { + databaseRW.endTransaction(); + } + } + + public boolean status() { + if (databaseRO == null || databaseRW == null || initialized == false) { + return false; + } + + return true; + } +} diff --git a/src/cgeo/geocaching/cgDirection.java b/src/cgeo/geocaching/cgDirection.java new file mode 100644 index 0000000..daf5df6 --- /dev/null +++ b/src/cgeo/geocaching/cgDirection.java @@ -0,0 +1,102 @@ +package cgeo.geocaching; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorManager; +import android.hardware.SensorEventListener; +import android.os.Build; +import android.util.Log; +import android.view.Display; +import android.view.Surface; + +public class cgDirection { + private Resources res = null; + private cgDirection dir = null; + private cgeoapplication app = null; + private Context context = null; + private cgWarning warning = null; + private SensorManager sensorManager = null; + private cgeoSensorListener sensorListener = null; + private cgUpdateDir dirUpdate = null; + private cg8wrap cg8 = null; + + public Double directionNow = null; + + public cgDirection(cgeoapplication appIn, Context contextIn, cgUpdateDir dirUpdateIn, cgWarning warningIn) { + app = appIn; + context = contextIn; + dirUpdate = dirUpdateIn; + warning = warningIn; + res = context.getResources(); + + try { + final int sdk = new Integer(Build.VERSION.SDK).intValue(); + if (sdk >= 8) cg8 = new cg8wrap((Activity)context); + } catch (Exception e) { + // nothing + } + + sensorListener = new cgeoSensorListener(); + } + + public void initDir() { + dir = this; + + if (sensorManager == null) { + sensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); + } + sensorManager.registerListener(sensorListener, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_NORMAL); + } + + public void closeDir() { + if (sensorManager != null && sensorListener != null) { + sensorManager.unregisterListener(sensorListener); + } + } + + public void replaceUpdate(cgUpdateDir dirUpdateIn) { + dirUpdate = dirUpdateIn; + + if (dirUpdate != null && directionNow != null) dirUpdate.updateDir(dir); + } + + private class cgeoSensorListener implements SensorEventListener { + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + /* There is a bug in Android, which appearently causes this method to be called every + * time the sensor _value_ changed, even if the _accuracy_ did not change. So logging + * this event leads to the log being flooded with multiple entries _per second_, + * which I experienced when running cgeo in a building (with GPS and network being + * unreliable). + * + * See for example https://code.google.com/p/android/issues/detail?id=14792 + */ + + //Log.i(cgSettings.tag, "Compass' accuracy is low (" + accuracy + ")"); + } + + @Override + public void onSensorChanged(SensorEvent event) { + Double directionNowPre = new Double(event.values[0]); + + if (cg8 != null) { + final int rotation = cg8.getRotation(); + if (rotation == Surface.ROTATION_90) directionNowPre = directionNowPre + 90; + else if (rotation == Surface.ROTATION_180) directionNowPre = directionNowPre + 180; + else if (rotation == Surface.ROTATION_270) directionNowPre = directionNowPre + 270; + } else { + final Display display = ((Activity)context).getWindowManager().getDefaultDisplay(); + final int rotation = display.getOrientation(); + if (rotation == Configuration.ORIENTATION_LANDSCAPE) directionNowPre = directionNowPre + 90; + } + + directionNow = directionNowPre; + + if (dirUpdate != null && directionNow != null) dirUpdate.updateDir(dir); + } + } +} diff --git a/src/cgeo/geocaching/cgDirectionImg.java b/src/cgeo/geocaching/cgDirectionImg.java new file mode 100644 index 0000000..fdfc2c5 --- /dev/null +++ b/src/cgeo/geocaching/cgDirectionImg.java @@ -0,0 +1,96 @@ +package cgeo.geocaching; + +import android.util.Log; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.impl.client.DefaultHttpClient; + +public class cgDirectionImg { + private cgSettings settings = null; + + public cgDirectionImg(cgSettings settingsIn) { + settings = settingsIn; + } + + public void getDrawable(String geocode, String code) { + String dirName; + String fileName; + + if (geocode == null || geocode.length() == 0 || code == null || code.length() == 0) { + return; + } + + if (geocode != null && geocode.length() > 0) { + dirName = settings.getStorage() + geocode + "/"; + fileName = settings.getStorage() + geocode + "/direction.png"; + } else { + return; + } + + File dir = null; + dir = new File(settings.getStorage()); + if (dir.exists() == false) { + dir.mkdirs(); + } + dir = new File(dirName); + if (dir.exists() == false) { + dir.mkdirs(); + } + dir = null; + + HttpClient client = null; + HttpGet getMethod = null; + HttpResponse httpResponse = null; + HttpEntity entity = null; + BufferedHttpEntity bufferedEntity = null; + + boolean ok = false; + + for (int i = 0; i < 3; i ++) { + if (i > 0) Log.w(cgSettings.tag, "cgDirectionImg.getDrawable: Failed to download data, retrying. Attempt #" + (i + 1)); + + try { + client = new DefaultHttpClient(); + getMethod = new HttpGet("http://www.geocaching.com/ImgGen/seek/CacheDir.ashx?k=" + code); + httpResponse = client.execute(getMethod); + entity = httpResponse.getEntity(); + bufferedEntity = new BufferedHttpEntity(entity); + + Log.i(cgSettings.tag, "[" + entity.getContentLength() + "B] Downloading direction image " + code); + + if (bufferedEntity != null) { + InputStream is = (InputStream)bufferedEntity.getContent(); + FileOutputStream fos = new FileOutputStream(fileName); + + try { + byte[] buffer = new byte[4096]; + int l; + while ((l = is.read(buffer)) != -1) { + fos.write(buffer, 0, l); + } + ok = true; + } catch (IOException e) { + Log.e(cgSettings.tag, "cgDirectionImg.getDrawable (saving to cache): " + e.toString()); + } finally { + is.close(); + fos.flush(); + fos.close(); + } + } + + if (ok == true) { + break; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgDirectionImg.getDrawable (downloading from web): " + e.toString()); + } + } + } +} diff --git a/src/cgeo/geocaching/cgDistanceView.java b/src/cgeo/geocaching/cgDistanceView.java new file mode 100644 index 0000000..9610e4f --- /dev/null +++ b/src/cgeo/geocaching/cgDistanceView.java @@ -0,0 +1,45 @@ +package cgeo.geocaching; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.TextView; + +public class cgDistanceView extends TextView { + private cgBase base = null; + private Double cacheLat = null; + private Double cacheLon = null; + + public cgDistanceView(Context context) { + super(context); + } + + public cgDistanceView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public cgDistanceView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setContent(cgBase baseIn, Double cacheLatIn, Double cacheLonIn) { + base = baseIn; + cacheLat = cacheLatIn; + cacheLon = cacheLonIn; + } + + public void update(Double latitude, Double longitude) { + if (cacheLat == null || cacheLon == null) return; + if (latitude == null || longitude == null) return; + if (base == null) return; + + setText(base.getHumanDistance(cgBase.getDistance(latitude, longitude, cacheLat, cacheLon))); + } + + public void setDistance(Double distance) { + setText("~" + base.getHumanDistance(distance)); + } + + public void clear() { + setText(null); + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgGPXListAdapter.java b/src/cgeo/geocaching/cgGPXListAdapter.java new file mode 100644 index 0000000..78ad881 --- /dev/null +++ b/src/cgeo/geocaching/cgGPXListAdapter.java @@ -0,0 +1,75 @@ +package cgeo.geocaching; + +import java.util.List; +import android.app.Activity; +import android.view.View; +import android.view.ViewGroup; +import android.view.LayoutInflater; +import android.widget.TextView; +import android.widget.ArrayAdapter; +import android.util.Log; +import java.io.File; + +public class cgGPXListAdapter extends ArrayAdapter<File> { + private cgGPXView holder = null; + private cgeogpxes parent = null; + private cgSettings settings = null; + private LayoutInflater inflater = null; + + public cgGPXListAdapter(cgeogpxes parentIn, cgSettings settingsIn, List<File> listIn) { + super(parentIn, 0, listIn); + + parent = parentIn; + settings = settingsIn; + } + + @Override + public View getView(int position, View rowView, ViewGroup parent) { + if (inflater == null) inflater = ((Activity)getContext()).getLayoutInflater(); + + if (position > getCount()) { + Log.w(cgSettings.tag, "cgGPXListAdapter.getView: Attempt to access missing item #" + position); + return null; + } + + File file = getItem(position); + + if (rowView == null) { + rowView = (View)inflater.inflate(R.layout.gpx_item, null); + + holder = new cgGPXView(); + holder.filepath = (TextView)rowView.findViewById(R.id.filepath); + holder.filename = (TextView)rowView.findViewById(R.id.filename); + + rowView.setTag(holder); + } else { + holder = (cgGPXView)rowView.getTag(); + } + + final touchListener touchLst = new touchListener(file); + rowView.setOnClickListener(touchLst); + + holder.filepath.setText(file.getParent()); + holder.filename.setText(file.getName()); + + return rowView; + } + + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + } + + private class touchListener implements View.OnClickListener { + private File file = null; + + public touchListener(File fileIn) { + file = fileIn; + } + + // tap on item + public void onClick(View view) { + parent.loadGPX(file); + } + } +} diff --git a/src/cgeo/geocaching/cgGPXParser.java b/src/cgeo/geocaching/cgGPXParser.java new file mode 100644 index 0000000..a4b1961 --- /dev/null +++ b/src/cgeo/geocaching/cgGPXParser.java @@ -0,0 +1,547 @@ +package cgeo.geocaching; + +import android.os.Handler; +import android.os.Message; +import android.sax.Element; +import android.sax.EndElementListener; +import android.sax.EndTextElementListener; +import android.sax.RootElement; +import android.sax.StartElementListener; +import android.text.Html; +import android.util.Log; +import android.util.Xml; +import java.io.File; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.xml.sax.Attributes; + +public class cgGPXParser { + + private cgeoapplication app = null; + private cgBase base = null; + private int listId = 1; + private cgSearch search = null; + private Handler handler = null; + private cgCache cache = new cgCache(); + private cgTrackable trackable = new cgTrackable(); + private cgLog log = new cgLog(); + private boolean htmlShort = true; + private boolean htmlLong = true; + private String type = null; + private String sym = null; + private String ns = null; + private ArrayList<String> nsGCList = new ArrayList<String>(); + private final Pattern patternGeocode = Pattern.compile("(GC[0-9A-Z]+)", Pattern.CASE_INSENSITIVE); + private String name = null; + private String cmt = null; + private String desc = null; + + public cgGPXParser(cgeoapplication appIn, cgBase baseIn, int listIdIn, cgSearch searchIn) { + app = appIn; + base = baseIn; + listId = listIdIn; + search = searchIn; + + nsGCList.add("http://www.groundspeak.com/cache/1/1"); // PQ 1.1 + nsGCList.add("http://www.groundspeak.com/cache/1/0/1"); // PQ 1.0.1 + nsGCList.add("http://www.groundspeak.com/cache/1/0"); // PQ 1.0 + } + + public long parse(File file, int version, Handler handlerIn) { + handler = handlerIn; + if (file == null) { + return 0l; + } + + if (version == 11) { + ns = "http://www.topografix.com/GPX/1/1"; // GPX 1.1 + } else { + ns = "http://www.topografix.com/GPX/1/0"; // GPX 1.0 + } + final RootElement root = new RootElement(ns, "gpx"); + final Element waypoint = root.getChild(ns, "wpt"); + + // waypoint - attributes + waypoint.setStartElementListener(new StartElementListener() { + + public void start(Attributes attrs) { + try { + if (attrs.getIndex("lat") > -1) { + cache.latitude = new Double(attrs.getValue("lat")); + } + if (attrs.getIndex("lon") > -1) { + cache.longitude = new Double(attrs.getValue("lon")); + } + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse waypoint's latitude and/or longitude."); + } + } + }); + + // waypoint + waypoint.setEndElementListener(new EndElementListener() { + + public void end() { + if (cache.geocode == null || cache.geocode.length() == 0) { + // try to find geocode somewhere else + String geocode = null; + Matcher matcherGeocode = null; + + if (name != null && geocode == null) { + matcherGeocode = patternGeocode.matcher(name); + while (matcherGeocode.find()) { + if (matcherGeocode.groupCount() > 0) { + geocode = matcherGeocode.group(1); + } + } + } + + if (desc != null && geocode == null) { + matcherGeocode = patternGeocode.matcher(desc); + while (matcherGeocode.find()) { + if (matcherGeocode.groupCount() > 0) { + geocode = matcherGeocode.group(1); + } + } + } + + if (cmt != null && geocode == null) { + matcherGeocode = patternGeocode.matcher(cmt); + while (matcherGeocode.find()) { + if (matcherGeocode.groupCount() > 0) { + geocode = matcherGeocode.group(1); + } + } + } + + if (geocode != null && geocode.length() > 0) { + cache.geocode = geocode; + } + + geocode = null; + matcherGeocode = null; + } + + if (cache.geocode != null && cache.geocode.length() > 0 + && cache.latitude != null && cache.longitude != null + && ((type == null && sym == null) + || (type != null && type.indexOf("geocache") > -1) + || (sym != null && sym.indexOf("geocache") > -1))) { + cache.latitudeString = base.formatCoordinate(cache.latitude, "lat", true); + cache.longitudeString = base.formatCoordinate(cache.longitude, "lon", true); + if (cache.inventory != null) { + cache.inventoryItems = cache.inventory.size(); + } else { + cache.inventoryItems = 0; + } + cache.reason = listId; + cache.updated = new Date().getTime(); + cache.detailedUpdate = new Date().getTime(); + cache.detailed = true; + + app.addCacheToSearch(search, cache); + } + + if (handler != null) { + final Message msg = new Message(); + msg.obj = search.getCount(); + handler.sendMessage(msg); + } + + htmlShort = true; + htmlLong = true; + type = null; + sym = null; + name = null; + desc = null; + cmt = null; + + cache = null; + cache = new cgCache(); + } + }); + + // waypoint.time + waypoint.getChild(ns, "time").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + try { + cache.hidden = cgBase.dateGPXIn.parse(body.trim()); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse cache date: " + e.toString()); + } + } + }); + + // waypoint.name + waypoint.getChild(ns, "name").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + name = body; + + final String content = Html.fromHtml(body).toString().trim(); + cache.name = content; + if (cache.name.length() > 2 && cache.name.substring(0, 2).equalsIgnoreCase("GC") == true) { + cache.geocode = cache.name.toUpperCase(); + } + } + }); + + // waypoint.desc + waypoint.getChild(ns, "desc").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + desc = body; + + final String content = Html.fromHtml(body).toString().trim(); + cache.shortdesc = content; + } + }); + + // waypoint.cmt + waypoint.getChild(ns, "cmt").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + cmt = body; + + final String content = Html.fromHtml(body).toString().trim(); + cache.description = content; + } + }); + + // waypoint.type + waypoint.getChild(ns, "type").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + final String[] content = body.split("\\|"); + if (content.length > 0) { + type = content[0].toLowerCase().trim(); + } + } + }); + + // waypoint.sym + waypoint.getChild(ns, "sym").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + body = body.toLowerCase(); + sym = body; + if (body.indexOf("geocache") != -1 && body.indexOf("found") != -1) { + cache.found = true; + } + } + }); + + for (String nsGC : nsGCList) { + // waypoints.cache + final Element gcCache = waypoint.getChild(nsGC, "cache"); + + gcCache.setStartElementListener(new StartElementListener() { + + public void start(Attributes attrs) { + try { + if (attrs.getIndex("id") > -1) { + cache.cacheid = attrs.getValue("id"); + } + if (attrs.getIndex("archived") > -1) { + final String at = attrs.getValue("archived").toLowerCase(); + if (at.equals("true")) { + cache.archived = true; + } else { + cache.archived = false; + } + } + if (attrs.getIndex("available") > -1) { + final String at = attrs.getValue("available").toLowerCase(); + if (at.equals("true")) { + cache.disabled = false; + } else { + cache.disabled = true; + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse cache attributes."); + } + } + }); + + // waypoint.cache.name + gcCache.getChild(nsGC, "name").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + final String content = Html.fromHtml(body).toString().trim(); + cache.name = content; + } + }); + + // waypoint.cache.owner + gcCache.getChild(nsGC, "owner").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + final String content = Html.fromHtml(body).toString().trim(); + cache.owner = content; + } + }); + + // waypoint.cache.type + gcCache.getChild(nsGC, "type").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + final String content = cgBase.cacheTypes.get(body.toLowerCase()); + cache.type = content; + } + }); + + // waypoint.cache.container + gcCache.getChild(nsGC, "container").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + final String content = body.toLowerCase(); + cache.size = content; + } + }); + + // waypoint.cache.difficulty + gcCache.getChild(nsGC, "difficulty").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + try { + cache.difficulty = new Float(body); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse difficulty: " + e.toString()); + } + } + }); + + // waypoint.cache.terrain + gcCache.getChild(nsGC, "terrain").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + try { + cache.terrain = new Float(body); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse terrain: " + e.toString()); + } + } + }); + + // waypoint.cache.country + gcCache.getChild(nsGC, "country").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + if (cache.location == null || cache.location.length() == 0) { + cache.location = body.trim(); + } else { + cache.location = cache.location + ", " + body.trim(); + } + } + }); + + // waypoint.cache.state + gcCache.getChild(nsGC, "state").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + if (cache.location == null || cache.location.length() == 0) { + cache.location = body.trim(); + } else { + cache.location = body.trim() + ", " + cache.location; + } + } + }); + + // waypoint.cache.encoded_hints + gcCache.getChild(nsGC, "encoded_hints").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + cache.hint = body.trim(); + } + }); + + // waypoint.cache.short_description + gcCache.getChild(nsGC, "short_description").setStartElementListener(new StartElementListener() { + + public void start(Attributes attrs) { + try { + if (attrs.getIndex("html") > -1) { + final String at = attrs.getValue("html").toLowerCase(); + if (at.equals("false")) { + htmlShort = false; + } + } + } catch (Exception e) { + // nothing + } + } + }); + + gcCache.getChild(nsGC, "short_description").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + if (htmlShort == false) { + cache.shortdesc = Html.fromHtml(body).toString(); + } else { + cache.shortdesc = body; + } + } + }); + + // waypoint.cache.long_description + gcCache.getChild(nsGC, "long_description").setStartElementListener(new StartElementListener() { + + public void start(Attributes attrs) { + try { + if (attrs.getIndex("html") > -1) { + final String at = attrs.getValue("html").toLowerCase(); + if (at.equals("false")) { + htmlLong = false; + } + } + } catch (Exception e) { + // nothing + } + } + }); + + gcCache.getChild(nsGC, "long_description").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + if (htmlLong == false) { + cache.description = Html.fromHtml(body).toString().trim(); + } else { + cache.description = body; + } + } + }); + + // waypoint.cache.travelbugs + final Element gcTBs = gcCache.getChild(nsGC, "travelbugs"); + + // waypoint.cache.travelbugs.travelbug + gcTBs.getChild(nsGC, "travelbug").setStartElementListener(new StartElementListener() { + + public void start(Attributes attrs) { + trackable = new cgTrackable(); + + try { + if (attrs.getIndex("ref") > -1) { + trackable.geocode = attrs.getValue("ref").toUpperCase(); + } + } catch (Exception e) { + // nothing + } + } + }); + + // waypoint.cache.travelbug + final Element gcTB = gcTBs.getChild(nsGC, "travelbug"); + + gcTB.setEndElementListener(new EndElementListener() { + + public void end() { + if (trackable.geocode != null && trackable.geocode.length() > 0 && trackable.name != null && trackable.name.length() > 0) { + if (cache.inventory == null) + cache.inventory = new ArrayList<cgTrackable>(); + cache.inventory.add(trackable); + } + } + }); + + // waypoint.cache.travelbugs.travelbug.name + gcTB.getChild(nsGC, "name").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + String content = Html.fromHtml(body).toString(); + trackable.name = content; + } + }); + + // waypoint.cache.logs + final Element gcLogs = gcCache.getChild(nsGC, "logs"); + + // waypoint.cache.log + final Element gcLog = gcLogs.getChild(nsGC, "log"); + + gcLog.setStartElementListener(new StartElementListener() { + + public void start(Attributes attrs) { + log = new cgLog(); + + try { + if (attrs.getIndex("id") > -1) { + log.id = Integer.parseInt(attrs.getValue("id")); + } + } catch (Exception e) { + // nothing + } + } + }); + + gcLog.setEndElementListener(new EndElementListener() { + + public void end() { + if (log.log != null && log.log.length() > 0) { + if (cache.logs == null) + cache.logs = new ArrayList<cgLog>(); + cache.logs.add(log); + } + } + }); + + // waypoint.cache.logs.log.date + gcLog.getChild(nsGC, "date").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + try { + log.date = cgBase.dateGPXIn.parse(body.trim()).getTime(); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to parse log date: " + e.toString()); + } + } + }); + + // waypoint.cache.logs.log.type + gcLog.getChild(nsGC, "type").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + final String content = body.trim().toLowerCase(); + if (cgBase.logTypes0.containsKey(content) == true) { + log.type = cgBase.logTypes0.get(content); + } else { + log.type = 4; + } + } + }); + + // waypoint.cache.logs.log.finder + gcLog.getChild(nsGC, "finder").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + String content = Html.fromHtml(body).toString(); + log.author = content; + } + }); + + // waypoint.cache.logs.log.finder + gcLog.getChild(nsGC, "text").setEndTextElementListener(new EndTextElementListener() { + + public void end(String body) { + String content = Html.fromHtml(body).toString(); + log.log = content; + } + }); + } + + try { + Xml.parse(new FileInputStream(file), Xml.Encoding.UTF_8, root.getContentHandler()); + + return search.getCurrentId(); + } catch (Exception e) { + Log.e(cgSettings.tag, "Cannot parse .gpx file " + file.getAbsolutePath() + " as GPX " + version + ": " + e.toString()); + } + + return 0l; + } +} diff --git a/src/cgeo/geocaching/cgGPXView.java b/src/cgeo/geocaching/cgGPXView.java new file mode 100644 index 0000000..f68b477 --- /dev/null +++ b/src/cgeo/geocaching/cgGPXView.java @@ -0,0 +1,9 @@ +package cgeo.geocaching; + +import android.widget.TextView; + +public class cgGPXView { + // layouts & views + public TextView filepath; + public TextView filename; +} diff --git a/src/cgeo/geocaching/cgGeo.java b/src/cgeo/geocaching/cgGeo.java new file mode 100644 index 0000000..95c0b9d --- /dev/null +++ b/src/cgeo/geocaching/cgGeo.java @@ -0,0 +1,445 @@ +package cgeo.geocaching; + +import android.content.Context; +import android.content.SharedPreferences; +import android.location.GpsSatellite; +import android.location.GpsStatus; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; +import android.util.Log; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; + +public class cgGeo { + + private Context context = null; + private cgeoapplication app = null; + private LocationManager geoManager = null; + private cgUpdateLoc geoUpdate = null; + private cgWarning warning = null; + private cgBase base = null; + private cgSettings settings = null; + private SharedPreferences prefs = null; + private cgeoGeoListener geoNetListener = null; + private cgeoGeoListener geoGpsListener = null; + private cgeoGpsStatusListener geoGpsStatusListener = null; + private Integer time = 0; + private Integer distance = 0; + private Location locGps = null; + private Location locNet = null; + private long locGpsLast = 0l; + private boolean g4cRunning = false; + private Double lastGo4cacheLat = null; + private Double lastGo4cacheLon = null; + public Location location = null; + public int gps = -1; + public Double latitudeNow = null; + public Double longitudeNow = null; + public Double latitudeBefore = null; + public Double longitudeBefore = null; + public Double altitudeNow = null; + public Double bearingNow = null; + public Float speedNow = null; + public Float accuracyNow = null; + public Integer satellitesVisible = null; + public Integer satellitesFixed = null; + public double distanceNow = 0d; + + public cgGeo(Context contextIn, cgeoapplication appIn, cgUpdateLoc geoUpdateIn, cgBase baseIn, cgSettings settingsIn, cgWarning warningIn, int timeIn, int distanceIn) { + context = contextIn; + app = appIn; + geoUpdate = geoUpdateIn; + base = baseIn; + settings = settingsIn; + warning = warningIn; + time = timeIn; + distance = distanceIn; + + if (prefs == null) { + prefs = context.getSharedPreferences(cgSettings.preferences, 0); + } + distanceNow = prefs.getFloat("dst", 0f); + if (Double.isNaN(distanceNow) == true) { + distanceNow = 0d; + } + if (distanceNow == 0f) { + final SharedPreferences.Editor prefsEdit = context.getSharedPreferences(cgSettings.preferences, 0).edit(); + if (prefsEdit != null) { + prefsEdit.putLong("dst-since", System.currentTimeMillis()); + prefsEdit.commit(); + } + } + + geoNetListener = new cgeoGeoListener(); + geoNetListener.setProvider(LocationManager.NETWORK_PROVIDER); + + geoGpsListener = new cgeoGeoListener(); + geoGpsListener.setProvider(LocationManager.GPS_PROVIDER); + + geoGpsStatusListener = new cgeoGpsStatusListener(); + } + + public void initGeo() { + location = null; + gps = -1; + latitudeNow = null; + longitudeNow = null; + altitudeNow = null; + bearingNow = null; + speedNow = null; + accuracyNow = null; + satellitesVisible = 0; + satellitesFixed = 0; + + if (geoManager == null) { + geoManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + } + + lastLoc(); + + geoNetListener.setProvider(LocationManager.NETWORK_PROVIDER); + geoGpsListener.setProvider(LocationManager.GPS_PROVIDER); + geoManager.addGpsStatusListener(geoGpsStatusListener); + + try { + geoManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, time, distance, geoNetListener); + } catch (Exception e) { + Log.e(cgSettings.tag, "There is no NETWORK location provider"); + } + + try { + geoManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, time, distance, geoGpsListener); + } catch (Exception e) { + Log.e(cgSettings.tag, "There is no GPS location provider"); + } + } + + public void closeGeo() { + if (geoManager != null && geoNetListener != null) { + geoManager.removeUpdates(geoNetListener); + } + if (geoManager != null && geoGpsListener != null) { + geoManager.removeUpdates(geoGpsListener); + } + if (geoManager != null) { + geoManager.removeGpsStatusListener(geoGpsStatusListener); + } + + final SharedPreferences.Editor prefsEdit = context.getSharedPreferences(cgSettings.preferences, 0).edit(); + if (prefsEdit != null && Double.isNaN(distanceNow) == false) { + prefsEdit.putFloat("dst", (float) distanceNow); + prefsEdit.commit(); + } + } + + public void replaceUpdate(cgUpdateLoc geoUpdateIn) { + geoUpdate = geoUpdateIn; + + if (geoUpdate != null) { + geoUpdate.updateLoc(this); + } + } + + public class cgeoGeoListener implements LocationListener { + + public String active = null; + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + // nothing + } + + @Override + public void onLocationChanged(Location location) { + if (location.getProvider().equals(LocationManager.GPS_PROVIDER) == true) { + locGps = location; + locGpsLast = System.currentTimeMillis(); + } else if (location.getProvider().equals(LocationManager.NETWORK_PROVIDER) == true) { + locNet = location; + } + + selectBest(location.getProvider()); + } + + @Override + public void onProviderDisabled(String provider) { + if (provider.equals(LocationManager.NETWORK_PROVIDER) == true) { + if (geoManager != null && geoNetListener != null) { + geoManager.removeUpdates(geoNetListener); + } + } else if (provider.equals(LocationManager.GPS_PROVIDER) == true) { + if (geoManager != null && geoGpsListener != null) { + geoManager.removeUpdates(geoGpsListener); + } + } + } + + @Override + public void onProviderEnabled(String provider) { + if (provider.equals(LocationManager.NETWORK_PROVIDER) == true) { + if (geoNetListener == null) { + geoNetListener = new cgeoGeoListener(); + } + geoNetListener.setProvider(LocationManager.NETWORK_PROVIDER); + } else if (provider.equals(LocationManager.GPS_PROVIDER) == true) { + if (geoGpsListener == null) { + geoGpsListener = new cgeoGeoListener(); + } + geoGpsListener.setProvider(LocationManager.GPS_PROVIDER); + } + } + + public void setProvider(String provider) { + if (provider.equals(LocationManager.GPS_PROVIDER) == true) { + if (geoManager != null && geoManager.isProviderEnabled(LocationManager.GPS_PROVIDER) == true) { + active = provider; + } else { + active = null; + } + } else if (provider.equals(LocationManager.NETWORK_PROVIDER) == true) { + if (geoManager != null && geoManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) == true) { + active = provider; + } else { + active = null; + } + } + } + } + + public class cgeoGpsStatusListener implements GpsStatus.Listener { + + @Override + public void onGpsStatusChanged(int event) { + if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) { + GpsStatus status = geoManager.getGpsStatus(null); + Iterator<GpsSatellite> statusIterator = status.getSatellites().iterator(); + + int satellites = 0; + int fixed = 0; + + while (statusIterator.hasNext()) { + GpsSatellite sat = statusIterator.next(); + if (sat.usedInFix() == true) { + fixed++; + } + satellites++; + + /* satellite signal strength + if (sat.usedInFix()) { + Log.d(cgSettings.tag, "Sat #" + satellites + ": " + sat.getSnr() + " FIX"); + } else { + Log.d(cgSettings.tag, "Sat #" + satellites + ": " + sat.getSnr()); + } + */ + } + + boolean changed = false; + if (satellitesVisible == null || satellites != satellitesVisible) { + satellitesVisible = satellites; + changed = true; + } + if (satellitesFixed == null || fixed != satellitesFixed) { + satellitesFixed = fixed; + changed = true; + } + + if (changed == true) { + selectBest(null); + } + } + } + } + + private void selectBest(String initProvider) { + if (locNet == null && locGps != null) { // we have only GPS + assign(locGps); + return; + } + + if (locNet != null && locGps == null) { // we have only NET + assign(locNet); + return; + } + + if (satellitesFixed > 0) { // GPS seems to be fixed + assign(locGps); + return; + } + + if (initProvider != null && initProvider.equals(LocationManager.GPS_PROVIDER) == true) { // we have new location from GPS + assign(locGps); + return; + } + + if (locGpsLast > (System.currentTimeMillis() - 30 * 1000)) { // GPS was working in last 30 seconds + assign(locGps); + return; + } + + assign(locNet); // nothing else, using NET + } + + private void assign(Double lat, Double lon) { + if (lat == null || lon == null) { + return; + } + + gps = -1; + latitudeNow = lat; + longitudeNow = lon; + altitudeNow = null; + bearingNow = new Double(0); + speedNow = 0f; + accuracyNow = 999f; + + if (geoUpdate != null) { + geoUpdate.updateLoc(this); + } + } + + private void assign(Location loc) { + if (loc == null) { + gps = -1; + return; + } + + location = loc; + + String provider = location.getProvider(); + if (provider.equals(LocationManager.GPS_PROVIDER) == true) { + gps = 1; + } else if (provider.equals(LocationManager.NETWORK_PROVIDER) == true) { + gps = 0; + } else if (provider.equals("last") == true) { + gps = -1; + } + + latitudeNow = location.getLatitude(); + longitudeNow = location.getLongitude(); + app.setLastLoc(latitudeNow, longitudeNow); + + if (location.hasAltitude() && gps != -1) { + altitudeNow = location.getAltitude() + settings.altCorrection; + } else { + altitudeNow = null; + } + if (location.hasBearing() && gps != -1) { + bearingNow = new Double(location.getBearing()); + } else { + bearingNow = new Double(0); + } + if (location.hasSpeed() && gps != -1) { + speedNow = location.getSpeed(); + } else { + speedNow = 0f; + } + if (location.hasAccuracy() && gps != -1) { + accuracyNow = location.getAccuracy(); + } else { + accuracyNow = 999f; + } + + if (gps == 1) { + // save travelled distance only when location is from GPS + if (latitudeBefore != null && longitudeBefore != null && latitudeNow != null && longitudeNow != null) { + final double dst = cgBase.getDistance(latitudeBefore, longitudeBefore, latitudeNow, longitudeNow); + + if (Double.isNaN(dst) == false && dst > 0.005) { + distanceNow += dst; + + latitudeBefore = latitudeNow; + longitudeBefore = longitudeNow; + } + } else if (latitudeBefore == null || longitudeBefore == null) { // values aren't initialized + latitudeBefore = latitudeNow; + longitudeBefore = longitudeNow; + } + } + + if (geoUpdate != null) { + geoUpdate.updateLoc(this); + } + + if (gps > -1) { + (new publishLoc()).start(); + } + } + + private class publishLoc extends Thread { + + private publishLoc() { + setPriority(Thread.MIN_PRIORITY); + } + + @Override + public void run() { + if (g4cRunning == true) { + return; + } + + if (settings.publicLoc == 1 && (lastGo4cacheLat == null || lastGo4cacheLon == null || cgBase.getDistance(latitudeNow, longitudeNow, lastGo4cacheLat, lastGo4cacheLon) > 0.75)) { + g4cRunning = true; + + final String host = "api.go4cache.com"; + final String path = "/"; + final String method = "POST"; + String action = null; + if (app != null) { + action = app.getAction(); + } else { + action = ""; + } + + final String username = settings.getUsername(); + if (username != null) { + final HashMap<String, String> params = new HashMap<String, String>(); + final String latStr = String.format((Locale) null, "%.6f", latitudeNow); + final String lonStr = String.format((Locale) null, "%.6f", longitudeNow); + params.put("u", username); + params.put("lt", latStr); + params.put("ln", lonStr); + params.put("a", action); + params.put("s", (cgBase.sha1(username + "|" + latStr + "|" + lonStr + "|" + action + "|" + cgBase.md5("carnero: developing your dreams"))).toLowerCase()); + if (base.version != null) { + params.put("v", base.version); + } + final String res = base.request(false, host, path, method, params, false, false, false).getData(); + + if (res != null && res.length() > 0) { + lastGo4cacheLat = latitudeNow; + lastGo4cacheLon = longitudeNow; + } + } + } + + g4cRunning = false; + } + } + + public void lastLoc() { + assign(app.getLastLat(), app.getLastLon()); + + Location lastGps = geoManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); + + if (lastGps != null) { + lastGps.setProvider("last"); + assign(lastGps); + + Log.i(cgSettings.tag, "Using last location from GPS"); + return; + } + + Location lastGsm = geoManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); + + if (lastGsm != null) { + lastGsm.setProvider("last"); + assign(lastGsm); + + Log.i(cgSettings.tag, "Using last location from NETWORK"); + return; + } + } +} diff --git a/src/cgeo/geocaching/cgHtmlImg.java b/src/cgeo/geocaching/cgHtmlImg.java new file mode 100644 index 0000000..023912a --- /dev/null +++ b/src/cgeo/geocaching/cgHtmlImg.java @@ -0,0 +1,286 @@ +package cgeo.geocaching; + +import android.app.Activity; +import android.util.Log; +import android.text.Html; +import android.view.Display; +import android.view.WindowManager; +import android.content.Context; +import android.graphics.Rect; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.net.Uri; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.impl.client.DefaultHttpClient; + +public class cgHtmlImg implements Html.ImageGetter { + + private Activity activity = null; + private cgSettings settings = null; + private String geocode = null; + private boolean placement = true; + private int reason = 0; + private boolean onlySave = false; + private BitmapFactory.Options bfOptions = new BitmapFactory.Options(); + private Display display = null; + private int maxWidth = 0; + private int maxHeight = 0; + private double ratio = 1.0d; + private int width = 0; + private int height = 0; + + public cgHtmlImg(Activity activityIn, cgSettings settingsIn, String geocodeIn, boolean placementIn, int reasonIn, boolean onlySaveIn) { + activity = activityIn; + settings = settingsIn; + geocode = geocodeIn; + placement = placementIn; + reason = reasonIn; + onlySave = onlySaveIn; + + bfOptions.inTempStorage = new byte[16 * 1024]; + + display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + maxWidth = display.getWidth() - 25; + maxHeight = display.getHeight() - 25; + } + + @Override + public BitmapDrawable getDrawable(String url) { + Bitmap imagePre = null; + String dirName = null; + String fileName = null; + String fileNameSec = null; + + if (url == null || url.length() == 0) { + return null; + } + + final String[] urlParts = url.split("\\."); + String urlExt = null; + if (urlParts.length > 1) { + urlExt = "." + urlParts[(urlParts.length - 1)]; + if (urlExt.length() > 5) { + urlExt = ""; + } + } else { + urlExt = ""; + } + + if (geocode != null && geocode.length() > 0) { + dirName = settings.getStorage() + geocode + "/"; + fileName = settings.getStorage() + geocode + "/" + cgBase.md5(url) + urlExt; + fileNameSec = settings.getStorageSec() + geocode + "/" + cgBase.md5(url) + urlExt; + } else { + dirName = settings.getStorage() + "_others/"; + fileName = settings.getStorage() + "_others/" + cgBase.md5(url) + urlExt; + fileNameSec = settings.getStorageSec() + "_others/" + cgBase.md5(url) + urlExt; + } + + File dir = null; + dir = new File(settings.getStorage()); + if (dir.exists() == false) { + dir.mkdirs(); + } + dir = new File(dirName); + if (dir.exists() == false) { + dir.mkdirs(); + } + dir = null; + + // load image from cache + if (onlySave == false) { + try { + final Date now = new Date(); + + final File file = new File(fileName); + if (file.exists() == true) { + final long imageSize = file.length(); + + // large images will be downscaled on input to save memory + if (imageSize > (6 * 1024 * 1024)) { + bfOptions.inSampleSize = 48; + } else if (imageSize > (4 * 1024 * 1024)) { + bfOptions.inSampleSize = 16; + } else if (imageSize > (2 * 1024 * 1024)) { + bfOptions.inSampleSize = 10; + } else if (imageSize > (1 * 1024 * 1024)) { + bfOptions.inSampleSize = 6; + } else if (imageSize > (0.5 * 1024 * 1024)) { + bfOptions.inSampleSize = 2; + } + + if (reason > 0 || file.lastModified() > (now.getTime() - (24 * 60 * 60 * 1000))) { + imagePre = BitmapFactory.decodeFile(fileName, bfOptions); + } + } + + if (imagePre == null) { + final File fileSec = new File(fileNameSec); + if (fileSec.exists() == true) { + final long imageSize = fileSec.length(); + + // large images will be downscaled on input to save memory + if (imageSize > (6 * 1024 * 1024)) { + bfOptions.inSampleSize = 48; + } else if (imageSize > (4 * 1024 * 1024)) { + bfOptions.inSampleSize = 16; + } else if (imageSize > (2 * 1024 * 1024)) { + bfOptions.inSampleSize = 10; + } else if (imageSize > (1 * 1024 * 1024)) { + bfOptions.inSampleSize = 6; + } else if (imageSize > (0.5 * 1024 * 1024)) { + bfOptions.inSampleSize = 2; + } + + if (reason > 0 || file.lastModified() > (now.getTime() - (24 * 60 * 60 * 1000))) { + imagePre = BitmapFactory.decodeFile(fileNameSec, bfOptions); + } + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgHtmlImg.getDrawable (reading cache): " + e.toString()); + } + } + + // download image and save it to the cache + if ((imagePre == null && reason == 0) || onlySave == true) { + Uri uri = null; + HttpClient client = null; + HttpGet getMethod = null; + HttpResponse httpResponse = null; + HttpEntity entity = null; + BufferedHttpEntity bufferedEntity = null; + + try { + // check if uri is absolute or not, if not attach geocaching.com hostname and scheme + uri = Uri.parse(url); + + if (uri.isAbsolute() == false) { + url = "http://www.geocaching.com" + url; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgHtmlImg.getDrawable (parse URL): " + e.toString()); + } + + if (uri != null) { + for (int i = 0; i < 2; i++) { + if (i > 0) { + Log.w(cgSettings.tag, "cgHtmlImg.getDrawable: Failed to download data, retrying. Attempt #" + (i + 1)); + } + + try { + client = new DefaultHttpClient(); + getMethod = new HttpGet(url); + httpResponse = client.execute(getMethod); + entity = httpResponse.getEntity(); + bufferedEntity = new BufferedHttpEntity(entity); + + final long imageSize = bufferedEntity.getContentLength(); + + // large images will be downscaled on input to save memory + if (imageSize > (6 * 1024 * 1024)) { + bfOptions.inSampleSize = 48; + } else if (imageSize > (4 * 1024 * 1024)) { + bfOptions.inSampleSize = 16; + } else if (imageSize > (2 * 1024 * 1024)) { + bfOptions.inSampleSize = 10; + } else if (imageSize > (1 * 1024 * 1024)) { + bfOptions.inSampleSize = 6; + } else if (imageSize > (0.5 * 1024 * 1024)) { + bfOptions.inSampleSize = 2; + } + + if (bufferedEntity != null) { + imagePre = BitmapFactory.decodeStream(bufferedEntity.getContent(), null, bfOptions); + } + if (imagePre != null) { + break; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgHtmlImg.getDrawable (downloading from web): " + e.toString()); + } + } + } + + try { + // save to memory/SD cache + if (bufferedEntity != null) { + final InputStream is = (InputStream) bufferedEntity.getContent(); + final FileOutputStream fos = new FileOutputStream(fileName); + try { + final byte[] buffer = new byte[4096]; + int l; + while ((l = is.read(buffer)) != -1) { + fos.write(buffer, 0, l); + } + } catch (IOException e) { + Log.e(cgSettings.tag, "cgHtmlImg.getDrawable (saving to cache): " + e.toString()); + } finally { + is.close(); + fos.flush(); + fos.close(); + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgHtmlImg.getDrawable (saving to cache): " + e.toString()); + } + + entity = null; + bufferedEntity = null; + } + + if (onlySave == true) { + return null; + } + + // get image and return + if (imagePre == null) { + Log.d(cgSettings.tag, "cgHtmlImg.getDrawable: Failed to obtain image"); + + if (placement == false) { + imagePre = BitmapFactory.decodeResource(activity.getResources(), R.drawable.image_no_placement); + } else { + imagePre = BitmapFactory.decodeResource(activity.getResources(), R.drawable.image_not_loaded); + } + } + + final int imgWidth = imagePre.getWidth(); + final int imgHeight = imagePre.getHeight(); + + if (imgWidth > maxWidth || imgHeight > maxHeight) { + if ((maxWidth / imgWidth) > (maxHeight / imgHeight)) { + ratio = (double) maxHeight / (double) imgHeight; + } else { + ratio = (double) maxWidth / (double) imgWidth; + } + + width = (int) Math.ceil(imgWidth * ratio); + height = (int) Math.ceil(imgHeight * ratio); + + try { + imagePre = Bitmap.createScaledBitmap(imagePre, width, height, true); + } catch (Exception e) { + Log.d(cgSettings.tag, "cgHtmlImg.getDrawable: Failed to scale image"); + return null; + } + } else { + width = imgWidth; + height = imgHeight; + } + + final BitmapDrawable image = new BitmapDrawable(imagePre); + image.setBounds(new Rect(0, 0, width, height)); + + return image; + } +} diff --git a/src/cgeo/geocaching/cgList.java b/src/cgeo/geocaching/cgList.java new file mode 100644 index 0000000..65d08ac --- /dev/null +++ b/src/cgeo/geocaching/cgList.java @@ -0,0 +1,20 @@ +package cgeo.geocaching; + +public class cgList { + public boolean def = false; + public int id = 0; + public String title = null; + public Long updated = null; + public Double latitude = null; + public Double longitude = null; + + public cgList(boolean defIn) { + def = defIn; + } + + public cgList(boolean defIn, int idIn, String titleIn) { + def = defIn; + id = idIn; + title = titleIn; + } +} diff --git a/src/cgeo/geocaching/cgLog.java b/src/cgeo/geocaching/cgLog.java new file mode 100644 index 0000000..5b1b44a --- /dev/null +++ b/src/cgeo/geocaching/cgLog.java @@ -0,0 +1,12 @@ +package cgeo.geocaching; + +public class cgLog { + public int id = 0; + public int type = 4; // note + public String author = ""; + public String log = ""; + public long date = 0; + public int found = -1; + public String cacheName = ""; // used for trackables + public String cacheGuid = ""; // used for trackables +} diff --git a/src/cgeo/geocaching/cgLogForm.java b/src/cgeo/geocaching/cgLogForm.java new file mode 100644 index 0000000..9cbd557 --- /dev/null +++ b/src/cgeo/geocaching/cgLogForm.java @@ -0,0 +1,10 @@ +package cgeo.geocaching; + +import android.app.Activity; +import java.util.Calendar; + +public class cgLogForm extends Activity { + public void setDate(Calendar dateIn) { + // to be overwritten + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgMapImg.java b/src/cgeo/geocaching/cgMapImg.java new file mode 100644 index 0000000..57639ff --- /dev/null +++ b/src/cgeo/geocaching/cgMapImg.java @@ -0,0 +1,114 @@ +package cgeo.geocaching; + +import android.util.Log; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.impl.client.DefaultHttpClient; + +public class cgMapImg { + /** + * in my tests the "no image available" image had 5470 bytes, while "street only" maps had at least 20000 bytes + */ + private static final int MIN_MAP_IMAGE_BYTES = 6000; + private cgSettings settings = null; + private String geocode = null; + + public cgMapImg(cgSettings settingsIn, String geocodeIn) { + geocode = geocodeIn; + settings = settingsIn; + + if (geocode != null && geocode.length() > 0) { + final String dirName = settings.getStorage() + geocode + "/"; + + File dir = null; + dir = new File(settings.getStorage()); + if (dir.exists() == false) { + dir.mkdirs(); + } + dir = new File(dirName); + if (dir.exists() == false) { + dir.mkdirs(); + } + dir = null; + } + } + + public void getDrawable(String url, int level) { + if (url == null || url.length() == 0) { + return; + } + + if (geocode == null || geocode.length() == 0) { + return; + } + + final String fileName = settings.getStorage() + geocode + "/map_" + level; + HttpClient client = null; + HttpGet getMethod = null; + HttpResponse httpResponse = null; + HttpEntity entity = null; + BufferedHttpEntity bufferedEntity = null; + + boolean ok = false; + + for (int i = 0; i < 3; i ++) { + if (i > 0) Log.w(cgSettings.tag, "cgMapImg.getDrawable: Failed to download data, retrying. Attempt #" + (i + 1)); + + try { + client = new DefaultHttpClient(); + getMethod = new HttpGet(url); + httpResponse = client.execute(getMethod); + entity = httpResponse.getEntity(); + + // if image is to small, don't download and save, there is no map data for this zoom level + long contentSize = entity.getContentLength(); + if (contentSize > 0 && contentSize <= MIN_MAP_IMAGE_BYTES) { + break; + } + + bufferedEntity = new BufferedHttpEntity(entity); + if (bufferedEntity != null) { + InputStream is = (InputStream)bufferedEntity.getContent(); + FileOutputStream fos = new FileOutputStream(fileName); + + int fileSize = 0; + try { + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = is.read(buffer)) != -1) { + fos.write(buffer, 0, bytesRead); + fileSize += bytesRead; + } + ok = true; + } catch (IOException e) { + Log.e(cgSettings.tag, "cgMapImg.getDrawable (saving to cache): " + e.toString()); + } finally { + is.close(); + fos.flush(); + fos.close(); + } + + bufferedEntity = null; + + // delete image if it has no contents + if (ok && fileSize < MIN_MAP_IMAGE_BYTES) { + (new File(fileName)).delete(); + } + } + + if (ok == true) { + break; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgMapImg.getDrawable (downloading from web): " + e.toString()); + } + } + } +} diff --git a/src/cgeo/geocaching/cgOAuth.java b/src/cgeo/geocaching/cgOAuth.java new file mode 100644 index 0000000..8897993 --- /dev/null +++ b/src/cgeo/geocaching/cgOAuth.java @@ -0,0 +1,53 @@ +package cgeo.geocaching; + +import java.util.Date; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashMap; + +public class cgOAuth { + public static String signOAuth(String host, String path, String method, boolean https, HashMap<String, String> params, String token, String tokenSecret) { + String paramsDone = ""; + if (method.equalsIgnoreCase("GET") == false && method.equalsIgnoreCase("POST") == false) { + method = "POST"; + } else { + method = method.toUpperCase(); + } + + if (token == null) token = ""; + if (tokenSecret == null) tokenSecret = ""; + + long currentTime = new Date().getTime(); // miliseconds + currentTime = currentTime / 1000; // seconds + currentTime = (long)Math.floor(currentTime); + + params.put("oauth_consumer_key", cgSettings.keyConsumerPublic); + params.put("oauth_nonce", cgBase.md5(Long.toString(System.currentTimeMillis()))); + params.put("oauth_signature_method", "HMAC-SHA1"); + params.put("oauth_timestamp", Long.toString(currentTime)); + params.put("oauth_token", token); + params.put("oauth_version", "1.0"); + + Object[] keys = params.keySet().toArray(); + Arrays.sort(keys); + + ArrayList<String> paramsEncoded = new ArrayList<String>(); + for (int i = 0; i < keys.length; i++) { + String value = params.get(keys[i].toString()); + + paramsEncoded.add(keys[i] + "=" + cgBase.urlencode_rfc3986(value.toString())); + } + + String keysPacked; + String requestPacked; + + keysPacked = cgSettings.keyConsumerSecret + "&" + tokenSecret; // both even if empty some of them! + if (https == true) requestPacked = method + "&" + cgBase.urlencode_rfc3986("https://" + host + path) + "&" + cgBase.urlencode_rfc3986(cgBase.implode("&", paramsEncoded.toArray())); + else requestPacked = method + "&" + cgBase.urlencode_rfc3986("http://" + host + path) + "&" + cgBase.urlencode_rfc3986(cgBase.implode("&", paramsEncoded.toArray())); + paramsEncoded.add("oauth_signature=" + cgBase.urlencode_rfc3986(cgBase.base64Encode(cgBase.hashHmac(requestPacked, keysPacked)))); + + paramsDone = cgBase.implode("&", paramsEncoded.toArray()); + + return paramsDone; + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgRating.java b/src/cgeo/geocaching/cgRating.java new file mode 100644 index 0000000..374ae7c --- /dev/null +++ b/src/cgeo/geocaching/cgRating.java @@ -0,0 +1,7 @@ +package cgeo.geocaching; + +public class cgRating { + public Float rating = null; + public Integer votes = null; + public Float myVote = null; +} diff --git a/src/cgeo/geocaching/cgResponse.java b/src/cgeo/geocaching/cgResponse.java new file mode 100644 index 0000000..6735d85 --- /dev/null +++ b/src/cgeo/geocaching/cgResponse.java @@ -0,0 +1,40 @@ +package cgeo.geocaching; + +public class cgResponse { + private String url; + private int statusCode; + private String statusMessage; + private String data; + + public void setUrl(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + public void setStatusCode(int code) { + statusCode = code; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusMessage(String message) { + statusMessage = message; + } + + public String getStatusMessage() { + return statusMessage; + } + + public void setData(String data) { + this.data = data; + } + + public String getData() { + return data; + } +} diff --git a/src/cgeo/geocaching/cgSearch.java b/src/cgeo/geocaching/cgSearch.java new file mode 100644 index 0000000..a6638e5 --- /dev/null +++ b/src/cgeo/geocaching/cgSearch.java @@ -0,0 +1,39 @@ +package cgeo.geocaching; + +import java.util.ArrayList; + +public class cgSearch { + private Long id = null; + private ArrayList<String> geocodes = new ArrayList<String>(); + + public int errorRetrieve = 0; + public String error = null; + public String url = ""; + public String viewstate = ""; + public String viewstate1 = ""; + public int totalCnt = 0; + + public cgSearch() { + id = System.currentTimeMillis(); // possible collisions here - not guaranteed to be unique + } + + public Long getCurrentId() { + return id; + } + + public ArrayList<String> getGeocodes() { + return geocodes; + } + + public int getCount() { + return geocodes.size(); + } + + public void addGeocode(String geocode) { + if (geocodes == null) { + geocodes = new ArrayList<String>(); + } + + geocodes.add(geocode); + } +} diff --git a/src/cgeo/geocaching/cgSearchHandler.java b/src/cgeo/geocaching/cgSearchHandler.java new file mode 100644 index 0000000..3563c6d --- /dev/null +++ b/src/cgeo/geocaching/cgSearchHandler.java @@ -0,0 +1,104 @@ +package cgeo.geocaching; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.ImageView; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +public class cgSearchHandler extends Handler { + private Activity activity = null; + private Resources res = null; + private cgSearchThread recaptchaThread = null; + private ImageView imageView = null; + private Bitmap img = null; + + private Handler imgHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + try { + if (img != null && imageView != null) { + imageView.setImageBitmap(img); + } + } catch (Exception e) { + // nothing + } + } + }; + + public cgSearchHandler(Activity activityIn, Resources resIn, cgSearchThread recaptchaThreadIn) { + activity = activityIn; + res = resIn; + recaptchaThread = recaptchaThreadIn; + } + + @Override + public void handleMessage(Message msg) { + try { + if (msg.what == 1) { + final AlertDialog.Builder dlg = new AlertDialog.Builder(activity); + final LayoutInflater inflater = activity.getLayoutInflater(); + final View view = inflater.inflate(R.layout.recaptcha_dialog, null); + + imageView = (ImageView) view.findViewById(R.id.image); + + (new getCaptcha(new URL("http://www.google.com/recaptcha/api/image?c=" + recaptchaThread.getChallenge()))).start(); + + dlg.setTitle(res.getString(R.string.caches_recaptcha_title)); + dlg.setView(view); + dlg.setNeutralButton(res.getString(R.string.caches_recaptcha_continue), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + final String text = ((EditText) view.findViewById(R.id.text)).getText().toString(); + + recaptchaThread.setText(text); + + dialog.cancel(); + } + }); + + dlg.create().show(); + } + } catch (Exception e) { + // nothing + } + } + + private class getCaptcha extends Thread { + private URL uri = null; + + public getCaptcha(URL uriIn) { + uri = uriIn; + } + + @Override + public void run() { + try { + HttpURLConnection connection = (HttpURLConnection)uri.openConnection(); + connection.setDoInput(true); + connection.connect(); + + InputStream is = connection.getInputStream(); + + img = BitmapFactory.decodeStream(is); + + is.close(); + + imgHandler.sendEmptyMessage(0); + } catch (IOException e) { + Log.e(cgSettings.tag, "Failed to download reCAPTCHA image"); + } + } + } +} diff --git a/src/cgeo/geocaching/cgSearchThread.java b/src/cgeo/geocaching/cgSearchThread.java new file mode 100644 index 0000000..0cd99ce --- /dev/null +++ b/src/cgeo/geocaching/cgSearchThread.java @@ -0,0 +1,46 @@ +package cgeo.geocaching; + +import android.os.Handler; +import android.util.Log; + +public class cgSearchThread extends Thread { + private Handler recaptchaHandler = null; + private String recaptchaChallenge = null; + private String recaptchaText = null; + + public void setRecaptchaHandler(Handler recaptchaHandlerIn) { + recaptchaHandler = recaptchaHandlerIn; + } + + public void notifyNeed() { + if (recaptchaHandler != null) { + recaptchaHandler.sendEmptyMessage(1); + } + } + + public synchronized void waitForUser() { + try { + wait(); + } catch (InterruptedException e) { + Log.w(cgSettings.tag, "searchThread is not waiting for user..."); + } + } + + public void setChallenge(String challenge) { + recaptchaChallenge = challenge; + } + + public String getChallenge() { + return recaptchaChallenge; + } + + public synchronized void setText(String text) { + recaptchaText = text; + + notify(); + } + + public String getText() { + return recaptchaText; + } +} diff --git a/src/cgeo/geocaching/cgSettings.java b/src/cgeo/geocaching/cgSettings.java new file mode 100644 index 0000000..b88de0b --- /dev/null +++ b/src/cgeo/geocaching/cgSettings.java @@ -0,0 +1,513 @@ +package cgeo.geocaching; + +import java.util.Locale; +import java.util.Map; +import java.util.HashMap; + +import cgeo.geocaching.googlemaps.googleMapFactory; +import cgeo.geocaching.mapinterfaces.MapFactory; +import cgeo.geocaching.mapsforge.mfMapFactory; +import android.os.Environment; +import android.content.Intent; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.util.Log; + +public class cgSettings { + + public enum mapSourceEnum { + googleMap, + mapsforgeMapnik, + mapsforgeOsmarender, + mapsforgeCycle, + mapsforgeOffline; + + static mapSourceEnum fromInt(int id) { + mapSourceEnum[] values = mapSourceEnum.values(); + if (id >= 0 && id < values.length) { + return values[id]; + } else { + return googleMap; + } + } + } + + // constants + public final static int unitsMetric = 1; + public final static int unitsImperial = 2; + public final static int mapSatellite = 1; + public final static int mapClassic = 2; + public final static String cache = ".cgeo"; + public final static String analytics = "UA-1103507-15"; + + // twitter api keys + public final static String keyConsumerPublic = "RFafPiNi3xRhcS1TPE3wTw"; + public final static String keyConsumerSecret = "7iDJprNPI9hzRwWhpzycSr9SPZMFrdVdsxD2OauI9k"; + + // version + public int version = 0; + + // skin + public int skin = 0; + public int buttonActive = R.drawable.action_button_dark; + public int buttonInactive = R.drawable.action_button_dark_off; + public int buttonPressed = R.drawable.action_button_dark_pressed; + + // settings + public boolean loaded = false; + public boolean hideMySearch = false; + public int helper = 0; + public int initialized = 0; + public String languages = null; + public int cachesFound = 0; + public int autoLoadDesc = 0; + public int units = unitsMetric; + public int livelist = 1; + public int maptype = mapSatellite; + public int mapzoom = 14; + public int maplive = 1; + public int maptrail = 1; + public boolean useEnglish = false; + public boolean showCaptcha = false; + public int excludeMine = 0; + public int excludeDisabled = 0; + public int storeOfflineMaps = 0; + public int asBrowser = 1; + public int useCompass = 1; + public int useGNavigation = 1; + public int showAddress = 1; + public int publicLoc = 0; + public int twitter = 0; + public int altCorrection = 0; + public String signature = null; + public String cacheType = null; + public String tokenPublic = null; + public String tokenSecret = null; + public String webDeviceName = null; + public String webDeviceCode = null; + + // usable values + public static final String tag = "c:geo"; + + // preferences file + public static final String preferences = "cgeo.pref"; + + // private variables + private Context context = null; + private SharedPreferences prefs = null; + private String username = null; + private String password = null; + private String passVote = null; + + // maps + public static final int MAP_GOOGLE = 0; + public static final int MAP_MF = 1; + public MapFactory mapFactory = null; + public mapSourceEnum mapProviderUsed = mapSourceEnum.googleMap; + public mapSourceEnum mapProvider = mapSourceEnum.googleMap; + public String mapFile = null; + + public cgSettings(Context contextIn, SharedPreferences prefsIn) { + context = contextIn; + prefs = prefsIn; + + load(); + } + + public void load() { + version = prefs.getInt("version", 0); + + initialized = prefs.getInt("initialized", 0); + helper = prefs.getInt("helper", 0); + + skin = prefs.getInt("skin", 0); + setSkinDefaults(); + + languages = prefs.getString("languages", null); + cachesFound = prefs.getInt("found", 0); + autoLoadDesc = prefs.getInt("autoloaddesc", 0); + units = prefs.getInt("units", 1); + livelist = prefs.getInt("livelist", 1); + maptype = prefs.getInt("maptype", 1); + maplive = prefs.getInt("maplive", 1); + mapzoom = prefs.getInt("mapzoom", 14); + maptrail = prefs.getInt("maptrail", 1); + useEnglish = prefs.getBoolean("useenglish", false); + showCaptcha = prefs.getBoolean("showcaptcha", false); + excludeMine = prefs.getInt("excludemine", 0); + excludeDisabled = prefs.getInt("excludedisabled", 0); + storeOfflineMaps = prefs.getInt("offlinemaps", 1); + asBrowser = prefs.getInt("asbrowser", 1); + useCompass = prefs.getInt("usecompass", 1); + useGNavigation = prefs.getInt("usegnav", 1); + showAddress = prefs.getInt("showaddress", 1); + publicLoc = prefs.getInt("publicloc", 0); + twitter = prefs.getInt("twitter", 0); + altCorrection = prefs.getInt("altcorrection", 0); + signature = prefs.getString("signature", null); + cacheType = prefs.getString("cachetype", null); + tokenPublic = prefs.getString("tokenpublic", null); + tokenSecret = prefs.getString("tokensecret", null); + mapFile = prefs.getString("mfmapfile", null); + mapProvider = mapSourceEnum.fromInt(prefs.getInt("mapsource", 0)); + webDeviceName = prefs.getString("webDeviceName", null); + webDeviceCode = prefs.getString("webDeviceCode", null); + + setLanguage(useEnglish); + } + + private void setSkinDefaults() { + if (skin == 1) { + buttonActive = R.drawable.action_button_light; + buttonInactive = R.drawable.action_button_light_off; + buttonPressed = R.drawable.action_button_light_pressed; + } else { + skin = 0; + buttonActive = R.drawable.action_button_dark; + buttonInactive = R.drawable.action_button_dark_off; + buttonPressed = R.drawable.action_button_dark_pressed; + } + } + + public void setSkin(int skinIn) { + if (skin == 1) { + skin = 1; + setSkinDefaults(); + } else { + skin = 0; + setSkinDefaults(); + } + } + + public void setLanguage(boolean useEnglish) { + Locale locale = Locale.getDefault(); + if (useEnglish) { + locale = new Locale("en"); + } + Configuration config = new Configuration(); + config.locale = locale; + context.getResources().updateConfiguration(config, + context.getResources().getDisplayMetrics()); + } + + public void reloadTwitterTokens() { + tokenPublic = prefs.getString("tokenpublic", null); + tokenSecret = prefs.getString("tokensecret", null); + } + + public void reloadCacheType() { + cacheType = prefs.getString("cachetype", null); + } + + public String getStorage() { + return getStorageSpecific(null)[0]; + } + + public String getStorageSec() { + return getStorageSpecific(null)[1]; + } + + public String[] getStorageSpecific(Boolean hidden) { + String[] storage = new String[2]; + + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + storage[0] = Environment.getExternalStorageDirectory() + "/" + cache + "/"; + storage[1] = Environment.getDataDirectory() + "/data/cgeo.geocaching/" + cache + "/"; + } else { + storage[0] = Environment.getDataDirectory() + "/data/cgeo.geocaching/" + cache + "/"; + storage[1] = Environment.getExternalStorageDirectory() + "/" + cache + "/"; + } + + return storage; + } + + public boolean isLogin() { + final String preUsername = prefs.getString("username", null); + final String prePassword = prefs.getString("password", null); + + if (preUsername == null || prePassword == null || preUsername.length() == 0 || prePassword.length() == 0) { + return false; + } else { + return true; + } + } + + public HashMap<String, String> getLogin() { + final HashMap<String, String> login = new HashMap<String, String>(); + + if (username == null || password == null) { + final String preUsername = prefs.getString("username", null); + final String prePassword = prefs.getString("password", null); + + if (initialized == 0 && (preUsername == null || prePassword == null)) { + Intent initIntent = new Intent(context, cgeoinit.class); + context.startActivity(initIntent); + + final SharedPreferences.Editor prefsEdit = prefs.edit(); + prefsEdit.putInt("initialized", 1); + prefsEdit.commit(); + + initialized = 1; + + return null; + } else if (initialized == 1 && (preUsername == null || prePassword == null)) { + return null; + } + + login.put("username", preUsername); + login.put("password", prePassword); + + username = preUsername; + password = prePassword; + } else { + login.put("username", username); + login.put("password", password); + } + + return login; + } + + public String getUsername() { + String user = null; + + if (username == null) { + user = prefs.getString("username", null); + } else { + user = username; + } + + return user; + } + + public boolean setLogin(String username, String password) { + final SharedPreferences.Editor prefsEdit = prefs.edit(); + + if (username == null || username.length() == 0 || password == null || password.length() == 0) { + // erase username and password + prefsEdit.remove("username"); + prefsEdit.remove("password"); + } else { + // save username and password + prefsEdit.putString("username", username); + prefsEdit.putString("password", password); + } + + this.username = username; + this.password = password; + + return prefsEdit.commit(); + } + + public boolean isGCvoteLogin() { + final String preUsername = prefs.getString("username", null); + final String prePassword = prefs.getString("pass-vote", null); + + if (preUsername == null || prePassword == null || preUsername.length() == 0 || prePassword.length() == 0) { + return false; + } else { + return true; + } + } + + public boolean setGCvoteLogin(String password) { + final SharedPreferences.Editor prefsEdit = prefs.edit(); + + if (password == null || password.length() == 0) { + // erase password + prefsEdit.remove("pass-vote"); + } else { + // save password + prefsEdit.putString("pass-vote", password); + } + + passVote = password; + + return prefsEdit.commit(); + } + + public HashMap<String, String> getGCvoteLogin() { + final HashMap<String, String> login = new HashMap<String, String>(); + + if (username == null || password == null) { + final String preUsername = prefs.getString("username", null); + final String prePassword = prefs.getString("pass-vote", null); + + if (preUsername == null || prePassword == null) { + return null; + } + + login.put("username", preUsername); + login.put("password", prePassword); + + username = preUsername; + passVote = prePassword; + } else { + login.put("username", username); + login.put("password", password); + } + + return login; + } + + public boolean setSignature(String signature) { + final SharedPreferences.Editor prefsEdit = prefs.edit(); + + if (signature == null || signature.length() == 0) { + // erase signature + prefsEdit.remove("signature"); + } else { + // save signature + prefsEdit.putString("signature", signature); + } + + this.signature = signature; + + return prefsEdit.commit(); + } + + public String getSignature() { + return prefs.getString("signature", null); + } + + public boolean setLanguages(String languages) { + final SharedPreferences.Editor prefsEdit = prefs.edit(); + + if (languages == null || languages.length() == 0) { + // erase languages + prefsEdit.remove("languages"); + } else { + // save langauges + languages = languages.toLowerCase(); + languages = languages.replaceAll("([^a-z]+)", " "); + languages = languages.replaceAll("([ ]+)", " "); + + prefsEdit.putString("languages", languages); + } + + this.languages = languages; + + return prefsEdit.commit(); + } + + public boolean setAltCorrection(int altitude) { + final SharedPreferences.Editor prefsEdit = prefs.edit(); + + prefsEdit.putInt("altcorrection", altitude); + + altCorrection = altitude; + + return prefsEdit.commit(); + } + + public String getLanguages() { + return prefs.getString("languages", null); + } + + public void deleteCookies() { + SharedPreferences.Editor prefsEdit = prefs.edit(); + + // delete cookies + Map<String, ?> prefsValues = prefs.getAll(); + + if (prefsValues != null && prefsValues.size() > 0) { + Log.i(cgSettings.tag, "Removing cookies"); + + Object[] keys = prefsValues.keySet().toArray(); + + for (int i = 0; i < keys.length; i++) { + if (keys[i].toString().length() > 7 && keys[i].toString().substring(0, 7).equals("cookie_") == true) { + prefsEdit.remove(keys[i].toString()); + } + } + } + + prefsEdit.commit(); + } + + public String setCacheType(String cacheTypeIn) { + final SharedPreferences.Editor edit = prefs.edit(); + edit.putString("cachetype", cacheTypeIn); + edit.commit(); + + cacheType = cacheTypeIn; + + return cacheType; + } + + public void liveMapEnable() { + final SharedPreferences.Editor edit = prefs.edit(); + edit.putInt("maplive", 1); + + if (edit.commit() == true) { + maplive = 1; + } + } + + public void liveMapDisable() { + final SharedPreferences.Editor edit = prefs.edit(); + edit.putInt("maplive", 0); + + if (edit.commit() == true) { + maplive = 0; + } + } + + public int getLastList() { + int listId = prefs.getInt("lastlist", -1); + + return listId; + } + + public void saveLastList(int listId) { + final SharedPreferences.Editor edit = prefs.edit(); + + edit.putInt("lastlist", listId); + edit.commit(); + } + + public void setWebNameCode(String name, String code) { + final SharedPreferences.Editor edit = prefs.edit(); + + this.webDeviceCode=code; + this.webDeviceName=name; + + edit.putString("webDeviceName", name); + edit.putString("webDeviceCode", code); + edit.commit(); + } + + public MapFactory getMapFactory() { + if (mapProvider == mapSourceEnum.googleMap) { + if (mapProviderUsed != mapSourceEnum.googleMap || mapFactory == null) { + mapFactory = new googleMapFactory(); + mapProviderUsed = mapProvider; + } + } else if (mapProvider != mapSourceEnum.googleMap) { + if (mapProviderUsed == mapSourceEnum.googleMap || mapFactory == null) { + mapFactory = new mfMapFactory(); + mapProviderUsed = mapProvider; + } + } + + return mapFactory; + } + + public String getMapFile() { + return mapFile; + } + + public boolean setMapFile(String mapFileIn) { + final SharedPreferences.Editor prefsEdit = prefs.edit(); + + prefsEdit.putString("mfmapfile", mapFileIn); + + mapFile = mapFileIn; + + return prefsEdit.commit(); + } + + public Context getContext() { + return context; + } +} diff --git a/src/cgeo/geocaching/cgSpoiler.java b/src/cgeo/geocaching/cgSpoiler.java new file mode 100644 index 0000000..fca5648 --- /dev/null +++ b/src/cgeo/geocaching/cgSpoiler.java @@ -0,0 +1,7 @@ +package cgeo.geocaching; + +public class cgSpoiler { + public String url = ""; + public String title = ""; + public String description = ""; +} diff --git a/src/cgeo/geocaching/cgTrackable.java b/src/cgeo/geocaching/cgTrackable.java new file mode 100644 index 0000000..2244fc7 --- /dev/null +++ b/src/cgeo/geocaching/cgTrackable.java @@ -0,0 +1,35 @@ +package cgeo.geocaching; + +import android.text.Spannable; +import java.util.ArrayList; +import java.util.Date; + +public class cgTrackable { + static public int SPOTTED_UNSET = 0; + static public int SPOTTED_CACHE = 1; + static public int SPOTTED_USER = 2; + static public int SPOTTED_UNKNOWN = 3; + static public int SPOTTED_OWNER = 4; + + public int errorRetrieve = 0; + public String error = ""; + public String guid = ""; + public String geocode = ""; + public String iconUrl = ""; + public String name = ""; + public String nameString = null; + public Spannable nameSp = null; + public String type = null; + public Date released = null; + public Double distance = null; + public String origin = null; + public String owner = null; + public String ownerGuid = null; + public String spottedName = null; + public int spottedType = SPOTTED_UNSET; + public String spottedGuid = null; + public String goal = null; + public String details = null; + public String image = null; + public ArrayList<cgLog> logs = new ArrayList<cgLog>(); +} diff --git a/src/cgeo/geocaching/cgTrackableLog.java b/src/cgeo/geocaching/cgTrackableLog.java new file mode 100644 index 0000000..7522d8d --- /dev/null +++ b/src/cgeo/geocaching/cgTrackableLog.java @@ -0,0 +1,9 @@ +package cgeo.geocaching; + +public class cgTrackableLog { + public int ctl = -1; + public int id = -1; + public String trackCode = null; + public String name = null; + public int action = 0; // base.logTrackablesAction - no action +} diff --git a/src/cgeo/geocaching/cgUpdateDir.java b/src/cgeo/geocaching/cgUpdateDir.java new file mode 100644 index 0000000..01346ec --- /dev/null +++ b/src/cgeo/geocaching/cgUpdateDir.java @@ -0,0 +1,7 @@ +package cgeo.geocaching; + +public class cgUpdateDir { + public void updateDir(cgDirection dir) { + // to be overriden + } +} diff --git a/src/cgeo/geocaching/cgUpdateLoc.java b/src/cgeo/geocaching/cgUpdateLoc.java new file mode 100644 index 0000000..3c063f2 --- /dev/null +++ b/src/cgeo/geocaching/cgUpdateLoc.java @@ -0,0 +1,7 @@ +package cgeo.geocaching; + +public class cgUpdateLoc { + public void updateLoc(cgGeo geo) { + // to be overriden + } +} diff --git a/src/cgeo/geocaching/cgUser.java b/src/cgeo/geocaching/cgUser.java new file mode 100644 index 0000000..1389de8 --- /dev/null +++ b/src/cgeo/geocaching/cgUser.java @@ -0,0 +1,12 @@ +package cgeo.geocaching; + +import java.util.Date; + +public class cgUser { + public Date located = null; + public String username = null; + public Double latitude = null; + public Double longitude = null; + public String action = null; + public String client = null; +} diff --git a/src/cgeo/geocaching/cgWarning.java b/src/cgeo/geocaching/cgWarning.java new file mode 100644 index 0000000..93ddec7 --- /dev/null +++ b/src/cgeo/geocaching/cgWarning.java @@ -0,0 +1,52 @@ +package cgeo.geocaching; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.widget.Toast; +import android.view.Gravity; + +public class cgWarning { + public Context context = null; + + public cgWarning(Context contextIn) { + context = contextIn; + } + + public void showToast(String text) { + if (text.length() > 0) { + Toast toast = Toast.makeText(context, text, Toast.LENGTH_LONG); + + toast.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM, 0, 100); + toast.show(); + } + } + + public void showShortToast(String text) { + if (text.length() > 0) { + Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); + + toast.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.BOTTOM, 0, 100); + toast.show(); + } + } + + public void helpDialog(String title, String message) { + if (message == null || message.length() == 0) { + return; + } + + AlertDialog.Builder dialog = new AlertDialog.Builder(context); + dialog.setTitle(title); + dialog.setMessage(message); + dialog.setCancelable(true); + dialog.setNeutralButton("Ok", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog alert = dialog.create(); + alert.show(); + } +} diff --git a/src/cgeo/geocaching/cgWaypoint.java b/src/cgeo/geocaching/cgWaypoint.java new file mode 100644 index 0000000..d3f703c --- /dev/null +++ b/src/cgeo/geocaching/cgWaypoint.java @@ -0,0 +1,16 @@ +package cgeo.geocaching; + +public class cgWaypoint { + public Integer id = 0; + public String geocode = "geocode"; + public String type = "waypoint"; + public String prefix = ""; + public String lookup = ""; + public String name = ""; + public String latlon = ""; + public String latitudeString = ""; + public String longitudeString = ""; + public Double latitude = null; + public Double longitude = null; + public String note = ""; +} diff --git a/src/cgeo/geocaching/cgeo.java b/src/cgeo/geocaching/cgeo.java new file mode 100644 index 0000000..67ff74a --- /dev/null +++ b/src/cgeo/geocaching/cgeo.java @@ -0,0 +1,669 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import android.os.Bundle; +import android.os.Handler; +import android.app.Activity; +import android.view.View; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.Button; +import android.widget.TextView; +import android.content.Intent; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.location.Address; +import android.location.Geocoder; +import android.os.Message; +import android.util.Log; +import android.view.ContextMenu; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map.Entry; + +public class cgeo extends Activity { + + private Resources res = null; + private cgeoapplication app = null; + private Context context = null; + private cgSettings settings = null; + private SharedPreferences prefs = null; + private cgBase base = null; + private cgWarning warning = null; + private Integer version = null; + private cgGeo geo = null; + private cgUpdateLoc geoUpdate = new update(); + private TextView navType = null; + private TextView navAccuracy = null; + private TextView navSatellites = null; + private TextView navLocation = null; + private TextView filterTitle = null; + private TextView countBubble = null; + private boolean cleanupRunning = false; + private int countBubbleCnt = 0; + private Double addLat = null; + private Double addLon = null; + private List<Address> addresses = null; + private boolean addressObtaining = false; + private boolean initialized = false; + private Handler countBubbleHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (countBubble == null) { + countBubble = (TextView) findViewById(R.id.offline_count); + } + + if (countBubbleCnt == 0) { + countBubble.setVisibility(View.GONE); + } else { + countBubble.setText(Integer.toString(countBubbleCnt)); + countBubble.bringToFront(); + countBubble.setVisibility(View.VISIBLE); + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgeo.countBubbleHander: " + e.toString()); + } + } + }; + private Handler obtainAddressHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (addresses != null && addresses.isEmpty() == false) { + final Address address = addresses.get(0); + final StringBuilder addText = new StringBuilder(); + + if (address.getCountryName() != null) { + addText.append(address.getCountryName()); + } + if (address.getLocality() != null) { + if (addText.length() > 0) { + addText.append(", "); + } + addText.append(address.getLocality()); + } else if (address.getAdminArea() != null) { + if (addText.length() > 0) { + addText.append(", "); + } + addText.append(address.getAdminArea()); + } + + addLat = geo.latitudeNow; + addLon = geo.longitudeNow; + + if (navLocation == null) { + navLocation = (TextView) findViewById(R.id.nav_location); + } + + navLocation.setText(addText.toString()); + } + } catch (Exception e) { + // nothing + } + + addresses = null; + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + context = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + app.setAction(null); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + prefs = getSharedPreferences(cgSettings.preferences, 0); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + app.cleanGeo(); + app.cleanDir(); + + setContentView(R.layout.main); + setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); // type to search + + try { + PackageManager manager = this.getPackageManager(); + PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0); + + version = info.versionCode; + + base.sendAnal(context, "/?ver=" + info.versionCode); + Log.i(cgSettings.tag, "Starting " + info.packageName + " " + info.versionCode + " a.k.a " + info.versionName + "..."); + + info = null; + manager = null; + } catch (Exception e) { + base.sendAnal(context, "/"); + Log.i(cgSettings.tag, "No info."); + } + + try { + if (settings.helper == 0) { + RelativeLayout helper = (RelativeLayout) findViewById(R.id.helper); + if (helper != null) { + helper.setVisibility(View.VISIBLE); + helper.setClickable(true); + helper.setOnClickListener(new View.OnClickListener() { + + public void onClick(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-intro", + context, + "http://cgeo.carnero.cc/manual/"); + } catch (Exception e) { + // nothing + } + + view.setVisibility(View.GONE); + } + }); + + final SharedPreferences.Editor edit = getSharedPreferences(cgSettings.preferences, 0).edit(); + edit.putInt("helper", 1); + edit.commit(); + } + } + } catch (Exception e) { + // nothing + } + + init(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + init(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + init(); + } + + @Override + public void onDestroy() { + initialized = false; + + if (geo != null) { + geo = app.removeGeo(); + } + + super.onDestroy(); + } + + @Override + public void onStop() { + initialized = false; + + if (geo != null) { + geo = app.removeGeo(); + } + + super.onStop(); + } + + @Override + public void onPause() { + initialized = false; + + if (geo != null) { + geo = app.removeGeo(); + } + + super.onPause(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, 0, 0, res.getString(R.string.menu_about)).setIcon(android.R.drawable.ic_menu_help); + menu.add(0, 1, 0, res.getString(R.string.menu_helpers)).setIcon(android.R.drawable.ic_menu_add); + menu.add(0, 2, 0, res.getString(R.string.menu_settings)).setIcon(android.R.drawable.ic_menu_preferences); + menu.add(0, 3, 0, res.getString(R.string.menu_history)).setIcon(android.R.drawable.ic_menu_recent_history); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int id = item.getItemId(); + if (id == 0) { + showAbout(null); + + return true; + } else if (id == 1) { + context.startActivity(new Intent(context, cgeohelpers.class)); + + return true; + } else if (id == 2) { + context.startActivity(new Intent(context, cgeoinit.class)); + + return true; + } else if (id == 3) { + final Intent cachesIntent = new Intent(context, cgeocaches.class); + cachesIntent.putExtra("type", "history"); + context.startActivity(cachesIntent); + + return true; + } + + return false; + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + menu.setHeaderTitle(res.getString(R.string.menu_filter)); + + //first add the most used types + menu.add(1, 0, 0, res.getString(R.string.all_types)); + menu.add(1, 1, 0, res.getString(R.string.traditional)); + menu.add(1, 2, 0, res.getString(R.string.multi)); + menu.add(1, 3, 0, res.getString(R.string.mystery)); + + // then add all other cache types sorted alphabetically + HashMap<String, String> allTypes = (HashMap<String, String>) base.cacheTypesInv.clone(); + allTypes.remove("traditional"); + allTypes.remove("multi"); + allTypes.remove("mystery"); + ArrayList<String> sorted = new ArrayList<String>(allTypes.values()); + Collections.sort(sorted); + for (String choice : sorted) { + menu.add(1, menu.size(), 0, choice); + } + + // mark current filter as checked + menu.setGroupCheckable(1, true, true); + boolean foundItem = false; + int itemCount = menu.size(); + if (settings.cacheType != null) { + String typeTitle = cgBase.cacheTypesInv.get(settings.cacheType); + if (typeTitle != null) { + for (int i = 0; i < itemCount; i++) { + if (menu.getItem(i).getTitle().equals(typeTitle)) { + menu.getItem(i).setChecked(true); + foundItem = true; + break; + } + } + } + } + if (!foundItem) { + menu.getItem(0).setChecked(true); + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + final int id = item.getItemId(); + + if (id == 0) { + settings.setCacheType(null); + setFilterTitle(); + + return true; + } else if (id > 0) { + String itemTitle = item.getTitle().toString(); + String choice = null; + for (Entry<String, String> entry : cgBase.cacheTypesInv.entrySet()) { + if (entry.getValue().equalsIgnoreCase(itemTitle)) { + choice = entry.getKey(); + break; + } + } + if (choice == null) { + settings.setCacheType(null); + } else { + settings.setCacheType(choice); + } + setFilterTitle(); + + return true; + } + + return false; + } + + private void setFilterTitle() { + if (filterTitle == null) { + filterTitle = (TextView) findViewById(R.id.filter_button_title); + } + if (settings.cacheType != null) { + filterTitle.setText(cgBase.cacheTypesInv.get(settings.cacheType)); + } else { + filterTitle.setText(res.getString(R.string.all)); + } + } + + private void init() { + if (initialized == true) { + return; + } + + initialized = true; + + settings.getLogin(); + settings.reloadCacheType(); + + if (app.firstRun == true) { + new Thread() { + + @Override + public void run() { + int status = base.login(); + + if (status == 1) { + app.firstRun = false; + } + } + }.start(); + } + + (new countBubbleUpdate()).start(); + (new cleanDatabase()).start(); + + if (settings.cacheType != null && cgBase.cacheTypesInv.containsKey(settings.cacheType) == false) { + settings.setCacheType(null); + } + + if (geo == null) { + geo = app.startGeo(context, geoUpdate, base, settings, warning, 0, 0); + } + + navType = (TextView) findViewById(R.id.nav_type); + navAccuracy = (TextView) findViewById(R.id.nav_accuracy); + navLocation = (TextView) findViewById(R.id.nav_location); + + final LinearLayout findOnMap = (LinearLayout) findViewById(R.id.map); + findOnMap.setClickable(true); + findOnMap.setOnClickListener(new cgeoFindOnMapListener()); + + final RelativeLayout findByOffline = (RelativeLayout) findViewById(R.id.search_offline); + findByOffline.setClickable(true); + findByOffline.setOnClickListener(new cgeoFindByOfflineListener()); + + (new countBubbleUpdate()).start(); + + final LinearLayout advanced = (LinearLayout) findViewById(R.id.advanced_button); + advanced.setClickable(true); + advanced.setOnClickListener(new cgeoSearchListener()); + + final LinearLayout any = (LinearLayout) findViewById(R.id.any_button); + any.setClickable(true); + any.setOnClickListener(new cgeoPointListener()); + + final LinearLayout filter = (LinearLayout) findViewById(R.id.filter_button); + registerForContextMenu(filter); + filter.setOnClickListener(new View.OnClickListener() { + + public void onClick(View view) { + openContextMenu(view); + } + }); + filter.setClickable(true); + + setFilterTitle(); + } + + private class update extends cgUpdateLoc { + + @Override + public void updateLoc(cgGeo geo) { + if (geo == null) { + return; + } + + try { + if (navType == null || navLocation == null || navAccuracy == null) { + navType = (TextView) findViewById(R.id.nav_type); + navAccuracy = (TextView) findViewById(R.id.nav_accuracy); + navSatellites = (TextView) findViewById(R.id.nav_satellites); + navLocation = (TextView) findViewById(R.id.nav_location); + } + + if (geo.latitudeNow != null && geo.longitudeNow != null) { + LinearLayout findNearest = (LinearLayout) findViewById(R.id.nearest); + findNearest.setClickable(true); + findNearest.setOnClickListener(new cgeoFindNearestListener()); + + String satellites = null; + if (geo.satellitesVisible != null && geo.satellitesFixed != null && geo.satellitesFixed > 0) { + satellites = res.getString(R.string.loc_sat) + ": " + geo.satellitesFixed + "/" + geo.satellitesVisible; + } else if (geo.satellitesVisible != null) { + satellites = res.getString(R.string.loc_sat) + ": 0/" + geo.satellitesVisible; + } else { + satellites = ""; + } + navSatellites.setText(satellites); + + if (geo.gps == -1) { + navType.setText(res.getString(R.string.loc_last)); + } else if (geo.gps == 0) { + navType.setText(res.getString(R.string.loc_net)); + } else { + navType.setText(res.getString(R.string.loc_gps)); + } + + if (geo.accuracyNow != null) { + if (settings.units == cgSettings.unitsImperial) { + navAccuracy.setText("±" + String.format(Locale.getDefault(), "%.0f", (geo.accuracyNow * 3.2808399)) + " ft"); + } else { + navAccuracy.setText("±" + String.format(Locale.getDefault(), "%.0f", geo.accuracyNow) + " m"); + } + } else { + navAccuracy.setText(null); + } + + if (settings.showAddress == 1) { + if (addLat == null || addLon == null) { + navLocation.setText(res.getString(R.string.loc_no_addr)); + } + if (addLat == null || addLon == null || (cgBase.getDistance(geo.latitudeNow, geo.longitudeNow, addLat, addLon) > 0.5 && addressObtaining == false)) { + (new obtainAddress()).start(); + } + } else { + if (geo.altitudeNow != null) { + String humanAlt; + if (settings.units == cgSettings.unitsImperial) { + humanAlt = String.format("%.0f", (geo.altitudeNow * 3.2808399)) + " ft"; + } else { + humanAlt = String.format("%.0f", geo.altitudeNow) + " m"; + } + navLocation.setText(base.formatCoordinate(geo.latitudeNow, "lat", true) + " | " + base.formatCoordinate(geo.longitudeNow, "lon", true) + " | " + humanAlt); + } else { + navLocation.setText(base.formatCoordinate(geo.latitudeNow, "lat", true) + " | " + base.formatCoordinate(geo.longitudeNow, "lon", true)); + } + } + } else { + Button findNearest = (Button) findViewById(R.id.nearest); + findNearest.setClickable(false); + findNearest.setOnClickListener(null); + + navType.setText(null); + navAccuracy.setText(null); + navLocation.setText(res.getString(R.string.loc_trying)); + } + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to update location."); + } + } + } + + private class cgeoFindNearestListener implements View.OnClickListener { + + public void onClick(View arg0) { + if (geo == null) { + return; + } + + final Intent cachesIntent = new Intent(context, cgeocaches.class); + cachesIntent.putExtra("type", "nearest"); + cachesIntent.putExtra("latitude", geo.latitudeNow); + cachesIntent.putExtra("longitude", geo.longitudeNow); + cachesIntent.putExtra("cachetype", settings.cacheType); + context.startActivity(cachesIntent); + } + } + + private class cgeoFindOnMapListener implements View.OnClickListener { + + public void onClick(View arg0) { + context.startActivity(new Intent(context, settings.getMapFactory().getMapClass())); + } + } + + private class cgeoFindByOfflineListener implements View.OnClickListener { + + public void onClick(View arg0) { + final Intent cachesIntent = new Intent(context, cgeocaches.class); + cachesIntent.putExtra("type", "offline"); + context.startActivity(cachesIntent); + } + } + + private class cgeoSearchListener implements View.OnClickListener { + + public void onClick(View arg0) { + context.startActivity(new Intent(context, cgeoadvsearch.class)); + } + } + + private class cgeoPointListener implements View.OnClickListener { + + public void onClick(View arg0) { + context.startActivity(new Intent(context, cgeopoint.class)); + } + } + + private class countBubbleUpdate extends Thread { + + @Override + public void run() { + if (app == null) { + return; + } + + int checks = 0; + while (app.storageStatus() == false) { + try { + wait(500); + checks++; + } catch (Exception e) { + // nothing; + } + + if (checks > 10) { + return; + } + } + + + countBubbleCnt = app.getAllStoredCachesCount(true, null, null); + + countBubbleHandler.sendEmptyMessage(0); + } + } + + private class cleanDatabase extends Thread { + + @Override + public void run() { + if (app == null) { + return; + } + if (cleanupRunning == true) { + return; + } + + boolean more = false; + if (version != settings.version) { + Log.i(cgSettings.tag, "Initializing hard cleanup - version changed from " + settings.version + " to " + version + "."); + + more = true; + } + + cleanupRunning = true; + app.cleanDatabase(more); + cleanupRunning = false; + + if (version != null && version > 0) { + SharedPreferences.Editor edit = prefs.edit(); + edit.putInt("version", version); + edit.commit(); + } + } + } + + private class obtainAddress extends Thread { + + public obtainAddress() { + setPriority(Thread.MIN_PRIORITY); + } + + @Override + public void run() { + if (geo == null) { + return; + } + if (addressObtaining == true) { + return; + } + addressObtaining = true; + + try { + Geocoder geocoder = new Geocoder(context, Locale.getDefault()); + + addresses = geocoder.getFromLocation(geo.latitudeNow, geo.longitudeNow, 1); + } catch (Exception e) { + Log.i(cgSettings.tag, "Failed to obtain address"); + } + + obtainAddressHandler.sendEmptyMessage(0); + + addressObtaining = false; + } + } + + public void showAbout(View view) { + context.startActivity(new Intent(context, cgeoabout.class)); + } + + public void goSearch(View view) { + onSearchRequested(); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-main-screen", + context, + "http://cgeo.carnero.cc/manual/"); + } catch (Exception e) { + // nothing + } + } +} diff --git a/src/cgeo/geocaching/cgeoabout.java b/src/cgeo/geocaching/cgeoabout.java new file mode 100644 index 0000000..fe11660 --- /dev/null +++ b/src/cgeo/geocaching/cgeoabout.java @@ -0,0 +1,110 @@ +package cgeo.geocaching; + +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.net.Uri; +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +public class cgeoabout extends Activity { + private Activity activity = null; + private Resources res = null; + private cgSettings settings = null; + private cgBase base = null; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + settings = new cgSettings(this, this.getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase((cgeoapplication) this.getApplication(), settings, this.getSharedPreferences(cgSettings.preferences, 0)); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.about); + base.setTitle(activity, res.getString(R.string.about)); + + // google analytics + base.sendAnal(activity, "/about"); + + init(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + private void init() { + try { + PackageManager manager = this.getPackageManager(); + PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0); + + base.setTitle(activity, res.getString(R.string.about) + " (ver. " + info.versionName + ")"); + + manager = null; + + ((TextView)findViewById(R.id.contributors)).setMovementMethod(LinkMovementMethod.getInstance()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoabout.init: Failed to obtain package version."); + } + } + + public void donateMore(View view) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=N2FKGNCPPRUVE"))); + //activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2Z69QWLRCBE9N&lc=US&item_name=c%3ageo¤cy_code=EUR&amount=15&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"))); + } + + public void donateLess(View view) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4PRD9CX4Y8XR6"))); + //activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2Z69QWLRCBE9N&lc=US&item_name=c%3ageo¤cy_code=EUR&amount=7&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"))); + } + + public void author(View view) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://carnero.cc/"))); + } + + public void support(View view) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("mailto:support@cgeo.org"))); + } + + public void website(View view) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.cgeo.org/"))); + } + + public void facebook(View view) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.facebook.com/pages/cgeo/297269860090"))); + } + + public void twitter(View view) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://twitter.com/android_gc"))); + } + + public void nutshellmanual(View view) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://cgeo.carnero.cc/manual/"))); + } + + public void goHome(View view) { + base.goHome(activity); + } +} diff --git a/src/cgeo/geocaching/cgeoaddresses.java b/src/cgeo/geocaching/cgeoaddresses.java new file mode 100644 index 0000000..c917d50 --- /dev/null +++ b/src/cgeo/geocaching/cgeoaddresses.java @@ -0,0 +1,201 @@ +package cgeo.geocaching; + +import android.app.Activity; +import android.app.ProgressDialog; +import java.util.ArrayList; +import android.os.Bundle; +import android.view.View; +import android.view.LayoutInflater; +import android.widget.Button; +import android.widget.LinearLayout; +import android.content.Intent; +import android.content.res.Resources; +import android.location.Address; +import android.location.Geocoder; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import java.util.List; +import java.util.Locale; + +public class cgeoaddresses extends Activity { + private final ArrayList<Address> addresses = new ArrayList<Address>(); + private String keyword = null; + private Activity activity = null; + private cgeoapplication app = null; + private cgSettings settings = null; + private cgBase base = null; + private Resources res = null; + private cgWarning warning = null; + private LayoutInflater inflater = null; + private LinearLayout addList = null; + private ProgressDialog waitDialog = null; + private Handler loadPlacesHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (addList == null) { + addList = (LinearLayout) findViewById(R.id.address_list); + } + + if (addresses.isEmpty()) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + + warning.showToast(res.getString(R.string.err_search_address_no_match)); + + finish(); + return; + } else { + LinearLayout oneAddPre = null; + for (Address address : addresses) { + oneAddPre = (LinearLayout) inflater.inflate(R.layout.address_button, null); + + Button oneAdd = (Button) oneAddPre.findViewById(R.id.button); + int index = 0; + StringBuilder allAdd = new StringBuilder(); + StringBuilder allAddLine = new StringBuilder(); + + while (address.getAddressLine(index) != null) { + if (allAdd.length() > 0) { + allAdd.append("\n"); + } + if (allAddLine.length() > 0) { + allAddLine.append("; "); + } + + allAdd.append(address.getAddressLine(index)); + allAddLine.append(address.getAddressLine(index)); + + index++; + } + + oneAdd.setText(allAdd.toString()); + oneAdd.setLines(allAdd.toString().split("\n").length); + oneAdd.setClickable(true); + oneAdd.setOnClickListener(new buttonListener(address.getLatitude(), address.getLongitude(), allAddLine.toString())); + addList.addView(oneAddPre); + } + } + + if (waitDialog != null) { + waitDialog.dismiss(); + } + } catch (Exception e) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + Log.e(cgSettings.tag, "cgeoaddresses.loadCachesHandler: " + e.toString()); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + inflater = getLayoutInflater(); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.addresses); + base.setTitle(activity, res.getString(R.string.search_address_result)); + + // google analytics + base.sendAnal(activity, "/addresses"); + + // get parameters + Bundle extras = getIntent().getExtras(); + + // try to get data from extras + if (extras != null) { + keyword = extras.getString("keyword"); + } + + if (keyword == null) { + warning.showToast(res.getString(R.string.err_search_address_forgot)); + finish(); + return; + } + + waitDialog = ProgressDialog.show(this, res.getString(R.string.search_address_started), keyword, true); + waitDialog.setCancelable(true); + + (new loadPlaces()).start(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + private class loadPlaces extends Thread { + + @Override + public void run() { + Geocoder geocoder = new Geocoder(activity, Locale.getDefault()); + try { + List<Address> knownLocations = geocoder.getFromLocationName(keyword, 20); + + addresses.clear(); + for (Address address : knownLocations) { + addresses.add(address); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoaddresses.loadPlaces.run: " + e.toString()); + } + + loadPlacesHandler.sendMessage(new Message()); + } + } + + private class buttonListener implements View.OnClickListener { + + private Double latitude = null; + private Double longitude = null; + private String address = null; + + public buttonListener(Double latitudeIn, Double longitudeIn, String addressIn) { + latitude = latitudeIn; + longitude = longitudeIn; + address = addressIn; + } + + public void onClick(View arg0) { + Intent addressIntent = new Intent(activity, cgeocaches.class); + addressIntent.putExtra("type", "address"); + addressIntent.putExtra("latitude", (Double) latitude); + addressIntent.putExtra("longitude", (Double) longitude); + addressIntent.putExtra("address", (String) address); + addressIntent.putExtra("cachetype", settings.cacheType); + activity.startActivity(addressIntent); + + finish(); + return; + } + } + + public void goHome(View view) { + base.goHome(activity); + } +} diff --git a/src/cgeo/geocaching/cgeoadvsearch.java b/src/cgeo/geocaching/cgeoadvsearch.java new file mode 100644 index 0000000..568cbce --- /dev/null +++ b/src/cgeo/geocaching/cgeoadvsearch.java @@ -0,0 +1,552 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import java.util.HashMap; +import android.os.Bundle; +import android.app.Activity; +import android.app.SearchManager; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.EditText; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.KeyEvent; +import android.view.inputmethod.EditorInfo; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class cgeoadvsearch extends Activity { + + private Resources res = null; + private Activity activity = null; + private cgeoapplication app = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private cgGeo geo = null; + private cgUpdateLoc geoUpdate = new update(); + private EditText latEdit = null; + private EditText lonEdit = null; + private String[] geocodesInCache = null; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + app.setAction(null); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // search query + Intent intent = getIntent(); + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + final String query = intent.getStringExtra(SearchManager.QUERY); + final boolean found = instantSearch(query); + + if (found) { + finish(); + + return; + } + } + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.search); + base.setTitle(activity, res.getString(R.string.search)); + + // google analytics + base.sendAnal(activity, "/advanced-search"); + + init(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + init(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + init(); + } + + @Override + public void onDestroy() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onDestroy(); + } + + @Override + public void onStop() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onStop(); + } + + @Override + public void onPause() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onPause(); + } + + private boolean instantSearch(String query) { + boolean found = false; + + final Pattern gcCode = Pattern.compile("^GC[0-9A-Z]+$", Pattern.CASE_INSENSITIVE); + final Pattern tbCode = Pattern.compile("^TB[0-9A-Z]+$", Pattern.CASE_INSENSITIVE); + final Matcher gcCodeM = gcCode.matcher(query); + final Matcher tbCodeM = tbCode.matcher(query); + + try { + if (gcCodeM.find()) { // GC-code + final Intent cachesIntent = new Intent(activity, cgeodetail.class); + cachesIntent.putExtra("geocode", query.trim().toUpperCase()); + activity.startActivity(cachesIntent); + + found = true; + } else if (tbCodeM.find()) { // TB-code + final Intent trackablesIntent = new Intent(activity, cgeotrackable.class); + trackablesIntent.putExtra("geocode", query.trim().toUpperCase()); + activity.startActivity(trackablesIntent); + + found = true; + } else { // keyword (fallback) + final Intent cachesIntent = new Intent(activity, cgeocaches.class); + cachesIntent.putExtra("type", "keyword"); + cachesIntent.putExtra("keyword", query); + cachesIntent.putExtra("cachetype", settings.cacheType); + activity.startActivity(cachesIntent); + + found = true; + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgeoadvsearch.instantSearch: " + e.toString()); + } + + return found; + } + + private void init() { + settings.getLogin(); + settings.reloadCacheType(); + + if (settings.cacheType != null && cgBase.cacheTypesInv.containsKey(settings.cacheType) == false) { + settings.setCacheType(null); + } + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + + ((EditText) findViewById(R.id.latitude)).setOnEditorActionListener(new findByCoordsAction()); + ((EditText) findViewById(R.id.longitude)).setOnEditorActionListener(new findByCoordsAction()); + + final Button findByCoords = (Button) findViewById(R.id.search_coordinates); + findByCoords.setOnClickListener(new findByCoordsListener()); + + ((EditText) findViewById(R.id.address)).setOnEditorActionListener(new findByAddressAction()); + + final Button findByAddress = (Button) findViewById(R.id.search_address); + findByAddress.setOnClickListener(new findByAddressListener()); + + final AutoCompleteTextView geocodeEdit = (AutoCompleteTextView) findViewById(R.id.geocode); + geocodeEdit.setOnEditorActionListener(new findByGeocodeAction()); + geocodesInCache = app.geocodesInCache(); + if (geocodesInCache != null) { + final ArrayAdapter<String> geocodesAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, geocodesInCache); + geocodeEdit.setAdapter(geocodesAdapter); + } + // geocodeEdit.addTextChangedListener(new UpperCaseTextWatcher(geocodeEdit)); + + final Button displayByGeocode = (Button) findViewById(R.id.display_geocode); + displayByGeocode.setOnClickListener(new findByGeocodeListener()); + + ((EditText) findViewById(R.id.keyword)).setOnEditorActionListener(new findByKeywordAction()); + + final Button findByKeyword = (Button) findViewById(R.id.search_keyword); + findByKeyword.setOnClickListener(new findByKeywordListener()); + + ((EditText) findViewById(R.id.username)).setOnEditorActionListener(new findByUsernameAction()); + + final Button findByUserName = (Button) findViewById(R.id.search_username); + findByUserName.setOnClickListener(new findByUsernameListener()); + + ((EditText) findViewById(R.id.owner)).setOnEditorActionListener(new findByOwnerAction()); + + final Button findByOwner = (Button) findViewById(R.id.search_owner); + findByOwner.setOnClickListener(new findByOwnerListener()); + + EditText trackable = (EditText) findViewById(R.id.trackable); + trackable.setOnEditorActionListener(new findTrackableAction()); + // trackable.addTextChangedListener(new UpperCaseTextWatcher(trackable)); // not working with HTC IMEs. + + final Button displayTrackable = (Button) findViewById(R.id.display_trackable); + displayTrackable.setOnClickListener(new findTrackableListener()); + } + + /** + * converts user input to uppercase during typing + * @author bananeweizen + * + */ + private final class UpperCaseTextWatcher implements TextWatcher { + + private final EditText editText; + + private UpperCaseTextWatcher(EditText editText) { + this.editText = editText; + } + + @Override + public void afterTextChanged(Editable arg0) { + // empty + } + + @Override + public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { + // empty + } + + @Override + public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { + String oldText = editText.getText().toString(); + String upperText = oldText.toUpperCase(); + if (!oldText.equals(upperText)) { + int selectionStart = editText.getSelectionStart(); + int selectionEnd = editText.getSelectionEnd(); + editText.setText(upperText); + editText.setSelection(selectionStart, selectionEnd); + } + } + } + + private class update extends cgUpdateLoc { + + @Override + public void updateLoc(cgGeo geo) { + if (geo == null) { + return; + } + + try { + if (latEdit == null) { + latEdit = (EditText) findViewById(R.id.latitude); + } + if (lonEdit == null) { + lonEdit = (EditText) findViewById(R.id.longitude); + } + + if (geo.latitudeNow != null && geo.longitudeNow != null) { + latEdit.setHint(base.formatCoordinate(geo.latitudeNow, "lat", false)); + lonEdit.setHint(base.formatCoordinate(geo.longitudeNow, "lon", false)); + } + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to update location."); + } + } + } + + private class findByCoordsAction implements TextView.OnEditorActionListener { + + @Override + public boolean onEditorAction(TextView view, int action, KeyEvent event) { + if (action == EditorInfo.IME_ACTION_GO) { + findByCoordsFn(); + return true; + } + + return false; + } + } + + private class findByCoordsListener implements View.OnClickListener { + + public void onClick(View arg0) { + findByCoordsFn(); + } + } + + private void findByCoordsFn() { + final EditText latView = (EditText) findViewById(R.id.latitude); + final EditText lonView = (EditText) findViewById(R.id.longitude); + final String latText = latView.getText().toString(); + final String lonText = lonView.getText().toString(); + + if (latText == null || latText.length() == 0 || lonText == null || lonText.length() == 0) { + latView.setText(base.formatCoordinate(geo.latitudeNow, "lat", true)); + lonView.setText(base.formatCoordinate(geo.longitudeNow, "lon", true)); + } else { + HashMap<String, Object> latParsed = base.parseCoordinate(latText, "lat"); + HashMap<String, Object> lonParsed = base.parseCoordinate(lonText, "lat"); + + if (latParsed == null || latParsed.get("coordinate") == null || latParsed.get("string") == null) { + warning.showToast(res.getString(R.string.err_parse_lat)); + return; + } + + if (lonParsed == null || lonParsed.get("coordinate") == null || lonParsed.get("string") == null) { + warning.showToast(res.getString(R.string.err_parse_lon)); + return; + } + + final Intent cachesIntent = new Intent(activity, cgeocaches.class); + cachesIntent.putExtra("type", "coordinate"); + cachesIntent.putExtra("latitude", (Double) latParsed.get("coordinate")); + cachesIntent.putExtra("longitude", (Double) lonParsed.get("coordinate")); + cachesIntent.putExtra("cachetype", settings.cacheType); + activity.startActivity(cachesIntent); + } + } + + private class findByKeywordAction implements TextView.OnEditorActionListener { + + @Override + public boolean onEditorAction(TextView view, int action, KeyEvent event) { + if (action == EditorInfo.IME_ACTION_GO) { + findByKeywordFn(); + return true; + } + + return false; + } + } + + private class findByKeywordListener implements View.OnClickListener { + + public void onClick(View arg0) { + findByKeywordFn(); + } + } + + private void findByKeywordFn() { + // find caches by coordinates + String keyText = ((EditText) findViewById(R.id.keyword)).getText().toString(); + + if (keyText == null || keyText.length() == 0) { + warning.helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_keyword)); + return; + } + + final Intent cachesIntent = new Intent(activity, cgeocaches.class); + cachesIntent.putExtra("type", "keyword"); + cachesIntent.putExtra("keyword", keyText); + cachesIntent.putExtra("cachetype", settings.cacheType); + activity.startActivity(cachesIntent); + } + + private class findByAddressAction implements TextView.OnEditorActionListener { + + @Override + public boolean onEditorAction(TextView view, int action, KeyEvent event) { + if (action == EditorInfo.IME_ACTION_GO) { + findByAddressFn(); + return true; + } + + return false; + } + } + + private class findByAddressListener implements View.OnClickListener { + + public void onClick(View arg0) { + findByAddressFn(); + } + } + + private void findByAddressFn() { + final String addText = ((EditText) findViewById(R.id.address)).getText().toString(); + + if (addText == null || addText.length() == 0) { + warning.helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_address)); + return; + } + + final Intent addressesIntent = new Intent(activity, cgeoaddresses.class); + addressesIntent.putExtra("keyword", addText); + activity.startActivity(addressesIntent); + } + + private class findByUsernameAction implements TextView.OnEditorActionListener { + + @Override + public boolean onEditorAction(TextView view, int action, KeyEvent event) { + if (action == EditorInfo.IME_ACTION_GO) { + findByUsernameFn(); + return true; + } + + return false; + } + } + + private class findByUsernameListener implements View.OnClickListener { + + public void onClick(View arg0) { + findByUsernameFn(); + } + } + + public void findByUsernameFn() { + final String usernameText = ((EditText) findViewById(R.id.username)).getText().toString(); + + if (usernameText == null || usernameText.length() == 0) { + warning.helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_user)); + return; + } + + final Intent cachesIntent = new Intent(activity, cgeocaches.class); + cachesIntent.putExtra("type", "username"); + cachesIntent.putExtra("username", usernameText); + cachesIntent.putExtra("cachetype", settings.cacheType); + activity.startActivity(cachesIntent); + } + + private class findByOwnerAction implements TextView.OnEditorActionListener { + + @Override + public boolean onEditorAction(TextView view, int action, KeyEvent event) { + if (action == EditorInfo.IME_ACTION_GO) { + findByOwnerFn(); + return true; + } + + return false; + } + } + + private class findByOwnerListener implements View.OnClickListener { + + public void onClick(View arg0) { + findByOwnerFn(); + } + } + + private void findByOwnerFn() { + final String usernameText = ((EditText) findViewById(R.id.owner)).getText().toString(); + + if (usernameText == null || usernameText.length() == 0) { + warning.helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_user)); + return; + } + + final Intent cachesIntent = new Intent(activity, cgeocaches.class); + cachesIntent.putExtra("type", "owner"); + cachesIntent.putExtra("username", usernameText); + cachesIntent.putExtra("cachetype", settings.cacheType); + activity.startActivity(cachesIntent); + } + + private class findByGeocodeAction implements TextView.OnEditorActionListener { + + @Override + public boolean onEditorAction(TextView view, int action, KeyEvent event) { + if (action == EditorInfo.IME_ACTION_GO) { + findByGeocodeFn(); + return true; + } + + return false; + } + } + + private class findByGeocodeListener implements View.OnClickListener { + + public void onClick(View arg0) { + findByGeocodeFn(); + } + } + + private void findByGeocodeFn() { + final String geocodeText = ((EditText) findViewById(R.id.geocode)).getText().toString(); + + if (geocodeText == null || geocodeText.length() == 0 || geocodeText.equalsIgnoreCase("GC")) { + warning.helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_gccode)); + return; + } + + final Intent cachesIntent = new Intent(activity, cgeodetail.class); + cachesIntent.putExtra("geocode", geocodeText.toUpperCase()); + activity.startActivity(cachesIntent); + } + + private class findTrackableAction implements TextView.OnEditorActionListener { + + @Override + public boolean onEditorAction(TextView view, int action, KeyEvent event) { + if (action == EditorInfo.IME_ACTION_GO) { + findTrackableFn(); + return true; + } + + return false; + } + } + + private class findTrackableListener implements View.OnClickListener { + + public void onClick(View arg0) { + findTrackableFn(); + } + } + + private void findTrackableFn() { + final String trackableText = ((EditText) findViewById(R.id.trackable)).getText().toString(); + + if (trackableText == null || trackableText.length() == 0 || trackableText.equalsIgnoreCase("TB")) { + warning.helpDialog(res.getString(R.string.warn_search_help_title), res.getString(R.string.warn_search_help_tb)); + return; + } + + final Intent trackablesIntent = new Intent(activity, cgeotrackable.class); + trackablesIntent.putExtra("geocode", trackableText.toUpperCase()); + activity.startActivity(trackablesIntent); + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-search", + activity, + "http://cgeo.carnero.cc/manual/"); + } catch (Exception e) { + // nothing + } + } +} diff --git a/src/cgeo/geocaching/cgeoapplication.java b/src/cgeo/geocaching/cgeoapplication.java new file mode 100644 index 0000000..374f323 --- /dev/null +++ b/src/cgeo/geocaching/cgeoapplication.java @@ -0,0 +1,828 @@ +package cgeo.geocaching; + +import android.app.Application; +import android.content.Context; +import android.util.Log; +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +public class cgeoapplication extends Application { + + private cgData storage = null; + private String action = null; + private Double lastLatitude = null; + private Double lastLongitude = null; + private cgGeo geo = null; + private boolean geoInUse = false; + private cgDirection dir = null; + private boolean dirInUse = false; + final private HashMap<Long, cgSearch> searches = new HashMap<Long, cgSearch>(); // information about searches + final private HashMap<String, cgCache> cachesCache = new HashMap<String, cgCache>(); // caching caches into memory + public boolean firstRun = true; // c:geo is just launched + public boolean warnedLanguage = false; // user was warned about different language settings on geocaching.com + private boolean databaseCleaned = false; // database was cleaned + + public cgeoapplication() { + if (storage == null) { + storage = new cgData(this); + } + } + + @Override + public void onLowMemory() { + Log.i(cgSettings.tag, "Cleaning applications cache."); + + cachesCache.clear(); + } + + @Override + public void onTerminate() { + Log.d(cgSettings.tag, "Terminating c:geo..."); + + if (geo != null) { + geo.closeGeo(); + geo = null; + } + + if (dir != null) { + dir.closeDir(); + dir = null; + } + + if (storage != null) { + storage.clean(); + storage.closeDb(); + storage = null; + } + + super.onTerminate(); + } + + public String backupDatabase() { + return storage.backupDatabase(); + } + + public File isRestoreFile() { + return storage.isRestoreFile(); + } + + public boolean restoreDatabase() { + return storage.restoreDatabase(); + } + + public void cleanGeo() { + if (geo != null) { + geo.closeGeo(); + geo = null; + } + } + + public void cleanDir() { + if (dir != null) { + dir.closeDir(); + dir = null; + } + } + + public boolean storageStatus() { + if (storage.status() == false) { + return false; + } + + return true; + } + + public cgGeo startGeo(Context context, cgUpdateLoc geoUpdate, cgBase base, cgSettings settings, cgWarning warning, int time, int distance) { + if (geo == null) { + geo = new cgGeo(context, this, geoUpdate, base, settings, warning, time, distance); + geo.initGeo(); + + Log.i(cgSettings.tag, "Location service started"); + } + + geo.replaceUpdate(geoUpdate); + geoInUse = true; + + return geo; + } + + public cgGeo removeGeo() { + if (geo != null) { + geo.replaceUpdate(null); + } + geoInUse = false; + + (new removeGeoThread()).start(); + + return null; + } + + private class removeGeoThread extends Thread { + + @Override + public void run() { + try { + sleep(2500); + } catch (Exception e) { + // nothing + } + + if (geoInUse == false && geo != null) { + geo.closeGeo(); + geo = null; + + Log.i(cgSettings.tag, "Location service stopped"); + } + } + } + + public cgDirection startDir(Context context, cgUpdateDir dirUpdate, cgWarning warning) { + if (dir == null) { + dir = new cgDirection(this, context, dirUpdate, warning); + dir.initDir(); + + Log.i(cgSettings.tag, "Direction service started"); + } + + dir.replaceUpdate(dirUpdate); + dirInUse = true; + + return dir; + } + + public cgDirection removeDir() { + if (dir != null) { + dir.replaceUpdate(null); + } + dirInUse = false; + + (new removeDirThread()).start(); + + return null; + } + + private class removeDirThread extends Thread { + + @Override + public void run() { + try { + sleep(2500); + } catch (Exception e) { + // nothing + } + + if (dirInUse == false && dir != null) { + dir.closeDir(); + dir = null; + + Log.i(cgSettings.tag, "Direction service stopped"); + } + } + } + + public void cleanDatabase(boolean more) { + if (databaseCleaned == true) { + return; + } + + if (storage == null) { + storage = new cgData(this); + } + storage.clean(more); + databaseCleaned = true; + } + + public Boolean isThere(String geocode, String guid, boolean detailed, boolean checkTime) { + if (storage == null) { + storage = new cgData(this); + } + return storage.isThere(geocode, guid, detailed, checkTime); + } + + public Boolean isOffline(String geocode, String guid) { + if (storage == null) { + storage = new cgData(this); + } + return storage.isOffline(geocode, guid); + } + + public String getGeocode(String guid) { + if (storage == null) { + storage = new cgData(this); + } + return storage.getGeocodeForGuid(guid); + } + + public String getCacheid(String geocode) { + if (storage == null) { + storage = new cgData(this); + } + return storage.getCacheidForGeocode(geocode); + } + + public String getError(Long searchId) { + if (searchId == null || searches.containsKey(searchId) == false) { + return null; + } + + return searches.get(searchId).error; + } + + public boolean setError(Long searchId, String error) { + if (searchId == null || searches.containsKey(searchId) == false) { + return false; + } + + searches.get(searchId).error = error; + + return true; + } + + public String getUrl(Long searchId) { + if (searchId == null || searches.containsKey(searchId) == false) { + return null; + } + + return searches.get(searchId).url; + } + + public boolean setUrl(Long searchId, String url) { + if (searchId == null || searches.containsKey(searchId) == false) { + return false; + } + + searches.get(searchId).url = url; + + return true; + } + + public String getViewstate(Long searchId) { + if (searchId == null || searches.containsKey(searchId) == false) { + return null; + } + + return searches.get(searchId).viewstate; + } + + public String getViewstate1(Long searchId) { + if (searchId == null || searches.containsKey(searchId) == false) { + return null; + } + + return searches.get(searchId).viewstate1; + } + + public boolean setViewstate(Long searchId, String viewstate) { + if (viewstate == null || viewstate.length() == 0) { + return false; + } + if (searchId == null || searches.containsKey(searchId) == false) { + return false; + } + + searches.get(searchId).viewstate = viewstate; + + return true; + } + + public boolean setViewstate1(Long searchId, String viewstate1) { + if (searchId == null || searches.containsKey(searchId) == false) { + return false; + } + + searches.get(searchId).viewstate1 = viewstate1; + + return true; + } + + public Integer getTotal(Long searchId) { + if (searchId == null || searches.containsKey(searchId) == false) { + return null; + } + + return searches.get(searchId).totalCnt; + } + + public Integer getCount(Long searchId) { + if (searchId == null || searches.containsKey(searchId) == false) { + return 0; + } + + return searches.get(searchId).getCount(); + } + + public Integer getNotOfflineCount(Long searchId) { + if (searchId == null || searches.containsKey(searchId) == false) { + return 0; + } + + int count = 0; + ArrayList<String> geocodes = searches.get(searchId).getGeocodes(); + if (geocodes != null) { + for (String geocode : geocodes) { + if (isOffline(geocode, null) == false) { + count++; + } + } + } + + return count; + } + + public cgCache getCacheByGeocode(String geocode) { + return getCacheByGeocode(geocode, false, true, false, false, false, false); + } + + public cgCache getCacheByGeocode(String geocode, boolean loadA, boolean loadW, boolean loadS, boolean loadL, boolean loadI, boolean loadO) { + if (geocode == null || geocode.length() == 0) { + return null; + } + + cgCache cache = null; + if (cachesCache.containsKey(geocode) == true) { + cache = cachesCache.get(geocode); + } else { + if (storage == null) { + storage = new cgData(this); + } + cache = storage.loadCache(geocode, null, loadA, loadW, loadS, loadL, loadI, loadO); + + if (cache != null && cache.detailed == true && loadA == true && loadW == true && loadS == true && loadL == true && loadI == true) { + putCacheInCache(cache); + } + } + + return cache; + } + + public cgTrackable getTrackableByGeocode(String geocode) { + if (geocode == null || geocode.length() == 0) { + return null; + } + + cgTrackable trackable = null; + trackable = storage.loadTrackable(geocode); + + return trackable; + } + + public void removeCacheFromCache(String geocode) { + if (geocode != null && cachesCache.containsKey(geocode) == true) { + cachesCache.remove(geocode); + } + } + + public void putCacheInCache(cgCache cache) { + if (cache == null || cache.geocode == null) { + return; + } + + if (cachesCache.containsKey(cache.geocode) == true) { + cachesCache.remove(cache.geocode); + } + + cachesCache.put(cache.geocode, cache); + } + + public String[] geocodesInCache() { + if (storage == null) { + storage = new cgData(this); + } + + return storage.allDetailedThere(); + } + + public cgWaypoint getWaypointById(Integer id) { + if (id == null || id == 0) { + return null; + } + + if (storage == null) { + storage = new cgData(this); + } + return storage.loadWaypoint(id); + } + + public ArrayList<Object> getBounds(String geocode) { + if (geocode == null) { + return null; + } + + List<String> geocodeList = new ArrayList<String>(); + geocodeList.add(geocode); + + return getBounds(geocodeList); + } + + public ArrayList<Object> getBounds(Long searchId) { + if (searchId == null || searches.containsKey(searchId) == false) { + return null; + } + + if (storage == null) { + storage = new cgData(this); + } + + final cgSearch search = searches.get(searchId); + final ArrayList<String> geocodeList = search.getGeocodes(); + + return getBounds(geocodeList); + } + + public ArrayList<Object> getBounds(List<String> geocodes) { + if (geocodes == null || geocodes.isEmpty()) { + return null; + } + + if (storage == null) { + storage = new cgData(this); + } + + return storage.getBounds(geocodes.toArray()); + } + + public cgCache getCache(Long searchId) { + if (searchId == null || searches.containsKey(searchId) == false) { + return null; + } + + cgSearch search = searches.get(searchId); + ArrayList<String> geocodeList = search.getGeocodes(); + + return getCacheByGeocode(geocodeList.get(0), true, true, true, true, true, true); + } + + public ArrayList<cgCache> getCaches(Long searchId) { + return getCaches(searchId, null, null, null, null, false, true, false, false, false, true); + } + + public ArrayList<cgCache> getCaches(Long searchId, boolean loadA, boolean loadW, boolean loadS, boolean loadL, boolean loadI, boolean loadO) { + return getCaches(searchId, null, null, null, null, loadA, loadW, loadS, loadL, loadI, loadO); + } + + public ArrayList<cgCache> getCaches(Long searchId, Long centerLat, Long centerLon, Long spanLat, Long spanLon) { + return getCaches(searchId, centerLat, centerLon, spanLat, spanLon, false, true, false, false, false, true); + } + + public ArrayList<cgCache> getCaches(Long searchId, Long centerLat, Long centerLon, Long spanLat, Long spanLon, boolean loadA, boolean loadW, boolean loadS, boolean loadL, boolean loadI, boolean loadO) { + if (searchId == null || searches.containsKey(searchId) == false) { + ArrayList<cgCache> cachesOut = new ArrayList<cgCache>(); + + final ArrayList<cgCache> cachesPre = storage.loadCaches(null , null, centerLat, centerLon, spanLat, spanLon, loadA, loadW, loadS, loadL, loadI, loadO); + + if (cachesPre != null) { + cachesOut.addAll(cachesPre); + } + + return cachesOut; + } + + ArrayList<cgCache> cachesOut = new ArrayList<cgCache>(); + + cgSearch search = searches.get(searchId); + ArrayList<String> geocodeList = search.getGeocodes(); + + if (storage == null) { + storage = new cgData(this); + } + + final ArrayList<cgCache> cachesPre = storage.loadCaches(geocodeList.toArray(), null, centerLat, centerLon, spanLat, spanLon, loadA, loadW, loadS, loadL, loadI, loadO); + if (cachesPre != null) { + cachesOut.addAll(cachesPre); + } + + return cachesOut; + } + + public cgSearch getBatchOfStoredCaches(boolean detailedOnly, Double latitude, Double longitude, String cachetype, int list) { + if (storage == null) { + storage = new cgData(this); + } + cgSearch search = new cgSearch(); + + ArrayList<String> geocodes = storage.loadBatchOfStoredGeocodes(detailedOnly, latitude, longitude, cachetype, list); + if (geocodes != null && geocodes.isEmpty() == false) { + for (String gccode : geocodes) { + search.addGeocode(gccode); + } + } + searches.put(search.getCurrentId(), search); + + return search; + } + + public cgSearch getHistoryOfCaches(boolean detailedOnly, String cachetype) { + if (storage == null) { + storage = new cgData(this); + } + cgSearch search = new cgSearch(); + + ArrayList<String> geocodes = storage.loadBatchOfHistoricGeocodes(detailedOnly, cachetype); + if (geocodes != null && geocodes.isEmpty() == false) { + for (String gccode : geocodes) { + search.addGeocode(gccode); + } + } + searches.put(search.getCurrentId(), search); + + return search; + } + + public Long getCachedInViewport(Long centerLat, Long centerLon, Long spanLat, Long spanLon, String cachetype) { + if (storage == null) { + storage = new cgData(this); + } + cgSearch search = new cgSearch(); + + ArrayList<String> geocodes = storage.getCachedInViewport(centerLat, centerLon, spanLat, spanLon, cachetype); + if (geocodes != null && geocodes.isEmpty() == false) { + for (String gccode : geocodes) { + search.addGeocode(gccode); + } + } + searches.put(search.getCurrentId(), search); + + return search.getCurrentId(); + } + + public Long getStoredInViewport(Long centerLat, Long centerLon, Long spanLat, Long spanLon, String cachetype) { + if (storage == null) { + storage = new cgData(this); + } + cgSearch search = new cgSearch(); + + ArrayList<String> geocodes = storage.getStoredInViewport(centerLat, centerLon, spanLat, spanLon, cachetype); + if (geocodes != null && geocodes.isEmpty() == false) { + for (String gccode : geocodes) { + search.addGeocode(gccode); + } + } + searches.put(search.getCurrentId(), search); + + return search.getCurrentId(); + } + + public Long getOfflineAll(String cachetype) { + if (storage == null) { + storage = new cgData(this); + } + cgSearch search = new cgSearch(); + + ArrayList<String> geocodes = storage.getOfflineAll(cachetype); + if (geocodes != null && geocodes.isEmpty() == false) { + for (String gccode : geocodes) { + search.addGeocode(gccode); + } + } + searches.put(search.getCurrentId(), search); + + return search.getCurrentId(); + } + + public int getAllStoredCachesCount(boolean detailedOnly, String cachetype, Integer list) { + if (storage == null) { + storage = new cgData(this); + } + + return storage.getAllStoredCachesCount(detailedOnly, cachetype, list); + } + + public int getAllHistoricCachesCount(boolean detailedOnly, String cachetype) { + if (storage == null) { + storage = new cgData(this); + } + + return storage.getAllHistoricCachesCount(detailedOnly, cachetype); + } + + public void markStored(String geocode, int listId) { + if (storage == null) { + storage = new cgData(this); + } + storage.markStored(geocode, listId); + } + + public boolean markDropped(String geocode) { + if (storage == null) { + storage = new cgData(this); + } + return storage.markDropped(geocode); + } + + public boolean markFound(String geocode) { + if (storage == null) { + storage = new cgData(this); + } + return storage.markFound(geocode); + } + + public boolean saveWaypoints(String geocode, ArrayList<cgWaypoint> waypoints, boolean drop) { + if (storage == null) { + storage = new cgData(this); + } + return storage.saveWaypoints(geocode, waypoints, drop); + } + + public boolean saveOwnWaypoint(int id, String geocode, cgWaypoint waypoint) { + if (storage == null) { + storage = new cgData(this); + } + return storage.saveOwnWaypoint(id, geocode, waypoint); + } + + public boolean deleteWaypoint(int id) { + if (storage == null) { + storage = new cgData(this); + } + return storage.deleteWaypoint(id); + } + + public boolean saveTrackable(cgTrackable trackable) { + if (storage == null) { + storage = new cgData(this); + } + + final ArrayList<cgTrackable> list = new ArrayList<cgTrackable>(); + list.add(trackable); + + return storage.saveInventory("---", list); + } + + public void addGeocode(Long searchId, String geocode) { + if (this.searches.containsKey(searchId) == false || geocode == null || geocode.length() == 0) { + return; + } + + this.searches.get(searchId).addGeocode(geocode); + } + + public Long addSearch(Long searchId, ArrayList<cgCache> cacheList, Boolean newItem, int reason) { + if (this.searches.containsKey(searchId) == false) { + return null; + } + + cgSearch search = this.searches.get(searchId); + + return addSearch(search, cacheList, newItem, reason); + } + + public Long addSearch(cgSearch search, ArrayList<cgCache> cacheList, Boolean newItem, int reason) { + if (cacheList == null || cacheList.isEmpty()) { + return null; + } + + final long searchId = search.getCurrentId(); + searches.put(searchId, search); + + if (storage == null) { + storage = new cgData(this); + } + if (newItem == true) { + // save only newly downloaded data + for (cgCache oneCache : cacheList) { + String oneGeocode = oneCache.geocode.toUpperCase(); + String oneGuid = oneCache.guid.toLowerCase(); + + oneCache.reason = reason; + + if (storage.isThere(oneGeocode, oneGuid, false, false) == false || reason >= 1) { + // cache is not saved, new data are for storing + storage.saveCache(oneCache); + } else { + cgCache mergedCache = oneCache.merge(storage); + + storage.saveCache(mergedCache); + } + } + } + + return searchId; + } + + public boolean addCacheToSearch(cgSearch search, cgCache cache) { + if (search == null || cache == null) { + return false; + } + + final long searchId = search.getCurrentId(); + + if (searches.containsKey(searchId) == false) { + searches.put(searchId, search); + } + + String geocode = cache.geocode.toUpperCase(); + String guid = cache.guid.toLowerCase(); + + boolean status = false; + + if (storage.isThere(geocode, guid, false, false) == false || cache.reason >= 1) { // if for offline, do not merge + status = storage.saveCache(cache); + } else { + cgCache mergedCache = cache.merge(storage); + + status = storage.saveCache(mergedCache); + } + + if (status == true) { + search.addGeocode(cache.geocode); + } + + return status; + } + + public void dropStored(int listId) { + if (storage == null) { + storage = new cgData(this); + } + storage.dropStored(listId); + } + + public ArrayList<cgTrackable> loadInventory(String geocode) { + return storage.loadInventory(geocode); + } + + public ArrayList<cgSpoiler> loadSpoilers(String geocode) { + return storage.loadSpoilers(geocode); + } + + public cgWaypoint loadWaypoint(int id) { + return storage.loadWaypoint(id); + } + + public void setAction(String act) { + action = act; + } + + public String getAction() { + if (action == null) { + return ""; + } + return action; + } + + public boolean addLog(String geocode, cgLog log) { + if (geocode == null || geocode.length() == 0) { + return false; + } + if (log == null) { + return false; + } + + ArrayList<cgLog> list = new ArrayList<cgLog>(); + list.add(log); + + return storage.saveLogs(geocode, list, false); + } + + public void setLastLoc(Double lat, Double lon) { + lastLatitude = lat; + lastLongitude = lon; + } + + public Double getLastLat() { + return lastLatitude; + } + + public Double getLastLon() { + return lastLongitude; + } + + public boolean saveLogOffline(String geocode, Date date, int logtype, String log) { + return storage.saveLogOffline(geocode, date, logtype, log); + } + + public cgLog loadLogOffline(String geocode) { + return storage.loadLogOffline(geocode); + } + + public void clearLogOffline(String geocode) { + storage.clearLogOffline(geocode); + } + + public void saveVisitDate(String geocode) { + storage.saveVisitDate(geocode); + } + + public ArrayList<cgList> getLists() { + return storage.getLists(getResources()); + } + + public cgList getList(int id) { + return storage.getList(id, getResources()); + } + + public int createList(String title) { + return storage.createList(title); + } + + public boolean removeList(int id) { + return storage.removeList(id); + } + + public void moveToList(String geocode, int listId) { + storage.moveToList(geocode, listId); + } +} diff --git a/src/cgeo/geocaching/cgeoauth.java b/src/cgeo/geocaching/cgeoauth.java new file mode 100644 index 0000000..84514ba --- /dev/null +++ b/src/cgeo/geocaching/cgeoauth.java @@ -0,0 +1,413 @@ +package cgeo.geocaching; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.BufferedReader; +import java.net.URL; +import java.net.URLConnection; +import java.util.HashMap; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import android.net.Uri; +import android.content.Intent; +import android.os.Bundle; +import android.app.Activity; +import android.app.ProgressDialog; +import android.view.View; +import android.widget.EditText; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.widget.Button; +import java.io.IOException; +import javax.net.ssl.HttpsURLConnection; + +public class cgeoauth extends Activity { + private cgeoapplication app = null; + private Resources res = null; + private Activity activity = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private SharedPreferences prefs = null; + private String OAtoken = null; + private String OAtokenSecret = null; + private final Pattern paramsPattern1 = Pattern.compile("oauth_token=([a-zA-Z0-9\\-\\_\\.]+)"); + private final Pattern paramsPattern2 = Pattern.compile("oauth_token_secret=([a-zA-Z0-9\\-\\_\\.]+)"); + private Button startButton = null; + private EditText pinEntry = null; + private Button pinEntryButton = null; + private ProgressDialog requestTokenDialog = null; + private ProgressDialog changeTokensDialog = null; + private Handler requestTokenHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + if (requestTokenDialog != null && requestTokenDialog.isShowing() == true) { + requestTokenDialog.dismiss(); + } + + startButton.setOnClickListener(new startListener()); + startButton.setEnabled(true); + + if (msg.what == 1) { + startButton.setText(res.getString(R.string.auth_again)); + + pinEntry.setVisibility(View.VISIBLE); + pinEntryButton.setVisibility(View.VISIBLE); + pinEntryButton.setOnClickListener(new confirmPINListener()); + } else { + warning.showToast(res.getString(R.string.err_auth_initialize)); + startButton.setText(res.getString(R.string.auth_start)); + } + } + }; + private Handler changeTokensHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + if (changeTokensDialog != null && changeTokensDialog.isShowing() == true) { + changeTokensDialog.dismiss(); + } + + pinEntryButton.setOnClickListener(new confirmPINListener()); + pinEntryButton.setEnabled(true); + + if (msg.what == 1) { + warning.showToast(res.getString(R.string.auth_dialog_completed)); + + pinEntryButton.setVisibility(View.GONE); + + finish(); + } else { + warning.showToast(res.getString(R.string.err_auth_process)); + + pinEntry.setVisibility(View.GONE); + pinEntryButton.setVisibility(View.GONE); + startButton.setText(res.getString(R.string.auth_start)); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + app.setAction("setting up"); + prefs = getSharedPreferences(cgSettings.preferences, 0); + settings = new cgSettings(this, prefs); + base = new cgBase(app, settings, prefs); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.auth); + base.setTitle(activity, res.getString(R.string.auth_twitter)); + + // google analytics + base.sendAnal(activity, "/auth"); + + init(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + private void init() { + startButton = (Button) findViewById(R.id.start); + pinEntry = (EditText) findViewById(R.id.pin); + pinEntryButton = (Button) findViewById(R.id.pin_button); + + OAtoken = prefs.getString("temp-token-public", null); + OAtokenSecret = prefs.getString("temp-token-secret", null); + + startButton.setEnabled(true); + startButton.setOnClickListener(new startListener()); + + if (OAtoken == null || OAtoken.length() == 0 || OAtokenSecret == null || OAtokenSecret.length() == 0) { + // start authorization process + startButton.setText(res.getString(R.string.auth_start)); + } else { + // already have temporary tokens, continue from pin + startButton.setText(res.getString(R.string.auth_again)); + + pinEntry.setVisibility(View.VISIBLE); + pinEntryButton.setVisibility(View.VISIBLE); + pinEntryButton.setOnClickListener(new confirmPINListener()); + } + } + + private void requestToken() { + final String host = "twitter.com"; + final String pathRequest = "/oauth/request_token"; + final String pathAuthorize = "/oauth/authorize"; + final String method = "GET"; + + int status = 0; + try { + String lineOne = null; + HttpsURLConnection connection = null; + + try { + final StringBuilder sb = new StringBuilder(); + final String params = cgOAuth.signOAuth(host, pathRequest, method, true, new HashMap<String, String>(), null, null); + + int code = -1; + int retries = 0; + + do { + // base.trustAllHosts(); + Log.d(cgSettings.tag, "https://" + host + pathRequest + "?" + params); + final URL u = new URL("https://" + host + pathRequest + "?" + params); + final URLConnection uc = u.openConnection(); + connection = (HttpsURLConnection) uc; + + // connection.setHostnameVerifier(base.doNotVerify); + connection.setReadTimeout(30000); + connection.setRequestMethod(method); + HttpsURLConnection.setFollowRedirects(true); + connection.setDoInput(true); + connection.setDoOutput(false); + + final InputStream in = connection.getInputStream(); + final InputStreamReader ins = new InputStreamReader(in); + final BufferedReader br = new BufferedReader(ins); + + while ((lineOne = br.readLine()) != null) { + sb.append(lineOne); + sb.append("\n"); + } + + code = connection.getResponseCode(); + retries++; + + Log.i(cgSettings.tag, host + ": " + connection.getResponseCode() + " " + connection.getResponseMessage()); + + br.close(); + in.close(); + ins.close(); + } while (code == -1 && retries < 5); + + final String line = sb.toString(); + + if (line != null && line.length() > 0) { + final Matcher paramsMatcher1 = paramsPattern1.matcher(line); + if (paramsMatcher1.find() == true && paramsMatcher1.groupCount() > 0) { + OAtoken = paramsMatcher1.group(1).toString(); + } + final Matcher paramsMatcher2 = paramsPattern2.matcher(line); + if (paramsMatcher2.find() == true && paramsMatcher2.groupCount() > 0) { + OAtokenSecret = paramsMatcher2.group(1).toString(); + } + + if (OAtoken != null && OAtoken.length() > 0 && OAtokenSecret != null && OAtokenSecret.length() > 0) { + final SharedPreferences.Editor prefsEdit = getSharedPreferences(cgSettings.preferences, 0).edit(); + prefsEdit.putString("temp-token-public", OAtoken); + prefsEdit.putString("temp-token-secret", OAtokenSecret); + prefsEdit.commit(); + + try { + final HashMap<String, String> paramsPre = new HashMap<String, String>(); + paramsPre.put("oauth_callback", "oob"); + + final String paramsBrowser = cgOAuth.signOAuth(host, pathAuthorize, "GET", true, paramsPre, OAtoken, OAtokenSecret); + + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://" + host + pathAuthorize + "?" + paramsBrowser))); + + status = 1; + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoauth.requestToken(2): " + e.toString()); + } + } + } + } catch (IOException eio) { + Log.e(cgSettings.tag, "cgeoauth.requestToken(IO): " + eio.toString() + " ~ " + connection.getResponseCode() + ": " + connection.getResponseMessage()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoauth.requestToken(1): " + e.toString()); + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } catch (Exception e2) { + Log.e(cgSettings.tag, "cgeoauth.requestToken(3): " + e2.toString()); + } + + requestTokenHandler.sendEmptyMessage(status); + } + + private void changeToken() { + final String host = "twitter.com"; + final String path = "/oauth/access_token"; + final String method = "POST"; + + int status = 0; + String lineOne = null; + + try { + final HashMap<String, String> paramsPre = new HashMap<String, String>(); + paramsPre.put("oauth_verifier", pinEntry.getText().toString()); + + int code = -1; + int retries = 0; + + final String params = cgOAuth.signOAuth(host, path, method, true, paramsPre, OAtoken, OAtokenSecret); + final StringBuilder sb = new StringBuilder(); + do { + // base.trustAllHosts(); + final URL u = new URL("https://" + host + path); + final URLConnection uc = u.openConnection(); + final HttpsURLConnection connection = (HttpsURLConnection) uc; + + // connection.setHostnameVerifier(base.doNotVerify); + connection.setReadTimeout(30000); + connection.setRequestMethod(method); + HttpsURLConnection.setFollowRedirects(true); + connection.setDoOutput(true); + connection.setDoInput(true); + + final OutputStream out = connection.getOutputStream(); + final OutputStreamWriter wr = new OutputStreamWriter(out); + + wr.write(params); + wr.flush(); + wr.close(); + out.close(); + + final InputStream in = connection.getInputStream(); + final InputStreamReader ins = new InputStreamReader(in); + final BufferedReader br = new BufferedReader(ins); + + while ((lineOne = br.readLine()) != null) { + sb.append(lineOne); + sb.append("\n"); + } + + code = connection.getResponseCode(); + retries++; + + Log.i(cgSettings.tag, host + ": " + connection.getResponseCode() + " " + connection.getResponseMessage()); + + br.close(); + ins.close(); + in.close(); + connection.disconnect(); + } while (code == -1 && retries < 5); + + final String line = sb.toString(); + + OAtoken = ""; + OAtokenSecret = ""; + + final Matcher paramsMatcher1 = paramsPattern1.matcher(line); + if (paramsMatcher1.find() == true && paramsMatcher1.groupCount() > 0) { + OAtoken = paramsMatcher1.group(1).toString(); + } + final Matcher paramsMatcher2 = paramsPattern2.matcher(line); + if (paramsMatcher2.find() == true && paramsMatcher2.groupCount() > 0) { + OAtokenSecret = paramsMatcher2.group(1).toString(); + } + + if (OAtoken.length() == 0 || OAtokenSecret.length() == 0) { + OAtoken = ""; + OAtokenSecret = ""; + + final SharedPreferences.Editor prefs = getSharedPreferences(cgSettings.preferences, 0).edit(); + prefs.putString("tokenpublic", null); + prefs.putString("tokensecret", null); + prefs.putInt("twitter", 0); + prefs.commit(); + } else { + final SharedPreferences.Editor prefs = getSharedPreferences(cgSettings.preferences, 0).edit(); + prefs.remove("temp-token-public"); + prefs.remove("temp-token-secret"); + prefs.putString("tokenpublic", OAtoken); + prefs.putString("tokensecret", OAtokenSecret); + prefs.putInt("twitter", 1); + prefs.commit(); + + status = 1; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeoauth.changeToken: " + e.toString()); + } + + changeTokensHandler.sendEmptyMessage(status); + } + + private class startListener implements View.OnClickListener { + + public void onClick(View arg0) { + if (requestTokenDialog == null) { + requestTokenDialog = new ProgressDialog(activity); + requestTokenDialog.setCancelable(false); + requestTokenDialog.setMessage(res.getString(R.string.auth_dialog_wait)); + } + requestTokenDialog.show(); + startButton.setEnabled(false); + startButton.setOnTouchListener(null); + startButton.setOnClickListener(null); + + final SharedPreferences.Editor prefs = getSharedPreferences(cgSettings.preferences, 0).edit(); + prefs.putString("temp-token-public", null); + prefs.putString("temp-token-secret", null); + prefs.commit(); + + (new Thread() { + + @Override + public void run() { + requestToken(); + } + }).start(); + } + } + + private class confirmPINListener implements View.OnClickListener { + + public void onClick(View arg0) { + if (((EditText) findViewById(R.id.pin)).getText().toString().length() == 0) { + warning.helpDialog(res.getString(R.string.auth_dialog_pin_title), res.getString(R.string.auth_dialog_pin_message)); + return; + } + + if (changeTokensDialog == null) { + changeTokensDialog = new ProgressDialog(activity); + changeTokensDialog.setCancelable(false); + changeTokensDialog.setMessage(res.getString(R.string.auth_dialog_wait)); + } + changeTokensDialog.show(); + pinEntryButton.setEnabled(false); + pinEntryButton.setOnTouchListener(null); + pinEntryButton.setOnClickListener(null); + + (new Thread() { + + @Override + public void run() { + changeToken(); + } + }).start(); + } + } + + public void goHome(View view) { + base.goHome(activity); + } +} diff --git a/src/cgeo/geocaching/cgeocaches.java b/src/cgeo/geocaching/cgeocaches.java new file mode 100644 index 0000000..9c730de --- /dev/null +++ b/src/cgeo/geocaching/cgeocaches.java @@ -0,0 +1,2193 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import android.os.Handler; +import android.os.Message; +import android.os.Bundle; +import android.util.Log; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ListActivity; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.view.Menu; +import android.view.MenuItem; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.SubMenu; +import android.view.View; +import android.view.WindowManager; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import cgeo.geocaching.filter.cgFilterBySize; +import cgeo.geocaching.filter.cgFilterByTrackables; +import cgeo.geocaching.filter.cgFilterByType; + +import com.google.android.apps.analytics.GoogleAnalyticsTracker; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.util.Locale; + +public class cgeocaches extends ListActivity { + + private GoogleAnalyticsTracker tracker = null; + private String action = null; + private String type = null; + private Double latitude = null; + private Double longitude = null; + private String cachetype = null; + private String keyword = null; + private String address = null; + private String username = null; + private Long searchId = null; + private ArrayList<cgCache> cacheList = new ArrayList<cgCache>(); + private cgeoapplication app = null; + private Resources res = null; + private static Activity activity = null; + private cgCacheListAdapter adapter = null; + private LayoutInflater inflater = null; + private View listFooter = null; + private TextView listFooterText = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private ProgressDialog waitDialog = null; + private Double northHeading = new Double(0); + private cgGeo geo = null; + private cgDirection dir = null; + private cgUpdateLoc geoUpdate = new update(); + private cgUpdateDir dirUpdate = new updateDir(); + private String title = ""; + private int detailTotal = 0; + private int detailProgress = 0; + private Long detailProgressTime = 0l; + private geocachesLoadDetails threadD = null; + private geocachesLoadFromWeb threadW = null; + private geocachesDropDetails threadR = null; + private int listId = 0; + private ArrayList<cgList> lists = null; + private String selectedFilter = null; + private cgCacheGeocodeComparator gcComparator = new cgCacheGeocodeComparator(); + private Handler loadCachesHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (searchId != null && searchId > 0) { + base.setTitle(activity, title + " [" + app.getCount(searchId) + "]"); + cacheList.clear(); + + final ArrayList<cgCache> cacheListTmp = app.getCaches(searchId); + if (cacheListTmp != null && cacheListTmp.isEmpty() == false) { + cacheList.addAll(cacheListTmp); + cacheListTmp.clear(); + + Collections.sort((List<cgCache>)cacheList, gcComparator); + } + } else { + base.setTitle(activity, title); + } + + setAdapter(); + + if (cacheList == null) { + warning.showToast(res.getString(R.string.err_list_load_fail)); + setMoreCaches(false); + } else { + final Integer count = app.getTotal(searchId); + + if (count != null && count > 0) { + if (cacheList.size() < app.getTotal(searchId) && cacheList.size() < 1000) { + setMoreCaches(true); + } else { + setMoreCaches(false); + } + } else { + setMoreCaches(false); + } + } + + if (cacheList != null && app.getError(searchId) != null && app.getError(searchId).equalsIgnoreCase(cgBase.errorRetrieve.get(-7)) == true) { + AlertDialog.Builder dialog = new AlertDialog.Builder(activity); + dialog.setTitle(res.getString(R.string.license)); + dialog.setMessage(res.getString(R.string.err_license)); + dialog.setCancelable(true); + dialog.setNegativeButton(res.getString(R.string.license_dismiss), new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + settings.deleteCookies(); + dialog.cancel(); + } + }); + dialog.setPositiveButton(res.getString(R.string.license_show), new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + settings.deleteCookies(); + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/software/agreement.aspx?ID=0"))); + } + }); + + AlertDialog alert = dialog.create(); + alert.show(); + } else if (app != null && app.getError(searchId) != null && app.getError(searchId).length() > 0) { + warning.showToast(res.getString(R.string.err_download_fail) + app.getError(searchId) + "."); + + hideLoading(); + base.showProgress(activity, false); + + finish(); + return; + } + + if (geo != null && geo.latitudeNow != null && geo.longitudeNow != null) { + adapter.setActualCoordinates(geo.latitudeNow, geo.longitudeNow); + adapter.setActualHeading(northHeading); + } + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_detail_cache_find_any)); + Log.e(cgSettings.tag, "cgeocaches.loadCachesHandler: " + e.toString()); + + hideLoading(); + base.showProgress(activity, false); + + finish(); + return; + } + + try { + hideLoading(); + base.showProgress(activity, false); + } catch (Exception e2) { + Log.e(cgSettings.tag, "cgeocaches.loadCachesHandler.2: " + e2.toString()); + } + + if (adapter != null) { + adapter.setSelectMode(false, true); + } + } + }; + private Handler loadNextPageHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (searchId != null && searchId > 0) { + base.setTitle(activity, title + " [" + app.getCount(searchId) + "]"); + cacheList.clear(); + + final ArrayList<cgCache> cacheListTmp = app.getCaches(searchId); + if (cacheListTmp != null && cacheListTmp.isEmpty() == false) { + cacheList.addAll(cacheListTmp); + cacheListTmp.clear(); + Collections.sort((List<cgCache>)cacheList, gcComparator); + } + if(adapter != null){ + adapter.reFilter(); + } + } else { + base.setTitle(activity, title); + } + + setAdapter(); + + if (cacheList == null) { + warning.showToast(res.getString(R.string.err_list_load_fail)); + setMoreCaches(false); + } else { + final Integer count = app.getTotal(searchId); + if (count != null && count > 0) { + if (cacheList.size() < app.getTotal(searchId) && cacheList.size() < 1000) { + setMoreCaches(true); + } else { + setMoreCaches(false); + } + } else { + setMoreCaches(false); + } + } + + if (app.getError(searchId) != null && app.getError(searchId).length() > 0) { + warning.showToast(res.getString(R.string.err_download_fail) + app.getError(searchId) + "."); + + listFooter.setOnClickListener(new moreCachesListener()); + hideLoading(); + base.showProgress(activity, false); + + finish(); + return; + } + + if (geo != null && geo.latitudeNow != null && geo.longitudeNow != null) { + adapter.setActualCoordinates(geo.latitudeNow, geo.longitudeNow); + adapter.setActualHeading(northHeading); + } + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_detail_cache_find_next)); + Log.e(cgSettings.tag, "cgeocaches.loadNextPageHandler: " + e.toString()); + } + + listFooter.setOnClickListener(new moreCachesListener()); + + hideLoading(); + base.showProgress(activity, false); + + if (adapter != null) { + adapter.setSelectMode(false, true); + } + } + }; + private Handler loadDetailsHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + setAdapter(); + + if (msg.what > -1) { + if (waitDialog != null) { + cacheList.get(msg.what).statusChecked = false; + + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + + Float diffTime = new Float((System.currentTimeMillis() - detailProgressTime) / 1000); // seconds left + Float oneCache = diffTime / detailProgress; // left time per cache + int etaTime = (int) ((detailTotal - detailProgress) * oneCache / 60); // seconds remaining + + waitDialog.setProgress(detailProgress); + if (etaTime < 1) { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + res.getString(R.string.caches_eta_ltm)); + } else if (etaTime == 1) { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + etaTime + " " + res.getString(R.string.caches_eta_min)); + } else { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + etaTime + " " + res.getString(R.string.caches_eta_mins)); + } + } + } else { + if (cacheList != null && searchId != null) { + final ArrayList<cgCache> cacheListTmp = app.getCaches(searchId); + if (cacheListTmp != null && cacheListTmp.isEmpty() == false) { + cacheList.clear(); + cacheList.addAll(cacheListTmp); + cacheListTmp.clear(); + Collections.sort((List<cgCache>)cacheList, gcComparator); + } + } + + if (geo != null && geo.latitudeNow != null && geo.longitudeNow != null) { + adapter.setActualCoordinates(geo.latitudeNow, geo.longitudeNow); + adapter.setActualHeading(northHeading); + } + + base.showProgress(activity, false); + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog.setOnCancelListener(null); + } + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + if (settings.livelist == 1 && settings.useCompass == 1 && dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + } + } + }; + private Handler downloadFromWebHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + setAdapter(); + + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + + if (msg.what == 0) { //no caches + waitDialog.setMessage(res.getString(R.string.web_import_waiting)); + } else if (msg.what == 1) { //cache downloading + waitDialog.setMessage(res.getString(R.string.web_downloading)+" "+(String)msg.obj+"..."); + } else if (msg.what == 2) { //Cache downloaded + waitDialog.setMessage(res.getString(R.string.web_downloaded)+" "+(String)msg.obj+"."); + //Once a cache is downloaded I used switchList to refresh it. + switchList(listId, -1); + } else if (msg.what == -2) { + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog.setOnCancelListener(null); + } + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + warning.showToast(res.getString(R.string.gpx_import_no_files)); + finish(); + return; + } else { + if (adapter != null) { + adapter.setSelectMode(false, true); + } + + cacheList.clear(); + + final ArrayList<cgCache> cacheListTmp = app.getCaches(searchId); + if (cacheListTmp != null && cacheListTmp.isEmpty() == false) { + cacheList.addAll(cacheListTmp); + cacheListTmp.clear(); + + Collections.sort((List<cgCache>)cacheList, gcComparator); + } + + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog.setOnCancelListener(null); + } + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + } + }; + private Handler dropDetailsHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + setAdapter(); + + if (msg.what > -1) { + cacheList.get(msg.what).statusChecked = false; + } else { + if (adapter != null) { + adapter.setSelectMode(false, true); + } + + cacheList.clear(); + + final ArrayList<cgCache> cacheListTmp = app.getCaches(searchId); + if (cacheListTmp != null && cacheListTmp.isEmpty() == false) { + cacheList.addAll(cacheListTmp); + cacheListTmp.clear(); + + Collections.sort((List<cgCache>)cacheList, gcComparator); + } + + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog.setOnCancelListener(null); + } + } + } + }; + private ContextMenuInfo lastMenuInfo; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + app.setAction(action); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.caches); + base.setTitle(activity, "caches"); + + // google analytics + tracker = GoogleAnalyticsTracker.getInstance(); + tracker.start(cgSettings.analytics, this); + tracker.dispatch(); + base.sendAnal(activity, tracker, "/cache/list"); + + // get parameters + Bundle extras = getIntent().getExtras(); + if (extras != null) { + type = extras.getString("type"); + latitude = extras.getDouble("latitude"); + longitude = extras.getDouble("longitude"); + cachetype = extras.getString("cachetype"); + keyword = extras.getString("keyword"); + address = extras.getString("address"); + username = extras.getString("username"); + } + + init(); + + Thread threadPure; + cgSearchThread thread; + + if (type.equals("offline") == true) { + listId = settings.getLastList(); + if (listId <= 0) { + listId = 1; + title = res.getString(R.string.caches_stored); + } else { + final cgList list = app.getList(listId); + title = list.title; + } + + base.setTitle(activity, title); + base.showProgress(activity, true); + setLoadingCaches(); + + threadPure = new geocachesLoadByOffline(loadCachesHandler, latitude, longitude, listId); + threadPure.start(); + } else if (type.equals("history") == true) { + if (adapter != null) { + adapter.setHistoric(true); + } + + title = res.getString(R.string.caches_history); + base.setTitle(activity, title); + base.showProgress(activity, true); + setLoadingCaches(); + + threadPure = new geocachesLoadByHistory(loadCachesHandler); + threadPure.start(); + } else if (type.equals("nearest") == true) { + action = "pending"; + title = res.getString(R.string.caches_nearby); + base.setTitle(activity, title); + base.showProgress(activity, true); + setLoadingCaches(); + + thread = new geocachesLoadByCoords(loadCachesHandler, latitude, longitude, cachetype); + thread.setRecaptchaHandler(new cgSearchHandler(activity, res, thread)); + thread.start(); + } else if (type.equals("coordinate") == true) { + action = "planning"; + title = base.formatCoordinate(latitude, res.getString(R.string.search_lat), true) + " | " + base.formatCoordinate(longitude, res.getString(R.string.search_lon), true); + base.setTitle(activity, title); + base.showProgress(activity, true); + setLoadingCaches(); + + thread = new geocachesLoadByCoords(loadCachesHandler, latitude, longitude, cachetype); + thread.setRecaptchaHandler(new cgSearchHandler(activity, res, thread)); + thread.start(); + } else if (type.equals("keyword") == true) { + title = keyword; + base.setTitle(activity, title); + base.showProgress(activity, true); + setLoadingCaches(); + + thread = new geocachesLoadByKeyword(loadCachesHandler, keyword, cachetype); + thread.setRecaptchaHandler(new cgSearchHandler(activity, res, thread)); + thread.start(); + } else if (type.equals("address") == true) { + action = "planning"; + if (address != null && address.length() > 0) { + title = address; + base.setTitle(activity, title); + base.showProgress(activity, true); + setLoadingCaches(); + } else { + title = base.formatCoordinate(latitude, res.getString(R.string.search_lat), true) + " | " + base.formatCoordinate(longitude, res.getString(R.string.search_lon), true); + base.setTitle(activity, title); + base.showProgress(activity, true); + setLoadingCaches(); + } + + thread = new geocachesLoadByCoords(loadCachesHandler, latitude, longitude, cachetype); + thread.setRecaptchaHandler(new cgSearchHandler(activity, res, thread)); + thread.start(); + } else if (type.equals("username") == true) { + title = username; + base.setTitle(activity, title); + base.showProgress(activity, true); + setLoadingCaches(); + + thread = new geocachesLoadByUserName(loadCachesHandler, username, cachetype); + thread.setRecaptchaHandler(new cgSearchHandler(activity, res, thread)); + thread.start(); + } else if (type.equals("owner") == true) { + title = username; + base.setTitle(activity, title); + base.showProgress(activity, true); + setLoadingCaches(); + + thread = new geocachesLoadByOwner(loadCachesHandler, username, cachetype); + thread.setRecaptchaHandler(new cgSearchHandler(activity, res, thread)); + thread.start(); + } else { + title = "caches"; + base.setTitle(activity, title); + Log.e(cgSettings.tag, "cgeocaches.onCreate: No action or unknown action specified"); + } + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + init(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + init(); + + if (adapter != null && geo != null && geo.latitudeNow != null && geo.longitudeNow != null) { + adapter.setActualCoordinates(geo.latitudeNow, geo.longitudeNow); + adapter.setActualHeading(northHeading); + } + + if (adapter != null) { + adapter.setSelectMode(false, true); + if (geo != null && geo.latitudeNow != null && geo.longitudeNow != null) { + adapter.forceSort(geo.latitudeNow, geo.longitudeNow); + } + } + + if (loadCachesHandler != null && searchId != null) { + loadCachesHandler.sendEmptyMessage(0); + } + } + + @Override + public void onDestroy() { + if (adapter != null) { + adapter = null; + } + + if (dir != null) { + dir = app.removeDir(); + } + if (geo != null) { + geo = app.removeGeo(); + } + if (tracker != null) { + tracker.stop(); + } + + super.onDestroy(); + } + + @Override + public void onStop() { + if (dir != null) { + dir = app.removeDir(); + } + if (geo != null) { + geo = app.removeGeo(); + } + + super.onStop(); + } + + @Override + public void onPause() { + if (dir != null) { + dir = app.removeDir(); + } + if (geo != null) { + geo = app.removeGeo(); + } + + super.onPause(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + SubMenu subMenuFilter = menu.addSubMenu(0, 105, 0, res.getString(R.string.caches_filter)).setIcon(android.R.drawable.ic_menu_search); + subMenuFilter.setHeaderTitle(res.getString(R.string.caches_filter_title)); + subMenuFilter.add(0, 21, 0, res.getString(R.string.caches_filter_type)); + subMenuFilter.add(0, 22, 0, res.getString(R.string.caches_filter_size)); + subMenuFilter.add(0, 23, 0, res.getString(R.string.caches_filter_track)); + subMenuFilter.add(0, 24, 0, res.getString(R.string.caches_filter_clear)); + + SubMenu subMenuSort = menu.addSubMenu(0, 104, 0, res.getString(R.string.caches_sort)).setIcon(android.R.drawable.ic_menu_sort_alphabetically); + subMenuSort.setHeaderTitle(res.getString(R.string.caches_sort_title)); + + // sort the context menu labels alphabetically for easier reading + HashMap<String, Integer> comparators = new HashMap<String, Integer>(); + comparators.put(res.getString(R.string.caches_sort_distance), 10); + comparators.put(res.getString(R.string.caches_sort_difficulty), 11); + comparators.put(res.getString(R.string.caches_sort_terrain), 12); + comparators.put(res.getString(R.string.caches_sort_size), 13); + comparators.put(res.getString(R.string.caches_sort_favorites), 14); + comparators.put(res.getString(R.string.caches_sort_name), 15); + comparators.put(res.getString(R.string.caches_sort_gccode), 16); + comparators.put(res.getString(R.string.caches_sort_rating), 18); + comparators.put(res.getString(R.string.caches_sort_vote), 19); + comparators.put(res.getString(R.string.caches_sort_inventory), 20); + + ArrayList<String> sortedLabels = new ArrayList<String>(comparators.keySet()); + Collections.sort(sortedLabels); + for (String label : sortedLabels) { + Integer id = comparators.get(label); + subMenuSort.add(1, id, 0, label).setCheckable(true).setChecked(id == 10); + } + + subMenuSort.setGroupCheckable(1, true, true); + + menu.add(0, 0, 0, res.getString(R.string.caches_select_mode)).setIcon(android.R.drawable.ic_menu_agenda); + menu.add(0, 9, 0, res.getString(R.string.caches_select_invert)).setIcon(android.R.drawable.ic_menu_agenda); + if (type.equals("offline") == true) { + SubMenu subMenu = menu.addSubMenu(0, 103, 0, res.getString(R.string.caches_manage)).setIcon(android.R.drawable.ic_menu_save); + subMenu.add(0, 5, 0, res.getString(R.string.caches_drop_all)); // delete saved caches + subMenu.add(0, 1, 0, res.getString(R.string.cache_offline_refresh)); // download details for all caches + if (settings.webDeviceCode == null) + { + menu.add(0, 6, 0, res.getString(R.string.gpx_import_title)).setIcon(android.R.drawable.ic_menu_upload); // import gpx file + } else { + SubMenu subMenuImport = menu.addSubMenu(0, 105, 0, res.getString(R.string.import_title)).setIcon(android.R.drawable.ic_menu_upload); // import + subMenuImport.add(1, 6, 0, res.getString(R.string.gpx_import_title)).setCheckable(false).setChecked(false); + subMenuImport.add(1, 25, 0, res.getString(R.string.web_import_title)).setCheckable(false).setChecked(false); + } + } else { + menu.add(0, 1, 0, res.getString(R.string.caches_store_offline)).setIcon(android.R.drawable.ic_menu_set_as); // download details for all caches + } + + final Intent intentTest = new Intent(Intent.ACTION_VIEW); + intentTest.setData(Uri.parse("menion.points:x")); + if (cgBase.isIntentAvailable(activity, intentTest) == true) { + SubMenu subMenu = menu.addSubMenu(0, 101, 0, res.getString(R.string.caches_on_map)).setIcon(android.R.drawable.ic_menu_mapmode); + subMenu.add(0, 2, 0, res.getString(R.string.caches_map_cgeo)); // show all caches on map using c:geo + subMenu.add(0, 3, 0, res.getString(R.string.caches_map_locus)); // show all caches on map using Locus + } else { + menu.add(0, 2, 0, res.getString(R.string.caches_on_map)).setIcon(android.R.drawable.ic_menu_mapmode); // show all caches on map + } + + if (type.equals("offline") == true) { + SubMenu subMenu = menu.addSubMenu(0, 102, 0, res.getString(R.string.list_menu)).setIcon(android.R.drawable.ic_menu_more); + subMenu.add(0, 7, 0, res.getString(R.string.list_menu_create)); + subMenu.add(0, 8, 0, res.getString(R.string.list_menu_drop)); + subMenu.add(0, 17, 0, res.getString(R.string.list_menu_change)); + } + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + try { + if (adapter != null && adapter.getSelectMode() == true) { + menu.findItem(0).setTitle(res.getString(R.string.caches_select_mode_exit)); + menu.findItem(9).setVisible(true); + } else { + menu.findItem(0).setTitle(res.getString(R.string.caches_select_mode)); + menu.findItem(9).setVisible(false); + } + + if (type != null && type.equals("offline") == true) { + if (adapter != null && adapter.getChecked() > 0) { + menu.findItem(5).setTitle(res.getString(R.string.caches_drop_selected) + " (" + adapter.getChecked() + ")"); + } else { + menu.findItem(5).setTitle(res.getString(R.string.caches_drop_all)); + } + + if (adapter != null && adapter.getChecked() > 0) { + menu.findItem(1).setTitle(res.getString(R.string.caches_refresh_selected) + " (" + adapter.getChecked() + ")"); + } else { + menu.findItem(1).setTitle(res.getString(R.string.caches_refresh_all)); + } + } else { + if (adapter == null) { + Log.i(cgSettings.tag, "No adapter"); + } else { + Log.i(cgSettings.tag, "Checked: " + adapter.getChecked()); + } + if (adapter != null && adapter.getChecked() > 0) { + menu.findItem(1).setTitle(res.getString(R.string.caches_store_selected) + " (" + adapter.getChecked() + ")"); + } else { + menu.findItem(1).setTitle(res.getString(R.string.caches_store_offline)); + } + } + + if (type != null && type.equals("offline") == false && (cacheList != null && app != null && cacheList.size() >= app.getTotal(searchId))) { // there are no more caches + menu.findItem(0).setEnabled(false); + } else { + menu.findItem(0).setEnabled(true); + } + + if (listId == 1) { + menu.findItem(8).setVisible(false); + } else { + menu.findItem(8).setVisible(true); + } + if (app.getLists().size() < 2) { + menu.findItem(17).setVisible(false); + } else { + menu.findItem(17).setVisible(true); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeocaches.onPrepareOptionsMenu: " + e.toString()); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case 0: + if (adapter != null) { + adapter.switchSelectMode(); + } + return true; + case 1: + refreshStored(); + return true; + case 2: + showOnMap(); + return false; + case 3: + showOnLocus(); + return false; + case 5: + dropStored(); + return false; + case 6: + importGpx(); + return false; + case 7: + createList(); + return false; + case 8: + removeList(); + return false; + case 9: + if (adapter != null) { + adapter.invertSelection(); + } + return false; + case 10: + setComparator(item, null); + return false; + case 11: + setComparator(item, new cgCacheDifficultyComparator()); + return false; + case 12: + setComparator(item, new cgCacheTerrainComparator()); + return false; + case 13: + setComparator(item, new cgCacheSizeComparator()); + return false; + case 14: + setComparator(item, new cgCachePopularityComparator()); + return false; + case 15: + setComparator(item, new cgCacheNameComparator()); + return false; + case 16: + setComparator(item, new cgCacheGeocodeComparator()); + return false; + case 17: + selectList(null); + return false; + case 18: + setComparator(item, new cgCacheRatingComparator()); + return false; + case 19: + setComparator(item, new cgCacheVoteComparator()); + return false; + case 20: + setComparator(item, new cgCacheInventoryComparator()); + return false; + case 21: + selectedFilter = res.getString(R.string.caches_filter_type); + openContextMenu(getListView()); + return false; + case 22: + selectedFilter = res.getString(R.string.caches_filter_size); + openContextMenu(getListView()); + return false; + case 23: + adapter.setFilter(new cgFilterByTrackables()); + return false; + case 24: + if (adapter != null) { + adapter.setFilter(null); + } + return false; + case 25: + importWeb(); + return false; + } + + return false; + } + + private void setComparator(MenuItem item, + Comparator<cgCache> comparator) { + if (adapter != null) { + adapter.setComparator(comparator); + } + item.setChecked(true); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo info) { + super.onCreateContextMenu(menu, view, info); + + if (adapter == null) { + return; + } + + AdapterContextMenuInfo adapterInfo = null; + try { + adapterInfo = (AdapterContextMenuInfo) info; + } catch (Exception e) { + Log.w(cgSettings.tag, "cgeocaches.onCreateContextMenu: " + e.toString()); + } + + if ((adapterInfo == null || adapterInfo.position < 0) && selectedFilter != null){ + // Context menu opened by selecting an option on the filter submenu + + if (selectedFilter.equals(res.getString(R.string.caches_filter_size))) { + menu.setHeaderTitle(res.getString(R.string.caches_filter_size_title)); + menu.add(0, 8, 0, res.getString(R.string.caches_filter_size_micro)); + menu.add(0, 9, 0, res.getString(R.string.caches_filter_size_small)); + menu.add(0, 10, 0, res.getString(R.string.caches_filter_size_regular)); + menu.add(0, 11, 0, res.getString(R.string.caches_filter_size_large)); + menu.add(0, 12, 0, res.getString(R.string.caches_filter_size_other)); + menu.add(0, 13, 0, res.getString(R.string.caches_filter_size_virtual)); + menu.add(0, 14, 0, res.getString(R.string.caches_filter_size_notchosen)); + } else if (selectedFilter.equals(res.getString(R.string.caches_filter_type))) { + menu.setHeaderTitle(res.getString(R.string.caches_filter_type_title)); + menu.add(0, 15, 0, res.getString(R.string.caches_filter_type_traditional)); + menu.add(0, 16, 0, res.getString(R.string.caches_filter_type_multi)); + menu.add(0, 17, 0, res.getString(R.string.caches_filter_type_mystery)); + menu.add(0, 18, 0, res.getString(R.string.caches_filter_type_letterbox)); + menu.add(0, 19, 0, res.getString(R.string.caches_filter_type_event)); + menu.add(0, 20, 0, res.getString(R.string.caches_filter_type_mega)); + menu.add(0, 21, 0, res.getString(R.string.caches_filter_type_earth)); + menu.add(0, 22, 0, res.getString(R.string.caches_filter_type_cito)); + menu.add(0, 23, 0, res.getString(R.string.caches_filter_type_webcam)); + menu.add(0, 24, 0, res.getString(R.string.caches_filter_type_virtual)); + menu.add(0, 25, 0, res.getString(R.string.caches_filter_type_wherigo)); + menu.add(0, 26, 0, res.getString(R.string.caches_filter_type_lostfound)); + menu.add(0, 27, 0, res.getString(R.string.caches_filter_type_ape)); + menu.add(0, 28, 0, res.getString(R.string.caches_filter_type_gchq)); + menu.add(0, 29, 0, res.getString(R.string.caches_filter_type_gps)); + } + } else{ + final cgCache cache = adapter.getItem(adapterInfo.position); + + if (cache.name != null && cache.name.length() > 0) { + menu.setHeaderTitle(cache.name); + } else { + menu.setHeaderTitle(cache.geocode); + } + + if (cache.latitude != null && cache.longitude != null) { + menu.add(0, 1, 0, res.getString(R.string.cache_menu_compass)); + menu.add(0, 2, 0, res.getString(R.string.cache_menu_radar)); + menu.add(0, 3, 0, res.getString(R.string.cache_menu_map)); + menu.add(0, 4, 0, res.getString(R.string.cache_menu_map_ext)); + menu.add(0, 5, 0, res.getString(R.string.cache_menu_tbt)); + menu.add(0, 6, 0, res.getString(R.string.cache_menu_visit)); + menu.add(0, 7, 0, res.getString(R.string.cache_menu_details)); + } + } + + ArrayList<cgList> cacheLists = app.getLists(); + int listCount = cacheLists.size(); + if (listCount > 1) { + SubMenu submenu = menu.addSubMenu(0, 8, 0, res.getString(R.string.cache_menu_move_list)); + for (int i = 0; i < listCount; i++) { + cgList list = cacheLists.get(i); + submenu.add(Menu.NONE, 100+list.id, Menu.NONE, list.title); + } + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + final int id = item.getItemId(); + ContextMenu.ContextMenuInfo info = item.getMenuInfo(); + + if (info == null) { + if(adapter != null){ + if (id == 8) { + adapter.setFilter(new cgFilterBySize(res.getString(R.string.caches_filter_size_micro))); + } else if (id == 9) { + adapter.setFilter(new cgFilterBySize(res.getString(R.string.caches_filter_size_small))); + } else if (id == 10) { + adapter.setFilter(new cgFilterBySize(res.getString(R.string.caches_filter_size_regular))); + } else if (id == 11) { + adapter.setFilter(new cgFilterBySize(res.getString(R.string.caches_filter_size_large))); + } else if (id == 12) { + adapter.setFilter(new cgFilterBySize(res.getString(R.string.caches_filter_size_other))); + } else if (id == 13) { + adapter.setFilter(new cgFilterBySize(res.getString(R.string.caches_filter_size_virtual))); + } else if (id == 14) { + adapter.setFilter(new cgFilterBySize(res.getString(R.string.caches_filter_size_notchosen))); + } else if (id == 15) { + adapter.setFilter(new cgFilterByType("traditional")); + } else if (id == 16) { + adapter.setFilter(new cgFilterByType("multi")); + } else if (id == 17) { + adapter.setFilter(new cgFilterByType("mystery")); + } else if (id == 18) { + adapter.setFilter(new cgFilterByType("letterbox")); + } else if (id == 19) { + adapter.setFilter(new cgFilterByType("event")); + } else if (id == 20) { + adapter.setFilter(new cgFilterByType("mega")); + } else if (id == 21) { + adapter.setFilter(new cgFilterByType("earth")); + } else if (id == 22) { + adapter.setFilter(new cgFilterByType("cito")); + } else if (id == 23) { + adapter.setFilter(new cgFilterByType("webcam")); + } else if (id == 24) { + adapter.setFilter(new cgFilterByType("virtual")); + } else if (id == 25) { + adapter.setFilter(new cgFilterByType("wherigo")); + } else if (id == 26) { + adapter.setFilter(new cgFilterByType("lostfound")); + } else if (id == 27) { + adapter.setFilter(new cgFilterByType("ape")); + } else if (id == 28) { + adapter.setFilter(new cgFilterByType("gchq")); + } else if (id == 29) { + adapter.setFilter(new cgFilterByType("gps")); + } else { + return false; + } + return true; + } else { + // restore menu info for sub menu items, see https://code.google.com/p/android/issues/detail?id=7139 + info = lastMenuInfo; + lastMenuInfo = null; + return false; + } + } + + AdapterContextMenuInfo adapterInfo = null; + try { + adapterInfo = (AdapterContextMenuInfo) info; + } catch (Exception e) { + Log.w(cgSettings.tag, "cgeocaches.onContextItemSelected: " + e.toString()); + } + + final int touchedPos = adapterInfo.position; + final cgCache cache = adapter.getItem(touchedPos); + + if (id == 1) { // compass + Intent navigateIntent = new Intent(activity, cgeonavigate.class); + navigateIntent.putExtra("latitude", cache.latitude); + navigateIntent.putExtra("longitude", cache.longitude); + navigateIntent.putExtra("geocode", cache.geocode.toUpperCase()); + navigateIntent.putExtra("name", cache.name); + + activity.startActivity(navigateIntent); + + return true; + } else if (id == 2) { // radar + try { + if (cgBase.isIntentAvailable(activity, "com.google.android.radar.SHOW_RADAR") == true) { + Intent radarIntent = new Intent("com.google.android.radar.SHOW_RADAR"); + radarIntent.putExtra("latitude", new Float(cache.latitude)); + radarIntent.putExtra("longitude", new Float(cache.longitude)); + activity.startActivity(radarIntent); + } else { + AlertDialog.Builder dialog = new AlertDialog.Builder(activity); + dialog.setTitle(res.getString(R.string.err_radar_title)); + dialog.setMessage(res.getString(R.string.err_radar_message)); + dialog.setCancelable(true); + dialog.setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:com.eclipsim.gpsstatus2"))); + dialog.cancel(); + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_radar_market)); + Log.e(cgSettings.tag, "cgeocaches.onContextItemSelected.radar.onClick: " + e.toString()); + } + } + }); + dialog.setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog alert = dialog.create(); + alert.show(); + } + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_radar_generic)); + Log.e(cgSettings.tag, "cgeocaches.onContextItemSelected.radar: " + e.toString()); + } + + return true; + } else if (id == 3) { // show on map + Intent mapIntent = new Intent(activity, settings.getMapFactory().getMapClass()); + mapIntent.putExtra("detail", false); + mapIntent.putExtra("geocode", cache.geocode); + + activity.startActivity(mapIntent); + + return true; + } else if (id == 4) { // show on external map + base.runExternalMap(0, activity, res, warning, tracker, cache); + + return true; + } else if (id == 5) { // turn-by-turn + if (geo != null) { + base.runNavigation(activity, res, settings, warning, tracker, cache.latitude, cache.longitude, geo.latitudeNow, geo.longitudeNow); + } else { + base.runNavigation(activity, res, settings, warning, tracker, cache.latitude, cache.longitude); + } + + return true; + } else if (id == 6) { // log visit + if (cache.cacheid == null || cache.cacheid.length() == 0) { + warning.showToast(res.getString(R.string.err_cannot_log_visit)); + return true; + } + + Intent logVisitIntent = new Intent(activity, cgeovisit.class); + logVisitIntent.putExtra("id", cache.cacheid); + logVisitIntent.putExtra("geocode", cache.geocode.toUpperCase()); + logVisitIntent.putExtra("type", cache.type.toLowerCase()); + + activity.startActivity(logVisitIntent); + + return true; + } else if (id == 7) { // cache details + Intent cachesIntent = new Intent(activity, cgeodetail.class); + cachesIntent.putExtra("geocode", cache.geocode.toUpperCase()); + cachesIntent.putExtra("name", cache.name); + activity.startActivity(cachesIntent); + + return true; + } else if (id == 8) { // move to list (sub menu) + // we must remember the menu info for the sub menu, there is a bug in Android: + // https://code.google.com/p/android/issues/detail?id=7139 + lastMenuInfo = info; + return true; + } else if (id >= 100) { // move to list + int newListId = id - 100; + app.moveToList(cache.geocode, newListId); + // refresh list by switching to the current list + switchListById(listId); + return true; + } + return false; + } + + @Override + public void onBackPressed() { + if (adapter != null) { + if (adapter.resetChecks() == true) { + return; + } else if (adapter.getSelectMode() == true) { + adapter.setSelectMode(false, true); + + return; + } + + if (adapter.isFilter()) { + adapter.clearFilter(); + + return; + } + } + + super.onBackPressed(); + + return; + } + + private void setAdapter() { + if (listFooter == null) { + if (inflater == null) { + inflater = activity.getLayoutInflater(); + } + listFooter = inflater.inflate(R.layout.caches_footer, null); + + listFooter.setClickable(true); + listFooter.setOnClickListener(new moreCachesListener()); + } + if (listFooterText == null) { + listFooterText = (TextView) listFooter.findViewById(R.id.more_caches); + } + + if (adapter == null) { + final ListView list = getListView(); + + registerForContextMenu(list); + list.setLongClickable(true); + list.addFooterView(listFooter); + + adapter = new cgCacheListAdapter(activity, settings, cacheList, base); + setListAdapter(adapter); + } else { + adapter.notifyDataSetChanged(); + } + + if (adapter != null && geo != null) { + adapter.setActualCoordinates(geo.latitudeNow, geo.longitudeNow); + } + if (adapter != null && dir != null) { + adapter.setActualHeading(dir.directionNow); + } + } + + private void setLoadingCaches() { + if (listFooter == null) { + return; + } + if (listFooterText == null) { + return; + } + + listFooterText.setText(res.getString(R.string.caches_more_caches_loading)); + listFooter.setClickable(false); + listFooter.setOnClickListener(null); + } + + private void setMoreCaches(boolean more) { + if (listFooter == null) { + return; + } + if (listFooterText == null) { + return; + } + + if (more == false) { + if (cacheList == null || cacheList.isEmpty()) { + listFooterText.setText(res.getString(R.string.caches_no_cache)); + } else { + listFooterText.setText(res.getString(R.string.caches_more_caches_no)); + } + listFooter.setClickable(false); + listFooter.setOnClickListener(null); + } else { + listFooterText.setText(res.getString(R.string.caches_more_caches)); + listFooter.setClickable(true); + listFooter.setOnClickListener(new moreCachesListener()); + } + } + + private void init() { + // sensor & geolocation manager + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + if (settings.livelist == 1 && settings.useCompass == 1 && dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + + if (cacheList != null) { + base.setTitle(activity, title); + } + + if (cacheList != null && cacheList.isEmpty() == false) { + final Integer count = app.getTotal(searchId); + if (count != null && count > 0) { + base.setTitle(activity, title); + if (cacheList.size() < app.getTotal(searchId) && cacheList.size() < 1000) { + setMoreCaches(true); + } else { + setMoreCaches(false); + } + } else { + base.setTitle(activity, title); + setMoreCaches(false); + } + } else { + base.setTitle(activity, title); + } + + setAdapter(); + + if (geo != null) { + geoUpdate.updateLoc(geo); + } + if (dir != null) { + dirUpdate.updateDir(dir); + } + } + + private void showOnMap() { + if (searchId == null || searchId == 0 || cacheList == null || cacheList.isEmpty() == true) { + warning.showToast(res.getString(R.string.warn_no_cache_coord)); + + return; + } + + Intent mapIntent = new Intent(activity, settings.getMapFactory().getMapClass()); + mapIntent.putExtra("detail", false); + mapIntent.putExtra("searchid", searchId); + + activity.startActivity(mapIntent); + } + + private void showOnLocus() { + if (cacheList == null || cacheList.isEmpty() == true) { + return; + } + + try { + final Intent intentTest = new Intent(Intent.ACTION_VIEW); + intentTest.setData(Uri.parse("menion.points:x")); + + if (cgBase.isIntentAvailable(activity, intentTest) == false) { + return; + } + + final ArrayList<cgCache> cacheListTemp = (ArrayList<cgCache>) cacheList.clone(); + final ArrayList<cgCache> cacheListCoord = new ArrayList<cgCache>(); + for (cgCache cache : cacheListTemp) { + if (cache.latitude != null && cache.longitude != null) { + cacheListCoord.add(cache); + } + } + cacheListTemp.clear(); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final DataOutputStream dos = new DataOutputStream(baos); + + dos.writeInt(1); // not used + dos.writeInt(cacheListCoord.size()); // cache and waypoints + + // cache waypoints + if (cacheListCoord != null && cacheListCoord.isEmpty() == false) { + for (cgCache cache : cacheListCoord) { + final int wpIcon = base.getIcon(true, cache.type, cache.own, cache.found, cache.disabled); + + if (wpIcon > 0) { + // load icon + Bitmap bitmap = BitmapFactory.decodeResource(res, wpIcon); + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos2); + byte[] image = baos2.toByteArray(); + + dos.writeInt(image.length); + dos.write(image); + } else { + // no icon + dos.writeInt(0); // no image + } + + // name + if (cache != null && cache.geocode != null && cache.geocode.length() > 0) { + dos.writeUTF(cache.geocode.toUpperCase()); + } else { + dos.writeUTF(""); + } + + // description + if (cache != null && cache.name != null && cache.name.length() > 0) { + dos.writeUTF(cache.name); + } else { + dos.writeUTF(""); + } + + // additional data :: keyword, button title, package, activity, data name, data content + if (cache != null && cache.geocode != null && cache.geocode.length() > 0) { + dos.writeUTF("intent;c:geo;cgeo.geocaching;cgeo.geocaching.cgeodetail;geocode;" + cache.geocode); + } else { + dos.writeUTF(""); + } + + dos.writeDouble(cache.latitude); // latitude + dos.writeDouble(cache.longitude); // longitude + } + } + + final Intent intent = new Intent(); + intent.setAction(Intent.ACTION_VIEW); + intent.setData(Uri.parse("menion.points:data")); + intent.putExtra("data", baos.toByteArray()); + + activity.startActivity(intent); + + base.sendAnal(activity, tracker, "/external/locus"); + } catch (Exception e) { + // nothing + } + } + + private void importGpx() { + final Intent intent = new Intent(activity, cgeogpxes.class); + intent.putExtra("list", listId); + activity.startActivity(intent); + + finish(); + } + + public void refreshStored() { + if (adapter != null && adapter.getChecked() > 0) { + // there are some checked caches + detailTotal = adapter.getChecked(); + } else { + // no checked caches, download everything (when already stored - refresh them) + detailTotal = cacheList.size(); + } + detailProgress = 0; + + base.showProgress(activity, false); + waitDialog = new ProgressDialog(this); + waitDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + + public void onCancel(DialogInterface arg0) { + try { + if (threadD != null) { + threadD.kill(); + } + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + if (settings.livelist == 1 && settings.useCompass == 1 && dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeocaches.onOptionsItemSelected.onCancel: " + e.toString()); + } + } + }); + + waitDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + int etaTime = (int) ((detailTotal * 25) / 60); + if (etaTime < 1) { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + res.getString(R.string.caches_eta_ltm)); + } else if (etaTime == 1) { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + etaTime + " " + res.getString(R.string.caches_eta_min)); + } else { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + etaTime + " " + res.getString(R.string.caches_eta_mins)); + } + waitDialog.setCancelable(true); + waitDialog.setMax(detailTotal); + waitDialog.show(); + + detailProgressTime = System.currentTimeMillis(); + + threadD = new geocachesLoadDetails(loadDetailsHandler, listId); + threadD.start(); + } + + public void importWeb() { + detailProgress = 0; + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + base.showProgress(activity, false); + waitDialog = new ProgressDialog(this); + waitDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + + public void onCancel(DialogInterface arg0) { + try { + if (threadW != null) { + threadW.kill(); + } + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + if (settings.livelist == 1 && settings.useCompass == 1 && dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeocaches.importWeb.onCancel: " + e.toString()); + } + } + }); + + waitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + waitDialog.setMessage(res.getString(R.string.web_import_waiting)); + waitDialog.setCancelable(true); + waitDialog.show(); + + threadW = new geocachesLoadFromWeb(downloadFromWebHandler, listId); + threadW.start(); + } + + public void dropStored() { + AlertDialog.Builder dialog = new AlertDialog.Builder(activity); + dialog.setCancelable(true); + dialog.setTitle(res.getString(R.string.caches_drop_stored)); + + if (adapter != null && adapter.getChecked() > 0) { + dialog.setMessage(res.getString(R.string.caches_drop_selected_ask)); + dialog.setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + dropSelected(); + dialog.cancel(); + } + }); + } else { + dialog.setMessage(res.getString(R.string.caches_drop_all_ask)); + dialog.setPositiveButton(getString(android.R.string.yes), new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + dropSelected(); + dialog.cancel(); + } + }); + } + dialog.setNegativeButton(getString(android.R.string.no), new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog alert = dialog.create(); + alert.show(); + } + + public void dropSelected() { + waitDialog = new ProgressDialog(this); + waitDialog.setMessage(res.getString(R.string.caches_drop_progress)); + waitDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + + public void onCancel(DialogInterface arg0) { + try { + if (threadR != null) { + threadR.kill(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeocaches.onOptionsItemSelected.onCancel: " + e.toString()); + } + } + }); + + waitDialog.setCancelable(true); + waitDialog.show(); + + threadR = new geocachesDropDetails(dropDetailsHandler); + threadR.start(); + } + + private class update extends cgUpdateLoc { + + @Override + public void updateLoc(cgGeo geo) { + if (geo == null) { + return; + } + if (adapter == null) { + return; + } + + try { + if (cacheList != null && geo.latitudeNow != null && geo.longitudeNow != null) { + adapter.setActualCoordinates(geo.latitudeNow, geo.longitudeNow); + } + + if (settings.useCompass == 0 || (geo.speedNow != null && geo.speedNow > 5)) { // use GPS when speed is higher than 18 km/h + if (settings.useCompass == 0) { + if (geo.bearingNow != null) { + adapter.setActualHeading(geo.bearingNow); + } else { + adapter.setActualHeading(new Double(0)); + } + } + if (northHeading != null) { + adapter.setActualHeading(northHeading); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to update location."); + } + } + } + + private class updateDir extends cgUpdateDir { + + @Override + public void updateDir(cgDirection dir) { + if (settings.livelist == 0) { + return; + } + if (dir == null || dir.directionNow == null) { + return; + } + + northHeading = dir.directionNow; + if (northHeading != null && adapter != null && (geo == null || geo.speedNow == null || geo.speedNow <= 5)) { // use compass when speed is lower than 18 km/h) { + adapter.setActualHeading(northHeading); + } + } + } + + private class geocachesLoadByOffline extends Thread { + + private Handler handler = null; + private Double latitude = null; + private Double longitude = null; + private int listId = 1; + + public geocachesLoadByOffline(Handler handlerIn, Double latitudeIn, Double longitudeIn, int listIdIn) { + handler = handlerIn; + latitude = latitudeIn; + longitude = longitudeIn; + listId = listIdIn; + } + + @Override + public void run() { + HashMap<String, Object> params = new HashMap<String, Object>(); + if (latitude != null && longitude != null) { + params.put("latitude", latitude); + params.put("longitude", longitude); + params.put("cachetype", settings.cacheType); + params.put("list", listId); + } + + searchId = base.searchByOffline(params); + + handler.sendMessage(new Message()); + } + } + + private class geocachesLoadByHistory extends Thread { + + private Handler handler = null; + + public geocachesLoadByHistory(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + HashMap<String, Object> params = new HashMap<String, Object>(); + if (latitude != null && longitude != null) { + params.put("cachetype", settings.cacheType); + } + + searchId = base.searchByHistory(params); + + handler.sendMessage(new Message()); + } + } + + private class geocachesLoadNextPage extends cgSearchThread { + + private Handler handler = null; + + public geocachesLoadNextPage(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + searchId = base.searchByNextPage(this, searchId, 0, settings.showCaptcha); + + handler.sendMessage(new Message()); + } + } + + private class geocachesLoadByCoords extends cgSearchThread { + + private Handler handler = null; + private Double latitude = null; + private Double longitude = null; + private String cachetype = null; + + public geocachesLoadByCoords(Handler handlerIn, Double latitudeIn, Double longitudeIn, String cachetypeIn) { + setPriority(Thread.MIN_PRIORITY); + + handler = handlerIn; + latitude = latitudeIn; + longitude = longitudeIn; + cachetype = cachetypeIn; + + if (latitude == null || longitude == null) { + warning.showToast(res.getString(R.string.warn_no_coordinates)); + + finish(); + return; + } + } + + @Override + public void run() { + HashMap<String, String> params = new HashMap<String, String>(); + params.put("latitude", String.format((Locale) null, "%.6f", latitude)); + params.put("longitude", String.format((Locale) null, "%.6f", longitude)); + params.put("cachetype", cachetype); + + searchId = base.searchByCoords(this, params, 0, settings.showCaptcha); + + handler.sendMessage(new Message()); + } + } + + private class geocachesLoadByKeyword extends cgSearchThread { + + private Handler handler = null; + private String keyword = null; + private String cachetype = null; + + public geocachesLoadByKeyword(Handler handlerIn, String keywordIn, String cachetypeIn) { + setPriority(Thread.MIN_PRIORITY); + + handler = handlerIn; + keyword = keywordIn; + cachetype = cachetypeIn; + + if (keyword == null) { + warning.showToast(res.getString(R.string.warn_no_keyword)); + + finish(); + return; + } + } + + @Override + public void run() { + HashMap<String, String> params = new HashMap<String, String>(); + params.put("keyword", keyword); + params.put("cachetype", cachetype); + + searchId = base.searchByKeyword(this, params, 0, settings.showCaptcha); + + handler.sendMessage(new Message()); + } + } + + private class geocachesLoadByUserName extends cgSearchThread { + + private Handler handler = null; + private String username = null; + private String cachetype = null; + + public geocachesLoadByUserName(Handler handlerIn, String usernameIn, String cachetypeIn) { + setPriority(Thread.MIN_PRIORITY); + + handler = handlerIn; + username = usernameIn; + cachetype = cachetypeIn; + + if (username == null || username.length() == 0) { + warning.showToast(res.getString(R.string.warn_no_username)); + + finish(); + return; + } + } + + @Override + public void run() { + HashMap<String, String> params = new HashMap<String, String>(); + params.put("username", username); + params.put("cachetype", cachetype); + + searchId = base.searchByUsername(this, params, 0, settings.showCaptcha); + + handler.sendMessage(new Message()); + } + } + + private class geocachesLoadByOwner extends cgSearchThread { + + private Handler handler = null; + private String username = null; + private String cachetype = null; + + public geocachesLoadByOwner(Handler handlerIn, String usernameIn, String cachetypeIn) { + setPriority(Thread.MIN_PRIORITY); + + handler = handlerIn; + username = usernameIn; + cachetype = cachetypeIn; + + if (username == null || username.length() == 0) { + warning.showToast(res.getString(R.string.warn_no_username)); + + finish(); + return; + } + } + + @Override + public void run() { + HashMap<String, String> params = new HashMap<String, String>(); + params.put("username", username); + params.put("cachetype", cachetype); + + searchId = base.searchByOwner(this, params, 0, settings.showCaptcha); + + handler.sendMessage(new Message()); + } + } + + private class geocachesLoadDetails extends Thread { + + private Handler handler = null; + private int reason = 1; + private volatile boolean needToStop = false; + private int checked = 0; + private long last = 0l; + + public geocachesLoadDetails(Handler handlerIn, int reasonIn) { + setPriority(Thread.MIN_PRIORITY); + + handler = handlerIn; + reason = reasonIn; + + if (adapter != null) { + checked = adapter.getChecked(); + } + } + + public void kill() { + needToStop = true; + } + + @Override + public void run() { + if (dir != null) { + dir = app.removeDir(); + } + if (geo != null) { + geo = app.removeGeo(); + } + + final ArrayList<cgCache> cacheListTemp = (ArrayList<cgCache>) cacheList.clone(); + for (cgCache cache : cacheListTemp) { + if (checked > 0 && cache.statusChecked == false) { + handler.sendEmptyMessage(0); + + yield(); + continue; + } + + try { + if (needToStop == true) { + Log.i(cgSettings.tag, "Stopped storing process."); + break; + } + + if ((System.currentTimeMillis() - last) < 1500) { + try { + int delay = 1000 + ((Double) (Math.random() * 1000)).intValue() - (int) (System.currentTimeMillis() - last); + if (delay < 0) { + delay = 500; + } + + Log.i(cgSettings.tag, "Waiting for next cache " + delay + " ms"); + sleep(delay); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeocaches.geocachesLoadDetails.sleep: " + e.toString()); + } + } + + if (needToStop == true) { + Log.i(cgSettings.tag, "Stopped storing process."); + break; + } + + detailProgress++; + base.storeCache(app, activity, cache, null, reason, handler); + + handler.sendEmptyMessage(cacheList.indexOf(cache)); + + yield(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeocaches.geocachesLoadDetails: " + e.toString()); + } + + last = System.currentTimeMillis(); + } + cacheListTemp.clear(); + + handler.sendEmptyMessage(-1); + } + } + + //tg + private class geocachesLoadFromWeb extends Thread { + + private Handler handler = null; + private int reason = 1; + private volatile boolean needToStop = false; + private int checked = 0; + private long last = 0l; + + public geocachesLoadFromWeb(Handler handlerIn, int reasonIn) { + setPriority(Thread.MIN_PRIORITY); + + handler = handlerIn; + reason = reasonIn; + } + + public void kill() { + needToStop = true; + } + + @Override + public void run() { + if (dir != null) { + dir = app.removeDir(); + } + if (geo != null) { + geo = app.removeGeo(); + } + + int delay=-1; + int times=0; + + while (times < 36) //3 minutes max + { + if (needToStop) + { + handler.sendEmptyMessage(-1); + break; + } + + //download new code + String deviceCode = settings.webDeviceCode; + if (deviceCode==null) deviceCode=""; + cgResponse responseFromWeb = base.request(false, "send2cgeo.carnero.cc", "/readCode.php", "GET", "d=" + cgBase.urlencode_rfc3986(deviceCode), 0, true); + + if ((responseFromWeb.getStatusCode() == 200)&&(responseFromWeb.getData().length()>2)) { + + String GCcode = responseFromWeb.getData(); + + delay=1; + Message mes = new Message(); + mes.what=1; + mes.obj=GCcode; + handler.sendMessage(mes); + yield(); + + base.storeCache(app, activity, null, GCcode, reason, null); + + Message mes1 = new Message(); + mes1.what=2; + mes1.obj=GCcode; + handler.sendMessage(mes1); + yield(); + } else { + delay=0; + handler.sendEmptyMessage(0); + yield(); + } + if (responseFromWeb.getStatusCode() != 200) { + needToStop = true; + settings.setWebNameCode(null, null); + handler.sendEmptyMessage(-2); + return; + } + + try { + yield(); + if (delay==0) + { + sleep(5000); //No caches 5s + times++; + } else { + sleep(500); //Cache was loaded 0.5s + times=0; + } + } catch (InterruptedException e) { + Log.e(cgSettings.tag, "cgeocaches.geocachesLoadFromWeb.sleep: " + e.toString()); + } + } + handler.sendEmptyMessage(-1); + } + } + //tg + + private class geocachesDropDetails extends Thread { + + private Handler handler = null; + private volatile boolean needToStop = false; + private int checked = 0; + + public geocachesDropDetails(Handler handlerIn) { + setPriority(Thread.MIN_PRIORITY); + + handler = handlerIn; + + if (adapter != null) { + checked = adapter.getChecked(); + } + } + + public void kill() { + needToStop = true; + } + + @Override + public void run() { + if (dir != null) { + dir = app.removeDir(); + } + if (geo != null) { + geo = app.removeGeo(); + } + + final ArrayList<cgCache> cacheListTemp = (ArrayList<cgCache>) cacheList.clone(); + for (cgCache cache : cacheListTemp) { + if (checked > 0 && cache.statusChecked == false) { + handler.sendEmptyMessage(0); + + yield(); + continue; + } + + try { + if (needToStop == true) { + Log.i(cgSettings.tag, "Stopped dropping process."); + break; + } + + app.markDropped(cache.geocode); + + handler.sendEmptyMessage(cacheList.indexOf(cache)); + + yield(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeocaches.geocachesDropDetails: " + e.toString()); + } + } + cacheListTemp.clear(); + + handler.sendEmptyMessage(-1); + } + } + + private class moreCachesListener implements View.OnClickListener { + + @Override + public void onClick(View arg0) { + base.showProgress(activity, true); + setLoadingCaches(); + listFooter.setOnClickListener(null); + + geocachesLoadNextPage thread; + thread = new geocachesLoadNextPage(loadNextPageHandler); + thread.setRecaptchaHandler(new cgSearchHandler(activity, res, thread)); + thread.start(); + } + } + + private void hideLoading() { + final ListView list = getListView(); + final RelativeLayout loading = (RelativeLayout) findViewById(R.id.loading); + + if (list.getVisibility() == View.GONE) { + list.setVisibility(View.VISIBLE); + loading.setVisibility(View.GONE); + } + } + + public void selectList(View view) { + if (type.equals("offline") == false) { + return; + } + + lists = app.getLists(); + + if (lists == null) { + return; + } + + final ArrayList<CharSequence> listsTitle = new ArrayList<CharSequence>(); + for (cgList list : lists) { + listsTitle.add(list.title); + } + + final CharSequence[] items = new CharSequence[listsTitle.size()]; + + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(res.getString(R.string.list_title)); + builder.setItems(listsTitle.toArray(items), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialogInterface, int item) { + switchListByOrder(item); + + return; + } + }); + builder.create().show(); + } + + public void switchListByOrder(int order) { + switchList(-1, order); + } + + public void switchListById(int id) { + switchList(id, -1); + } + + public void switchList(int id, int order) { + cgList list = null; + + if (id >= 0) { + list = app.getList(id); + } else if (order >= 0) { + lists = app.getLists(); + list = lists.get(order); + } else { + return; + } + + if (list == null) { + return; + } + + listId = list.id; + title = list.title; + + settings.saveLastList(listId); + + base.showProgress(activity, true); + setLoadingCaches(); + + Handler handlerMove = new Handler() { + @Override + public void handleMessage(Message msg) { + Thread threadPure = new geocachesLoadByOffline(loadCachesHandler, latitude, longitude, msg.what); + threadPure.start(); + } + }; + + (new moveCachesToList(listId, handlerMove)).start(); + } + + private class moveCachesToList extends Thread { + int listId = -1; + Handler handler = null; + + public moveCachesToList(int listIdIn, Handler handlerIn) { + listId = listIdIn; + handler = handlerIn; + } + + @Override + public void run() { + int checked = adapter.getChecked(); + if (checked > 0) { + final ArrayList<cgCache> cacheListTemp = (ArrayList<cgCache>) cacheList.clone(); + for (cgCache cache : cacheListTemp) { + if (cache.statusChecked != false) { + app.moveToList(cache.geocode, listId); + } + } + } + + handler.sendEmptyMessage(listId); + } + } + + private void createList() { + final AlertDialog.Builder alert = new AlertDialog.Builder(this); + final View view = inflater.inflate(R.layout.list_create_dialog, null); + final EditText input = (EditText) view.findViewById(R.id.text); + + alert.setTitle(R.string.list_dialog_create_title); + alert.setView(view); + alert.setPositiveButton(R.string.list_dialog_create, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + String value = input.getText().toString(); + // remove whitespaces added by autocompletion of Android keyboard + if (value != null) { + value = value.trim(); + } + + if (value != null && value.length() > 0) { + int newId = app.createList(value); + + if (newId >= 10) { + warning.showToast(res.getString(R.string.list_dialog_create_ok)); + } else { + warning.showToast(res.getString(R.string.list_dialog_create_err)); + } + } + } + }); + alert.setNegativeButton(res.getString(R.string.list_dialog_cancel), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + dialog.dismiss(); + } + }); + + alert.show(); + } + + private void removeList() { + final AlertDialog.Builder alert = new AlertDialog.Builder(this); + + alert.setTitle(R.string.list_dialog_remove_title); + alert.setMessage(R.string.list_dialog_remove_description); + alert.setPositiveButton(R.string.list_dialog_remove, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + boolean status = app.removeList(listId); + + if (status) { + warning.showToast(res.getString(R.string.list_dialog_remove_ok)); + switchListById(1); + } else { + warning.showToast(res.getString(R.string.list_dialog_remove_err)); + } + } + }); + alert.setNegativeButton(res.getString(R.string.list_dialog_cancel), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + dialog.dismiss(); + } + }); + + alert.show(); + } + + public void goMap(View view) { + showOnMap(); + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + if (type != null && type.equals("offline") == true) { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-stored", + activity, + "http://cgeo.carnero.cc/manual/"); + } else if (type != null && type.equals("history") == true) { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-history", + activity, + "http://cgeo.carnero.cc/manual/"); + } else { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-nearby", + activity, + "http://cgeo.carnero.cc/manual/"); + } + } catch (Exception e) { + // nothing + } + } +} diff --git a/src/cgeo/geocaching/cgeodate.java b/src/cgeo/geocaching/cgeodate.java new file mode 100644 index 0000000..b0d5b5b --- /dev/null +++ b/src/cgeo/geocaching/cgeodate.java @@ -0,0 +1,62 @@ +package cgeo.geocaching; + +import android.app.Activity; +import android.os.Bundle; +import android.app.Dialog; +import android.view.ViewGroup.LayoutParams; +import android.view.Window; +import android.widget.DatePicker; +import java.util.Calendar; + +public class cgeodate extends Dialog { + + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private cgLogForm parent = null; + private Calendar date = Calendar.getInstance(); + + public cgeodate(Activity contextIn, cgLogForm parentIn, Calendar dateIn) { + super(contextIn); + + // init + settings = new cgSettings(contextIn, contextIn.getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase((cgeoapplication) contextIn.getApplication(), settings, contextIn.getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(contextIn); + date = dateIn; + + parent = parentIn; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + try { + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setLayout(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); + } catch (Exception e) { + // nothing + } + + setContentView(R.layout.date); + + // google analytics + base.sendAnal(this.getContext(), "/date"); + + DatePicker picker = (DatePicker) findViewById(R.id.picker); + picker.init(date.get(Calendar.YEAR), date.get(Calendar.MONTH), date.get(Calendar.DATE), new pickerListener()); + } + + public class pickerListener implements DatePicker.OnDateChangedListener { + + @Override + public void onDateChanged(DatePicker picker, int year, int month, int day) { + if (parent != null) { + date.set(year, month, day); + + parent.setDate(date); + } + } + } +} diff --git a/src/cgeo/geocaching/cgeodetail.java b/src/cgeo/geocaching/cgeodetail.java new file mode 100644 index 0000000..a786cb3 --- /dev/null +++ b/src/cgeo/geocaching/cgeodetail.java @@ -0,0 +1,1902 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import java.util.ArrayList; +import java.util.HashMap; +import android.os.Handler; +import android.os.Message; +import android.os.Bundle; +import android.util.Log; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.ContentValues; +import android.content.Context; +import android.text.Html; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.view.ContextMenu; +import android.view.View; +import android.view.Menu; +import android.view.MenuItem; +import android.view.LayoutInflater; +import android.widget.ScrollView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.ImageView; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.util.DisplayMetrics; +import android.view.Display; +import android.view.SubMenu; +import android.view.WindowManager; +import android.widget.Button; + +import com.google.android.apps.analytics.GoogleAnalyticsTracker; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.Locale; +import java.util.Map.Entry; + +public class cgeodetail extends Activity { + public Long searchId = null; + public cgCache cache = null; + public String geocode = null; + public String name = null; + public String guid = null; + private GoogleAnalyticsTracker tracker = null; + private Resources res = null; + private Activity activity = null; + private LayoutInflater inflater = null; + private cgeoapplication app = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private cgGeo geo = null; + private cgUpdateLoc geoUpdate = new update(); + private float pixelRatio = 1; + private TextView cacheDistance = null; + private String contextMenuUser = null; + private ProgressDialog waitDialog = null; + private ProgressDialog descDialog = null; + private Spanned longDesc = null; + private Boolean longDescDisplayed = false; + private loadCache threadCache = null; + private loadLongDesc threadLongDesc = null; + private Thread storeThread = null; + private Thread refreshThread = null; + private HashMap<String, Integer> gcIcons = new HashMap<String, Integer>(); + private ProgressDialog storeDialog = null; + private ProgressDialog refreshDialog = null; + private ProgressDialog dropDialog = null; + private HashMap<Integer, String> calendars = new HashMap<Integer, String>(); + private Handler storeCacheHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + storeThread = null; + + try { + cache = app.getCache(searchId); // reload cache details + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_store_failed)); + + Log.e(cgSettings.tag, "cgeodetail.storeCacheHandler: " + e.toString()); + } + + setView(); + } + }; + + private Handler refreshCacheHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + refreshThread = null; + + try { + cache = app.getCache(searchId); // reload cache details + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_refresh_failed)); + + Log.e(cgSettings.tag, "cgeodetail.refreshCacheHandler: " + e.toString()); + } + + setView(); + } + }; + + private Handler dropCacheHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + try { + cache = app.getCache(searchId); // reload cache details + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_drop_failed)); + + Log.e(cgSettings.tag, "cgeodetail.dropCacheHandler: " + e.toString()); + } + + setView(); + } + }; + + private Handler loadCacheHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (searchId == null || searchId <= 0) { + warning.showToast(res.getString(R.string.err_dwld_details_failed)); + + finish(); + return; + } + + if (app.getError(searchId) != null) { + warning.showToast(res.getString(R.string.err_dwld_details_failed_reason) + " " + app.getError(searchId) + "."); + + finish(); + return; + } + + setView(); + + if (settings.autoLoadDesc == 1) { + try { + loadLongDesc(); + } catch (Exception e) { + // activity is not visible + } + } + + (new loadMapPreview(cache, loadMapPreviewHandler)).start(); + } + }; + + final Handler loadMapPreviewHandler = new Handler() { + @Override + public void handleMessage(Message message) { + BitmapDrawable image = (BitmapDrawable) message.obj; + ScrollView scroll = (ScrollView) findViewById(R.id.details_list_box); + ImageView view = (ImageView) findViewById(R.id.map_preview); + + if (image != null && view != null) { + view.setImageDrawable(image); + + if (scroll.getScrollY() == 0) { + scroll.scrollTo(0, (int) (80 * pixelRatio)); + } + view.setVisibility(View.VISIBLE); + } + } + }; + + private Handler loadDescriptionHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (longDesc == null && cache != null && cache.description != null) { + longDesc = Html.fromHtml(cache.description.trim(), new cgHtmlImg(activity, settings, geocode, true, cache.reason, false), null); + } + + if (longDesc != null) { + ((LinearLayout) findViewById(R.id.desc_box)).setVisibility(View.VISIBLE); + TextView descView = (TextView) findViewById(R.id.description); + if (cache.description.length() > 0) { + descView.setVisibility(View.VISIBLE); + descView.setText(longDesc, TextView.BufferType.SPANNABLE); + descView.setMovementMethod(LinkMovementMethod.getInstance()); + } + else { + descView.setVisibility(View.GONE); + } + + Button showDesc = (Button) findViewById(R.id.show_description); + showDesc.setVisibility(View.GONE); + showDesc.setOnTouchListener(null); + showDesc.setOnClickListener(null); + } else { + warning.showToast(res.getString(R.string.err_load_descr_failed)); + } + + if (descDialog != null && descDialog.isShowing()) { + descDialog.dismiss(); + } + + longDescDisplayed = true; + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.detail); + base.setTitle(activity, res.getString(R.string.cache)); + + init(); + + // get parameters + final Bundle extras = getIntent().getExtras(); + final Uri uri = getIntent().getData(); + + // try to get data from extras + if (geocode == null && extras != null) { + geocode = extras.getString("geocode"); + name = extras.getString("name"); + guid = extras.getString("guid"); + } + + // try to get data from URI + if (geocode == null && guid == null && uri != null) { + String uriHost = uri.getHost().toLowerCase(); + String uriPath = uri.getPath().toLowerCase(); + String uriQuery = uri.getQuery(); + + if (uriQuery != null) { + Log.i(cgSettings.tag, "Opening URI: " + uriHost + uriPath + "?" + uriQuery); + } else { + Log.i(cgSettings.tag, "Opening URI: " + uriHost + uriPath); + } + + if (uriHost.contains("geocaching.com") == true) { + geocode = uri.getQueryParameter("wp"); + guid = uri.getQueryParameter("guid"); + + if (geocode != null && geocode.length() > 0) { + geocode = geocode.toUpperCase(); + guid = null; + } else if (guid != null && guid.length() > 0) { + geocode = null; + guid = guid.toLowerCase(); + } else { + warning.showToast(res.getString(R.string.err_detail_open)); + finish(); + return; + } + } else if (uriHost.contains("coord.info") == true) { + if (uriPath != null && uriPath.startsWith("/gc") == true) { + geocode = uriPath.substring(1).toUpperCase(); + } else { + warning.showToast(res.getString(R.string.err_detail_open)); + finish(); + return; + } + } + } + + // google analytics + tracker = GoogleAnalyticsTracker.getInstance(); + tracker.start(cgSettings.analytics, this); + tracker.dispatch(); + if (geocode != null) { + base.sendAnal(activity, tracker, "/cache/detail#" + geocode); + } + + // no given data + if (geocode == null && guid == null) { + warning.showToast(res.getString(R.string.err_detail_cache)); + finish(); + return; + } + + app.setAction(geocode); + + try { + if (name != null && name.length() > 0) { + waitDialog = ProgressDialog.show(this, name, res.getString(R.string.cache_dialog_loading_details), true); + } else if (geocode != null && geocode.length() > 0) { + waitDialog = ProgressDialog.show(this, geocode.toUpperCase(), res.getString(R.string.cache_dialog_loading_details), true); + } else { + waitDialog = ProgressDialog.show(this, res.getString(R.string.cache), res.getString(R.string.cache_dialog_loading_details), true); + } + waitDialog.setCancelable(true); + } catch (Exception e) { + // nothing, we lost the window + } + + threadCache = new loadCache(loadCacheHandler, geocode, guid); + threadCache.start(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + setView(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + setView(); + } + + @Override + public void onDestroy() { + if (geo != null) { + geo = app.removeGeo(); + } + if (tracker != null) tracker.stop(); + + super.onDestroy(); + } + + @Override + public void onStop() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onStop(); + } + + @Override + public void onPause() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onPause(); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo info) { + super.onCreateContextMenu(menu, view, info); + final int viewId = view.getId(); + + if (viewId == R.id.author || viewId == R.id.value) { + if (viewId == R.id.author) { // Author of a log entry + contextMenuUser = ((TextView)view).getText().toString(); + } else if (viewId == R.id.value) { // The owner of the cache + if (cache.ownerReal != null && cache.ownerReal.length() > 0) { + contextMenuUser = cache.ownerReal; + } else { + contextMenuUser = cache.owner; + } + } + + menu.setHeaderTitle(res.getString(R.string.user_menu_title) + " " + contextMenuUser); + menu.add(viewId, 1, 0, res.getString(R.string.user_menu_view_hidden)); + menu.add(viewId, 2, 0, res.getString(R.string.user_menu_view_found)); + menu.add(viewId, 3, 0, res.getString(R.string.user_menu_open_browser)); + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + final int group = item.getGroupId(); + + if (group == R.id.author || group == R.id.value) { + final int id = item.getItemId(); + + if (id == 1) { + final Intent cachesIntent = new Intent(activity, cgeocaches.class); + + cachesIntent.putExtra("type", "owner"); + cachesIntent.putExtra("username", contextMenuUser); + cachesIntent.putExtra("cachetype", settings.cacheType); + + activity.startActivity(cachesIntent); + + return true; + } else if (id == 2) { + final Intent cachesIntent = new Intent(activity, cgeocaches.class); + + cachesIntent.putExtra("type", "username"); + cachesIntent.putExtra("username", contextMenuUser); + cachesIntent.putExtra("cachetype", settings.cacheType); + + activity.startActivity(cachesIntent); + + return true; + } else if (id == 3) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/profile/?u=" + URLEncoder.encode(contextMenuUser)))); + + return true; + } + } + return false; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (cache != null && cache.latitude != null && cache.longitude != null) { + menu.add(0, 2, 0, res.getString(R.string.cache_menu_compass)).setIcon(android.R.drawable.ic_menu_compass); // compass + + SubMenu subMenu = menu.addSubMenu(1, 0, 0, res.getString(R.string.cache_menu_navigate)).setIcon(android.R.drawable.ic_menu_more); + subMenu.add(0, 8, 0, res.getString(R.string.cache_menu_radar)); // radar + if (cache != null && cache.reason >= 1 && settings.storeOfflineMaps == 1) { + subMenu.add(1, 6, 0, res.getString(R.string.cache_menu_map_static)); // static maps + } + subMenu.add(0, 1, 0, res.getString(R.string.cache_menu_map)); // c:geo map + if (base.isLocus(activity)) { + subMenu.add(0, 20, 0, res.getString(R.string.cache_menu_locus)); // ext.: locus + } + if (base.isRmaps(activity)) { + subMenu.add(0, 21, 0, res.getString(R.string.cache_menu_rmaps)); // ext.: rmaps + } + subMenu.add(0, 23, 0, res.getString(R.string.cache_menu_map_ext)); // ext.: other + subMenu.add(0, 9, 0, res.getString(R.string.cache_menu_tbt)); // turn-by-turn + } + + if (cache != null && cache.hidden != null && (cache.type.equalsIgnoreCase("event") == true || cache.type.equalsIgnoreCase("mega") == true || cache.type.equalsIgnoreCase("cito") == true)) { + menu.add(1, 11, 0, res.getString(R.string.cache_menu_event)).setIcon(android.R.drawable.ic_menu_agenda); // add event to calendar + } + if (settings.isLogin() == true) { + menu.add(1, 3, 0, res.getString(R.string.cache_menu_visit)).setIcon(android.R.drawable.ic_menu_agenda); // log visit + } + + if (cache != null && cache.spoilers != null && cache.spoilers.size() > 0) { + menu.add(1, 5, 0, res.getString(R.string.cache_menu_spoilers)).setIcon(android.R.drawable.ic_menu_gallery); // spoiler images + } + + if (cache != null && cache.latitude != null && cache.longitude != null) { + menu.add(0, 10, 0, res.getString(R.string.cache_menu_around)).setIcon(android.R.drawable.ic_menu_rotate); // caches around + } + + menu.add(1, 7, 0, res.getString(R.string.cache_menu_browser)).setIcon(android.R.drawable.ic_menu_info_details); // browser + menu.add(0, 12, 0, res.getString(R.string.cache_menu_share)).setIcon(android.R.drawable.ic_menu_share); // share cache + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int menuItem = item.getItemId(); + + if (menuItem == 1) { + showOnMap(); + return true; + } else if (menuItem == 2) { + navigateTo(); + return true; + } else if (menuItem == 3) { + logVisit(); + return true; + } else if (menuItem == 5) { + showSpoilers(); + return true; + } else if (menuItem == 6) { + showSmaps(); + return true; + } else if (menuItem == 7) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/seek/cache_details.aspx?wp=" + cache.geocode))); + return true; + } else if (menuItem == 8) { + radarTo(); + return true; + } else if (menuItem == 9) { + if (geo != null) { + base.runNavigation(activity, res, settings, warning, tracker, cache.latitude, cache.longitude, geo.latitudeNow, geo.longitudeNow); + } else { + base.runNavigation(activity, res, settings, warning, tracker, cache.latitude, cache.longitude); + } + + return true; + } else if (menuItem == 10) { + cachesAround(); + return true; + } else if (menuItem == 11) { + addToCalendar(); + return true; + } else if (menuItem == 12) { + shareCache(); + return true; + } else if (menuItem == 20) { + base.runExternalMap(cgBase.mapAppLocus, activity, res, warning, tracker, cache); // locus + return true; + } else if (menuItem == 21) { + base.runExternalMap(cgBase.mapAppRmaps, activity, res, warning, tracker, cache); // rmaps + return true; + } else if (menuItem == 23) { + base.runExternalMap(cgBase.mapAppAny, activity, res, warning, tracker, cache); // rmaps + return true; + } + + return false; + } + + private void init() { + final DisplayMetrics dm = getResources().getDisplayMetrics(); + pixelRatio = dm.density; + + if (inflater == null) { + inflater = activity.getLayoutInflater(); + } + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + + if (searchId != null && searchId > 0) { + cache = app.getCache(searchId); + if (cache != null && cache.geocode != null) { + geocode = cache.geocode; + } + } + + if (geocode != null && geocode.length() > 0) { + app.setAction(geocode); + } + } + + private void setView() { + RelativeLayout itemLayout; + TextView itemName; + TextView itemValue; + + if (searchId == null) { + return; + } + + cache = app.getCache(searchId); + + if (cache == null) { + if (waitDialog != null && waitDialog.isShowing()) waitDialog.dismiss(); + + if (geocode != null && geocode.length() > 0) { + warning.showToast(res.getString(R.string.err_detail_cache_find) + " " + geocode + "."); + } else { + geocode = null; + warning.showToast(res.getString(R.string.err_detail_cache_find_some)); + } + + finish(); + return; + } + + if (cache.reason >= 1) { + base.sendAnal(activity, tracker, "/cache/detail/stored"); + } else { + base.sendAnal(activity, tracker, "/cache/detail/online"); + } + + try { + if (gcIcons == null || gcIcons.isEmpty()) { + gcIcons.put("ape", R.drawable.type_ape); + gcIcons.put("cito", R.drawable.type_cito); + gcIcons.put("earth", R.drawable.type_earth); + gcIcons.put("event", R.drawable.type_event); + gcIcons.put("letterbox", R.drawable.type_letterbox); + gcIcons.put("locationless", R.drawable.type_locationless); + gcIcons.put("mega", R.drawable.type_mega); + gcIcons.put("multi", R.drawable.type_multi); + gcIcons.put("traditional", R.drawable.type_traditional); + gcIcons.put("virtual", R.drawable.type_virtual); + gcIcons.put("webcam", R.drawable.type_webcam); + gcIcons.put("wherigo", R.drawable.type_wherigo); + gcIcons.put("gchq", R.drawable.type_hq); + gcIcons.put("mystery", R.drawable.type_mystery); + } + + if (cache.name != null && cache.name.length() > 0) { + base.setTitle(activity, cache.name); + } else { + base.setTitle(activity, geocode.toUpperCase()); + } + + inflater = activity.getLayoutInflater(); + geocode = cache.geocode.toUpperCase(); + + ScrollView scroll = (ScrollView) findViewById(R.id.details_list_box); + scroll.setVisibility(View.VISIBLE); + + LinearLayout detailsList = (LinearLayout) findViewById(R.id.details_list); + detailsList.removeAllViews(); + + // actionbar icon + if (cache.type != null && gcIcons.containsKey(cache.type) == true) { // cache icon + ((TextView) findViewById(R.id.actionbar_title)).setCompoundDrawablesWithIntrinsicBounds((Drawable) activity.getResources().getDrawable(gcIcons.get(cache.type)), null, null, null); + } else { // unknown cache type, "mystery" icon + ((TextView) findViewById(R.id.actionbar_title)).setCompoundDrawablesWithIntrinsicBounds((Drawable) activity.getResources().getDrawable(gcIcons.get("mystery")), null, null, null); + } + + // cache name (full name) + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_name)); + itemValue.setText(Html.fromHtml(cache.name).toString()); + detailsList.addView(itemLayout); + + // cache type + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_type)); + + String size = null; + if (cache.size != null && cache.size.length() > 0) { + size = " (" + cache.size + ")"; + } else { + size = ""; + } + + if (cgBase.cacheTypesInv.containsKey(cache.type) == true) { // cache icon + itemValue.setText(cgBase.cacheTypesInv.get(cache.type) + size); + } else { + itemValue.setText(cgBase.cacheTypesInv.get("mystery") + size); + } + detailsList.addView(itemLayout); + + // gc-code + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_geocode)); + itemValue.setText(cache.geocode.toUpperCase()); + detailsList.addView(itemLayout); + + // cache state + if (cache.logOffline == true || cache.archived == true || cache.disabled == true || cache.members == true || cache.found == true) { + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_status)); + + StringBuilder state = new StringBuilder(); + if (cache.logOffline == true) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_offline_log)); + } + if (cache.found == true) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_found)); + } + if (cache.archived == true) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_archived)); + } + if (cache.disabled == true) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_disabled)); + } + if (cache.members == true) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_premium)); + } + + itemValue.setText(state.toString()); + detailsList.addView(itemLayout); + + state = null; + } + + // distance + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_distance)); + if (cache.distance != null) { + itemValue.setText("~" + base.getHumanDistance(cache.distance)); + } else { + itemValue.setText("--"); + } + detailsList.addView(itemLayout); + cacheDistance = itemValue; + + // difficulty + if (cache.difficulty != null && cache.difficulty > 0) { + addStarRating(detailsList, res.getString(R.string.cache_difficulty), cache.difficulty); + } + + // terrain + if (cache.terrain != null && cache.terrain > 0) { + addStarRating(detailsList, res.getString(R.string.cache_terrain), cache.terrain); + } + + // rating + if (cache.rating != null && cache.rating > 0) { + itemLayout = addStarRating(detailsList, res.getString(R.string.cache_rating), cache.rating); + if (cache.votes != null) { + final TextView itemAddition = (TextView)itemLayout.findViewById(R.id.addition); + itemAddition.setText("(" + cache.votes + ")"); + itemAddition.setVisibility(View.VISIBLE); + } + } + + // favourite count + if (cache.favouriteCnt != null) { + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_favourite)); + itemValue.setText(String.format("%d", cache.favouriteCnt) + "×"); + detailsList.addView(itemLayout); + } + + // cache author + if ((cache.owner != null && cache.owner.length() > 0) || (cache.ownerReal != null && cache.ownerReal.length() > 0)) { + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_owner)); + if (cache.owner != null && cache.owner.length() > 0) { + itemValue.setText(Html.fromHtml(cache.owner), TextView.BufferType.SPANNABLE); + } else if (cache.ownerReal != null && cache.ownerReal.length() > 0) { + itemValue.setText(Html.fromHtml(cache.ownerReal), TextView.BufferType.SPANNABLE); + } + itemValue.setOnClickListener(new userActions()); + detailsList.addView(itemLayout); + } + + // cache hidden + if (cache.hidden != null && cache.hidden.getTime() > 0) { + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + if (cache.type != null && (cache.type.equalsIgnoreCase("event") == true || cache.type.equalsIgnoreCase("mega") == true || cache.type.equalsIgnoreCase("cito") == true)) { + itemName.setText(res.getString(R.string.cache_event)); + } else { + itemName.setText(res.getString(R.string.cache_hidden)); + } + itemValue.setText(cgBase.dateOut.format(cache.hidden)); + detailsList.addView(itemLayout); + } + + // cache location + if (cache.location != null && cache.location.length() > 0) { + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_location)); + itemValue.setText(cache.location); + detailsList.addView(itemLayout); + } + + // cache coordinates + if (cache.latitude != null && cache.longitude != null) { + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_coordinates)); + itemValue.setText(cache.latitudeString + " | " + cache.longitudeString); + detailsList.addView(itemLayout); + } + + // cache attributes + if (cache.attributes != null && cache.attributes.size() > 0) { + final LinearLayout attribBox = (LinearLayout) findViewById(R.id.attributes_box); + final TextView attribView = (TextView) findViewById(R.id.attributes); + + StringBuilder buffer = new StringBuilder(); + String attribute; + for (int i = 0; i < cache.attributes.size(); i++) { + attribute = cache.attributes.get(i); + + // dynamically search for a translation of the attribute + int id = res.getIdentifier("attribute_" + attribute, "string", base.context.getPackageName()); + if (id > 0) { + String translated = res.getString(id); + if (translated != null && translated.length() > 0) { + attribute = translated; + } + } + if (buffer.length() > 0) { + buffer.append('\n'); + } + buffer.append(attribute); + } + + attribView.setText(buffer); + attribBox.setVisibility(View.VISIBLE); + } + + // cache inventory + if (cache.inventory != null && cache.inventory.size() > 0) { + final LinearLayout inventBox = (LinearLayout) findViewById(R.id.inventory_box); + final TextView inventView = (TextView) findViewById(R.id.inventory); + + StringBuilder inventoryString = new StringBuilder(); + for (cgTrackable inventoryItem : cache.inventory) { + if (inventoryString.length() > 0) { + inventoryString.append("\n"); + } + // avoid HTML parsing where possible + if (inventoryItem.name.indexOf('<') >= 0 || inventoryItem.name.indexOf('&') >= 0 ) { + inventoryString.append(Html.fromHtml(inventoryItem.name).toString()); + } + else { + inventoryString.append(inventoryItem.name); + } + } + inventView.setText(inventoryString); + inventBox.setClickable(true); + inventBox.setOnClickListener(new selectTrackable()); + inventBox.setVisibility(View.VISIBLE); + } + + // offline use + final TextView offlineText = (TextView) findViewById(R.id.offline_text); + final Button offlineRefresh = (Button) findViewById(R.id.offline_refresh); + final Button offlineStore = (Button) findViewById(R.id.offline_store); + + if (cache.reason >= 1) { + Long diff = (System.currentTimeMillis() / (60 * 1000)) - (cache.detailedUpdate / (60 * 1000)); // minutes + + String ago = ""; + if (diff < 15) { + ago = res.getString(R.string.cache_offline_time_mins_few); + } else if (diff < 50) { + ago = res.getString(R.string.cache_offline_time_about) + " " + diff + " " + res.getString(R.string.cache_offline_time_mins); + } else if (diff < 90) { + ago = res.getString(R.string.cache_offline_time_about) + " " + res.getString(R.string.cache_offline_time_hour); + } else if (diff < (48 * 60)) { + ago = res.getString(R.string.cache_offline_time_about) + " " + (diff / 60) + " " + res.getString(R.string.cache_offline_time_hours); + } else { + ago = res.getString(R.string.cache_offline_time_about) + " " + (diff / (24 * 60)) + " " + res.getString(R.string.cache_offline_time_days); + } + + offlineText.setText(res.getString(R.string.cache_offline_stored) + "\n" + ago); + + offlineRefresh.setVisibility(View.VISIBLE); + offlineRefresh.setClickable(true); + offlineRefresh.setOnClickListener(new storeCache()); + + offlineStore.setText(res.getString(R.string.cache_offline_drop)); + offlineStore.setClickable(true); + offlineStore.setOnClickListener(new dropCache()); + } else { + offlineText.setText(res.getString(R.string.cache_offline_not_ready)); + + offlineRefresh.setVisibility(View.VISIBLE); + offlineRefresh.setClickable(true); + offlineRefresh.setOnClickListener(new refreshCache()); + + offlineStore.setText(res.getString(R.string.cache_offline_store)); + offlineStore.setClickable(true); + offlineStore.setOnClickListener(new storeCache()); + } + + // cache short desc + if (cache.shortdesc != null && cache.shortdesc.length() > 0) { + ((LinearLayout) findViewById(R.id.desc_box)).setVisibility(View.VISIBLE); + + TextView descView = (TextView) findViewById(R.id.shortdesc); + descView.setVisibility(View.VISIBLE); + descView.setText(Html.fromHtml(cache.shortdesc.trim(), new cgHtmlImg(activity, settings, geocode, true, cache.reason, false), null), TextView.BufferType.SPANNABLE); + descView.setMovementMethod(LinkMovementMethod.getInstance()); + } + + // cache long desc + if (longDescDisplayed == true) { + if (longDesc == null && cache != null && cache.description != null) { + longDesc = Html.fromHtml(cache.description.trim(), new cgHtmlImg(activity, settings, geocode, true, cache.reason, false), null); + } + + if (longDesc != null && longDesc.length() > 0) { + ((LinearLayout) findViewById(R.id.desc_box)).setVisibility(View.VISIBLE); + + TextView descView = (TextView) findViewById(R.id.description); + descView.setVisibility(View.VISIBLE); + descView.setText(longDesc, TextView.BufferType.SPANNABLE); + descView.setMovementMethod(LinkMovementMethod.getInstance()); + + Button showDesc = (Button) findViewById(R.id.show_description); + showDesc.setVisibility(View.GONE); + showDesc.setOnTouchListener(null); + showDesc.setOnClickListener(null); + } + } else if (longDescDisplayed == false && cache.description != null && cache.description.length() > 0) { + ((LinearLayout) findViewById(R.id.desc_box)).setVisibility(View.VISIBLE); + + Button showDesc = (Button) findViewById(R.id.show_description); + showDesc.setVisibility(View.VISIBLE); + showDesc.setOnClickListener(new View.OnClickListener() { + public void onClick(View arg0) { + loadLongDesc(); + } + }); + } + + // waypoints + LinearLayout waypoints = (LinearLayout) findViewById(R.id.waypoints); + waypoints.removeAllViews(); + + if (cache.waypoints != null && cache.waypoints.size() > 0) { + LinearLayout waypointView; + + // sort waypoints: PP, Sx, FI, OWN + ArrayList<cgWaypoint> sortedWaypoints = new ArrayList<cgWaypoint>(cache.waypoints); + Collections.sort(sortedWaypoints, new Comparator<cgWaypoint>() { + + @Override + public int compare(cgWaypoint wayPoint1, cgWaypoint wayPoint2) { + + return order(wayPoint1) - order(wayPoint2); + } + + private int order(cgWaypoint waypoint) { + if (waypoint.prefix == null || waypoint.prefix.length() == 0) { + return 0; + } + // check only the first character. sometimes there are inconsistencies like FI or FN for the FINAL + char firstLetter = Character.toUpperCase(waypoint.prefix.charAt(0)); + switch (firstLetter) { + case 'P' : return -100; // parking + case 'S' : { // stage N + try { + Integer stageNumber = Integer.valueOf(waypoint.prefix.substring(1)); + return stageNumber; + } catch (NumberFormatException e) { + // nothing + } + return 0; + } + case 'F' : return 1000; // final + case 'O' : return 10000; // own + } + return 0; + }}); + + for (cgWaypoint wpt : sortedWaypoints) { + waypointView = (LinearLayout) inflater.inflate(R.layout.waypoint_item, null); + final TextView identification = (TextView) waypointView.findViewById(R.id.identification); + + ((TextView) waypointView.findViewById(R.id.type)).setText(cgBase.waypointTypes.get(wpt.type)); + if (wpt.prefix.equalsIgnoreCase("OWN") == false) { + identification.setText(wpt.prefix.trim() + "/" + wpt.lookup.trim()); + } else { + identification.setText(res.getString(R.string.waypoint_custom)); + } + + if (wpt.name.trim().length() == 0) { + ((TextView) waypointView.findViewById(R.id.name)).setText(base.formatCoordinate(wpt.latitude, "lat", true) + " | " + base.formatCoordinate(wpt.longitude, "lon", true)); + } else { + // avoid HTML parsing + if (wpt.name.indexOf('<') >= 0 || wpt.name.indexOf('&') >= 0) { + ((TextView) waypointView.findViewById(R.id.name)).setText(Html.fromHtml(wpt.name.trim()), TextView.BufferType.SPANNABLE); + } + else { + ((TextView) waypointView.findViewById(R.id.name)).setText(wpt.name.trim()); + } + } + // avoid HTML parsing + if (wpt.note.indexOf('<') >= 0 || wpt.note.indexOf('&') >= 0) { + ((TextView) waypointView.findViewById(R.id.note)).setText(Html.fromHtml(wpt.note.trim()), TextView.BufferType.SPANNABLE); + } + else { + ((TextView) waypointView.findViewById(R.id.note)).setText(wpt.note.trim()); + } + + waypointView.setOnClickListener(new waypointInfo(wpt.id)); + + waypoints.addView(waypointView); + } + } + + Button addWaypoint = (Button) findViewById(R.id.add_waypoint); + addWaypoint.setClickable(true); + addWaypoint.setOnClickListener(new addWaypoint()); + + // cache hint + if (cache.hint != null && cache.hint.length() > 0) { + ((LinearLayout) findViewById(R.id.hint_box)).setVisibility(View.VISIBLE); + TextView hintView = ((TextView) findViewById(R.id.hint)); + hintView.setText(cgBase.rot13(cache.hint.trim())); + hintView.setClickable(true); + hintView.setOnClickListener(new codeHint()); + } else { + ((LinearLayout) findViewById(R.id.hint_box)).setVisibility(View.GONE); + TextView hintView = ((TextView) findViewById(R.id.hint)); + hintView.setClickable(false); + hintView.setOnClickListener(null); + } + + if (geo != null && geo.latitudeNow != null && geo.longitudeNow != null && cache != null && cache.latitude != null && cache.longitude != null) { + cacheDistance.setText(base.getHumanDistance(cgBase.getDistance(geo.latitudeNow, geo.longitudeNow, cache.latitude, cache.longitude))); + cacheDistance.bringToFront(); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeodetail.setView: " + e.toString()); + } + + if (waitDialog != null && waitDialog.isShowing()) waitDialog.dismiss(); + if (storeDialog != null && storeDialog.isShowing()) storeDialog.dismiss(); + if (dropDialog != null && dropDialog.isShowing()) dropDialog.dismiss(); + if (refreshDialog != null && refreshDialog.isShowing()) refreshDialog.dismiss(); + + displayLogs(); + + if (geo != null) geoUpdate.updateLoc(geo); + } + + private RelativeLayout addStarRating(final LinearLayout detailsList, final String name, final float value) { + RelativeLayout itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_layout, null); + TextView itemName = (TextView) itemLayout.findViewById(R.id.name); + TextView itemValue = (TextView) itemLayout.findViewById(R.id.value); + LinearLayout itemStars = (LinearLayout) itemLayout.findViewById(R.id.stars); + + itemName.setText(name); + itemValue.setText(String.format(Locale.getDefault(), "%.1f", value) + ' ' + res.getString(R.string.cache_rating_of) + " 5"); + for (int i = 0; i <= 4; i++) { + ImageView star = (ImageView) inflater.inflate(R.layout.star, null); + if ((value - i) >= 1.0) { + star.setImageResource(R.drawable.star_on); + } else if ((value - i) > 0.0) { + star.setImageResource(R.drawable.star_half); + } else { + star.setImageResource(R.drawable.star_off); + } + itemStars.addView(star, (1 + i)); + } + detailsList.addView(itemLayout); + return itemLayout; + } + + private void displayLogs() { + // cache logs + TextView textView = (TextView) findViewById(R.id.logcount); + int logCounter = 0; + if (cache != null && cache.logCounts != null) { + final StringBuffer buff = new StringBuffer(); + buff.append(res.getString(R.string.cache_log_types)); + buff.append(": "); + + // sort the log counts by type id ascending. that way the FOUND, DNF log types are the first and most visible ones + ArrayList<Entry<Integer, Integer>> sortedLogCounts = new ArrayList<Entry<Integer,Integer>>(); + sortedLogCounts.addAll(cache.logCounts.entrySet()); + Collections.sort(sortedLogCounts, new Comparator<Entry<Integer, Integer>>() { + + @Override + public int compare(Entry<Integer, Integer> logCountItem1, + Entry<Integer, Integer> logCountItem2) { + return logCountItem1.getKey().compareTo(logCountItem2.getKey()); + }}); + for (Entry<Integer, Integer> pair : sortedLogCounts) { + int logTypeId = pair.getKey().intValue(); + String logTypeLabel = cgBase.logTypes1.get(logTypeId); + // it may happen that the label is unknown -> then avoid any output for this type + if (logTypeLabel != null) { + if (logCounter > 0) { + buff.append(", "); + } + buff.append(pair.getValue().intValue()); + buff.append("× "); + buff.append(logTypeLabel); + } + logCounter ++; + } + textView.setText(buff.toString()); + } + // it may happen, that the logCounts map is available, but every log type has zero counts, + // therefore check again for the number of counted logs + if (logCounter > 0) { + textView.setVisibility(View.VISIBLE); + } else { + textView.setVisibility(View.GONE); + } + + // cache logs + LinearLayout listView = (LinearLayout) findViewById(R.id.log_list); + listView.removeAllViews(); + + RelativeLayout rowView; + + if (cache != null && cache.logs != null) { + for (cgLog log : cache.logs) { + rowView = (RelativeLayout) inflater.inflate(R.layout.log_item, null); + + if (log.date > 0) { + final Date logDate = new Date(log.date); + ((TextView) rowView.findViewById(R.id.added)).setText(cgBase.dateOutShort.format(logDate)); + } + + if (cgBase.logTypes1.containsKey(log.type) == true) { + ((TextView) rowView.findViewById(R.id.type)).setText(cgBase.logTypes1.get(log.type)); + } else { + ((TextView) rowView.findViewById(R.id.type)).setText(cgBase.logTypes1.get(4)); // note if type is unknown + } + // avoid parsing HTML if not necessary + if (log.author.indexOf('<') >= 0 || log.author.indexOf('&') >= 0) { + ((TextView) rowView.findViewById(R.id.author)).setText(Html.fromHtml(log.author), TextView.BufferType.SPANNABLE); + } + else { + ((TextView) rowView.findViewById(R.id.author)).setText(log.author); + } + + if (log.found == -1) { + ((TextView) rowView.findViewById(R.id.count)).setVisibility(View.GONE); + } else if (log.found == 0) { + ((TextView) rowView.findViewById(R.id.count)).setText(res.getString(R.string.cache_count_no)); + } else if (log.found == 1) { + ((TextView) rowView.findViewById(R.id.count)).setText(res.getString(R.string.cache_count_one)); + } else { + ((TextView) rowView.findViewById(R.id.count)).setText(log.found + " " + res.getString(R.string.cache_count_more)); + } + // avoid parsing HTML if not necessary + if (log.log.indexOf('<') >= 0 || log.log.indexOf('&') >= 0) { + ((TextView) rowView.findViewById(R.id.log)).setText(Html.fromHtml(log.log, new cgHtmlImg(activity, settings, null, false, cache.reason, false), null), TextView.BufferType.SPANNABLE); + } + else { + ((TextView) rowView.findViewById(R.id.log)).setText(log.log); + } + + final ImageView markFound = (ImageView) rowView.findViewById(R.id.found_mark); + final ImageView markDNF = (ImageView) rowView.findViewById(R.id.dnf_mark); + final ImageView markDisabled = (ImageView) rowView.findViewById(R.id.disabled_mark); + if (log.type == 2 || log.type == 9 || log.type == 10) { // found, will attend, attended + markFound.setVisibility(View.VISIBLE); + markDNF.setVisibility(View.GONE); + markDisabled.setVisibility(View.GONE); + } else if (log.type == 3) { // did not find + markFound.setVisibility(View.GONE); + markDNF.setVisibility(View.VISIBLE); + markDisabled.setVisibility(View.GONE); + } else if (log.type == 7 || log.type == 8) { // disabled, archived + markFound.setVisibility(View.GONE); + markDNF.setVisibility(View.GONE); + markDisabled.setVisibility(View.VISIBLE); + } else { + markFound.setVisibility(View.GONE); + markDNF.setVisibility(View.GONE); + markDisabled.setVisibility(View.GONE); + } + + ((TextView) rowView.findViewById(R.id.author)).setOnClickListener(new userActions()); + ((TextView) rowView.findViewById(R.id.log)).setOnClickListener(new decryptLog()); + + listView.addView(rowView); + } + + if (cache.logs.size() > 0) { + ((LinearLayout) findViewById(R.id.log_box)).setVisibility(View.VISIBLE); + } + } + } + + private class loadCache extends Thread { + + private Handler handler = null; + private String geocode = null; + private String guid = null; + + public loadCache(Handler handlerIn, String geocodeIn, String guidIn) { + handler = handlerIn; + geocode = geocodeIn; + guid = guidIn; + + if (geocode == null && guid == null) { + warning.showToast(res.getString(R.string.err_detail_cache_forgot)); + + finish(); + return; + } + } + + @Override + public void run() { + HashMap<String, String> params = new HashMap<String, String>(); + if (geocode != null && geocode.length() > 0) { + params.put("geocode", geocode); + } else if (guid != null && guid.length() > 0) { + params.put("guid", guid); + } else { + return; + } + + searchId = base.searchByGeocode(params, 0, false); + + handler.sendMessage(new Message()); + } + } + + private class loadMapPreview extends Thread { + private cgCache cache = null; + private Handler handler = null; + + public loadMapPreview(cgCache cacheIn, Handler handlerIn) { + cache = cacheIn; + handler = handlerIn; + } + + @Override + public void run() { + if (cache == null || cache.latitude == null || cache.longitude == null) { + return; + } + + BitmapDrawable image = null; + + try { + final String latlonMap = String.format((Locale) null, "%.6f", cache.latitude) + "," + String.format((Locale) null, "%.6f", cache.longitude); + final Display display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + + int width = display.getWidth(); + int height = (int) (90 * pixelRatio); + + String markerUrl = cgBase.urlencode_rfc3986("http://cgeo.carnero.cc/_markers/my_location_mdpi.png"); + + cgHtmlImg mapGetter = new cgHtmlImg(activity, settings, cache.geocode, false, 0, false); + image = mapGetter.getDrawable("http://maps.google.com/maps/api/staticmap?center=" + latlonMap + "&zoom=15&size=" + width + "x" + height + "&maptype=terrain&markers=icon%3A" + markerUrl + "%7C" + latlonMap + "&sensor=false"); + Message message = handler.obtainMessage(0, image); + handler.sendMessage(message); + } catch (Exception e) { + Log.w(cgSettings.tag, "cgeodetail.loadMapPreview.run: " + e.toString()); + } + } + } + + public void loadLongDesc() { + if (activity != null && (waitDialog == null || waitDialog.isShowing() == false)) { + descDialog = ProgressDialog.show(activity, null, res.getString(R.string.cache_dialog_loading_description), true); + descDialog.setCancelable(true); + } + + threadLongDesc = new loadLongDesc(loadDescriptionHandler); + threadLongDesc.start(); + } + + private class loadLongDesc extends Thread { + private Handler handler = null; + + public loadLongDesc(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + if (cache == null || cache.description == null || handler == null) { + return; + } + + longDesc = Html.fromHtml(cache.description.trim(), new cgHtmlImg(activity, settings, geocode, true, cache.reason, false), null); + handler.sendMessage(new Message()); + } + } + + public ArrayList<cgCoord> getCoordinates() { + cgCoord coords = null; + ArrayList<cgCoord> coordinates = new ArrayList<cgCoord>(); + + try { + // cache + coords = new cgCoord(); + coords.type = "cache"; + if (name != null && name.length() > 0) { + coords.name = name; + } else { + coords.name = geocode.toUpperCase(); + } + coords.latitude = cache.latitude; + coords.longitude = cache.longitude; + coordinates.add(coords); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeodetail.getCoordinates (cache): " + e.toString()); + } + + try { + // waypoints + for (cgWaypoint waypoint : cache.waypoints) { + if (waypoint.latitude == null || waypoint.longitude == null) { + continue; + } + + coords = new cgCoord(); + coords.type = "waypoint"; + coords.name = waypoint.name; + coords.latitude = waypoint.latitude; + coords.longitude = waypoint.longitude; + coordinates.add(coords); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeodetail.getCoordinates (waypoint): " + e.toString()); + } + + return coordinates; + } + + private void showOnMap() { + Intent mapIntent = new Intent(activity, settings.getMapFactory().getMapClass()); + mapIntent.putExtra("detail", true); + mapIntent.putExtra("searchid", searchId); + + activity.startActivity(mapIntent); + } + + private void cachesAround() { + cgeocaches cachesActivity = new cgeocaches(); + + Intent cachesIntent = new Intent(activity, cachesActivity.getClass()); + cachesIntent.putExtra("type", "coordinate"); + cachesIntent.putExtra("latitude", cache.latitude); + cachesIntent.putExtra("longitude", cache.longitude); + cachesIntent.putExtra("cachetype", settings.cacheType); + + activity.startActivity(cachesIntent); + + finish(); + } + + private void addToCalendar() { + String[] projection = new String[] { "_id", "displayName" }; + Uri calendarProvider = null; + final int sdk = new Integer(Build.VERSION.SDK).intValue(); + if (sdk >= 8) { + calendarProvider = Uri.parse("content://com.android.calendar/calendars"); + } else { + calendarProvider = Uri.parse("content://calendar/calendars"); + } + + Cursor cursor = managedQuery(calendarProvider, projection, "selected=1", null, null); + + calendars.clear(); + int cnt = 0; + if (cursor != null) { + cnt = cursor.getCount(); + + if (cnt > 0) { + cursor.moveToFirst(); + + int calId = 0; + String calIdPre = null; + String calName = null; + int calIdIn = cursor.getColumnIndex("_id"); + int calNameIn = cursor.getColumnIndex("displayName"); + + do { + calIdPre = cursor.getString(calIdIn); + if (calIdPre != null) { + calId = new Integer(calIdPre); + } + calName = cursor.getString(calNameIn); + + if (calId > 0 && calName != null) { + calendars.put(calId, calName); + } + } while (cursor.moveToNext() == true); + } + } + + final CharSequence[] items = calendars.values().toArray(new CharSequence[calendars.size()]); + + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(R.string.cache_calendars); + builder.setItems(items, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + addToCalendarFn(item); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + } + + private void addToCalendarFn(int index) { + if (calendars == null || calendars.isEmpty() == true) { + return; + } + + try { + Uri calendarProvider = null; + final int sdk = new Integer(Build.VERSION.SDK).intValue(); + if (sdk >= 8) { + calendarProvider = Uri.parse("content://com.android.calendar/events"); + } else { + calendarProvider = Uri.parse("content://calendar/events"); + } + + final Integer[] keys = calendars.keySet().toArray(new Integer[calendars.size()]); + final Integer calId = keys[index]; + + final Date eventDate = cache.hidden; + eventDate.setHours(0); + eventDate.setMinutes(0); + eventDate.setSeconds(0); + + StringBuilder description = new StringBuilder(); + description.append("http://coord.info/"); + description.append(cache.geocode.toUpperCase()); + description.append("\n\n"); + if (cache.shortdesc != null && cache.shortdesc.length() > 0) { + description.append(Html.fromHtml(cache.shortdesc).toString()); + } + + ContentValues event = new ContentValues(); + event.put("calendar_id", calId); + event.put("dtstart", eventDate.getTime() + 43200000); // noon + event.put("dtend", eventDate.getTime() + 43200000 + 3600000); // + one hour + event.put("eventTimezone", "UTC"); + event.put("title", Html.fromHtml(cache.name).toString()); + event.put("description", description.toString()); + String location = ""; + if (cache.latitudeString != null && cache.latitudeString.length() > 0 && cache.longitudeString != null && cache.longitudeString.length() > 0) { + location += cache.latitudeString + " " + cache.longitudeString; + } + if (cache.location != null && cache.location.length() > 0) { + boolean addParenteses = false; + if (location.length() > 0) { + addParenteses = true; + location += " ("; + } + + location += Html.fromHtml(cache.location).toString(); + if (addParenteses) { + location += ")"; + } + } + if (location.length() > 0) { + event.put("eventLocation", location); + } + event.put("allDay", 1); + event.put("hasAlarm", 0); + + getContentResolver().insert(calendarProvider, event); + + warning.showToast(res.getString(R.string.event_success)); + } catch (Exception e) { + warning.showToast(res.getString(R.string.event_fail)); + + Log.e(cgSettings.tag, "cgeodetail.addToCalendarFn: " + e.toString()); + } + } + + private void navigateTo() { + if (cache == null || cache.latitude == null || cache.longitude == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + cgeonavigate navigateActivity = new cgeonavigate(); + + Intent navigateIntent = new Intent(activity, navigateActivity.getClass()); + navigateIntent.putExtra("latitude", cache.latitude); + navigateIntent.putExtra("longitude", cache.longitude); + navigateIntent.putExtra("geocode", cache.geocode.toUpperCase()); + navigateIntent.putExtra("name", cache.name); + + if (cgeonavigate.coordinates != null) { + cgeonavigate.coordinates.clear(); + } + cgeonavigate.coordinates = getCoordinates(); + activity.startActivity(navigateIntent); + } + + private void radarTo() { + try { + if (cgBase.isIntentAvailable(activity, "com.google.android.radar.SHOW_RADAR") == true) { + Intent radarIntent = new Intent("com.google.android.radar.SHOW_RADAR"); + radarIntent.putExtra("latitude", new Float(cache.latitude)); + radarIntent.putExtra("longitude", new Float(cache.longitude)); + activity.startActivity(radarIntent); + } else { + AlertDialog.Builder dialog = new AlertDialog.Builder(activity); + dialog.setTitle(res.getString(R.string.err_radar_title)); + dialog.setMessage(res.getString(R.string.err_radar_message)); + dialog.setCancelable(true); + dialog.setPositiveButton("yes", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:com.eclipsim.gpsstatus2"))); + dialog.cancel(); + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_radar_market)); + Log.e(cgSettings.tag, "cgeodetail.radarTo.onClick: " + e.toString()); + } + } + }); + dialog.setNegativeButton("no", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog alert = dialog.create(); + alert.show(); + } + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_radar_generic)); + Log.e(cgSettings.tag, "cgeodetail.radarTo: " + e.toString()); + } + } + + public void shareCache() { + if (geocode == null && cache == null) { + return; + } + + final Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + + if (cache != null && cache.geocode != null) { + String subject = cache.geocode.toUpperCase(); + if (cache.name != null && cache.name.length() > 0){ + subject = subject + " - " + cache.name; + } + intent.putExtra(Intent.EXTRA_SUBJECT, "Geocache " + subject); + intent.putExtra(Intent.EXTRA_TEXT, "http://coord.info/" + cache.geocode.toUpperCase()); + } else if (geocode != null) { + intent.putExtra(Intent.EXTRA_SUBJECT, "Geocache " + geocode.toUpperCase()); + intent.putExtra(Intent.EXTRA_TEXT, "http://coord.info/" + geocode.toUpperCase()); + } + + startActivity(Intent.createChooser(intent, res.getText(R.string.action_bar_share_title))); + } + + private class waypointInfo implements View.OnClickListener { + private int id = -1; + + public waypointInfo(int idIn) { + id = idIn; + } + + public void onClick(View arg0) { + Intent waypointIntent = new Intent(activity, cgeowaypoint.class); + waypointIntent.putExtra("waypoint", id); + waypointIntent.putExtra("geocode", cache.geocode); + activity.startActivity(waypointIntent); + } + } + + private void logVisit() { + Intent logVisitIntent = new Intent(activity, cgeovisit.class); + logVisitIntent.putExtra("id", cache.cacheid); + logVisitIntent.putExtra("geocode", cache.geocode.toUpperCase()); + logVisitIntent.putExtra("type", cache.type.toLowerCase()); + logVisitIntent.putExtra("found", cache.found); + activity.startActivity(logVisitIntent); + } + + private void showSpoilers() { + if (cache == null || cache.spoilers == null || cache.spoilers.isEmpty() == true) { + warning.showToast(res.getString(R.string.err_detail_no_spoiler)); + } + + Intent spoilersIntent = new Intent(activity, cgeospoilers.class); + spoilersIntent.putExtra("geocode", geocode.toUpperCase()); + activity.startActivity(spoilersIntent); + } + + private void showSmaps() { + if (cache == null || cache.reason == 0) { + warning.showToast(res.getString(R.string.err_detail_no_map_static)); + } + + Intent smapsIntent = new Intent(activity, cgeosmaps.class); + smapsIntent.putExtra("geocode", geocode.toUpperCase()); + activity.startActivity(smapsIntent); + } + + public class codeHint implements View.OnClickListener { + public void onClick(View arg0) { + // code hint + TextView hintView = ((TextView) findViewById(R.id.hint)); + hintView.setText(cgBase.rot13(hintView.getText().toString())); + + } + } + + private class update extends cgUpdateLoc { + @Override + public void updateLoc(cgGeo geo) { + if (geo == null) { + return; + } + + try { + StringBuilder dist = new StringBuilder(); + + if (geo.latitudeNow != null && geo.longitudeNow != null && cache != null && cache.latitude != null && cache.longitude != null) { + dist.append(base.getHumanDistance(cgBase.getDistance(geo.latitudeNow, geo.longitudeNow, cache.latitude, cache.longitude))); + } + + if (cache.elevation != null) { + Double diff = null; + if (geo.altitudeNow != null) { + diff = (cache.elevation - geo.altitudeNow); + } + + if (diff != null && diff >= 0) { + dist.append(" ↗"); + if (settings.units == cgSettings.unitsImperial) { + dist.append(String.format(Locale.getDefault(), "%.0f", (Math.abs(diff) * 3.2808399))); + dist.append(" ft"); + } else { + dist.append(String.format(Locale.getDefault(), "%.0f", (Math.abs(diff)))); + dist.append(" m"); + } + } else if (diff != null && diff < 0) { + dist.append(" ↘"); + if (settings.units == cgSettings.unitsImperial) { + dist.append(String.format(Locale.getDefault(), "%.0f", (Math.abs(diff) * 3.2808399))); + dist.append(" ft"); + } else { + dist.append(String.format(Locale.getDefault(), "%.0f", (Math.abs(diff)))); + dist.append(" m"); + } + } + } + + cacheDistance.setText(dist.toString()); + cacheDistance.bringToFront(); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to update location."); + } + } + } + + private class selectTrackable implements View.OnClickListener { + public void onClick(View arg0) { + // show list of trackables + try { + Intent trackablesIntent = new Intent(activity, cgeotrackables.class); + trackablesIntent.putExtra("geocode", geocode.toUpperCase()); + activity.startActivity(trackablesIntent); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeodetail.selectTrackable: " + e.toString()); + } + } + } + + private class storeCache implements View.OnClickListener { + public void onClick(View arg0) { + if (dropDialog != null && dropDialog.isShowing() == true) { + warning.showToast(res.getString(R.string.err_detail_still_removing)); + return; + } + if (refreshDialog != null && refreshDialog.isShowing() == true) { + warning.showToast(res.getString(R.string.err_detail_still_refreshing)); + return; + } + + storeDialog = ProgressDialog.show(activity, res.getString(R.string.cache_dialog_offline_save_title), res.getString(R.string.cache_dialog_offline_save_message), true); + storeDialog.setCancelable(true); + + if (storeThread != null) { + storeThread.interrupt(); + } + + storeThread = new storeCacheThread(storeCacheHandler); + storeThread.start(); + } + } + + private class refreshCache implements View.OnClickListener { + public void onClick(View arg0) { + if (dropDialog != null && dropDialog.isShowing() == true) { + warning.showToast(res.getString(R.string.err_detail_still_removing)); + return; + } + if (storeDialog != null && storeDialog.isShowing() == true) { + warning.showToast(res.getString(R.string.err_detail_still_saving)); + return; + } + + refreshDialog = ProgressDialog.show(activity, res.getString(R.string.cache_dialog_refresh_title), res.getString(R.string.cache_dialog_refresh_message), true); + refreshDialog.setCancelable(true); + + if (refreshThread != null) { + refreshThread.interrupt(); + } + + refreshThread = new refreshCacheThread(refreshCacheHandler); + refreshThread.start(); + } + } + + private class storeCacheThread extends Thread { + private Handler handler = null; + + public storeCacheThread(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + int reason = 1; + if (cache.reason > 1) { + reason = cache.reason; + } + base.storeCache(app, activity, cache, null, reason, handler); + } + } + + private class refreshCacheThread extends Thread { + private Handler handler = null; + + public refreshCacheThread(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + app.removeCacheFromCache(geocode); + + final HashMap<String, String> params = new HashMap<String, String>(); + params.put("geocode", cache.geocode); + searchId = base.searchByGeocode(params, 0, true); + + handler.sendEmptyMessage(0); + } + } + + private class dropCache implements View.OnClickListener { + public void onClick(View arg0) { + if (storeDialog != null && storeDialog.isShowing() == true) { + warning.showToast(res.getString(R.string.err_detail_still_saving)); + return; + } + if (refreshDialog != null && refreshDialog.isShowing() == true) { + warning.showToast(res.getString(R.string.err_detail_still_refreshing)); + return; + } + + dropDialog = ProgressDialog.show(activity, res.getString(R.string.cache_dialog_offline_drop_title), res.getString(R.string.cache_dialog_offline_drop_message), true); + dropDialog.setCancelable(false); + Thread thread = new dropCacheThread(dropCacheHandler); + thread.start(); + } + } + + private class dropCacheThread extends Thread { + + private Handler handler = null; + + public dropCacheThread(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + base.dropCache(app, activity, cache, handler); + } + } + + private class addWaypoint implements View.OnClickListener { + + public void onClick(View view) { + Intent addWptIntent = new Intent(activity, cgeowaypointadd.class); + + addWptIntent.putExtra("geocode", geocode); + int wpCount = 0; + if (cache.waypoints != null) { + wpCount = cache.waypoints.size(); + } + addWptIntent.putExtra("count", wpCount); + + activity.startActivity(addWptIntent); + } + } + + private class decryptLog implements View.OnClickListener { + + public void onClick(View view) { + if (view == null) { + return; + } + + try { + final TextView logView = (TextView)view; + Spannable span = (Spannable) logView.getText(); + + // I needed to re-implement the base.rot13() encryption here because we must work on + // a SpannableStringBuilder instead of the pure text and we must replace each character inline. + // Otherwise we loose all the images, colors and so on... + SpannableStringBuilder buffer = new SpannableStringBuilder(span); + boolean plaintext = false; + + int length = span.length(); + for (int index = 0; index < length; index++) { + int c = span.charAt(index); + if (c == '[') { + plaintext = true; + } else if (c == ']') { + plaintext = false; + } else if (!plaintext) { + int capitalized = c & 32; + c &= ~capitalized; + c = ((c >= 'A') && (c <= 'Z') ? ((c - 'A' + 13) % 26 + 'A') : c) + | capitalized; + } + buffer.replace(index, index + 1, String.valueOf((char) c)); + } + logView.setText(buffer); + } catch (Exception e) { + // nothing + } + } + } + + private class userActions implements View.OnClickListener { + + public void onClick(View view) { + if (view == null) { + return; + } + + try { + registerForContextMenu(view); + openContextMenu(view); + } catch (Exception e) { + // nothing + } + } + } + + + public void goHome(View view) { + base.goHome(activity); + } + + public void goCompass(View view) { + if (cache == null || cache.latitude == null || cache.longitude == null) { + warning.showToast(res.getString(R.string.cache_coordinates_no)); + + return; + } + + Intent navigateIntent = new Intent(activity, cgeonavigate.class); + navigateIntent.putExtra("latitude", cache.latitude); + navigateIntent.putExtra("longitude", cache.longitude); + navigateIntent.putExtra("geocode", cache.geocode.toUpperCase()); + navigateIntent.putExtra("name", cache.name); + + if (cgeonavigate.coordinates != null) { + cgeonavigate.coordinates.clear(); + } + cgeonavigate.coordinates = getCoordinates(); + activity.startActivity(navigateIntent); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-cache-details", + activity, + "http://cgeo.carnero.cc/manual/" + ); + } catch (Exception e) { + // nothing + } + } +} diff --git a/src/cgeo/geocaching/cgeogpxes.java b/src/cgeo/geocaching/cgeogpxes.java new file mode 100644 index 0000000..f64100d --- /dev/null +++ b/src/cgeo/geocaching/cgeogpxes.java @@ -0,0 +1,292 @@ +package cgeo.geocaching; + +import android.app.Activity; +import android.app.ListActivity; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.View; +import java.io.File; +import java.util.ArrayList; + +public class cgeogpxes extends ListActivity { + + private ArrayList<File> files = new ArrayList<File>(); + private cgeoapplication app = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private Activity activity = null; + private cgGPXListAdapter adapter = null; + private ProgressDialog waitDialog = null; + private ProgressDialog parseDialog = null; + private Resources res = null; + private loadFiles searchingThread = null; + private boolean endSearching = false; + private int listId = 1; + private int imported = 0; + final private Handler changeWaitDialogHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + if (msg.obj != null && waitDialog != null) { + waitDialog.setMessage(res.getString(R.string.gpx_import_searching_in) + " " + (String) msg.obj); + } + } + }; + final private Handler changeParseDialogHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + if (msg.obj != null && parseDialog != null) { + parseDialog.setMessage(res.getString(R.string.gpx_import_loading_stored) + " " + (Integer) msg.obj); + } + } + }; + final private Handler loadFilesHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (files == null || files.isEmpty()) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + + warning.showToast(res.getString(R.string.gpx_import_no_files)); + + finish(); + return; + } else { + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } + + if (waitDialog != null) { + waitDialog.dismiss(); + } + } catch (Exception e) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + Log.e(cgSettings.tag, "cgeogpxes.loadFilesHandler: " + e.toString()); + } + } + }; + final private Handler loadCachesHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (parseDialog != null) { + parseDialog.dismiss(); + } + + warning.helpDialog(res.getString(R.string.gpx_import_title_caches_imported), imported + " " + res.getString(R.string.gpx_import_caches_imported)); + imported = 0; + } catch (Exception e) { + if (parseDialog != null) { + parseDialog.dismiss(); + } + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.gpx); + base.setTitle(activity, res.getString(R.string.gpx_import_title)); + + // google analytics + base.sendAnal(activity, "/gpx-import"); + + Bundle extras = getIntent().getExtras(); + if (extras != null) { + listId = extras.getInt("list"); + } + if (listId <= 0) { + listId = 1; + } + + setAdapter(); + + waitDialog = ProgressDialog.show( + this, + res.getString(R.string.gpx_import_title_searching), + res.getString(R.string.gpx_import_searching), + true, + true, + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface arg0) { + if (searchingThread != null && searchingThread.isAlive()) { + searchingThread.notifyEnd(); + } + if (files.isEmpty() == true) { + finish(); + } + } + } + ); + + endSearching = false; + searchingThread = new loadFiles(); + searchingThread.start(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + private void setAdapter() { + if (adapter == null) { + adapter = new cgGPXListAdapter(this, settings, files); + setListAdapter(adapter); + } + } + + private class loadFiles extends Thread { + public void notifyEnd() { + endSearching = true; + } + + @Override + public void run() { + ArrayList<File> list = new ArrayList<File>(); + + try { + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) == true) { + final File gpx = new File(Environment.getExternalStorageDirectory().toString() + "/gpx"); + + if (gpx.exists() && gpx.isDirectory()) { + listDir(list, gpx); + } else { + listDir(list, Environment.getExternalStorageDirectory()); + } + } else { + Log.w(cgSettings.tag, "No external media mounted."); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeogpxes.loadFiles.run: " + e.toString()); + } + + final Message msg = new Message(); + msg.obj = "loaded directories"; + changeWaitDialogHandler.sendMessage(msg); + + files.addAll(list); + list.clear(); + + loadFilesHandler.sendMessage(new Message()); + } + } + + private void listDir(ArrayList<File> list, File directory) { + if (directory == null || directory.isDirectory() == false || directory.canRead() == false) { + return; + } + + final File[] listPre = directory.listFiles(); + + if (listPre != null && listPre.length > 0) { + final int listCnt = listPre.length; + + for (int i = 0; i < listCnt; i++) { + if (endSearching == true) { + return; + } + + if (listPre[i].canRead() == true && listPre[i].isFile() == true) { + final String[] nameParts = listPre[i].getName().split("\\."); + if (nameParts.length > 1) { + final String extension = nameParts[(nameParts.length - 1)].toLowerCase(); + + if (extension.equals("gpx") == false) { + continue; + } + } else { + continue; // file has no extension + } + + list.add(listPre[i]); // add file to list + } else if (listPre[i].canRead() == true && listPre[i].isDirectory() == true) { + final Message msg = new Message(); + String name = listPre[i].getName(); + if (name.substring(0, 1).equals(".") == true) { + continue; // skip hidden directories + } + if (name.length() > 16) { + name = name.substring(0, 14) + "..."; + } + msg.obj = name; + changeWaitDialogHandler.sendMessage(msg); + + listDir(list, listPre[i]); // go deeper + } + } + } + + return; + } + + public void loadGPX(File file) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + + parseDialog = ProgressDialog.show( + activity, + res.getString(R.string.gpx_import_title_reading_file), + res.getString(R.string.gpx_import_loading), + true, + false); + + new loadCaches(file).start(); + } + + private class loadCaches extends Thread { + + File file = null; + + public loadCaches(File fileIn) { + file = fileIn; + } + + @Override + public void run() { + final long searchId = base.parseGPX(app, file, listId, changeParseDialogHandler); + + imported = app.getCount(searchId); + + loadCachesHandler.sendMessage(new Message()); + } + } + + public void goHome(View view) { + base.goHome(activity); + } +} diff --git a/src/cgeo/geocaching/cgeohelpers.java b/src/cgeo/geocaching/cgeohelpers.java new file mode 100644 index 0000000..cb38b64 --- /dev/null +++ b/src/cgeo/geocaching/cgeohelpers.java @@ -0,0 +1,107 @@ +package cgeo.geocaching; + +import android.os.Bundle; +import android.app.Activity; +import android.content.Intent; +import android.view.View; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.net.Uri; +import java.util.Locale; + +public class cgeohelpers extends Activity { + + private cgeoapplication app = null; + private Resources res = null; + private Activity activity = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private SharedPreferences prefs = null; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + prefs = getSharedPreferences(cgSettings.preferences, 0); + settings = new cgSettings(this, prefs); + base = new cgBase(app, settings, prefs); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.helpers); + base.setTitle(activity, res.getString(R.string.helpers)); + + // google analytics + base.sendAnal(activity, "/helpers"); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + public void installManual(View view) { + final Locale loc = Locale.getDefault(); + final String lng = loc.getLanguage(); + + try { + if (lng.equalsIgnoreCase("de")) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:gnu.android.app.cgeomanual.de"))); + } else { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:gnu.android.app.cgeomanual.en"))); + } + } catch (Exception e) { + // market not available in standard emulator + } + + + finish(); + } + + public void installLocus(View view) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:menion.android.locus"))); + } catch (Exception e) { + // market not available in standard emulator + } + + + finish(); + } + + public void installGpsStatus(View view) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:com.eclipsim.gpsstatus2"))); + } catch (Exception e) { + // market not available in standard emulator + } + + finish(); + } + + public void installBluetoothGps(View view) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:googoo.android.btgps"))); + } catch (Exception e) { + // market not available in standard emulator + } + + finish(); + } + + public void goHome(View view) { + base.goHome(activity); + } +} diff --git a/src/cgeo/geocaching/cgeoinit.java b/src/cgeo/geocaching/cgeoinit.java new file mode 100644 index 0000000..0be2093 --- /dev/null +++ b/src/cgeo/geocaching/cgeoinit.java @@ -0,0 +1,965 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import android.os.Bundle; +import android.app.Activity; +import android.app.ProgressDialog; +import android.view.View; +import android.widget.EditText; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.net.Uri; +import android.util.Log; +import android.os.Handler; +import android.os.Message; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.AdapterView.OnItemSelectedListener; + +import java.io.File; + +import cgeo.geocaching.cgSettings.mapSourceEnum; + +public class cgeoinit extends Activity { + + private cgeoapplication app = null; + private Resources res = null; + private Activity activity = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private SharedPreferences prefs = null; + private ProgressDialog loginDialog = null; + private ProgressDialog webDialog = null; + private Handler logInHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (loginDialog != null && loginDialog.isShowing() == true) { + loginDialog.dismiss(); + } + + if (msg.what == 1) { + warning.helpDialog(res.getString(R.string.init_login_popup), res.getString(R.string.init_login_popup_ok)); + } else { + if (cgBase.errorRetrieve.containsKey(msg.what) == true) { + warning.helpDialog(res.getString(R.string.init_login_popup), + res.getString(R.string.init_login_popup_failed_reason) + " " + cgBase.errorRetrieve.get(msg.what) + "."); + } else { + warning.helpDialog(res.getString(R.string.init_login_popup), res.getString(R.string.init_login_popup_failed)); + } + } + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_login_failed)); + + Log.e(cgSettings.tag, "cgeoinit.logInHandler: " + e.toString()); + } + + if (loginDialog != null && loginDialog.isShowing() == true) { + loginDialog.dismiss(); + } + + init(); + } + }; + + private Handler webAuthHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (webDialog != null && webDialog.isShowing() == true) { + webDialog.dismiss(); + } + + if (msg.what > 0) { + warning.helpDialog(res.getString(R.string.init_sendToCgeo), res.getString(R.string.init_sendToCgeo_register_ok).replace("####", ""+msg.what)); + } else { + warning.helpDialog(res.getString(R.string.init_sendToCgeo), res.getString(R.string.init_sendToCgeo_register_fail)); + } + } catch (Exception e) { + warning.showToast(res.getString(R.string.init_sendToCgeo_register_fail)); + + Log.e(cgSettings.tag, "cgeoinit.webHandler: " + e.toString()); + } + + if (webDialog != null && webDialog.isShowing() == true) { + webDialog.dismiss(); + } + + init(); + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + prefs = getSharedPreferences(cgSettings.preferences, 0); + settings = new cgSettings(this, prefs); + base = new cgBase(app, settings, prefs); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.init); + base.setTitle(activity, res.getString(R.string.settings)); + + // google analytics + base.sendAnal(activity, "/init"); + + init(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + init(); + } + + @Override + public void onPause() { + saveValues(); + super.onPause(); + } + + @Override + public void onStop() { + saveValues(); + super.onStop(); + } + + @Override + public void onDestroy() { + saveValues(); + + super.onDestroy(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, 0, 0, res.getString(R.string.init_clear)).setIcon(android.R.drawable.ic_menu_delete); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == 0) { + boolean status = false; + + ((EditText) findViewById(R.id.username)).setText(""); + ((EditText) findViewById(R.id.password)).setText(""); + ((EditText) findViewById(R.id.passvote)).setText(""); + + status = saveValues(); + if (status == true) { + warning.showToast(res.getString(R.string.init_cleared)); + } else { + warning.showToast(res.getString(R.string.err_init_cleared)); + } + + finish(); + } + + return false; + } + + public void init() { + + // geocaching.com settings + String usernameNow = prefs.getString("username", null); + if (usernameNow != null) { + ((EditText) findViewById(R.id.username)).setText(usernameNow); + } + String passwordNow = prefs.getString("password", null); + if (usernameNow != null) { + ((EditText) findViewById(R.id.password)).setText(passwordNow); + } + + Button logMeIn = (Button) findViewById(R.id.log_me_in); + logMeIn.setOnClickListener(new logIn()); + + TextView legalNote = (TextView) findViewById(R.id.legal_note); + legalNote.setClickable(true); + legalNote.setOnClickListener(new View.OnClickListener() { + + public void onClick(View arg0) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/about/termsofuse.aspx"))); + } + }); + + // gcvote settings + String passvoteNow = prefs.getString("pass-vote", null); + if (passvoteNow != null) { + ((EditText) findViewById(R.id.passvote)).setText(passvoteNow); + } + + // go4cache settings + TextView go4cache = (TextView) findViewById(R.id.about_go4cache); + go4cache.setClickable(true); + go4cache.setOnClickListener(new View.OnClickListener() { + + public void onClick(View arg0) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://go4cache.com/"))); + } + }); + + CheckBox publicButton = (CheckBox) findViewById(R.id.publicloc); + if (prefs.getInt("publicloc", 0) == 0) { + publicButton.setChecked(false); + } else { + publicButton.setChecked(true); + } + publicButton.setOnClickListener(new cgeoChangePublic()); + + // Twitter settings + Button authorizeTwitter = (Button) findViewById(R.id.authorize_twitter); + authorizeTwitter.setOnClickListener(new View.OnClickListener() { + + public void onClick(View arg0) { + Intent authIntent = new Intent(activity, cgeoauth.class); + activity.startActivity(authIntent); + } + }); + + CheckBox twitterButton = (CheckBox) findViewById(R.id.twitter_option); + if (prefs.getInt("twitter", 0) == 0 || settings.tokenPublic == null || settings.tokenPublic.length() == 0 || settings.tokenSecret == null || settings.tokenSecret.length() == 0) { + twitterButton.setChecked(false); + } else { + twitterButton.setChecked(true); + } + twitterButton.setOnClickListener(new cgeoChangeTwitter()); + + // Signature settings + EditText sigEdit = (EditText) findViewById(R.id.signature); + if (sigEdit.getText().length() == 0) { + sigEdit.setText(settings.getSignature()); + } + Button sigBtn = (Button) findViewById(R.id.signature_help); + sigBtn.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + warning.helpDialog(res.getString(R.string.init_signature_help_title), res.getString(R.string.init_signature_help_text)); + } + }); + + // Other settings + CheckBox skinButton = (CheckBox) findViewById(R.id.skin); + if (prefs.getInt("skin", 0) == 0) { + skinButton.setChecked(false); + } else { + skinButton.setChecked(true); + } + skinButton.setOnClickListener(new cgeoChangeSkin()); + + CheckBox addressButton = (CheckBox) findViewById(R.id.address); + if (prefs.getInt("showaddress", 1) == 0) { + addressButton.setChecked(false); + } else { + addressButton.setChecked(true); + } + addressButton.setOnClickListener(new cgeoChangeAddress()); + + CheckBox captchaButton = (CheckBox) findViewById(R.id.captcha); + if (prefs.getBoolean("showcaptcha", false) == false) { + captchaButton.setChecked(false); + } else { + captchaButton.setChecked(true); + } + captchaButton.setOnClickListener(new cgeoChangeCaptcha()); + + CheckBox useEnglishButton = (CheckBox) findViewById(R.id.useenglish); + if (prefs.getBoolean("useenglish", false) == false) { + useEnglishButton.setChecked(false); + } else { + useEnglishButton.setChecked(true); + } + useEnglishButton.setOnClickListener(new cgeoChangeUseEnglish()); + + CheckBox excludeButton = (CheckBox) findViewById(R.id.exclude); + if (prefs.getInt("excludemine", 0) == 0) { + excludeButton.setChecked(false); + } else { + excludeButton.setChecked(true); + } + excludeButton.setOnClickListener(new cgeoChangeExclude()); + + CheckBox disabledButton = (CheckBox) findViewById(R.id.disabled); + if (prefs.getInt("excludedisabled", 0) == 0) { + disabledButton.setChecked(false); + } else { + disabledButton.setChecked(true); + } + disabledButton.setOnClickListener(new cgeoChangeDisabled()); + + CheckBox offlineButton = (CheckBox) findViewById(R.id.offline); + if (prefs.getInt("offlinemaps", 1) == 0) { + offlineButton.setChecked(false); + } else { + offlineButton.setChecked(true); + } + offlineButton.setOnClickListener(new cgeoChangeOffline()); + + CheckBox autoloadButton = (CheckBox) findViewById(R.id.autoload); + if (prefs.getInt("autoloaddesc", 0) == 0) { + autoloadButton.setChecked(false); + } else { + autoloadButton.setChecked(true); + } + autoloadButton.setOnClickListener(new cgeoChangeAutoload()); + + CheckBox livelistButton = (CheckBox) findViewById(R.id.livelist); + if (prefs.getInt("livelist", 1) == 0) { + livelistButton.setChecked(false); + } else { + livelistButton.setChecked(true); + } + livelistButton.setOnClickListener(new cgeoChangeLivelist()); + + CheckBox unitsButton = (CheckBox) findViewById(R.id.units); + if (prefs.getInt("units", cgSettings.unitsMetric) == cgSettings.unitsMetric) { + unitsButton.setChecked(false); + } else { + unitsButton.setChecked(true); + } + unitsButton.setOnClickListener(new cgeoChangeUnits()); + + CheckBox gnavButton = (CheckBox) findViewById(R.id.gnav); + if (prefs.getInt("usegnav", 1) == 1) { + gnavButton.setChecked(true); + } else { + gnavButton.setChecked(false); + } + gnavButton.setOnClickListener(new cgeoChangeGNav()); + + CheckBox browserButton = (CheckBox) findViewById(R.id.browser); + if (prefs.getInt("asbrowser", 1) == 0) { + browserButton.setChecked(false); + } else { + browserButton.setChecked(true); + } + browserButton.setOnClickListener(new cgeoChangeBrowser()); + + // Altitude settings + EditText altitudeEdit = (EditText) findViewById(R.id.altitude); + altitudeEdit.setText("" + prefs.getInt("altcorrection", 0)); + + //Send2cgeo settings + String webDeviceName = prefs.getString("webDeviceName", null); + + if ((webDeviceName != null) &&(webDeviceName.length() > 0)) { + ((EditText) findViewById(R.id.webDeviceName)).setText(webDeviceName); + } else { + String s = android.os.Build.MODEL; + ((EditText) findViewById(R.id.webDeviceName)).setText(s); + } + + Button webAuth = (Button) findViewById(R.id.sendToCgeo_register); + webAuth.setOnClickListener(new webAuth()); + + /*TextView webText = (TextView) findViewById(R.id.sendToCgeo); + webText.setClickable(true); + webText.setOnClickListener(new View.OnClickListener() { + + public void onClick(View arg0) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://send2cgeo.carnero.cc/"))); + } + });*/ + + // Map source settings + Spinner mapSourceSelector = (Spinner) findViewById(R.id.mapsource); + ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( + this, R.array.map_sources, android.R.layout.simple_spinner_item); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mapSourceSelector.setAdapter(adapter); + int mapsource = prefs.getInt("mapsource", 0); + mapSourceSelector.setSelection(mapsource); + mapSourceSelector.setOnItemSelectedListener(new cgeoChangeMapSource()); + + EditText mfmapFileEdit = (EditText) findViewById(R.id.mapfile); + mfmapFileEdit.setText(prefs.getString("mfmapfile", "")); + + setMapFileEditState(); + + // Cache db backup + TextView lastBackup = (TextView) findViewById(R.id.backup_last); + File lastBackupFile = app.isRestoreFile(); + if (lastBackupFile != null) { + lastBackup.setText(res.getString(R.string.init_backup_last) + " " + cgBase.timeOut.format(lastBackupFile.lastModified()) + ", " + cgBase.dateOut.format(lastBackupFile.lastModified())); + } else { + lastBackup.setText(res.getString(R.string.init_backup_last_no)); + } + + } + + public void backup(View view) { + final String file = app.backupDatabase(); + + if (file != null) { + warning.helpDialog(res.getString(R.string.init_backup_backup), res.getString(R.string.init_backup_success) + "\n" + file); + } else { + warning.helpDialog(res.getString(R.string.init_backup_backup), res.getString(R.string.init_backup_failed)); + } + + TextView lastBackup = (TextView) findViewById(R.id.backup_last); + File lastBackupFile = app.isRestoreFile(); + if (lastBackupFile != null) { + lastBackup.setText(res.getString(R.string.init_backup_last) + " " + cgBase.timeOut.format(lastBackupFile.lastModified()) + ", " + cgBase.dateOut.format(lastBackupFile.lastModified())); + } else { + lastBackup.setText(res.getString(R.string.init_backup_last_no)); + } + } + + public void restore(View view) { + final boolean status = app.restoreDatabase(); + + if (status) { + warning.helpDialog(res.getString(R.string.init_backup_restore), res.getString(R.string.init_restore_success)); + } else { + warning.helpDialog(res.getString(R.string.init_backup_restore), res.getString(R.string.init_restore_failed)); + } + } + + private void setMapFileEditState() { + EditText mapFileEdit = (EditText) findViewById(R.id.mapfile); + if (settings.mapProvider == mapSourceEnum.mapsforgeOffline) { + mapFileEdit.setVisibility(View.VISIBLE); + } else { + mapFileEdit.setVisibility(View.INVISIBLE); + } + } + + public boolean saveValues() { + String usernameNew = ((EditText) findViewById(R.id.username)).getText().toString(); + String passwordNew = ((EditText) findViewById(R.id.password)).getText().toString(); + String passvoteNew = ((EditText) findViewById(R.id.passvote)).getText().toString(); + String signatureNew = ((EditText) findViewById(R.id.signature)).getText().toString(); + String altitudeNew = ((EditText) findViewById(R.id.altitude)).getText().toString(); + String mfmapFileNew = ((EditText) findViewById(R.id.mapfile)).getText().toString(); + + if (usernameNew == null) { + usernameNew = ""; + } + if (passwordNew == null) { + passwordNew = ""; + } + if (passvoteNew == null) { + passvoteNew = ""; + } + if (signatureNew == null) { + signatureNew = ""; + } + + int altitudeNewInt = 0; + if (altitudeNew == null) { + altitudeNewInt = 0; + } else { + altitudeNewInt = new Integer(altitudeNew); + } + + if (mfmapFileNew == null) { + mfmapFileNew = ""; + } + + final boolean status1 = settings.setLogin(usernameNew, passwordNew); + final boolean status2 = settings.setGCvoteLogin(passvoteNew); + final boolean status3 = settings.setSignature(signatureNew); + final boolean status4 = settings.setAltCorrection(altitudeNewInt); + final boolean status5 = settings.setMapFile(mfmapFileNew); + + if (status1 && status2 && status3 && status4 && status5) { + return true; + } + + return false; + } + + private class cgeoChangeTwitter implements View.OnClickListener { + + public void onClick(View arg0) { + CheckBox twitterButton = (CheckBox) findViewById(R.id.twitter_option); + + if (twitterButton.isChecked() == true) { + settings.reloadTwitterTokens(); + + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("twitter", 0) == 0) { + edit.putInt("twitter", 1); + settings.twitter = 1; + } else { + edit.putInt("twitter", 0); + settings.twitter = 0; + } + edit.commit(); + + if (settings.twitter == 1 && (settings.tokenPublic == null || settings.tokenPublic.length() == 0 || settings.tokenSecret == null || settings.tokenSecret.length() == 0)) { + Intent authIntent = new Intent(activity, cgeoauth.class); + activity.startActivity(authIntent); + } + + if (prefs.getInt("twitter", 0) == 0) { + twitterButton.setChecked(false); + } else { + twitterButton.setChecked(true); + } + } else { + SharedPreferences.Editor edit = prefs.edit(); + edit.putInt("twitter", 0); + settings.twitter = 0; + edit.commit(); + + twitterButton.setChecked(false); + } + + return; + } + } + + private class cgeoChangeSkin implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("skin", 0) == 0) { + edit.putInt("skin", 1); + settings.setSkin(1); + } else { + edit.putInt("skin", 0); + settings.setSkin(0); + } + edit.commit(); + + CheckBox skinButton = (CheckBox) findViewById(R.id.skin); + if (prefs.getInt("skin", 0) == 0) { + skinButton.setChecked(false); + } else { + skinButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangeAddress implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("showaddress", 1) == 0) { + edit.putInt("showaddress", 1); + } else { + edit.putInt("showaddress", 0); + } + edit.commit(); + + CheckBox transparentButton = (CheckBox) findViewById(R.id.address); + if (prefs.getInt("showaddress", 1) == 0) { + transparentButton.setChecked(false); + } else { + transparentButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangePublic implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("publicloc", 0) == 0) { + edit.putInt("publicloc", 1); + settings.publicLoc = 1; + } else { + edit.putInt("publicloc", 0); + settings.publicLoc = 0; + } + edit.commit(); + + CheckBox publicloc = (CheckBox) findViewById(R.id.publicloc); + if (prefs.getInt("publicloc", 0) == 0) { + publicloc.setChecked(false); + } else { + publicloc.setChecked(true); + } + + return; + } + } + + private class cgeoChangeCaptcha implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getBoolean("showcaptcha", false) == false) { + edit.putBoolean("showcaptcha", true); + settings.showCaptcha = true; + } else { + edit.putBoolean("showcaptcha", false); + settings.showCaptcha = false; + } + edit.commit(); + + CheckBox captchaButton = (CheckBox) findViewById(R.id.captcha); + if (prefs.getBoolean("showcaptcha", false) == false) { + captchaButton.setChecked(false); + } else { + captchaButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangeUseEnglish implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getBoolean("useenglish", false) == false) { + edit.putBoolean("useenglish", true); + settings.useEnglish = true; + settings.setLanguage(true); + } else { + edit.putBoolean("useenglish", false); + settings.useEnglish = false; + settings.setLanguage(false); + } + edit.commit(); + + CheckBox useEnglishButton = (CheckBox) findViewById(R.id.useenglish); + if (prefs.getBoolean("useenglish", false) == false) { + useEnglishButton.setChecked(false); + } else { + useEnglishButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangeExclude implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("excludemine", 0) == 0) { + edit.putInt("excludemine", 1); + settings.excludeMine = 1; + } else { + edit.putInt("excludemine", 0); + settings.excludeMine = 0; + } + edit.commit(); + + CheckBox excludeButton = (CheckBox) findViewById(R.id.exclude); + if (prefs.getInt("excludemine", 0) == 0) { + excludeButton.setChecked(false); + } else { + excludeButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangeDisabled implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("excludedisabled", 0) == 0) { + edit.putInt("excludedisabled", 1); + settings.excludeDisabled = 1; + } else { + edit.putInt("excludedisabled", 0); + settings.excludeDisabled = 0; + } + edit.commit(); + + CheckBox disabledButton = (CheckBox) findViewById(R.id.disabled); + if (prefs.getInt("excludedisabled", 0) == 0) { + disabledButton.setChecked(false); + } else { + disabledButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangeOffline implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("offlinemaps", 1) == 0) { + edit.putInt("offlinemaps", 1); + settings.excludeDisabled = 1; + } else { + edit.putInt("offlinemaps", 0); + settings.excludeDisabled = 0; + } + edit.commit(); + + CheckBox offlineButton = (CheckBox) findViewById(R.id.offline); + if (prefs.getInt("offlinemaps", 0) == 0) { + offlineButton.setChecked(false); + } else { + offlineButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangeLivelist implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("livelist", 1) == 0) { + edit.putInt("livelist", 1); + settings.livelist = 1; + } else { + edit.putInt("livelist", 0); + settings.livelist = 0; + } + edit.commit(); + + CheckBox livelistButton = (CheckBox) findViewById(R.id.livelist); + if (prefs.getInt("livelist", 1) == 0) { + livelistButton.setChecked(false); + } else { + livelistButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangeAutoload implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("autoloaddesc", 0) == 0) { + edit.putInt("autoloaddesc", 1); + settings.autoLoadDesc = 1; + } else { + edit.putInt("autoloaddesc", 0); + settings.autoLoadDesc = 0; + } + edit.commit(); + + CheckBox autoloadButton = (CheckBox) findViewById(R.id.autoload); + if (prefs.getInt("autoloaddesc", 0) == 0) { + autoloadButton.setChecked(false); + } else { + autoloadButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangeUnits implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("units", cgSettings.unitsMetric) == cgSettings.unitsMetric) { + edit.putInt("units", cgSettings.unitsImperial); + settings.units = cgSettings.unitsImperial; + } else { + edit.putInt("units", cgSettings.unitsMetric); + settings.units = cgSettings.unitsMetric; + } + edit.commit(); + + CheckBox unitsButton = (CheckBox) findViewById(R.id.units); + if (prefs.getInt("units", cgSettings.unitsMetric) == cgSettings.unitsMetric) { + unitsButton.setChecked(false); + } else { + unitsButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangeGNav implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("usegnav", 1) == 1) { + edit.putInt("usegnav", 0); + settings.useGNavigation = 0; + } else { + edit.putInt("usegnav", 1); + settings.useGNavigation = 1; + } + edit.commit(); + + CheckBox gnavButton = (CheckBox) findViewById(R.id.gnav); + if (prefs.getInt("usegnav", 1) == 1) { + gnavButton.setChecked(true); + } else { + gnavButton.setChecked(false); + } + + return; + } + } + + private class cgeoChangeBrowser implements View.OnClickListener { + + public void onClick(View arg0) { + SharedPreferences.Editor edit = prefs.edit(); + if (prefs.getInt("asbrowser", 1) == 0) { + edit.putInt("asbrowser", 1); + settings.asBrowser = 1; + } else { + edit.putInt("asbrowser", 0); + settings.asBrowser = 0; + } + edit.commit(); + + CheckBox browserButton = (CheckBox) findViewById(R.id.browser); + if (prefs.getInt("asbrowser", 1) == 0) { + browserButton.setChecked(false); + } else { + browserButton.setChecked(true); + } + + return; + } + } + + private class cgeoChangeMapSource implements OnItemSelectedListener { + + @Override + public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, + long arg3) { + settings.mapProvider = mapSourceEnum.fromInt(arg2); + SharedPreferences.Editor edit = prefs.edit(); + edit.putInt("mapsource", arg2); + edit.commit(); + setMapFileEditState(); + } + + @Override + public void onNothingSelected(AdapterView<?> arg0) { + arg0.setSelection(settings.mapProvider.ordinal()); + setMapFileEditState(); + } + } + + private class logIn implements View.OnClickListener { + + public void onClick(View arg0) { + final String username = ((EditText) findViewById(R.id.username)).getText().toString(); + final String password = ((EditText) findViewById(R.id.password)).getText().toString(); + + if (username == null || username.length() == 0 || password == null || password.length() == 0) { + warning.showToast(res.getString(R.string.err_missing_auth)); + return; + } + + loginDialog = ProgressDialog.show(activity, res.getString(R.string.init_login_popup), res.getString(R.string.init_login_popup_working), true); + loginDialog.setCancelable(false); + + settings.setLogin(username, password); + settings.deleteCookies(); + + (new Thread() { + + @Override + public void run() { + logInHandler.sendEmptyMessage(base.login()); + } + }).start(); + } + } + + private class webAuth implements View.OnClickListener { + + public void onClick(View arg0) { + final String deviceName = ((EditText) findViewById(R.id.webDeviceName)).getText().toString(); + final String deviceCode = prefs.getString("webDeviceCode", null); + + + if (deviceName == null || deviceName.length() == 0) { + warning.showToast(res.getString(R.string.err_missing_device_name)); + return; + } + + webDialog = ProgressDialog.show(activity, res.getString(R.string.init_sendToCgeo), res.getString(R.string.init_sendToCgeo_registering), true); + webDialog.setCancelable(false); + + (new Thread() { + + @Override + public void run() { + int pin = 0; + + String nam = deviceName==null?"":deviceName; + String cod = deviceCode==null?"":deviceCode; + + String params = "name="+cgBase.urlencode_rfc3986(nam)+"&code="+cgBase.urlencode_rfc3986(cod); + + cgResponse response = base.request(false, "send2cgeo.carnero.cc", "/authDev.php", "GET", params, 0, true); + + if (response.getStatusCode() == 200) + { + //response was OK + String[] strings = response.getData().split(","); + try { + pin=Integer.parseInt(strings[1].trim()); + } catch (Exception e) { + Log.e(cgSettings.tag, "webDialog: " + e.toString()); + } + String code = strings[0]; + settings.setWebNameCode(nam, code); + } + + webAuthHandler.sendEmptyMessage(pin); + } + }).start(); + } + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-configuration", + activity, + "http://cgeo.carnero.cc/manual/" + ); + } catch (Exception e) { + // nothing + } + } +} diff --git a/src/cgeo/geocaching/cgeonavigate.java b/src/cgeo/geocaching/cgeonavigate.java new file mode 100644 index 0000000..65afa17 --- /dev/null +++ b/src/cgeo/geocaching/cgeonavigate.java @@ -0,0 +1,503 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import java.util.ArrayList; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.PowerManager; +import android.util.Log; +import android.app.Activity; +import android.view.Menu; +import android.view.SubMenu; +import android.view.MenuItem; +import android.widget.TextView; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.view.View; +import android.view.WindowManager; +import java.util.HashMap; +import java.util.Locale; + + +public class cgeonavigate extends Activity { + + public static ArrayList<cgCoord> coordinates = new ArrayList<cgCoord>(); + private Resources res = null; + private cgeoapplication app = null; + private Activity activity = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private PowerManager pm = null; + private cgGeo geo = null; + private cgDirection dir = null; + private cgUpdateLoc geoUpdate = new update(); + private cgUpdateDir dirUpdate = new updateDir(); + private Double dstLatitude = null; + private Double dstLongitude = null; + private Double cacheHeading = new Double(0); + private Double northHeading = new Double(0); + private String title = null; + private String name = null; + private TextView navType = null; + private TextView navAccuracy = null; + private TextView navSatellites = null; + private TextView navLocation = null; + private TextView distanceView = null; + private TextView headingView = null; + private cgCompass compassView = null; + private updaterThread updater = null; + private Handler updaterHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (compassView != null) { + compassView.updateNorth(northHeading, cacheHeading); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeonavigate.updaterHandler: " + e.toString()); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // class init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.navigate); + base.setTitle(activity, res.getString(R.string.compass_title)); + + // google analytics + base.sendAnal(activity, "/navigate"); + + // sensor & geolocation manager + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + if (settings.useCompass == 1 && dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + + // get parameters + Bundle extras = getIntent().getExtras(); + if (extras != null) { + title = extras.getString("geocode"); + name = extras.getString("name"); + dstLatitude = extras.getDouble("latitude"); + dstLongitude = extras.getDouble("longitude"); + + if (name != null && name.length() > 0) { + if (title != null && title.length() > 0) { + title = title + ": " + name; + } else { + title = name; + } + } + } else { + Intent pointIntent = new Intent(activity, cgeopoint.class); + activity.startActivity(pointIntent); + + finish(); + return; + } + + if (title != null && title.length() > 0) { + app.setAction(title); + } else if (name != null && name.length() > 0) { + app.setAction(name); + } + + // set header + setTitle(); + setDestCoords(); + + // get textviews once + compassView = (cgCompass) findViewById(R.id.rose); + + // start updater thread + updater = new updaterThread(updaterHandler); + updater.start(); + + if (geo != null) { + geoUpdate.updateLoc(geo); + } + if (dir != null) { + dirUpdate.updateDir(dir); + } + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + + if (title != null && title.length() > 0) { + app.setAction(title); + } else if (name != null && name.length() > 0) { + app.setAction(name); + } + + // sensor & geolocation manager + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + if (settings.useCompass == 1 && dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + + // keep backlight on + if (pm == null) { + pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + } + + // updater thread + if (updater == null) { + updater = new updaterThread(updaterHandler); + updater.start(); + } + } + + @Override + public void onStop() { + if (geo != null) { + geo = app.removeGeo(); + } + if (dir != null) { + dir = app.removeDir(); + } + + super.onStop(); + } + + @Override + public void onPause() { + if (geo != null) { + geo = app.removeGeo(); + } + if (dir != null) { + dir = app.removeDir(); + } + + super.onPause(); + } + + @Override + public void onDestroy() { + if (geo != null) { + geo = app.removeGeo(); + } + if (dir != null) { + dir = app.removeDir(); + } + + compassView.destroyDrawingCache(); + compassView = null; + + super.onDestroy(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (settings.useCompass == 1) { + menu.add(0, 1, 0, res.getString(R.string.use_gps)).setIcon(android.R.drawable.ic_menu_compass); + } else { + menu.add(0, 1, 0, res.getString(R.string.use_compass)).setIcon(android.R.drawable.ic_menu_compass); + } + menu.add(0, 0, 0, res.getString(R.string.caches_on_map)).setIcon(android.R.drawable.ic_menu_mapmode); + menu.add(0, 2, 0, res.getString(R.string.destination_set)).setIcon(android.R.drawable.ic_menu_edit); + if (coordinates != null && coordinates.size() > 1) { + SubMenu subMenu = menu.addSubMenu(0, 3, 0, res.getString(R.string.destination_select)).setIcon(android.R.drawable.ic_menu_myplaces); + + int cnt = 4; + for (cgCoord coordinate : coordinates) { + subMenu.add(0, cnt, 0, coordinate.name + " (" + coordinate.type + ")"); + cnt++; + } + + return true; + } else { + menu.add(0, 3, 0, res.getString(R.string.destination_select)).setIcon(android.R.drawable.ic_menu_myplaces).setEnabled(false); + + return true; + } + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + MenuItem item; + item = menu.findItem(1); + if (settings.useCompass == 1) { + item.setTitle(res.getString(R.string.use_gps)); + } else { + item.setTitle(res.getString(R.string.use_compass)); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + + if (id == 0) { + Intent mapIntent = new Intent(activity, settings.getMapFactory().getMapClass()); + mapIntent.putExtra("detail", false); + mapIntent.putExtra("latitude", dstLatitude); + mapIntent.putExtra("longitude", dstLongitude); + + activity.startActivity(mapIntent); + } else if (id == 1) { + if (settings.useCompass == 1) { + settings.useCompass = 0; + + if (dir != null) { + dir = app.removeDir(); + } + + SharedPreferences.Editor prefsEdit = getSharedPreferences(cgSettings.preferences, 0).edit(); + prefsEdit.putInt("usecompass", settings.useCompass); + prefsEdit.commit(); + } else { + settings.useCompass = 1; + + if (dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + + SharedPreferences.Editor prefsEdit = getSharedPreferences(cgSettings.preferences, 0).edit(); + prefsEdit.putInt("usecompass", settings.useCompass); + prefsEdit.commit(); + } + } else if (id == 2) { + Intent pointIntent = new Intent(activity, cgeopoint.class); + activity.startActivity(pointIntent); + + finish(); + return true; + } else if (id > 3 && coordinates != null && coordinates.get(id - 4) != null) { + cgCoord coordinate = coordinates.get(id - 4); + + title = coordinate.name; + dstLatitude = coordinate.latitude; + dstLongitude = coordinate.longitude; + setTitle(); + setDestCoords(); + updateDistanceInfo(); + + Log.d(cgSettings.tag, "destination set: " + title + " (" + String.format(Locale.getDefault(), "%.8f", dstLatitude) + " | " + String.format(Locale.getDefault(), "%.8f", dstLatitude) + ")"); + return true; + } + + return false; + } + + private void setTitle() { + if (title != null && title.length() > 0) { + base.setTitle(activity, title); + } else { + base.setTitle(activity, res.getString(R.string.navigation)); + } + } + + private void setDestCoords() { + if (dstLatitude == null || dstLatitude == null) { + return; + } + + ((TextView) findViewById(R.id.destination)).setText(base.formatCoordinate(dstLatitude, "lat", true) + " | " + base.formatCoordinate(dstLongitude, "lon", true)); + } + + public void setDest(Double lat, Double lon) { + if (lat == null || lon == null) { + return; + } + + title = "some place"; + setTitle(); + setDestCoords(); + + dstLatitude = lat; + dstLongitude = lon; + updateDistanceInfo(); + } + + public HashMap<String, Double> getCoordinatesNow() { + HashMap<String, Double> coordsNow = new HashMap<String, Double>(); + if (geo != null) { + coordsNow.put("latitude", geo.latitudeNow); + coordsNow.put("longitude", geo.longitudeNow); + } + return coordsNow; + } + + private void updateDistanceInfo() { + if (geo == null || geo.latitudeNow == null || geo.longitudeNow == null || dstLatitude == null || dstLongitude == null) { + return; + } + + if (distanceView == null) { + distanceView = (TextView) findViewById(R.id.distance); + } + if (headingView == null) { + headingView = (TextView) findViewById(R.id.heading); + } + + cacheHeading = cgBase.getHeading(geo.latitudeNow, geo.longitudeNow, dstLatitude, dstLongitude); + distanceView.setText(base.getHumanDistance(cgBase.getDistance(geo.latitudeNow, geo.longitudeNow, dstLatitude, dstLongitude))); + headingView.setText(String.format(Locale.getDefault(), "%.0f", cacheHeading) + "°"); + } + + private class update extends cgUpdateLoc { + + @Override + public void updateLoc(cgGeo geo) { + if (geo == null) { + return; + } + + try { + if (navType == null || navLocation == null || navAccuracy == null) { + navType = (TextView) findViewById(R.id.nav_type); + navAccuracy = (TextView) findViewById(R.id.nav_accuracy); + navSatellites = (TextView) findViewById(R.id.nav_satellites); + navLocation = (TextView) findViewById(R.id.nav_location); + } + + if (geo.latitudeNow != null && geo.longitudeNow != null) { + String satellites = null; + if (geo.satellitesVisible != null && geo.satellitesFixed != null && geo.satellitesFixed > 0) { + satellites = res.getString(R.string.loc_sat) + ": " + geo.satellitesFixed + "/" + geo.satellitesVisible; + } else if (geo.satellitesVisible != null) { + satellites = res.getString(R.string.loc_sat) + ": 0/" + geo.satellitesVisible; + } else { + satellites = ""; + } + navSatellites.setText(satellites); + + if (geo.gps == -1) { + navType.setText(res.getString(R.string.loc_last)); + } else if (geo.gps == 0) { + navType.setText(res.getString(R.string.loc_net)); + } else { + navType.setText(res.getString(R.string.loc_gps)); + } + + if (geo.accuracyNow != null) { + if (settings.units == cgSettings.unitsImperial) { + navAccuracy.setText("±" + String.format(Locale.getDefault(), "%.0f", (geo.accuracyNow * 3.2808399)) + " ft"); + } else { + navAccuracy.setText("±" + String.format(Locale.getDefault(), "%.0f", geo.accuracyNow) + " m"); + } + } else { + navAccuracy.setText(null); + } + + if (geo.altitudeNow != null) { + String humanAlt; + if (settings.units == cgSettings.unitsImperial) { + humanAlt = String.format("%.0f", (geo.altitudeNow * 3.2808399)) + " ft"; + } else { + humanAlt = String.format("%.0f", geo.altitudeNow) + " m"; + } + navLocation.setText(base.formatCoordinate(geo.latitudeNow, "lat", true) + " | " + base.formatCoordinate(geo.longitudeNow, "lon", true) + " | " + humanAlt); + } else { + navLocation.setText(base.formatCoordinate(geo.latitudeNow, "lat", true) + " | " + base.formatCoordinate(geo.longitudeNow, "lon", true)); + } + + updateDistanceInfo(); + } else { + navType.setText(null); + navAccuracy.setText(null); + navLocation.setText(res.getString(R.string.loc_trying)); + } + + if (settings.useCompass == 0 || (geo.speedNow != null && geo.speedNow > 5)) { // use GPS when speed is higher than 18 km/h + if (geo != null && geo.bearingNow != null) { + northHeading = geo.bearingNow; + } else { + northHeading = new Double(0); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to update location."); + } + } + } + + private class updateDir extends cgUpdateDir { + + @Override + public void updateDir(cgDirection dir) { + if (dir == null || dir.directionNow == null) { + return; + } + + if (geo == null || geo.speedNow == null || geo.speedNow <= 5) { // use compass when speed is lower than 18 km/h + northHeading = dir.directionNow; + } + } + } + + private class updaterThread extends Thread { + + private Handler handler = null; + + public updaterThread(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + if (handler != null) { + handler.sendMessage(new Message()); + } + + try { + Thread.sleep(20); + } catch (Exception e) { + Thread.currentThread().interrupt(); + } + } + } + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-compass", + activity, + "http://cgeo.carnero.cc/manual/"); + } catch (Exception e) { + // nothing + } + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgeopoint.java b/src/cgeo/geocaching/cgeopoint.java new file mode 100644 index 0000000..142bf53 --- /dev/null +++ b/src/cgeo/geocaching/cgeopoint.java @@ -0,0 +1,535 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.net.Uri; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +import com.google.android.apps.analytics.GoogleAnalyticsTracker; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class cgeopoint extends Activity { + + private Resources res = null; + private cgeoapplication app = null; + private cgSettings settings = null; + private SharedPreferences prefs = null; + private cgBase base = null; + private cgWarning warning = null; + private Activity activity = null; + private GoogleAnalyticsTracker tracker = null; + private cgGeo geo = null; + private cgUpdateLoc geoUpdate = new update(); + private EditText latEdit = null; + private EditText lonEdit = null; + private boolean changed = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + app = (cgeoapplication) this.getApplication(); + res = this.getResources(); + settings = new cgSettings(activity, activity.getSharedPreferences(cgSettings.preferences, 0)); + prefs = getSharedPreferences(cgSettings.preferences, 0); + base = new cgBase(app, settings, activity.getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(activity); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.point); + base.setTitle(activity, res.getString(R.string.search_destination)); + + // google analytics + tracker = GoogleAnalyticsTracker.getInstance(); + tracker.start(cgSettings.analytics, this); + tracker.dispatch(); + base.sendAnal(activity, tracker, "/point"); + + init(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + init(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + init(); + } + + @Override + public void onDestroy() { + if (geo != null) { + geo = app.removeGeo(); + } + if (tracker != null) { + tracker.stop(); + } + + super.onDestroy(); + } + + @Override + public void onStop() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onStop(); + } + + @Override + public void onPause() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onPause(); + } + + private void init() { + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + + EditText latitudeEdit = (EditText) findViewById(R.id.latitude); + latitudeEdit.setOnKeyListener(new View.OnKeyListener() { + + public boolean onKey(View v, int i, KeyEvent k) { + changed = true; + + return false; + } + }); + + EditText longitudeEdit = (EditText) findViewById(R.id.longitude); + longitudeEdit.setOnKeyListener(new View.OnKeyListener() { + + public boolean onKey(View v, int i, KeyEvent k) { + changed = true; + + return false; + } + }); + + if (prefs.contains("anylatitude") == true && prefs.contains("anylongitude") == true) { + latitudeEdit.setText(base.formatCoordinate(new Double(prefs.getFloat("anylatitude", 0f)), "lat", true)); + longitudeEdit.setText(base.formatCoordinate(new Double(prefs.getFloat("anylongitude", 0f)), "lon", true)); + } + + Button buttonCurrent = (Button) findViewById(R.id.current); + buttonCurrent.setOnClickListener(new currentListener()); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, 2, 0, res.getString(R.string.cache_menu_compass)).setIcon(android.R.drawable.ic_menu_compass); // compass + + SubMenu subMenu = menu.addSubMenu(1, 0, 0, res.getString(R.string.cache_menu_navigate)).setIcon(android.R.drawable.ic_menu_more); + subMenu.add(0, 3, 0, res.getString(R.string.cache_menu_radar)); // radar + subMenu.add(0, 1, 0, res.getString(R.string.cache_menu_map)); // c:geo map + if (base.isLocus(activity)) { + subMenu.add(0, 20, 0, res.getString(R.string.cache_menu_locus)); // ext.: locus + } + if (base.isRmaps(activity)) { + subMenu.add(0, 21, 0, res.getString(R.string.cache_menu_rmaps)); // ext.: rmaps + } + subMenu.add(0, 23, 0, res.getString(R.string.cache_menu_map_ext)); // ext.: other + subMenu.add(0, 4, 0, res.getString(R.string.cache_menu_tbt)); // turn-by-turn + + menu.add(0, 5, 0, res.getString(R.string.cache_menu_around)).setIcon(android.R.drawable.ic_menu_rotate); // caches around + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + try { + ArrayList<Double> coords = getDestination(); + + if (coords != null && coords.get(0) != null && coords.get(1) != null) { + menu.findItem(0).setVisible(true); + menu.findItem(2).setVisible(true); + menu.findItem(5).setVisible(true); + } else { + menu.findItem(0).setVisible(false); + menu.findItem(2).setVisible(false); + menu.findItem(5).setVisible(false); + } + } catch (Exception e) { + // nothing + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int menuItem = item.getItemId(); + + ArrayList<Double> coords = getDestination(); + + if (menuItem == 1) { + showOnMap(); + return true; + } else if (menuItem == 2) { + navigateTo(); + return true; + } else if (menuItem == 3) { + radarTo(); + return true; + } else if (menuItem == 4) { + if (geo != null) { + base.runNavigation(activity, res, settings, warning, tracker, coords.get(0), coords.get(1), geo.latitudeNow, geo.longitudeNow); + } else { + base.runNavigation(activity, res, settings, warning, tracker, coords.get(0), coords.get(1)); + } + + return true; + } else if (menuItem == 5) { + cachesAround(); + return true; + } else if (menuItem == 20) { + base.runExternalMap(cgBase.mapAppLocus, activity, res, warning, tracker, coords.get(0), coords.get(1)); // locus + return true; + } else if (menuItem == 21) { + base.runExternalMap(cgBase.mapAppRmaps, activity, res, warning, tracker, coords.get(0), coords.get(1)); // rmaps + return true; + } else if (menuItem == 23) { + base.runExternalMap(cgBase.mapAppAny, activity, res, warning, tracker, coords.get(0), coords.get(1)); // rmaps + return true; + } + + return false; + } + + private void showOnMap() { + ArrayList<Double> coords = getDestination(); + + if (coords == null || coords.get(0) == null || coords.get(1) == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + Intent mapIntent = new Intent(activity, settings.getMapFactory().getMapClass()); + + mapIntent.putExtra("latitude", coords.get(0)); + mapIntent.putExtra("longitude", coords.get(1)); + + activity.startActivity(mapIntent); + } + + private void navigateTo() { + ArrayList<Double> coords = getDestination(); + + if (coords == null || coords.get(0) == null || coords.get(1) == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + cgeonavigate navigateActivity = new cgeonavigate(); + + Intent navigateIntent = new Intent(activity, navigateActivity.getClass()); + navigateIntent.putExtra("latitude", coords.get(0)); + navigateIntent.putExtra("longitude", coords.get(1)); + navigateIntent.putExtra("geocode", ""); + navigateIntent.putExtra("name", "Some destination"); + + activity.startActivity(navigateIntent); + } + + private void radarTo() { + ArrayList<Double> coords = getDestination(); + + if (coords == null || coords.get(0) == null || coords.get(1) == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + try { + if (cgBase.isIntentAvailable(activity, "com.google.android.radar.SHOW_RADAR") == true) { + Intent radarIntent = new Intent("com.google.android.radar.SHOW_RADAR"); + radarIntent.putExtra("latitude", new Float(coords.get(0))); + radarIntent.putExtra("longitude", new Float(coords.get(1))); + activity.startActivity(radarIntent); + } else { + AlertDialog.Builder dialog = new AlertDialog.Builder(activity); + dialog.setTitle(res.getString(R.string.err_radar_title)); + dialog.setMessage(res.getString(R.string.err_radar_message)); + dialog.setCancelable(true); + dialog.setPositiveButton("yes", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:com.eclipsim.gpsstatus2"))); + dialog.cancel(); + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_radar_market)); + Log.e(cgSettings.tag, "cgeopoint.radarTo.onClick: " + e.toString()); + } + } + }); + dialog.setNegativeButton("no", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog alert = dialog.create(); + alert.show(); + } + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_radar_generic)); + Log.e(cgSettings.tag, "cgeopoint.radarTo: " + e.toString()); + } + } + + private void cachesAround() { + ArrayList<Double> coords = getDestination(); + + if (coords == null || coords.get(0) == null || coords.get(1) == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + cgeocaches cachesActivity = new cgeocaches(); + + Intent cachesIntent = new Intent(activity, cachesActivity.getClass()); + + cachesIntent.putExtra("type", "coordinate"); + cachesIntent.putExtra("latitude", coords.get(0)); + cachesIntent.putExtra("longitude", coords.get(1)); + cachesIntent.putExtra("cachetype", settings.cacheType); + + activity.startActivity(cachesIntent); + + finish(); + } + + private class update extends cgUpdateLoc { + + @Override + public void updateLoc(cgGeo geo) { + if (geo == null) { + return; + } + + try { + if (latEdit == null) { + latEdit = (EditText) findViewById(R.id.latitude); + } + if (lonEdit == null) { + lonEdit = (EditText) findViewById(R.id.longitude); + } + + latEdit.setHint(base.formatCoordinate(geo.latitudeNow, "lat", false)); + lonEdit.setHint(base.formatCoordinate(geo.longitudeNow, "lon", false)); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to update location."); + } + } + } + + private class currentListener implements View.OnClickListener { + + public void onClick(View arg0) { + if (geo == null || geo.latitudeNow == null || geo.longitudeNow == null) { + warning.showToast(res.getString(R.string.err_point_unknown_position)); + return; + } + + ((EditText) findViewById(R.id.latitude)).setText(base.formatCoordinate(geo.latitudeNow, "lat", true)); + ((EditText) findViewById(R.id.longitude)).setText(base.formatCoordinate(geo.longitudeNow, "lon", true)); + + changed = false; + } + } + + private ArrayList<Double> getDestination() { + ArrayList<Double> coords = new ArrayList<Double>(); + Double latitude = null; + Double longitude = null; + + String bearingText = ((EditText) findViewById(R.id.bearing)).getText().toString(); + String distanceText = ((EditText) findViewById(R.id.distance)).getText().toString(); + String latText = ((EditText) findViewById(R.id.latitude)).getText().toString(); + String lonText = ((EditText) findViewById(R.id.longitude)).getText().toString(); + + if ((bearingText == null || bearingText.length() == 0) && (distanceText == null || distanceText.length() == 0) + && (latText == null || latText.length() == 0) && (lonText == null || lonText.length() == 0)) { + warning.helpDialog(res.getString(R.string.err_point_no_position_given_title), res.getString(R.string.err_point_no_position_given)); + return null; + } + + if (latText != null && latText.length() > 0 && lonText != null && lonText.length() > 0) { + // latitude & longitude + HashMap<String, Object> latParsed = base.parseCoordinate(latText, "lat"); + HashMap<String, Object> lonParsed = base.parseCoordinate(lonText, "lat"); + + if (latParsed == null || latParsed.get("coordinate") == null || latParsed.get("string") == null) { + warning.showToast(res.getString(R.string.err_parse_lat)); + return null; + } + + if (lonParsed == null || lonParsed.get("coordinate") == null || lonParsed.get("string") == null) { + warning.showToast(res.getString(R.string.err_parse_lon)); + return null; + } + + latitude = (Double) latParsed.get("coordinate"); + longitude = (Double) lonParsed.get("coordinate"); + } else { + if (geo == null || geo.latitudeNow == null || geo.longitudeNow == null) { + warning.showToast(res.getString(R.string.err_point_curr_position_unavailable)); + return null; + } + + latitude = geo.latitudeNow; + longitude = geo.longitudeNow; + } + + if (bearingText != null && bearingText.length() > 0 && distanceText != null && distanceText.length() > 0) { + // bearing & distance + Double bearing = null; + try { + bearing = new Double(bearingText); + } catch (Exception e) { + // probably not a number + } + if (bearing == null) { + warning.helpDialog(res.getString(R.string.err_point_bear_and_dist_title), res.getString(R.string.err_point_bear_and_dist)); + return null; + } + + Double distance = null; // km + + final Pattern patternA = Pattern.compile("^([0-9\\.\\,]+)[ ]*m$", Pattern.CASE_INSENSITIVE); // m + final Pattern patternB = Pattern.compile("^([0-9\\.\\,]+)[ ]*km$", Pattern.CASE_INSENSITIVE); // km + final Pattern patternC = Pattern.compile("^([0-9\\.\\,]+)[ ]*ft$", Pattern.CASE_INSENSITIVE); // ft - 0.3048m + final Pattern patternD = Pattern.compile("^([0-9\\.\\,]+)[ ]*yd$", Pattern.CASE_INSENSITIVE); // yd - 0.9144m + final Pattern patternE = Pattern.compile("^([0-9\\.\\,]+)[ ]*mi$", Pattern.CASE_INSENSITIVE); // mi - 1609.344m + + Matcher matcherA = patternA.matcher(distanceText); + Matcher matcherB = patternB.matcher(distanceText); + Matcher matcherC = patternC.matcher(distanceText); + Matcher matcherD = patternD.matcher(distanceText); + Matcher matcherE = patternE.matcher(distanceText); + + if (matcherA.find() == true && matcherA.groupCount() > 0) { + distance = (new Double(matcherA.group(1))) * 0.001; + } else if (matcherB.find() == true && matcherB.groupCount() > 0) { + distance = new Double(matcherB.group(1)); + } else if (matcherC.find() == true && matcherC.groupCount() > 0) { + distance = (new Double(matcherC.group(1))) * 0.0003048; + } else if (matcherD.find() == true && matcherD.groupCount() > 0) { + distance = (new Double(matcherD.group(1))) * 0.0009144; + } else if (matcherE.find() == true && matcherE.groupCount() > 0) { + distance = (new Double(matcherE.group(1))) * 1.609344; + } else { + try { + if (settings.units == cgSettings.unitsImperial) { + distance = (new Double(distanceText)) * 0.0003048; // considering it feet + } else { + distance = (new Double(distanceText)) * 0.001; // considering it meters + } + } catch (Exception e) { + // probably not a number + } + } + + if (distance == null) { + warning.showToast(res.getString(R.string.err_parse_dist)); + return null; + } + + Double latParsed = null; + Double lonParsed = null; + + HashMap<String, Double> coordsDst = base.getRadialDistance(latitude, longitude, bearing, distance); + + latParsed = coordsDst.get("latitude"); + lonParsed = coordsDst.get("longitude"); + + if (latParsed == null || lonParsed == null) { + warning.showToast(res.getString(R.string.err_point_location_error)); + return null; + } + + coords.add(0, (Double) latParsed); + coords.add(1, (Double) lonParsed); + } else if (latitude != null && longitude != null) { + coords.add(0, latitude); + coords.add(1, longitude); + } else { + return null; + } + + saveCoords(coords.get(0), coords.get(1)); + + return coords; + } + + private void saveCoords(Double latitude, Double longitude) { + if (changed == true && latitude == null || longitude == null) { + SharedPreferences.Editor edit = prefs.edit(); + + edit.putFloat("anylatitude", new Float(latitude)); + edit.putFloat("anylongitude", new Float(longitude)); + + edit.commit(); + } else { + SharedPreferences.Editor edit = prefs.edit(); + + edit.remove("anylatitude"); + edit.remove("anylongitude"); + + edit.commit(); + } + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-navigate-any", + activity, + "http://cgeo.carnero.cc/manual/"); + } catch (Exception e) { + // nothing + } + } +} diff --git a/src/cgeo/geocaching/cgeopopup.java b/src/cgeo/geocaching/cgeopopup.java new file mode 100644 index 0000000..92d1338 --- /dev/null +++ b/src/cgeo/geocaching/cgeopopup.java @@ -0,0 +1,834 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.google.android.apps.analytics.GoogleAnalyticsTracker; +import java.util.HashMap; +import java.util.Locale; + +public class cgeopopup extends Activity { + + private GoogleAnalyticsTracker tracker = null; + private Activity activity = null; + private Resources res = null; + private cgeoapplication app = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private Boolean fromDetail = false; + private LayoutInflater inflater = null; + private String geocode = null; + private cgCache cache = null; + private cgGeo geo = null; + private cgUpdateLoc geoUpdate = new update(); + private ProgressDialog storeDialog = null; + private ProgressDialog dropDialog = null; + private TextView cacheDistance = null; + private HashMap<String, Integer> gcIcons = new HashMap<String, Integer>(); + private Handler ratingHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + final Bundle data = msg.getData(); + + setRating(data.getFloat("rating"), data.getInt("votes")); + } catch (Exception e) { + // nothing + } + } + }; + private Handler storeCacheHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (storeDialog != null) { + storeDialog.dismiss(); + } + + finish(); + return; + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_store)); + + Log.e(cgSettings.tag, "cgeopopup.storeCacheHandler: " + e.toString()); + } + + if (storeDialog != null) { + storeDialog.dismiss(); + } + init(); + } + }; + private Handler dropCacheHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (dropDialog != null) { + dropDialog.dismiss(); + } + + finish(); + return; + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_drop)); + + Log.e(cgSettings.tag, "cgeopopup.dropCacheHandler: " + e.toString()); + } + + if (dropDialog != null) { + dropDialog.dismiss(); + } + init(); + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + setTheme(R.style.transparent); + setContentView(R.layout.popup); + base.setTitle(activity, res.getString(R.string.detail)); + + // google analytics + tracker = GoogleAnalyticsTracker.getInstance(); + tracker.start(cgSettings.analytics, this); + tracker.dispatch(); + base.sendAnal(activity, tracker, "/popup"); + + // get parameters + Bundle extras = getIntent().getExtras(); + if (extras != null) { + fromDetail = extras.getBoolean("fromdetail"); + geocode = extras.getString("geocode"); + } + + if (geocode == null || geocode.length() == 0) { + warning.showToast(res.getString(R.string.err_detail_cache_find)); + + finish(); + return; + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, 2, 0, res.getString(R.string.cache_menu_compass)).setIcon(android.R.drawable.ic_menu_compass); // compass + + SubMenu subMenu = menu.addSubMenu(1, 0, 0, res.getString(R.string.cache_menu_navigate)).setIcon(android.R.drawable.ic_menu_more); + subMenu.add(0, 3, 0, res.getString(R.string.cache_menu_radar)); // radar + subMenu.add(0, 1, 0, res.getString(R.string.cache_menu_map)); // c:geo map + if (base.isLocus(activity)) { + subMenu.add(0, 20, 0, res.getString(R.string.cache_menu_locus)); // ext.: locus + } + if (base.isRmaps(activity)) { + subMenu.add(0, 21, 0, res.getString(R.string.cache_menu_rmaps)); // ext.: rmaps + } + subMenu.add(0, 23, 0, res.getString(R.string.cache_menu_map_ext)); // ext.: other + subMenu.add(0, 4, 0, res.getString(R.string.cache_menu_tbt)); // turn-by-turn + + menu.add(0, 6, 0, res.getString(R.string.cache_menu_visit)).setIcon(android.R.drawable.ic_menu_agenda); // log visit + menu.add(0, 5, 0, res.getString(R.string.cache_menu_around)).setIcon(android.R.drawable.ic_menu_rotate); // caches around + menu.add(0, 7, 0, res.getString(R.string.cache_menu_browser)).setIcon(android.R.drawable.ic_menu_info_details); // browser + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + try { + if (cache != null && cache.latitude != null && cache.longitude != null) { + menu.findItem(0).setVisible(true); + menu.findItem(2).setVisible(true); + menu.findItem(5).setVisible(true); + } else { + menu.findItem(0).setVisible(false); + menu.findItem(2).setVisible(false); + menu.findItem(5).setVisible(false); + } + + if (fromDetail == false && settings.isLogin() == true) { + menu.findItem(6).setEnabled(true); + } else { + menu.findItem(6).setEnabled(false); + } + } catch (Exception e) { + // nothing + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int menuItem = item.getItemId(); + + if (menuItem == 1) { + showOnMap(); + return true; + } else if (menuItem == 2) { + navigateTo(); + return true; + } else if (menuItem == 3) { + radarTo(); + return true; + } else if (menuItem == 4) { + if (geo != null) { + base.runNavigation(activity, res, settings, warning, tracker, cache.latitude, cache.longitude, geo.latitudeNow, geo.longitudeNow); + } else { + base.runNavigation(activity, res, settings, warning, tracker, cache.latitude, cache.longitude); + } + + return true; + } else if (menuItem == 5) { + cachesAround(); + return true; + } else if (menuItem == 6) { + if (cache.cacheid == null || cache.cacheid.length() == 0) { + warning.showToast(res.getString(R.string.err_cannot_log_visit)); + return false; + } + + Intent logVisitIntent = new Intent(activity, cgeovisit.class); + logVisitIntent.putExtra("id", cache.cacheid); + logVisitIntent.putExtra("geocode", cache.geocode.toUpperCase()); + logVisitIntent.putExtra("type", cache.type.toLowerCase()); + activity.startActivity(logVisitIntent); + + activity.finish(); + + return true; + } else if (menuItem == 7) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/seek/cache_details.aspx?wp=" + cache.geocode))); + return true; + } else if (menuItem == 20) { + base.runExternalMap(cgBase.mapAppLocus, activity, res, warning, tracker, cache.latitude, cache.longitude); // locus + return true; + } else if (menuItem == 21) { + base.runExternalMap(cgBase.mapAppRmaps, activity, res, warning, tracker, cache.latitude, cache.longitude); // rmaps + return true; + } else if (menuItem == 23) { + base.runExternalMap(cgBase.mapAppAny, activity, res, warning, tracker, cache.latitude, cache.longitude); // rmaps + return true; + } + + return false; + } + + private void init() { + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + + app.setAction(geocode); + + cache = app.getCacheByGeocode(geocode); + + if (cache == null) { + warning.showToast(res.getString(R.string.err_detail_cache_find)); + + finish(); + return; + } + + try { + RelativeLayout itemLayout; + TextView itemName; + TextView itemValue; + LinearLayout itemStars; + + if (gcIcons == null || gcIcons.isEmpty()) { + gcIcons.put("ape", R.drawable.type_ape); + gcIcons.put("cito", R.drawable.type_cito); + gcIcons.put("earth", R.drawable.type_earth); + gcIcons.put("event", R.drawable.type_event); + gcIcons.put("letterbox", R.drawable.type_letterbox); + gcIcons.put("locationless", R.drawable.type_locationless); + gcIcons.put("mega", R.drawable.type_mega); + gcIcons.put("multi", R.drawable.type_multi); + gcIcons.put("traditional", R.drawable.type_traditional); + gcIcons.put("virtual", R.drawable.type_virtual); + gcIcons.put("webcam", R.drawable.type_webcam); + gcIcons.put("wherigo", R.drawable.type_wherigo); + gcIcons.put("mystery", R.drawable.type_mystery); + gcIcons.put("gchq", R.drawable.type_hq); + } + + if (cache.name != null && cache.name.length() > 0) { + base.setTitle(activity, cache.name); + } else { + base.setTitle(activity, geocode.toUpperCase()); + } + + inflater = activity.getLayoutInflater(); + geocode = cache.geocode.toUpperCase(); + + ((ScrollView) findViewById(R.id.details_list_box)).setVisibility(View.VISIBLE); + LinearLayout detailsList = (LinearLayout) findViewById(R.id.details_list); + detailsList.removeAllViews(); + + // actionbar icon + if (cache.type != null && gcIcons.containsKey(cache.type) == true) { // cache icon + ((TextView) findViewById(R.id.actionbar_title)).setCompoundDrawablesWithIntrinsicBounds((Drawable) activity.getResources().getDrawable(gcIcons.get(cache.type)), null, null, null); + } else { // unknown cache type, "mystery" icon + ((TextView) findViewById(R.id.actionbar_title)).setCompoundDrawablesWithIntrinsicBounds((Drawable) activity.getResources().getDrawable(gcIcons.get("mystery")), null, null, null); + } + + // cache type + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_type)); + if (cgBase.cacheTypesInv.containsKey(cache.type) == true) { // cache icon + if (cache.size != null && cache.size.length() > 0) { + itemValue.setText(cgBase.cacheTypesInv.get(cache.type) + " (" + cache.size + ")"); + } else { + itemValue.setText(cgBase.cacheTypesInv.get(cache.type)); + } + } else { + if (cache.size != null && cache.size.length() > 0) { + itemValue.setText(cgBase.cacheTypesInv.get("mystery") + " (" + cache.size + ")"); + } else { + itemValue.setText(cgBase.cacheTypesInv.get("mystery")); + } + } + detailsList.addView(itemLayout); + + // gc-code + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_geocode)); + itemValue.setText(cache.geocode.toUpperCase()); + detailsList.addView(itemLayout); + + // cache state + if (cache.archived == true || cache.disabled == true || cache.members == true || cache.found == true) { + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_status)); + + StringBuilder state = new StringBuilder(); + if (cache.found == true) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_found)); + } + if (cache.archived == true) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_archived)); + } + if (cache.disabled == true) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_disabled)); + } + if (cache.members == true) { + if (state.length() > 0) { + state.append(", "); + } + state.append(res.getString(R.string.cache_status_premium)); + } + + itemValue.setText(state.toString()); + detailsList.addView(itemLayout); + + state = null; + } + + // distance + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.cache_distance)); + itemValue.setText("--"); + detailsList.addView(itemLayout); + cacheDistance = itemValue; + + // difficulty + if (cache.difficulty > 0f) { + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_layout, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + itemStars = (LinearLayout) itemLayout.findViewById(R.id.stars); + + itemName.setText(res.getString(R.string.cache_difficulty)); + itemValue.setText(String.format(Locale.getDefault(), "%.1f", cache.difficulty) + " of 5"); + for (int i = 0; i <= 4; i++) { + ImageView star = (ImageView) inflater.inflate(R.layout.star, null); + if ((cache.difficulty - i) >= 1.0) { + star.setImageResource(R.drawable.star_on); + } else if ((cache.difficulty - i) > 0.0) { + star.setImageResource(R.drawable.star_half); + } else { + star.setImageResource(R.drawable.star_off); + } + itemStars.addView(star); + } + detailsList.addView(itemLayout); + } + + // terrain + if (cache.terrain > 0f) { + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_layout, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + itemStars = (LinearLayout) itemLayout.findViewById(R.id.stars); + + itemName.setText(res.getString(R.string.cache_terrain)); + itemValue.setText(String.format(Locale.getDefault(), "%.1f", cache.terrain) + " of 5"); + for (int i = 0; i <= 4; i++) { + ImageView star = (ImageView) inflater.inflate(R.layout.star, null); + if ((cache.terrain - i) >= 1.0) { + star.setImageResource(R.drawable.star_on); + } else if ((cache.terrain - i) > 0.0) { + star.setImageResource(R.drawable.star_half); + } else { + star.setImageResource(R.drawable.star_off); + } + itemStars.addView(star); + } + detailsList.addView(itemLayout); + } + + // rating + if (cache.rating != null && cache.rating > 0) { + setRating(cache.rating, cache.votes); + } else { + (new Thread() { + + public void run() { + cgRating rating = base.getRating(cache.guid, geocode); + + Message msg = new Message(); + Bundle bundle = new Bundle(); + + if (rating == null || rating.rating == null) { + return; + } + + bundle.putFloat("rating", rating.rating); + bundle.putInt("votes", rating.votes); + msg.setData(bundle); + + ratingHandler.sendMessage(msg); + } + }).start(); + } + + // more details + if (fromDetail == false) { + ((LinearLayout) findViewById(R.id.more_details_box)).setVisibility(View.VISIBLE); + + Button buttonMore = (Button) findViewById(R.id.more_details); + buttonMore.setOnClickListener(new OnClickListener() { + + public void onClick(View arg0) { + Intent cachesIntent = new Intent(activity, cgeodetail.class); + cachesIntent.putExtra("geocode", geocode.toUpperCase()); + activity.startActivity(cachesIntent); + + activity.finish(); + return; + } + }); + } else { + ((LinearLayout) findViewById(R.id.more_details_box)).setVisibility(View.GONE); + } + + if (fromDetail == false) { + ((LinearLayout) findViewById(R.id.offline_box)).setVisibility(View.VISIBLE); + + // offline use + final TextView offlineText = (TextView) findViewById(R.id.offline_text); + final Button offlineRefresh = (Button) findViewById(R.id.offline_refresh); + final Button offlineStore = (Button) findViewById(R.id.offline_store); + + if (cache.reason > 0) { + Long diff = (System.currentTimeMillis() / (60 * 1000)) - (cache.detailedUpdate / (60 * 1000)); // minutes + + String ago = ""; + if (diff < 15) { + ago = res.getString(R.string.cache_offline_time_mins_few); + } else if (diff < 50) { + ago = res.getString(R.string.cache_offline_time_about) + " " + diff + " " + res.getString(R.string.cache_offline_time_mins); + } else if (diff < 90) { + ago = res.getString(R.string.cache_offline_time_about) + " " + res.getString(R.string.cache_offline_time_hour); + } else if (diff < (48 * 60)) { + ago = res.getString(R.string.cache_offline_time_about) + " " + (diff / 60) + " " + res.getString(R.string.cache_offline_time_hours); + } else { + ago = res.getString(R.string.cache_offline_time_about) + " " + (diff / (24 * 60)) + " " + res.getString(R.string.cache_offline_time_days); + } + + offlineText.setText(res.getString(R.string.cache_offline_stored) + "\n" + ago); + + offlineRefresh.setVisibility(View.VISIBLE); + offlineRefresh.setEnabled(true); + offlineRefresh.setOnClickListener(new storeCache()); + + offlineStore.setText(res.getString(R.string.cache_offline_drop)); + offlineStore.setEnabled(true); + offlineStore.setOnClickListener(new dropCache()); + } else { + offlineText.setText(res.getString(R.string.cache_offline_not_ready)); + + offlineRefresh.setVisibility(View.GONE); + offlineRefresh.setEnabled(false); + offlineRefresh.setOnTouchListener(null); + offlineRefresh.setOnClickListener(null); + + offlineStore.setText(res.getString(R.string.cache_offline_store)); + offlineStore.setEnabled(true); + offlineStore.setOnClickListener(new storeCache()); + } + } else { + ((LinearLayout) findViewById(R.id.offline_box)).setVisibility(View.GONE); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeopopup.init: " + e.toString()); + } + + if (geo != null) { + geoUpdate.updateLoc(geo); + } + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + init(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + init(); + } + + @Override + public void onDestroy() { + if (geo != null) { + geo = app.removeGeo(); + } + if (tracker != null) { + tracker.stop(); + } + + super.onDestroy(); + } + + @Override + public void onStop() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onStop(); + } + + @Override + public void onPause() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onPause(); + } + + private class update extends cgUpdateLoc { + + @Override + public void updateLoc(cgGeo geo) { + if (geo == null) { + return; + } + + try { + if (geo.latitudeNow != null && geo.longitudeNow != null && cache != null && cache.latitude != null && cache.longitude != null) { + cacheDistance.setText(base.getHumanDistance(cgBase.getDistance(geo.latitudeNow, geo.longitudeNow, cache.latitude, cache.longitude))); + cacheDistance.bringToFront(); + } + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to update location."); + } + } + } + + private void showOnMap() { + if (cache == null || cache.latitude == null || cache.longitude == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + Intent mapIntent = new Intent(activity, settings.getMapFactory().getMapClass()); + + mapIntent.putExtra("latitude", cache.latitude); + mapIntent.putExtra("longitude", cache.longitude); + + activity.startActivity(mapIntent); + } + + private void navigateTo() { + if (cache == null || cache.latitude == null || cache.longitude == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + cgeonavigate navigateActivity = new cgeonavigate(); + + Intent navigateIntent = new Intent(activity, navigateActivity.getClass()); + navigateIntent.putExtra("latitude", cache.latitude); + navigateIntent.putExtra("longitude", cache.longitude); + navigateIntent.putExtra("geocode", ""); + navigateIntent.putExtra("name", "Some destination"); + + activity.startActivity(navigateIntent); + } + + private void radarTo() { + if (cache == null || cache.latitude == null || cache.longitude == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + try { + if (cgBase.isIntentAvailable(activity, "com.google.android.radar.SHOW_RADAR") == true) { + Intent radarIntent = new Intent("com.google.android.radar.SHOW_RADAR"); + radarIntent.putExtra("latitude", new Float(cache.latitude)); + radarIntent.putExtra("longitude", new Float(cache.longitude)); + activity.startActivity(radarIntent); + } else { + AlertDialog.Builder dialog = new AlertDialog.Builder(activity); + dialog.setTitle(res.getString(R.string.err_radar_title)); + dialog.setMessage(res.getString(R.string.err_radar_message)); + dialog.setCancelable(true); + dialog.setPositiveButton("yes", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:com.eclipsim.gpsstatus2"))); + dialog.cancel(); + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_radar_market)); + Log.e(cgSettings.tag, "cgeopoint.radarTo.onClick: " + e.toString()); + } + } + }); + dialog.setNegativeButton("no", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog alert = dialog.create(); + alert.show(); + } + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_radar_generic)); + Log.e(cgSettings.tag, "cgeopoint.radarTo: " + e.toString()); + } + } + + private void cachesAround() { + if (cache == null || cache.latitude == null || cache.longitude == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + cgeocaches cachesActivity = new cgeocaches(); + + Intent cachesIntent = new Intent(activity, cachesActivity.getClass()); + + cachesIntent.putExtra("type", "coordinate"); + cachesIntent.putExtra("latitude", cache.latitude); + cachesIntent.putExtra("longitude", cache.longitude); + cachesIntent.putExtra("cachetype", settings.cacheType); + + activity.startActivity(cachesIntent); + + finish(); + } + + private class storeCache implements View.OnClickListener { + + public void onClick(View arg0) { + if (dropDialog != null && dropDialog.isShowing() == true) { + warning.showToast("Still removing this cache."); + return; + } + + storeDialog = ProgressDialog.show(activity, res.getString(R.string.cache_dialog_offline_save_title), res.getString(R.string.cache_dialog_offline_save_message), true); + storeDialog.setCancelable(false); + Thread thread = new storeCacheThread(storeCacheHandler); + thread.start(); + } + } + + private class storeCacheThread extends Thread { + + private Handler handler = null; + + public storeCacheThread(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + base.storeCache(app, activity, cache, null, 1, handler); + } + } + + private class dropCache implements View.OnClickListener { + + public void onClick(View arg0) { + if (storeDialog != null && storeDialog.isShowing() == true) { + warning.showToast("Still saving this cache."); + return; + } + + dropDialog = ProgressDialog.show(activity, res.getString(R.string.cache_dialog_offline_drop_title), res.getString(R.string.cache_dialog_offline_drop_message), true); + dropDialog.setCancelable(false); + Thread thread = new dropCacheThread(dropCacheHandler); + thread.start(); + } + } + + private class dropCacheThread extends Thread { + + private Handler handler = null; + + public dropCacheThread(Handler handlerIn) { + handler = handlerIn; + } + + @Override + public void run() { + base.dropCache(app, activity, cache, handler); + } + } + + private void setRating(Float rating, Integer votes) { + if (rating == null || rating <= 0) { + return; + } + + RelativeLayout itemLayout; + TextView itemName; + TextView itemValue; + LinearLayout itemStars; + LinearLayout detailsList = (LinearLayout) findViewById(R.id.details_list); + + itemLayout = (RelativeLayout) inflater.inflate(R.layout.cache_layout, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + itemStars = (LinearLayout) itemLayout.findViewById(R.id.stars); + + itemName.setText(res.getString(R.string.cache_rating)); + itemValue.setText(String.format(Locale.getDefault(), "%.1f", rating) + " of 5"); + for (int i = 0; i <= 4; i++) { + ImageView star = (ImageView) inflater.inflate(R.layout.star, null); + if ((rating - i) >= 1.0) { + star.setImageResource(R.drawable.star_on); + } else if ((rating - i) > 0.0) { + star.setImageResource(R.drawable.star_half); + } else { + star.setImageResource(R.drawable.star_off); + } + itemStars.addView(star, (1 + i)); + } + if (votes != null) { + final TextView itemAddition = (TextView) itemLayout.findViewById(R.id.addition); + itemAddition.setText("(" + votes + ")"); + itemAddition.setVisibility(View.VISIBLE); + } + detailsList.addView(itemLayout); + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goCompass(View view) { + if (cache == null || cache.latitude == null || cache.longitude == null) { + warning.showToast(res.getString(R.string.cache_coordinates_no)); + + return; + } + + cgeonavigate navigateActivity = new cgeonavigate(); + + Intent navigateIntent = new Intent(activity, navigateActivity.getClass()); + navigateIntent.putExtra("latitude", cache.latitude); + navigateIntent.putExtra("longitude", cache.longitude); + navigateIntent.putExtra("geocode", cache.geocode.toUpperCase()); + navigateIntent.putExtra("name", cache.name); + + activity.startActivity(navigateIntent); + + finish(); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-cache-info", + activity, + "http://cgeo.carnero.cc/manual/" + ); + } catch (Exception e) { + // nothing + } + + finish(); + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgeosmaps.java b/src/cgeo/geocaching/cgeosmaps.java new file mode 100644 index 0000000..9979ab5 --- /dev/null +++ b/src/cgeo/geocaching/cgeosmaps.java @@ -0,0 +1,172 @@ +package cgeo.geocaching; + +import java.util.ArrayList; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; + +public class cgeosmaps extends Activity { + + private ArrayList<Bitmap> maps = new ArrayList<Bitmap>(); + private String geocode = null; + private Resources res = null; + private cgeoapplication app = null; + private Activity activity = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private LayoutInflater inflater = null; + private ProgressDialog waitDialog = null; + private LinearLayout smapsView = null; + private BitmapFactory factory = null; + private Handler loadMapsHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (maps == null || maps.isEmpty()) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + + warning.showToast(res.getString(R.string.err_detail_not_load_map_static)); + + finish(); + return; + } else { + if (waitDialog != null) { + waitDialog.dismiss(); + } + + if (inflater == null) { + inflater = activity.getLayoutInflater(); + } + + if (smapsView == null) { + smapsView = (LinearLayout) findViewById(R.id.maps_list); + } + smapsView.removeAllViews(); + + int cnt = 1; + for (Bitmap image : maps) { + if (image != null) { + final ImageView map = (ImageView) inflater.inflate(R.layout.map_static_item, null); + map.setImageBitmap(image); + smapsView.addView(map); + + cnt++; + } + } + } + } catch (Exception e) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + Log.e(cgSettings.tag, "cgeosmaps.loadMapsHandler: " + e.toString()); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.map_static); + base.setTitle(activity, res.getString(R.string.map_static_title)); + + // get parameters + Bundle extras = getIntent().getExtras(); + + // try to get data from extras + if (extras != null) { + geocode = extras.getString("geocode"); + } + + if (geocode == null) { + warning.showToast("Sorry, c:geo forgot for what cache you want to load static maps."); + finish(); + return; + } + + waitDialog = ProgressDialog.show(this, null, res.getString(R.string.map_static_loading), true); + waitDialog.setCancelable(true); + + (new loadMaps()).start(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + private class loadMaps extends Thread { + + @Override + public void run() { + try { + if (factory == null) { + factory = new BitmapFactory(); + } + + for (int level = 1; level <= 5; level++) { + try { + Bitmap image = BitmapFactory.decodeFile(settings.getStorage() + geocode + "/map_" + level); + if (image != null) { + maps.add(image); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeosmaps.loadMaps.run.1: " + e.toString()); + } + } + + if (maps.isEmpty() == true) { + for (int level = 1; level <= 5; level++) { + try { + Bitmap image = BitmapFactory.decodeFile(settings.getStorageSec() + geocode + "/map_" + level); + if (image != null) { + maps.add(image); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeosmaps.loadMaps.run.2: " + e.toString()); + } + } + } + + loadMapsHandler.sendMessage(new Message()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeosmaps.loadMaps.run: " + e.toString()); + } + } + } + + public void goHome(View view) { + base.goHome(activity); + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgeospoilers.java b/src/cgeo/geocaching/cgeospoilers.java new file mode 100644 index 0000000..4d2bd58 --- /dev/null +++ b/src/cgeo/geocaching/cgeospoilers.java @@ -0,0 +1,231 @@ +package cgeo.geocaching; + +import java.util.ArrayList; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.net.Uri; +import android.app.Activity; +import android.app.ProgressDialog; +import android.util.Log; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.LinearLayout; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.view.ViewGroup.LayoutParams; + +public class cgeospoilers extends Activity { + private ArrayList<cgSpoiler> spoilers = new ArrayList<cgSpoiler>(); + private Resources res = null; + private String geocode = null; + private cgeoapplication app = null; + private Activity activity = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private LayoutInflater inflater = null; + private ProgressDialog progressDialog = null; + private ProgressDialog waitDialog = null; + private LinearLayout spoilerView = null; + private int offline = 0; + private int count = 0; + private int countDone = 0; + private Handler loadSpoilersHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (spoilers.isEmpty()) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + + warning.showToast("Sorry, c:geo failed to load spoiler images."); + + finish(); + return; + } else { + if (waitDialog != null) { + waitDialog.dismiss(); + } + + if (app.isOffline(geocode, null) == true) { + offline = 1; + } else { + offline = 0; + } + + count = spoilers.size(); + progressDialog = new ProgressDialog(activity); + progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + progressDialog.setMessage(res.getString(R.string.cache_spoiler_images_loading)); + progressDialog.setCancelable(true); + progressDialog.setMax(count); + progressDialog.show(); + + LinearLayout rowView = null; + for (final cgSpoiler spl : spoilers) { + rowView = (LinearLayout) inflater.inflate(R.layout.spoiler_item, null); + + ((TextView) rowView.findViewById(R.id.title)).setText(Html.fromHtml(spl.title)); + + if (spl.description != null && spl.description.length() > 0) { + final TextView descView = (TextView) rowView.findViewById(R.id.description); + descView.setText(Html.fromHtml(spl.description), TextView.BufferType.SPANNABLE); + descView.setVisibility(View.VISIBLE); + } + + final Handler handler = new onLoadHandler(rowView, spl); + + new Thread() { + + @Override + public void run() { + BitmapDrawable image = null; + try { + cgHtmlImg imgGetter = new cgHtmlImg(activity, settings, geocode, true, offline, false); + + image = imgGetter.getDrawable(spl.url); + Message message = handler.obtainMessage(0, image); + handler.sendMessage(message); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeospoilers.onCreate.onClick.run: " + e.toString()); + } + + } + }.start(); + + spoilerView.addView(rowView); + } + } + } catch (Exception e) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + Log.e(cgSettings.tag, "cgeospoilers.loadSpoilersHandler: " + e.toString()); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.spoilers); + base.setTitle(activity, res.getString(R.string.cache_spoiler_images_title)); + + // google analytics + base.sendAnal(activity, "/spoilers"); + + // get parameters + Bundle extras = getIntent().getExtras(); + + // try to get data from extras + if (extras != null) { + geocode = extras.getString("geocode"); + } + + if (geocode == null) { + warning.showToast("Sorry, c:geo forgot for what cache you want to load spoiler images."); + finish(); + return; + } + + inflater = activity.getLayoutInflater(); + if (spoilerView == null) { + spoilerView = (LinearLayout) findViewById(R.id.spoiler_list); + } + + waitDialog = ProgressDialog.show(this, null, res.getString(R.string.cache_spoiler_images_loading), true); + waitDialog.setCancelable(true); + + (new loadSpoilers()).start(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + private class loadSpoilers extends Thread { + + @Override + public void run() { + try { + spoilers = app.loadSpoilers(geocode); + + loadSpoilersHandler.sendMessage(new Message()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeospoilers.loadSpoilers.run: " + e.toString()); + } + } + } + + private class onLoadHandler extends Handler { + + LinearLayout view = null; + cgSpoiler spoiler = null; + + public onLoadHandler(LinearLayout view, cgSpoiler spoiler) { + this.view = view; + this.spoiler = spoiler; + } + + @Override + public void handleMessage(Message message) { + BitmapDrawable image = (BitmapDrawable) message.obj; + if (image != null) { + ImageView spoilerImage = null; + spoilerImage = (ImageView) inflater.inflate(R.layout.image_item, null); + + Rect bounds = image.getBounds(); + + spoilerImage.setImageResource(R.drawable.image_not_loaded); + spoilerImage.setClickable(true); + spoilerImage.setOnClickListener(new View.OnClickListener() { + + public void onClick(View arg0) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(spoiler.url))); + } + }); + spoilerImage.setImageDrawable((BitmapDrawable) message.obj); + spoilerImage.setScaleType(ImageView.ScaleType.CENTER_CROP); + spoilerImage.setLayoutParams(new LayoutParams(bounds.width(), bounds.height())); + + view.addView(spoilerImage); + } + + countDone++; + progressDialog.setProgress(countDone); + if (progressDialog.getProgress() >= count) { + progressDialog.dismiss(); + } + } + } + + public void goHome(View view) { + base.goHome(activity); + } +} diff --git a/src/cgeo/geocaching/cgeotouch.java b/src/cgeo/geocaching/cgeotouch.java new file mode 100644 index 0000000..07f83d5 --- /dev/null +++ b/src/cgeo/geocaching/cgeotouch.java @@ -0,0 +1,487 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import android.app.Activity; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class cgeotouch extends cgLogForm { + private cgeoapplication app = null; + private Activity activity = null; + private Resources res = null; + private LayoutInflater inflater = null; + private cgBase base = null; + private cgSettings settings = null; + private cgWarning warning = null; + private cgTrackable trackable = null; + private ArrayList<Integer> types = new ArrayList<Integer>(); + private ProgressDialog waitDialog = null; + private String guid = null; + private String geocode = null; + private String text = null; + private String viewstate = null; + private String viewstate1 = null; + private Boolean gettingViewstate = true; + private Calendar date = Calendar.getInstance(); + private int typeSelected = -1; + private int attempts = 0; + private CheckBox tweetCheck = null; + private LinearLayout tweetBox = null; + + private Handler showProgressHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + base.showProgress(activity, true); + } + }; + + private Handler loadDataHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if ((viewstate == null || viewstate.length() == 0) && attempts < 2) { + warning.showToast(res.getString(R.string.err_log_load_data_again)); + + loadData thread; + thread = new loadData(guid); + thread.start(); + + return; + } else if ((viewstate == null || viewstate.length() == 0) && attempts >= 2) { + warning.showToast(res.getString(R.string.err_log_load_data)); + base.showProgress(activity, false); + + return; + } + + gettingViewstate = false; // we're done, user can post log + + Button buttonPost = (Button)findViewById(R.id.post); + buttonPost.setEnabled(true); + buttonPost.setOnClickListener(new postListener()); + + base.showProgress(activity, false); + } + }; + + private Handler postLogHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (msg.what == 1) { + warning.showToast(res.getString(R.string.info_log_posted)); + + if (waitDialog != null) { + waitDialog.dismiss(); + } + finish(); + return; + } else if (msg.what >= 1000) { + if (msg.what == 1001) { + warning.showToast(res.getString(R.string.warn_log_text_fill)); + } else if(msg.what == 1002) { + warning.showToast(res.getString(R.string.err_log_failed_server)); + } else { + warning.showToast(res.getString(R.string.err_log_post_failed)); + } + } else { + if (cgBase.errorRetrieve.get(msg.what) != null) { + warning.showToast(res.getString(R.string.err_log_post_failed_because) + cgBase.errorRetrieve.get(msg.what) + "."); + } else { + warning.showToast(res.getString(R.string.err_log_post_failed)); + } + } + + if (waitDialog != null) { + waitDialog.dismiss(); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication)this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.touch); + base.setTitle(activity, res.getString(R.string.trackable_touch)); + + // google analytics + base.sendAnal(activity, "/trackable/touch"); + + // get parameters + Bundle extras = getIntent().getExtras(); + if (extras != null) { + geocode = extras.getString("geocode"); + guid = extras.getString("guid"); + text = extras.getString("text"); + } + + trackable = app.getTrackableByGeocode("logging trackable"); + + if (trackable.name != null && trackable.name.length() > 0) { + base.setTitle(activity, res.getString(R.string.trackable_touch) + trackable.name); + } else { + base.setTitle(activity, res.getString(R.string.trackable_touch) + trackable.geocode.toUpperCase()); + } + + app.setAction("logging trackable"); + + if (trackable == null || guid == null) { + warning.showToast(res.getString(R.string.err_tb_forgot_saw)); + + finish(); + return; + } + + init(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + init(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + SubMenu subMenu = menu.addSubMenu(0, 0, 0, res.getString(R.string.log_add)).setIcon(android.R.drawable.ic_menu_add); + + subMenu.add(0, 0x6, 0, res.getString(R.string.log_date_time)); + subMenu.add(0, 0x4, 0, res.getString(R.string.log_date)); + subMenu.add(0, 0x2, 0, res.getString(R.string.log_time)); + subMenu.add(0, 0x1, 0, res.getString(R.string.init_signature)); + subMenu.add(0, 0x7, 0, res.getString(R.string.log_date_time) + " & " + res.getString(R.string.init_signature)); + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + if (settings.getSignature() == null) { + menu.findItem(0x1).setVisible(false); + menu.findItem(0x7).setVisible(false); + } else { + menu.findItem(0x1).setVisible(true); + menu.findItem(0x7).setVisible(true); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + + EditText text = null; + String textContent = null; + String dateString = null; + String timeString = null; + String addText = ""; + + if ((id >= 0x1 && id <= 0x7)) { + text = (EditText) findViewById(R.id.log); + textContent = text.getText().toString(); + dateString = cgBase.dateOut.format(new Date()); + timeString = cgBase.timeOut.format(new Date()); + + if ((id & 0x4) == 0x4) { + addText += dateString; + if ((id & 0x2) == 0x2) { + addText += " | "; + } + } + if ((id & 0x2) == 0x2) { + addText += timeString; + } + if ((id & 0x1) == 0x1 && settings.getSignature() != null) { + if (addText.length() > 0) { + addText += "\n"; + } + addText += settings.getSignature() + .replaceAll("\\[DATE\\]", dateString) + .replaceAll("\\[TIME\\]", timeString) + .replaceAll("\\[USER\\]", settings.getUsername()) + .replaceAll("\\[NUMBER\\]", ""); + } + if (textContent.length() > 0 && addText.length() > 0 ) { + addText = "\n" + addText; + } + text.setText(textContent + addText, TextView.BufferType.NORMAL); + text.setSelection(text.getText().toString().length()); + return true; + } + + return false; + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo info) { + super.onCreateContextMenu(menu, view, info); + final int viewId = view.getId(); + + if (viewId == R.id.type) { + for (final int typeOne : types) menu.add(viewId, typeOne, 0, cgBase.logTypes2.get(typeOne)); + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + final int group = item.getGroupId(); + final int id = item.getItemId(); + + if (group == R.id.type) { + setType(id); + + return true; + } + + return false; + } + + public void init() { + if (geocode != null) app.setAction("logging trackable"); + + types.clear(); + types.add(cgBase.LOG_RETRIEVED_IT); + types.add(cgBase.LOG_GRABBED_IT); + types.add(cgBase.LOG_NOTE); + types.add(cgBase.LOG_DISCOVERED_IT); + + if (typeSelected < 0 && cgBase.logTypes2.get(typeSelected) == null) typeSelected = types.get(2); + setType(typeSelected); + + Button typeButton = (Button)findViewById(R.id.type); + registerForContextMenu(typeButton); + typeButton.setText(cgBase.logTypes2.get(typeSelected)); + typeButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + openContextMenu(view); + } + }); + + Button dateButton = (Button)findViewById(R.id.date); + dateButton.setText(cgBase.dateOutShort.format(date.getTime())); + dateButton.setOnClickListener(new cgeotouchDateListener()); + + if (tweetBox == null) tweetBox = (LinearLayout)findViewById(R.id.tweet_box); + if (tweetCheck == null) tweetCheck = (CheckBox)findViewById(R.id.tweet); + tweetCheck.setChecked(true); + + Button buttonPost = (Button)findViewById(R.id.post); + if (viewstate == null || viewstate.length() == 0) { + buttonPost.setEnabled(false); + buttonPost.setOnTouchListener(null); + buttonPost.setOnClickListener(null); + + loadData thread; + thread = new loadData(guid); + thread.start(); + } else { + buttonPost.setEnabled(true); + buttonPost.setOnClickListener(new postListener()); + } + } + + public void setDate(Calendar dateIn) { + date = dateIn; + + final Button dateButton = (Button)findViewById(R.id.date); + dateButton.setText(cgBase.dateOutShort.format(date.getTime())); + } + + public void setType(int type) { + final Button typeButton = (Button)findViewById(R.id.type); + + if (cgBase.logTypes2.get(type) != null) typeSelected = type; + if (cgBase.logTypes2.get(typeSelected) == null) typeSelected = 0; + typeButton.setText(cgBase.logTypes2.get(typeSelected)); + + if (tweetBox == null) tweetBox = (LinearLayout)findViewById(R.id.tweet_box); + if (settings.twitter == 1) tweetBox.setVisibility(View.VISIBLE); + else tweetBox.setVisibility(View.GONE); + } + + private class cgeotouchDateListener implements View.OnClickListener { + public void onClick(View arg0) { + Dialog dateDialog = new cgeodate(activity, (cgeotouch)activity, date); + dateDialog.setCancelable(true); + dateDialog.show(); + } + } + + private class postListener implements View.OnClickListener { + public void onClick(View arg0) { + if (gettingViewstate == false) { + waitDialog = ProgressDialog.show(activity, null, res.getString(R.string.log_saving), true); + waitDialog.setCancelable(true); + + String tracking = ((EditText)findViewById(R.id.tracking)).getText().toString(); + String log = ((EditText)findViewById(R.id.log)).getText().toString(); + Thread thread = new postLog(postLogHandler, tracking, log); + thread.start(); + } else { + warning.showToast(res.getString(R.string.err_log_load_data_still)); + } + } + } + + private class loadData extends Thread { + private String guid = null; + + public loadData(String guidIn) { + guid = guidIn; + + if (guid == null) { + warning.showToast(res.getString(R.string.err_tb_forgot_saw)); + + finish(); + return; + } + } + + @Override + public void run() { + final HashMap<String, String> params = new HashMap<String, String>(); + + showProgressHandler.sendEmptyMessage(0); + gettingViewstate = true; + attempts ++; + + try { + if (guid != null && guid.length() > 0) { + params.put("wid", guid); + } else { + loadDataHandler.sendEmptyMessage(0); + return; + } + + final String page = base.request(false, "www.geocaching.com", "/track/log.aspx", "GET", params, false, false, false).getData(); + + viewstate = base.findViewstate(page, 0); + viewstate1 = base.findViewstate(page, 1); + + final ArrayList<Integer> typesPre = base.parseTypes(page); + if (typesPre.size() > 0) { + types.clear(); + types.addAll(typesPre); + } + typesPre.clear(); + + if (types.contains(typeSelected) == false) { + typeSelected = types.get(0); + setType(typeSelected); + warning.showToast(res.getString(R.string.info_log_type_changed)); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeotouch.loadData.run: " + e.toString()); + } + + loadDataHandler.sendEmptyMessage(0); + } + } + + private class postLog extends Thread { + Handler handler = null; + String tracking = null; + String log = null; + + public postLog(Handler handlerIn, String trackingIn, String logIn) { + handler = handlerIn; + tracking = trackingIn; + log = logIn; + } + + @Override + public void run() { + int ret = -1; + + ret = postLogFn(tracking, log); + + handler.sendEmptyMessage(ret); + } + } + + public int postLogFn(String tracking, String log) { + int status = -1; + + try { + if (tweetBox == null) tweetBox = (LinearLayout)findViewById(R.id.tweet_box); + if (tweetCheck == null) tweetCheck = (CheckBox)findViewById(R.id.tweet); + + status = base.postLogTrackable(guid, tracking, viewstate, viewstate1, typeSelected, date.get(Calendar.YEAR), (date.get(Calendar.MONTH ) + 1), date.get(Calendar.DATE), log); + + if ( + status == 1 && settings.twitter == 1 && + settings.tokenPublic != null && settings.tokenPublic.length() > 0 && settings.tokenSecret != null && settings.tokenSecret.length() > 0 && + tweetCheck.isChecked() == true && tweetBox.getVisibility() == View.VISIBLE + ) { + base.postTweetTrackable(app, settings, geocode); + } + + return status; + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeotouch.postLogFn: " + e.toString()); + } + + return 1000; + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-log-trackable", + activity, + "http://cgeo.carnero.cc/manual/" + ); + } catch (Exception e) { + // nothing + } + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgeotrackable.java b/src/cgeo/geocaching/cgeotrackable.java new file mode 100644 index 0000000..98b8cbc --- /dev/null +++ b/src/cgeo/geocaching/cgeotrackable.java @@ -0,0 +1,671 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import java.net.URLEncoder; +import java.util.Date; +import java.util.HashMap; +import android.os.Handler; +import android.os.Message; +import android.os.Bundle; +import android.util.Log; +import android.app.Activity; +import android.app.ProgressDialog; +import android.text.Html; +import android.text.method.LinkMovementMethod; +import android.view.ContextMenu; +import android.view.View; +import android.view.Menu; +import android.view.MenuItem; +import android.view.LayoutInflater; +import android.widget.ScrollView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.widget.ImageView; + +public class cgeotrackable extends Activity { + public cgTrackable trackable = null; + public String geocode = null; + public String name = null; + public String guid = null; + public String id = null; + private String contextMenuUser = null; + private Resources res = null; + private cgeoapplication app = null; + private Activity activity = null; + private LayoutInflater inflater = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private ProgressDialog waitDialog = null; + private Handler loadTrackableHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + RelativeLayout itemLayout; + TextView itemName; + TextView itemValue; + + if (trackable != null && trackable.errorRetrieve != 0) { + warning.showToast(res.getString(R.string.err_tb_details_download) + " " + cgBase.errorRetrieve.get(trackable.errorRetrieve) + "."); + + finish(); + return; + } + + if (trackable != null && trackable.error.length() > 0) { + warning.showToast(res.getString(R.string.err_tb_details_download) + " " + trackable.error + "."); + + finish(); + return; + } + + if (trackable == null) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + + if (geocode != null && geocode.length() > 0) { + warning.showToast(res.getString(R.string.err_tb_find) + " " + geocode + "."); + } else { + warning.showToast(res.getString(R.string.err_tb_find_that)); + } + + finish(); + return; + } + + try { + inflater = activity.getLayoutInflater(); + geocode = trackable.geocode.toUpperCase(); + + if (trackable.name != null && trackable.name.length() > 0) { + base.setTitle(activity, Html.fromHtml(trackable.name).toString()); + } else { + base.setTitle(activity, trackable.name.toUpperCase()); + } + + ((ScrollView) findViewById(R.id.details_list_box)).setVisibility(View.VISIBLE); + LinearLayout detailsList = (LinearLayout) findViewById(R.id.details_list); + + // actiobar icon + if (trackable.iconUrl != null && trackable.iconUrl.length() > 0) { + final tbIconHandler iconHandler = new tbIconHandler(((TextView) findViewById(R.id.actionbar_title))); + final tbIconThread iconThread = new tbIconThread(trackable.iconUrl, iconHandler); + iconThread.start(); + } + + // trackable name + itemLayout = (RelativeLayout)inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.trackable_name)); + if (trackable.name != null) { + itemValue.setText(Html.fromHtml(trackable.name).toString()); + } else { + itemValue.setText(res.getString(R.string.trackable_unknown)); + } + detailsList.addView(itemLayout); + + // trackable type + itemLayout = (RelativeLayout)inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + String tbType = null; + if (trackable.type != null && trackable.type.length() > 0) { + tbType = Html.fromHtml(trackable.type).toString(); + } else { + tbType = res.getString(R.string.trackable_unknown); + } + itemName.setText(res.getString(R.string.trackable_type)); + itemValue.setText(tbType); + detailsList.addView(itemLayout); + + // trackable geocode + itemLayout = (RelativeLayout)inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.trackable_code)); + itemValue.setText(trackable.geocode.toUpperCase()); + detailsList.addView(itemLayout); + + // trackable owner + itemLayout = (RelativeLayout)inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.trackable_owner)); + if (trackable.owner != null) { + itemValue.setText(Html.fromHtml(trackable.owner), TextView.BufferType.SPANNABLE); + itemLayout.setOnClickListener(new userActions()); + } else { + itemValue.setText(res.getString(R.string.trackable_unknown)); + } + detailsList.addView(itemLayout); + + // trackable spotted + if ( + (trackable.spottedName != null && trackable.spottedName.length() > 0) || + trackable.spottedType == cgTrackable.SPOTTED_UNKNOWN || + trackable.spottedType == cgTrackable.SPOTTED_OWNER + ) { + itemLayout = (RelativeLayout)inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.trackable_spotted)); + String text = null; + + if (trackable.spottedType == cgTrackable.SPOTTED_CACHE) { + text = res.getString(R.string.trackable_spotted_in_cache) + " " + Html.fromHtml(trackable.spottedName).toString(); + } else if (trackable.spottedType == cgTrackable.SPOTTED_USER) { + text = res.getString(R.string.trackable_spotted_at_user) + " " + Html.fromHtml(trackable.spottedName).toString(); + } else if (trackable.spottedType == cgTrackable.SPOTTED_UNKNOWN) { + text = res.getString(R.string.trackable_spotted_unknown_location); + } else if (trackable.spottedType == cgTrackable.SPOTTED_OWNER) { + text = res.getString(R.string.trackable_spotted_owner); + } else { + text = "N/A"; + } + + itemValue.setText(text); + itemLayout.setClickable(true); + if (cgTrackable.SPOTTED_CACHE == trackable.spottedType) { + itemLayout.setOnClickListener(new View.OnClickListener() { + public void onClick(View arg0) { + Intent cacheIntent = new Intent(activity, cgeodetail.class); + cacheIntent.putExtra("guid", (String) trackable.spottedGuid); + cacheIntent.putExtra("name", (String) trackable.spottedName); + activity.startActivity(cacheIntent); + } + }); + } else if (cgTrackable.SPOTTED_USER == trackable.spottedType) { + itemLayout.setOnClickListener(new userActions()); + //activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/profile/?guid=" + trackable.spottedGuid))); + } + + detailsList.addView(itemLayout); + } + + // trackable origin + if (trackable.origin != null && trackable.origin.length() > 0) { + itemLayout = (RelativeLayout)inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.trackable_origin)); + itemValue.setText(Html.fromHtml(trackable.origin), TextView.BufferType.SPANNABLE); + detailsList.addView(itemLayout); + } + + // trackable released + if (trackable.released != null) { + itemLayout = (RelativeLayout)inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.trackable_released)); + itemValue.setText(cgBase.dateOut.format(trackable.released)); + detailsList.addView(itemLayout); + } + + // trackable distance + if (trackable.distance != null) { + itemLayout = (RelativeLayout)inflater.inflate(R.layout.cache_item, null); + itemName = (TextView) itemLayout.findViewById(R.id.name); + itemValue = (TextView) itemLayout.findViewById(R.id.value); + + itemName.setText(res.getString(R.string.trackable_distance)); + itemValue.setText(base.getHumanDistance(trackable.distance)); + detailsList.addView(itemLayout); + } + + + // trackable goal + if (trackable.goal != null && trackable.goal.length() > 0) { + ((LinearLayout) findViewById(R.id.goal_box)).setVisibility(View.VISIBLE); + TextView descView = (TextView) findViewById(R.id.goal); + descView.setVisibility(View.VISIBLE); + descView.setText(Html.fromHtml(trackable.goal, new cgHtmlImg(activity, settings, geocode, true, 0, false), null), TextView.BufferType.SPANNABLE); + descView.setMovementMethod(LinkMovementMethod.getInstance()); + } + + // trackable details + if (trackable.details != null && trackable.details.length() > 0) { + ((LinearLayout) findViewById(R.id.details_box)).setVisibility(View.VISIBLE); + TextView descView = (TextView) findViewById(R.id.details); + descView.setVisibility(View.VISIBLE); + descView.setText(Html.fromHtml(trackable.details, new cgHtmlImg(activity, settings, geocode, true, 0, false), null), TextView.BufferType.SPANNABLE); + descView.setMovementMethod(LinkMovementMethod.getInstance()); + } + + // trackable image + if (trackable.image != null && trackable.image.length() > 0) { + ((LinearLayout) findViewById(R.id.image_box)).setVisibility(View.VISIBLE); + LinearLayout imgView = (LinearLayout) findViewById(R.id.image); + + final ImageView trackableImage = (ImageView) inflater.inflate(R.layout.trackable_image, null); + + trackableImage.setImageResource(R.drawable.image_not_loaded); + trackableImage.setClickable(true); + trackableImage.setOnClickListener(new View.OnClickListener() { + + public void onClick(View arg0) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(trackable.image))); + } + }); + + // try to load image + final Handler handler = new Handler() { + + @Override + public void handleMessage(Message message) { + BitmapDrawable image = (BitmapDrawable) message.obj; + if (image != null) { + trackableImage.setImageDrawable((BitmapDrawable) message.obj); + } + } + }; + + new Thread() { + + @Override + public void run() { + BitmapDrawable image = null; + try { + cgHtmlImg imgGetter = new cgHtmlImg(activity, settings, geocode, true, 0, false); + + image = imgGetter.getDrawable(trackable.image); + Message message = handler.obtainMessage(0, image); + handler.sendMessage(message); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeospoilers.onCreate.onClick.run: " + e.toString()); + } + } + }.start(); + + imgView.addView(trackableImage); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeotrackable.loadTrackableHandler: " + e.toString() + e.getStackTrace()); + } + + displayLogs(); + + if (waitDialog != null) { + waitDialog.dismiss(); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.trackable_detail); + base.setTitle(activity, res.getString(R.string.trackable)); + + // google analytics + base.sendAnal(activity, "/trackable/detail"); + + // get parameters + Bundle extras = getIntent().getExtras(); + Uri uri = getIntent().getData(); + + // try to get data from extras + if (extras != null) { + geocode = extras.getString("geocode"); + name = extras.getString("name"); + guid = extras.getString("guid"); + id = extras.getString("id"); + } + + // try to get data from URI + if (geocode == null && guid == null && id == null && uri != null) { + String uriHost = uri.getHost().toLowerCase(); + if (uriHost.contains("geocaching.com") == true) { + geocode = uri.getQueryParameter("tracker"); + guid = uri.getQueryParameter("guid"); + id = uri.getQueryParameter("id"); + + if (geocode != null && geocode.length() > 0) { + geocode = geocode.toUpperCase(); + guid = null; + id = null; + } else if (guid != null && guid.length() > 0) { + geocode = null; + guid = guid.toLowerCase(); + id = null; + } else if (id != null && id.length() > 0) { + geocode = null; + guid = null; + id = id.toLowerCase(); + } else { + warning.showToast(res.getString(R.string.err_tb_details_open)); + finish(); + return; + } + } else if (uriHost.contains("coord.info") == true) { + String uriPath = uri.getPath().toLowerCase(); + if (uriPath != null && uriPath.startsWith("/tb") == true) { + geocode = uriPath.substring(1).toUpperCase(); + guid = null; + id = null; + } else { + warning.showToast(res.getString(R.string.err_tb_details_open)); + finish(); + return; + } + } + } + + // no given data + if (geocode == null && guid == null && id == null) { + warning.showToast(res.getString(R.string.err_tb_display)); + finish(); + return; + } + + if (name != null && name.length() > 0) { + waitDialog = ProgressDialog.show(this, Html.fromHtml(name).toString(), res.getString(R.string.trackable_details_loading), true); + } else if (geocode != null && geocode.length() > 0) { + waitDialog = ProgressDialog.show(this, geocode.toUpperCase(), res.getString(R.string.trackable_details_loading), true); + } else { + waitDialog = ProgressDialog.show(this, res.getString(R.string.trackable), res.getString(R.string.trackable_details_loading), true); + } + waitDialog.setCancelable(true); + + loadTrackable thread; + thread = new loadTrackable(loadTrackableHandler, geocode, guid, id); + thread.start(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo info) { + super.onCreateContextMenu(menu, view, info); + final int viewId = view.getId(); + + if (viewId == R.id.author) { // Log item author + contextMenuUser = ((TextView)view).getText().toString(); + } else { // Trackable owner, and user holding trackable now + RelativeLayout itemLayout = (RelativeLayout)view; + TextView itemName = (TextView) itemLayout.findViewById(R.id.name); + + String selectedName = itemName.getText().toString(); + if (selectedName.equals(res.getString(R.string.trackable_owner))) { + contextMenuUser = trackable.owner; + } else if (selectedName.equals(res.getString(R.string.trackable_spotted))) { + contextMenuUser = trackable.spottedName; + } + } + + menu.setHeaderTitle(res.getString(R.string.user_menu_title) + " " + contextMenuUser); + menu.add(viewId, 1, 0, res.getString(R.string.user_menu_view_hidden)); + menu.add(viewId, 2, 0, res.getString(R.string.user_menu_view_found)); + menu.add(viewId, 3, 0, res.getString(R.string.user_menu_open_browser)); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + final int id = item.getItemId(); + + if (id == 1) { + final Intent cachesIntent = new Intent(activity, cgeocaches.class); + + cachesIntent.putExtra("type", "owner"); + cachesIntent.putExtra("username", contextMenuUser); + cachesIntent.putExtra("cachetype", settings.cacheType); + + activity.startActivity(cachesIntent); + + return true; + } else if (id == 2) { + final Intent cachesIntent = new Intent(activity, cgeocaches.class); + + cachesIntent.putExtra("type", "username"); + cachesIntent.putExtra("username", contextMenuUser); + cachesIntent.putExtra("cachetype", settings.cacheType); + + activity.startActivity(cachesIntent); + + return true; + } else if (id == 3) { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/profile/?u=" + URLEncoder.encode(contextMenuUser)))); + + return true; + } + return false; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, 1, 0, res.getString(R.string.trackable_log_touch)).setIcon(android.R.drawable.ic_menu_agenda); // log touch + + menu.add(0, 2, 0, res.getString(R.string.trackable_browser_open)).setIcon(android.R.drawable.ic_menu_info_details); // browser + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case 1: + logTouch(); + return true; + case 2: + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.geocaching.com/track/details.aspx?tracker=" + trackable.geocode))); + return true; + } + + return false; + } + + private class loadTrackable extends Thread { + + private Handler handler = null; + private String geocode = null; + private String guid = null; + private String id = null; + + public loadTrackable(Handler handlerIn, String geocodeIn, String guidIn, String idIn) { + handler = handlerIn; + geocode = geocodeIn; + guid = guidIn; + id = idIn; + + if (geocode == null && guid == null && id == null) { + warning.showToast(res.getString(R.string.err_tb_forgot)); + + stop(); + finish(); + return; + } + } + + @Override + public void run() { + loadTrackableFn(geocode, guid, id); + handler.sendMessage(new Message()); + } + } + + public void loadTrackableFn(String geocode, String guid, String id) { + HashMap<String, String> params = new HashMap<String, String>(); + if (geocode != null && geocode.length() > 0) { + params.put("geocode", geocode); + } else if (guid != null && guid.length() > 0) { + params.put("guid", guid); + } else if (id != null && id.length() > 0) { + params.put("id", id); + } else { + return; + } + + trackable = base.searchTrackable(params); + } + + private void displayLogs() { + // trackable logs + LinearLayout listView = (LinearLayout) findViewById(R.id.log_list); + listView.removeAllViews(); + + RelativeLayout rowView; + + if (trackable != null && trackable.logs != null) { + for (cgLog log : trackable.logs) { + rowView = (RelativeLayout) inflater.inflate(R.layout.trackable_logitem, null); + + if (log.date > 0) { + final Date logDate = new Date(log.date); + ((TextView) rowView.findViewById(R.id.added)).setText(cgBase.dateOutShort.format(logDate)); + } + + + if (cgBase.logTypes1.containsKey(log.type) == true) { + ((TextView) rowView.findViewById(R.id.type)).setText(cgBase.logTypes1.get(log.type)); + } else { + ((TextView) rowView.findViewById(R.id.type)).setText(cgBase.logTypes1.get(4)); // note if type is unknown + } + ((TextView) rowView.findViewById(R.id.author)).setText(Html.fromHtml(log.author), TextView.BufferType.SPANNABLE); + + if (log.cacheName == null || log.cacheName.length() == 0) { + ((TextView) rowView.findViewById(R.id.location)).setVisibility(View.GONE); + } else { + ((TextView) rowView.findViewById(R.id.location)).setText(Html.fromHtml(log.cacheName)); + final String cacheGuid = log.cacheGuid; + final String cacheName = log.cacheName; + ((TextView) rowView.findViewById(R.id.location)).setOnClickListener(new View.OnClickListener() { + public void onClick(View arg0) { + Intent cacheIntent = new Intent(activity, cgeodetail.class); + cacheIntent.putExtra("guid", (String) cacheGuid); + cacheIntent.putExtra("name", (String) Html.fromHtml(cacheName).toString()); + activity.startActivity(cacheIntent); + } + }); + } + + ((TextView) rowView.findViewById(R.id.log)).setText(Html.fromHtml(log.log, new cgHtmlImg(activity, settings, null, false, 0, false), null), TextView.BufferType.SPANNABLE); + + ((TextView) rowView.findViewById(R.id.author)).setOnClickListener(new userActions()); + listView.addView(rowView); + } + + if (trackable.logs.size() > 0) { + ((LinearLayout) findViewById(R.id.log_box)).setVisibility(View.VISIBLE); + } + } + } + + private class userActions implements View.OnClickListener { + + public void onClick(View view) { + if (view == null) { + return; + } + + try { + registerForContextMenu(view); + openContextMenu(view); + } catch (Exception e) { + // nothing + } + } + } + + private void logTouch() { + Intent logTouchIntent = new Intent(activity, cgeotouch.class); + logTouchIntent.putExtra("geocode", trackable.geocode.toUpperCase()); + logTouchIntent.putExtra("guid", trackable.guid); + activity.startActivity(logTouchIntent); + } + + private class tbIconThread extends Thread { + String url = null; + Handler handler = null; + + public tbIconThread(String urlIn, Handler handlerIn) { + url = urlIn; + handler = handlerIn; + } + + @Override + public void run() { + if (url == null || handler == null) { + return; + } + + BitmapDrawable image = null; + try { + cgHtmlImg imgGetter = new cgHtmlImg(activity, settings, trackable.geocode, false, 0, false); + + image = imgGetter.getDrawable(url); + Message message = handler.obtainMessage(0, image); + handler.sendMessage(message); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeotrackable.tbIconThread.run: " + e.toString()); + } + } + } + + private class tbIconHandler extends Handler { + TextView view = null; + + public tbIconHandler(TextView viewIn) { + view = viewIn; + } + + @Override + public void handleMessage(Message message) { + BitmapDrawable image = (BitmapDrawable) message.obj; + if (image != null && view != null) { + view.setCompoundDrawablesWithIntrinsicBounds((Drawable) image, null, null, null); + } + } + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-trackable-details", + activity, + "http://cgeo.carnero.cc/manual/" + ); + } catch (Exception e) { + // nothing + } + } +} diff --git a/src/cgeo/geocaching/cgeotrackables.java b/src/cgeo/geocaching/cgeotrackables.java new file mode 100644 index 0000000..d414e16 --- /dev/null +++ b/src/cgeo/geocaching/cgeotrackables.java @@ -0,0 +1,186 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import java.util.ArrayList; +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.text.Html; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; + +public class cgeotrackables extends Activity { + private ArrayList<cgTrackable> trackables = new ArrayList<cgTrackable>(); + private String geocode = null; + private cgeoapplication app = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private Activity activity = null; + private LayoutInflater inflater = null; + private LinearLayout addList = null; + private ProgressDialog waitDialog = null; + private Handler loadInventoryHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (inflater == null) { + inflater = activity.getLayoutInflater(); + } + + if (addList == null) { + addList = (LinearLayout) findViewById(R.id.trackable_list); + } + + if (trackables.isEmpty()) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + + warning.showToast("Sorry, c:geo failed to load cache inventory."); + + finish(); + return; + } else { + LinearLayout oneTbPre = null; + for (cgTrackable trackable : trackables) { + oneTbPre = (LinearLayout) inflater.inflate(R.layout.trackable_button, null); + + Button oneTb = (Button) oneTbPre.findViewById(R.id.button); + + if (trackable.name != null) { + oneTb.setText(Html.fromHtml(trackable.name).toString()); + } else { + oneTb.setText("some trackable"); + } + oneTb.setClickable(true); + oneTb.setOnClickListener(new buttonListener(trackable.guid, trackable.geocode, trackable.name)); + addList.addView(oneTbPre); + } + } + + if (waitDialog != null) { + waitDialog.dismiss(); + } + } catch (Exception e) { + if (waitDialog != null) { + waitDialog.dismiss(); + } + Log.e(cgSettings.tag, "cgeotrackables.loadInventoryHandler: " + e.toString()); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.trackables); + base.setTitle(activity, "Trackables"); + + // google analytics + base.sendAnal(activity, "/trackable/list"); + + // get parameters + Bundle extras = getIntent().getExtras(); + + // try to get data from extras + if (extras != null) { + geocode = extras.getString("geocode"); + } + + if (geocode == null) { + warning.showToast("Sorry, c:geo forgot for what cache you want to load trackables."); + finish(); + return; + } + + waitDialog = ProgressDialog.show(this, null, "loading cache inventory...", true); + waitDialog.setCancelable(true); + + (new loadInventory()).start(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + private class loadInventory extends Thread { + + @Override + public void run() { + try { + trackables = app.loadInventory(geocode); + + loadInventoryHandler.sendMessage(new Message()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeotrackables.loadInventory.run: " + e.toString()); + } + } + } + + private class buttonListener implements View.OnClickListener { + + private String guid = null; + private String geocode = null; + private String name = null; + + public buttonListener(String guidIn, String geocodeIn, String nameIn) { + guid = guidIn; + geocode = geocodeIn; + name = nameIn; + } + + public void onClick(View arg0) { + Intent trackableIntent = new Intent(activity, cgeotrackable.class); + trackableIntent.putExtra("guid", guid); + trackableIntent.putExtra("geocode", geocode); + trackableIntent.putExtra("name", name); + activity.startActivity(trackableIntent); + + finish(); + return; + } + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-trackable-list", + activity, + "http://cgeo.carnero.cc/manual/" + ); + } catch (Exception e) { + // nothing + } + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgeovisit.java b/src/cgeo/geocaching/cgeovisit.java new file mode 100644 index 0000000..d99b581 --- /dev/null +++ b/src/cgeo/geocaching/cgeovisit.java @@ -0,0 +1,912 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import android.os.Bundle; +import android.app.Activity; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Handler; +import android.os.Message; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.util.Log; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; + +public class cgeovisit extends cgLogForm { + private cgeoapplication app = null; + private Activity activity = null; + private Resources res = null; + private LayoutInflater inflater = null; + private cgBase base = null; + private cgSettings settings = null; + private cgWarning warning = null; + private cgCache cache = null; + private ArrayList<Integer> types = new ArrayList<Integer>(); + private ProgressDialog waitDialog = null; + private String cacheid = null; + private String geocode = null; + private String text = null; + private boolean alreadyFound = false; + private String viewstate = null; + private String viewstate1 = null; + private Boolean gettingViewstate = true; + private ArrayList<cgTrackableLog> trackables = null; + private Calendar date = Calendar.getInstance(); + private int typeSelected = 1; + private int attempts = 0; + private boolean progressBar = false; + private Button post = null; + private Button save = null; + private Button clear = null; + private CheckBox tweetCheck = null; + private LinearLayout tweetBox = null; + private int rating = 0; + private boolean tbChanged = false; + // constants + private final static int LOG_SIGNATURE = 0x1; + private final static int LOG_TIME = 0x2; + private final static int LOG_DATE = 0x4; + private final static int LOG_DATE_TIME = 0x6; + private final static int LOG_SIGNATURE_DATE_TIME = 0x7; + // handlers + private Handler showProgressHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (progressBar == true) { + base.showProgress(activity, true); + } + } + }; + private Handler loadDataHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + if (types.contains(typeSelected) == false) { + typeSelected = types.get(0); + setType(typeSelected); + + warning.showToast(res.getString(R.string.info_log_type_changed)); + } + + if ((viewstate == null || viewstate.length() == 0) && attempts < 2) { + warning.showToast(res.getString(R.string.err_log_load_data_again)); + + loadData thread; + thread = new loadData(cacheid); + thread.start(); + + return; + } else if ((viewstate == null || viewstate.length() == 0) && attempts >= 2) { + warning.showToast(res.getString(R.string.err_log_load_data)); + base.showProgress(activity, false); + + return; + } + + gettingViewstate = false; // we're done, user can post log + + if (post == null) { + post = (Button) findViewById(R.id.post); + } + post.setEnabled(true); + post.setOnClickListener(new postListener()); + + // add trackables + if (trackables != null && trackables.isEmpty() == false) { + if (inflater == null) { + inflater = activity.getLayoutInflater(); + } + + final LinearLayout inventoryView = (LinearLayout) findViewById(R.id.inventory); + inventoryView.removeAllViews(); + + for (cgTrackableLog tb : trackables) { + LinearLayout inventoryItem = (LinearLayout) inflater.inflate(R.layout.visit_trackable, null); + + ((TextView) inventoryItem.findViewById(R.id.trackcode)).setText(tb.trackCode); + ((TextView) inventoryItem.findViewById(R.id.name)).setText(tb.name); + ((TextView) inventoryItem.findViewById(R.id.action)).setText(cgBase.logTypesTrackable.get(0)); + + inventoryItem.setId(tb.id); + final String tbCode = tb.trackCode; + inventoryItem.setClickable(true); + registerForContextMenu(inventoryItem); + inventoryItem.findViewById(R.id.info).setOnClickListener(new View.OnClickListener() { + + public void onClick(View view) { + final Intent trackablesIntent = new Intent(activity, cgeotrackable.class); + trackablesIntent.putExtra("geocode", tbCode); + activity.startActivity(trackablesIntent); + } + }); + inventoryItem.findViewById(R.id.action).setOnClickListener(new View.OnClickListener() { + + public void onClick(View view) { + openContextMenu(view); + } + }); + + inventoryView.addView(inventoryItem); + } + + if (inventoryView.getChildCount() > 0) { + ((LinearLayout) findViewById(R.id.inventory_box)).setVisibility(View.VISIBLE); + } + if (inventoryView.getChildCount() > 1 && inventoryView.getChildCount() <= 20) { + final LinearLayout inventoryChangeAllView = (LinearLayout) findViewById(R.id.inventory_changeall); + + Button changeButton = (Button) inventoryChangeAllView.findViewById(R.id.changebutton); + registerForContextMenu(changeButton); + changeButton.setOnClickListener(new View.OnClickListener() { + + public void onClick(View view) { + openContextMenu(view); + } + }); + + ((LinearLayout) findViewById(R.id.inventory_changeall)).setVisibility(View.VISIBLE); + } + } + + base.showProgress(activity, false); + } + }; + + private Handler postLogHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + if (msg.what == 1) { + warning.showToast(res.getString(R.string.info_log_posted)); + + if (waitDialog != null) { + waitDialog.dismiss(); + } + + finish(); + return; + } else if (msg.what == 2) { + warning.showToast(res.getString(R.string.info_log_saved)); + + if (waitDialog != null) { + waitDialog.dismiss(); + } + + finish(); + return; + } else if (msg.what >= 1000) { + if (msg.what == 1001) { + warning.showToast(res.getString(R.string.warn_log_text_fill)); + } else if (msg.what == 1002) { + warning.showToast(res.getString(R.string.err_log_failed_server)); + } else { + warning.showToast(res.getString(R.string.err_log_post_failed)); + } + } else { + if (cgBase.errorRetrieve.get(msg.what) != null) { + warning.showToast(res.getString(R.string.err_log_post_failed_because) + " " + cgBase.errorRetrieve.get(msg.what) + "."); + } else { + warning.showToast(res.getString(R.string.err_log_post_failed)); + } + } + + if (waitDialog != null) { + waitDialog.dismiss(); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.visit); + base.setTitle(activity, res.getString(R.string.log_new_log)); + + // google analytics + base.sendAnal(activity, "/visit"); + + // get parameters + Bundle extras = getIntent().getExtras(); + if (extras != null) { + cacheid = extras.getString("id"); + geocode = extras.getString("geocode"); + text = extras.getString("text"); + alreadyFound = extras.getBoolean("found"); + } + + if ((cacheid == null || cacheid.length() == 0) && geocode != null && geocode.length() > 0) { + cacheid = app.getCacheid(geocode); + } + if ((geocode == null || geocode.length() == 0) && cacheid != null && cacheid.length() > 0) { + geocode = app.getGeocode(cacheid); + } + + cache = app.getCacheByGeocode(geocode); + + if (cache.name != null && cache.name.length() > 0) { + base.setTitle(activity, res.getString(R.string.log_new_log) + " " + cache.name); + } else { + base.setTitle(activity, res.getString(R.string.log_new_log) + " " + cache.geocode.toUpperCase()); + } + + app.setAction(geocode); + + if (cache == null) { + warning.showToast(res.getString(R.string.err_detail_cache_forgot_visit)); + + finish(); + return; + } + + init(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + init(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + SubMenu subMenu = null; + + subMenu = menu.addSubMenu(0, 0, 0, res.getString(R.string.log_add)).setIcon(android.R.drawable.ic_menu_add); + subMenu.add(0, LOG_DATE_TIME, 0, res.getString(R.string.log_date_time)); + subMenu.add(0, LOG_DATE, 0, res.getString(R.string.log_date)); + subMenu.add(0, LOG_TIME, 0, res.getString(R.string.log_time)); + subMenu.add(0, LOG_SIGNATURE, 0, res.getString(R.string.init_signature)); + subMenu.add(0, LOG_SIGNATURE_DATE_TIME, 0, res.getString(R.string.log_date_time) + " & " + res.getString(R.string.init_signature)); + + subMenu = menu.addSubMenu(0, 9, 0, res.getString(R.string.log_rating)).setIcon(android.R.drawable.ic_menu_sort_by_size); + subMenu.add(0, 10, 0, res.getString(R.string.log_no_rating)); + subMenu.add(0, 15, 0, res.getString(R.string.log_stars_5)); + subMenu.add(0, 14, 0, res.getString(R.string.log_stars_4)); + subMenu.add(0, 13, 0, res.getString(R.string.log_stars_3)); + subMenu.add(0, 12, 0, res.getString(R.string.log_stars_2)); + subMenu.add(0, 11, 0, res.getString(R.string.log_stars_1)); + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + if (settings.getSignature() == null) { + menu.findItem(LOG_SIGNATURE).setVisible(false); + menu.findItem(LOG_SIGNATURE_DATE_TIME).setVisible(false); + } else { + menu.findItem(LOG_SIGNATURE).setVisible(true); + menu.findItem(LOG_SIGNATURE_DATE_TIME).setVisible(true); + } + + if (settings.isGCvoteLogin() && typeSelected == cgBase.LOG_FOUND_IT && cache.guid != null && cache.guid.length() > 0) { + menu.findItem(9).setVisible(true); + } else { + menu.findItem(9).setVisible(false); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + + if ((id >= LOG_SIGNATURE && id <= LOG_SIGNATURE_DATE_TIME)) { + addSignature(id); + + return true; + } else if (id >= 10 && id <= 15) { + rating = id - 10; + + if (post == null) { + post = (Button) findViewById(R.id.post); + } + if (rating == 0) { + post.setText(res.getString(R.string.log_post_no_rate)); + } else { + post.setText(res.getString(R.string.log_post_rate) + " " + rating + "*"); + } + } + + return false; + } + + public void addSignature(int id) { + EditText text = null; + String textContent = null; + String dateString = null; + String timeString = null; + StringBuilder addText = new StringBuilder(); + + text = (EditText) findViewById(R.id.log); + textContent = text.getText().toString(); + dateString = cgBase.dateOut.format(new Date()); + timeString = cgBase.timeOut.format(new Date()); + + if ((id & LOG_DATE) == LOG_DATE) { + addText.append(dateString); + if ((id & LOG_TIME) == LOG_TIME) { + addText.append(" | "); + } + } + + if ((id & LOG_TIME) == LOG_TIME) { + addText.append(timeString); + } + + if ((id & LOG_SIGNATURE) == LOG_SIGNATURE && settings.getSignature() != null) { + String findCount = ""; + if (addText.length() > 0) { + addText.append("\n"); + } + + if (settings.getSignature().contains("[NUMBER]") == true) { + final HashMap<String, String> params = new HashMap<String, String>(); + final String page = base.request(false, "www.geocaching.com", "/my/", "GET", params, false, false, false).getData(); + int current = base.parseFindCount(page); + + if (current >= 0) { + findCount = "" + (current + 1); + } + } + + String signature = settings.getSignature() + .replaceAll("\\[DATE\\]", dateString) + .replaceAll("\\[TIME\\]", timeString) + .replaceAll("\\[USER\\]", settings.getUsername()) + .replaceAll("\\[NUMBER\\]", findCount); + + addText.append(signature); + } + + final String addTextDone; + if (textContent.length() > 0 && addText.length() > 0 ) { + addTextDone = textContent + "\n" + addText.toString(); + } else { + addTextDone = textContent + addText.toString(); + } + + text.setText(addTextDone, TextView.BufferType.NORMAL); + text.setSelection(text.getText().toString().length()); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo info) { + super.onCreateContextMenu(menu, view, info); + final int viewId = view.getId(); + + if (viewId == R.id.type) { + for (final int typeOne : types) { + menu.add(viewId, typeOne, 0, cgBase.logTypes2.get(typeOne)); + Log.w(cgSettings.tag, "Addig " + typeOne + " " + cgBase.logTypes2.get(typeOne)); + } + } else if (viewId == R.id.changebutton) { + final int textId = ((TextView) findViewById(viewId)).getId(); + + menu.setHeaderTitle(res.getString(R.string.log_tb_changeall)); + for (final int logTbAction : cgBase.logTypesTrackable.keySet()) { + menu.add(textId, logTbAction, 0, cgBase.logTypesTrackable.get(logTbAction)); + } + } else { + final int realViewId = ((LinearLayout) findViewById(viewId)).getId(); + + for (final cgTrackableLog tb : trackables) { + if (tb.id == realViewId) { + menu.setHeaderTitle(tb.name); + } + } + for (final int logTbAction : cgBase.logTypesTrackable.keySet()) { + menu.add(realViewId, logTbAction, 0, cgBase.logTypesTrackable.get(logTbAction)); + } + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + final int group = item.getGroupId(); + final int id = item.getItemId(); + + if (group == R.id.type) { + setType(id); + + return true; + } else if (group == R.id.changebutton) { + try { + final String logTbAction = cgBase.logTypesTrackable.get(id); + if (logTbAction != null) { + final LinearLayout inventView = (LinearLayout) findViewById(R.id.inventory); + for (int count = 0; count < inventView.getChildCount(); count++) { + final LinearLayout tbView = (LinearLayout) inventView.getChildAt(count); + if (tbView == null) { + return false; + } + + final TextView tbText = (TextView) tbView.findViewById(R.id.action); + if (tbText == null) { + return false; + } + tbText.setText(logTbAction); + } + for (cgTrackableLog tb : trackables) { + tb.action = id; + } + tbChanged = true; + return true; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeovisit.onContextItemSelected: " + e.toString()); + } + } else { + try { + final String logTbAction = cgBase.logTypesTrackable.get(id); + if (logTbAction != null) { + final LinearLayout tbView = (LinearLayout) findViewById(group); + if (tbView == null) { + return false; + } + + final TextView tbText = (TextView) tbView.findViewById(R.id.action); + if (tbText == null) { + return false; + } + + for (cgTrackableLog tb : trackables) { + if (tb.id == group) { + tbChanged = true; + + tb.action = id; + tbText.setText(logTbAction); + + Log.i(cgSettings.tag, "Trackable " + tb.trackCode + " (" + tb.name + ") has new action: #" + id); + } + } + + return true; + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeovisit.onContextItemSelected: " + e.toString()); + } + } + + return false; + } + + public void init() { + if (geocode != null) { + app.setAction(geocode); + } + + types.clear(); + + if (cache.type.equals("event") || cache.type.equals("mega") || cache.type.equals("cito") || cache.type.equals("lostfound")) { + types.add(cgBase.LOG_WILL_ATTEND); + types.add(cgBase.LOG_NOTE); + types.add(cgBase.LOG_ATTENDED); + types.add(cgBase.LOG_NEEDS_ARCHIVE); + } else if (cache.type.equals("earth")) { + types.add(cgBase.LOG_FOUND_IT); + types.add(cgBase.LOG_DIDNT_FIND_IT); + types.add(cgBase.LOG_NOTE); + types.add(cgBase.LOG_NEEDS_MAINTENANCE); + types.add(cgBase.LOG_NEEDS_ARCHIVE); + } else if (cache.type.equals("webcam")) { + types.add(cgBase.LOG_WEBCAM_PHOTO_TAKEN); + types.add(cgBase.LOG_DIDNT_FIND_IT); + types.add(cgBase.LOG_NOTE); + types.add(cgBase.LOG_NEEDS_ARCHIVE); + types.add(cgBase.LOG_NEEDS_MAINTENANCE); + } else { + types.add(cgBase.LOG_FOUND_IT); + types.add(cgBase.LOG_DIDNT_FIND_IT); + types.add(cgBase.LOG_NOTE); + types.add(cgBase.LOG_NEEDS_ARCHIVE); + types.add(cgBase.LOG_NEEDS_MAINTENANCE); + } + if (cache.owner.equalsIgnoreCase(settings.getUsername()) == true) { + types.add(cgBase.LOG_OWNER_MAINTENANCE); + types.add(cgBase.LOG_TEMP_DISABLE_LISTING); + types.add(cgBase.LOG_ENABLE_LISTING); + types.add(cgBase.LOG_ARCHIVE); + types.remove(new Integer(cgBase.LOG_UPDATE_COORDINATES)); + if (cache.type.equals("event") || cache.type.equals("mega") || cache.type.equals("cito") || cache.type.equals("lostfound")) { + types.add(cgBase.LOG_ANNOUNCEMENT); + } + } + + final cgLog log = app.loadLogOffline(geocode); + if (log != null) { + typeSelected = log.type; + date.setTime(new Date(log.date)); + text = log.log; + if (typeSelected == cgBase.LOG_FOUND_IT && settings.isGCvoteLogin() == true) { + if (post == null) { + post = (Button) findViewById(R.id.post); + } + post.setText(res.getString(R.string.log_post_no_rate)); + } + } else if (settings.getSignature() != null && settings.getSignature().length() > 0) { + addSignature(LOG_SIGNATURE); + } + + if (types.contains(typeSelected) == false) { + if (alreadyFound == true) { + typeSelected = cgBase.LOG_NOTE; + } else { + typeSelected = types.get(0); + } + setType(typeSelected); + } + + Button typeButton = (Button) findViewById(R.id.type); + registerForContextMenu(typeButton); + typeButton.setText(cgBase.logTypes2.get(typeSelected)); + typeButton.setOnClickListener(new View.OnClickListener() { + + public void onClick(View view) { + openContextMenu(view); + } + }); + + Button dateButton = (Button) findViewById(R.id.date); + dateButton.setText(cgBase.dateOutShort.format(date.getTime())); + dateButton.setOnClickListener(new cgeovisitDateListener()); + + EditText logView = (EditText) findViewById(R.id.log); + if (logView.getText().length() == 0 && text != null && text.length() > 0) { + logView.setText(text); + } + + + if (tweetBox == null) { + tweetBox = (LinearLayout) findViewById(R.id.tweet_box); + } + if (tweetCheck == null) { + tweetCheck = (CheckBox) findViewById(R.id.tweet); + } + tweetCheck.setChecked(true); + + if (post == null) { + post = (Button) findViewById(R.id.post); + } + if (viewstate == null || viewstate.length() == 0) { + post.setEnabled(false); + post.setOnTouchListener(null); + post.setOnClickListener(null); + + loadData thread; + thread = new loadData(cacheid); + thread.start(); + } else { + post.setEnabled(true); + post.setOnClickListener(new postListener()); + } + + if (save == null) { + save = (Button) findViewById(R.id.save); + } + save.setOnClickListener(new saveListener()); + + if (clear == null) { + clear = (Button) findViewById(R.id.clear); + } + clear.setOnClickListener(new clearListener()); + } + + public void setDate(Calendar dateIn) { + date = dateIn; + + final Button dateButton = (Button) findViewById(R.id.date); + dateButton.setText(cgBase.dateOutShort.format(date.getTime())); + } + + public void setType(int type) { + final Button typeButton = (Button) findViewById(R.id.type); + + if (cgBase.logTypes2.get(type) != null) { + typeSelected = type; + } + if (cgBase.logTypes2.get(typeSelected) == null) { + typeSelected = 1; + } + typeButton.setText(cgBase.logTypes2.get(typeSelected)); + + if (tweetBox == null) { + tweetBox = (LinearLayout) findViewById(R.id.tweet_box); + } + + if (type == 2 && tbChanged == false) { + // TODO: change action + } else if (type != 2 && tbChanged == false) { + // TODO: change action + } + + if (type == cgBase.LOG_FOUND_IT && settings.twitter == 1) { + tweetBox.setVisibility(View.VISIBLE); + } else { + tweetBox.setVisibility(View.GONE); + } + + if (post == null) { + post = (Button) findViewById(R.id.post); + } + + if (type == cgBase.LOG_FOUND_IT && settings.isGCvoteLogin() == true) { + if (rating == 0) { + post.setText(res.getString(R.string.log_post_no_rate)); + } else { + post.setText(res.getString(R.string.log_post_rate) + " " + rating + "*"); + } + } else { + post.setText(res.getString(R.string.log_post)); + } + } + + private class cgeovisitDateListener implements View.OnClickListener { + + public void onClick(View arg0) { + Dialog dateDialog = new cgeodate(activity, (cgeovisit) activity, date); + dateDialog.setCancelable(true); + dateDialog.show(); + } + } + + private class postListener implements View.OnClickListener { + + public void onClick(View arg0) { + if (gettingViewstate == false) { + waitDialog = ProgressDialog.show(activity, null, res.getString(R.string.log_saving), true); + waitDialog.setCancelable(true); + + String log = ((EditText) findViewById(R.id.log)).getText().toString(); + Thread thread = new postLog(postLogHandler, log); + thread.start(); + } else { + warning.showToast(res.getString(R.string.err_log_load_data_still)); + } + } + } + + private class saveListener implements View.OnClickListener { + + public void onClick(View arg0) { + String log = ((EditText) findViewById(R.id.log)).getText().toString(); + final boolean status = app.saveLogOffline(geocode, date.getTime(), typeSelected, log); + if (save == null) { + save = (Button) findViewById(R.id.save); + } + save.setOnClickListener(new saveListener()); + + if (status == true) { + warning.showToast(res.getString(R.string.info_log_saved)); + app.saveVisitDate(geocode); + } else { + warning.showToast(res.getString(R.string.err_log_post_failed)); + } + } + } + + private class clearListener implements View.OnClickListener { + + public void onClick(View arg0) { + app.clearLogOffline(geocode); + + if (alreadyFound == true) { + typeSelected = cgBase.LOG_NOTE; + } else { + typeSelected = types.get(0); + } + date.setTime(new Date()); + text = null; + + setType(typeSelected); + + Button dateButton = (Button) findViewById(R.id.date); + dateButton.setText(cgBase.dateOutShort.format(date.getTime())); + dateButton.setOnClickListener(new cgeovisitDateListener()); + + EditText logView = (EditText) findViewById(R.id.log); + if (text != null && text.length() > 0) { + logView.setText(text); + } else { + logView.setText(""); + } + + if (clear == null) { + clear = (Button) findViewById(R.id.clear); + } + clear.setOnClickListener(new clearListener()); + + warning.showToast(res.getString(R.string.info_log_cleared)); + } + } + + private class loadData extends Thread { + + private String cacheid = null; + + public loadData(String cacheidIn) { + cacheid = cacheidIn; + + if (cacheid == null) { + warning.showToast(res.getString(R.string.err_detail_cache_forgot_visit)); + + finish(); + return; + } + } + + @Override + public void run() { + final HashMap<String, String> params = new HashMap<String, String>(); + + showProgressHandler.sendEmptyMessage(0); + gettingViewstate = true; + attempts++; + + try { + if (cacheid != null && cacheid.length() > 0) { + params.put("ID", cacheid); + } else { + loadDataHandler.sendEmptyMessage(0); + return; + } + + final String page = base.request(false, "www.geocaching.com", "/seek/log.aspx", "GET", params, false, false, false).getData(); + + viewstate = base.findViewstate(page, 0); + viewstate1 = base.findViewstate(page, 1); + trackables = base.parseTrackableLog(page); + + final ArrayList<Integer> typesPre = base.parseTypes(page); + if (typesPre.size() > 0) { + types.clear(); + types.addAll(typesPre); + types.remove(new Integer(cgBase.LOG_UPDATE_COORDINATES)); + } + typesPre.clear(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeovisit.loadData.run: " + e.toString()); + } + + loadDataHandler.sendEmptyMessage(0); + } + } + + private class postLog extends Thread { + + Handler handler = null; + String log = null; + + public postLog(Handler handlerIn, String logIn) { + handler = handlerIn; + log = logIn; + } + + @Override + public void run() { + int ret = -1; + + ret = postLogFn(log); + + handler.sendEmptyMessage(ret); + } + } + + public int postLogFn(String log) { + int status = -1; + + try { + if (tweetBox == null) { + tweetBox = (LinearLayout) findViewById(R.id.tweet_box); + } + if (tweetCheck == null) { + tweetCheck = (CheckBox) findViewById(R.id.tweet); + } + + status = base.postLog(app, geocode, cacheid, viewstate, viewstate1, typeSelected, date.get(Calendar.YEAR), (date.get(Calendar.MONTH) + 1), date.get(Calendar.DATE), log, trackables); + + if (status == 1) { + cgLog logNow = new cgLog(); + logNow.author = settings.getUsername(); + logNow.date = date.getTimeInMillis(); + logNow.type = typeSelected; + logNow.log = log; + + cache.logs.add(0, logNow); + app.addLog(geocode, logNow); + + if (typeSelected == cgBase.LOG_FOUND_IT) { + app.markFound(geocode); + if (cache != null) { + cache.found = true; + } + } + + if (cache != null) { + app.putCacheInCache(cache); + } else { + app.removeCacheFromCache(geocode); + } + } + + if (status == 1) { + app.clearLogOffline(geocode); + } + + if ( + status == 1 && typeSelected == cgBase.LOG_FOUND_IT && settings.twitter == 1 + && settings.tokenPublic != null && settings.tokenPublic.length() > 0 && settings.tokenSecret != null + && settings.tokenSecret.length() > 0 && tweetCheck.isChecked() == true && tweetBox.getVisibility() == View.VISIBLE + ) { + base.postTweetCache(app, settings, geocode); + } + + if (status == 1 && typeSelected == cgBase.LOG_FOUND_IT && settings.isGCvoteLogin() == true) { + base.setRating(cache.guid, rating); + } + + return status; + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeovisit.postLogFn: " + e.toString()); + } + + return 1000; + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-log", + activity, + "http://cgeo.carnero.cc/manual/" + ); + } catch (Exception e) { + // nothing + } + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgeowaypoint.java b/src/cgeo/geocaching/cgeowaypoint.java new file mode 100644 index 0000000..5121a38 --- /dev/null +++ b/src/cgeo/geocaching/cgeowaypoint.java @@ -0,0 +1,465 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.util.Log; +import android.text.Html; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import android.content.Intent; +import android.content.res.Resources; +import android.net.Uri; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.widget.Button; + +import com.google.android.apps.analytics.GoogleAnalyticsTracker; +import java.util.ArrayList; + +public class cgeowaypoint extends Activity { + + private static final int MENU_ID_EXTERN = 23; + private static final int MENU_ID_RMAPS = 21; + private static final int MENU_ID_LOCUS = 20; + private static final int MENU_ID_NAVIGATION = 0; + private static final int MENU_ID_CACHES_AROUND = 5; + private static final int MENU_ID_TURNBYTURN = 4; + private static final int MENU_ID_MAP = 1; + private static final int MENU_ID_RADAR = 3; + private static final int MENU_ID_COMPASS = 2; + private GoogleAnalyticsTracker tracker = null; + private cgWaypoint waypoint = null; + private String geocode = null; + private int id = -1; + private cgeoapplication app = null; + private Resources res = null; + private Activity activity = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private ProgressDialog waitDialog = null; + private cgGeo geo = null; + private cgUpdateLoc geoUpdate = new update(); + private Handler loadWaypointHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (waypoint == null) { + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog = null; + } + + warning.showToast(res.getString(R.string.err_waypoint_load_failed)); + + finish(); + return; + } else { + final TextView identification = (TextView) findViewById(R.id.identification); + final TextView coords = (TextView) findViewById(R.id.coordinates); + final TextView note = (TextView) findViewById(R.id.note); + final ImageView compass = (ImageView) findViewById(R.id.compass); + final View separator = (View) findViewById(R.id.separator); + + if (waypoint.name != null && waypoint.name.length() > 0) { + base.setTitle(activity, Html.fromHtml(waypoint.name.trim()).toString()); + } else { + base.setTitle(activity, res.getString(R.string.waypoint_title)); + } + + if (waypoint.prefix.equalsIgnoreCase("OWN") == false) { + identification.setText(waypoint.prefix.trim() + "/" + waypoint.lookup.trim()); + } else { + identification.setText(res.getString(R.string.waypoint_custom)); + } + + if (waypoint.latitude != null && waypoint.longitude != null) { + coords.setText(Html.fromHtml(base.formatCoordinate(waypoint.latitude, "lat", true) + " | " + base.formatCoordinate(waypoint.longitude, "lon", true)), TextView.BufferType.SPANNABLE); + compass.setVisibility(View.VISIBLE); + separator.setVisibility(View.VISIBLE); + } else { + coords.setText(res.getString(R.string.waypoint_unknown_coordinates)); + compass.setVisibility(View.GONE); + separator.setVisibility(View.GONE); + } + + if (waypoint.note != null && waypoint.note.length() > 0) { + note.setText(Html.fromHtml(waypoint.note.trim()), TextView.BufferType.SPANNABLE); + } + + Button buttonEdit = (Button) findViewById(R.id.edit); + buttonEdit.setOnClickListener(new editWaypointListener(waypoint.id)); + + Button buttonDelete = (Button) findViewById(R.id.delete); + if (waypoint.type != null && waypoint.type.equalsIgnoreCase("own") == true) { + buttonDelete.setOnClickListener(new deleteWaypointListener(waypoint.id)); + buttonDelete.setVisibility(View.VISIBLE); + } + + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog = null; + } + } + } catch (Exception e) { + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog = null; + } + Log.e(cgSettings.tag, "cgeowaypoint.loadWaypointHandler: " + e.toString()); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(this, getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(this); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.waypoint); + base.setTitle(activity, "waypoint"); + + // google analytics + tracker = GoogleAnalyticsTracker.getInstance(); + tracker.start(cgSettings.analytics, this); + tracker.dispatch(); + base.sendAnal(activity, tracker, "/waypoint/detail"); + + // get parameters + Bundle extras = getIntent().getExtras(); + + // try to get data from extras + if (extras != null) { + id = extras.getInt("waypoint"); + geocode = extras.getString("geocode"); + } + + if (id <= 0) { + warning.showToast(res.getString(R.string.err_waypoint_unknown)); + finish(); + return; + } + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + + waitDialog = ProgressDialog.show(this, null, res.getString(R.string.waypoint_loading), true); + waitDialog.setCancelable(true); + + (new loadWaypoint()).start(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + + if (waitDialog == null) { + waitDialog = ProgressDialog.show(this, null, res.getString(R.string.waypoint_loading), true); + waitDialog.setCancelable(true); + + (new loadWaypoint()).start(); + } + } + + @Override + public void onDestroy() { + if (geo != null) { + geo = app.removeGeo(); + } + if (tracker != null) { + tracker.stop(); + } + + super.onDestroy(); + } + + @Override + public void onStop() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onStop(); + } + + @Override + public void onPause() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onPause(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, MENU_ID_COMPASS, 0, res.getString(R.string.cache_menu_compass)).setIcon(android.R.drawable.ic_menu_compass); // compass + + SubMenu subMenu = menu.addSubMenu(1, MENU_ID_NAVIGATION, 0, res.getString(R.string.cache_menu_navigate)).setIcon(android.R.drawable.ic_menu_more); + subMenu.add(0, MENU_ID_RADAR, 0, res.getString(R.string.cache_menu_radar)); // radar + subMenu.add(0, MENU_ID_MAP, 0, res.getString(R.string.cache_menu_map)); // c:geo map + if (base.isLocus(activity)) { + subMenu.add(0, MENU_ID_LOCUS, 0, res.getString(R.string.cache_menu_locus)); // ext.: locus + } + if (base.isRmaps(activity)) { + subMenu.add(0, MENU_ID_RMAPS, 0, res.getString(R.string.cache_menu_rmaps)); // ext.: rmaps + } + subMenu.add(0, MENU_ID_EXTERN, 0, res.getString(R.string.cache_menu_map_ext)); // ext.: other + subMenu.add(0, MENU_ID_TURNBYTURN, 0, res.getString(R.string.cache_menu_tbt)); // turn-by-turn + + menu.add(0, MENU_ID_CACHES_AROUND, 0, res.getString(R.string.cache_menu_around)).setIcon(android.R.drawable.ic_menu_rotate); // caches around + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + try { + boolean visible = waypoint != null && waypoint.latitude != null && waypoint.longitude != null; + menu.findItem(MENU_ID_NAVIGATION).setVisible(visible); + menu.findItem(MENU_ID_COMPASS).setVisible(visible); + menu.findItem(MENU_ID_CACHES_AROUND).setVisible(visible); + } catch (Exception e) { + // nothing + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int menuItem = item.getItemId(); + + if (menuItem == MENU_ID_MAP) { + showOnMap(); + return true; + } else if (menuItem == MENU_ID_COMPASS) { + goCompass(null); + return true; + } else if (menuItem == MENU_ID_RADAR) { + radarTo(); + return true; + } else if (menuItem == MENU_ID_TURNBYTURN) { + if (geo != null) { + base.runNavigation(activity, res, settings, warning, tracker, waypoint.latitude, waypoint.longitude, geo.latitudeNow, geo.longitudeNow); + } else { + base.runNavigation(activity, res, settings, warning, tracker, waypoint.latitude, waypoint.longitude); + } + + return true; + } else if (menuItem == MENU_ID_CACHES_AROUND) { + cachesAround(); + return true; + } else if (menuItem == MENU_ID_LOCUS) { + base.runExternalMap(cgBase.mapAppLocus, activity, res, warning, tracker, waypoint); // locus + return true; + } else if (menuItem == MENU_ID_RMAPS) { + base.runExternalMap(cgBase.mapAppRmaps, activity, res, warning, tracker, waypoint); // rmaps + return true; + } else if (menuItem == MENU_ID_EXTERN) { + base.runExternalMap(cgBase.mapAppAny, activity, res, warning, tracker, waypoint); // extern + return true; + } + + return false; + } + + private void showOnMap() { + if (waypoint == null || waypoint.latitude == null || waypoint.longitude == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + Intent mapIntent = new Intent(activity, settings.getMapFactory().getMapClass()); + mapIntent.putExtra("latitude", waypoint.latitude); + mapIntent.putExtra("longitude", waypoint.longitude); + mapIntent.putExtra("wpttype", waypoint.type); + + activity.startActivity(mapIntent); + } + + private void radarTo() { + if (waypoint == null || waypoint.latitude == null || waypoint.longitude == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + try { + if (cgBase.isIntentAvailable(activity, "com.google.android.radar.SHOW_RADAR") == true) { + Intent radarIntent = new Intent("com.google.android.radar.SHOW_RADAR"); + radarIntent.putExtra("latitude", new Float(waypoint.latitude)); + radarIntent.putExtra("longitude", new Float(waypoint.longitude)); + activity.startActivity(radarIntent); + } else { + AlertDialog.Builder dialog = new AlertDialog.Builder(activity); + dialog.setTitle(res.getString(R.string.err_radar_title)); + dialog.setMessage(res.getString(R.string.err_radar_message)); + dialog.setCancelable(true); + dialog.setPositiveButton("yes", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + try { + activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:com.eclipsim.gpsstatus2"))); + dialog.cancel(); + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_radar_market)); + Log.e(cgSettings.tag, "cgeowaypoint.radarTo.onClick: " + e.toString()); + } + } + }); + dialog.setNegativeButton("no", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog alert = dialog.create(); + alert.show(); + } + } catch (Exception e) { + warning.showToast(res.getString(R.string.err_radar_generic)); + Log.e(cgSettings.tag, "cgeowaypoint.radarTo: " + e.toString()); + } + } + + private void cachesAround() { + if (waypoint == null || waypoint.latitude == null || waypoint.longitude == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + cgeocaches cachesActivity = new cgeocaches(); + + Intent cachesIntent = new Intent(activity, cachesActivity.getClass()); + cachesIntent.putExtra("type", "coordinate"); + cachesIntent.putExtra("latitude", waypoint.latitude); + cachesIntent.putExtra("longitude", waypoint.longitude); + cachesIntent.putExtra("cachetype", settings.cacheType); + + activity.startActivity(cachesIntent); + + finish(); + } + + private class loadWaypoint extends Thread { + + @Override + public void run() { + try { + waypoint = app.loadWaypoint(id); + + loadWaypointHandler.sendMessage(new Message()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeowaypoint.loadWaypoint.run: " + e.toString()); + } + } + } + + private class update extends cgUpdateLoc { + + @Override + public void updateLoc(cgGeo geo) { + // nothing + } + } + + private class editWaypointListener implements View.OnClickListener { + + private int id = -1; + + public editWaypointListener(int idIn) { + id = idIn; + } + + public void onClick(View arg0) { + Intent editIntent = new Intent(activity, cgeowaypointadd.class); + editIntent.putExtra("waypoint", id); + activity.startActivity(editIntent); + } + } + + private class deleteWaypointListener implements View.OnClickListener { + + private Integer id = null; + + public deleteWaypointListener(int idIn) { + id = idIn; + } + + public void onClick(View arg0) { + if (app.deleteWaypoint(id) == false) { + warning.showToast(res.getString(R.string.err_waypoint_delete_failed)); + } else { + app.removeCacheFromCache(geocode); + + finish(); + return; + } + } + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-waypoint-details", + activity, + "http://cgeo.carnero.cc/manual/"); + } catch (Exception e) { + // nothing + } + } + + public void goCompass(View view) { + if (waypoint == null || waypoint.latitude == null || waypoint.longitude == null) { + warning.showToast(res.getString(R.string.err_location_unknown)); + } + + Intent navigateIntent = new Intent(activity, cgeonavigate.class); + navigateIntent.putExtra("latitude", waypoint.latitude); + navigateIntent.putExtra("longitude", waypoint.longitude); + navigateIntent.putExtra("geocode", waypoint.prefix.trim() + "/" + waypoint.lookup.trim()); + navigateIntent.putExtra("name", waypoint.name); + + if (cgeonavigate.coordinates != null) { + cgeonavigate.coordinates.clear(); + } + cgeonavigate.coordinates = new ArrayList<cgCoord>(); + cgeonavigate.coordinates.add(new cgCoord(waypoint)); + activity.startActivity(navigateIntent); + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/cgeowaypointadd.java b/src/cgeo/geocaching/cgeowaypointadd.java new file mode 100644 index 0000000..ec58257 --- /dev/null +++ b/src/cgeo/geocaching/cgeowaypointadd.java @@ -0,0 +1,434 @@ +package cgeo.geocaching; + +import gnu.android.app.appmanualclient.*; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.os.Bundle; +import android.content.res.Resources; +import android.os.Handler; +import android.os.Message; +import android.text.Html; +import android.util.Log; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.Button; +import android.widget.EditText; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class cgeowaypointadd extends Activity { + + private cgeoapplication app = null; + private Resources res = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private Activity activity = null; + private String geocode = null; + private int id = -1; + private cgGeo geo = null; + private cgUpdateLoc geoUpdate = new update(); + private EditText latEdit = null; + private EditText lonEdit = null; + private ProgressDialog waitDialog = null; + private cgWaypoint waypoint = null; + private String type = "own"; + private String prefix = "OWN"; + private String lookup = "---"; + /** + * number of waypoints that the corresponding cache has until now + */ + private int wpCount = 0; + private Handler loadWaypointHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + try { + if (waypoint == null) { + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog = null; + } + + id = -1; + } else { + geocode = waypoint.geocode; + type = waypoint.type; + prefix = waypoint.prefix; + lookup = waypoint.lookup; + + app.setAction(geocode); + + ((EditText) findViewById(R.id.latitude)).setText(base.formatCoordinate(waypoint.latitude, "lat", true)); + ((EditText) findViewById(R.id.longitude)).setText(base.formatCoordinate(waypoint.longitude, "lon", true)); + ((EditText) findViewById(R.id.name)).setText(Html.fromHtml(waypoint.name.trim()).toString()); + ((EditText) findViewById(R.id.note)).setText(Html.fromHtml(waypoint.note.trim()).toString()); + + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog = null; + } + } + } catch (Exception e) { + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog = null; + } + Log.e(cgSettings.tag, "cgeowaypointadd.loadWaypointHandler: " + e.toString()); + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // init + activity = this; + res = this.getResources(); + app = (cgeoapplication) this.getApplication(); + settings = new cgSettings(activity, activity.getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, activity.getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(activity); + + // set layout + if (settings.skin == 1) { + setTheme(R.style.light); + } else { + setTheme(R.style.dark); + } + setContentView(R.layout.waypoint_new); + base.setTitle(activity, "waypoint"); + + // google analytics + base.sendAnal(activity, "/waypoint/new"); + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + + // get parameters + Bundle extras = getIntent().getExtras(); + if (extras != null) { + geocode = extras.getString("geocode"); + wpCount = extras.getInt("count", 0); + id = extras.getInt("waypoint"); + } + + if ((geocode == null || geocode.length() == 0) && id <= 0) { + warning.showToast(res.getString(R.string.err_waypoint_cache_unknown)); + + finish(); + return; + } + + if (id <= 0) { + base.setTitle(activity, res.getString(R.string.waypoint_add_title)); + } else { + base.setTitle(activity, res.getString(R.string.waypoint_edit_title)); + } + + if (geocode != null) { + app.setAction(geocode); + } + + Button buttonCurrent = (Button) findViewById(R.id.current); + buttonCurrent.setOnClickListener(new currentListener()); + + Button addWaypoint = (Button) findViewById(R.id.add_waypoint); + addWaypoint.setOnClickListener(new coordsListener()); + + ArrayList<String> wayPointNames = new ArrayList<String>(cgBase.waypointTypes.values()); + AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.name); + ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, wayPointNames); + textView.setAdapter(adapter); + + + if (id > 0) { + waitDialog = ProgressDialog.show(this, null, res.getString(R.string.waypoint_loading), true); + waitDialog.setCancelable(true); + + (new loadWaypoint()).start(); + } + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + + if (id > 0) { + if (waitDialog == null) { + waitDialog = ProgressDialog.show(this, null, res.getString(R.string.waypoint_loading), true); + waitDialog.setCancelable(true); + + (new loadWaypoint()).start(); + } + } + } + + @Override + public void onDestroy() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onDestroy(); + } + + @Override + public void onStop() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onStop(); + } + + @Override + public void onPause() { + if (geo != null) { + geo = app.removeGeo(); + } + + super.onPause(); + } + + private class update extends cgUpdateLoc { + + @Override + public void updateLoc(cgGeo geo) { + if (geo == null) { + return; + } + + try { + if (latEdit == null) { + latEdit = (EditText) findViewById(R.id.latitude); + } + if (lonEdit == null) { + lonEdit = (EditText) findViewById(R.id.longitude); + } + + latEdit.setHint(base.formatCoordinate(geo.latitudeNow, "lat", false)); + lonEdit.setHint(base.formatCoordinate(geo.longitudeNow, "lon", false)); + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to update location."); + } + } + } + + private class loadWaypoint extends Thread { + + @Override + public void run() { + try { + waypoint = app.loadWaypoint(id); + + loadWaypointHandler.sendMessage(new Message()); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeowaypoint.loadWaypoint.run: " + e.toString()); + } + } + } + + private class currentListener implements View.OnClickListener { + + public void onClick(View arg0) { + if (geo == null || geo.latitudeNow == null || geo.longitudeNow == null) { + warning.showToast(res.getString(R.string.err_point_unknown_position)); + return; + } + + ((EditText) findViewById(R.id.latitude)).setText(base.formatCoordinate(geo.latitudeNow, "lat", true)); + ((EditText) findViewById(R.id.longitude)).setText(base.formatCoordinate(geo.longitudeNow, "lon", true)); + } + } + + private class coordsListener implements View.OnClickListener { + + public void onClick(View arg0) { + ArrayList<Double> coords = new ArrayList<Double>(); + Double latitude = null; + Double longitude = null; + + final String bearingText = ((EditText) findViewById(R.id.bearing)).getText().toString(); + final String distanceText = ((EditText) findViewById(R.id.distance)).getText().toString(); + final String latText = ((EditText) findViewById(R.id.latitude)).getText().toString(); + final String lonText = ((EditText) findViewById(R.id.longitude)).getText().toString(); + + if ((bearingText == null || bearingText.length() == 0) && (distanceText == null || distanceText.length() == 0) + && (latText == null || latText.length() == 0) && (lonText == null || lonText.length() == 0)) { + warning.helpDialog(res.getString(R.string.err_point_no_position_given_title), res.getString(R.string.err_point_no_position_given)); + return; + } + + if (latText != null && latText.length() > 0 && lonText != null && lonText.length() > 0) { + // latitude & longitude + HashMap<String, Object> latParsed = base.parseCoordinate(latText, "lat"); + HashMap<String, Object> lonParsed = base.parseCoordinate(lonText, "lat"); + + if (latParsed == null || latParsed.get("coordinate") == null || latParsed.get("string") == null) { + warning.showToast(res.getString(R.string.err_parse_lat)); + return; + } + + if (lonParsed == null || lonParsed.get("coordinate") == null || lonParsed.get("string") == null) { + warning.showToast(res.getString(R.string.err_parse_lon)); + return; + } + + latitude = (Double) latParsed.get("coordinate"); + longitude = (Double) lonParsed.get("coordinate"); + } else { + if (geo == null || geo.latitudeNow == null || geo.longitudeNow == null) { + warning.showToast(res.getString(R.string.err_point_curr_position_unavailable)); + return; + } + + latitude = geo.latitudeNow; + longitude = geo.longitudeNow; + } + + if (bearingText != null && bearingText.length() > 0 && distanceText != null && distanceText.length() > 0) { + // bearing & distance + Double bearing = null; + try { + bearing = new Double(bearingText); + } catch (Exception e) { + // probably not a number + } + if (bearing == null) { + warning.helpDialog(res.getString(R.string.err_point_bear_and_dist_title), res.getString(R.string.err_point_bear_and_dist)); + return; + } + + Double distance = null; // km + + final Pattern patternA = Pattern.compile("^([0-9\\.\\,]+)[ ]*m$", Pattern.CASE_INSENSITIVE); // m + final Pattern patternB = Pattern.compile("^([0-9\\.\\,]+)[ ]*km$", Pattern.CASE_INSENSITIVE); // km + final Pattern patternC = Pattern.compile("^([0-9\\.\\,]+)[ ]*ft$", Pattern.CASE_INSENSITIVE); // ft - 0.3048m + final Pattern patternD = Pattern.compile("^([0-9\\.\\,]+)[ ]*yd$", Pattern.CASE_INSENSITIVE); // yd - 0.9144m + final Pattern patternE = Pattern.compile("^([0-9\\.\\,]+)[ ]*mi$", Pattern.CASE_INSENSITIVE); // mi - 1609.344m + + Matcher matcherA = patternA.matcher(distanceText); + Matcher matcherB = patternB.matcher(distanceText); + Matcher matcherC = patternC.matcher(distanceText); + Matcher matcherD = patternD.matcher(distanceText); + Matcher matcherE = patternE.matcher(distanceText); + + if (matcherA.find() == true && matcherA.groupCount() > 0) { + distance = (new Double(matcherA.group(1))) * 0.001; + } else if (matcherB.find() == true && matcherB.groupCount() > 0) { + distance = new Double(matcherB.group(1)); + } else if (matcherC.find() == true && matcherC.groupCount() > 0) { + distance = (new Double(matcherC.group(1))) * 0.0003048; + } else if (matcherD.find() == true && matcherD.groupCount() > 0) { + distance = (new Double(matcherD.group(1))) * 0.0009144; + } else if (matcherE.find() == true && matcherE.groupCount() > 0) { + distance = (new Double(matcherE.group(1))) * 1.609344; + } else { + try { + if (settings.units == cgSettings.unitsImperial) { + distance = (new Double(distanceText)) * 1.609344; // considering it miles + } else { + distance = (new Double(distanceText)) * 0.001; // considering it meters + } + } catch (Exception e) { + // probably not a number + } + } + + if (distance == null) { + warning.showToast(res.getString(R.string.err_parse_dist)); + return; + } + + Double latParsed = null; + Double lonParsed = null; + + HashMap<String, Double> coordsDst = base.getRadialDistance(latitude, longitude, bearing, distance); + + latParsed = coordsDst.get("latitude"); + lonParsed = coordsDst.get("longitude"); + + if (latParsed == null || lonParsed == null) { + warning.showToast(res.getString(R.string.err_point_location_error)); + return; + } + + coords.add(0, (Double) latParsed); + coords.add(1, (Double) lonParsed); + } else if (latitude != null && longitude != null) { + coords.add(0, latitude); + coords.add(1, longitude); + } else { + warning.showToast(res.getString(R.string.err_point_location_error)); + return; + } + + String name = ((EditText) findViewById(R.id.name)).getText().toString().trim(); + // if no name is given, just give the waypoint its number as name + if (name.length() == 0) { + name = res.getString(R.string.waypoint) + " " + String.valueOf(wpCount + 1); + } + final String note = ((EditText) findViewById(R.id.note)).getText().toString().trim(); + + final cgWaypoint waypoint = new cgWaypoint(); + waypoint.type = type; + waypoint.geocode = geocode; + waypoint.prefix = prefix; + waypoint.lookup = lookup; + waypoint.name = name; + waypoint.latitude = coords.get(0); + waypoint.longitude = coords.get(1); + waypoint.latitudeString = base.formatCoordinate(coords.get(0), "lat", true); + waypoint.longitudeString = base.formatCoordinate(coords.get(1), "lon", true); + waypoint.note = note; + + if (app.saveOwnWaypoint(id, geocode, waypoint) == true) { + app.removeCacheFromCache(geocode); + + finish(); + return; + } else { + warning.showToast(res.getString(R.string.err_waypoint_add_failed)); + } + } + } + + public void goHome(View view) { + base.goHome(activity); + } + + public void goManual(View view) { + try { + if (id >= 0) { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-waypoint-edit", + activity, + "http://cgeo.carnero.cc/manual/" + ); + } else { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-waypoint-new", + activity, + "http://cgeo.carnero.cc/manual/" + ); + } + } catch (Exception e) { + // nothing + } + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/filter/cgFilter.java b/src/cgeo/geocaching/filter/cgFilter.java new file mode 100644 index 0000000..20f04c9 --- /dev/null +++ b/src/cgeo/geocaching/filter/cgFilter.java @@ -0,0 +1,20 @@ +package cgeo.geocaching.filter; + +import java.util.ArrayList; +import java.util.List; + +import cgeo.geocaching.cgCache; + +public abstract class cgFilter { + abstract boolean applyFilter(cgCache cache); + + public void filter(List<cgCache> list){ + List<cgCache> itemsToRemove = new ArrayList<cgCache>(); + for(cgCache item : list){ + if(!applyFilter(item)){ + itemsToRemove.add(item); + } + } + list.removeAll(itemsToRemove); + } +} diff --git a/src/cgeo/geocaching/filter/cgFilterBySize.java b/src/cgeo/geocaching/filter/cgFilterBySize.java new file mode 100644 index 0000000..b32afc0 --- /dev/null +++ b/src/cgeo/geocaching/filter/cgFilterBySize.java @@ -0,0 +1,17 @@ +package cgeo.geocaching.filter; + +import cgeo.geocaching.cgCache; + +public class cgFilterBySize extends cgFilter { + private String size; + + public cgFilterBySize(String size){ + this.size = size; + } + + @Override + boolean applyFilter(cgCache cache) { + return cache.size.equals(size); + } + +} diff --git a/src/cgeo/geocaching/filter/cgFilterByTrackables.java b/src/cgeo/geocaching/filter/cgFilterByTrackables.java new file mode 100644 index 0000000..44c85c1 --- /dev/null +++ b/src/cgeo/geocaching/filter/cgFilterByTrackables.java @@ -0,0 +1,12 @@ +package cgeo.geocaching.filter; + +import cgeo.geocaching.cgCache; + +public class cgFilterByTrackables extends cgFilter { + + @Override + boolean applyFilter(cgCache cache) { + return cache.hasTrackables(); + } + +} diff --git a/src/cgeo/geocaching/filter/cgFilterByType.java b/src/cgeo/geocaching/filter/cgFilterByType.java new file mode 100644 index 0000000..8ba177b --- /dev/null +++ b/src/cgeo/geocaching/filter/cgFilterByType.java @@ -0,0 +1,17 @@ +package cgeo.geocaching.filter; + +import cgeo.geocaching.cgCache; + +public class cgFilterByType extends cgFilter { + private String type; + + public cgFilterByType(String type){ + this.type = type; + } + + @Override + boolean applyFilter(cgCache cache) { + return cache.type.equals(type); + } + +} diff --git a/src/cgeo/geocaching/googlemaps/googleCacheOverlay.java b/src/cgeo/geocaching/googlemaps/googleCacheOverlay.java new file mode 100644 index 0000000..68929cf --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleCacheOverlay.java @@ -0,0 +1,101 @@ +package cgeo.geocaching.googlemaps; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Point; +import android.graphics.drawable.Drawable; + +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapcommon.cgMapOverlay; +import cgeo.geocaching.mapinterfaces.ItemizedOverlayImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.MapViewImpl; + +import com.google.android.maps.ItemizedOverlay; +import com.google.android.maps.MapView; + +/** + * Google specific implementation of the itemized cache overlay + * @author rsudev + * + */ +public class googleCacheOverlay extends ItemizedOverlay<googleCacheOverlayItem> implements ItemizedOverlayImpl { + + private cgMapOverlay base; + + public googleCacheOverlay(cgSettings settingsIn, Context contextIn, Drawable markerIn, Boolean fromDetailIn) { + super(boundCenterBottom(markerIn)); + base = new cgMapOverlay(settingsIn, this, contextIn, fromDetailIn); + } + + @Override + public cgMapOverlay getBase() { + return base; + } + + @Override + protected googleCacheOverlayItem createItem(int i) { + if (base == null) + return null; + + return (googleCacheOverlayItem) base.createItem(i); + } + + @Override + public int size() { + if (base == null) + return 0; + + return base.size(); + } + + @Override + protected boolean onTap(int arg0) { + if (base == null) + return false; + + return base.onTap(arg0); + } + + @Override + public void draw(Canvas canvas, MapView mapView, boolean shadow) { + base.draw(canvas, (MapViewImpl) mapView, shadow); + } + + @Override + public void superPopulate() { + populate(); + } + + @Override + public Drawable superBoundCenter(Drawable markerIn) { + return super.boundCenter(markerIn); + } + + @Override + public Drawable superBoundCenterBottom(Drawable marker) { + return super.boundCenterBottom(marker); + } + + @Override + public void superSetLastFocusedItemIndex(int i) { + super.setLastFocusedIndex(i); + } + + @Override + public boolean superOnTap(int index) { + return super.onTap(index); + } + + @Override + public void superDraw(Canvas canvas, MapViewImpl mapView, boolean shadow) { + super.draw(canvas, (MapView) mapView, shadow); + } + + @Override + public void superDrawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel) { + // Nothing to do here... + } + +} diff --git a/src/cgeo/geocaching/googlemaps/googleCacheOverlayItem.java b/src/cgeo/geocaching/googlemaps/googleCacheOverlayItem.java new file mode 100644 index 0000000..5546077 --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleCacheOverlayItem.java @@ -0,0 +1,28 @@ +package cgeo.geocaching.googlemaps; + +import com.google.android.maps.GeoPoint; +import com.google.android.maps.OverlayItem; + +import cgeo.geocaching.cgCoord; +import cgeo.geocaching.mapinterfaces.CacheOverlayItemImpl; + +public class googleCacheOverlayItem extends OverlayItem implements CacheOverlayItemImpl { + private String cacheType = null; + private cgCoord coord; + + public googleCacheOverlayItem(cgCoord coordinate, String type) { + super(new GeoPoint((int)(coordinate.latitude * 1e6), (int)(coordinate.longitude * 1e6)), coordinate.name, ""); + + this.cacheType = type; + this.coord = coordinate; + } + + public cgCoord getCoord() { + return coord; + } + + public String getType() { + return cacheType; + } + +} diff --git a/src/cgeo/geocaching/googlemaps/googleGeoPoint.java b/src/cgeo/geocaching/googlemaps/googleGeoPoint.java new file mode 100644 index 0000000..a22e7d4 --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleGeoPoint.java @@ -0,0 +1,13 @@ +package cgeo.geocaching.googlemaps; + +import cgeo.geocaching.mapinterfaces.GeoPointImpl; + +import com.google.android.maps.GeoPoint; + +public class googleGeoPoint extends GeoPoint implements GeoPointImpl { + + public googleGeoPoint(int latitudeE6, int longitudeE6) { + super(latitudeE6, longitudeE6); + } + +} diff --git a/src/cgeo/geocaching/googlemaps/googleMapActivity.java b/src/cgeo/geocaching/googlemaps/googleMapActivity.java new file mode 100644 index 0000000..a95b741 --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleMapActivity.java @@ -0,0 +1,101 @@ +package cgeo.geocaching.googlemaps; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import cgeo.geocaching.mapcommon.MapBase; +import cgeo.geocaching.mapcommon.cgeomap; +import cgeo.geocaching.mapinterfaces.ActivityImpl; + +import com.google.android.maps.MapActivity; + +public class googleMapActivity extends MapActivity implements ActivityImpl { + + private MapBase mapBase; + + public googleMapActivity() { + mapBase = new cgeomap(this); + } + + @Override + protected boolean isRouteDisplayed() { + return false; + } + + @Override + public Activity getActivity() { + return this; + } + + @Override + protected void onCreate(Bundle icicle) { + mapBase.onCreate(icicle); + } + + @Override + protected void onDestroy() { + mapBase.onDestroy(); + } + + @Override + protected void onPause() { + mapBase.onPause(); + } + + @Override + protected void onResume() { + mapBase.onResume(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + return mapBase.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return mapBase.onOptionsItemSelected(item); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + return mapBase.onPrepareOptionsMenu(menu); + } + + @Override + protected void onStop() { + mapBase.onStop(); + } + + @Override + public void superOnCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public boolean superOnCreateOptionsMenu(Menu menu) { + return superOnCreateOptionsMenu(menu); + } + + @Override + public void superOnDestroy() { + super.onDestroy(); + } + + @Override + public boolean superOnOptionsItemSelected(MenuItem item) { + return super.onOptionsItemSelected(item); + } + + @Override + public void superOnResume() { + super.onResume(); + } + + @Override + public boolean superOnPrepareOptionsMenu(Menu menu) { + return super.onPrepareOptionsMenu(menu); + } + +} diff --git a/src/cgeo/geocaching/googlemaps/googleMapController.java b/src/cgeo/geocaching/googlemaps/googleMapController.java new file mode 100644 index 0000000..c779b32 --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleMapController.java @@ -0,0 +1,37 @@ +package cgeo.geocaching.googlemaps; + +import com.google.android.maps.GeoPoint; +import com.google.android.maps.MapController; + +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapControllerImpl; + +public class googleMapController implements MapControllerImpl { + + private MapController mapController; + + public googleMapController(MapController mapControllerIn) { + mapController = mapControllerIn; + } + + @Override + public void animateTo(GeoPointImpl geoPoint) { + mapController.animateTo((GeoPoint)geoPoint); + } + + @Override + public void setCenter(GeoPointImpl geoPoint) { + mapController.setCenter((GeoPoint)geoPoint); + } + + @Override + public void setZoom(int mapzoom) { + mapController.setZoom(mapzoom); + } + + @Override + public void zoomToSpan(int latSpanE6, int lonSpanE6) { + mapController.zoomToSpan(latSpanE6, lonSpanE6); + } + +} diff --git a/src/cgeo/geocaching/googlemaps/googleMapFactory.java b/src/cgeo/geocaching/googlemaps/googleMapFactory.java new file mode 100644 index 0000000..86e75a0 --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleMapFactory.java @@ -0,0 +1,54 @@ +package cgeo.geocaching.googlemaps; + +import android.content.Context; +import cgeo.geocaching.R; +import cgeo.geocaching.cgCoord; +import cgeo.geocaching.cgUser; +import cgeo.geocaching.mapinterfaces.CacheOverlayItemImpl; +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapFactory; +import cgeo.geocaching.mapinterfaces.OverlayImpl; +import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.UserOverlayItemImpl; + +public class googleMapFactory implements MapFactory{ + + @Override + public Class getMapClass() { + return googleMapActivity.class; + } + + @Override + public int getMapViewId() { + return R.id.map; + } + + @Override + public int getMapLayoutId() { + return R.layout.googlemap; + } + + @Override + public GeoPointImpl getGeoPointBase(int latE6, int lonE6) { + return new googleGeoPoint(latE6, lonE6); + } + + @Override + public OverlayImpl getOverlayBaseWrapper(OverlayBase ovlIn) { + googleOverlay baseOvl = new googleOverlay(ovlIn); + return baseOvl; + } + + @Override + public CacheOverlayItemImpl getCacheOverlayItem(cgCoord coordinate, String type) { + googleCacheOverlayItem baseItem = new googleCacheOverlayItem(coordinate, type); + return baseItem; + } + + @Override + public UserOverlayItemImpl getUserOverlayItemBase(Context context, cgUser userOne) { + googleUsersOverlayItem baseItem = new googleUsersOverlayItem(context, userOne); + return baseItem; + } + +} diff --git a/src/cgeo/geocaching/googlemaps/googleMapProjection.java b/src/cgeo/geocaching/googlemaps/googleMapProjection.java new file mode 100644 index 0000000..476d78d --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleMapProjection.java @@ -0,0 +1,28 @@ +package cgeo.geocaching.googlemaps; + +import com.google.android.maps.GeoPoint; +import com.google.android.maps.Projection; + +import android.graphics.Point; +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; + +public class googleMapProjection implements MapProjectionImpl { + + private Projection projection; + + public googleMapProjection(Projection projectionIn) { + projection = projectionIn; + } + + @Override + public void toPixels(GeoPointImpl leftGeo, Point left) { + projection.toPixels((GeoPoint) leftGeo, left); + } + + @Override + public Object getImpl() { + return projection; + } + +} diff --git a/src/cgeo/geocaching/googlemaps/googleMapView.java b/src/cgeo/geocaching/googlemaps/googleMapView.java new file mode 100644 index 0000000..b63ca2e --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleMapView.java @@ -0,0 +1,118 @@ +package cgeo.geocaching.googlemaps; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.Log; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapcommon.cgMapOverlay; +import cgeo.geocaching.mapcommon.cgUsersOverlay; +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapControllerImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.MapViewImpl; +import cgeo.geocaching.mapinterfaces.OverlayImpl; + +import com.google.android.maps.GeoPoint; +import com.google.android.maps.MapView; +import com.google.android.maps.Overlay; + +public class googleMapView extends MapView implements MapViewImpl{ + + public googleMapView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public googleMapView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public googleMapView(Context context, String apiKey) { + super(context, apiKey); + } + + @Override + public void draw(Canvas canvas) { + try { + if (getMapZoomLevel() >= 22) { // to avoid too close zoom level (mostly on Samsung Galaxy S series) + getController().setZoom(22); + } + + super.draw(canvas); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgMapView.draw: " + e.toString()); + } + } + + @Override + public void displayZoomControls(boolean takeFocus) { + try { + super.displayZoomControls(takeFocus); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgMapView.displayZoomControls: " + e.toString()); + } + } + + @Override + public MapControllerImpl getMapController() { + return new googleMapController(getController()); + } + + @Override + public GeoPointImpl getMapViewCenter() { + GeoPoint point = getMapCenter(); + return new googleGeoPoint(point.getLatitudeE6(), point.getLongitudeE6()); + } + + @Override + public void addOverlay(OverlayImpl ovl) { + getOverlays().add((Overlay)ovl); + } + + @Override + public void clearOverlays() { + getOverlays().clear(); + } + + @Override + public MapProjectionImpl getMapProjection() { + return new googleMapProjection(getProjection()); + } + + @Override + public cgMapOverlay createAddMapOverlay(cgSettings settings, + Context context, Drawable drawable, boolean fromDetailIntent) { + + googleCacheOverlay ovl = new googleCacheOverlay(settings, context, drawable, fromDetailIntent); + getOverlays().add(ovl); + return ovl.getBase(); + } + + @Override + public cgUsersOverlay createAddUsersOverlay(Context context, Drawable markerIn) { + googleUsersOverlay ovl = new googleUsersOverlay(context, markerIn); + getOverlays().add(ovl); + return ovl.getBase(); + } + + @Override + public int getMapZoomLevel() { + return getZoomLevel(); + } + + @Override + public void setMapSource(cgSettings settings) { + // nothing to do for google maps... + } + + @Override + public boolean needsScaleOverlay() { + return true; + } + + @Override + public void setBuiltinScale(boolean b) { + //Nothing to do for google maps... + } +} diff --git a/src/cgeo/geocaching/googlemaps/googleOverlay.java b/src/cgeo/geocaching/googlemaps/googleOverlay.java new file mode 100644 index 0000000..3847d9f --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleOverlay.java @@ -0,0 +1,26 @@ +package cgeo.geocaching.googlemaps; + +import android.graphics.Canvas; +import cgeo.geocaching.mapinterfaces.MapViewImpl; +import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.OverlayImpl; + +import com.google.android.maps.MapView; +import com.google.android.maps.Overlay; + +public class googleOverlay extends Overlay implements OverlayImpl { + + private OverlayBase overlayBase; + + public googleOverlay(OverlayBase overlayBaseIn) { + overlayBase = overlayBaseIn; + } + + @Override + public void draw(Canvas canvas, MapView mapView, boolean shadow) { + super.draw(canvas, mapView, shadow); + + overlayBase.draw(canvas, (MapViewImpl) mapView, shadow); + } + +} diff --git a/src/cgeo/geocaching/googlemaps/googleUsersOverlay.java b/src/cgeo/geocaching/googlemaps/googleUsersOverlay.java new file mode 100644 index 0000000..5019e6e --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleUsersOverlay.java @@ -0,0 +1,94 @@ +package cgeo.geocaching.googlemaps; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Point; +import android.graphics.drawable.Drawable; +import cgeo.geocaching.mapcommon.cgUsersOverlay; +import cgeo.geocaching.mapinterfaces.ItemizedOverlayImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.MapViewImpl; + +import com.google.android.maps.ItemizedOverlay; +import com.google.android.maps.MapView; + +public class googleUsersOverlay extends ItemizedOverlay<googleUsersOverlayItem> implements ItemizedOverlayImpl { + + private cgUsersOverlay base; + + public googleUsersOverlay(Context contextIn, Drawable markerIn) { + super(boundCenter(markerIn)); + base = new cgUsersOverlay(this, contextIn); + } + + @Override + public cgUsersOverlay getBase() { + return base; + } + + @Override + protected googleUsersOverlayItem createItem(int i) { + if (base == null) + return null; + + return (googleUsersOverlayItem) base.createItem(i); + } + + @Override + public int size() { + if (base == null) + return 0; + + return base.size(); + } + + @Override + protected boolean onTap(int arg0) { + if (base == null) + return false; + + return base.onTap(arg0); + } + + @Override + public void draw(Canvas canvas, MapView mapView, boolean shadow) { + base.draw(canvas, (MapViewImpl) mapView, shadow); + } + + @Override + public void superPopulate() { + populate(); + } + + @Override + public Drawable superBoundCenter(Drawable markerIn) { + return super.boundCenter(markerIn); + } + + @Override + public Drawable superBoundCenterBottom(Drawable marker) { + return super.boundCenterBottom(marker); + } + + @Override + public void superSetLastFocusedItemIndex(int i) { + super.setLastFocusedIndex(i); + } + + @Override + public boolean superOnTap(int index) { + return super.onTap(index); + } + + @Override + public void superDraw(Canvas canvas, MapViewImpl mapView, boolean shadow) { + super.draw(canvas, (MapView) mapView, shadow); + } + + @Override + public void superDrawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel) { + // Nothing to do here + } + +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/googlemaps/googleUsersOverlayItem.java b/src/cgeo/geocaching/googlemaps/googleUsersOverlayItem.java new file mode 100644 index 0000000..c3d404e --- /dev/null +++ b/src/cgeo/geocaching/googlemaps/googleUsersOverlayItem.java @@ -0,0 +1,43 @@ +package cgeo.geocaching.googlemaps; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import cgeo.geocaching.R; +import cgeo.geocaching.cgUser; +import cgeo.geocaching.mapinterfaces.UserOverlayItemImpl; + +import com.google.android.maps.GeoPoint; +import com.google.android.maps.OverlayItem; + +public class googleUsersOverlayItem extends OverlayItem implements UserOverlayItemImpl { + private Context context = null; + private cgUser user = null; + + public googleUsersOverlayItem(Context contextIn, cgUser userIn) { + super(new GeoPoint((int)(userIn.latitude * 1e6), (int)(userIn.longitude * 1e6)), userIn.username, ""); + + context = contextIn; + user = userIn; + } + + @Override + public Drawable getMarker(int state) { + Drawable marker = null; + + if (user != null && user.located != null && user.located.getTime() >= (System.currentTimeMillis() - (20 * 60 * 1000))) { + marker = context.getResources().getDrawable(R.drawable.user_location_active); + } else { + marker = context.getResources().getDrawable(R.drawable.user_location); + } + + marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight()); + marker.setAlpha(190); + setMarker(marker); + + return marker; + } + + public cgUser getUser() { + return user; + } +} diff --git a/src/cgeo/geocaching/mapcommon/ItemizedOverlayBase.java b/src/cgeo/geocaching/mapcommon/ItemizedOverlayBase.java new file mode 100644 index 0000000..41739e2 --- /dev/null +++ b/src/cgeo/geocaching/mapcommon/ItemizedOverlayBase.java @@ -0,0 +1,57 @@ +package cgeo.geocaching.mapcommon; + +import android.graphics.Canvas; +import android.graphics.Point; +import android.graphics.drawable.Drawable; +import cgeo.geocaching.mapinterfaces.ItemizedOverlayImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.MapViewImpl; +import cgeo.geocaching.mapinterfaces.OverlayItemImpl; + +/** + * Base class for itemized overlays. Delegates calls from deriving classes to the contained + * provider-specific implementation. + * @author rsudev + * + */ +public abstract class ItemizedOverlayBase { + + private ItemizedOverlayImpl ovlImpl; + + public ItemizedOverlayBase(ItemizedOverlayImpl ovlImplIn) { + ovlImpl = ovlImplIn; + } + + void populate() { + ovlImpl.superPopulate(); + } + + public boolean onTap(int index) { + return ovlImpl.superOnTap(index); + } + + Drawable boundCenter(Drawable markerIn) { + return ovlImpl.superBoundCenter(markerIn); + } + + Drawable boundCenterBottom(Drawable markerIn) { + return ovlImpl.superBoundCenterBottom(markerIn); + } + + void setLastFocusedItemIndex(int index){ + ovlImpl.superSetLastFocusedItemIndex(index); + } + + public void draw(Canvas canvas, MapViewImpl mapView, boolean shadow) { + ovlImpl.superDraw(canvas, mapView, shadow); + } + + public void drawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel) { + ovlImpl.superDrawOverlayBitmap(canvas, drawPosition, projection, drawZoomLevel); + } + + public abstract OverlayItemImpl createItem(int index); + + public abstract int size(); +} diff --git a/src/cgeo/geocaching/mapcommon/MapBase.java b/src/cgeo/geocaching/mapcommon/MapBase.java new file mode 100644 index 0000000..2340998 --- /dev/null +++ b/src/cgeo/geocaching/mapcommon/MapBase.java @@ -0,0 +1,64 @@ +package cgeo.geocaching.mapcommon; + +import cgeo.geocaching.mapinterfaces.ActivityImpl; +import android.app.Activity; +import android.content.res.Resources; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +/** + * Base class for the map activity. Delegates base class calls to the + * provider-specific implementation. + * @author rsudev + * + */ +public class MapBase { + + ActivityImpl mapActivity; + + public MapBase(ActivityImpl activity) { + mapActivity = activity; + } + + public Resources getResources() { + return mapActivity.getResources(); + } + + public Activity getActivity() { + return mapActivity.getActivity(); + } + + public void onCreate(Bundle savedInstanceState) { + mapActivity.superOnCreate(savedInstanceState); + } + + public void onResume() { + mapActivity.superOnResume(); + } + + public void onStop() { + mapActivity.superOnResume(); + } + + public void onPause() { + mapActivity.superOnResume(); + } + + public void onDestroy() { + mapActivity.superOnDestroy(); + } + + public boolean onCreateOptionsMenu(Menu menu) { + return mapActivity.superOnCreateOptionsMenu(menu); + } + + public boolean onPrepareOptionsMenu(Menu menu) { + return mapActivity.superOnPrepareOptionsMenu(menu); + } + + public boolean onOptionsItemSelected(MenuItem item) { + return mapActivity.superOnOptionsItemSelected(item); + } + +} diff --git a/src/cgeo/geocaching/mapcommon/cgMapMyOverlay.java b/src/cgeo/geocaching/mapcommon/cgMapMyOverlay.java new file mode 100644 index 0000000..3951c14 --- /dev/null +++ b/src/cgeo/geocaching/mapcommon/cgMapMyOverlay.java @@ -0,0 +1,198 @@ +package cgeo.geocaching.mapcommon; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Paint.Style; +import android.graphics.PaintFlagsDrawFilter; +import android.location.Location; +import cgeo.geocaching.R; +import cgeo.geocaching.cgBase; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapFactory; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.MapViewImpl; + +import java.util.ArrayList; + +public class cgMapMyOverlay implements OverlayBase { + private cgSettings settings = null; + private Location coordinates = null; + private GeoPointImpl location = null; + private Double heading = new Double(0); + private Paint accuracyCircle = null; + private Paint historyLine = null; + private Paint historyLineShadow = null; + private Point center = new Point(); + private Point left = new Point(); + private Bitmap arrow = null; + private int widthArrow = 0; + private int heightArrow = 0; + private PaintFlagsDrawFilter setfil = null; + private PaintFlagsDrawFilter remfil = null; + private Location historyRecent = null; + private ArrayList<Location> history = new ArrayList<Location>(); + private Point historyPointN = new Point(); + private Point historyPointP = new Point(); + + public cgMapMyOverlay(cgSettings settingsIn) { + settings = settingsIn; + } + + public void setCoordinates(Location coordinatesIn) { + coordinates = coordinatesIn; + location = settings.getMapFactory().getGeoPointBase((int)(coordinatesIn.getLatitude() * 1e6), (int)(coordinatesIn.getLongitude() * 1e6)); + } + + public void setHeading(Double headingIn) { + heading = headingIn; + } + + @Override + public void drawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel) { + + drawInternal(canvas, projection); + } + + @Override + public void draw(Canvas canvas, MapViewImpl mapView, boolean shadow) { + + drawInternal(canvas, mapView.getMapProjection()); + } + + private void drawInternal(Canvas canvas, MapProjectionImpl projection) { + + if (coordinates == null || location == null) return; + + MapFactory mapFactory = settings.getMapFactory(); + + if (accuracyCircle == null) { + accuracyCircle = new Paint(); + accuracyCircle.setAntiAlias(true); + accuracyCircle.setStrokeWidth(1.0f); + } + + if (historyLine == null) { + historyLine = new Paint(); + historyLine.setAntiAlias(true); + historyLine.setStrokeWidth(3.0f); + historyLine.setColor(0xFFFFFFFF); + } + + if (historyLineShadow == null) { + historyLineShadow = new Paint(); + historyLineShadow.setAntiAlias(true); + historyLineShadow.setStrokeWidth(7.0f); + historyLineShadow.setColor(0x66000000); + } + + if (setfil == null) setfil = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG); + if (remfil == null) remfil = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0); + + canvas.setDrawFilter(setfil); + + double latitude = coordinates.getLatitude(); + double longitude = coordinates.getLongitude(); + float accuracy = coordinates.getAccuracy(); + + float[] result = new float[1]; + + Location.distanceBetween(latitude, longitude, latitude, longitude + 1, result); + float longitudeLineDistance = result[0]; + + GeoPointImpl leftGeo = mapFactory.getGeoPointBase((int)(latitude * 1e6), (int)((longitude - accuracy / longitudeLineDistance) * 1e6)); + projection.toPixels(leftGeo, left); + projection.toPixels(location, center); + int radius = center.x - left.x; + + accuracyCircle.setColor(0x66000000); + accuracyCircle.setStyle(Style.STROKE); + canvas.drawCircle(center.x, center.y, radius, accuracyCircle); + + accuracyCircle.setColor(0x08000000); + accuracyCircle.setStyle(Style.FILL); + canvas.drawCircle(center.x, center.y, radius, accuracyCircle); + + if (coordinates.getAccuracy() < 50f && ((historyRecent != null && cgBase.getDistance(historyRecent.getLatitude(), historyRecent.getLongitude(), coordinates.getLatitude(), coordinates.getLongitude()) > 0.005) || historyRecent == null)) { + if (historyRecent != null) history.add(historyRecent); + historyRecent = coordinates; + + int toRemove = history.size() - 700; + + if (toRemove > 0) { + for (int cnt = 0; cnt < toRemove; cnt ++) { + history.remove(cnt); + } + } + } + + if (settings.maptrail == 1) { + int size = history.size(); + if (size > 1) { + int alpha = 0; + int alphaCnt = size - 201; + if (alphaCnt < 1) alphaCnt = 1; + + for (int cnt = 1; cnt < size; cnt ++) { + Location prev = history.get(cnt - 1); + Location now = history.get(cnt); + + if (prev != null && now != null) { + projection.toPixels(mapFactory.getGeoPointBase((int)(prev.getLatitude() * 1e6), (int)(prev.getLongitude() * 1e6)), historyPointP); + projection.toPixels(mapFactory.getGeoPointBase((int)(now.getLatitude() * 1e6), (int)(now.getLongitude() * 1e6)), historyPointN); + + if ((alphaCnt - cnt) > 0) alpha = Math.round(255 / (alphaCnt - cnt)); + else alpha = 255; + + historyLineShadow.setAlpha(alpha); + historyLine.setAlpha(alpha); + + canvas.drawLine(historyPointP.x, historyPointP.y, historyPointN.x, historyPointN.y, historyLineShadow); + canvas.drawLine(historyPointP.x, historyPointP.y, historyPointN.x, historyPointN.y, historyLine); + } + } + } + + if (size > 0) { + Location prev = history.get(size - 1); + Location now = coordinates; + + if (prev != null && now != null) { + projection.toPixels(mapFactory.getGeoPointBase((int)(prev.getLatitude() * 1e6), (int)(prev.getLongitude() * 1e6)), historyPointP); + projection.toPixels(mapFactory.getGeoPointBase((int)(now.getLatitude() * 1e6), (int)(now.getLongitude() * 1e6)), historyPointN); + + historyLineShadow.setAlpha(255); + historyLine.setAlpha(255); + + canvas.drawLine(historyPointP.x, historyPointP.y, historyPointN.x, historyPointN.y, historyLineShadow); + canvas.drawLine(historyPointP.x, historyPointP.y, historyPointN.x, historyPointN.y, historyLine); + } + } + } + + if (arrow == null) { + arrow = BitmapFactory.decodeResource(settings.getContext().getResources(), R.drawable.my_location_chevron); + widthArrow = arrow.getWidth(); + heightArrow = arrow.getHeight(); + } + + int marginLeft; + int marginTop; + + marginLeft = center.x - (widthArrow / 2); + marginTop = center.y - (heightArrow / 2); + + canvas.rotate(new Float(heading), center.x, center.y); + canvas.drawBitmap(arrow, marginLeft, marginTop, null); + canvas.rotate(-(new Float(heading)), center.x, center.y); + + canvas.setDrawFilter(remfil); + + //super.draw(canvas, mapView, shadow); + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/mapcommon/cgMapOverlay.java b/src/cgeo/geocaching/mapcommon/cgMapOverlay.java new file mode 100644 index 0000000..7433ccb --- /dev/null +++ b/src/cgeo/geocaching/mapcommon/cgMapOverlay.java @@ -0,0 +1,322 @@ +package cgeo.geocaching.mapcommon; + +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.content.DialogInterface; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.Point; +import android.location.Location; +import android.text.Html; +import android.util.Log; +import cgeo.geocaching.cgBase; +import cgeo.geocaching.cgCoord; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.cgeodetail; +import cgeo.geocaching.cgeonavigate; +import cgeo.geocaching.cgeopopup; +import cgeo.geocaching.cgeowaypoint; +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.ItemizedOverlayImpl; +import cgeo.geocaching.mapinterfaces.MapFactory; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.MapViewImpl; +import cgeo.geocaching.mapinterfaces.CacheOverlayItemImpl; + +import java.util.ArrayList; + +import org.mapsforge.android.maps.Projection; + +public class cgMapOverlay extends ItemizedOverlayBase implements OverlayBase { + + private ArrayList<CacheOverlayItemImpl> items = new ArrayList<CacheOverlayItemImpl>(); + private Context context = null; + private Boolean fromDetail = false; + private boolean displayCircles = false; + private ProgressDialog waitDialog = null; + private Point center = new Point(); + private Point left = new Point(); + private Paint blockedCircle = null; + private PaintFlagsDrawFilter setfil = null; + private PaintFlagsDrawFilter remfil = null; + private cgSettings settings; + + public cgMapOverlay(cgSettings settingsIn, ItemizedOverlayImpl ovlImpl, Context contextIn, Boolean fromDetailIn) { + super(ovlImpl); + + populate(); + settings = settingsIn; + + context = contextIn; + fromDetail = fromDetailIn; + } + + public void updateItems(CacheOverlayItemImpl item) { + ArrayList<CacheOverlayItemImpl> itemsPre = new ArrayList<CacheOverlayItemImpl>(); + itemsPre.add(item); + + updateItems(itemsPre); + } + + public void updateItems(ArrayList<CacheOverlayItemImpl> itemsPre) { + if (itemsPre == null) { + return; + } + + for (CacheOverlayItemImpl item : itemsPre) { + item.setMarker(boundCenterBottom(item.getMarker(0))); + } + +// items.clear(); + +// if (itemsPre.size() > 0) { + items = (ArrayList<CacheOverlayItemImpl>) itemsPre.clone(); +// } + + setLastFocusedItemIndex(-1); // to reset tap during data change + populate(); + } + + public boolean getCircles() { + return displayCircles; + } + + public void switchCircles() { + displayCircles = !displayCircles; + } + + @Override + public void draw(Canvas canvas, MapViewImpl mapView, boolean shadow) { + + drawInternal(canvas, mapView.getMapProjection()); + + super.draw(canvas, mapView, false); + } + + @Override + public void drawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel) { + + drawInternal(canvas, projection); + + super.drawOverlayBitmap(canvas, drawPosition, projection, drawZoomLevel); + } + + private void drawInternal(Canvas canvas, MapProjectionImpl projection) { + + MapFactory mapFactory = settings.getMapFactory(); + + if (displayCircles) { + if (blockedCircle == null) { + blockedCircle = new Paint(); + blockedCircle.setAntiAlias(true); + blockedCircle.setStrokeWidth(1.0f); + } + + if (setfil == null) setfil = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG); + if (remfil == null) remfil = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0); + + canvas.setDrawFilter(setfil); + + for (CacheOverlayItemImpl item : items) { + final cgCoord itemCoord = item.getCoord(); + float[] result = new float[1]; + + Location.distanceBetween(itemCoord.latitude, itemCoord.longitude, itemCoord.latitude, itemCoord.longitude + 1, result); + final float longitudeLineDistance = result[0]; + + GeoPointImpl itemGeo = mapFactory.getGeoPointBase((int)(itemCoord.latitude * 1e6), (int)(itemCoord.longitude * 1e6)); + GeoPointImpl leftGeo = mapFactory.getGeoPointBase((int)(itemCoord.latitude * 1e6), (int)((itemCoord.longitude - 161 / longitudeLineDistance) * 1e6)); + + projection.toPixels(itemGeo, center); + projection.toPixels(leftGeo, left); + int radius = center.x - left.x; + + final String type = item.getType(); + if (type == null || "multi".equals(type) || "mystery".equals(type) || "virtual".equals(type)) { + blockedCircle.setColor(0x66000000); + blockedCircle.setStyle(Style.STROKE); + canvas.drawCircle(center.x, center.y, radius, blockedCircle); + } else { + blockedCircle.setColor(0x66BB0000); + blockedCircle.setStyle(Style.STROKE); + canvas.drawCircle(center.x, center.y, radius, blockedCircle); + + blockedCircle.setColor(0x44BB0000); + blockedCircle.setStyle(Style.FILL); + canvas.drawCircle(center.x, center.y, radius, blockedCircle); + } + } + + canvas.setDrawFilter(remfil); + } + } + + @Override + public boolean onTap(int index) { + try { + if (items.size() <= index) { + return false; + } + + if (waitDialog == null) { + waitDialog = new ProgressDialog(context); + waitDialog.setMessage("loading details..."); + waitDialog.setCancelable(false); + } + waitDialog.show(); + + CacheOverlayItemImpl item = items.get(index); + cgCoord coordinate = item.getCoord(); + + if (coordinate.type != null && coordinate.type.equalsIgnoreCase("cache") == true && coordinate.geocode != null && coordinate.geocode.length() > 0) { + Intent popupIntent = new Intent(context, cgeopopup.class); + + popupIntent.putExtra("fromdetail", fromDetail); + popupIntent.putExtra("geocode", coordinate.geocode); + + context.startActivity(popupIntent); + } else if (coordinate.type != null && coordinate.type.equalsIgnoreCase("waypoint") == true && coordinate.id != null && coordinate.id > 0) { + Intent popupIntent = new Intent(context, cgeowaypoint.class); + + popupIntent.putExtra("waypoint", coordinate.id); + popupIntent.putExtra("geocode", coordinate.geocode); + + context.startActivity(popupIntent); + } else { + waitDialog.dismiss(); + return false; + } + + waitDialog.dismiss(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgMapOverlay.onTap: " + e.toString()); + } + + return false; + } + + @Override + public CacheOverlayItemImpl createItem(int index) { + try { + return items.get(index); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgMapOverlay.createItem: " + e.toString()); + } + + return null; + } + + @Override + public int size() { + try { + return items.size(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgMapOverlay.size: " + e.toString()); + } + + return 0; + } + + public void infoDialog(int index) { + final CacheOverlayItemImpl item = items.get(index); + final cgCoord coordinate = item.getCoord(); + + if (coordinate == null) { + Log.e(cgSettings.tag, "cgMapOverlay:infoDialog: No coordinates given"); + return; + } + + try { + AlertDialog.Builder dialog = new AlertDialog.Builder(context); + dialog.setCancelable(true); + + if (coordinate.type.equalsIgnoreCase("cache")) { + dialog.setTitle("cache"); + + String cacheType; + if (cgBase.cacheTypesInv.containsKey(coordinate.typeSpec) == true) { + cacheType = cgBase.cacheTypesInv.get(coordinate.typeSpec); + } else { + cacheType = cgBase.cacheTypesInv.get("mystery"); + } + + dialog.setMessage(Html.fromHtml(item.getTitle()) + "\n\ngeocode: " + coordinate.geocode.toUpperCase() + "\ntype: " + cacheType); + if (fromDetail == false) { + dialog.setPositiveButton("detail", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + Intent cachesIntent = new Intent(context, cgeodetail.class); + cachesIntent.putExtra("geocode", coordinate.geocode.toUpperCase()); + context.startActivity(cachesIntent); + + dialog.cancel(); + } + }); + } else { + dialog.setPositiveButton("navigate", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + cgeonavigate navigateActivity = new cgeonavigate(); + + cgeonavigate.coordinates = new ArrayList<cgCoord>(); + cgeonavigate.coordinates.add(coordinate); + + Intent navigateIntent = new Intent(context, navigateActivity.getClass()); + navigateIntent.putExtra("latitude", coordinate.latitude); + navigateIntent.putExtra("longitude", coordinate.longitude); + navigateIntent.putExtra("geocode", coordinate.geocode.toUpperCase()); + context.startActivity(navigateIntent); + dialog.cancel(); + } + }); + } + } else { + dialog.setTitle("waypoint"); + + String waypointType; + if (cgBase.cacheTypesInv.containsKey(coordinate.typeSpec) == true) { + waypointType = cgBase.waypointTypes.get(coordinate.typeSpec); + } else { + waypointType = cgBase.waypointTypes.get("waypoint"); + } + + dialog.setMessage(Html.fromHtml(item.getTitle()) + "\n\ntype: " + waypointType); + dialog.setPositiveButton("navigate", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + cgeonavigate navigateActivity = new cgeonavigate(); + + cgeonavigate.coordinates = new ArrayList<cgCoord>(); + cgeonavigate.coordinates.add(coordinate); + + Intent navigateIntent = new Intent(context, navigateActivity.getClass()); + navigateIntent.putExtra("latitude", coordinate.latitude); + navigateIntent.putExtra("longitude", coordinate.longitude); + navigateIntent.putExtra("geocode", coordinate.name); + + context.startActivity(navigateIntent); + dialog.cancel(); + } + }); + } + + dialog.setNegativeButton("dismiss", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog alert = dialog.create(); + alert.show(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgMapOverlay.infoDialog: " + e.toString()); + } + } +} diff --git a/src/cgeo/geocaching/mapcommon/cgOverlayScale.java b/src/cgeo/geocaching/mapcommon/cgOverlayScale.java new file mode 100644 index 0000000..1e4a6e6 --- /dev/null +++ b/src/cgeo/geocaching/mapcommon/cgOverlayScale.java @@ -0,0 +1,140 @@ +package cgeo.geocaching.mapcommon; + +import android.app.Activity; +import android.graphics.BlurMaskFilter; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.Typeface; +import android.util.DisplayMetrics; +import cgeo.geocaching.cgBase; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.MapViewImpl; + +public class cgOverlayScale implements OverlayBase { + private cgSettings settings = null; + private Paint scale = null; + private Paint scaleShadow = null; + private BlurMaskFilter blur = null; + private float pixelDensity = 0l; + private double pixels = 0d; + private int bottom = 0; + private double distance = 0d; + private double distanceRound = 0d; + private String units = null; + + public cgOverlayScale(Activity activity, cgSettings settingsIn) { + settings = settingsIn; + + DisplayMetrics metrics = new DisplayMetrics(); + activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); + pixelDensity = metrics.density; + } + + @Override + public void drawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel) { + // TODO Auto-generated method stub + + } + + @Override + public void draw(Canvas canvas, MapViewImpl mapView, boolean shadow) { + //super.draw(canvas, mapView, shadow); + + final double span = mapView.getLongitudeSpan() / 1e6; + final GeoPointImpl center = mapView.getMapViewCenter(); + + pixels = mapView.getWidth() / 2; // pixels related to following latitude span + bottom = mapView.getHeight() - 14; // pixels from bottom side of screen + distance = cgBase.getDistance((center.getLatitudeE6() / 1e6), ((center.getLongitudeE6() / 1e6) - (span /2)), (center.getLatitudeE6() / 1e6), ((center.getLongitudeE6() / 1e6) + (span /2))); + distance = distance / 2; + distanceRound = 0d; + + if(settings.units == cgSettings.unitsImperial) { + distance *= cgBase.kmInMiles; + + if (distance > 100) { // 100+ mi > 1xx mi + distanceRound = Math.floor(distance / 100) * 100; + units = "mi"; + } else if (distance > 10) { // 10 - 100 mi > 1x mi + distanceRound = Math.floor(distance / 10) * 10; + units = "mi"; + } else if (distance > 1) { // 1 - 10 mi > 1.x mi + distanceRound = Math.floor(distance); + units = "mi"; + } else if (distance > 0.1) { // 0.1 mi - 1.0 mi > 1xx ft + distance *= 5280; + distanceRound = Math.floor(distance / 100) * 100; + units = "ft"; + } else { // 1 - 100 ft > 1x ft + distance *= 5280; + distanceRound = Math.round(distance / 10) * 10; + units = "ft"; + } + } else { + if (distance > 100) { // 100+ km > 1xx km + distanceRound = Math.floor(distance / 100) * 100; + units = "km"; + } else if (distance > 10) { // 10 - 100 km > 1x km + distanceRound = Math.floor(distance / 10) * 10; + units = "km"; + } else if (distance > 1) { // 1 - 10 km > 1.x km + distanceRound = Math.floor(distance); + units = "km"; + } else if (distance > 0.1) { // 100 m - 1 km > 1xx m + distance *= 1000; + distanceRound = Math.floor(distance / 100) * 100; + units = "m"; + } else { // 1 - 100 m > 1x m + distance *= 1000; + distanceRound = Math.round(distance / 10) * 10; + units = "m"; + } + } + + pixels = Math.round((pixels / distance) * distanceRound); + + if (blur == null) { + blur = new BlurMaskFilter(3, BlurMaskFilter.Blur.NORMAL); + } + + if (scaleShadow == null) { + scaleShadow = new Paint(); + scaleShadow.setAntiAlias(true); + scaleShadow.setStrokeWidth(4 * pixelDensity); + scaleShadow.setMaskFilter(blur); + scaleShadow.setTextSize(14 * pixelDensity); + scaleShadow.setTypeface(Typeface.DEFAULT_BOLD); + } + + if (scale == null) { + scale = new Paint(); + scale.setAntiAlias(true); + scale.setStrokeWidth(2 * pixelDensity); + scale.setTextSize(14 * pixelDensity); + scale.setTypeface(Typeface.DEFAULT_BOLD); + } + + if (mapView.isSatellite()) { + scaleShadow.setColor(0xFF000000); + scale.setColor(0xFFFFFFFF); + } else { + scaleShadow.setColor(0xFFFFFFFF); + scale.setColor(0xFF000000); + } + + canvas.drawLine(10, bottom, 10, (bottom - (8 * pixelDensity)), scaleShadow); + canvas.drawLine((int)(pixels + 10), bottom, (int)(pixels + 10), (bottom - (8 * pixelDensity)), scaleShadow); + canvas.drawLine(8, bottom, (int)(pixels + 12), bottom, scaleShadow); + canvas.drawText(String.format("%.0f", distanceRound) + " " + units, (float)(pixels - (10 * pixelDensity)), (bottom - (10 * pixelDensity)), scaleShadow); + + canvas.drawLine(11, bottom, 11, (bottom - (6 * pixelDensity)), scale); + canvas.drawLine((int)(pixels + 9), bottom, (int)(pixels + 9), (bottom - (6 * pixelDensity)), scale); + canvas.drawLine(10, bottom, (int)(pixels + 10), bottom, scale); + canvas.drawText(String.format("%.0f", distanceRound) + " " + units, (float)(pixels - (10 * pixelDensity)), (bottom - (10 * pixelDensity)), scale); + } +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/mapcommon/cgUsersOverlay.java b/src/cgeo/geocaching/mapcommon/cgUsersOverlay.java new file mode 100644 index 0000000..8eb76cd --- /dev/null +++ b/src/cgeo/geocaching/mapcommon/cgUsersOverlay.java @@ -0,0 +1,185 @@ +package cgeo.geocaching.mapcommon; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Point; +import android.util.Log; + +import cgeo.geocaching.R; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.cgUser; +import cgeo.geocaching.cgeodetail; +import cgeo.geocaching.mapinterfaces.ItemizedOverlayImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.MapViewImpl; +import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.UserOverlayItemImpl; + +public class cgUsersOverlay extends ItemizedOverlayBase implements OverlayBase { + + private ArrayList<UserOverlayItemImpl> items = new ArrayList<UserOverlayItemImpl>(); + private Context context = null; + private final Pattern patternGeocode = Pattern.compile("^(GC[A-Z0-9]+)(\\: ?(.+))?$", Pattern.CASE_INSENSITIVE); + + public cgUsersOverlay(ItemizedOverlayImpl ovlImplIn, Context contextIn) { + super(ovlImplIn); + populate(); + + context = contextIn; + } + + protected void updateItems(UserOverlayItemImpl item) { + ArrayList<UserOverlayItemImpl> itemsPre = new ArrayList<UserOverlayItemImpl>(); + itemsPre.add(item); + + updateItems(itemsPre); + } + + public void updateItems(ArrayList<UserOverlayItemImpl> itemsPre) { + if (itemsPre == null) { + return; + } + + for (UserOverlayItemImpl item : itemsPre) { + item.setMarker(boundCenter(item.getMarker(0))); + } + + items.clear(); + + if (itemsPre.size() > 0) { + items = (ArrayList<UserOverlayItemImpl>) itemsPre.clone(); + } + + setLastFocusedItemIndex(-1); // to reset tap during data change + populate(); + } + + @Override + public boolean onTap(int index) { + try { + if (items.size() <= index) { + return false; + } + + final UserOverlayItemImpl item = items.get(index); + final cgUser user = item.getUser(); + + // set action + String action = null; + String geocode = null; + final Matcher matcherGeocode = patternGeocode.matcher(user.action.trim()); + + if (user.action.length() == 0 || user.action.equalsIgnoreCase("pending")) { + action = "Looking around"; + } else if (user.action.equalsIgnoreCase("tweeting")) { + action = "Tweeting"; + } else if (matcherGeocode.find() == true) { + if (matcherGeocode.group(1) != null) { + geocode = matcherGeocode.group(1).trim().toUpperCase(); + } + if (matcherGeocode.group(3) != null) { + action = "Heading to " + geocode + " (" + matcherGeocode.group(3).trim() + ")"; + } else { + action = "Heading to " + geocode; + } + } else { + action = user.action; + } + + // set icon + int icon = -1; + if (user.client.equalsIgnoreCase("c:geo") == true) { + icon = R.drawable.client_cgeo; + } else if (user.client.equalsIgnoreCase("preCaching") == true) { + icon = R.drawable.client_precaching; + } else if (user.client.equalsIgnoreCase("Handy Geocaching") == true) { + icon = R.drawable.client_handygeocaching; + } + + final AlertDialog.Builder dialog = new AlertDialog.Builder(context); + if (icon > -1) { + dialog.setIcon(icon); + } + dialog.setTitle(user.username); + dialog.setMessage(action); + dialog.setCancelable(true); + if (geocode != null && geocode.length() > 0) { + dialog.setPositiveButton(geocode + "?", new cacheDetails(geocode)); + } + dialog.setNeutralButton("Dismiss", new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + + AlertDialog alert = dialog.create(); + alert.show(); + + return true; + } catch (Exception e) { + Log.e(cgSettings.tag, "cgUsersOverlay.onTap: " + e.toString()); + } + + return false; + } + + @Override + public void draw(Canvas canvas, MapViewImpl mapView, boolean shadow) { + super.draw(canvas, mapView, false); + } + + @Override + public void drawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel) { + super.drawOverlayBitmap(canvas, drawPosition, projection, drawZoomLevel); + } + + @Override + public UserOverlayItemImpl createItem(int index) { + try { + return items.get(index); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgUsersOverlay.createItem: " + e.toString()); + } + + return null; + } + + @Override + public int size() { + try { + return items.size(); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgUsersOverlay.size: " + e.toString()); + } + + return 0; + } + + private class cacheDetails implements DialogInterface.OnClickListener { + + private String geocode = null; + + public cacheDetails(String geocodeIn) { + geocode = geocodeIn; + } + + public void onClick(DialogInterface dialog, int id) { + if (geocode != null) { + Intent detailIntent = new Intent(context, cgeodetail.class); + detailIntent.putExtra("geocode", geocode); + context.startActivity(detailIntent); + } + + dialog.cancel(); + } + } +} diff --git a/src/cgeo/geocaching/mapcommon/cgeomap.java b/src/cgeo/geocaching/mapcommon/cgeomap.java new file mode 100644 index 0000000..7ba7532 --- /dev/null +++ b/src/cgeo/geocaching/mapcommon/cgeomap.java @@ -0,0 +1,1686 @@ +package cgeo.geocaching.mapcommon; + +import gnu.android.app.appmanualclient.*; + +import android.app.Activity; +import android.app.ProgressDialog; +import java.util.ArrayList; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import cgeo.geocaching.R; +import cgeo.geocaching.cgBase; +import cgeo.geocaching.cgCache; +import cgeo.geocaching.cgCoord; +import cgeo.geocaching.cgDirection; +import cgeo.geocaching.cgGeo; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.cgUpdateDir; +import cgeo.geocaching.cgUpdateLoc; +import cgeo.geocaching.cgUser; +import cgeo.geocaching.cgWarning; +import cgeo.geocaching.cgWaypoint; +import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.mapinterfaces.ActivityImpl; +import cgeo.geocaching.mapinterfaces.CacheOverlayItemImpl; +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapControllerImpl; +import cgeo.geocaching.mapinterfaces.MapFactory; +import cgeo.geocaching.mapinterfaces.MapViewImpl; +import cgeo.geocaching.mapinterfaces.UserOverlayItemImpl; + +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; +import cgeo.geocaching.cgSearch; + +import java.util.HashMap; +import java.util.Locale; + +public class cgeomap extends MapBase { + + private Resources res = null; + private Activity activity = null; + private MapViewImpl mapView = null; + private MapControllerImpl mapController = null; + private cgSettings settings = null; + private cgBase base = null; + private cgWarning warning = null; + private cgeoapplication app = null; + private SharedPreferences.Editor prefsEdit = null; + private cgGeo geo = null; + private cgDirection dir = null; + private cgUpdateLoc geoUpdate = new UpdateLoc(); + private cgUpdateDir dirUpdate = new UpdateDir(); + // from intent + private boolean fromDetailIntent = false; + private Long searchIdIntent = null; + private String geocodeIntent = null; + private Double latitudeIntent = null; + private Double longitudeIntent = null; + private String waypointTypeIntent = null; + // status data + private Long searchId = null; + private String token = null; + private boolean noMapTokenShowed = false; + // map status data + private boolean followMyLocation = false; + private Integer centerLatitude = null; + private Integer centerLongitude = null; + private Integer spanLatitude = null; + private Integer spanLongitude = null; + private Integer centerLatitudeUsers = null; + private Integer centerLongitudeUsers = null; + private Integer spanLatitudeUsers = null; + private Integer spanLongitudeUsers = null; + // thread + private LoadTimer loadTimer = null; + private UsersTimer usersTimer = null; + private LoadThread loadThread = null; + private DownloadThread downloadThread = null; + private DisplayThread displayThread = null; + private UsersThread usersThread = null; + private DisplayUsersThread displayUsersThread = null; + private LoadDetails loadDetailsThread = null; + private volatile long loadThreadRun = 0l; + private volatile long downloadThreadRun = 0l; + private volatile long usersThreadRun = 0l; + private volatile boolean downloaded = false; + // overlays + private cgMapOverlay overlayCaches = null; + private cgUsersOverlay overlayUsers = null; + private cgOverlayScale overlayScale = null; + private cgMapMyOverlay overlayMyLoc = null; + // data for overlays + private int cachesCnt = 0; + private HashMap<Integer, Drawable> iconsCache = new HashMap<Integer, Drawable>(); + private ArrayList<cgCache> caches = new ArrayList<cgCache>(); + private ArrayList<cgUser> users = new ArrayList<cgUser>(); + private ArrayList<cgCoord> coordinates = new ArrayList<cgCoord>(); + // storing for offline + private ProgressDialog waitDialog = null; + private int detailTotal = 0; + private int detailProgress = 0; + private Long detailProgressTime = 0l; + // views + private ImageView myLocSwitch = null; + // other things + private boolean live = true; // live map (live, dead) or rest (displaying caches on map) + private boolean liveChanged = false; // previous state for loadTimer + private boolean centered = false; // if map is already centered + private boolean alreadyCentered = false; // -""- for setting my location + // handlers + final private Handler displayHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + final int what = msg.what; + + if (what == 0) { + // set title + final StringBuilder title = new StringBuilder(); + + if (live == true) { + title.append(res.getString(R.string.map_live)); + } else { + title.append(res.getString(R.string.map_map)); + } + + if (caches != null && cachesCnt > 0) { + title.append(" "); + title.append("["); + title.append(caches.size()); + title.append("]"); + } + + base.setTitle(activity, title.toString()); + } else if (what == 1 && mapView != null) { + mapView.invalidate(); + } + } + }; + final private Handler showProgressHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + final int what = msg.what; + + if (what == 0) { + base.showProgress(activity, false); + } else if (what == 1) { + base.showProgress(activity, true); + } + } + }; + final private Handler loadDetailsHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + if (msg.what == 0) { + if (waitDialog != null) { + Float diffTime = new Float((System.currentTimeMillis() - detailProgressTime) / 1000); // seconds left + Float oneCache = diffTime / detailProgress; // left time per cache + Float etaTime = (detailTotal - detailProgress) * oneCache; // seconds remaining + + waitDialog.setProgress(detailProgress); + if (etaTime < 40) { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + res.getString(R.string.caches_eta_ltm)); + } else if (etaTime < 90) { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + String.format(Locale.getDefault(), "%.0f", (etaTime / 60)) + " " + res.getString(R.string.caches_eta_min)); + } else { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + String.format(Locale.getDefault(), "%.0f", (etaTime / 60)) + " " + res.getString(R.string.caches_eta_mins)); + } + } + } else { + if (waitDialog != null) { + waitDialog.dismiss(); + waitDialog.setOnCancelListener(null); + } + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + if (settings.useCompass == 1 && dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + } + } + }; + final private Handler noMapTokenHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + if (!noMapTokenShowed) { + warning.showToast(res.getString(R.string.map_token_err)); + + noMapTokenShowed = true; + } + } + }; + + public cgeomap(ActivityImpl activity) { + super(activity); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // class init + res = this.getResources(); + activity = this.getActivity(); + app = (cgeoapplication) activity.getApplication(); + app.setAction(null); + settings = new cgSettings(activity, activity.getSharedPreferences(cgSettings.preferences, 0)); + base = new cgBase(app, settings, activity.getSharedPreferences(cgSettings.preferences, 0)); + warning = new cgWarning(activity); + prefsEdit = activity.getSharedPreferences(cgSettings.preferences, 0).edit(); + MapFactory mapFactory = settings.getMapFactory(); + + // reset status + noMapTokenShowed = false; + + // set layout + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + // set layout + if (settings.skin == 1) { + activity.setTheme(R.style.light); + } else { + activity.setTheme(R.style.dark); + } + activity.setContentView(settings.getMapFactory().getMapLayoutId()); + base.setTitle(activity, res.getString(R.string.map_map)); + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + if (settings.useCompass == 1 && dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + + mapView = (MapViewImpl) activity.findViewById(mapFactory.getMapViewId()); + mapView.setMapSource(settings); + if (!mapView.needsScaleOverlay()) { + mapView.setBuiltinScale(true); + } + + // initialize map + if (settings.maptype == cgSettings.mapSatellite) { + mapView.setSatellite(true); + } else { + mapView.setSatellite(false); + } + mapView.setBuiltInZoomControls(true); + mapView.displayZoomControls(true); + mapView.preLoad(); + + // initialize overlays + mapView.clearOverlays(); + + if (overlayMyLoc == null) { + overlayMyLoc = new cgMapMyOverlay(settings); + mapView.addOverlay(mapFactory.getOverlayBaseWrapper(overlayMyLoc)); + } + + if (settings.publicLoc > 0 && overlayUsers == null) { + overlayUsers = mapView.createAddUsersOverlay(activity, getResources().getDrawable(R.drawable.user_location)); + } + + if (overlayCaches == null) { + overlayCaches = mapView.createAddMapOverlay(settings, mapView.getContext(), getResources().getDrawable(R.drawable.marker), fromDetailIntent); + } + + if (overlayScale == null && mapView.needsScaleOverlay()) { + overlayScale = new cgOverlayScale(activity, settings); + mapView.addOverlay(mapFactory.getOverlayBaseWrapper(overlayScale)); + } + + mapView.invalidate(); + + mapController = mapView.getMapController(); + mapController.setZoom(settings.mapzoom); + + // start location and directory services + if (geo != null) { + geoUpdate.updateLoc(geo); + } + if (dir != null) { + dirUpdate.updateDir(dir); + } + + // get parameters + Bundle extras = activity.getIntent().getExtras(); + if (extras != null) { + fromDetailIntent = extras.getBoolean("detail"); + searchIdIntent = extras.getLong("searchid"); + geocodeIntent = extras.getString("geocode"); + latitudeIntent = extras.getDouble("latitude"); + longitudeIntent = extras.getDouble("longitude"); + waypointTypeIntent = extras.getString("wpttype"); + + if (searchIdIntent == 0l) { + searchIdIntent = null; + } + if (latitudeIntent == 0.0) { + latitudeIntent = null; + } + if (longitudeIntent == 0.0) { + longitudeIntent = null; + } + } + + // live or death + if (searchIdIntent == null && geocodeIntent == null && (latitudeIntent == null || longitudeIntent == null)) { + live = true; + } else { + live = false; + } + + // google analytics + if (live) { + base.sendAnal(activity, "/map/live"); + + followMyLocation = true; + } else { + base.sendAnal(activity, "/map/normal"); + + followMyLocation = false; + + if (geocodeIntent != null || searchIdIntent != null || (latitudeIntent != null && longitudeIntent != null)) { + centerMap(geocodeIntent, searchIdIntent, latitudeIntent, longitudeIntent); + } + } + setMyLoc(null); + startTimer(); + } + + @Override + public void onResume() { + super.onResume(); + + settings.load(); + + app.setAction(null); + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + if (settings.useCompass == 1 && dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + + if (geo != null) { + geoUpdate.updateLoc(geo); + } + if (dir != null) { + dirUpdate.updateDir(dir); + } + + startTimer(); + } + + @Override + public void onStop() { + if (loadTimer != null) { + loadTimer.stopIt(); + loadTimer = null; + } + + if (usersTimer != null) { + usersTimer.stopIt(); + usersTimer = null; + } + + if (dir != null) { + dir = app.removeDir(); + } + if (geo != null) { + geo = app.removeGeo(); + } + + savePrefs(); + + if (mapView != null) { + mapView.destroyDrawingCache(); + } + + super.onStop(); + } + + @Override + public void onPause() { + if (loadTimer != null) { + loadTimer.stopIt(); + loadTimer = null; + } + + if (usersTimer != null) { + usersTimer.stopIt(); + usersTimer = null; + } + + if (dir != null) { + dir = app.removeDir(); + } + if (geo != null) { + geo = app.removeGeo(); + } + + savePrefs(); + + if (mapView != null) { + mapView.destroyDrawingCache(); + } + + super.onPause(); + } + + @Override + public void onDestroy() { + if (loadTimer != null) { + loadTimer.stopIt(); + loadTimer = null; + } + + if (usersTimer != null) { + usersTimer.stopIt(); + usersTimer = null; + } + + if (dir != null) { + dir = app.removeDir(); + } + if (geo != null) { + geo = app.removeGeo(); + } + + savePrefs(); + + if (mapView != null) { + mapView.destroyDrawingCache(); + } + + super.onDestroy(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, 1, 0, res.getString(R.string.caches_on_map)).setIcon(android.R.drawable.ic_menu_mapmode); + menu.add(0, 3, 0, res.getString(R.string.map_live_disable)).setIcon(android.R.drawable.ic_menu_close_clear_cancel); + menu.add(0, 4, 0, res.getString(R.string.caches_store_offline)).setIcon(android.R.drawable.ic_menu_set_as).setEnabled(false); + menu.add(0, 2, 0, res.getString(R.string.map_trail_hide)).setIcon(android.R.drawable.ic_menu_recent_history); + menu.add(0, 5, 0, res.getString(R.string.map_circles_hide)).setIcon(android.R.drawable.ic_menu_view); + + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + MenuItem item; + try { + item = menu.findItem(1); // view + if (mapView != null && mapView.isSatellite() == false) { + item.setTitle(res.getString(R.string.map_view_satellite)); + } else { + item.setTitle(res.getString(R.string.map_view_map)); + } + + item = menu.findItem(2); // show trail + if (settings.maptrail == 1) { + item.setTitle(res.getString(R.string.map_trail_hide)); + } else { + item.setTitle(res.getString(R.string.map_trail_show)); + } + + item = menu.findItem(3); // live map + if (live == false) { + item.setEnabled(false); + item.setTitle(res.getString(R.string.map_live_enable)); + } else { + if (settings.maplive == 1) { + item.setTitle(res.getString(R.string.map_live_disable)); + } else { + item.setTitle(res.getString(R.string.map_live_enable)); + } + } + + item = menu.findItem(4); // store loaded + if (live && !isLoading() && app.getNotOfflineCount(searchId) > 0 && caches != null && caches.size() > 0) { + item.setEnabled(true); + } else { + item.setEnabled(false); + } + + item = menu.findItem(5); // show circles + if (overlayCaches != null && overlayCaches.getCircles()) { + item.setTitle(res.getString(R.string.map_circles_hide)); + } else { + item.setTitle(res.getString(R.string.map_circles_show)); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeomap.onPrepareOptionsMenu: " + e.toString()); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int id = item.getItemId(); + + if (id == 1) { + if (mapView != null && mapView.isSatellite() == false) { + mapView.setSatellite(true); + + prefsEdit.putInt("maptype", cgSettings.mapSatellite); + prefsEdit.commit(); + } else { + mapView.setSatellite(false); + + prefsEdit.putInt("maptype", cgSettings.mapClassic); + prefsEdit.commit(); + } + + return true; + } else if (id == 2) { + if (settings.maptrail == 1) { + prefsEdit.putInt("maptrail", 0); + prefsEdit.commit(); + + settings.maptrail = 0; + } else { + prefsEdit.putInt("maptrail", 1); + prefsEdit.commit(); + + settings.maptrail = 1; + } + } else if (id == 3) { + if (settings.maplive == 1) { + settings.liveMapDisable(); + } else { + settings.liveMapEnable(); + } + liveChanged = true; + searchId = null; + searchIdIntent = null; + } else if (id == 4) { + if (live && !isLoading() && caches != null && !caches.isEmpty()) { + final ArrayList<String> geocodes = new ArrayList<String>(); + + ArrayList<cgCache> cachesProtected = (ArrayList<cgCache>) caches.clone(); + try { + if (cachesProtected != null && cachesProtected.size() > 0) { + final GeoPointImpl mapCenter = mapView.getMapViewCenter(); + final int mapCenterLat = mapCenter.getLatitudeE6(); + final int mapCenterLon = mapCenter.getLongitudeE6(); + final int mapSpanLat = mapView.getLatitudeSpan(); + final int mapSpanLon = mapView.getLongitudeSpan(); + + for (cgCache oneCache : cachesProtected) { + if (oneCache != null && oneCache.latitude != null && oneCache.longitude != null) { + if (base.isCacheInViewPort(mapCenterLat, mapCenterLon, mapSpanLat, mapSpanLon, oneCache.latitude, oneCache.longitude) && app.isOffline(oneCache.geocode, null) == false) { + geocodes.add(oneCache.geocode); + } + } + } + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeomap.onOptionsItemSelected.#4: " + e.toString()); + } + + detailTotal = geocodes.size(); + + if (detailTotal == 0) { + warning.showToast(res.getString(R.string.warn_save_nothing)); + + return true; + } + + waitDialog = new ProgressDialog(activity); + waitDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + waitDialog.setCancelable(true); + waitDialog.setMax(detailTotal); + waitDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + + public void onCancel(DialogInterface arg0) { + try { + if (loadDetailsThread != null) { + loadDetailsThread.stopIt(); + } + + if (geo == null) { + geo = app.startGeo(activity, geoUpdate, base, settings, warning, 0, 0); + } + if (settings.useCompass == 1 && dir == null) { + dir = app.startDir(activity, dirUpdate, warning); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeocaches.onPrepareOptionsMenu.onCancel: " + e.toString()); + } + } + }); + + Float etaTime = new Float((detailTotal * (float) 7) / 60); + if (etaTime < 0.4) { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + res.getString(R.string.caches_eta_ltm)); + } else if (etaTime < 1.5) { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + String.format(Locale.getDefault(), "%.0f", etaTime) + " " + res.getString(R.string.caches_eta_min)); + } else { + waitDialog.setMessage(res.getString(R.string.caches_downloading) + " " + String.format(Locale.getDefault(), "%.0f", etaTime) + " " + res.getString(R.string.caches_eta_mins)); + } + waitDialog.show(); + + detailProgressTime = System.currentTimeMillis(); + + loadDetailsThread = new LoadDetails(loadDetailsHandler, geocodes); + loadDetailsThread.start(); + + return true; + } + } else if (id == 5) { + if (overlayCaches == null) { + return false; + } + + overlayCaches.switchCircles(); + } + + return false; + } + + private void savePrefs() { + if (mapView == null) { + return; + } + + if (mapView.isSatellite()) { + prefsEdit.putInt("maptype", cgSettings.mapSatellite); + settings.maptype = cgSettings.mapSatellite; + } else { + prefsEdit.putInt("maptype", cgSettings.mapClassic); + settings.maptype = cgSettings.mapClassic; + } + + if (prefsEdit == null) { + prefsEdit = activity.getSharedPreferences(cgSettings.preferences, 0).edit(); + } + prefsEdit.putInt("mapzoom", mapView.getMapZoomLevel()); + prefsEdit.commit(); + } + + // set center of map to my location + private void myLocationInMiddle() { + if (geo == null) { + return; + } + if (!followMyLocation) { + return; + } + + centerMap(geo.latitudeNow, geo.longitudeNow); + } + + // class: update location + private class UpdateLoc extends cgUpdateLoc { + + @Override + public void updateLoc(cgGeo geo) { + if (geo == null) { + return; + } + + try { + if (overlayMyLoc == null && mapView != null) { + overlayMyLoc = new cgMapMyOverlay(settings); + mapView.addOverlay(settings.getMapFactory().getOverlayBaseWrapper(overlayMyLoc)); + } + + if (overlayMyLoc != null && geo.location != null) { + overlayMyLoc.setCoordinates(geo.location); + } + + if (geo.latitudeNow != null && geo.longitudeNow != null) { + if (followMyLocation == true) { + myLocationInMiddle(); + } + } + + if (settings.useCompass == 0 || (geo.speedNow != null && geo.speedNow > 5)) { // use GPS when speed is higher than 18 km/h + if (geo.bearingNow != null) { + overlayMyLoc.setHeading(geo.bearingNow); + } else { + overlayMyLoc.setHeading(new Double(0)); + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "Failed to update location."); + } + } + } + + // class: update direction + private class UpdateDir extends cgUpdateDir { + + @Override + public void updateDir(cgDirection dir) { + if (dir == null || dir.directionNow == null) { + return; + } + + if (overlayMyLoc != null && mapView != null && (geo == null || geo.speedNow == null || geo.speedNow <= 5)) { // use compass when speed is lower than 18 km/h + overlayMyLoc.setHeading(dir.directionNow); + mapView.invalidate(); + } + } + } + + public void startTimer() { + if (latitudeIntent != null && longitudeIntent != null) { + // display just one point + (new DisplayPointThread()).start(); + } else { + // start timer + if (loadTimer != null) { + loadTimer.stopIt(); + loadTimer = null; + } + loadTimer = new LoadTimer(); + loadTimer.start(); + } + + if (settings.publicLoc > 0) { + if (usersTimer != null) { + usersTimer.stopIt(); + usersTimer = null; + } + usersTimer = new UsersTimer(); + usersTimer.start(); + } + } + + // loading timer + private class LoadTimer extends Thread { + + private volatile boolean stop = false; + + public void stopIt() { + stop = true; + + if (loadThread != null) { + loadThread.stopIt(); + loadThread = null; + } + + if (downloadThread != null) { + downloadThread.stopIt(); + downloadThread = null; + } + + if (displayThread != null) { + displayThread.stopIt(); + displayThread = null; + } + } + + @Override + public void run() { + GeoPointImpl mapCenterNow; + int centerLatitudeNow; + int centerLongitudeNow; + int spanLatitudeNow; + int spanLongitudeNow; + boolean moved = false; + boolean force = false; + long currentTime = 0; + + while (!stop) { + try { + sleep(250); + + if (mapView != null) { + // get current viewport + mapCenterNow = mapView.getMapViewCenter(); + centerLatitudeNow = mapCenterNow.getLatitudeE6(); + centerLongitudeNow = mapCenterNow.getLongitudeE6(); + spanLatitudeNow = mapView.getLatitudeSpan(); + spanLongitudeNow = mapView.getLongitudeSpan(); + + // check if map moved or zoomed + moved = false; + force = false; + + if (liveChanged) { + moved = true; + force = true; + } else if (live && settings.maplive == 1 && downloaded == false) { + moved = true; + } else if (centerLatitude == null || centerLongitude == null) { + moved = true; + } else if (spanLatitude == null || spanLongitude == null) { + moved = true; + } else if (((Math.abs(spanLatitudeNow - spanLatitude) > 50) || (Math.abs(spanLongitudeNow - spanLongitude) > 50) || // changed zoom + (Math.abs(centerLatitudeNow - centerLatitude) > (spanLatitudeNow / 4)) || (Math.abs(centerLongitudeNow - centerLongitude) > (spanLongitudeNow / 4)) // map moved + ) && (cachesCnt <= 0 || caches == null || caches.isEmpty() + || !base.isInViewPort(centerLatitude, centerLongitude, centerLatitudeNow, centerLongitudeNow, spanLatitude, spanLongitude, spanLatitudeNow, spanLongitudeNow))) { + moved = true; + } + + if (moved && caches != null && centerLatitude != null && centerLongitude != null && ((Math.abs(centerLatitudeNow - centerLatitude) > (spanLatitudeNow * 1.2)) || (Math.abs(centerLongitudeNow - centerLongitude) > (spanLongitudeNow * 1.2)))) { + force = true; + } + + //LeeB + // save new values + if (moved) { + liveChanged = false; + + currentTime = System.currentTimeMillis(); + + if (1000 < (currentTime - loadThreadRun)) { + // from web + if (20000 < (currentTime - loadThreadRun)) { + force = true; // probably stucked thread + } + + if (force && loadThread != null && loadThread.isWorking()) { + loadThread.stopIt(); + + try { + sleep(100); + } catch (Exception e) { + // nothing + } + } + + if (loadThread != null && loadThread.isWorking()) { + continue; + } + + centerLatitude = centerLatitudeNow; + centerLongitude = centerLongitudeNow; + spanLatitude = spanLatitudeNow; + spanLongitude = spanLongitudeNow; + + showProgressHandler.sendEmptyMessage(1); // show progress + + loadThread = new LoadThread(centerLatitude, centerLongitude, spanLatitude, spanLongitude); + loadThread.setName("loadThread"); + loadThread.start(); //loadThread will kick off downloadThread once it's done + } + } + } + + if (!isLoading()) { + showProgressHandler.sendEmptyMessage(0); // hide progress + } + + yield(); + } catch (Exception e) { + Log.w(cgSettings.tag, "cgeomap.LoadTimer.run: " + e.toString()); + } + }; + } + } + + // loading timer + private class UsersTimer extends Thread { + + private volatile boolean stop = false; + + public void stopIt() { + stop = true; + + if (usersThread != null) { + usersThread.stopIt(); + usersThread = null; + } + + if (displayUsersThread != null) { + displayUsersThread.stopIt(); + displayUsersThread = null; + } + } + + @Override + public void run() { + GeoPointImpl mapCenterNow; + int centerLatitudeNow; + int centerLongitudeNow; + int spanLatitudeNow; + int spanLongitudeNow; + boolean moved = false; + long currentTime = 0; + + while (!stop) { + try { + sleep(250); + + if (mapView != null) { + // get current viewport + mapCenterNow = mapView.getMapViewCenter(); + centerLatitudeNow = mapCenterNow.getLatitudeE6(); + centerLongitudeNow = mapCenterNow.getLongitudeE6(); + spanLatitudeNow = mapView.getLatitudeSpan(); + spanLongitudeNow = mapView.getLongitudeSpan(); + + // check if map moved or zoomed + moved = false; + + currentTime = System.currentTimeMillis(); + + if (60000 < (currentTime - usersThreadRun)) { + moved = true; + } else if (centerLatitudeUsers == null || centerLongitudeUsers == null) { + moved = true; + } else if (spanLatitudeUsers == null || spanLongitudeUsers == null) { + moved = true; + } else if (((Math.abs(spanLatitudeNow - spanLatitudeUsers) > 50) || (Math.abs(spanLongitudeNow - spanLongitudeUsers) > 50) || // changed zoom + (Math.abs(centerLatitudeNow - centerLatitudeUsers) > (spanLatitudeNow / 4)) || (Math.abs(centerLongitudeNow - centerLongitudeUsers) > (spanLongitudeNow / 4)) // map moved + ) && !base.isInViewPort(centerLatitudeUsers, centerLongitudeUsers, centerLatitudeNow, centerLongitudeNow, spanLatitudeUsers, spanLongitudeUsers, spanLatitudeNow, spanLongitudeNow)) { + moved = true; + } + + // save new values + if (moved && (1000 < (currentTime - usersThreadRun))) { + if (usersThread != null && usersThread.isWorking()) { + continue; + } + + centerLatitudeUsers = centerLatitudeNow; + centerLongitudeUsers = centerLongitudeNow; + spanLatitudeUsers = spanLatitudeNow; + spanLongitudeUsers = spanLongitudeNow; + + usersThread = new UsersThread(centerLatitude, centerLongitude, spanLatitude, spanLongitude); + usersThread.start(); + } + } + + yield(); + } catch (Exception e) { + Log.w(cgSettings.tag, "cgeomap.LoadUsersTimer.run: " + e.toString()); + } + }; + } + } + + // load caches from database + private class LoadThread extends DoThread { + + public LoadThread(long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { + super(centerLatIn, centerLonIn, spanLatIn, spanLonIn); + } + + @Override + public void run() { + try { + stop = false; + working = true; + loadThreadRun = System.currentTimeMillis(); + + if (stop) { + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + //LeeB - I think this can be done better: + //1. fetch and draw(in another thread) caches from the db (fast? db read will be the slow bit) + //2. fetch and draw(in another thread) and then insert into the db caches from geocaching.com - dont draw/insert if exist in memory? + + // stage 1 - pull and render from the DB only + if (settings.maplive == 0) { + searchId = app.getStoredInViewport(centerLat, centerLon, spanLat, spanLon, settings.cacheType); + } else { + searchId = app.getCachedInViewport(centerLat, centerLon, spanLat, spanLon, settings.cacheType); + } + + if (searchId != null) { + downloaded = true; + } + + if (stop) { + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + caches = app.getCaches(searchId); + + if (stop) { + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + //render + if (displayThread != null && displayThread.isWorking()) { + displayThread.stopIt(); + } + displayThread = new DisplayThread(centerLat, centerLon, spanLat, spanLon); + displayThread.start(); + + if (stop) { + displayThread.stopIt(); + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + //*** this needs to be in it's own thread + // stage 2 - pull and render from geocaching.com + //this should just fetch and insert into the db _and_ be cancel-able if the viewport changes + + if (settings.maplive >= 1) { + if (downloadThread != null && downloadThread.isWorking()) { + downloadThread.stopIt(); + } + downloadThread = new DownloadThread(centerLat, centerLon, spanLat, spanLon); + downloadThread.setName("downloadThread"); + downloadThread.start(); + } + } finally { + working = false; + } + } + } + + // load caches from internet + private class DownloadThread extends DoThread { + + public DownloadThread(long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { + super(centerLatIn, centerLonIn, spanLatIn, spanLonIn); + } + + @Override + public void run() { //first time we enter we have crappy long/lat.... + try { + stop = false; + working = true; + downloadThreadRun = System.currentTimeMillis(); + + if (stop) { + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + double latMin = (centerLat / 1e6) - ((spanLat / 1e6) / 2) - ((spanLat / 1e6) / 4); + double latMax = (centerLat / 1e6) + ((spanLat / 1e6) / 2) + ((spanLat / 1e6) / 4); + double lonMin = (centerLon / 1e6) - ((spanLon / 1e6) / 2) - ((spanLon / 1e6) / 4); + double lonMax = (centerLon / 1e6) + ((spanLon / 1e6) / 2) + ((spanLon / 1e6) / 4); + double llCache; + + if (latMin > latMax) { + llCache = latMax; + latMax = latMin; + latMin = llCache; + } + if (lonMin > lonMax) { + llCache = lonMax; + lonMax = lonMin; + lonMin = llCache; + } + + //*** this needs to be in it's own thread + // stage 2 - pull and render from geocaching.com + //this should just fetch and insert into the db _and_ be cancel-able if the viewport changes + + if (token == null) { + token = base.getMapUserToken(noMapTokenHandler); + } + + if (stop) { + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + HashMap<String, String> params = new HashMap<String, String>(); + params.put("usertoken", token); + params.put("latitude-min", String.format((Locale) null, "%.6f", latMin)); + params.put("latitude-max", String.format((Locale) null, "%.6f", latMax)); + params.put("longitude-min", String.format((Locale) null, "%.6f", lonMin)); + params.put("longitude-max", String.format((Locale) null, "%.6f", lonMax)); + + searchId = base.searchByViewport(params, 0); + if (searchId != null) { + downloaded = true; + } + + if (stop) { + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + caches = app.getCaches(searchId, centerLat, centerLon, spanLat, spanLon); + + if (stop) { + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + //render + if (displayThread != null && displayThread.isWorking()) { + displayThread.stopIt(); + } + displayThread = new DisplayThread(centerLat, centerLon, spanLat, spanLon); + displayThread.start(); + + } finally { + working = false; + } + } + } + + // display (down)loaded caches + private class DisplayThread extends DoThread { + + public DisplayThread(long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { + super(centerLatIn, centerLonIn, spanLatIn, spanLonIn); + } + + @Override + public void run() { + try { + stop = false; + working = true; + + if (mapView == null || caches == null) { + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + // display caches + final ArrayList<cgCache> cachesProtected = (ArrayList<cgCache>) caches.clone(); + final ArrayList<CacheOverlayItemImpl> items = new ArrayList<CacheOverlayItemImpl>(); + + if (cachesProtected != null && !cachesProtected.isEmpty()) { + int counter = 0; + int icon = 0; + Drawable pin = null; + CacheOverlayItemImpl item = null; + + for (cgCache cacheOne : cachesProtected) { + if (stop) { + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + if (cacheOne.latitude == null && cacheOne.longitude == null) { + continue; + } + + final cgCoord coord = new cgCoord(cacheOne); + coordinates.add(coord); + + item = settings.getMapFactory().getCacheOverlayItem(coord, cacheOne.type); + icon = base.getIcon(true, cacheOne.type, cacheOne.own, cacheOne.found, cacheOne.disabled || cacheOne.archived); + pin = null; + + if (iconsCache.containsKey(icon)) { + pin = iconsCache.get(icon); + } else { + pin = getResources().getDrawable(icon); + pin.setBounds(0, 0, pin.getIntrinsicWidth(), pin.getIntrinsicHeight()); + + iconsCache.put(icon, pin); + } + item.setMarker(pin); + + items.add(item); + + /* + counter++; + if ((counter % 10) == 0) { + overlayCaches.updateItems(items); + displayHandler.sendEmptyMessage(1); + } + */ + } + + overlayCaches.updateItems(items); + displayHandler.sendEmptyMessage(1); + + cachesCnt = cachesProtected.size(); + + if (stop) { + displayHandler.sendEmptyMessage(0); + working = false; + + return; + } + + // display cache waypoints + if (cachesCnt == 1 && (geocodeIntent != null || searchIdIntent != null) && !live) { + if (cachesCnt == 1 && live == false) { + cgCache oneCache = cachesProtected.get(0); + + if (oneCache != null && oneCache.waypoints != null && !oneCache.waypoints.isEmpty()) { + for (cgWaypoint oneWaypoint : oneCache.waypoints) { + if (oneWaypoint.latitude == null && oneWaypoint.longitude == null) { + continue; + } + + cgCoord coord = new cgCoord(oneWaypoint); + + coordinates.add(coord); + item = settings.getMapFactory().getCacheOverlayItem(coord, null); + + icon = base.getIcon(false, oneWaypoint.type, false, false, false); + if (iconsCache.containsKey(icon)) { + pin = iconsCache.get(icon); + } else { + pin = getResources().getDrawable(icon); + pin.setBounds(0, 0, pin.getIntrinsicWidth(), pin.getIntrinsicHeight()); + iconsCache.put(icon, pin); + } + item.setMarker(pin); + + items.add(item); + } + + overlayCaches.updateItems(items); + displayHandler.sendEmptyMessage(1); + } + } + } + } else { + overlayCaches.updateItems(items); + displayHandler.sendEmptyMessage(1); + } + + cachesProtected.clear(); + + displayHandler.sendEmptyMessage(0); + } finally { + working = false; + } + } + } + + // load users from Go 4 Cache + private class UsersThread extends DoThread { + + public UsersThread(long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { + super(centerLatIn, centerLonIn, spanLatIn, spanLonIn); + } + + @Override + public void run() { + try { + stop = false; + working = true; + usersThreadRun = System.currentTimeMillis(); + + if (stop) { + return; + } + + double latMin = (centerLat / 1e6) - ((spanLat / 1e6) / 2) - ((spanLat / 1e6) / 4); + double latMax = (centerLat / 1e6) + ((spanLat / 1e6) / 2) + ((spanLat / 1e6) / 4); + double lonMin = (centerLon / 1e6) - ((spanLon / 1e6) / 2) - ((spanLon / 1e6) / 4); + double lonMax = (centerLon / 1e6) + ((spanLon / 1e6) / 2) + ((spanLon / 1e6) / 4); + double llCache; + + if (latMin > latMax) { + llCache = latMax; + latMax = latMin; + latMin = llCache; + } + if (lonMin > lonMax) { + llCache = lonMax; + lonMax = lonMin; + lonMin = llCache; + } + + users = base.getGeocachersInViewport(settings.getUsername(), latMin, latMax, lonMin, lonMax); + + if (stop) { + return; + } + + if (displayUsersThread != null && displayUsersThread.isWorking()) { + displayUsersThread.stopIt(); + } + displayUsersThread = new DisplayUsersThread(users, centerLat, centerLon, spanLat, spanLon); + displayUsersThread.start(); + } finally { + working = false; + } + } + } + + // display users of Go 4 Cache + private class DisplayUsersThread extends DoThread { + + private ArrayList<cgUser> users = null; + + public DisplayUsersThread(ArrayList<cgUser> usersIn, long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { + super(centerLatIn, centerLonIn, spanLatIn, spanLonIn); + + users = usersIn; + } + + @Override + public void run() { + try { + stop = false; + working = true; + + if (mapView == null || users == null || users.isEmpty()) { + return; + } + + // display users + ArrayList<UserOverlayItemImpl> items = new ArrayList<UserOverlayItemImpl>(); + + int counter = 0; + UserOverlayItemImpl item = null; + + for (cgUser userOne : users) { + if (stop) { + return; + } + + if (userOne.latitude == null && userOne.longitude == null) { + continue; + } + + item = settings.getMapFactory().getUserOverlayItemBase(activity, userOne); + items.add(item); + + counter++; + if ((counter % 10) == 0) { + overlayUsers.updateItems(items); + displayHandler.sendEmptyMessage(1); + } + } + + overlayUsers.updateItems(items); + } finally { + working = false; + } + } + } + + // display one point + private class DisplayPointThread extends Thread { + + @Override + public void run() { + if (mapView == null || caches == null) { + return; + } + + if (latitudeIntent != null && longitudeIntent != null) { + cgCoord coord = new cgCoord(); + coord.type = "waypoint"; + coord.latitude = latitudeIntent; + coord.longitude = longitudeIntent; + coord.name = "some place"; + + coordinates.add(coord); + CacheOverlayItemImpl item = settings.getMapFactory().getCacheOverlayItem(coord, null); + + final int icon = base.getIcon(false, waypointTypeIntent, false, false, false); + Drawable pin = null; + if (iconsCache.containsKey(icon)) { + pin = iconsCache.get(icon); + } else { + pin = getResources().getDrawable(icon); + pin.setBounds(0, 0, pin.getIntrinsicWidth(), pin.getIntrinsicHeight()); + iconsCache.put(icon, pin); + } + item.setMarker(pin); + + overlayCaches.updateItems(item); + displayHandler.sendEmptyMessage(1); + + cachesCnt = 1; + } else { + cachesCnt = 0; + } + + displayHandler.sendEmptyMessage(0); + } + } + + // parent for those above :) + private class DoThread extends Thread { + + protected boolean working = true; + protected boolean stop = false; + protected long centerLat = 0l; + protected long centerLon = 0l; + protected long spanLat = 0l; + protected long spanLon = 0l; + + public DoThread(long centerLatIn, long centerLonIn, long spanLatIn, long spanLonIn) { + centerLat = centerLatIn; + centerLon = centerLonIn; + spanLat = spanLatIn; + spanLon = spanLonIn; + } + + public synchronized boolean isWorking() { + return working; + } + + public synchronized void stopIt() { + stop = true; + } + } + + // get if map is loading something + private synchronized boolean isLoading() { + boolean loading = false; + + if (loadThread != null && loadThread.isWorking()) { + loading = true; + } else if (downloadThread != null && downloadThread.isWorking()) { + loading = true; + } else if (displayThread != null && displayThread.isWorking()) { + loading = true; + } + + return loading; + } + + // store caches + private class LoadDetails extends Thread { + + private Handler handler = null; + private ArrayList<String> geocodes = null; + private volatile boolean stop = false; + private long last = 0l; + + public LoadDetails(Handler handlerIn, ArrayList<String> geocodesIn) { + handler = handlerIn; + geocodes = geocodesIn; + } + + public void stopIt() { + stop = true; + } + + @Override + public void run() { + if (geocodes == null || geocodes.isEmpty()) { + return; + } + + if (dir != null) { + dir = app.removeDir(); + } + if (geo != null) { + geo = app.removeGeo(); + } + + for (String geocode : geocodes) { + try { + if (stop == true) { + break; + } + + if (!app.isOffline(geocode, null)) { + if ((System.currentTimeMillis() - last) < 1500) { + try { + int delay = 1000 + ((Double) (Math.random() * 1000)).intValue() - (int) (System.currentTimeMillis() - last); + if (delay < 0) { + delay = 500; + } + + sleep(delay); + } catch (Exception e) { + // nothing + } + } + + if (stop == true) { + Log.i(cgSettings.tag, "Stopped storing process."); + + break; + } + + base.storeCache(app, activity, null, geocode, 1, handler); + } + } catch (Exception e) { + Log.e(cgSettings.tag, "cgeocaches.LoadDetails.run: " + e.toString()); + } finally { + // one more cache over + detailProgress++; + handler.sendEmptyMessage(0); + } + + yield(); + + last = System.currentTimeMillis(); + } + + // we're done + handler.sendEmptyMessage(1); + } + } + + // center map to desired location + private void centerMap(Double latitude, Double longitude) { + if (latitude == null || longitude == null) { + return; + } + if (mapView == null) { + return; + } + + if (!alreadyCentered) { + alreadyCentered = true; + + mapController.setCenter(makeGeoPoint(latitude, longitude)); + } else { + mapController.animateTo(makeGeoPoint(latitude, longitude)); + } + } + + // move map to view results of searchIdIntent + private void centerMap(String geocodeCenter, Long searchIdCenter, Double latitudeCenter, Double longitudeCenter) { + if (!centered && (geocodeCenter != null || searchIdIntent != null)) { + try { + ArrayList<Object> viewport; + + if (geocodeCenter != null) { + viewport = app.getBounds(geocodeCenter); + } else { + viewport = app.getBounds(searchIdCenter); + } + + Integer cnt = (Integer) viewport.get(0); + Integer minLat = null; + Integer maxLat = null; + Integer minLon = null; + Integer maxLon = null; + + if (viewport.get(1) != null) { + minLat = new Double((Double) viewport.get(1) * 1e6).intValue(); + } + if (viewport.get(2) != null) { + maxLat = new Double((Double) viewport.get(2) * 1e6).intValue(); + } + if (viewport.get(3) != null) { + maxLon = new Double((Double) viewport.get(3) * 1e6).intValue(); + } + if (viewport.get(4) != null) { + minLon = new Double((Double) viewport.get(4) * 1e6).intValue(); + } + + if (cnt == null || cnt <= 0 || minLat == null || maxLat == null || minLon == null || maxLon == null) { + return; + } + + int centerLat = 0; + int centerLon = 0; + + if ((Math.abs(maxLat) - Math.abs(minLat)) != 0) { + centerLat = minLat + ((maxLat - minLat) / 2); + } else { + centerLat = maxLat; + } + if ((Math.abs(maxLon) - Math.abs(minLon)) != 0) { + centerLon = minLon + ((maxLon - minLon) / 2); + } else { + centerLon = maxLon; + } + + if (cnt != null && cnt > 0) { + mapController.setCenter(settings.getMapFactory().getGeoPointBase(centerLat, centerLon)); + if (Math.abs(maxLat - minLat) != 0 && Math.abs(maxLon - minLon) != 0) { + mapController.zoomToSpan(Math.abs(maxLat - minLat), Math.abs(maxLon - minLon)); + } + } + } catch (Exception e) { + // nothing at all + } + + centered = true; + alreadyCentered = true; + } else if (!centered && latitudeCenter != null && longitudeCenter != null) { + try { + mapController.setCenter(makeGeoPoint(latitudeCenter, longitudeCenter)); + } catch (Exception e) { + // nothing at all + } + + centered = true; + alreadyCentered = true; + } + } + + // switch My Location button image + private void setMyLoc(Boolean status) { + if (myLocSwitch == null) { + myLocSwitch = (ImageView) activity.findViewById(R.id.my_position); + } + + if (status == null) { + if (followMyLocation == true) { + myLocSwitch.setImageResource(R.drawable.my_location_on); + } else { + myLocSwitch.setImageResource(R.drawable.my_location_off); + } + } else { + if (status == true) { + myLocSwitch.setImageResource(R.drawable.my_location_on); + } else { + myLocSwitch.setImageResource(R.drawable.my_location_off); + } + } + + myLocSwitch.setOnClickListener(new MyLocationListener()); + } + + // set my location listener + private class MyLocationListener implements View.OnClickListener { + + public void onClick(View view) { + if (myLocSwitch == null) { + myLocSwitch = (ImageView) activity.findViewById(R.id.my_position); + } + + if (followMyLocation == true) { + followMyLocation = false; + + myLocSwitch.setImageResource(R.drawable.my_location_off); + } else { + followMyLocation = true; + myLocationInMiddle(); + + myLocSwitch.setImageResource(R.drawable.my_location_on); + } + } + } + + // make geopoint + private GeoPointImpl makeGeoPoint(Double latitude, Double longitude) { + return settings.getMapFactory().getGeoPointBase((int) (latitude * 1e6), (int) (longitude * 1e6)); + } + + // close activity and open homescreen + public void goHome(View view) { + base.goHome(activity); + } + + // open manual entry + public void goManual(View view) { + try { + AppManualReaderClient.openManual( + "c-geo", + "c:geo-live-map", + activity, + "http://cgeo.carnero.cc/manual/"); + } catch (Exception e) { + // nothing + } + } +} diff --git a/src/cgeo/geocaching/mapinterfaces/ActivityImpl.java b/src/cgeo/geocaching/mapinterfaces/ActivityImpl.java new file mode 100644 index 0000000..1895744 --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/ActivityImpl.java @@ -0,0 +1,33 @@ +package cgeo.geocaching.mapinterfaces; + +import android.app.Activity; +import android.content.res.Resources; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +/** + * Defines the common functions of the provider-specific + * MapActivity implementations. + * @author rsudev + * + */ +public interface ActivityImpl { + + Resources getResources(); + + Activity getActivity(); + + void superOnCreate(Bundle savedInstanceState); + + void superOnResume(); + + void superOnDestroy(); + + boolean superOnCreateOptionsMenu(Menu menu); + + boolean superOnPrepareOptionsMenu(Menu menu); + + boolean superOnOptionsItemSelected(MenuItem item); + +} diff --git a/src/cgeo/geocaching/mapinterfaces/CacheOverlayItemImpl.java b/src/cgeo/geocaching/mapinterfaces/CacheOverlayItemImpl.java new file mode 100644 index 0000000..bdb8511 --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/CacheOverlayItemImpl.java @@ -0,0 +1,17 @@ +package cgeo.geocaching.mapinterfaces; + +import cgeo.geocaching.cgCoord; + +/** + * Covers the common functions of the provider-specific + * CacheOverlayItem implementations + * @author rsudev + * + */ +public interface CacheOverlayItemImpl extends OverlayItemImpl { + + public cgCoord getCoord(); + + public String getType(); + +} diff --git a/src/cgeo/geocaching/mapinterfaces/GeoPointImpl.java b/src/cgeo/geocaching/mapinterfaces/GeoPointImpl.java new file mode 100644 index 0000000..c6e80d4 --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/GeoPointImpl.java @@ -0,0 +1,15 @@ +package cgeo.geocaching.mapinterfaces; + +/** + * Defines the common functions of the provider-specific + * GeoPoint implementations + * @author rsudev + * + */ +public interface GeoPointImpl { + + int getLatitudeE6(); + + int getLongitudeE6(); + +} diff --git a/src/cgeo/geocaching/mapinterfaces/ItemizedOverlayImpl.java b/src/cgeo/geocaching/mapinterfaces/ItemizedOverlayImpl.java new file mode 100644 index 0000000..73eed1f --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/ItemizedOverlayImpl.java @@ -0,0 +1,33 @@ +package cgeo.geocaching.mapinterfaces; + +import cgeo.geocaching.mapcommon.ItemizedOverlayBase; +import android.graphics.Canvas; +import android.graphics.Point; +import android.graphics.drawable.Drawable; + +/** + * Defines the common functions to access the provider-specific + * ItemizedOverlay implementation + * @author rsudev + * + */ +public interface ItemizedOverlayImpl { + + ItemizedOverlayBase getBase(); + + void superPopulate(); + + void superSetLastFocusedItemIndex(int i); + + Drawable superBoundCenter(Drawable markerIn); + + Drawable superBoundCenterBottom(Drawable marker); + + boolean superOnTap(int index); + + void superDraw(Canvas canvas, MapViewImpl mapView, boolean shadow); + + void superDrawOverlayBitmap(Canvas canvas, Point drawPosition, MapProjectionImpl projection, + byte drawZoomLevel); + +} diff --git a/src/cgeo/geocaching/mapinterfaces/MapControllerImpl.java b/src/cgeo/geocaching/mapinterfaces/MapControllerImpl.java new file mode 100644 index 0000000..dbdb955 --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/MapControllerImpl.java @@ -0,0 +1,19 @@ +package cgeo.geocaching.mapinterfaces; + +/** + * Defines the common functions of the provider-specific + * MapController implementations + * @author rsudev + * + */ +public interface MapControllerImpl { + + void setZoom(int mapzoom); + + void setCenter(GeoPointImpl geoPoint); + + void animateTo(GeoPointImpl geoPoint); + + void zoomToSpan(int latSpanE6, int lonSpanE6); + +} diff --git a/src/cgeo/geocaching/mapinterfaces/MapFactory.java b/src/cgeo/geocaching/mapinterfaces/MapFactory.java new file mode 100644 index 0000000..fe09626 --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/MapFactory.java @@ -0,0 +1,30 @@ +package cgeo.geocaching.mapinterfaces; + +import android.content.Context; +import cgeo.geocaching.cgCoord; +import cgeo.geocaching.cgUser; + +/** + * Defines functions of a factory class to get implementation specific objects + * (GeoPoints, OverlayItems, ...) + * @author rsudev + * + */ +public interface MapFactory { + + public Class getMapClass(); + + public int getMapViewId(); + + public int getMapLayoutId(); + + public GeoPointImpl getGeoPointBase(int latE6, int lonE6); + + public OverlayImpl getOverlayBaseWrapper(OverlayBase ovlIn); + + CacheOverlayItemImpl getCacheOverlayItem(cgCoord coordinate, String type); + + public UserOverlayItemImpl getUserOverlayItemBase(Context context, + cgUser userOne); + +} diff --git a/src/cgeo/geocaching/mapinterfaces/MapProjectionImpl.java b/src/cgeo/geocaching/mapinterfaces/MapProjectionImpl.java new file mode 100644 index 0000000..10d36ee --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/MapProjectionImpl.java @@ -0,0 +1,17 @@ +package cgeo.geocaching.mapinterfaces; + +import android.graphics.Point; + +/** + * Defines common functions of the provider-specific + * MapProjection implementations + * @author rsudev + * + */ +public interface MapProjectionImpl { + + Object getImpl(); + + void toPixels(GeoPointImpl leftGeo, Point left); + +} diff --git a/src/cgeo/geocaching/mapinterfaces/MapViewImpl.java b/src/cgeo/geocaching/mapinterfaces/MapViewImpl.java new file mode 100644 index 0000000..651b39f --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/MapViewImpl.java @@ -0,0 +1,64 @@ +package cgeo.geocaching.mapinterfaces; + +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapcommon.cgMapOverlay; +import cgeo.geocaching.mapcommon.cgUsersOverlay; +import android.content.Context; +import android.graphics.drawable.Drawable; + +/** + * Defines common functions of the provider-specific + * MapView implementations + * @author rsudev + * + */ +public interface MapViewImpl { + + void invalidate(); + + void setSatellite(boolean b); + + void setBuiltInZoomControls(boolean b); + + void displayZoomControls(boolean b); + + void preLoad(); + + void clearOverlays(); + + void addOverlay(OverlayImpl ovl); + + MapControllerImpl getMapController(); + + void destroyDrawingCache(); + + boolean isSatellite(); + + GeoPointImpl getMapViewCenter(); + + int getLatitudeSpan(); + + int getLongitudeSpan(); + + int getMapZoomLevel(); + + int getWidth(); + + int getHeight(); + + MapProjectionImpl getMapProjection(); + + Context getContext(); + + cgMapOverlay createAddMapOverlay(cgSettings settings, Context context, + Drawable drawable, boolean fromDetailIntent); + + cgUsersOverlay createAddUsersOverlay(Context context, Drawable markerIn); + + boolean needsScaleOverlay(); + + void setBuiltinScale(boolean b); + + void setMapSource(cgSettings settings); + +} diff --git a/src/cgeo/geocaching/mapinterfaces/OverlayBase.java b/src/cgeo/geocaching/mapinterfaces/OverlayBase.java new file mode 100644 index 0000000..59afb49 --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/OverlayBase.java @@ -0,0 +1,19 @@ +package cgeo.geocaching.mapinterfaces; + +import android.graphics.Canvas; +import android.graphics.Point; + +/** + * Defines the base functions of the provider-independent + * Overlay implementations + * @author rsudev + * + */ +public interface OverlayBase { + + void draw(Canvas canvas, MapViewImpl mapView, boolean shadow); + + void drawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel); + +} diff --git a/src/cgeo/geocaching/mapinterfaces/OverlayImpl.java b/src/cgeo/geocaching/mapinterfaces/OverlayImpl.java new file mode 100644 index 0000000..6680b6a --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/OverlayImpl.java @@ -0,0 +1,11 @@ +package cgeo.geocaching.mapinterfaces; + +/** + * Marker interface of the provider-specific + * Overlay implementations + * @author rsudev + * + */ +public interface OverlayImpl { + +} diff --git a/src/cgeo/geocaching/mapinterfaces/OverlayItemImpl.java b/src/cgeo/geocaching/mapinterfaces/OverlayItemImpl.java new file mode 100644 index 0000000..0f0297e --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/OverlayItemImpl.java @@ -0,0 +1,18 @@ +package cgeo.geocaching.mapinterfaces; + +import android.graphics.drawable.Drawable; + +/** + * Common functions of the provider-specific + * OverlayItem implementations + * @author rsudev + * + */ +public interface OverlayItemImpl { + + public String getTitle(); + + public Drawable getMarker(int index); + + public void setMarker(Drawable markerIn); +} diff --git a/src/cgeo/geocaching/mapinterfaces/UserOverlayItemImpl.java b/src/cgeo/geocaching/mapinterfaces/UserOverlayItemImpl.java new file mode 100644 index 0000000..6b1532d --- /dev/null +++ b/src/cgeo/geocaching/mapinterfaces/UserOverlayItemImpl.java @@ -0,0 +1,14 @@ +package cgeo.geocaching.mapinterfaces; + +import cgeo.geocaching.cgUser; + +/** + * Common functions of the provider-specific + * UserOverlayItem implementations + * @author rsudev + * + */ +public interface UserOverlayItemImpl extends OverlayItemImpl { + + public cgUser getUser(); +} diff --git a/src/cgeo/geocaching/mapsforge/mfCacheOverlay.java b/src/cgeo/geocaching/mapsforge/mfCacheOverlay.java new file mode 100644 index 0000000..a7665cb --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfCacheOverlay.java @@ -0,0 +1,99 @@ +package cgeo.geocaching.mapsforge; + +import org.mapsforge.android.maps.ItemizedOverlay; +import org.mapsforge.android.maps.MapView; +import org.mapsforge.android.maps.Projection; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Point; +import android.graphics.drawable.Drawable; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapcommon.cgMapOverlay; +import cgeo.geocaching.mapinterfaces.ItemizedOverlayImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.MapViewImpl; + + +public class mfCacheOverlay extends ItemizedOverlay<mfCacheOverlayItem> implements ItemizedOverlayImpl { + + private cgMapOverlay base; + + public mfCacheOverlay(cgSettings settingsIn, Context contextIn, Drawable markerIn, Boolean fromDetailIn) { + super(boundCenterBottom(markerIn)); + base = new cgMapOverlay(settingsIn, this, contextIn, fromDetailIn); + } + + @Override + public cgMapOverlay getBase() { + return base; + } + + @Override + protected mfCacheOverlayItem createItem(int i) { + if (base == null) + return null; + + return (mfCacheOverlayItem) base.createItem(i); + } + + @Override + public int size() { + if (base == null) + return 0; + + return base.size(); + } + + @Override + protected boolean onTap(int arg0) { + if (base == null) + return false; + + return base.onTap(arg0); + } + + @Override + protected void drawOverlayBitmap(Canvas canvas, Point drawPosition, + Projection projection, byte drawZoomLevel) { + base.drawOverlayBitmap(canvas, drawPosition, new mfMapProjection(projection), drawZoomLevel); + } + + @Override + public void superPopulate() { + populate(); + } + + @Override + public Drawable superBoundCenter(Drawable markerIn) { + return super.boundCenter(markerIn); + } + + @Override + public Drawable superBoundCenterBottom(Drawable marker) { + return super.boundCenterBottom(marker); + } + + @Override + public void superSetLastFocusedItemIndex(int i) { + // nothing to do + } + + @Override + public boolean superOnTap(int index) { + return super.onTap(index); + } + + @Override + public void superDraw(Canvas canvas, MapViewImpl mapView, boolean shadow) { + // nothing to do here... + } + + @Override + public void superDrawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel) { + super.drawOverlayBitmap(canvas, drawPosition, (Projection) projection.getImpl(), drawZoomLevel); + } + +} + diff --git a/src/cgeo/geocaching/mapsforge/mfCacheOverlayItem.java b/src/cgeo/geocaching/mapsforge/mfCacheOverlayItem.java new file mode 100644 index 0000000..b6b7d62 --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfCacheOverlayItem.java @@ -0,0 +1,35 @@ +package cgeo.geocaching.mapsforge; + +import org.mapsforge.android.maps.GeoPoint; +import org.mapsforge.android.maps.OverlayItem; + +import android.graphics.drawable.Drawable; + +import cgeo.geocaching.cgCoord; +import cgeo.geocaching.mapinterfaces.CacheOverlayItemImpl; + +public class mfCacheOverlayItem extends OverlayItem implements CacheOverlayItemImpl { + private String cacheType = null; + private cgCoord coord; + + public mfCacheOverlayItem(cgCoord coordinate, String type) { + super(new GeoPoint((int)(coordinate.latitude * 1e6), (int)(coordinate.longitude * 1e6)), coordinate.name, ""); + + this.cacheType = type; + this.coord = coordinate; + } + + public cgCoord getCoord() { + return coord; + } + + public String getType() { + return cacheType; + } + + @Override + public Drawable getMarker(int index) { + return getMarker(); + } + +} diff --git a/src/cgeo/geocaching/mapsforge/mfGeoPoint.java b/src/cgeo/geocaching/mapsforge/mfGeoPoint.java new file mode 100644 index 0000000..1261887 --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfGeoPoint.java @@ -0,0 +1,12 @@ +package cgeo.geocaching.mapsforge; + +import org.mapsforge.android.maps.GeoPoint; + +import cgeo.geocaching.mapinterfaces.GeoPointImpl; + +public class mfGeoPoint extends GeoPoint implements GeoPointImpl { + + public mfGeoPoint(int latitudeE6, int longitudeE6) { + super(latitudeE6, longitudeE6); + } +} diff --git a/src/cgeo/geocaching/mapsforge/mfMapActivity.java b/src/cgeo/geocaching/mapsforge/mfMapActivity.java new file mode 100644 index 0000000..b878def --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfMapActivity.java @@ -0,0 +1,96 @@ +package cgeo.geocaching.mapsforge; + +import org.mapsforge.android.maps.MapActivity; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import cgeo.geocaching.mapcommon.MapBase; +import cgeo.geocaching.mapcommon.cgeomap; +import cgeo.geocaching.mapinterfaces.ActivityImpl; + + +public class mfMapActivity extends MapActivity implements ActivityImpl { + + private MapBase mapBase; + + public mfMapActivity() { + mapBase = new cgeomap(this); + } + + @Override + public Activity getActivity() { + return this; + } + + @Override + protected void onCreate(Bundle icicle) { + mapBase.onCreate(icicle); + } + + @Override + protected void onDestroy() { + mapBase.onDestroy(); + } + + @Override + protected void onPause() { + mapBase.onPause(); + } + + @Override + protected void onResume() { + mapBase.onResume(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + return mapBase.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return mapBase.onOptionsItemSelected(item); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + return mapBase.onPrepareOptionsMenu(menu); + } + + @Override + protected void onStop() { + mapBase.onStop(); + } + + @Override + public void superOnCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public boolean superOnCreateOptionsMenu(Menu menu) { + return superOnCreateOptionsMenu(menu); + } + + @Override + public void superOnDestroy() { + super.onDestroy(); + } + + @Override + public boolean superOnOptionsItemSelected(MenuItem item) { + return super.onOptionsItemSelected(item); + } + + @Override + public void superOnResume() { + super.onResume(); + } + + @Override + public boolean superOnPrepareOptionsMenu(Menu menu) { + return super.onPrepareOptionsMenu(menu); + } +} diff --git a/src/cgeo/geocaching/mapsforge/mfMapController.java b/src/cgeo/geocaching/mapsforge/mfMapController.java new file mode 100644 index 0000000..30f1c29 --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfMapController.java @@ -0,0 +1,43 @@ +package cgeo.geocaching.mapsforge; + +import org.mapsforge.android.maps.GeoPoint; +import org.mapsforge.android.maps.MapController; + +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapControllerImpl; + +public class mfMapController implements MapControllerImpl { + + private MapController mapController; + + public mfMapController(MapController mapControllerIn) { + mapController = mapControllerIn; + } + + @Override + public void animateTo(GeoPointImpl geoPoint) { + mapController.setCenter((GeoPoint)geoPoint); + } + + @Override + public void setCenter(GeoPointImpl geoPoint) { + mapController.setCenter((GeoPoint)geoPoint); + } + + @Override + public void setZoom(int mapzoom) { + mapController.setZoom(mapzoom); + } + + @Override + public void zoomToSpan(int latSpanE6, int lonSpanE6) { + + if (latSpanE6 != 0 && lonSpanE6 != 0) { + // calculate zoomlevel + int distDegree = Math.max(latSpanE6, lonSpanE6); + int zoomLevel = (int) Math.floor(Math.log(360.0*1e6/distDegree)/Math.log(2)); + mapController.setZoom(zoomLevel); + } + } +} + diff --git a/src/cgeo/geocaching/mapsforge/mfMapFactory.java b/src/cgeo/geocaching/mapsforge/mfMapFactory.java new file mode 100644 index 0000000..2031187 --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfMapFactory.java @@ -0,0 +1,54 @@ +package cgeo.geocaching.mapsforge; + +import android.content.Context; +import cgeo.geocaching.R; +import cgeo.geocaching.cgCoord; +import cgeo.geocaching.cgUser; +import cgeo.geocaching.mapinterfaces.CacheOverlayItemImpl; +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapFactory; +import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.OverlayImpl; +import cgeo.geocaching.mapinterfaces.UserOverlayItemImpl; + +public class mfMapFactory implements MapFactory{ + + @Override + public Class getMapClass() { + return mfMapActivity.class; + } + + @Override + public int getMapViewId() { + return R.id.mfmap; + } + + @Override + public int getMapLayoutId() { + return R.layout.mfmap; + } + + @Override + public GeoPointImpl getGeoPointBase(int latE6, int lonE6) { + return new mfGeoPoint(latE6, lonE6); + } + + @Override + public OverlayImpl getOverlayBaseWrapper(OverlayBase ovlIn) { + mfOverlay baseOvl = new mfOverlay(ovlIn); + return baseOvl; + } + + @Override + public CacheOverlayItemImpl getCacheOverlayItem(cgCoord coordinate, String type) { + mfCacheOverlayItem baseItem = new mfCacheOverlayItem(coordinate, type); + return baseItem; + } + + @Override + public UserOverlayItemImpl getUserOverlayItemBase(Context context, cgUser userOne) { + mfUsersOverlayItem baseItem = new mfUsersOverlayItem(context, userOne); + return baseItem; + } + +} diff --git a/src/cgeo/geocaching/mapsforge/mfMapProjection.java b/src/cgeo/geocaching/mapsforge/mfMapProjection.java new file mode 100644 index 0000000..7252b17 --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfMapProjection.java @@ -0,0 +1,28 @@ +package cgeo.geocaching.mapsforge; + +import org.mapsforge.android.maps.GeoPoint; +import org.mapsforge.android.maps.Projection; + +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import android.graphics.Point; + +public class mfMapProjection implements MapProjectionImpl { + + private Projection projection; + + public mfMapProjection(Projection projectionIn) { + projection = projectionIn; + } + + @Override + public void toPixels(GeoPointImpl leftGeo, Point left) { + projection.toPixels((GeoPoint) leftGeo, left); + } + + @Override + public Object getImpl() { + return projection; + } + +} diff --git a/src/cgeo/geocaching/mapsforge/mfMapView.java b/src/cgeo/geocaching/mapsforge/mfMapView.java new file mode 100644 index 0000000..a68e7c0 --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfMapView.java @@ -0,0 +1,159 @@ +package cgeo.geocaching.mapsforge; + +import org.mapsforge.android.maps.GeoPoint; +import org.mapsforge.android.maps.MapView; +import org.mapsforge.android.maps.MapViewMode; +import org.mapsforge.android.maps.Overlay; +import org.mapsforge.android.maps.Projection; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.Log; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.mapcommon.cgMapOverlay; +import cgeo.geocaching.mapcommon.cgUsersOverlay; +import cgeo.geocaching.mapinterfaces.GeoPointImpl; +import cgeo.geocaching.mapinterfaces.MapControllerImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.MapViewImpl; +import cgeo.geocaching.mapinterfaces.OverlayImpl; + +public class mfMapView extends MapView implements MapViewImpl { + + public mfMapView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void draw(Canvas canvas) { + try { + if (getMapZoomLevel() >= 22) { // to avoid too close zoom level (mostly on Samsung Galaxy S series) + getController().setZoom(22); + } + + super.draw(canvas); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgMapView.draw: " + e.toString()); + } + } + + @Override + public void displayZoomControls(boolean takeFocus) { + // nothing to do here + } + + @Override + public MapControllerImpl getMapController() { + return new mfMapController(getController()); + } + + @Override + public GeoPointImpl getMapViewCenter() { + GeoPoint point = getMapCenter(); + return new mfGeoPoint(point.getLatitudeE6(), point.getLongitudeE6()); + } + + @Override + public void addOverlay(OverlayImpl ovl) { + getOverlays().add((Overlay)ovl); + } + + @Override + public void clearOverlays() { + getOverlays().clear(); + } + + @Override + public MapProjectionImpl getMapProjection() { + return new mfMapProjection(getProjection()); + } + + @Override + public cgMapOverlay createAddMapOverlay(cgSettings settings, + Context context, Drawable drawable, boolean fromDetailIntent) { + + mfCacheOverlay ovl = new mfCacheOverlay(settings, context, drawable, fromDetailIntent); + getOverlays().add(ovl); + return ovl.getBase(); + } + + @Override + public cgUsersOverlay createAddUsersOverlay(Context context, Drawable markerIn) { + mfUsersOverlay ovl = new mfUsersOverlay(context, markerIn); + getOverlays().add(ovl); + return ovl.getBase(); + } + + @Override + public int getLatitudeSpan() { + + Projection projection = getProjection(); + + GeoPoint low = projection.fromPixels(0, 0); + GeoPoint high = projection.fromPixels(0, getHeight()); + + return Math.abs(high.getLatitudeE6() - low.getLatitudeE6()); + } + + @Override + public int getLongitudeSpan() { + Projection projection = getProjection(); + + GeoPoint low = projection.fromPixels(0, 0); + GeoPoint high = projection.fromPixels(getWidth(), 0); + + return Math.abs(high.getLongitudeE6() - low.getLongitudeE6()); + } + + @Override + public boolean isSatellite() { + return false; + } + + @Override + public void preLoad() { + // Nothing to do here + } + + @Override + public void setSatellite(boolean b) { + // Nothing to do here + } + + @Override + public int getMapZoomLevel() { + return getZoomLevel(); + } + + @Override + public boolean needsScaleOverlay() { + return false; + } + + @Override + public void setBuiltinScale(boolean b) { + setScaleBar(b); + } + + @Override + public void setMapSource(cgSettings settings) { + + setMapViewMode(MapViewMode.MAPNIK_TILE_DOWNLOAD); + + switch(settings.mapProvider) { + case mapsforgeMapnik: + // is default + break; + case mapsforgeOsmarender: + setMapViewMode(MapViewMode.OSMARENDER_TILE_DOWNLOAD); + break; + case mapsforgeOffline: + if (isValidMapFile(settings.getMapFile())) { + setMapViewMode(MapViewMode.CANVAS_RENDERER); + super.setMapFile(settings.getMapFile()); + } + } + } +} diff --git a/src/cgeo/geocaching/mapsforge/mfOverlay.java b/src/cgeo/geocaching/mapsforge/mfOverlay.java new file mode 100644 index 0000000..e015307 --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfOverlay.java @@ -0,0 +1,26 @@ +package cgeo.geocaching.mapsforge; + +import org.mapsforge.android.maps.Overlay; +import org.mapsforge.android.maps.Projection; + +import android.graphics.Canvas; +import android.graphics.Point; +import cgeo.geocaching.mapinterfaces.OverlayBase; +import cgeo.geocaching.mapinterfaces.OverlayImpl; + +public class mfOverlay extends Overlay implements OverlayImpl { + + private OverlayBase overlayBase; + + public mfOverlay(OverlayBase overlayBaseIn) { + overlayBase = overlayBaseIn; + } + + @Override + protected void drawOverlayBitmap(Canvas canvas, Point drawPosition, + Projection projection, byte drawZoomLevel) { + + overlayBase.drawOverlayBitmap(canvas, drawPosition, new mfMapProjection(projection), drawZoomLevel); + } + +} diff --git a/src/cgeo/geocaching/mapsforge/mfUsersOverlay.java b/src/cgeo/geocaching/mapsforge/mfUsersOverlay.java new file mode 100644 index 0000000..64ccc37 --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfUsersOverlay.java @@ -0,0 +1,98 @@ +package cgeo.geocaching.mapsforge; + +import org.mapsforge.android.maps.ItemizedOverlay; +import org.mapsforge.android.maps.MapView; +import org.mapsforge.android.maps.Projection; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Point; +import android.graphics.drawable.Drawable; +import cgeo.geocaching.mapcommon.cgUsersOverlay; +import cgeo.geocaching.mapinterfaces.ItemizedOverlayImpl; +import cgeo.geocaching.mapinterfaces.MapProjectionImpl; +import cgeo.geocaching.mapinterfaces.MapViewImpl; + +public class mfUsersOverlay extends ItemizedOverlay<mfUsersOverlayItem> implements ItemizedOverlayImpl { + + private cgUsersOverlay base; + + public mfUsersOverlay(Context contextIn, Drawable markerIn) { + super(boundCenter(markerIn)); + base = new cgUsersOverlay(this, contextIn); + } + + @Override + public cgUsersOverlay getBase() { + return base; + } + + @Override + protected mfUsersOverlayItem createItem(int i) { + if (base == null) + return null; + + return (mfUsersOverlayItem) base.createItem(i); + } + + @Override + public int size() { + if (base == null) + return 0; + + return base.size(); + } + + @Override + protected boolean onTap(int arg0) { + if (base == null) + return false; + + return base.onTap(arg0); + } + + @Override + protected void drawOverlayBitmap(Canvas canvas, Point drawPosition, + Projection projection, byte drawZoomLevel) { + + base.drawOverlayBitmap(canvas, drawPosition, new mfMapProjection(projection), drawZoomLevel); + } + + @Override + public void superPopulate() { + populate(); + } + + @Override + public Drawable superBoundCenter(Drawable markerIn) { + return super.boundCenter(markerIn); + } + + @Override + public Drawable superBoundCenterBottom(Drawable marker) { + return super.boundCenterBottom(marker); + } + + @Override + public void superSetLastFocusedItemIndex(int i) { + // Nothing to do here + } + + @Override + public boolean superOnTap(int index) { + return super.onTap(index); + } + + @Override + public void superDraw(Canvas canvas, MapViewImpl mapView, boolean shadow) { + // Nothing to do here + } + + @Override + public void superDrawOverlayBitmap(Canvas canvas, Point drawPosition, + MapProjectionImpl projection, byte drawZoomLevel) { + + super.drawOverlayBitmap(canvas, drawPosition, (Projection) projection.getImpl(), drawZoomLevel); + } + +}
\ No newline at end of file diff --git a/src/cgeo/geocaching/mapsforge/mfUsersOverlayItem.java b/src/cgeo/geocaching/mapsforge/mfUsersOverlayItem.java new file mode 100644 index 0000000..a38a0a6 --- /dev/null +++ b/src/cgeo/geocaching/mapsforge/mfUsersOverlayItem.java @@ -0,0 +1,43 @@ +package cgeo.geocaching.mapsforge; + +import org.mapsforge.android.maps.GeoPoint; +import org.mapsforge.android.maps.OverlayItem; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import cgeo.geocaching.R; +import cgeo.geocaching.cgUser; +import cgeo.geocaching.mapinterfaces.UserOverlayItemImpl; + +public class mfUsersOverlayItem extends OverlayItem implements UserOverlayItemImpl { + private Context context = null; + private cgUser user = null; + + public mfUsersOverlayItem(Context contextIn, cgUser userIn) { + super(new GeoPoint((int)(userIn.latitude * 1e6), (int)(userIn.longitude * 1e6)), userIn.username, ""); + + context = contextIn; + user = userIn; + } + + @Override + public Drawable getMarker(int state) { + Drawable marker = null; + + if (user != null && user.located != null && user.located.getTime() >= (System.currentTimeMillis() - (20 * 60 * 1000))) { + marker = context.getResources().getDrawable(R.drawable.user_location_active); + } else { + marker = context.getResources().getDrawable(R.drawable.user_location); + } + + marker.setBounds(0, 0, marker.getIntrinsicWidth(), marker.getIntrinsicHeight()); + marker.setAlpha(190); + setMarker(marker); + + return marker; + } + + public cgUser getUser() { + return user; + } +} |
