fr.gotorennes.AbstractMapActivity.java Source code

Java tutorial

Introduction

Here is the source code for fr.gotorennes.AbstractMapActivity.java

Source

/*******************************************************************************
 * 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();
    }
}