package com.papagiannis.tuberun; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicInteger; import android.app.ListActivity; import android.content.Context; import android.location.Address; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.opengl.Visibility; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.SimpleAdapter; import android.widget.TextView; import com.papagiannis.tuberun.binders.FavoritesBinder; import com.papagiannis.tuberun.favorites.DeparturesFavorite; import com.papagiannis.tuberun.favorites.Favorite; import com.papagiannis.tuberun.fetchers.DeparturesBusFetcher; import com.papagiannis.tuberun.fetchers.DeparturesFetcher; import com.papagiannis.tuberun.fetchers.Fetcher; import com.papagiannis.tuberun.fetchers.Observer; import com.papagiannis.tuberun.fetchers.ReverseGeocodeFetcher; import com.papagiannis.tuberun.fetchers.StatusesFetcher; public class FavoritesActivity extends ListActivity implements Observer, OnClickListener, LocationListener { private ListView listView; private TextView location_textview; private TextView location_accuracy_textview; private LinearLayout location_layout; private ProgressBar location_progressbar; private ArrayList<Favorite> favorites = new ArrayList<Favorite>(); private HashMap<Favorite, ArrayList<Location>> locations = null; private int fetchers_count = 0; private boolean uses_status_weekend = false; private boolean uses_status_now = false; private LinearLayout emptyLayout; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new SlidingBehaviour(this, R.layout.favorites); View updateButton = findViewById(R.id.button_update); updateButton.setOnClickListener(this); create(); } public void create() { setListAdapter(null); listView = (ListView) getListView(); emptyLayout = (LinearLayout) findViewById(R.id.empty_layout); location_textview = (TextView) findViewById(R.id.location_textview); location_accuracy_textview = (TextView) findViewById(R.id.location_accuracy_textview); location_progressbar = (ProgressBar) findViewById(R.id.location_progressbar); location_layout = (LinearLayout) findViewById(R.id.location_layout); setupLocationManager(); updateFavorites(); onClick(null); } private boolean shouldStartLocationManager() { ArrayList<Favorite> favs = Favorite.getFavorites(this); return favs!=null && favs.size()>0; } private void updateFavorites() { // count how many unique fetchers are needed // and initialise those with this as a callback favorites = Favorite.getFavorites(this); lastFavoritesCount = favorites.size(); fetchers_count = 0; uses_status_weekend = false; uses_status_now = false; for (Favorite f : favorites) { fetchers_count++; Fetcher fc = f.getFetcher(); if (fc instanceof StatusesFetcher) { boolean forWeekend = Boolean .parseBoolean(f.getIdentification()); if (forWeekend) { if (uses_status_weekend) { fetchers_count--; } else { uses_status_weekend = true; } } else { if (uses_status_now) { fetchers_count--; } else { uses_status_now = true; } } fc = StatusesFetcher.getInstance(forWeekend); f.setFetcher(fc); } fc.clearCallbacks(); fc.registerCallback(this); } findLocations(favorites); } @SuppressWarnings("unchecked") private void findLocations(ArrayList<Favorite> list) { AsyncTask<ArrayList<Favorite>, Integer,HashMap<Favorite,ArrayList<Location>>> findLocationsTask=new AsyncTask<ArrayList<Favorite>, Integer,HashMap<Favorite,ArrayList<Location>>>(){ @Override protected HashMap<Favorite,ArrayList<Location>> doInBackground(ArrayList<Favorite>... favs) { if (favs==null || favs.length==0) return new HashMap<Favorite, ArrayList<Location>>(); DatabaseHelper myDbHelper = new DatabaseHelper(FavoritesActivity.this); HashMap<Favorite,ArrayList<Location>> res=new HashMap<Favorite,ArrayList<Location>>(); try { myDbHelper.openDataBase(); res = myDbHelper.getFavoriteLocations(favs[0]); } catch (Exception e) { Log.w("DeparturesActivity", e); } finally { myDbHelper.close(); } return res; } @Override protected void onPostExecute(HashMap<Favorite,ArrayList<Location>> res) { locations = res; showFavorites(false); } }; findLocationsTask.execute(list); } @Override public void onClick(View arg0) { if (favorites.size() > 0) { emptyLayout.setVisibility(View.GONE); setListAdapter(null); replies.set(0); for (Favorite f : favorites) { f.getFetcher().update(); } } else { emptyLayout.setVisibility(View.VISIBLE); } } private AtomicInteger replies = new AtomicInteger(0); @Override public void update() { if (!countReply()) { return; } showFavorites(true); } private boolean countReply() { return replies.incrementAndGet() == fetchers_count; } private boolean repliesReceived() { return replies.get() == fetchers_count; } /** * Call this method when there is reasonable reason to believe * that the favorites should be drawn right now. This method * ensures all data has been loaded before proceeding with drawing. */ public void showFavorites(boolean isDataUpdate) { if (!repliesReceived() || lastKnownLocation==null) { return; } if (lastKnownLocation.getProvider() == LocationHelper.FAKE_PROVIDER) { return; } if (locations == null) { return; } updateList(false, sortByDistance(lastKnownLocation, favorites), isDataUpdate); } private ArrayList<Favorite> sortByDistance(final Location l, ArrayList<Favorite> list) { if (l == null || l.getProvider()==LocationHelper.FAKE_PROVIDER) return list; ArrayList<Favorite> result = new ArrayList<Favorite>(list); Collections.sort(result, new Comparator<Favorite>() { private HashMap<List<Location>, Location> minLocations = new HashMap<List<Location>, Location>(); private Location findMin(List<Location> locArr) { if (locArr.size()==1) return locArr.get(0); if (minLocations.containsKey(locArr)) return minLocations.get(locArr); Float maxDistance = Float.MAX_VALUE; Location closestLoc = null; for (Location lCandidate : locArr) { Float distance=l.distanceTo(lCandidate); if (distance<maxDistance) { maxDistance=distance; closestLoc=lCandidate; } } minLocations.put(locArr, closestLoc); return closestLoc; } @Override public int compare(Favorite lhs, Favorite rhs) { Location lLeft = findMin(locations.get(lhs)); Location lRight = findMin(locations.get(rhs)); if (lLeft != null && lRight != null) { Float dLeft = lLeft.distanceTo(l); Float dRight = lRight.distanceTo(l); return dLeft.compareTo(dRight); } //unknown locations are displayed last else if (lLeft != null && lRight == null) { return -1; } else if (lLeft == null && lRight != null) { return 1; } return lhs.getIdentification().compareTo(rhs.getIdentification()); } }); return result; } private ArrayList<Favorite> reorderedList = new ArrayList<Favorite>(); private void updateList( Boolean asEmpty, ArrayList<Favorite> list, boolean forceUpdate) { if (!forceUpdate && list.equals(reorderedList)) { reorderedList = list; return; } ArrayList<HashMap<String, Object>> favorites_list = new ArrayList<HashMap<String, Object>>(); ArrayList<String> content = new ArrayList<String>(); for (Favorite fav : list) { Fetcher f = fav.getFetcher(); HashMap<String, Object> m = new HashMap<String, Object>(); m.put("index", Integer.toString(favorites.indexOf(fav))); if (f instanceof DeparturesFetcher) { DeparturesFetcher fetcher = (DeparturesFetcher) f; String platform = ((DeparturesFavorite) fav).getPlatform(); ArrayList<HashMap<String, String>> trains = fetcher .getDepartures(platform); m.put("line", LinePresentation.getStringRespresentation(fav .getLine())); content.add((String) m.get("line")); m.put("icon", LinePresentation.getIcon(fav.getLine())); DeparturesFavorite dfav = (DeparturesFavorite) fav; String platform_trimmed = dfav.getStation_nice() + " " + platform; m.put("platform", platform_trimmed.toUpperCase(Locale.ENGLISH)); int i = 1; if (!asEmpty) { for (HashMap<String, String> train : trains) { String s = train.get("destination"); m.put("destination" + i, s); s = train.get("position"); // display per train platform // only if the favourite is not associated with a // platform if (platform.length() == 0) { String plat = train.get("platform").trim(); if (plat.length() > 0) { s += "/" + plat; } } m.put("position" + i, s); s = train.get("time"); if (s.equals("")) s = "due"; m.put("time" + i, s); if (i++ > 3) break; // show only up to 3 departures } } favorites_list.add(m); } else if (f instanceof DeparturesBusFetcher) { DeparturesBusFetcher fetcher = (DeparturesBusFetcher) f; HashMap<String, ArrayList<HashMap<String, String>>> reply = fetcher .getDepartures(); for (String platform : reply.keySet()) { ArrayList<HashMap<String, String>> trains = reply .get(platform); m = new HashMap<String, Object>(); m.put("index", Integer.toString(favorites.indexOf(fav))); m.put("line", LinePresentation .getStringRespresentation(LineType.BUSES)); content.add((String) m.get("line")); m.put("icon", LinePresentation.getIcon(LineType.BUSES)); m.put("platform", platform.toUpperCase(Locale.ENGLISH)); int i = 1; if (!asEmpty) { for (HashMap<String, String> train : trains) { m.put("destination" + i, train.get("routeId")); m.put("position" + i, train.get("destination")); String time = train.get("estimatedWait"); m.put("time" + i, time); if (i++ > 3) break; // show only up to 3 departures } } favorites_list.add(m); } } else if (f instanceof StatusesFetcher) { StatusesFetcher fetcher = (StatusesFetcher) f; m.put("line", LinePresentation.getStringRespresentation(fav .getLine())); content.add((String) m.get("line")); m.put("platform", LinePresentation .getStringRespresentation(fav.getLine()) .toUpperCase(Locale.ENGLISH)); m.put("icon", LinePresentation.getIcon(fav.getLine())); if (!asEmpty) { m.put("time1", ""); Status s = fetcher.getStatus(fav.getLine()); if (s != null) { m.put("destination1", s.short_status); m.put("position1", s.long_status); } else { m.put("destination1", "Failed"); m.put("position1", ""); } } favorites_list.add(m); } } SimpleAdapter adapter = new SimpleAdapter(this, favorites_list, R.layout.favorites_item, new String[] { "line", "platform", "index", "icon", "index", "destination1", "position1", "time1", "destination2", "position2", "time2", "destination3", "position3", "time3" }, new int[] { R.id.linee_favorites, R.id.platform_favorites, R.id.platform_favorites, R.id.icon_favorites, R.id.remove_favorite, R.id.favorites_destination1, R.id.favorites_position1, R.id.favorites_time1, R.id.favorites_destination2, R.id.favorites_position2, R.id.favorites_time2, R.id.favorites_destination3, R.id.favorites_position3, R.id.favorites_time3 }); //adapter.setData(favorites_list); adapter.setViewBinder(new FavoritesBinder(this)); setListAdapter(adapter); reorderedList = list; } private int lastFavoritesCount = 0; @Override protected void onStart() { super.onStart(); if (Favorite.getFavorites(this).size() != lastFavoritesCount) { updateFavorites(); onClick(null); } } @Override protected void onPause() { lastFavoritesCount = Favorite.getFavorites(this).size(); if (locationManager != null) { locationManager.removeUpdates(this); } super.onPause(); } @Override protected void onResume() { super.onResume(); if (shouldStartLocationManager() && locationManager != null) requestLocationUpdates(); } // LocationListener Methods private LocationManager locationManager; private Location lastKnownLocation; ReverseGeocodeFetcher geocoder = new ReverseGeocodeFetcher(this, null); Observer geolocationObserver = new Observer() { @Override public void update() { displayLocation(geocoder.getResult()); } }; private void setupLocationManager() { if (shouldStartLocationManager()) { location_layout.setVisibility(View.VISIBLE); locationManager = (LocationManager) this .getSystemService(Context.LOCATION_SERVICE); lastKnownLocation = LocationHelper.getLastKnownLocation(locationManager); } else { location_layout.setVisibility(View.GONE); } } private void requestLocationUpdates() { try { LocationHelper.requestLocationUpdates(locationManager, this); } catch (Exception e) { Log.w("LocationService", e); } } @Override public void onLocationChanged(Location l) { if (LocationHelper.isBetterLocation(l, lastKnownLocation)) { lastKnownLocation = l; displayLocation(null); reverseGeocode(l); showFavorites(false); } } private void reverseGeocode(Location l) { geocoder.abort(); geocoder = new ReverseGeocodeFetcher(this, l); geocoder.registerCallback(geolocationObserver).update(); } private void displayLocation(List<Address> result) { if (result == null || result.size() < 1) { location_textview.setText("Fetching address..."); } else { String geoc_result = result.get(0).getAddressLine(0); location_textview.setText(geoc_result); } location_accuracy_textview.setText("accuracy=" + Math.round(lastKnownLocation.getAccuracy()) + "m"); } @Override public void onProviderDisabled(String provider) { // TODO Show Err message } @Override public void onProviderEnabled(String provider) { // TODO Trigger nearby calculation } @Override public void onStatusChanged(String provider, int status, Bundle extras) { // TODO Auto-generated method stub } }