Java tutorial
/******************************************************************************* * Copyright (c) 2011 Michel DAVID mimah35-at-gmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package fr.gotorennes; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.rmi.server.RemoteServer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.location.Address; import android.location.Geocoder; import android.os.Bundle; import android.text.Html; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.ZoomControls; import com.google.ads.AdRequest; import com.google.ads.AdSize; import com.google.ads.AdView; import com.google.android.maps.GeoPoint; import com.google.android.maps.ItemizedOverlay; import com.google.android.maps.MapActivity; import com.google.android.maps.MapController; import com.google.android.maps.MapView; import com.google.android.maps.MyLocationOverlay; import com.google.android.maps.Overlay; import com.google.android.maps.OverlayItem; import fr.gotorennes.ItineraireMapActivity.RouteOverlay; import fr.gotorennes.domain.Arret; import fr.gotorennes.domain.BikeStation; import fr.gotorennes.domain.MetroStation; import fr.gotorennes.domain.Station; import fr.gotorennes.remote.RemoteService; import fr.gotorennes.util.BackgroundTask; import fr.gotorennes.util.LocationUtils; import fr.gotorennes.view.MapDrawable; public abstract class AbstractMapActivity extends MapActivity { public static final double deg2rad = Math.PI / 180; public static final float erad = 6371.0f; protected MapView mapView; protected MapController mapController; protected ZoomControls zoomControls; protected MyLocationOverlay myLocationOverlay; protected GoToRennes goToRennes; protected BroadcastReceiver receiver; protected int getLayout() { return R.layout.map; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayout()); mapView = (MapView) findViewById(R.id.mapView); mapController = mapView.getController(); myLocationOverlay = new MyLocationOverlay(this, mapView); // Add ZoomControls zoomControls = (ZoomControls) findViewById(R.id.zoomControl); zoomControls.setOnZoomInClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mapController.zoomIn(); onMapChange(); } }); zoomControls.setOnZoomOutClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { mapController.zoomOut(); onMapChange(); } }); addMyLocationOverlay(); receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { finish(); } }; IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(AbstractActivity.ACTION_LOGOUT); registerReceiver(receiver, intentFilter); BackgroundTask<Boolean> backgroundInit = new BackgroundTask<Boolean>() { @Override protected Boolean execute() { try { goToRennes = GoToRennes.getInstance(AbstractMapActivity.this, null); } catch (Exception ex) { return false; } return true; } @Override protected void callback(Boolean result) { if (result == null || !result) { showError(getString(R.string.erreurInitialisation), true); } else { init(); goToRennes.track(getTrackingName()); } } }; backgroundInit.start(this); } protected abstract String getTrackingName(); protected abstract void init(); protected void logout() { Intent broadcastIntent = new Intent(); broadcastIntent.setAction(AbstractActivity.ACTION_LOGOUT); sendBroadcast(broadcastIntent); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.accueil: goToRennes.trackEvent("Menu", "Accueil"); Intent intentAccueil = new Intent(getApplicationContext(), GoToRennesActivity.class); intentAccueil.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intentAccueil); return true; case R.id.quitter: goToRennes.trackEvent("Menu", "Quitter"); logout(); return true; default: return super.onOptionsItemSelected(item); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) loadProximityData(); } protected abstract void loadProximityData(); @Override public boolean dispatchTouchEvent(MotionEvent event) { boolean result = super.dispatchTouchEvent(event); if (event.getAction() == MotionEvent.ACTION_UP) onMapChange(); return result; } protected void onMapChange() { } protected void addOverlay(Overlay overlay) { if (!mapView.getOverlays().contains(overlay)) { mapView.getOverlays().add(overlay); } } protected void addMyLocationOverlay() { myLocationOverlay.enableMyLocation(); addOverlay(myLocationOverlay); } protected void centerMap(double latitude, double longitude) { mapController.setZoom(18); mapController.animateTo(new GeoPoint((int) (latitude * 1e6), (int) (longitude * 1e6))); } protected void addEtapeOverlay(Etape etape) { if (etape.route == null) { return; } boolean first = true; GeoPoint lastGp = null; for (Iterator<Location> iter = etape.route.locations.iterator(); iter.hasNext();) { Location location = iter.next(); GeoPoint gp = new GeoPoint((int) (location.latitude * 1E6), (int) (location.longitude * 1E6)); if (first) { mapView.getOverlays().add(new RouteOverlay(gp, gp, 1)); first = false; if (etape.type != TypeEtape.BUS) { MapItemOverlay overlay = new MapItemOverlay( new MapDrawable(getApplicationContext(), etape.type.icon)); overlay.addItem(new MapLocationOverlay("", location.latitude, location.longitude, null)); mapView.getOverlays().add(overlay); } else { MapItemOverlay overlay = new MapItemOverlay( new MapDrawable(getApplicationContext(), etape.bitmapIcon, etape.bitmapCarre)); overlay.addItem(new MapLocationOverlay("", location.latitude, location.longitude, null)); mapView.getOverlays().add(overlay); } } else if (!iter.hasNext()) { mapView.getOverlays().add(new RouteOverlay(lastGp, gp, 3)); } else { mapView.getOverlays().add(new RouteOverlay(lastGp, gp, 2)); } lastGp = gp; } } public static Location getLocation(Context context, String address) { if ("".equals(address.trim())) { android.location.Location l = LocationUtils.getLocation(context); return l != null ? new Location(l.getLatitude(), l.getLongitude(), context.getString(R.string.positionActuelle)) : null; } Geocoder geocoder = new Geocoder(context); try { List<Address> addresses = geocoder.getFromLocationName(address + ",35 ille et vilaine", 1); if (addresses != null && !addresses.isEmpty()) { Address foundAddress = addresses.get(0); return new Location(foundAddress.getLatitude(), foundAddress.getLongitude(), foundAddress.getAddressLine(0)); } } catch (Exception ex) { Log.e("GoToRennes", ex.getMessage()); } return null; } protected Itineraire getItinerairePieton(int depart, Location locationDepart, int arrivee, Location locationArrivee) { return getItinerairePieton(depart, locationDepart, arrivee, locationArrivee, true); } protected Itineraire getItinerairePieton(int depart, Location locationDepart, int arrivee, Location locationArrivee, boolean calculeRoute) { Itineraire itineraire = new Itineraire(); itineraire.etapes .add(getEtape(TypeEtape.PIETON, depart, locationDepart, arrivee, locationArrivee, calculeRoute)); return itineraire; } public Etape getEtape(TypeEtape type, int depart, Location locationDepart, int arrivee, Location locationArrivee) { return getEtape(type, depart, locationDepart, arrivee, locationArrivee, true); } public Etape getEtape(TypeEtape type, int depart, Location locationDepart, int arrivee, Location locationArrivee, boolean calculeRoute) { Etape etape = new Etape(); etape.type = type; etape.locationDepart = locationDepart; etape.adresseDepart = locationDepart.adresse + (locationDepart.direction != null && type != TypeEtape.PIETON ? "<br/>" + getString(R.string.direction) + locationDepart.direction : ""); etape.locationArrivee = locationArrivee; etape.adresseArrivee = locationArrivee.adresse; etape.depart = depart; etape.arrivee = arrivee; if (calculeRoute) { etape.route = getRoute(locationDepart, locationArrivee); } return etape; } protected void calculeRoutes(Itineraire itineraire) { for (Etape etape : itineraire.etapes) { if (etape.type == TypeEtape.PIETON || etape.type == TypeEtape.VELO) { etape.route = getRoute(etape.locationDepart, etape.locationArrivee); } else { etape.route = getRoute(etape.locationDepart); } } } protected static Route getRoute(Location depart) { Route route = new Route(); route.locations.add(depart); return route; } /** * Decode a polyline string into a list of GeoPoints. * * @param poly * polyline encoded string to decode. * @return the list of GeoPoints represented by this polystring. */ private static List<Location> decodePolyLine(final String poly) { int len = poly.length(); int index = 0; List<Location> decoded = new ArrayList<Location>(); int lat = 0; int lng = 0; while (index < len) { int b; int shift = 0; int result = 0; do { b = poly.charAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); lat += dlat; shift = 0; result = 0; do { b = poly.charAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); lng += dlng; decoded.add(new Location(lat * 1E-5, lng * 1E-5, "")); } return decoded; } protected static Route getRoute(Location depart, Location arrivee) { final StringBuilder urlString = new StringBuilder(); urlString.append("http://maps.google.com/maps/api/directions/json?&sensor=true&mode=walking"); urlString.append("&origin="); urlString.append(depart.latitude); urlString.append(","); urlString.append(depart.longitude); urlString.append("&destination="); urlString.append(arrivee.latitude); urlString.append(","); urlString.append(arrivee.longitude); Log.d("GoToRennes", urlString.toString()); String result = RemoteService.readFully(urlString.toString()); if (result == null) { return null; } try { JSONObject json = new JSONObject(result); JSONObject jsonRoute = json.getJSONArray("routes").getJSONObject(0); JSONObject polyline = jsonRoute.getJSONObject("overview_polyline"); Route route = new Route(); route.locations = decodePolyLine(polyline.getString("points")); return route; } catch (JSONException e) { Log.e("GoToRennes", e.getMessage()); } return null; } protected void populateItineraireDetails(Itineraire itineraire) { LinearLayout details = (LinearLayout) findViewById(R.id.details); for (final Etape etape : itineraire.etapes) { addEtapeOverlay(etape); RelativeLayout view = (RelativeLayout) getLayoutInflater().inflate(R.layout.itineraire_listitem, null); ImageView lineIcon = (ImageView) view.findViewById(R.id.icon); if (etape.bitmapIcon != null) { lineIcon.setImageBitmap(etape.bitmapIcon); } else { lineIcon.setImageResource(etape.type.icon); } TextView name = (TextView) view.findViewById(R.id.name); name.setText(Html.fromHtml(Arret.getTime(etape.depart) + " : " + etape.adresseDepart + "<br />" + Arret.getTime(etape.arrivee) + " : " + etape.adresseArrivee)); TextView duree = (TextView) view.findViewById(R.id.duree); duree.setText(etape.getDuree() + "min"); TextView distance = (TextView) view.findViewById(R.id.distance); distance.setText(getFormattedDistance(etape.locationDepart, etape.locationArrivee)); distance.setVisibility( etape.type == TypeEtape.PIETON || etape.type == TypeEtape.VELO ? View.VISIBLE : View.GONE); view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Location depart = etape.locationDepart; centerMap(depart.latitude, depart.longitude); } }); details.addView(view); } Location first = itineraire.etapes.get(0).locationDepart; centerMap(first.latitude, first.longitude); } protected static class Itineraire { public int index; public List<Etape> etapes = new ArrayList<Etape>(); public Itineraire(Etape... etapes) { for (Etape etape : etapes) { this.etapes.add(etape); } } public int getArrivee() { return etapes.get(etapes.size() - 1).arrivee; } public String getKey() { StringBuffer buffer = new StringBuffer(); for (Etape etape : etapes) { buffer.append(etape.getKey()); } return buffer.toString(); } } protected static enum TypeEtape { PIETON(R.drawable.pieton), VELO(R.drawable.bike), BUS(R.drawable.bus), METRO(R.drawable.metro); public int icon; private TypeEtape(int icon) { this.icon = icon; } } protected static class Etape { public TypeEtape type; public Bitmap bitmapIcon; public boolean bitmapCarre; public int depart; public String adresseDepart; public Location locationDepart; public int arrivee; public String adresseArrivee; public Location locationArrivee; public String ligne; public Route route; public int getDuree() { return arrivee - depart; } public String getKey() { switch (type) { case VELO: return "V"; case METRO: return "M"; case PIETON: return "P"; default: return ligne; } } } protected static class Route { List<Location> locations = new ArrayList<Location>(); } protected static class Location { double latitude; double longitude; String adresse; String direction; public Location(BikeStation station, Context context) { latitude = station.latitude; longitude = station.longitude; adresse = context.getString(R.string.station) + " " + station.name; } public Location(MetroStation station, String direction, Context context) { latitude = station.latitude; longitude = station.longitude; adresse = context.getString(R.string.station) + " " + station.name; this.direction = direction; } public Location(Station station, String direction, Context context) { latitude = station.latitude; longitude = station.longitude; adresse = context.getString(R.string.arret) + " " + station.nom; this.direction = direction; } public Location(String[] lngLat) { this(lngLat, null); } public Location(String[] lngLat, String adresse) { latitude = Double.parseDouble(lngLat[1]); longitude = Double.parseDouble(lngLat[0]); this.adresse = adresse; } public Location(double latitude, double longitude, String adresse) { this.latitude = latitude; this.longitude = longitude; this.adresse = adresse; } } public static int getTempsMarche(Location depart, Location arrivee) { double distance = getDistance(depart, arrivee); return (int) (distance * 60 / 4); // 4km/h } public static int getTempsVelo(Location depart, Location arrivee) { double distance = getDistance(depart, arrivee); return (int) (distance * 60 / 11); // 11km/h } public static String getFormattedDistance(Location depart, Location arrivee) { return formatDistance(getDistance(depart, arrivee)); } public static double getDistance(Location depart, Location arrivee) { return AbstractMapActivity.getDistance(depart.latitude, depart.longitude, arrivee.latitude, arrivee.longitude); } public static String getFormattedDistance(double lat1, double lon1, double lat2, double lon2) { return formatDistance(getDistance(lat1, lon1, lat2, lon2)); } public static double getDistance(double lat1, double lon1, double lat2, double lon2) { double dLat = (lat2 - lat1) * deg2rad; double dLon = (lon2 - lon1) * deg2rad; double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * deg2rad) * Math.cos(lat2 * deg2rad) * Math.sin(dLon / 2) * Math.sin(dLon / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return c * erad; } public static String formatDistance(double distance) { 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 { return String.format(Locale.getDefault(), "%.0f", new Double(Math.round(distance * 1000.0))) + " m"; } } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(receiver); myLocationOverlay.disableMyLocation(); if (isTaskRoot()) { if (goToRennes != null) goToRennes.stop(); } if (adView != null) { adView.destroy(); } } @Override protected void onPause() { super.onPause(); myLocationOverlay.disableMyLocation(); LocationUtils.desactiveLocation(this); } private AdView adView = null; @Override protected void onResume() { super.onResume(); myLocationOverlay.enableMyLocation(); LocationUtils.activeLocation(this); ViewGroup layout = (ViewGroup) findViewById(R.id.adLayout); if (layout != null && adView == null) { adView = new AdView(this, AdSize.BANNER, "ca-app-pub-5396385527314971/1328911094"); layout.addView(adView); } if (adView != null) { AdRequest adRequest = new AdRequest(); adRequest.setLocation(LocationUtils.getLocation(this)); adRequest.addTestDevice(AdRequest.TEST_EMULATOR); adRequest.addTestDevice("8A95E7269CA459C74F63A410B14D6444"); adView.loadAd(adRequest); } } class MapItemOverlay extends ItemizedOverlay<MapLocationOverlay> { protected List<MapLocationOverlay> items = new ArrayList<MapLocationOverlay>(); public MapItemOverlay(Drawable defaultMarker) { super(defaultMarker); populate(); } @Override protected MapLocationOverlay createItem(int paramInt) { return items.get(paramInt); } @Override public int size() { return items.size(); } public void addItem(MapLocationOverlay item) { setLastFocusedIndex(-1); items.add(item); populate(); } public void removeAllItems() { setLastFocusedIndex(-1); items.clear(); populate(); } } @Override protected boolean isRouteDisplayed() { return false; } class MapLocationOverlay<T> extends OverlayItem { private T item; public MapLocationOverlay(String label, double latitude, double longitude, T item) { super(new GeoPoint((int) (latitude * 1e6), (int) (longitude * 1e6)), label, null); this.item = item; } public T getItem() { return item; } } protected void showError(String message) { showError(message, false); } protected void showError(String message, final boolean finish) { AlertDialog.Builder build = new AlertDialog.Builder(this); build.setMessage(message); build.setPositiveButton("Ok", new android.content.DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); if (finish) finish(); } }); build.create().show(); } }