gc.david.dfm.ui.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for gc.david.dfm.ui.MainActivity.java

Source

package gc.david.dfm.ui;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.app.SearchManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Color;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.common.collect.Lists;
import com.inmobi.commons.InMobi;
import com.inmobi.monetization.IMBanner;
import com.inmobi.monetization.IMBannerListener;
import com.inmobi.monetization.IMErrorCode;
import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.GraphViewSeries;
import com.jjoe64.graphview.GraphViewSeries.GraphViewSeriesStyle;
import com.jjoe64.graphview.LineGraphView;
import com.splunk.mint.Mint;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import butterknife.InjectView;
import gc.david.dfm.DFMApplication;
import gc.david.dfm.R;
import gc.david.dfm.adapter.DistanceAdapter;
import gc.david.dfm.adapter.MarkerInfoWindowAdapter;
import gc.david.dfm.adapter.NavigationDrawerItemAdapter;
import gc.david.dfm.map.Haversine;
import gc.david.dfm.map.LocationUtils;
import gc.david.dfm.model.DaoSession;
import gc.david.dfm.model.Distance;
import gc.david.dfm.model.Position;

import static butterknife.ButterKnife.inject;
import static gc.david.dfm.Utils.isOnline;
import static gc.david.dfm.Utils.showAlertDialog;
import static gc.david.dfm.Utils.toastIt;

/**
 * Implements the app main Activity.
 *
 * @author David
 */
public class MainActivity extends ActionBarActivity implements LocationListener,
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

    private static final int ELEVATION_SAMPLES = 100;
    private final int FIRST_DRAWER_ITEM_INDEX = 1;

    @InjectView(R.id.elevationchart)
    protected RelativeLayout rlElevationChart;
    @InjectView(R.id.closeChart)
    protected ImageView ivCloseElevationChart;

    private GoogleMap googleMap = null;
    // A request to connect to Location Services
    private LocationRequest locationRequest = null;
    // Stores the current instantiation of the location client in this object
    private GoogleApiClient googleApiClient = null;
    private Location currentLocation = null;
    // Moves to current position if app has just started
    private boolean appHasJustStarted = true;
    private String distanceMeasuredAsText = "";
    private MenuItem searchMenuItem = null;
    // Show position if we come from other app (p.e. Whatsapp)
    private boolean mustShowPositionWhenComingFromOutside = false;
    private LatLng sendDestinationPosition = null;
    private IMBanner banner = null;
    private boolean bannerShown = false;
    private boolean elevationChartShown = false;
    @SuppressWarnings("rawtypes")
    private AsyncTask showingElevationTask = null;
    private GraphView graphView = null;
    private float DEVICE_DENSITY;
    private ActionBarDrawerToggle actionBarDrawerToggle;
    private DrawerLayout drawerLayout;
    private ListView drawerList;
    private DistanceMode distanceMode;
    private List<LatLng> coordinates;
    private boolean calculatingDistance;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Mint.initAndStartSession(MainActivity.this, "6f2149e6");
        // Enable logging
        Mint.enableLogging(true);
        Mint.leaveBreadcrumb("MainActivity::onCreate");

        setContentView(R.layout.activity_main);
        inject(this);

        DEVICE_DENSITY = getResources().getDisplayMetrics().density;

        googleApiClient = new GoogleApiClient.Builder(this).addApi(LocationServices.API)
                .addConnectionCallbacks(this).addOnConnectionFailedListener(this).build();

        final SupportMapFragment fragment = ((SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map));
        googleMap = fragment.getMap();

        if (googleMap != null) {
            googleMap.setMyLocationEnabled(true);
            googleMap.getUiSettings().setZoomControlsEnabled(true);
            googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);

            // InMobi Ads
            InMobi.initialize(this, "9b61f509a1454023b5295d8aea4482c2");
            banner = (IMBanner) findViewById(R.id.banner);
            if (banner != null) {
                // Si no hay red el banner no carga ni aunque est vaco
                banner.setRefreshInterval(30);
                banner.setIMBannerListener(new IMBannerListener() {
                    @Override
                    public void onShowBannerScreen(IMBanner arg0) {
                        Mint.leaveBreadcrumb("MainActivity::banner onShowBannerScreen");
                    }

                    @Override
                    public void onLeaveApplication(IMBanner arg0) {
                        Mint.leaveBreadcrumb("MainActivity::banner onLeaveApplication");
                    }

                    @Override
                    public void onDismissBannerScreen(IMBanner arg0) {
                        Mint.leaveBreadcrumb("MainActivity::banner onDismissBannerScreen");
                    }

                    @Override
                    public void onBannerRequestSucceeded(IMBanner arg0) {
                        Mint.leaveBreadcrumb("MainActivity::banner onBannerRequestSucceeded");
                        bannerShown = true;
                        fixMapPadding();
                    }

                    @Override
                    public void onBannerRequestFailed(IMBanner arg0, IMErrorCode arg1) {
                        Mint.leaveBreadcrumb("MainActivity::banner onBannerRequestFailed");
                    }

                    @Override
                    public void onBannerInteraction(IMBanner arg0, Map<String, String> arg1) {
                        Mint.leaveBreadcrumb("MainActivity::banner onBannerInteraction");
                        Mint.logEvent("Ad tapped");
                    }
                });
                banner.loadBanner();
            }

            if (!isOnline(getApplicationContext())) {
                showWifiAlertDialog();
            }

            googleMap.setOnMapLongClickListener(new OnMapLongClickListener() {
                @Override
                public void onMapLongClick(LatLng point) {
                    Mint.leaveBreadcrumb("MainActivity::googleMap onMapLongClick");
                    calculatingDistance = true;

                    if (distanceMode == DistanceMode.DISTANCE_FROM_ANY_POINT) {
                        if (coordinates == null || coordinates.isEmpty()) {
                            toastIt(getString(R.string.toast_first_point_needed), getApplicationContext());
                        } else {
                            coordinates.add(point);
                            drawAndShowMultipleDistances(coordinates, "", false, true);
                        }
                    }
                    // Si no hemos encontrado la posicin actual, no podremos
                    // calcular la distancia
                    else if (currentLocation != null) {
                        if ((distanceMode == DistanceMode.DISTANCE_FROM_CURRENT_POINT) && (coordinates.isEmpty())) {
                            coordinates
                                    .add(new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude()));
                        }
                        coordinates.add(point);
                        drawAndShowMultipleDistances(coordinates, "", false, true);
                    }

                    calculatingDistance = false;
                }
            });

            googleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
                @Override
                public void onMapClick(LatLng point) {
                    Mint.leaveBreadcrumb("MainActivity::googleMap onMapClick");
                    if (distanceMode == DistanceMode.DISTANCE_FROM_ANY_POINT) {
                        if (!calculatingDistance) {
                            coordinates.clear();
                        }

                        calculatingDistance = true;

                        if (coordinates.isEmpty()) {
                            googleMap.clear();
                        }
                        coordinates.add(point);
                        googleMap.addMarker(new MarkerOptions().position(point));
                    } else {
                        // Si no hemos encontrado la posicin actual, no podremos
                        // calcular la distancia
                        if (currentLocation != null) {
                            if (coordinates != null) {
                                if (!calculatingDistance) {
                                    coordinates.clear();
                                }
                                calculatingDistance = true;

                                if (coordinates.isEmpty()) {
                                    googleMap.clear();
                                    coordinates.add(new LatLng(currentLocation.getLatitude(),
                                            currentLocation.getLongitude()));
                                }
                                coordinates.add(point);
                                googleMap.addMarker(new MarkerOptions().position(point));
                            } else {
                                final IllegalStateException illegalStateException = new IllegalStateException(
                                        "Empty coordinates list");
                                Mint.logException(illegalStateException);
                                throw illegalStateException;
                            }
                        }
                    }
                }
            });

            // TODO Future release
            // Cambiar esto: debera modificar solamentela posicin que estemos tuneando y recalcular
            //         googleMap.setOnMarkerDragListener(new OnMarkerDragListener() {
            ////            private String selectedMarkerId;
            //
            //            @Override
            //            public void onMarkerDragStart(Marker marker) {
            ////               selectedMarkerId = null;
            ////               final String markerId = marker.getId();
            ////               if (coordinates.contains(markerId)) {
            ////                  for (int i = 0; i < coordinates.size(); i++) {
            ////                     final LatLng position = coordinates.get(i);
            ////                     if (markerId.latitude == position.latitude &&
            ////                           markerId.longitude == position.longitude) {
            ////                        selectedMarkerId = i;
            ////                        break;
            ////                     }
            ////                  }
            ////               }
            //            }
            //
            //            @Override
            //            public void onMarkerDragEnd(Marker marker) {
            ////               if (selectedMarkerId != -1) {
            ////                  coordinates.set(selectedMarkerId, marker.getPosition());
            ////               }
            ////               // NO movemos el zoom porque estamos simplemente afinando la
            ////               // posicin
            ////               drawAndShowMultipleDistances(coordinates, "", false, false);
            //            }
            //
            //            @Override
            //            public void onMarkerDrag(Marker marker) {
            //               // nothing
            //            }
            //         });

            googleMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
                @Override
                public void onInfoWindowClick(Marker marker) {
                    Mint.leaveBreadcrumb("MainActivity::googleMap onInfoWindowClick");
                    final Intent showInfoActivityIntent = new Intent(MainActivity.this, ShowInfoActivity.class);

                    showInfoActivityIntent.putExtra(ShowInfoActivity.POSITIONS_LIST_EXTRA_KEY_NAME,
                            Lists.newArrayList(coordinates));
                    showInfoActivityIntent.putExtra(ShowInfoActivity.DISTANCE_EXTRA_KEY_NAME,
                            distanceMeasuredAsText);
                    startActivity(showInfoActivityIntent);
                }
            });

            googleMap.setInfoWindowAdapter(new MarkerInfoWindowAdapter(this));

            // Iniciando la app
            if (currentLocation == null) {
                toastIt(getString(R.string.toast_loading_position), getApplicationContext());
            }

            handleIntents(getIntent());

            final List<String> distanceModes = Lists.newArrayList(
                    getString(R.string.navigation_drawer_starting_point_current_position_item),
                    getString(R.string.navigation_drawer_starting_point_any_position_item));
            final List<Integer> distanceIcons = Lists.newArrayList(R.drawable.ic_action_device_gps_fixed,
                    R.drawable.ic_action_communication_location_on);
            drawerList = (ListView) findViewById(R.id.left_drawer);

            // TODO cambiar esto por un header como dios manda
            final LayoutInflater inflater = (LayoutInflater) getApplicationContext()
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            final View convertView = inflater.inflate(R.layout.simple_textview_list_item, drawerList, false);
            final TextView tvListElement = (TextView) convertView.findViewById(R.id.simple_textview);
            tvListElement.setText(getString(R.string.navigation_drawer_starting_point_header));
            tvListElement.setClickable(false);
            tvListElement.setTextColor(getResources().getColor(R.color.white));
            drawerList.addHeaderView(convertView);
            drawerList.setAdapter(new NavigationDrawerItemAdapter(this, distanceModes, distanceIcons));

            drawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                    selectItem(position);
                }
            });

            drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
            actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
                    R.string.progressdialog_search_position_message,
                    R.string.progressdialog_search_position_message) {
                @Override
                public void onDrawerOpened(View drawerView) {
                    Mint.leaveBreadcrumb("MainActivity::actionBarDrawerToggle onDrawerOpened");
                    super.onDrawerOpened(drawerView);
                    supportInvalidateOptionsMenu();
                }

                @Override
                public void onDrawerClosed(View drawerView) {
                    Mint.leaveBreadcrumb("MainActivity::actionBarDrawerToggle onDrawerClosed");
                    super.onDrawerClosed(drawerView);
                    supportInvalidateOptionsMenu();
                }
            };

            drawerLayout.setDrawerListener(actionBarDrawerToggle);

            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setHomeButtonEnabled(true);

            if (savedInstanceState == null) {
                Mint.leaveBreadcrumb("MainActivity savedInstanceState == null");
                // TODO change this because the header!!!!
                selectItem(FIRST_DRAWER_ITEM_INDEX);
            }
        }
    }

    private DaoSession getApplicationDaoSession() {
        Mint.leaveBreadcrumb("MainActivity::getApplicationDaoSession");
        return ((DFMApplication) getApplicationContext()).getDaoSession();
    }

    /**
     * Swaps starting point in the main content view
     */
    private void selectItem(int position) {
        Mint.leaveBreadcrumb("MainActivity::selectItem " + position);
        if (position != 0) {
            if (position == 1) {
                Mint.logEvent("Distance from current point");
            } else if (position == 2) {
                Mint.logEvent("Distance from any point");
            }
            distanceMode = (position == FIRST_DRAWER_ITEM_INDEX) ? // TODO change this because the header!!!!
                    DistanceMode.DISTANCE_FROM_CURRENT_POINT : DistanceMode.DISTANCE_FROM_ANY_POINT;

            Mint.addExtraData("distanceMode", String.valueOf(distanceMode));

            // Highlight the selected item and close the drawer
            drawerList.setItemChecked(position, true);
            drawerLayout.closeDrawer(drawerList);

            calculatingDistance = false;

            coordinates = Lists.newArrayList();
            googleMap.clear();
            if (showingElevationTask != null) {
                showingElevationTask.cancel(true);
            }
            rlElevationChart.setVisibility(View.INVISIBLE);
            elevationChartShown = false;
            fixMapPadding();
        } else {
            Mint.leaveBreadcrumb("MainActivity::selectItem 0 not valid!");
        }
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        Mint.leaveBreadcrumb("MainActivity::onPostCreate");
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        if (actionBarDrawerToggle != null) {
            actionBarDrawerToggle.syncState();
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        Mint.leaveBreadcrumb("MainActivity::onConfigurationChanged");
        super.onConfigurationChanged(newConfig);
        actionBarDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        Mint.leaveBreadcrumb("MainActivity::onNewIntent");
        setIntent(intent);
        handleIntents(intent);
    }

    /**
     * Handles all Intent types.
     *
     * @param intent The input intent.
     */
    private void handleIntents(final Intent intent) {
        Mint.leaveBreadcrumb("MainActivity::handleIntents");
        if (intent != null) {
            if (intent.getAction().equals(Intent.ACTION_SEARCH)) {
                handleSearchIntent(intent);
            } else if (intent.getAction().equals(Intent.ACTION_VIEW)) {
                try {
                    handleViewPositionIntent(intent);
                } catch (Exception e) {
                    e.printStackTrace();
                    Mint.logException(e);
                }
            }
        }
    }

    /**
     * Handles a search intent.
     *
     * @param intent Input intent.
     */
    private void handleSearchIntent(final Intent intent) {
        Mint.leaveBreadcrumb("MainActivity::handleSearchIntent");
        // Para controlar instancias nicas, no queremos que cada vez que
        // busquemos nos inicie una nueva instancia de la aplicacin
        final String query = intent.getStringExtra(SearchManager.QUERY);
        if (currentLocation != null) {
            new SearchPositionByName().execute(query);
        }
        if (searchMenuItem != null) {
            MenuItemCompat.collapseActionView(searchMenuItem);
        }
    }

    /**
     * Handles a send intent with position data.
     *
     * @param intent Input intent with position data.
     */
    private void handleViewPositionIntent(final Intent intent) throws Exception {
        Mint.leaveBreadcrumb("MainActivity::handleViewPositionIntent");
        final Uri uri = intent.getData();
        Mint.addExtraData("queryParameter", uri.toString());

        final String uriScheme = uri.getScheme();
        if (uriScheme.equals("geo")) {
            final String schemeSpecificPart = uri.getSchemeSpecificPart();
            final Matcher matcher = getMatcherForUri(schemeSpecificPart);
            if (matcher.find()) {
                if (matcher.group(1).equals("0") && matcher.group(2).equals("0")) {
                    if (matcher.find()) { // Manage geo:0,0?q=lat,lng(label)
                        setDestinationPosition(matcher);
                    } else { // Manage geo:0,0?q=my+street+address
                        String destination = Uri.decode(uri.getQuery()).replace('+', ' ');
                        destination = destination.replace("q=", "");

                        // TODO check this ugly workaround
                        new SearchPositionByName().execute(destination);
                        mustShowPositionWhenComingFromOutside = true;
                    }
                } else { // Manage geo:latitude,longitude or geo:latitude,longitude?z=zoom
                    setDestinationPosition(matcher);
                }
            } else {
                final NoSuchFieldException noSuchFieldException = new NoSuchFieldException(
                        "Error al obtener las coordenadas. Matcher = " + matcher.toString());
                Mint.logException(noSuchFieldException);
                throw noSuchFieldException;
            }
        } else if ((uriScheme.equals("http") || uriScheme.equals("https"))
                && (uri.getHost().equals("maps.google.com"))) { // Manage maps.google.com?q=latitude,longitude

            final String queryParameter = uri.getQueryParameter("q");
            if (queryParameter != null) {
                final Matcher matcher = getMatcherForUri(queryParameter);
                if (matcher.find()) {
                    setDestinationPosition(matcher);
                } else {
                    final NoSuchFieldException noSuchFieldException = new NoSuchFieldException(
                            "Error al obtener las coordenadas. Matcher = " + matcher.toString());
                    Mint.logException(noSuchFieldException);
                    throw noSuchFieldException;
                }
            } else {
                final NoSuchFieldException noSuchFieldException = new NoSuchFieldException(
                        "Query sin parmetro q.");
                Mint.logException(noSuchFieldException);
                throw noSuchFieldException;
            }
        } else {
            final Exception exception = new Exception("Imposible tratar la query " + uri.toString());
            Mint.logException(exception);
            throw exception;
        }
    }

    private void setDestinationPosition(final Matcher matcher) {
        Mint.leaveBreadcrumb("MainActivity::setDestinationPosition");
        sendDestinationPosition = new LatLng(Double.valueOf(matcher.group(1)), Double.valueOf(matcher.group(2)));
        mustShowPositionWhenComingFromOutside = true;
    }

    private Matcher getMatcherForUri(final String schemeSpecificPart) {
        Mint.leaveBreadcrumb("MainActivity::getMatcherForUri " + schemeSpecificPart);
        // http://regex101.com/
        // http://www.regexplanet.com/advanced/java/index.html
        final String regex = "(\\-?\\d+\\.*\\d*),(\\-?\\d+\\.*\\d*)";
        final Pattern pattern = Pattern.compile(regex);
        return pattern.matcher(schemeSpecificPart);
    }

    /**
     * Shows the wireless centralized settings in API<11, otherwise shows general settings
     */
    private void showWifiAlertDialog() {
        Mint.leaveBreadcrumb("MainActivity::showWifiAlertDialog");
        showAlertDialog(
                (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB
                        ? android.provider.Settings.ACTION_WIRELESS_SETTINGS
                        : android.provider.Settings.ACTION_SETTINGS),
                getString(R.string.dialog_connection_problems_title),
                getString(R.string.dialog_connection_problems_message),
                getString(R.string.dialog_connection_problems_positive_button),
                getString(R.string.dialog_connection_problems_negative_button), MainActivity.this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        Mint.leaveBreadcrumb("MainActivity::onCreateOptionsMenu");
        // Inflate the options menu from XML
        final MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main, menu);

        // Expandir el EditText de la bsqueda a lo largo del ActionBar
        searchMenuItem = menu.findItem(R.id.action_search);
        final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem);
        // Configure the search info and add any event listeners
        final SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        // Indicamos que la activity actual sea la buscadora
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setSubmitButtonEnabled(false);
        searchView.setQueryRefinementEnabled(true);
        searchView.setIconifiedByDefault(true);

        // Muestra el item de men de cargar si hay elementos en la BD
        final MenuItem loadItem = menu.findItem(R.id.action_load);
        // TODO hacerlo en segundo plano
        final List<Distance> allDistances = getApplicationDaoSession().loadAll(Distance.class);
        if (allDistances.size() == 0) {
            loadItem.setVisible(false);
        }
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Mint.leaveBreadcrumb("MainActivity::onOptionsItemSelected");
        // The action bar home/up action should open or close the drawer.
        // ActionBarDrawerToggle will take care of this.
        if (actionBarDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }

        switch (item.getItemId()) {
        case R.id.action_search:
            return true;
        case R.id.action_load:
            loadDistancesFromDB();
            return true;
        case R.id.menu_rateapp:
            showRateDialog();
            return true;
        case R.id.menu_legalnotices:
            showGooglePlayServiceLicenseDialog();
            return true;
        case R.id.menu_settings:
            openSettingsActivity();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onBackPressed() {
        Mint.leaveBreadcrumb("MainActivity::onBackPressed");
        if (drawerLayout.isDrawerOpen(Gravity.START)) {
            drawerLayout.closeDrawer(Gravity.START);
        } else {
            super.onBackPressed();
        }
    }

    /**
     * Loads all entries stored in the database and show them to the user in a
     * dialog.
     */
    private void loadDistancesFromDB() {
        Mint.leaveBreadcrumb("MainActivity::loadDistancesFromDB");
        // TODO hacer esto en segundo plano
        final List<Distance> allDistances = getApplicationDaoSession().loadAll(Distance.class);

        if (allDistances != null && allDistances.size() > 0) {
            final DistanceAdapter distanceAdapter = new DistanceAdapter(this, allDistances);
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle(getString(R.string.dialog_load_distances_title))
                    .setAdapter(distanceAdapter, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            final Distance distance = distanceAdapter.getDistanceList().get(which);
                            final List<Position> positionList = getApplicationDaoSession().getPositionDao()
                                    ._queryDistance_PositionList(distance.getId());
                            coordinates.clear();
                            coordinates.addAll(convertPositionListToLatLngList(positionList));

                            drawAndShowMultipleDistances(coordinates, distance.getName() + "\n", true, true);
                        }
                    }).create().show();
        }
    }

    private List<LatLng> convertPositionListToLatLngList(final List<Position> positionList) {
        Mint.leaveBreadcrumb("MainActivity::convertPositionListToLatLngList");
        final List<LatLng> result = Lists.newArrayList();
        for (final Position position : positionList) {
            result.add(new LatLng(position.getLatitude(), position.getLongitude()));
        }
        return result;
    }

    /**
     * Shows settings activity.
     */
    private void openSettingsActivity() {
        Mint.leaveBreadcrumb("MainActivity::openSettingsActivity");
        startActivity(new Intent(MainActivity.this, SettingsActivity.class));
    }

    /**
     * Shows rate dialog.
     */
    private void showRateDialog() {
        Mint.leaveBreadcrumb("MainActivity::showRateDialog");
        final AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.dialog_rate_app_title).setMessage(R.string.dialog_rate_app_message)
                .setPositiveButton(getString(R.string.dialog_rate_app_positive_button),
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                                openPlayStoreAppPage();
                            }
                        })
                .setNegativeButton(getString(R.string.dialog_rate_app_negative_button),
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                                openFeedbackActivity();
                            }
                        })
                .create().show();
    }

    /**
     * Opens Google Play Store, in Distance From Me page
     */
    private void openPlayStoreAppPage() {
        Mint.leaveBreadcrumb("MainActivity::openPlayStoreAppPage");
        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=gc.david.dfm")));
    }

    /**
     * Opens the feedback activity.
     */
    private void openFeedbackActivity() {
        Mint.leaveBreadcrumb("MainActivity::openFeedbackActivity");
        final Intent openFeedbackActivityIntent = new Intent(MainActivity.this, FeedbackActivity.class);
        startActivity(openFeedbackActivityIntent);
    }

    /**
     * Shows an AlertDialog with the Google Play Services License.
     */
    private void showGooglePlayServiceLicenseDialog() {
        Mint.leaveBreadcrumb("MainActivity::showGooglePlayServiceLicenseDialog");
        final String LicenseInfo = GooglePlayServicesUtil.getOpenSourceSoftwareLicenseInfo(getApplicationContext());
        final AlertDialog.Builder LicenseDialog = new AlertDialog.Builder(MainActivity.this);
        LicenseDialog.setTitle(R.string.menu_legal_notices_title);
        LicenseDialog.setMessage(LicenseInfo);
        LicenseDialog.show();
    }

    /**
     * Called when the Activity is no longer visible at all. Stop updates and
     * disconnect.
     */
    @Override
    public void onStop() {
        Mint.leaveBreadcrumb("MainActivity::onStop");
        if (googleApiClient.isConnected()) {
            stopPeriodicUpdates();
        }
        super.onStop();
    }

    /**
     * Called when the Activity is restarted, even before it becomes visible.
     */
    @Override
    public void onStart() {
        Mint.leaveBreadcrumb("MainActivity::onStart");
        super.onStart();
        /*
         * Connect the client. Don't re-start any requests here; instead, wait
        * for onResume()
        */
        googleApiClient.connect();
    }

    /**
     * Called when the system detects that this Activity is now visible.
     */
    @SuppressLint("NewApi")
    @Override
    public void onResume() {
        Mint.leaveBreadcrumb("MainActivity::onResume");
        super.onResume();
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
            invalidateOptionsMenu();
        }
        checkPlayServices();
    }

    @Override
    public void onDestroy() {
        Mint.leaveBreadcrumb("MainActivity::onDestroy");
        if (showingElevationTask != null) {
            showingElevationTask.cancel(true);
        }
        super.onDestroy();
    }

    /**
     * Handle results returned to this Activity by other Activities started with
     * startActivityForResult(). In particular, the method onConnectionFailed()
     * in LocationUpdateRemover and LocationUpdateRequester may call
     * startResolutionForResult() to start an Activity that handles Google Play
     * services problems. The result of this call returns here, to
     * onActivityResult.
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        Mint.leaveBreadcrumb("MainActivity::onActivityResult");

        // Choose what to do based on the request code
        switch (requestCode) {

        // If the request code matches the code sent in onConnectionFailed
        case LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST:

            switch (resultCode) {
            // If Google Play services resolved the problem
            case Activity.RESULT_OK:

                // Log the result
                // Log.d(LocationUtils.APPTAG, getString(R.string.resolved));

                // Display the result
                // mConnectionState.setText(R.string.connected);
                // mConnectionStatus.setText(R.string.resolved);
                break;

            // If any other result was returned by Google Play services
            default:
                // Log the result
                // Log.d(LocationUtils.APPTAG,
                // getString(R.string.no_resolution));

                // Display the result
                // mConnectionState.setText(R.string.disconnected);
                // mConnectionStatus.setText(R.string.no_resolution);

                break;
            }

            // If any other request code was received
        default:
            // Report that this Activity received an unknown requestCode
            // Log.d(LocationUtils.APPTAG,
            // getString(R.string.unknown_activity_request_code, requestCode));

            break;
        }
    }

    /**
     * Checks if Google Play Services is available on the device.
     *
     * @return Returns <code>true</code> if available; <code>false</code>
     * otherwise.
     */
    private boolean checkPlayServices() {
        Mint.leaveBreadcrumb("MainActivity::checkPlayServices");
        // Comprobamos que Google Play Services est disponible en el terminal
        final int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());

        // Si est disponible, devolvemos verdadero. Si no, mostramos un mensaje
        // de error y devolvemos falso
        if (resultCode == ConnectionResult.SUCCESS) {
            return true;
        } else {
            if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
                final int RQS_GooglePlayServices = 1;
                GooglePlayServicesUtil.getErrorDialog(resultCode, this, RQS_GooglePlayServices).show();
            } else {
                // Log.i("checkPlayServices", "Dispositivo no soportado");
                finish();
            }
            return false;
        }
    }

    /**
     * Called by Location Services when the request to connect the client
     * finishes successfully. At this point, you can request the current
     * location or start periodic updates
     */
    @Override
    public void onConnected(Bundle bundle) {
        Mint.leaveBreadcrumb("MainActivity::onConnected");
        startPeriodicUpdates();
    }

    @Override
    public void onConnectionSuspended(int i) {
        Mint.leaveBreadcrumb("MainActivity::onConnectionSuspended");
        Log.i("onConnectionSuspended", "GoogleApiClient connection has been suspended");
    }

    /**
     * Called by Location Services if the attempt to Location Services fails.
     */
    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Mint.leaveBreadcrumb("MainActivity::onConnectionFailed");
        /*
         * Google Play services can resolve some errors it detects. If the error
        * has a resolution, try sending an Intent to start a Google Play
        * services activity that can resolve error.
        */
        if (connectionResult.hasResolution()) {
            try {
                // Start an Activity that tries to resolve the error
                connectionResult.startResolutionForResult(this,
                        LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);
                /*
                 * Thrown if Google Play services cancelled the original
                * PendingIntent
                */
            } catch (IntentSender.SendIntentException e) {
                // Log the error
                e.printStackTrace();
                Mint.logException(e);
            }
        } else {
            // If no resolution is available, display a dialog to the user with
            // the error.
            showErrorDialog(connectionResult.getErrorCode());
        }
    }

    /**
     * Report location updates to the UI.
     */
    @Override
    public void onLocationChanged(Location location) {
        Mint.leaveBreadcrumb("MainActivity::onLocationChanged");
        if (currentLocation != null) {
            currentLocation.set(location);
        } else {
            currentLocation = new Location(location);
        }

        if (appHasJustStarted) {
            if (mustShowPositionWhenComingFromOutside) {
                if (currentLocation != null && sendDestinationPosition != null) {
                    new SearchPositionByCoordinates().execute(sendDestinationPosition);
                    mustShowPositionWhenComingFromOutside = false;
                }
            } else {
                final LatLng latlng = new LatLng(location.getLatitude(), location.getLongitude());
                // 17 is a good zoom level for this action
                googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latlng, 17));
            }
            appHasJustStarted = false;
        }
    }

    /**
     * In response to a request to start updates, send a request to Location
     * Services.
     */
    private void startPeriodicUpdates() {
        Mint.leaveBreadcrumb("MainActivity::startPeriodicUpdates");
        locationRequest = LocationRequest.create();
        locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        locationRequest.setInterval(LocationUtils.UPDATE_INTERVAL_IN_MILLISECONDS);
        // Set the interval ceiling to one minute
        locationRequest.setFastestInterval(LocationUtils.FAST_INTERVAL_CEILING_IN_MILLISECONDS);

        LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
    }

    /**
     * In response to a request to stop updates, send a request to Location
     * Services.
     */
    private void stopPeriodicUpdates() {
        Mint.leaveBreadcrumb("MainActivity::stopPeriodicUpdates");
        // After disconnect() is called, the client is considered "dead".
        googleApiClient.disconnect();
    }

    /**
     * Shows a dialog returned by Google Play services for the connection error
     * code
     *
     * @param errorCode An error code returned from onConnectionFailed
     */
    private void showErrorDialog(final int errorCode) {
        Mint.leaveBreadcrumb("MainActivity::showErrorDialog");

        // Get the error dialog from Google Play services
        final Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(errorCode, this,
                LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);

        // If Google Play services can provide an error dialog
        if (errorDialog != null) {
            // Create a new DialogFragment in which to show the error dialog
            final ErrorDialogFragment errorFragment = new ErrorDialogFragment();

            // Set the dialog in the DialogFragment
            errorFragment.setDialog(errorDialog);

            // Show the error dialog in the DialogFragment
            errorFragment.show(getSupportFragmentManager(), "Geofence detection");
        }
    }

    private void drawAndShowMultipleDistances(final List<LatLng> coordinates, final String message,
            final boolean isLoadingFromDB, final boolean mustApplyZoomIfNeeded) {
        Mint.leaveBreadcrumb("MainActivity::drawAndShowMultipleDistances");
        // Borramos los antiguos marcadores y lineas
        googleMap.clear();

        // Calculamos la distancia
        distanceMeasuredAsText = calculateDistance(coordinates);

        // Pintar todos menos el primero si es desde la posicin actual
        addMarkers(coordinates, distanceMeasuredAsText, message, isLoadingFromDB);

        // Aadimos las lneas
        addLines(coordinates, isLoadingFromDB);

        // Aqu hacer la animacin de la cmara
        moveCameraZoom(coordinates.get(0), coordinates.get(coordinates.size() - 1), mustApplyZoomIfNeeded);

        // Muestra el perfil de elevacin si est en las preferencias
        // y si est conectado a internet
        if (getSharedPreferences(getBaseContext()).getBoolean("elevation_chart", false)
                && isOnline(getApplicationContext())) {
            getElevation(coordinates);
        }
    }

    /**
     * Adds a marker to the map in a specified position and shows its info
     * window.
     *
     * @param coordinates     Positions list.
     * @param distance        Distance to destination.
     * @param message         Destination address (if needed).
     * @param isLoadingFromDB Indicates whether we are loading data from database.
     */
    private void addMarkers(final List<LatLng> coordinates, final String distance, final String message,
            final boolean isLoadingFromDB) {
        Mint.leaveBreadcrumb("MainActivity::addMarkers");
        for (int i = 0; i < coordinates.size(); i++) {
            if ((i == 0 && (isLoadingFromDB || distanceMode == DistanceMode.DISTANCE_FROM_ANY_POINT))
                    || (i == coordinates.size() - 1)) {
                final LatLng coordinate = coordinates.get(i);
                final Marker marker = addMarker(coordinate);
                // TODO Release 1.5
                //             marker.setDraggable(true);
                if (i == coordinates.size() - 1) {
                    marker.setTitle(message + distance);
                    marker.showInfoWindow();
                }
            }
        }
    }

    private Marker addMarker(final LatLng coordinate) {
        Mint.leaveBreadcrumb("MainActivity::addMarker");
        return googleMap.addMarker(new MarkerOptions().position(coordinate));
    }

    private void addLines(final List<LatLng> coordinates, final boolean isLoadingFromDB) {
        Mint.leaveBreadcrumb("MainActivity::addLines");
        for (int i = 0; i < coordinates.size() - 1; i++) {
            addLine(coordinates.get(i), coordinates.get(i + 1), isLoadingFromDB);
        }
    }

    /**
     * Adds a line between start and end positions.
     *
     * @param start Start position.
     * @param end   Destination position.
     */
    private void addLine(final LatLng start, final LatLng end, final boolean isLoadingFromDB) {
        Mint.leaveBreadcrumb("MainActivity::addLine");
        final PolylineOptions lineOptions = new PolylineOptions().add(start).add(end);
        lineOptions.width(3 * getResources().getDisplayMetrics().density);
        lineOptions.color(isLoadingFromDB ? Color.YELLOW : Color.GREEN);
        googleMap.addPolyline(lineOptions);
    }

    /**
     * Returns the distance between start and end positions normalized by device
     * locale.
     *
     * @param coordinates position list.
     * @return The normalized distance.
     */
    private String calculateDistance(final List<LatLng> coordinates) {
        Mint.leaveBreadcrumb("MainActivity::calculateDistance");
        double distanceInMetres = 0.0;
        for (int i = 0; i < coordinates.size() - 1; i++) {
            distanceInMetres += Haversine.getDistance(coordinates.get(i).latitude, coordinates.get(i).longitude,
                    coordinates.get(i + 1).latitude, coordinates.get(i + 1).longitude);
        }
        return Haversine.normalizeDistance(distanceInMetres, getResources().getConfiguration().locale);
    }

    /**
     * Moves camera position and applies zoom if needed.
     *
     * @param p1 Start position.
     * @param p2 Destination position.
     */
    private void moveCameraZoom(final LatLng p1, final LatLng p2, final boolean mustApplyZoomIfNeeded) {
        Mint.leaveBreadcrumb("MainActivity::moveCameraZoom");
        double centerLat = 0.0;
        double centerLon = 0.0;

        // Diferenciamos segn preferencias
        final String centre = getSharedPreferences(getBaseContext()).getString("animation", "CEN");
        if (centre.equals("CEN")) {
            centerLat = (p1.latitude + p2.latitude) / 2;
            centerLon = (p1.longitude + p2.longitude) / 2;
        } else if (centre.equals("DES")) {
            centerLat = p2.latitude;
            centerLon = p2.longitude;
        } else if (centre.equals("NO")) {
            return;
        }

        if (mustApplyZoomIfNeeded) {
            googleMap.animateCamera(
                    CameraUpdateFactory.newLatLngZoom(new LatLng(centerLat, centerLon), calculateZoom(p1, p2)));
        } else {
            googleMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(p2.latitude, p2.longitude)));
        }
    }

    private SharedPreferences getSharedPreferences(final Context context) {
        Mint.leaveBreadcrumb("MainActivity::getSharedPreferences");
        return PreferenceManager.getDefaultSharedPreferences(context);
    }

    /**
     * Calculates zoom level to make possible current and destination positions
     * appear in the device.
     *
     * @param origin      Current position.
     * @param destination Destination position.
     * @return Zoom level.
     */
    private float calculateZoom(final LatLng origin, final LatLng destination) {
        Mint.leaveBreadcrumb("MainActivity::calculateZoom");
        double distanceInMetres = Haversine.getDistance(origin.latitude, origin.longitude, destination.latitude,
                destination.longitude);
        double kms = distanceInMetres / 1000;

        if (kms > 2700) {
            return 3;
        } else if (kms > 1300) {
            return 4;
        } else if (kms > 650) {
            return 5;
        } else if (kms > 325) {
            return 6;
        } else if (kms > 160) {
            return 7;
        } else if (kms > 80) {
            return 8;
        } else if (kms > 40) {
            return 9;
        } else if (kms > 20) {
            return 10;
        } else if (kms > 10) {
            return 11;
        } else if (kms > 5) {
            return 12;
        } else if (kms > 2.5) {
            return 13;
        } else if (kms > 1.25) {
            return 14;
        } else if (kms > 0.6) {
            return 15;
        } else if (kms > 0.3) {
            return 16;
        } else if (kms > 0.15) {
            return 17;
        }
        return 18;
    }

    /**
     * Calculates elevation points in background and shows elevation chart.
     *
     * @param coordinates Positions list.
     */
    private void getElevation(final List<LatLng> coordinates) {
        Mint.leaveBreadcrumb("MainActivity::getElevation");
        String positionListUrlParameter = "";
        for (int i = 0; i < coordinates.size(); i++) {
            final LatLng coordinate = coordinates.get(i);
            positionListUrlParameter += String.valueOf(coordinate.latitude) + ","
                    + String.valueOf(coordinate.longitude);
            if (i != coordinates.size() - 1) {
                positionListUrlParameter += "|";
            }
        }
        if (positionListUrlParameter.isEmpty()) {
            final IllegalStateException illegalStateException = new IllegalStateException("Coordinates list empty");
            Mint.logException(illegalStateException);
            throw illegalStateException;
        }

        if (showingElevationTask != null) {
            showingElevationTask.cancel(true);
        }
        showingElevationTask = new GetAltitude().execute(positionListUrlParameter);
    }

    /**
     * Sets map attending to the action which is performed.
     */
    private void fixMapPadding() {
        Mint.leaveBreadcrumb("MainActivity::fixMapPadding");
        if (bannerShown) {
            if (elevationChartShown) {
                googleMap.setPadding(0, rlElevationChart.getHeight(), 0, banner.getLayoutParams().height);
            } else {
                googleMap.setPadding(0, 0, 0, banner.getLayoutParams().height);
            }
        } else {
            if (elevationChartShown) {
                googleMap.setPadding(0, rlElevationChart.getHeight(), 0, 0);
            } else {
                googleMap.setPadding(0, 0, 0, 0);
            }
        }
    }

    private static enum DistanceMode {
        DISTANCE_FROM_CURRENT_POINT, DISTANCE_FROM_ANY_POINT
    }

    /**
     * A subclass of AsyncTask that calls getFromLocationName() in the background.
     */
    private class SearchPositionByName extends AsyncTask<Object, Void, Integer> {

        protected List<Address> addressList;
        protected StringBuilder fullAddress;
        protected LatLng selectedPosition;
        protected ProgressDialog progressDialog;

        @Override
        protected void onPreExecute() {
            Mint.leaveBreadcrumb("SearchPositionByName::onPreExecute");
            addressList = null;
            fullAddress = new StringBuilder();
            selectedPosition = null;

            // Comprobamos que haya conexin con internet (WiFi o Datos)
            if (!isOnline(getApplicationContext())) {
                showWifiAlertDialog();

                // Restauramos el men y que vuelva a empezar de nuevo
                MenuItemCompat.collapseActionView(searchMenuItem);
                cancel(false);
            } else {
                progressDialog = new ProgressDialog(MainActivity.this);
                progressDialog.setTitle(R.string.progressdialog_search_position_title);
                progressDialog.setMessage(getString(R.string.progressdialog_search_position_message));
                progressDialog.setCancelable(false);
                progressDialog.setIndeterminate(true);
                progressDialog.show();
            }
        }

        @Override
        protected Integer doInBackground(Object... params) {
            Mint.leaveBreadcrumb("SearchPositionByName::doInBackground");
            /* get latitude and longitude from the addressList */
            final Geocoder geoCoder = new Geocoder(getApplicationContext(), Locale.getDefault());
            try {
                addressList = geoCoder.getFromLocationName((String) params[0], 5);
            } catch (IOException e) {
                e.printStackTrace();
                Mint.logException(e);
                return -1; // Network is unavailable or any other I/O problem occurs
            }
            if (addressList == null) {
                return -3; // No backend service available
            } else if (addressList.isEmpty()) {
                return -2; // No matches were found
            } else {
                return 0;
            }
        }

        @Override
        protected void onPostExecute(Integer result) {
            Mint.leaveBreadcrumb("SearchPositionByName::onPostExecute");
            switch (result) {
            case 0:
                if (addressList != null && addressList.size() > 0) {
                    // Si hay varios, elegimos uno. Si solo hay uno, mostramos ese
                    if (addressList.size() == 1) {
                        processSelectedAddress(0);
                        handleSelectedAddress();
                    } else {
                        final AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                        builder.setTitle(getString(R.string.dialog_select_address_title));
                        builder.setItems(groupAddresses(addressList).toArray(new String[addressList.size()]),
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int item) {
                                        processSelectedAddress(item);
                                        handleSelectedAddress();
                                    }
                                });
                        builder.create().show();
                    }
                }
                break;
            case -1:
                toastIt(getString(R.string.toast_no_find_address), getApplicationContext());
                break;
            case -2:
                toastIt(getString(R.string.toast_no_results), getApplicationContext());
                break;
            case -3:
                toastIt(getString(R.string.toast_no_find_address), getApplicationContext());
                break;
            }
            progressDialog.dismiss();
            if (searchMenuItem != null) {
                MenuItemCompat.collapseActionView(searchMenuItem);
            }
        }

        private void handleSelectedAddress() {
            Mint.leaveBreadcrumb("SearchPositionByName::handleSelectedAddress");
            if (distanceMode == DistanceMode.DISTANCE_FROM_ANY_POINT) {
                coordinates.add(selectedPosition);
                if (coordinates.isEmpty()) {
                    // add marker
                    final Marker marker = addMarker(selectedPosition);
                    marker.setTitle(fullAddress.toString());
                    marker.showInfoWindow();
                    // moveCamera
                    moveCameraZoom(selectedPosition, selectedPosition, false);
                    distanceMeasuredAsText = calculateDistance(
                            Lists.newArrayList(selectedPosition, selectedPosition));
                    // That means we are looking for a first position, so we want to calculate a distance starting
                    // from here
                    calculatingDistance = true;
                } else {
                    drawAndShowMultipleDistances(coordinates, fullAddress.toString(), false, true);
                }
            } else {
                if (!appHasJustStarted) {
                    if (coordinates == null || coordinates.isEmpty()) {
                        coordinates = Lists.newArrayList();
                        coordinates.add(new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude()));
                    }
                    coordinates.add(selectedPosition);
                    drawAndShowMultipleDistances(coordinates, fullAddress.toString(), false, true);
                } else {
                    // Coming from View Action Intent
                    sendDestinationPosition = selectedPosition;
                }
            }
        }

        /**
         * Processes the address selected by the user and sets the new destination
         * position.
         *
         * @param item The item index in the AlertDialog.
         */
        protected void processSelectedAddress(final int item) {
            Mint.leaveBreadcrumb("SearchPositionByName::processSelectedAddress");
            // Fill address info to show in the marker info window
            final Address address = addressList.get(item);
            for (int i = 0; i <= address.getMaxAddressLineIndex(); i++) {
                fullAddress.append(address.getAddressLine(i)).append("\n");
            }
            selectedPosition = new LatLng(address.getLatitude(), address.getLongitude());
        }

        /**
         * Extract a list of address from a list of Address objects.
         *
         * @param addressList An Address's list.
         * @return A string list with only addresses in text.
         */
        protected List<String> groupAddresses(final List<Address> addressList) {
            Mint.leaveBreadcrumb("SearchPositionByName::groupAddresses");
            final List<String> result = Lists.newArrayList();
            StringBuilder stringBuilder;
            for (final Address l : addressList) {
                stringBuilder = new StringBuilder();
                for (int j = 0; j < l.getMaxAddressLineIndex() + 1; j++) {
                    stringBuilder.append(l.getAddressLine(j)).append("\n");
                }
                result.add(stringBuilder.toString());
            }
            return result;
        }
    }

    /**
     * A subclass of SearchPositionByName to get position by coordinates.
     */
    private class SearchPositionByCoordinates extends SearchPositionByName {
        @Override
        protected Integer doInBackground(Object... params) {
            Mint.leaveBreadcrumb("SearchPositionByCoordinates::doInBackground");
            /* get latitude and longitude from the addressList */
            final Geocoder geoCoder = new Geocoder(getApplicationContext(), Locale.getDefault());
            final LatLng latLng = (LatLng) params[0];
            try {
                addressList = geoCoder.getFromLocation(latLng.latitude, latLng.longitude, 1);
            } catch (final IOException e) {
                e.printStackTrace();
                Mint.logException(e);
                return -1; // No encuentra una direccin, no puede conectar con el servidor
            } catch (final IllegalArgumentException e) {
                final IllegalArgumentException illegalArgumentException = new IllegalArgumentException(
                        String.format("Error en latitud=%f o longitud=%f.\n%s", latLng.latitude, latLng.longitude,
                                e.toString()));
                Mint.logException(illegalArgumentException);
                throw illegalArgumentException;
            }
            if (addressList == null) {
                return -3; // empty list if there is no backend service available
            } else if (addressList.size() > 0) {
                return 0;
            } else {
                return -2; // null if no matches were found // Cuando no hay conexin que sirva
            }
        }

        @Override
        protected void onPostExecute(Integer result) {
            Mint.leaveBreadcrumb("SearchPositionByCoordinates::onPostExecute");
            switch (result) {
            case 0:
                processSelectedAddress(0);
                drawAndShowMultipleDistances(Lists.newArrayList(
                        new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude()),
                        selectedPosition), fullAddress.toString(), false, true);
                break;
            case -1:
                toastIt(getString(R.string.toast_no_find_address), getApplicationContext());
                break;
            case -2:
                toastIt(getString(R.string.toast_no_results), getApplicationContext());
                break;
            case -3:
                toastIt(getString(R.string.toast_no_find_address), getApplicationContext());
                break;
            }
            progressDialog.dismiss();
        }

    }

    /**
     * A subclass of AsyncTask that gets elevation points from coordinates in
     * background and shows an elevation chart.
     *
     * @author David
     */
    private class GetAltitude extends AsyncTask<String, Void, Double> {

        private HttpClient httpClient = null;
        private HttpGet httpGet = null;
        private HttpResponse httpResponse;
        private String responseAsString;
        private InputStream inputStream = null;
        private JSONObject responseJSON;

        @Override
        protected void onPreExecute() {
            Mint.leaveBreadcrumb("GetAltitude::onPreExecute");
            httpClient = new DefaultHttpClient();
            responseAsString = null;

            // Delete elevation chart if exists
            if (graphView != null) {
                rlElevationChart.removeView(graphView);
            }
            rlElevationChart.setVisibility(View.INVISIBLE);
            graphView = null;
            elevationChartShown = false;
            fixMapPadding();
        }

        @Override
        protected Double doInBackground(String... params) {
            Mint.leaveBreadcrumb("GetAltitude::doInBackground");
            httpGet = new HttpGet("http://maps.googleapis.com/maps/api/elevation/json?sensor=true" + "&path="
                    + Uri.encode(params[0]) + "&samples=" + ELEVATION_SAMPLES);
            httpGet.setHeader("content-type", "application/json");
            try {
                httpResponse = httpClient.execute(httpGet);
                inputStream = httpResponse.getEntity().getContent();
                if (inputStream != null) {
                    responseAsString = convertInputStreamToString(inputStream);
                    responseJSON = new JSONObject(responseAsString);
                    if (responseJSON.get("status").equals("OK")) {
                        buildElevationChart(responseJSON.getJSONArray("results"));
                    }
                }
            } catch (ClientProtocolException e) {
                e.printStackTrace();
                Mint.logException(e);
            } catch (IllegalStateException e) {
                e.printStackTrace();
                Mint.logException(e);
            } catch (IOException e) {
                e.printStackTrace();
                Mint.logException(e);
            } catch (JSONException e) {
                e.printStackTrace();
                Mint.logException(e);
            }
            return null;
        }

        @Override
        protected void onPostExecute(Double result) {
            Mint.leaveBreadcrumb("GetAltitude::onPostExecute");
            showElevationProfileChart();
            // When HttpClient instance is no longer needed
            // shut down the connection manager to ensure
            // immediate deallocation of all system resources
            httpClient.getConnectionManager().shutdown();
        }

        /**
         * Converts the InputStream with the retrieved data to String.
         *
         * @param inputStream The input stream.
         * @return The InputStream converted to String.
         * @throws IOException
         */
        private String convertInputStreamToString(final InputStream inputStream) throws IOException {
            Mint.leaveBreadcrumb("GetAltitude::convertInputStreamToString");
            final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            StringBuilder result = new StringBuilder();
            while ((line = bufferedReader.readLine()) != null) {
                result.append(line);
            }

            inputStream.close();
            bufferedReader.close();
            return result.toString();
        }

        /**
         * Builds the information about the elevation profile chart. Use this in
         * a background task.
         *
         * @param array JSON array with the response data.
         * @throws JSONException
         */
        private void buildElevationChart(final JSONArray array) throws JSONException {
            Mint.leaveBreadcrumb("GetAltitude::buildElevationChart");
            // Creates the serie and adds data to it
            final GraphViewSeries series = new GraphViewSeries(null,
                    new GraphViewSeriesStyle(getResources().getColor(R.color.elevation_chart_line),
                            (int) (3 * DEVICE_DENSITY)),
                    new GraphView.GraphViewData[] {});

            for (int w = 0; w < array.length(); w++) {
                series.appendData(new GraphView.GraphViewData(w, Haversine.normalizeAltitudeByLocale(
                        Double.valueOf(array.getJSONObject(w).get("elevation").toString()), Locale.getDefault())),
                        false, array.length());
            }

            // Creates the line and add it to the chart
            graphView = new LineGraphView(getApplicationContext(), getString(R.string.elevation_chart_title,
                    Haversine.getAltitudeUnitByLocale(Locale.getDefault())));
            graphView.addSeries(series);
            graphView.getGraphViewStyle().setGridColor(Color.TRANSPARENT);
            graphView.getGraphViewStyle().setNumHorizontalLabels(1); // Con cero no va
            graphView.getGraphViewStyle().setTextSize(15 * DEVICE_DENSITY);
            graphView.getGraphViewStyle().setVerticalLabelsWidth((int) (50 * DEVICE_DENSITY));
        }

        /**
         * Shows the elevation profile chart.
         */
        private void showElevationProfileChart() {
            Mint.leaveBreadcrumb("GetAltitude::showElevationProfileChart");
            if (graphView != null) {
                rlElevationChart.setVisibility(LinearLayout.VISIBLE);
                rlElevationChart.setBackgroundColor(getResources().getColor(R.color.elevation_chart_background));
                rlElevationChart.addView(graphView);
                elevationChartShown = true;
                fixMapPadding();

                ivCloseElevationChart.setVisibility(View.VISIBLE);
                ivCloseElevationChart.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        rlElevationChart.removeView(graphView);
                        rlElevationChart.setVisibility(View.INVISIBLE);
                        elevationChartShown = false;
                        fixMapPadding();
                    }
                });
            }
        }
    }
}