Bienvenidos sean a este post, en el post anterior hemos visto como definir el mejor proveedor para obtener los datos de localizacion y como emular movimientos en nuestro emulador y hoy nos centraremos en añadir una nueva habilidad a nuestra app llamada Mis Lugares, si necesitan crearla les recomiendo este post donde estan todos los pasos necesarios para su creacion.

Nuestro primer paso sera abrir el proyecto por medio de Android Studio, nuestra modificacion sera en AndroidManifest.xml donde agregaremos la siguiente linea:

<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"
/>

Con esto permitiremos utilizar la ubicacion en nuestra app, nuestro siguiente paso sera comenzar a modificar a la clase MainActivity, primero agreguemos estas dos variables:

    private LocationManager manejador;
    private Location mejorLocaliz;

La primera sera el encargada de nuestro localizador y la segunda variable almacenara la mejor posicion, pasemos a agregar las siguientes lineas a nuestro metodo onCreate():

        manejador = (LocationManager) getSystemService(
                LOCATION_SERVICE);
        if (manejador.isProviderEnabled(LocationManager.
                GPS_PROVIDER)){
            actualizaMejorLocaliz(manejador.getLastKnownLocation(
                    LocationManager.GPS_PROVIDER));
        }
        if (manejador.isProviderEnabled(LocationManager.
                NETWORK_PROVIDER)){
            actualizaMejorLocaliz(manejador.getLastKnownLocation(
                    LocationManager.NETWORK_PROVIDER));
        }

Aca designaremos a manejador el servicio de ubicacion, despues chequearemos por medio de dos condicionales si esta activado por GPS o por red, y en los casos llamara al metodo actualizaMejorLocaliz() y enviara la ultima ubicacion mejor conocida de la red pertinente, sobre este metodo hablaremos luego cuando lo definamos. Ahora agregaremos los metodos para trabajar en los ciclos de vida de nuestra app, agreguemos las siguientes lineas:

    @Override
    protected void onResume(){
        super.onResume();
        activarProveedores();
    }

    private void activarProveedores(){
        if (manejador.isProviderEnabled(LocationManager.
                GPS_PROVIDER)){
            manejador.requestLocationUpdates(LocationManager.
                    GPS_PROVIDER,20 * 1000, 5, this);
            }
        if (manejador.isProviderEnabled(LocationManager.
                NETWORK_PROVIDER)){
            manejador.requestLocationUpdates(LocationManager.
                    NETWORK_PROVIDER,10 * 1000, 10, this);
        }
    }

    @Override
    protected void onPause(){
        super.onPause();
        manejador.removeUpdates(this);
    }

El primer metodo se usa cuando la app vuelve al primer plano y llama a nuestro siguiente metodo llamada activarProveedores(), este metodo se encarga de asignar las actualizaciones de ubicaciones mediante el GPS o la RED dependiendo cual proveedor este activo, la estructura del metodo requestLocationUpdates(), primero el proveedor, luego el tiempo minimo, despues la distancia minima y por ultimo el contexto. Nuestro ultimo metodo se encarga de remover todas las actualizaciones cuando la app pasa a segundo plano y no es usada. Pasemos a permitir la utilizacion la interfaz de LocationManager en nuestra clase para ello modificaremos la linea de nuestra clase de la siguiente manera:

public class MainActivity extends AppCompatActivity
implements AdapterView.OnItemClickListener,
LocationListener

Observen como solo agregamos una coma con el LocationManager, nuestro siguiente paso es agregar los metodos para poder usar esta interfaz:

    @Override
    public void onLocationChanged(Location location){
        Log.d(Lugares.TAG, "Nueva Localizacion: " + location);
        actualizaMejorLocaliz(location);
    }
    
    @Override
    public void onProviderDisabled(String proveedor){
        Log.d(Lugares.TAG,"Se deshabilita: " + proveedor);
        activarProveedores();
    }
    
    @Override
    public void onProviderEnabled(String proveedor){
        Log.d(Lugares.TAG,"Se habilita: " + proveedor);
        activarProveedores();
    }
    
    @Override
    public void onStatusChanged(String proveedor, int estado,
                                Bundle extras){
        Log.d(Lugares.TAG,"Cambia estado: " + proveedor);
        activarProveedores();
    }

Estos metodos son solamente para implementar la interfaz y la unica funcion es monitoerla, agregarla al log y ejecutar un metodo, en el primer caso chequeara si cambio la ubicacion, si es asi lo informa en el log y luego ejecuta actualizaMejorLocaliz() para actualizar su ubicacion, nuestro siguiente metodo si el proveedor esta desactivado, lo pone en el log y lo activa, el tercer metodo chequea si el proveedor esta activada, lo informa en el log y llama al metodo activarProveedores(), el ultimo metodo chequea si cambio el estado, tambien lo informa en el log y llama de nuevo a activarProveedores(), nuestra siguiente modificacion sera para agregar el metodo actualizarMejorLocaliz():

    private static final long DOS_MINUTOS = 2 * 60 * 1000;

    private void actualizaMejorLocaliz(Location localiz){
        if(localiz != null && (mejorLocaliz == null
        || localiz.getAccuracy() < 2 * mejorLocaliz.getAccuracy()
        || localiz.getTime() - mejorLocaliz.
                getTime() > DOS_MINUTOS)) {
            Log.d(Lugares.TAG,"Nueva mejor ubicacion");
            mejorLocaliz = localiz;
            Lugares.posicionActual.setLatitud(
                    localiz.getLatitude());
            Lugares.posicionActual.setLongitud(
                    localiz.getLongitude());
        }
    }

La primera linea es una constante que son dos minutos pero en milisegundos, despues en el metodo chequeara unas condiciones, primero que localiz y mejorLocaliz sean distintos de null, y despues que la precision de uno sea mayor que otro y otro tenga un tiempo mayor a DOS_MINUTOS, si cumplen alguna de las condiciones primero enviara la informacion al log y luego enviara a la clase Lugares los valores de Latitud y Longitud de la ubicacion, antes de continuar con nuestras modificaciones veamos el codigo final de nuestra clase MainActivity:

package com.tinchicus.mislugares;

import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity
        implements AdapterView.OnItemClickListener,
        LocationListener {

    public AdaptadorLugares adaptador;
    private LocationManager manejador;
    private Location mejorLocaliz;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        adaptador = new AdaptadorLugares(this);
        ListView listavista = (ListView) findViewById(R.id.ListaVista);
        listavista.setAdapter(adaptador);
        listavista.setOnItemClickListener(this);
        manejador = (LocationManager) getSystemService(
                LOCATION_SERVICE);
        if (manejador.isProviderEnabled(LocationManager.
                GPS_PROVIDER)){
            actualizaMejorLocaliz(manejador.getLastKnownLocation(
                    LocationManager.GPS_PROVIDER));
        }
        if (manejador.isProviderEnabled(LocationManager.
                NETWORK_PROVIDER)){
            actualizaMejorLocaliz(manejador.getLastKnownLocation(
                    LocationManager.NETWORK_PROVIDER));
        }
    }

    public void onItemClick(AdapterView<?> parent, View vista,
                               int posicion, long id){
        Intent i = new Intent(this,VistaLugar.class);
        i.putExtra("id", id);
        startActivity(i);
    }

    public boolean onCreateOptionsMenu(Menu menu){
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    public void lanzarAcercaDe(View view){
        Intent i = new Intent(this,AcercaDe.class);
        startActivity(i);
    }

    public void lanzarVistaLugar(View view){
        final EditText entrada = new EditText(this);
        entrada.setText("0");
        new AlertDialog.Builder(this)
                .setTitle("Seleccion del Lugar")
                .setMessage("Indica su id")
                .setView(entrada)
                .setPositiveButton("Ok",
                        new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog,
                                        int whichButton) {
                        long id = Long.parseLong(entrada.getText().toString());
                        Intent i = new Intent(MainActivity.this,
                                VistaLugar.class);
                        i.putExtra("id",id);
                        startActivity(i);
                    }
                })
                .setNegativeButton("Cancelar",null)
                .show();
    }

    public boolean onOptionsItemSelected(MenuItem item){
        int id = item.getItemId();
        if (id == R.id.config){
            return true;
        }
        if (id == R.id.buscar){
            lanzarVistaLugar(null);
            return true;
        }
        if (id == R.id.about){
            lanzarAcercaDe(null);
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onResume(){
        super.onResume();
        activarProveedores();
    }

    private void activarProveedores(){
        if (manejador.isProviderEnabled(LocationManager.
                GPS_PROVIDER)){
            manejador.requestLocationUpdates(LocationManager.
                    GPS_PROVIDER,20 * 1000, 5, this);
            }
        if (manejador.isProviderEnabled(LocationManager.
                NETWORK_PROVIDER)){
            manejador.requestLocationUpdates(LocationManager.
                    NETWORK_PROVIDER,10 * 1000, 10, this);
        }
    }

    @Override
    protected void onPause(){
        super.onPause();
        manejador.removeUpdates(this);
    }

    @Override
    public void onLocationChanged(Location location){
        Log.d(Lugares.TAG, "Nueva Localizacion: " + location);
        actualizaMejorLocaliz(location);
    }

    @Override
    public void onProviderDisabled(String proveedor){
        Log.d(Lugares.TAG,"Se deshabilita: " + proveedor);
        activarProveedores();
    }

    @Override
    public void onProviderEnabled(String proveedor){
        Log.d(Lugares.TAG,"Se habilita: " + proveedor);
        activarProveedores();
    }

    @Override
    public void onStatusChanged(String proveedor, int estado,
                                Bundle extras){
        Log.d(Lugares.TAG,"Cambia estado: " + proveedor);
        activarProveedores();
    }

    private static final long DOS_MINUTOS = 2 * 60 * 1000;

    private void actualizaMejorLocaliz(Location localiz){
        if(localiz != null && (mejorLocaliz == null
        || localiz.getAccuracy() < 2 * mejorLocaliz.getAccuracy()
        || localiz.getTime() - mejorLocaliz.
                getTime() > DOS_MINUTOS)) {
            Log.d(Lugares.TAG,"Nueva mejor ubicacion");
            mejorLocaliz = localiz;
            Lugares.posicionActual.setLatitud(
                    localiz.getLatitude());
            Lugares.posicionActual.setLongitud(
                    localiz.getLongitude());
        }
    }
}

Pasemos a la clase Lugares donde agregaremos las siguientes variables:

    final static String TAG = "MisLugares";
    protected static GeoPunto posicionActual = new GeoPunto(0,0);

Con la primera variable indicaremos en el log el nombre de la app y con la segunda linea podremos establecer una nueva ubicacion para nuestros lugares, nos resta modificar el archivo elemento_lista.xml para agregar un nuevo elemento:

    <TextView
        android:id="@+id/distancia"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_below="@id/direccion"
        android:singleLine="true"
        android:text=".... km" />

Este elemento es un TextView y se agrega dentro del RelativeLayout, la particularidad es que se ubicara por debajo de la direccion, debajo y a la izquierda y se identificara como distancia, pasemos ahora a la clase AdaptadorLugares donde haremos las ultimas modificaciones, primero agreguemos la siguiente variable:

TextView distancia;

Luego agregaremos las siguientes instrucciones a nuestro metodo getView():

        distancia = (TextView) vistaReciclada.findViewById(
                R.id.distancia);
        if (Lugares.posicionActual != null 
                && lugar.getPosicion() != null){
            int d = (int) Lugares.posicionActual.
                    distancia(lugar.getPosicion());
            if (d < 2000){
                distancia.setText(d + "m");
            } else {
                distancia.setText(d/1000 + "km");
            }
        }

La primera linea se encargara de crear el vinculo entre el codigo y el elemento (distancia), luego chequeara tanto posicionActual como getPosicion() sean distintas de null, en caso de ser verdad calculara la distancia en base a la posicion del lugar y por ultimo comprobara si es menor a 2000, en caso afirmativo lo mostrara en metros de caso contrario lo dividira por 1000 y lo expresara en km, nuestra app esta lista para ser probada pero antes veamos como quedo finalmente el codigo de AdaptadorLugares:

package com.tinchicus.mislugares;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.RatingBar;
import android.widget.TextView;

public class AdaptadorLugares extends BaseAdapter {

    private LayoutInflater inflador;
    TextView nombre, direccion;
    ImageView foto;
    RatingBar valoracion;
    public AdaptadorLugares(Context contexto){
        inflador = (LayoutInflater) contexto
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    TextView distancia;

    @Override
    public View getView(int posicion, View vistaReciclada,
                        ViewGroup padre) {
        Lugar lugar = Lugares.elemento(posicion);
        if (vistaReciclada == null) {
            vistaReciclada =
                    inflador.inflate(R.layout.elemento_lista, null);
        }
        nombre = (TextView) vistaReciclada.findViewById(
                R.id.nombre);
        direccion = (TextView) vistaReciclada.findViewById(
                R.id.direccion);
        foto = (ImageView) vistaReciclada.findViewById(
                R.id.foto);
        valoracion = (RatingBar) vistaReciclada.findViewById(
                R.id.valoracion);
        distancia = (TextView) vistaReciclada.findViewById(
                R.id.distancia);
        if (Lugares.posicionActual != null
                && lugar.getPosicion() != null){
            int d = (int) Lugares.posicionActual.
                    distancia(lugar.getPosicion());
            if (d < 2000){
                distancia.setText(d + "m");
            } else {
                distancia.setText(d/1000 + "km");
            }
        }
        nombre.setText(lugar.getNombre());
        direccion.setText(lugar.getDireccion());

        int id = R.drawable.otros;
        switch(lugar.getTipo()){
            case RESTAURANTE:id = R.drawable.restaurante; break;
            case BAR:id = R.drawable.bar; break;
            case COPAS:id = R.drawable.copas; break;
            case ESPECTACULO:id = R.drawable.espectaculos; break;
            case HOTEL:id = R.drawable.hotel; break;
            case COMPRAS:id = R.drawable.compras; break;
            case EDUCACION:id = R.drawable.educacion; break;
            case DEPORTE:id = R.drawable.deporte; break;
            case NATURALEZA:id = R.drawable.naturaleza; break;
            case GASOLINERA:id = R.drawable.gasolinera; break;
        }
        foto.setImageResource(id);
        foto.setScaleType(ImageView.ScaleType.FIT_END);
        valoracion.setRating(lugar.getValoracion());
        return vistaReciclada;
    }

    public int getCount(){
        return Lugares.size();
    }

    public Object getItem(int posicion) {
        return Lugares.elemento(posicion);
    }

    public long getItemId(int posicion){
        return posicion;
    }
}

Con todas las modificaciones realizadas podemos probar finalmente nuestra app, si la prueban en un emulador deberan tener una imagen similar a esta

En la imagen podemos ver la distancia desde donde estamos ubicados a os distintos lugares almacenados, tambien se observa como dependiendo la distancia te la expresa en metros (m) o kilometros (km).
En resumen, hoy hemos visto como utilizar la localizacion de una forma practica, permitiendonos calcular las distancia de nosotros con respecto a los lugares almacenados en nuestra app, hemos visto como obtener tanto la latitud y la longitud de un lugar y como almacenarlo, espero les haya sido util sigueme en Twitter, Facebook o Google+ para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios