jueves, 17 de septiembre de 2015

Sensores

La mayoría de los dispositivos Android tienen incorporados sensores de movimiento (aceleración y rotación en los tres ejes), posición (orientación) y ambientales (temperatura, iluminación, humedad, …). Para hacer uso de cualquiera de esos sensores Android proporciona Sensor Framework, una API que permite un acceso sencillo y unificado a la información provista por los mismos.

Lo mínimo necesario

Definir un listener encargado de recibir la información cuando se produzca algún evento relacionado. El Listener se registra a través del método registerListener() de la clase SensorManager.
//Instancia de SensorManager
SensorManager mSensorManager
(SensorManager) getSystemService(Context.SENSOR_SERVICE);

//Obtener el sensor en concreto
Sensor mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);

// Definimos el listener que recibirá las notificaciones
SensorEventListener sensorListener = new SensorEventListener(){
   @Override
   public void onAccuracyChanged(Sensor sensor, int accuracy) { }

   @Override
   public void onSensorChanged(SensorEvent event) {
      // El sensor de luz solamente devuelve un valor
      float lux = event.values[0];
     
   }

};

// Registro del listener
mSensorManager.registerListener(sensorListener, mLight,
                        SensorManager.SENSOR_DELAY_NORMAL);

Añadir al manifest las características hardware que emplea la app:
<uses-feature android:name=android.hardware.sensor.light"/>

Para dejar de recibir las actualizaciones hay que eliminar el listener:
@Override
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(sensorListener);
}

Referencias

http://developer.android.com/guide/topics/sensors/sensors_environment.html

Localización

Para la localización de un usuario, Android puede utilizar GPS o las señales inalámbricas usando la red de localización de Android.

Lo mínimo necesario

Definir un listener encargado de recibir la información cuando se produzca algún evento relacionado. El Listener se registra a través del método requestLocationUpdates() de la clase LocationManager.
//Instancia de LocationManager
LocationManager locationManager
(LocationManager) getSystemService(Context.LOCATION_SERVICE);

// Definimos el listener que recibirá las coordenadas
LocationListener locationListener = newLocationListener(){
    @Override
    public void onLocationChanged(Location location){
      ...
    }

    @Override
    public void onStatusChanged(String provider,int status,
Bundle extras){}

    @Override
    public void onProviderEnabled(String provider){}

    @Override
    public void onProviderDisabled(String provider){}
};

// Registro del listener en el LocationManager
// Se indica por parámetro el proveedor (NETWORK/GPS), el
// mínimo tiempo y mínimo cambio en distancia entre notificaciones
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,0,0, locationListener);

Añadir el permiso de acceso a la localización al AndroidManifest.xml:
<uses-permission android:name=android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

Para dejar de recibir las actualizaciones hay que eliminar el listener:
@Override
protected void onPause() {
super.onPause();
locationManager.removeUpdates(locationListener);
}

Referencias

http://developer.android.com/reference/android/location/LocationListener.html

Google Maps API

La API de Google Maps v2 es parte del SDK de Google Play Services, el cual se instala como parte del Android SDK completo.
Es necesario tener en cuenta para el desarrollo que esta solamente permite la utilización de emuladores configurados con Google Apis y Google Play Services, con versión mínima 4.2.2 (API 17) del SDK.

Lo mínimo necesario

Incluir en el Manifest los permisos mínimos, así como la clave de Google Maps.
La aplicación debe referenciar la librería Google Play Services para poder funcionar correctamente:
<uses-permission
android:name="es.hubiqus.mapas.permission.MAPS_RECEIVE"/>

<uses-permission 
android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-permission
android:name="android.permission.INTERNET"/>
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
   
<uses-feature
      android:glEsVersion="0x00020000"
      android:required="true"/>

<application
        android:icon="@drawable/ic_launcher"
  android:label="@string/app_name"
  android:theme="@android:style/Theme.Light">
       
<meta-data android:name="com.google.android.maps.v2.API_KEY"
               android:value=...

<meta-data android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />


Cabe destacar del Manifest anterior que en los permisos de tipo MAPS_RECEIVE debe incluirse el nombre de paquete de la app.

Esta clase debe chequear si se encuentra habilitado el APK de Google Play Services en el dispositivo, en caso contrario lanza una ventana de descarga del mismo, y cierra la app:
/**
 * Chequear la APK Google Play Services APK
 * Si no, lanza diálogo para la descarga
 */
private boolean checkPlayServices() {
   int resultCode =  
   GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
   if (resultCode != ConnectionResult.SUCCESS) {
    if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, this,
                        PLAY_SERVICES_RESOLUTION_REQUEST).show();
    } else {
      finish();
    }
    return false;
  }
  return true;
}

Incluir el fragment de Google maps dentro de un layout de la app, por defecto incluye los controles de zoom:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/map"
  android:name="com.google.android.gms.maps.SupportMapFragment"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />

Sobre el XML del fragment se pueden establecer los valores de configuración del mapa, de forma que al cargarlo ya aparecerá con las propiedades establecidas:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:map="http://schemas.android.com/apk/res-auto"
  android:id="@+id/map"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  class="com.google.android.gms.maps.SupportMapFragment"
  map:cameraBearing="92.5"
  map:cameraTargetLat="36.7212"
  map:cameraTargetLng="-4.4213"
  map:cameraTilt="40"
  map:cameraZoom="16"
  map:mapType="hybrid"
  map:uiCompass="false"
  map:uiRotateGestures="true"
  map:uiScrollGestures="true"
  map:uiTiltGestures="false"
  map:uiZoomControls="false"
  map:uiZoomGestures="true"/>

Gestión del mapa

De la misma forma que a través del XML, por código se pueden establecer algunas propiedades del mapa y/o modificarlas en tiempo de ejecución:
private GoogleMap mMap;
...

mMap = ((SupportMapFragment) getSupportFragmentManager().
findFragmentById(R.id.map)).getMap();
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);

A continuación se muestra la forma de realizar acciones concretas en el mapa a través de la clase CameraUpdate, que facilita todas las operaciones sobre el mismo.
Para centrar el mapa en un punto simplemente será necesario trasladar la cámara hacia el mismo:
CameraUpdate camUpdate = CameraUpdateFactory.newLatLng(new LatLng(36.719726, -4.421611));
mMap.moveCamera(camUpdate);

Otra opción es animar la cámara, de forma que se produzca en el mapa un efecto de traslado desde el punto de inicio al punto destino:
mMap.animateCamera(camUpdate);


Otra de las acciones habituales será conocer la posición actual de la cámara, es decir, en qué punto se encuentra centrado el mapa:
CameraPosition camPosition = mMap.getCameraPosition();
String position = "Latitud: " + camPosition.target.latitude +
      ",Longitud: " + camPosition.target.longitude;

El objeto CameraPosition se puede emplear también para modificar la posición actual de la cámara, permitiendo establecer un mayor número de parámetros que los permitidos por los métodos de CameraUpdateFactory:
LatLng target = new LatLng(36.719726, -4.421611);
CameraPosition pos = new CameraPosition.Builder()
.target(target)   //Centrar el mapa
.zoom(16)         //Zoom
      .bearing(45)      //Orientación
      .tilt(70)         //Ángulo de cámara
      .build();
           
CameraUpdate camUpdate = CameraUpdateFactory.newCameraPosition(pos);

Agregar un marcador a un mapa con esta API es sencillo. El siguiente código establece un marcador con icono personalizado, que al ser pulsado mostrará un pequeño diálogo sobre el mapa con título y descripción (snippet):
mMap.addMarker(new MarkerOptions()
.position(new LatLng(36.719726, -4.421611))
.icon(BitmapDescriptorFactory.fromResource(R.drawable.blank))
.title("Málaga")
.snippet("Centro de Málaga"));

Para responder a eventos sobre la pulsación en el icono y poder realizar acciones personalizadas, será necesario establecer un Listener sobre el mapa:
mMap.setOnMarkerClickListener(new OnMarkerClickListener() {
      public boolean onMarkerClick(Marker marker) {
            Toast t = Toast.makeText(MapaActivity.this,
                  marker.getTitle(),
                  Toast.LENGTH_SHORT);
t.show();
            return false;
      }
});

Además de Listeners para esta tarea, se pueden establecer para otras acciones como el cambio de posición (OnCameraChangeListener) o también para la pulsación de un punto cualquiera sobre el mapa:
mMap.setOnMapClickListener(new OnMapClickListener() {
      public void onMapClick(LatLng point) {
Toast t = Toast.makeText(MapaActivity.this,
                  "Latitud: " + point.latitude +
                  ", Longitud: " + point.longitude,
                  Toast.LENGTH_SHORT);
t.show();
      }
});

Para que la ventana de información (InfoWindow) no aparezca sobre el mapa, simplemente se pueden añadir marcadores sin título. Esto debe combinarse con el OnMarkerClickListener para que la aplicación realice alguna acción personalizada al pulsar sobre los marcadores:
mMap.addMarker(new MarkerOptions()
.position(new LatLng(36.719726, -4.421611))
.snippet("Centro de Málaga"));

También es posible personalizar la ventana de información, creando un InfoWindowAdapter y estableciéndolo en el mapa a través del método setInfoWindowAdapter.

App Google Maps

Para acciones avanzadas con Google Maps siempre se puede recurrir a la aplicación propia de Google que viene instalada en todos los dispositivos. Esta aplicación está basada en las urls que se emplean en la propia web de Google, de forma que si se intenta lanzar dicha url en un Intent, el dispositivo abrirá la aplicación mostrando la información necesaria.
El siguiente código muestra una ruta entre dos puntos:
//Incluir dirflg=w para ruta a pie
String uri = "http://maps.google.com/maps?saddr="                            + origen.getLatitud() + ","
            + origen.getLongitud() + "&daddr="
            + punto.getLatitud() + ","
            + punto.getLongitud();
           
Intent intent = new Intent(                    android.content.Intent.ACTION_VIEW, Uri.parse(uri));
                                intent.setClassName("com.google.android.apps.maps",
                  "com.google.android.maps.MapsActivity");
startActivity(intent);


Referencia