package com.papagiannis.tuberun.fetchers; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.concurrent.atomic.AtomicBoolean; import org.json.JSONArray; import org.json.JSONTokener; public class DeparturesBusFetcher extends Fetcher { private static final long serialVersionUID = 1L; private AtomicBoolean isFirst = new AtomicBoolean(true); private transient RequestTask task = null; @Override public void update() { boolean first = isFirst.compareAndSet(true, false); if (!first) return; // only one at a time String request_query = "http://countdown.api.tfl.gov.uk/interfaces/ura/instant_V1?StopCode1=" + stop_code + "&ReturnList=LineName,DestinationText,EstimatedTime," //for departures + "MessageType,MessagePriority,MessageText"; //for statuses task = new RequestTask(new HttpCallback() { public void onReturn(String s) { getDeparturesCallBack(s); } }); task.execute(request_query); } private Date last_update = new Date(); @Override public Date getUpdateTime() { return last_update; } protected int update_counter = 0; protected String stop_code; protected String stop_nice; protected ArrayList<HashMap<String, String>> departures = new ArrayList<HashMap<String, String>>(); protected String warnings=""; public DeparturesBusFetcher(String stop_code, String stop_nice) { this.stop_code = stop_code; this.stop_nice = stop_nice; } private void getDeparturesCallBack(String reply) { try { departures.clear(); warnings=""; last_update = new Date(); JSONTokener tokener = new JSONTokener(reply); while (tokener.more()) { JSONArray locations = (JSONArray) tokener.nextValue(); int type=locations.getInt(0); switch (type){ case 4: continue; case 1: HashMap<String, String> bus_map = new HashMap<String, String>(); bus_map.put("platform", stop_nice); bus_map.put("destination", locations.getString(2)); bus_map.put("routeId", locations.getString(1)); bus_map.put("routeName", locations.getString(1)); Long time = locations.getLong(3); Long now = last_update.getTime(); Long total = time - now; bus_map.put("estimatedWait", toTextual(total)); departures.add(bus_map); break; case 2: if (warnings.length()>0) warnings+="\n"; warnings+=locations.getString(3); break; } } //the following two calls are required because the TfL api messes results up removeDuplicates(departures); sort(departures); notifyClients(); isFirst.set(true); } catch (Exception e) { notifyClients(); isFirst.set(true); } } private void removeDuplicates(ArrayList<HashMap<String, String>> list) { HashSet<HashMap<String, String>> hs = new HashSet<HashMap<String,String>>(list); list.clear(); list.addAll(hs); } private void sort(ArrayList<HashMap<String, String>> ar) { Collections.sort(ar, new Comparator<HashMap<String,String>>() { @Override public int compare(HashMap<String, String> lhs, HashMap<String, String> rhs) { String time1 = lhs.get("estimatedWait").split(" ")[0]; String time2 = rhs.get("estimatedWait").split(" ")[0]; Integer t1 = 0; Integer t2 = 0; if (!time1.equals("due")) t1 = Integer.parseInt(time1); if (!time2.equals("due")) t2 = Integer.parseInt(time2); if (t1<t2) return -1; else if (t1==t2) return 0; else return 1; } }); } private String toTextual(Long timespan) { String result = ""; long sec = timespan / 1000; long min = sec / 60; if (min == 0) result = "due"; else result = String.valueOf(min) + " min"; return result; } public HashMap<String, ArrayList<HashMap<String, String>>> getDepartures() { HashMap<String, ArrayList<HashMap<String, String>>> categorised = new HashMap<String, ArrayList<HashMap<String, String>>>(); for (HashMap<String, String> train : departures) { if (!categorised.containsKey(train.get("platform"))) { categorised.put(train.get("platform"), new ArrayList<HashMap<String, String>>()); } categorised.get(train.get("platform")).add(train); } return categorised; } public boolean hasWarnings() { return warnings.length()>0; } public String getWarnings() { return warnings; } @Override public void abort() { isFirst.set(true); if (task != null) task.cancel(true); } }