1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
package cgeo.geocaching.go4cache;
import cgeo.geocaching.Parameters;
import cgeo.geocaching.Settings;
import cgeo.geocaching.cgBase;
import cgeo.geocaching.cgeoapplication;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.geopoint.GeopointFormatter.Format;
import cgeo.geocaching.geopoint.Viewport;
import cgeo.geocaching.utils.CryptUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
/**
*
* Thread to send location information to go4cache.com. The singleton will be created
* only if, at any time, the user opts in to send this information. Then the same thread
* will take care of sending updated positions when available.
*
*/
public final class Go4Cache extends Thread {
private static class InstanceHolder { // initialization on demand holder
private static final Go4Cache INSTANCE = new Go4Cache();
}
private final static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 2010-07-25 14:44:01
final private ArrayBlockingQueue<Geopoint> queue = new ArrayBlockingQueue<Geopoint>(1);
public static Go4Cache getInstance() { // no need to be synchronized
return InstanceHolder.INSTANCE;
}
private Go4Cache() { // private singleton constructor
super("Go4Cache");
setPriority(Thread.MIN_PRIORITY);
start();
}
/**
* Send the coordinates to go4cache.com if the user opted in to do so.
*
* @param app
* the current application
* @param coords
* the current coordinates
*/
public static void signalCoordinates(final Geopoint coords) {
if (Settings.isPublicLoc()) {
getInstance().queue.offer(coords);
}
}
@Override
public void run() {
Log.d(Settings.tag, "Go4Cache task started");
Geopoint latestCoords = null;
String latestAction = null;
try {
for (;;) {
final Geopoint currentCoords = queue.take();
final String currentAction = cgeoapplication.getInstance().getAction();
// If we are too close and we haven't changed our current action, no need
// to update our situation.
if (null != latestCoords && latestCoords.distanceTo(currentCoords) < 0.75 && StringUtils.equals(latestAction, currentAction)) {
continue;
}
final String username = Settings.getUsername();
if (StringUtils.isBlank(username)) {
continue;
}
final String latStr = currentCoords.format(Format.LAT_DECDEGREE_RAW);
final String lonStr = currentCoords.format(Format.LON_DECDEGREE_RAW);
final Parameters params = new Parameters(
"u", username,
"lt", latStr,
"ln", lonStr,
"a", currentAction,
"s", (CryptUtils.sha1(username + "|" + latStr + "|" + lonStr + "|" + currentAction + "|" + CryptUtils.md5("carnero: developing your dreams"))).toLowerCase());
if (null != cgBase.version) {
params.put("v", cgBase.version);
}
cgBase.postRequest("http://api.go4cache.com/", params);
// Update our coordinates even if the request was not successful, as not to hammer the server
// with invalid requests for every new GPS position.
latestCoords = currentCoords;
latestAction = currentAction;
}
} catch (InterruptedException e) {
Log.e(Settings.tag, "Go4Cache.run: interrupted", e);
}
}
/**
* Return an immutable list of users present in the given viewport.
*
* @param username
* the current username
* @param viewport
* the current viewport
* @return the list of users present in the viewport
*/
public static List<Go4CacheUser> getGeocachersInViewport(final String username, final Viewport viewport) {
final List<Go4CacheUser> users = new ArrayList<Go4CacheUser>();
if (null == username) {
return users;
}
final Parameters params = new Parameters(
"u", username,
"ltm", viewport.bottomLeft.format(Format.LAT_DECDEGREE_RAW),
"ltx", viewport.topRight.format(Format.LAT_DECDEGREE_RAW),
"lnm", viewport.bottomLeft.format(Format.LON_DECDEGREE_RAW),
"lnx", viewport.topRight.format(Format.LON_DECDEGREE_RAW));
final String data = cgBase.getResponseData(cgBase.postRequest("http://api.go4cache.com/get.php", params));
if (StringUtils.isBlank(data)) {
Log.e(Settings.tag, "cgeoBase.getGeocachersInViewport: No data from server");
return null;
}
try {
final JSONArray usersData = new JSONObject(data).getJSONArray("users");
final int count = usersData.length();
for (int i = 0; i < count; i++) {
final JSONObject oneUser = usersData.getJSONObject(i);
users.add(parseUser(oneUser));
}
} catch (Exception e) {
Log.e(Settings.tag, "cgBase.getGeocachersInViewport: " + e.toString());
}
return Collections.unmodifiableList(users);
}
/**
* Parse user information from go4cache.com.
*
* @param user
* a JSON object
* @return a cgCache user filled with information
* @throws JSONException
* if JSON could not be parsed correctly
* @throws ParseException
* if the date could not be parsed as expected
*/
private static Go4CacheUser parseUser(final JSONObject user) throws JSONException, ParseException {
final Date date = dateFormat.parse(user.getString("located"));
final String username = user.getString("user");
final Geopoint coords = new Geopoint(user.getDouble("latitude"), user.getDouble("longitude"));
final String action = user.getString("action");
final String client = user.getString("client");
return new Go4CacheUser(username, coords, date, action, client);
}
}
|