com.jesjimher.bicipalma.MesProperesActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.jesjimher.bicipalma.MesProperesActivity.java

Source

/* Copyright 2012 Jess Jimnez Herranz
 * 
 * This file is part of BuscaBici
 * 
 * BuscaBici 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, or 
 * (at your option) any later version.
 * 
 * BuscaBici 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 BuscaBici. If not, see <http://www.gnu.org/licenses/>.
 */

package com.jesjimher.bicipalma;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import org.json.JSONArray;
import org.json.JSONException;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.jesjimher.bicipalma.ResultadoBusqueda;

public class MesProperesActivity extends Activity implements LocationListener, DialogInterface.OnDismissListener,
        SharedPreferences.OnSharedPreferenceChangeListener, AdapterView.OnItemClickListener,
        AdapterView.OnItemLongClickListener {
    LocationManager locationManager;
    Location lBest = null;
    // Tiempo inicial de bsqueda de ubicacin
    long tIni;
    ProgressDialog dRecuperaEst;
    private String mUbic;
    private SharedPreferences prefs;

    private ArrayList<Estacion> estaciones;

    private RecuperarEstacionesTask descargaEstaciones;

    private boolean estatWifi = false;
    private String providerCoarse;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mesproperes);
        this.prefs = PreferenceManager.getDefaultSharedPreferences(this);

        // Si s'ha d'activar el wifi en inici, fer-ho
        WifiManager wm = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
        // Guardar el estado actual para restaurarlo al salir
        this.estatWifi = wm.isWifiEnabled();
        if (prefs.getBoolean("activarWifiPref", false))
            wm.setWifiEnabled(true);

        // Cargamos la versin cacheada de las estaciones
        leerCacheEstaciones();

        actualizarListado();

        // Iniciamos la descarga de las estaciones y su estado desde la web (en un thread aparte)
        descargaEstaciones = new RecuperarEstacionesTask(this);
        descargaEstaciones.execute();

        // Inicialmente se busca por red (ms rpido)
        //       dBuscaUbic=ProgressDialog.show(c, "",getString(R.string.buscandoubica),true,true);
        //        Toast.makeText(getApplicationContext(), "Activando", Toast.LENGTH_SHORT).show();
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        Criteria criteria = new Criteria();
        criteria.setAccuracy(Criteria.ACCURACY_COARSE);
        providerCoarse = locationManager.getBestProvider(criteria, true);

        if (providerCoarse == null) {
            Toast.makeText(getApplicationContext(), "No hay forma de posicionarse", Toast.LENGTH_SHORT).show();
            return;
        }
        locationManager.requestLocationUpdates(providerCoarse, 10, 0, (LocationListener) this);

        // Usar ltima ubicacin conocida de red para empezar y recibir futuras actualizaciones
        lBest = locationManager.getLastKnownLocation(providerCoarse);

        // Guardar el inicio de bsqueda de ubicacin para no pasarse de tiempo
        //tIni=new Date().getTime();
        tIni = System.currentTimeMillis();

        // Crear listeners para mostrar estacin en mapa, o abrir men con clic largo
        ListView lv = (ListView) findViewById(R.id.listado);
        lv.setOnItemClickListener((OnItemClickListener) this);
        lv.setOnItemLongClickListener((OnItemLongClickListener) this);
    }

    /**
     * Carga la versin esttica de la lista de estaciones para acelerar el arranque
     * Puede ser la ltima descargada, o la que viene en el APK si no hay ninguna descargada 
     */
    private void leerCacheEstaciones() {
        estaciones = new ArrayList<Estacion>();
        JSONArray js = null;
        String s = "";

        // Se busca primero una copia previa        
        try {
            BufferedReader fis;
            File f = new File(getFilesDir(), "estaciones.json");
            if (f.exists()) {
                //              Toast.makeText(getApplicationContext(), "Leyendo estaciones cacheadas", Toast.LENGTH_SHORT).show();

                fis = new BufferedReader(new FileReader(f));
                s = fis.readLine();
                fis.close();
                js = new JSONArray(s);
                // Si no hay datos dar error aunque sea un JSON vlido
                if (js.length() == 0) {
                    //                 Toast.makeText(getApplicationContext(), "JSON ok, longitud 0", Toast.LENGTH_SHORT).show();
                    js = null;
                }
            }
        } catch (IOException e) {
            js = null;
        } catch (JSONException e) {
            // Si no es un JSON vlido anular
            //           Toast.makeText(getApplicationContext(), "JSON no vlido", Toast.LENGTH_SHORT).show();
            js = null;
        }

        // Si el fichero cacheado no era vlido, leer la versin esttica que incluye el APK
        // Esta tiene que ir bien s o s
        if (js == null) {
            //           Toast.makeText(getApplicationContext(), "Leyendo estaciones raw", Toast.LENGTH_SHORT).show();
            try {
                BufferedReader fis = new BufferedReader(
                        new InputStreamReader(getResources().openRawResource(R.raw.estaciones)));
                s = fis.readLine();
                fis.close();
                js = new JSONArray(s);
            } catch (IOException ie) {
            } catch (JSONException jse) {
            }
        }

        // Una vez ledo el JSON, procesarlo
        estaciones = leerFicheroEstaciones(js);
        // Poner n de bicis/anclajes a desconocido
        for (Estacion e : estaciones) {
            e.setAnclajesLibres(-1);
            e.setBicisLibres(-1);
        }
    }

    /**
     * Activa la bsqueda de ubicacin usando el mejor mtodo disponible
     */
    private void activarUbicacion() {
        locationManager.removeUpdates(this);
        // Comprobar si se ha activado o no el GPS, y decidir el mtodo para ubicarse
        if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
            mUbic = LocationManager.GPS_PROVIDER;
        else {
            //           Toast.makeText(getApplicationContext(), R.string.avisonogps, Toast.LENGTH_LONG).show();
            mUbic = providerCoarse;
        }
        locationManager.requestLocationUpdates(mUbic, 10, 0, (LocationListener) this);
    }

    /**
     *   Actualiza el listado de estaciones 
     */
    public void actualizarListado() {
        // Si no se han descargado las estaciones y hay ubicacin disponible, no hacer nada
        if ((estaciones.size() == 0) || (lBest == null))
            return;

        // Ocultar el dilogo de bsqueda de ubicacin si se estaba visualizando
        if (dRecuperaEst.isShowing())
            dRecuperaEst.dismiss();

        // Mirar si est activa la opcin de ocultar estaciones vacas
        SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        boolean ocultarVacios = sharedPrefs.getBoolean("ocultarVaciosPref", false);

        // Calcular distancias desde la ubicacin actual hasta cada estacin, generando
        // un objeto Resultado
        ArrayList<ResultadoBusqueda> result = new ArrayList<ResultadoBusqueda>();
        Iterator<Estacion> i = estaciones.iterator();
        while (i.hasNext()) {
            Estacion e = (Estacion) i.next();
            Location aux = e.getLoc();
            Double dist = Double.valueOf(lBest.distanceTo(aux));
            if (!(ocultarVacios && (e.getBicisLibres() <= 0)))
                result.add(new ResultadoBusqueda(e, dist));
        }

        // Ordenar por distancia
        Collections.sort(result);

        // Mostrar
        ListView l = (ListView) this.findViewById(R.id.listado);
        l.setAdapter(new ResultadoAdapter(this, result));
    }

    // Cuando llega una nueva ubicacin mejor que la actual, reordenamos el listado
    public void onLocationChanged(Location location) {
        // Slo hacer algo si la nueva ubicacin es mejor que la actual
        if (isBetterLocation(location, lBest)) {

            //          Toast.makeText(getApplicationContext(), "Ubicacin encontrada", Toast.LENGTH_SHORT).show();
            dRecuperaEst.setMessage(getString(R.string.recuperandolista));
            // Actualizar precisin
            TextView pre = (TextView) this.findViewById(R.id.precisionNum);
            if (location.hasAccuracy())
                pre.setText(String.format("%.0f m", location.getAccuracy()));
            else
                pre.setText("Desconocida");

            lBest = location;

            actualizarListado();

        } else {
            //Toast.makeText(getApplicationContext(), "Ignorando ubicacin chunga", Toast.LENGTH_SHORT).show();
        }
        // Si estamos en red y est el GPS activado, pasar a GPS
        // TODO: En API 9 se puede recibir un evento cuando se active el GPS. Investigar si se puede hacer con los modernos sin perder compatibilidad con API 8
        if ((mUbic.equals(providerCoarse)) && (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)))
            activarUbicacion();
    }

    /** Determines whether one Location reading is better than the current Location fix
     *  (EXTRADO DEL SDK, MODIFICADO PARA ADAPTARLO A UBICACIN RPIDA)
     * @param location  The new Location that you want to evaluate
     * @param currentBestLocation  The current Location fix, to which you want to compare the new one
     */
    protected boolean isBetterLocation(Location location, Location currentBestLocation) {
        // A new location is always better than no location
        if (currentBestLocation == null)
            return true;

        // Check whether the new location fix is newer or older
        long timeDelta = location.getTime() - currentBestLocation.getTime();
        boolean isNewer = timeDelta > 0;

        // Check whether the new location fix is more or less accurate
        int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
        boolean isLessAccurate = accuracyDelta > 0;
        boolean isMoreAccurate = accuracyDelta < 0;
        boolean isSignificantlyLessAccurate = accuracyDelta > 200;

        // Check if the old and new location are from the same provider
        boolean isFromSameProvider = location.getProvider().equals(currentBestLocation.getProvider());

        // Determine location quality using a combination of timeliness and accuracy
        if (isMoreAccurate) {
            return true;
        } else if (isNewer && !isLessAccurate) {
            return true;
        } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider)
            return true;
        return false;
    }

    // Si el GPS deja de funcionar, pasar a modo red  
    public void onStatusChanged(String provider, int status, Bundle extras) {
        Toast.makeText(getApplicationContext(), "Cambio estado GPS", Toast.LENGTH_SHORT).show();
        if (provider.equals(LocationManager.GPS_PROVIDER)) {
            if (status != LocationProvider.AVAILABLE)
                activarUbicacion();
        }
    }

    public void onProviderEnabled(String provider) {
    }

    public void onProviderDisabled(String provider) {
    }

    // Dejamos de buscar ubicacin al salir y restauramos el wifi
    @Override
    public void onPause() {
        if (locationManager != null)
            locationManager.removeUpdates(this);
        // Si habia descargas en curso, pararlas
        if (descargaEstaciones != null) {
            descargaEstaciones.cancel(true);
            descargaEstaciones = null;
        }

        WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        wm.setWifiEnabled(this.estatWifi);

        super.onPause();
    }

    // Reactivar wifi si es necesario
    public void onResume() {
        // Reactivar suscripcin a ubicaciones
        activarUbicacion();

        // Reactivar wifi si es necesario
        if (prefs.getBoolean("activarWifiPref", false)) {
            WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
            wm.setWifiEnabled(true);
        }

        super.onResume();
    }

    protected void OnStop() {
        super.onStop();
    }

    public void onDismiss(DialogInterface arg0) {
        //      Toast.makeText(getApplicationContext(), "Fin de bsqueda de ubicacin", Toast.LENGTH_SHORT).show();
        if (locationManager != null)
            locationManager.removeUpdates(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
        case R.id.preferencias:
            Intent settingsActivity = new Intent(getBaseContext(), PreferenciasActivity.class);
            startActivity(settingsActivity);
            prefs.registerOnSharedPreferenceChangeListener(this);
            return true;
        case R.id.actualizar:
            Toast.makeText(getApplicationContext(), getString(R.string.recuperandolista), Toast.LENGTH_SHORT)
                    .show();
            activarUbicacion();
            descargaEstaciones = new RecuperarEstacionesTask(this);
            descargaEstaciones.execute();
            return true;
        case R.id.estado:
            mostrarEstadisticas();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    /**
     * 
     */
    private void mostrarEstadisticas() {
        int bTot = 0;
        int bAver = 0;
        int aAver = 0;
        int bLib = 0;
        int aLib = 0;
        int noBicis = 0;

        for (Estacion e : estaciones) {
            bTot += e.getAnclajesAveriados() + e.getAnclajesLibres() + e.getAnclajesUsados();
            bAver += e.getBicisAveriadas();
            aAver += e.getAnclajesAveriados();
            bLib += e.getBicisLibres();
            aLib += e.getAnclajesLibres();
            if (e.getBicisLibres() == 0)
                noBicis++;
        }
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        // Si el n de bicis es negativo, es q an no se han descargado los datos
        if (bLib < 0) {
            builder.setMessage(R.string.sinDescargaTodavia).setCancelable(true).setPositiveButton(R.string.cerrar,
                    new DialogInterface.OnClickListener() {

                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    });
            AlertDialog alert = builder.create();
            alert.show();
        } else {
            String mensBicis = String.format(getString(R.string.estadisticasBicis), bLib, bAver);
            String mensAnclajes = String.format(getString(R.string.estadisticasAnclajes), bTot,
                    bTot - (aLib + aAver), 100 * (1 - aLib / (0.0 + bTot - aAver)), aLib,
                    100 * aLib / (0.0 + bTot - aAver), aAver, 100 * aAver / Double.valueOf(bTot));

            String mensVacias = String.format(getString(R.string.estadisticasVacias), noBicis, estaciones.size());

            builder.setMessage(mensBicis + mensAnclajes + mensVacias).setTitle(R.string.estadoservicio)
                    .setCancelable(true).setPositiveButton(R.string.cerrar, new DialogInterface.OnClickListener() {

                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    });
            AlertDialog alert = builder.create();
            alert.show();
        }
    }

    // Clase privada para recuperar la lista de estaciones en segundo plano
    private class RecuperarEstacionesTask extends AsyncTask<Void, Void, ArrayList<Estacion>> {

        Context c;

        public RecuperarEstacionesTask(Context c) {
            this.c = c;
        }

        @Override
        protected void onPreExecute() {
            int mensaje;
            if (lBest == null)
                mensaje = R.string.buscandoubicaylista;
            else
                mensaje = R.string.recuperandolista;
            dRecuperaEst = ProgressDialog.show(c, "", getString(mensaje), true, true);
            // Si ya tenemos las estaciones cacheadas, cambiar el texto
            if (estaciones.size() > 0) {
                if (lBest == null)
                    dRecuperaEst.setMessage(getText(R.string.buscandoubica));
                else
                    dRecuperaEst.dismiss();
            }

            ProgressBar pb = (ProgressBar) findViewById(R.id.progreso);
            pb.setIndeterminate(true);
            pb.setVisibility(View.VISIBLE);
        }

        // Cuando acabe de descargar, activar la bsqueda de ubicacin 
        protected void onPostExecute(ArrayList<Estacion> result) {
            //             Toast.makeText(getApplicationContext(), "Descargadas estaciones", Toast.LENGTH_SHORT).show();
            // Cerrar dilogo y guardar resultados
            if (result == null) {
                AlertDialog.Builder builder = new AlertDialog.Builder(c);
                builder.setMessage(R.string.errorconexion).setCancelable(true).setTitle(R.string.error)
                        .setPositiveButton(R.string.cerrar, new DialogInterface.OnClickListener() {

                            public void onClick(DialogInterface dialog, int which) {
                                dialog.cancel();
                            }
                        });
                AlertDialog alert = builder.create();
                alert.show();
            } else {
                estaciones = result;
                actualizarListado();
            }

            if (lBest == null)
                dRecuperaEst.setMessage(getString(R.string.buscandoubica));
            else
                dRecuperaEst.dismiss();

            ProgressBar pb = (ProgressBar) findViewById(R.id.progreso);
            pb.setVisibility(View.INVISIBLE);

        }

        @Override
        protected ArrayList<Estacion> doInBackground(Void... arg0) {
            JSONArray json = BicipalmaJsonClient.connect(
                    "http://83.36.51.60:8080/eTraffic3/DataServer?ele=equ&type=401&li=2.6226425170898&ld=2.6837539672852&ln=39.588022779794&ls=39.555621694894&zoom=15&adm=N&mapId=1&lang=es");

            if (json.length() > 0)
                return leerFicheroEstaciones(json);
            else {
                return null;
            }
        }
    }

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (key.equals("ocultarVaciosPref"))
            actualizarListado();

        if (key.equals("activarWifiPref")) {
            if (sharedPreferences.getBoolean("activarWifiPref", false)) {
                WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
                wm.setWifiEnabled(true);
            }
        }
    }

    /**
     * @param json
     * @return
     * @throws FileNotFoundException 
     */
    private ArrayList<Estacion> leerFicheroEstaciones(JSONArray json) {
        // Extraer estaciones del JSON
        ArrayList<Estacion> est = new ArrayList<Estacion>();
        for (int i = 0; i < json.length(); i++) {
            try {
                String nombre = json.getJSONObject(i).getString("alia");
                // Extraer el n de estacin y eliminarlo del nombre
                int num = Integer.valueOf(nombre.substring(1, 2));
                nombre = nombre.substring(5);
                Location pos = new Location("network");
                pos.setLatitude(json.getJSONObject(i).getDouble("realLat"));
                pos.setLongitude(json.getJSONObject(i).getDouble("realLon"));
                Estacion e = new Estacion(nombre, pos);
                e.setNumEstacion(num);
                String html = json.getJSONObject(i).getString("paramsHtml");
                int pos2 = html.indexOf("Bicis Libres:</span>") + "Bicis Libres:</span>".length();
                if (pos2 > 0)
                    e.setBicisLibres(Integer.valueOf(html.substring(pos2, pos2 + 3).trim()));
                else
                    e.setBicisLibres(0);
                pos2 = html.indexOf("Bicis Averiadas:</span>") + "Bicis Averiadas:</span>".length();
                if (pos2 > 0)
                    e.setBicisAveriadas(Integer.valueOf(html.substring(pos2, pos2 + 3).trim()));
                else
                    e.setBicisAveriadas(0);
                pos2 = html.indexOf("Anclajes Libres:</span>") + "Anclajes Libres:</span>".length();
                if (pos2 > 0)
                    e.setAnclajesLibres(Integer.valueOf(html.substring(pos2, pos2 + 3).trim()));
                else
                    e.setAnclajesLibres(0);
                pos2 = html.indexOf("Anclajes Usados:</span>") + "Anclajes Usados:</span>".length();
                if (pos2 > 0)
                    e.setAnclajesUsados(Integer.valueOf(html.substring(pos2, pos2 + 3).trim()));
                else
                    e.setAnclajesUsados(0);
                pos2 = html.indexOf("Anclajes Averiados:</span>") + "Anclajes Averiados:</span>".length();
                if (pos2 > 0)
                    e.setAnclajesAveriados(Integer.valueOf(html.substring(pos2, pos2 + 3).trim()));
                else
                    e.setAnclajesAveriados(0);
                est.add(e);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        // Escribir el JSON a disco para acelerar futuros accesos
        try {
            if (est.size() > 0) {
                FileOutputStream fos = openFileOutput("estaciones.json", Context.MODE_PRIVATE);
                fos.write(json.toString().getBytes());
                fos.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return est;
    }

    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
        ResultadoBusqueda rb = (ResultadoBusqueda) parent.getAdapter().getItem(position);
        // Ms rpido en posicionar, pero no muestra pin
        //      String uri="geo:"+rb.getEstacion().getLoc().getLatitude()+","+rb.getEstacion().getLoc().getLongitude();
        /*      String uri="geo:0,0?q="+rb.getEstacion().getLoc().getLatitude()+","+rb.getEstacion().getLoc().getLongitude()+" ("+rb.getEstacion().getNombre()+")";
              startActivity(new Intent(android.content.Intent.ACTION_VIEW,Uri.parse(uri)));*/

        Intent i = new Intent(this, MapaActivity.class);
        i.putExtra("estaciones", estaciones);
        i.putExtra("latcentro", rb.getEstacion().getLoc().getLatitude());
        i.putExtra("longcentro", rb.getEstacion().getLoc().getLongitude());
        startActivity(i);
    }

    // Con clic largo abrimos Google Maps con la ruta desde la posicin actual
    public boolean onItemLongClick(final AdapterView<?> parent, final View v, final int position, final long id) {
        final CharSequence[] items = getResources().getTextArray(R.array.menu_contextual);
        AlertDialog.Builder b = new AlertDialog.Builder(this);
        final ResultadoBusqueda rb = (ResultadoBusqueda) parent.getAdapter().getItem(position);

        b.setItems(items, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int item) {
                dialog.dismiss();
                switch (item) {
                case 0:
                    onItemClick(parent, v, position, id);
                    break;
                case 1:
                    //TODO: Pasar a GMaps la versin localizada de "Current location" para que haga l la bsqueda de origen
                    String latlongactual = lBest.getLatitude() + "," + lBest.getLongitude();
                    String latlongdestino = rb.getEstacion().getLoc().getLatitude() + ","
                            + rb.getEstacion().getLoc().getLongitude();
                    Intent intent = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse(
                            "http://maps.google.com/maps?saddr=" + latlongactual + "&daddr=" + latlongdestino));
                    startActivity(intent);
                    break;
                }
            }
        });
        AlertDialog alert = b.create();
        alert.show();

        return true;
    }

}